예제 #1
0
            /// <summary>
            /// Reads a layer record from a stream and populates a PSD layer object with the information.
            /// </summary>
            /// <param name="psd">
            /// The PSD file being read. Used to obtain the file format version and to populate the
            /// <see cref="PSDFile.Layers"/> array if general layer information has been "outsourced" to an additional
            /// layer information block.
            /// </param>
            /// <param name="layer">The layer to populate with information read from the stream.</param>
            /// <param name="stream">The stream from which to read layer information.</param>
            public static void ReadLayerRecord(PSDFile psd, PSDLayer layer, Stream stream)
            {
                layer.Top    = stream.ReadBigEndianInt32();
                layer.Left   = stream.ReadBigEndianInt32();
                layer.Bottom = stream.ReadBigEndianInt32();
                layer.Right  = stream.ReadBigEndianInt32();

                short numberChannels = stream.ReadBigEndianInt16();

                layer.Channels = new PSDLayerChannel[numberChannels];
                for (int i = 0; i < numberChannels; ++i)
                {
                    var channel = new PSDLayerChannel();
                    channel.ID         = stream.ReadBigEndianInt16();
                    channel.DataLength = (psd.Version == 2)
                        ? stream.ReadBigEndianInt64()
                        : stream.ReadBigEndianInt32();
                    layer.Channels[i] = channel;
                }

                string blendModeSignature = stream.ReadUsAsciiString(4);

                if (blendModeSignature != BlendModeSignature)
                {
                    throw new PSDFormatException($"expected blend mode signature \"{BlendModeSignature}\", got \"{blendModeSignature}\"");
                }

                int blendModeValue = stream.ReadBigEndianInt32();

                if (!Enum.IsDefined(typeof(BlendMode), blendModeValue))
                {
                    char[] blendModeChars =
                    {
                        (char)((blendModeValue >> 24) & 0xFF),
                        (char)((blendModeValue >> 16) & 0xFF),
                        (char)((blendModeValue >> 8) & 0xFF),
                        (char)((blendModeValue >> 0) & 0xFF)
                    };
                    string blendModeString = new string(blendModeChars);
                    throw new PSDFormatException($"invalid blend mode {blendModeValue} (\"{blendModeString}\")");
                }
                layer.BlendMode = (BlendMode)blendModeValue;

                byte opacity = stream.ReadByteOrThrow();

                layer.Opacity = opacity;

                byte clipping = stream.ReadByteOrThrow();

                if (clipping > 1)
                {
                    throw new PSDFormatException($"invalid clipping value {clipping}; expected 0 (base) or 1 (non-base)");
                }
                layer.NonBaseClipping = (clipping == 1);

                byte flags = stream.ReadByteOrThrow();

                if ((flags & 0x01) != 0)
                {
                    layer.TransparencyProtected = true;
                }
                if ((flags & 0x02) != 0)
                {
                    layer.Visible = true;
                }
                if ((flags & 0x04) != 0)
                {
                    layer.Obsolete = true;
                }
                if ((flags & 0x18) == 0x18)
                {
                    // (both bits 3 and 4 must be set)
                    layer.PixelDataIrrelevantToDocumentAppearance = true;
                }

                // filler
                stream.ReadByteOrThrow();

                int  extraDataLength = stream.ReadBigEndianInt32();
                long extraDataStart  = stream.Position;

                ReadLayerMask(layer, stream);
                ReadLayerBlendingRanges(layer, stream);

                byte layerNameLength = stream.ReadByteOrThrow();

                layer.Name = stream.ReadWindows1252String(layerNameLength);
                int padTo4Bytes = 4 - ((layerNameLength + 1) % 4);

                if (padTo4Bytes != 4)  // there is zero padding if == 4
                {
                    stream.ReadBytes(padTo4Bytes);
                }

                layer.AdditionalInformation = new List <PSDAdditionalLayerInformation>();
                while (stream.Position < extraDataStart + extraDataLength)
                {
                    PSDAdditionalLayerInformation pali = PSDFile.Reading.ReadAdditionalLayerInformation(psd, stream);
                    layer.AdditionalInformation.Add(pali);
                }
            }
예제 #2
0
            /// <summary>
            /// Reads the layer information subsection of a PSD layer and mask information section from a stream and
            /// populates a <see cref="PSDFile"/> with the information.
            /// </summary>
            /// <param name="psd">The PSD file object to populate.</param>
            /// <param name="stream">The stream from which to read the layer information subsection.</param>
            /// <param name="roundToFourBytes">
            /// Whether the layer information block is padded to a multiple of 4 bytes.
            /// </param>
            public static void ReadLayerInformation(PSDFile psd, Stream stream, bool roundToFourBytes = false)
            {
                long layerInfoLength = (psd.Version == 2)
                    ? stream.ReadBigEndianInt64()
                    : stream.ReadBigEndianInt32();

                if (layerInfoLength == 0)
                {
                    // no layers at all
                    psd.Layers = new PSDLayer[0];
                    return;
                }

                long layerInfoStart = stream.Position;

                short layerCount = stream.ReadBigEndianInt16();

                if (layerCount < 0)
                {
                    // FIXME: "If it is a negative number, its absolute value is the number of layers and the first alpha channel contains the transparency data for the merged result."
                    layerCount = (short)-layerCount;
                }

                psd.Layers = new PSDLayer[layerCount];
                for (int i = 0; i < layerCount; ++i)
                {
                    psd.Layers[i] = new PSDLayer();
                    PSDLayer.Reading.ReadLayerRecord(psd, psd.Layers[i], stream);
                }

                long totalDataLength = 0;

                for (int l = 0; l < layerCount; ++l)
                {
                    PSDLayer layer = psd.Layers[l];

                    for (int c = 0; c < layer.Channels.Length; ++c)
                    {
                        PSDLayerChannel channel = layer.Channels[c];

                        // get compression type
                        short compressionValue = stream.ReadBigEndianInt16();
                        if (!Enum.IsDefined(typeof(CompressionType), compressionValue))
                        {
                            throw new PSDFormatException($"unknown layer data compression type {compressionValue}");
                        }
                        var compression = (CompressionType)compressionValue;

                        channel.Data = new PSDLayerChannelDataPlaceholder
                        {
                            Compression = compression,
                            Offset      = stream.Position,
                            DataLength  = channel.DataLength - 2
                        };

                        long dataLengthWithCompressionInfo = channel.DataLength;
                        totalDataLength += dataLengthWithCompressionInfo;

                        // skip
                        stream.Seek(channel.Data.DataLength, SeekOrigin.Current);
                    }
                }

                // skip padding if any
                if (stream.Position < layerInfoStart + layerInfoLength)
                {
                    // padding; skip
                    int skipBytes = (int)((layerInfoStart + layerInfoLength) - stream.Position);
                    stream.ReadBytes(skipBytes);
                }

                // additionally round up to 4 bytes if requested (part of Layr/Lr16/Lr32)
                // WARNING: spec is a liar; says "rounded up to an even byte count"
                if (roundToFourBytes && layerInfoLength % 4 != 0)
                {
                    var skipCount = (int)(4 - (layerInfoLength % 4));
                    stream.ReadBytes(skipCount);
                }
            }