/// <summary> /// Reads an item of additional layer information within the additional layer information subsection of a /// PSD layer and mask information section from a stream and either returns a /// <see cref="PSDAdditionalLayerInformation"/> object containing the additional information, or (if this /// is an "out-of-band" layer information block) populates a <see cref="PSDFile"/> with the layer /// information within. /// </summary> /// <param name="psd"> /// The PSD file object to populate if this is an "out-of-band" layer information block. /// </param> /// <param name="stream">The stream from which to read the layer information subsection.</param> /// <returns> /// The additional layer information, or <c>null</c> if the encountered block is an "out-of-band" layer /// information block. /// </returns> public static PSDAdditionalLayerInformation ReadAdditionalLayerInformation(PSDFile psd, Stream stream) { var ali = new PSDAdditionalLayerInformation(); string signature = stream.ReadUsAsciiString(4); if (signature != AdditionalLayerInfoSignature && signature != AdditionalLayerInfoSignature2) { throw new PSDFormatException($"unknown additional layer information signature \"{signature}\", expected \"{AdditionalLayerInfoSignature}\" or \"{AdditionalLayerInfoSignature2}\""); } string key = stream.ReadUsAsciiString(4); bool length64Bit = false; if (key == "Layr" || key == "Lr16" || key == "Lr32") { // layer info has been shunted in here instead of where it belongs ReadLayerInformation(psd, stream, roundToFourBytes: true); return(null); } if (psd.Version == 2) { // some keys have 64-bit lengths length64Bit = (key == "LMsk") || (key == "Mt16") || (key == "Mt32") || (key == "Mtrn") || (key == "Alph") || (key == "FMsk") || (key == "Ink2") || (key == "FEid") || (key == "FXid") || (key == "PxSD") ; } long length = length64Bit ? stream.ReadBigEndianInt64() : stream.ReadBigEndianInt32(); byte[] data = stream.ReadBytes((int)length); ali.Key = key; ali.Data = data; // round up to 4 bytes // WARNING: spec is a liar; says "rounded up to an even byte count" if (length % 4 != 0) { var skipCount = (int)(4 - (length % 4)); stream.ReadBytes(skipCount); } return(ali); }
/// <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); } }