public ThumbnailBlock(BufferedBinaryReader br) { this.width = br.ReadInt32(); this.height = br.ReadInt32(); this.bitDepth = br.ReadUInt16(); this.compressionType = (PSPCompression)br.ReadUInt16(); this.planeCount = br.ReadUInt16(); this.colorCount = br.ReadUInt32(); this.paletteEntryCount = br.ReadUInt32(); this.channelCount = br.ReadUInt16(); this.channelBlocks = new ChannelSubBlock[this.channelCount]; int index = 0; do { uint blockSig = br.ReadUInt32(); if (blockSig != PSPConstants.blockIdentifier) { throw new System.FormatException(Properties.Resources.InvalidBlockSignature); } PSPBlockID blockType = (PSPBlockID)br.ReadUInt16(); #pragma warning disable IDE0059 // Value assigned to symbol is never used uint chunkLength = br.ReadUInt32(); #pragma warning restore IDE0059 // Value assigned to symbol is never used switch (blockType) { case PSPBlockID.ColorPalette: this.paletteSubBlock = new ColorPaletteBlock(br, PSPConstants.majorVersion5); break; case PSPBlockID.Channel: ChannelSubBlock block = new ChannelSubBlock(br, this.compressionType, PSPConstants.majorVersion5); this.channelBlocks[index] = block; index++; break; } }while (index < this.channelCount); }
private unsafe ChannelSubBlock[] SplitImageChannels( Surface source, Rectangle savedBounds, int channelCount, ushort majorVersion, bool composite, ProgressEventHandler callback) { ChannelSubBlock[] channels = new ChannelSubBlock[channelCount]; int channelSize = savedBounds.Width * savedBounds.Height; for (int i = 0; i < channelCount; i++) { channels[i] = new ChannelSubBlock(majorVersion, (uint)channelSize); if (composite) { switch (majorVersion) { case PSPConstants.majorVersion5: channels[i].bitmapType = PSPDIBType.Thumbnail; break; default: channels[i].bitmapType = i < 3 ? PSPDIBType.Composite : PSPDIBType.CompositeTransparencyMask; break; } } else { channels[i].bitmapType = i < 3 ? PSPDIBType.Image : PSPDIBType.TransparencyMask; } switch (i) { case 0: channels[i].channelType = PSPChannelType.Red; break; case 1: channels[i].channelType = PSPChannelType.Green; break; case 2: channels[i].channelType = PSPChannelType.Blue; break; case 3: channels[i].channelType = PSPChannelType.Composite; break; } } if (channelSize > 0) { byte[] red = new byte[channelSize]; byte[] green = new byte[channelSize]; byte[] blue = new byte[channelSize]; byte[] alpha = channelCount == 4 ? new byte[channelSize] : null; for (int y = savedBounds.Top; y < savedBounds.Bottom; y++) { ColorBgra *p = source.GetPointAddressUnchecked(savedBounds.Left, y); int index = ((y - savedBounds.Top) * savedBounds.Width); for (int x = savedBounds.Left; x < savedBounds.Right; x++) { red[index] = p->R; green[index] = p->G; blue[index] = p->B; if (channelCount == 4) { alpha[index] = p->A; } p++; index++; } } for (int channelIndex = 0; channelIndex < channelCount; channelIndex++) { byte[] inData = null; switch (channelIndex) { case 0: inData = red; break; case 1: inData = green; break; case 2: inData = blue; break; case 3: inData = alpha; break; } byte[] compBuffer = null; switch (this.imageAttributes.CompressionType) { case PSPCompression.None: compBuffer = inData; channels[channelIndex].uncompressedChannelLength = 0; break; case PSPCompression.LZ77: using (MemoryStream ms = new MemoryStream()) { using (ZlibStream zs = new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.Level9, true)) { int length = inData.Length; int offset = 0; do { int chunkSize = Math.Min(ZlibConstants.WorkingBufferSizeDefault, length); zs.Write(inData, offset, chunkSize); offset += chunkSize; length -= chunkSize; } while (length > 0); } compBuffer = ms.ToArray(); } break; } if (callback != null) { this.doneProgress++; callback(this, new ProgressEventArgs(100.0 * ((double)this.doneProgress / this.totalProgress))); } channels[channelIndex].compressedChannelLength = (uint)compBuffer.Length; channels[channelIndex].channelData = compBuffer; } } return(channels); }
public Document Load(Stream input) { LoadPSPFile(input); if (this.imageAttributes == null || this.layerBlock == null) { throw new FormatException(Properties.Resources.InvalidPSPFile); } if (this.imageAttributes.BitDepth <= 8 && this.globalPalette == null) { throw new FormatException(Properties.Resources.ColorPaletteNotFound); } LayerInfoChunk[] infoChunks = this.layerBlock.LayerInfo; int layerCount = infoChunks.Length; // Some PSP files may have layers that are larger than the dimensions stored in the image attributes. int maxWidth = this.imageAttributes.Width; int maxHeight = this.imageAttributes.Height; for (int i = 0; i < layerCount; i++) { Rectangle savedBounds = infoChunks[i].saveRect; if (savedBounds.Width > maxWidth) { maxWidth = savedBounds.Width; } if (savedBounds.Height > maxHeight) { maxHeight = savedBounds.Height; } } if (maxWidth <= 0 || maxHeight <= 0) { throw new FormatException(Properties.Resources.InvalidDocumentDimensions); } Document doc = new Document(maxWidth, maxHeight); if (this.imageAttributes.ResValue > 0.0) { switch (this.imageAttributes.ResUnit) { case ResolutionMetric.Inch: doc.DpuUnit = MeasurementUnit.Inch; doc.DpuX = doc.DpuY = this.imageAttributes.ResValue; break; case ResolutionMetric.Centimeter: doc.DpuUnit = MeasurementUnit.Centimeter; doc.DpuX = doc.DpuY = this.imageAttributes.ResValue; break; default: break; } } short transIndex = -1; if (this.imageAttributes.BitDepth < 24 && this.extData != null) { byte[] data; if (this.extData.Values.TryGetValue(PSPExtendedDataID.TransparencyIndex, out data)) { transIndex = BitConverter.ToInt16(data, 0); } } LayerBitmapInfoChunk[] bitmapInfoChunks = this.layerBlock.LayerBitmapInfo; for (int i = 0; i < layerCount; i++) { LayerInfoChunk info = infoChunks[i]; BitmapLayer layer = new BitmapLayer(doc.Width, doc.Height) { Name = info.name, Opacity = info.opacity }; UserBlendOp blendOp = BlendModetoBlendOp(info.blendMode); layer.SetBlendOp(blendOp); layer.Visible = (info.layerFlags & PSPLayerProperties.Visible) == PSPLayerProperties.Visible; Rectangle saveRect = info.saveRect; if (!saveRect.IsEmpty) { LayerBitmapInfoChunk bitmapInfo = bitmapInfoChunks[i]; if (bitmapInfo.bitmapCount == 1) { new UnaryPixelOps.SetAlphaChannelTo255().Apply(layer.Surface, saveRect); } int alphaIndex = 0; int bitDepth = this.imageAttributes.BitDepth; int bytesPerPixel = 1; int stride = saveRect.Width; byte[] expandedPalette = null; switch (bitDepth) { case 48: bytesPerPixel = 2; stride *= 2; break; case 4: case 1: expandedPalette = ExpandPackedPalette(saveRect, bitmapInfo, bitDepth); break; } unsafe { int palIdx = 0; NativeStructs.RGBQUAD entry; Surface surface = layer.Surface; for (int y = saveRect.Top; y < saveRect.Bottom; y++) { ColorBgra *ptr = surface.GetPointAddressUnchecked(saveRect.Left, y); ColorBgra *endPtr = ptr + saveRect.Width; int index = ((y - saveRect.Top) * stride); while (ptr < endPtr) { switch (bitDepth) { case 48: for (int ci = 0; ci < bitmapInfo.channelCount; ci++) { ChannelSubBlock ch = bitmapInfo.channels[ci]; if (ch.bitmapType == PSPDIBType.Image) { ushort col = (ushort)(ch.channelData[index] | (ch.channelData[index + 1] << 8)); // PSP format is always little endian byte clamped = (byte)((col * 255) / 65535); switch (ch.channelType) { case PSPChannelType.Red: ptr->R = clamped; break; case PSPChannelType.Green: ptr->G = clamped; break; case PSPChannelType.Blue: ptr->B = clamped; break; } } else if (ch.bitmapType == PSPDIBType.TransparencyMask) { ptr->A = ch.channelData[alphaIndex]; alphaIndex++; } } break; case 24: for (int ci = 0; ci < bitmapInfo.channelCount; ci++) { ChannelSubBlock ch = bitmapInfo.channels[ci]; if (ch.bitmapType == PSPDIBType.Image) { switch (ch.channelType) { case PSPChannelType.Red: ptr->R = ch.channelData[index]; break; case PSPChannelType.Green: ptr->G = ch.channelData[index]; break; case PSPChannelType.Blue: ptr->B = ch.channelData[index]; break; } } else if (ch.bitmapType == PSPDIBType.TransparencyMask) { ptr->A = ch.channelData[index]; } } break; case 8: for (int ci = 0; ci < bitmapInfo.channelCount; ci++) { ChannelSubBlock ch = bitmapInfo.channels[ci]; palIdx = ch.channelData[index]; entry = this.globalPalette.entries[palIdx]; switch (ch.bitmapType) { case PSPDIBType.Image: ptr->R = entry.rgbRed; ptr->G = entry.rgbGreen; ptr->B = entry.rgbBlue; if (palIdx == transIndex) { ptr->A = 0; } break; case PSPDIBType.TransparencyMask: ptr->A = entry.rgbRed; break; } } break; case 4: case 1: palIdx = expandedPalette[index]; entry = this.globalPalette.entries[palIdx]; ptr->R = entry.rgbRed; ptr->G = entry.rgbGreen; ptr->B = entry.rgbBlue; if (palIdx == transIndex) { ptr->A = 0; } else if ((bitmapInfo.bitmapCount == 2) && bitmapInfo.channels[1].bitmapType == PSPDIBType.TransparencyMask) { ptr->A = bitmapInfo.channels[1].channelData[index]; } break; default: throw new FormatException(string.Format(Properties.Resources.UnsupportedBitDepth, bitDepth)); } ptr++; index += bytesPerPixel; } } } } #if DEBUG using (Bitmap temp = layer.Surface.CreateAliasedBitmap()) { } #endif doc.Layers.Add(layer); } string creatorData = SerializeToBase64(this.creator); doc.Metadata.SetUserValue(PSPCreatorMetaData, creatorData); return(doc); }