示例#1
0
            /// <summary>
            /// Reads the blending ranges of a layer from a stream and store them in a PSD layer object.
            /// </summary>
            /// <param name="layer">The layer whose mask is being read.</param>
            /// <param name="stream">The stream from which to read the layer mask.</param>
            public static void ReadLayerBlendingRanges(PSDLayer layer, Stream stream)
            {
                int length = stream.ReadBigEndianInt32();

                if (length % 8 != 0)
                {
                    throw new PSDFormatException($"expected a layer blending ranges block length divisible by 8, got {length} ({length % 8} remainder)");
                }

                int count          = length / 8;
                var blendingRanges = new PSDLayerBlendingRange[count];

                for (int i = 0; i < count; ++i)
                {
                    blendingRanges[i].SourceLowFirst        = stream.ReadByteOrThrow();
                    blendingRanges[i].SourceLowSecond       = stream.ReadByteOrThrow();
                    blendingRanges[i].SourceHighFirst       = stream.ReadByteOrThrow();
                    blendingRanges[i].SourceHighSecond      = stream.ReadByteOrThrow();
                    blendingRanges[i].DestinationLowFirst   = stream.ReadByteOrThrow();
                    blendingRanges[i].DestinationLowSecond  = stream.ReadByteOrThrow();
                    blendingRanges[i].DestinationHighFirst  = stream.ReadByteOrThrow();
                    blendingRanges[i].DestinationHighSecond = stream.ReadByteOrThrow();
                }
                layer.BlendingRanges = blendingRanges;
            }
示例#2
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);
                }
            }
示例#3
0
            /// <summary>
            /// Reads information of a PSD layer's mask from a stream.
            /// </summary>
            /// <param name="layer">The layer whose mask is being read.</param>
            /// <param name="stream">The stream from which to read the layer mask.</param>
            public static void ReadLayerMask(PSDLayer layer, Stream stream)
            {
                int maskSize = stream.ReadBigEndianInt32();

                if (maskSize == 0)
                {
                    layer.LayerMask = null;
                    return;
                }

                var mask = new PSDLayerMask();

                mask.Top    = stream.ReadBigEndianInt32();
                mask.Left   = stream.ReadBigEndianInt32();
                mask.Bottom = stream.ReadBigEndianInt32();
                mask.Right  = stream.ReadBigEndianInt32();

                byte defaultColor = stream.ReadByteOrThrow();

                if (defaultColor != 0 && defaultColor != 255)
                {
                    throw new PSDFormatException($"expected layer mask default color to be 0 or 255, got {defaultColor}");
                }
                mask.DefaultColor = defaultColor;

                byte flags = stream.ReadByteOrThrow();

                if ((flags & 0x01) == 0x01)
                {
                    mask.PositionIsRelativeToLayerData = true;
                }
                if ((flags & 0x02) == 0x02)
                {
                    mask.Disabled = true;
                }
                if ((flags & 0x04) == 0x04)
                {
                    mask.InvertMaskWhenBlending = true;
                }
                if ((flags & 0x08) == 0x08)
                {
                    mask.OriginatesFromRenderingOtherData = true;
                }
                bool hasParameters = ((flags & 0x10) == 0x10);

                if (maskSize == 20 && !hasParameters)
                {
                    // skip two bytes of padding and we're done
                    stream.ReadBytes(2);
                    return;
                }

                // rectangle + default color + flags + real flags + real BG + real rectangle
                int actualMaskSize = 4 * 4 + 1 + 1 + 1 + 1 + 4 * 4;

                if (hasParameters)
                {
                    actualMaskSize += 1;

                    // read parameters
                    byte availableParameters = stream.ReadByteOrThrow();
                    if ((availableParameters & 0x01) == 0x01)
                    {
                        actualMaskSize      += 1;
                        mask.UserMaskDensity = stream.ReadByteOrThrow();
                    }
                    if ((availableParameters & 0x02) == 0x02)
                    {
                        actualMaskSize      += 8;
                        mask.UserMaskFeather = stream.ReadBigEndianDouble();
                    }
                    if ((availableParameters & 0x04) == 0x04)
                    {
                        actualMaskSize        += 1;
                        mask.VectorMaskDensity = stream.ReadByteOrThrow();
                    }
                    if ((availableParameters & 0x08) == 0x08)
                    {
                        actualMaskSize        += 8;
                        mask.VectorMaskFeather = stream.ReadBigEndianDouble();
                    }
                }

                if (actualMaskSize != maskSize)
                {
                    throw new PSDFormatException(
                              $"actual calculated layer mask size ({actualMaskSize}) does not match mask size in header ({maskSize})"
                              );
                }

                // yay, duplicated data...
                // FIXME: let's just skip it
                stream.ReadBytes(18);
            }
示例#4
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);
                }
            }