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); }