Пример #1
0
        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);
        }
Пример #2
0
        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);
        }
Пример #3
0
        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);
        }