Ejemplo n.º 1
0
        //this is where all the main layer information is parsed
        private void parseLayerInfo()
        {
            Console.Out.WriteLine("parsing layer info");

            //get the length of the layers data
            uint lengthOfLayersData = bytes.getUI32();

            //and the number of layers
            short layerCount = bytes.getI16();

            traceProperty("number of layers", layerCount);

            //if the layers count is negative, it maens there is something special
            //about the alpha channel
            if (layerCount < 0)
            {
                layerCount *= -1;
                //throw new Exception("negative layers not implemented yet");
            }

            //numberOfLayers is a uint
            numberOfLayers = (uint)layerCount;

            //cycle through each layer and parse it
            for (uint i = 0; i < layerCount; i++)
            {
                //using the builder pattern here to avoid a massive constructor
                Layer layer = new Layer();

                //doing the parsing here rather than delegating it to the layer class for readability
                //think these are ints
                layer.top    = bytes.getI32();
                layer.left   = bytes.getI32();
                layer.bottom = bytes.getI32();
                layer.right  = bytes.getI32();

                //although the channels are stored in a different section, the number
                //of channels is stored here
                layer.numberOfChannels = bytes.getUI16();
                layer.hasTransparency  = layer.numberOfChannels > 3;

                //parse the channels info, (not actually the channels yet/0
                for (uint a = 0; a < layer.numberOfChannels; a++)
                {
                    LayerChannelData channelData = new LayerChannelData();
                    channelData.ID = bytes.getI16();
                    channelData.channelDataLength = bytes.getUI32();
                    layer.channels.Add(channelData);
                }

                layer.signiture = bytes.getString(4);

                if (layer.signiture != "8BIM")
                {
                    throw new Exception("Encountered layer with signiture: " + layer.signiture);
                }

                //options like the blendModeKey arent used, but still parsed
                bytes.skipBytes(4);

                //between 0-255
                layer.opacity = bytes.getByte();

                //the base
                bytes.skipBytes(1);

                //the flags are booleans which are stored as single bits in one byte
                byte flags = bytes.getByte();
                //checked in libpsd, its a bit cryptic, but looks like 0 is true, 1 is false
                layer.visible = (flags & 2) == 0;

                //padding
                bytes.skipBytes(1);

                //the extra data fields are tags found at the end of the layer
                //and are very similar to image resource blocks
                uint extraDataFieldLength = bytes.getUI32();

                //end of the whole layer, i think
                uint possibleEnd = (uint)bytes.Position + extraDataFieldLength;

                //parse the layer mask, dont think this is implemented yet
                parseLayerMask(layer);

                //parse the blending ranges, dont think this is implemented yet
                parseLayerBlendingRanges(layer);

                //read the name of the layer,  the name also exists as a unicode name
                //in an extra tag block
                layer.name = bytes.getPascalString(4);

                //cycle through the extra tags
                while (bytes.Position != possibleEnd)
                {
                    if (bytes.Position > possibleEnd)
                    {
                        throw new Exception("overflow when parsing extra layer tags");
                    }

                    String sig = bytes.getString(4);

                    if (sig != "8BIM")
                    {
                        //really screwed up here
                        throw new Exception("unknown sig for layer tag: " + sig);
                    }

                    //get the tag name and the tag length
                    String tagName   = bytes.getString(4);
                    int    tagLength = bytes.getI32();

                    //the tag length is padded to an even number
                    if (tagLength % 2 == 1)
                    {
                        tagLength++;
                    }

                    uint tagEnd = (uint)(bytes.Position + tagLength);

                    //i dont like giving 'bytes' to the layer, but im resetting the position afterwards
                    //so it should be fine.
                    //parse the tag
                    layer.parseAdditionalLayerInfo(tagName, tagLength, bytes);

                    //make sure that we are at the start of the next tag,
                    bytes.Position = tagEnd;
                }

                //make sure we are at the end of the section
                bytes.Position = possibleEnd;

                layers.Add(layer);
            }
        }
