///////////////////////////////////////////////////////////////////////////////// // // Photoshop PSD FileType Plugin for Paint.NET // http://psdplugin.codeplex.com/ // // This software is provided under the MIT License: // Copyright (c) 2006-2007 Frank Blumenberg // Copyright (c) 2010-2014 Tao Yue // // Portions of this file are provided under the BSD 3-clause License: // Copyright (c) 2006, Jonas Beckeman // // See LICENSE.txt for complete licensing and attribution information. // ///////////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Specialized; using System.Diagnostics; using PDNWrapper; using System.Globalization; using Unity.Collections; namespace PhotoshopFile { internal class Mask { /// /// The layer to which this mask belongs /// public Layer Layer { get; private set; } /// /// The rectangle enclosing the mask. /// public Rectangle Rect { get; set; } private byte backgroundColor; public byte BackgroundColor { get { return backgroundColor; } set { if ((value != 0) && (value != 255)) throw new PsdInvalidException("Mask background must be fully-opaque or fully-transparent."); backgroundColor = value; } } private static int positionVsLayerBit = BitVector32.CreateMask(); private static int disabledBit = BitVector32.CreateMask(positionVsLayerBit); private static int invertOnBlendBit = BitVector32.CreateMask(disabledBit); private BitVector32 flags; public BitVector32 Flags { get { return flags; } } /// /// If true, the position of the mask is relative to the layer. /// public bool PositionVsLayer { get { return flags[positionVsLayerBit]; } set { flags[positionVsLayerBit] = value; } } public bool Disabled { get { return flags[disabledBit]; } set { flags[disabledBit] = value; } } /// /// if true, invert the mask when blending. /// public bool InvertOnBlend { get { return flags[invertOnBlendBit]; } set { flags[invertOnBlendBit] = value; } } /// /// Mask image data. /// public NativeArray ImageData { get; set; } public Mask(Layer layer) { Layer = layer; this.flags = new BitVector32(); } public Mask(Layer layer, Rectangle rect, byte color, BitVector32 flags) { Layer = layer; Rect = rect; BackgroundColor = color; this.flags = flags; } } /// /// Mask info for a layer. Contains both the layer and user masks. /// internal class MaskInfo { private static int s_UserMaskDensityBit = BitVector32.CreateMask(); private static int s_UserMaskFeatherBit = BitVector32.CreateMask(s_UserMaskDensityBit); private static int s_VectorMaskDensityBit = BitVector32.CreateMask(s_UserMaskFeatherBit); private static int s_VectorMaskFeatherBit = BitVector32.CreateMask(s_VectorMaskDensityBit); public Mask LayerMask { get; set; } public Mask UserMask { get; set; } /// /// Construct MaskInfo with null masks. /// public MaskInfo() { } public MaskInfo(PsdBinaryReader reader, Layer layer) { Util.DebugMessage(reader.BaseStream, "Load, Begin, MaskInfo"); var maskLength = reader.ReadUInt32(); if (maskLength <= 0) return; var startPosition = reader.BaseStream.Position; var endPosition = startPosition + maskLength; // Read layer mask var rectangle = reader.ReadRectangle(); var backgroundColor = reader.ReadByte(); var flagsByte = reader.ReadByte(); LayerMask = new Mask(layer, rectangle, backgroundColor, new BitVector32(flagsByte)); // User mask is supplied separately when there is also a vector mask. if (maskLength == 36) { var userFlagsByte = reader.ReadByte(); var userBackgroundColor = reader.ReadByte(); var userRectangle = reader.ReadRectangle(); UserMask = new Mask(layer, userRectangle, userBackgroundColor, new BitVector32(userFlagsByte)); } else { // Only check if bit 4 is set. Testing shows there are discrepancy in file format documentation. if (flagsByte == 16) { // Not using them so just read and discard the values var maskParameters = new BitVector32(reader.ReadByte()); if (maskParameters[s_UserMaskDensityBit]) reader.ReadByte(); if (maskParameters[s_UserMaskFeatherBit]) reader.ReadDouble(); if (maskParameters[s_VectorMaskDensityBit]) reader.ReadByte(); if (maskParameters[s_VectorMaskFeatherBit]) reader.ReadDouble(); } // The rest should be vector mask if (reader.BaseStream.Position + 18 <= endPosition) { var userFlagsByte = reader.ReadByte(); var userBackgroundColor = reader.ReadByte(); var userRectangle = reader.ReadRectangle(); UserMask = new Mask(layer, userRectangle, userBackgroundColor, new BitVector32(userFlagsByte)); } } // 20-byte mask data will end with padding. reader.BaseStream.Position = endPosition; Util.DebugMessage(reader.BaseStream, "Load, End, MaskInfo"); } } }