Ejemplo n.º 2
0
        internal void parseImageChannelData(ByteArray bytes)
        {
            Console.Out.WriteLine("layer: " + name);

            //dont want to render folders or invalid bounds
            if (type != Layer.TYPE_NORMAL || bottom <= top || right <= left)
            {
                return;
            }

            image = new Bitmap(right - left, bottom - top, PixelFormat.Format32bppArgb);

            //cycle through the layers, parsing the bytes, and decoding if necessary
            foreach (LayerChannelData channel in channels)
            {
                //get the next channel pos, so if something goes wrong parsing
                //this channel, it wont screw up the next one
                uint nextChannelPos = (uint)(bytes.Position + channel.channelDataLength);

                if (channel.ID < -1)
                {
                    //skipping user supplied masks
                    bytes.Position = nextChannelPos;
                    continue;
                }

                //lets hope bytes.position is in the right place
                uint compressionMethod = bytes.getUI16();

                //if it needs decompressing
                switch (compressionMethod)
                {
                case 0:
                    //not compressed
                    //get the data and store it in the individual channel objects
                    //-2 is for the 2 bytes taken up by the compression type
                    channel.data          = bytes.getBytes((uint)(image.Width * image.Height));
                    channel.data.Position = 0;
                    break;

                case 1:
                    //rle
                    channel.data = new ByteArray(new byte[0]);

                    //each scan line is compressed seperately, so we need to get the
                    //length of each encoded line
                    List <uint> lineLengths = new List <uint>(image.Height);

                    //then we can cycle though each line and decode it
                    for (int i = 0; i < image.Height; ++i)
                    {
                        lineLengths.Add(bytes.getUI16());
                    }

                    //now that the lines have been decoded, we can write them back to
                    //the channel
                    MemoryStream data = new MemoryStream();
                    for (int i = 0; i < image.Height; ++i)
                    {
                        ByteArray line  = bytes.getBytes(lineLengths[i]);
                        byte[]    unrle = ByteArray.unRLE(line);
                        data.Write(unrle, 0, unrle.Length);
                    }

                    channel.data = new ByteArray(data.ToArray());

                    break;

                case 2:
                case 3:
                    //cant handle these compression methods,
                    //going to skip for now
                    return;
                }

                //just in case the rle doesnt reset the position properly
                channel.data.Position = 0;

                bytes.Position = nextChannelPos;
            }

            //will be null if it doesnt exist
            LayerChannelData alphaChannel = getChannel(LayerChannelData.TRANSPARENCY_MASK);

            LayerChannelData redChannel   = getChannel(LayerChannelData.RED);
            LayerChannelData greenChannel = getChannel(LayerChannelData.GREEN);
            LayerChannelData blueChannel  = getChannel(LayerChannelData.BLUE);

            for (uint j = 0; j < image.Height; j++)
            {
                for (uint k = 0; k < image.Width; k++)
                {
                    //if there is alpha we have to use setPixel32 as there are 4 bytes
                    //to worry about
                    if (alphaChannel != null)
                    {
                        image.SetPixel((int)k, (int)j, Color.FromArgb(alphaChannel.data.getByte(),
                                                                      redChannel.data.getByte(),
                                                                      greenChannel.data.getByte(),
                                                                      blueChannel.data.getByte()));
                    }
                    else
                    {
                        image.SetPixel((int)k, (int)j, Color.FromArgb(redChannel.data.getByte(),
                                                                      greenChannel.data.getByte(),
                                                                      blueChannel.data.getByte()));
                    }
                }
            }

            //now sort out out the mask, cant do masks very well
            if (mask != null && mask.right > mask.left && mask.top < mask.bottom)
            {
                //this represents the bounds of the image
                Rectangle realRect = new Rectangle(left, top, right - left, bottom - top);

                //this represents the bounds of the mask
                Rectangle maskRect = new Rectangle(mask.left, mask.top, mask.right - mask.left, mask.bottom - mask.top);

                //we combine them to get the new bounds
                Rectangle insideRect = new Rectangle();
                insideRect.X      = Math.Max(realRect.X, maskRect.X);
                insideRect.Y      = Math.Max(realRect.Y, maskRect.Y);
                insideRect.Width  = Math.Min(realRect.Right, maskRect.Right) - insideRect.X;
                insideRect.Height = Math.Min(realRect.Bottom, maskRect.Bottom) - insideRect.Y;

                image  = image.Clone(insideRect, PixelFormat.Format32bppArgb);
                left   = insideRect.X;
                right  = insideRect.Right;
                top    = insideRect.Y;
                bottom = insideRect.Bottom;
            }
        }