private void ParseBrushPreset(BinaryReverseReader reader, uint count)
        {
            string    presetName = null;
            BrushData brushData  = null;

            for (uint i = 0; i < count; i++)
            {
                string          key  = ParseKey(reader);
                DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

#if DEBUG
                System.Diagnostics.Debug.WriteLine(string.Format(
                                                       CultureInfo.CurrentCulture,
                                                       "brushPreset item {0}: {1} ({2}) at 0x{3:X8}",
                                                       new object[] { i, key, type, reader.BaseStream.Position }));
#endif
                if (key.Equals("Nm  ", StringComparison.Ordinal))
                {
                    presetName = ParseString(reader);
                }
                else if (key.Equals("Brsh", StringComparison.Ordinal) && type == DescriptorTypes.Descriptor)
                {
                    brushData = ParseBrushDescriptor(reader);
                }
                else
                {
                    ParseType(reader, type);
                }
            }

            if (brushData != null && !string.IsNullOrEmpty(brushData.sampledDataTag))
            {
                this.sampledBrushes.Add(new SampledBrush(presetName, brushData.sampledDataTag, brushData.diameter, brushData.spacing));
            }
        }
        private void ParseDescriptor(BinaryReverseReader reader)
        {
            string name = ParseString(reader);

            string classId = ParseClassId(reader);

            uint count = reader.ReadUInt32();

#if DEBUG
            System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Parsing descriptor '{0}' ({1} items)", classId, count));
#endif
            if (classId.Equals("brushPreset", StringComparison.Ordinal))
            {
                ParseBrushPreset(reader, count);
            }
            else
            {
                for (uint i = 0; i < count; i++)
                {
                    string          key  = ParseKey(reader);
                    DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

#if DEBUG
                    System.Diagnostics.Debug.WriteLine(string.Format(
                                                           CultureInfo.CurrentCulture,
                                                           "Item {0}: {1} ({2}) at 0x{3:X8}",
                                                           new object[] { i, key, type, reader.BaseStream.Position }));
#endif
                    ParseType(reader, type);
                }
            }
        }
        private static EnumeratedValue ParseEnumerated(BinaryReverseReader reader)
        {
            EnumeratedValue value = new EnumeratedValue()
            {
                type  = ParseKey(reader),
                value = ParseKey(reader)
            };

            return(value);
        }
        private static UnitFloat ParseUnitFloat(BinaryReverseReader reader)
        {
            UnitFloat value = new UnitFloat()
            {
                type  = (UnitTypes)reader.ReadUInt32(),
                value = reader.ReadDouble()
            };

            return(value);
        }
        private void ParseList(BinaryReverseReader reader)
        {
            uint count = reader.ReadUInt32();

            for (int i = 0; i < count; i++)
            {
                DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

                ParseType(reader, type);
            }
        }
        private static string ParseClassId(BinaryReverseReader reader)
        {
            int length = reader.ReadInt32();

            if (length == 0)
            {
                length = 4;
            }

            return(Encoding.ASCII.GetString(reader.ReadBytes(length)).TrimEnd('\0'));
        }
        private const uint DescriptorSectionId = 0x64657363; // desc

        /// <summary>
        /// Initializes a new instance of the <see cref="BrushSectionParser"/> class.
        /// </summary>
        /// <param name="reader">The reader.</param>
        public BrushSectionParser(BinaryReverseReader reader)
        {
            this.sectionOffsets = GetBrushSectionOffsets(reader);
            this.sampledBrushes = new SampledBrushCollection();

            if (this.sectionOffsets != null && this.sectionOffsets.descriptorSectionOffset >= 0)
            {
                reader.BaseStream.Position = this.sectionOffsets.descriptorSectionOffset;
                ParseBrushDescriptorSection(reader);
            }
        }
        private static string ParseKey(BinaryReverseReader reader)
        {
            int length = reader.ReadInt32();

            if (length == 0)
            {
                length = 4;
            }

            byte[] bytes = reader.ReadBytes(length);

            return(Encoding.ASCII.GetString(bytes));
        }
        private BrushData ParseSampledBrush(BinaryReverseReader reader, uint count)
        {
            BrushData data = new BrushData();

            for (uint i = 0; i < count; i++)
            {
                string key = ParseKey(reader);

                DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

#if DEBUG
                System.Diagnostics.Debug.WriteLine(string.Format(
                                                       CultureInfo.CurrentCulture,
                                                       "sampledBrush item {0}: {1} ({2}) at 0x{3:X8}",
                                                       new object[] { i, key, type, reader.BaseStream.Position }));
#endif
                UnitFloat unitFloat;
                switch (key)
                {
                case "Dmtr":
                    unitFloat = ParseUnitFloat(reader);
                    if (unitFloat.type == UnitTypes.Pixel)
                    {
                        data.diameter = (int)unitFloat.value;
                    }
                    break;

                case "Spcn":
                    unitFloat = ParseUnitFloat(reader);
                    if (unitFloat.type == UnitTypes.Percent)
                    {
                        data.spacing = (int)unitFloat.value;
                    }
                    break;

                case "sampledData":
                    data.sampledDataTag = ParseString(reader);
                    break;

                default:
                    ParseType(reader, type);
                    break;
                }
            }

            return(data);
        }
        private void ParseType(BinaryReverseReader reader, DescriptorTypes type)
        {
            switch (type)
            {
            case DescriptorTypes.List:
                ParseList(reader);
                break;

            case DescriptorTypes.Descriptor:
                ParseDescriptor(reader);
                break;

            case DescriptorTypes.String:
                ParseString(reader);
                break;

            case DescriptorTypes.UnitFloat:
                ParseUnitFloat(reader);
                break;

            case DescriptorTypes.Boolean:
                ParseBoolean(reader);
                break;

            case DescriptorTypes.Integer:
                ParseInteger(reader);
                break;

            case DescriptorTypes.Float:
                ParseFloat(reader);
                break;

            case DescriptorTypes.Enumerated:
                ParseEnumerated(reader);
                break;

            default:
                throw new FormatException(string.Format(CultureInfo.CurrentCulture, "Unsupported brush descriptor type: '{0}'", DescriptorTypeToString(type)));
            }
        }
Example #11
0
        ////////////////////////////////////////////////////////////////////////

        public static void DecodedRow(BinaryReverseReader reader, byte[] imgData, int startIdx, int columns)
        {
            int count = 0;

            while (count < columns)
            {
                byte byteValue = reader.ReadByte();

                int len = byteValue;
                if (len < 128)
                {
                    len++;
                    while (len != 0 && (startIdx + count) < imgData.Length)
                    {
                        byteValue = reader.ReadByte();

                        imgData[startIdx + count] = byteValue;
                        count++;
                        len--;
                    }
                }
                else if (len > 128)
                {
                    // Next -len+1 bytes in the dest are replicated from next source byte.
                    // (Interpret len as a negative 8-bit int.)
                    len ^= 0x0FF;
                    len += 2;

                    byteValue = reader.ReadByte();

                    while (len != 0 && (startIdx + count) < imgData.Length)
                    {
                        imgData[startIdx + count] = byteValue;
                        count++;
                        len--;
                    }
                }
            }
        }
        private BrushData ParseBrushDescriptor(BinaryReverseReader reader)
        {
            string name = ParseString(reader);

            string classId = ParseClassId(reader);

            uint count = reader.ReadUInt32();

#if DEBUG
            System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Parsing {0} ({1} items)", classId, count));
#endif

            BrushData data = null;

            if (classId.Equals("sampledBrush", StringComparison.Ordinal))
            {
                data = ParseSampledBrush(reader, count);
            }
            else
            {
                for (uint i = 0; i < count; i++)
                {
                    string          key  = ParseKey(reader);
                    DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

#if DEBUG
                    System.Diagnostics.Debug.WriteLine(string.Format(
                                                           CultureInfo.CurrentCulture,
                                                           "{0} item {1}: {2} ({3}) at 0x{4:X8}",
                                                           new object[] { classId, i, key, type, reader.BaseStream.Position }));
#endif
                    ParseType(reader, type);
                }
            }

            return(data);
        }
        private static BrushSectionOffsets GetBrushSectionOffsets(BinaryReverseReader reader)
        {
            long sampleSectionOffset     = -1;
            long descriptorSectionOffset = -1;

            while (reader.BaseStream.Position < reader.BaseStream.Length)
            {
                uint sig = reader.ReadUInt32();

                if (sig != PhotoshopSignature)
                {
                    return(null);
                }

                uint sectionId = reader.ReadUInt32();

                switch (sectionId)
                {
                case SampleSectionId:
                    sampleSectionOffset = reader.BaseStream.Position;
                    break;

                case DescriptorSectionId:
                    descriptorSectionOffset = reader.BaseStream.Position;
                    break;

                default:
                    break;
                }

                uint size = reader.ReadUInt32();

                reader.BaseStream.Position += size;
            }

            return(new BrushSectionOffsets(sampleSectionOffset, descriptorSectionOffset));
        }
        private void ParseBrushDescriptorSection(BinaryReverseReader reader)
        {
            uint sectionSize = reader.ReadUInt32();

            long sectionEnd = reader.BaseStream.Position + sectionSize;

            // Skip the unknown data.
            reader.BaseStream.Position += 22L;

            if (reader.BaseStream.Position < sectionEnd)
            {
                string          key  = ParseKey(reader);
                DescriptorTypes type = (DescriptorTypes)reader.ReadUInt32();

#if DEBUG
                System.Diagnostics.Debug.WriteLine(string.Format(
                                                       CultureInfo.CurrentCulture,
                                                       "Item: {0} ({1}) at {2:X8}",
                                                       new object[] { key, type, reader.BaseStream.Position }));
#endif

                ParseType(reader, type);
            }
        }
Example #15
0
        public static Document Load(Stream stream)
        {
            using (BinaryReverseReader reader = new BinaryReverseReader(stream))
            {
                ReadOnlyCollection <Brush> brushes;
                short version = reader.ReadInt16();

                switch (version)
                {
                case 1:
                case 2:
                    brushes = DecodeVersion1(reader, version);
                    break;

                case 6:
                case 7:                         // Used by Photoshop CS and later for brushes containing 16-bit data.
                case 10:                        // Used by Photoshop CS6 and/or CC?
                    brushes = DecodeVersion6(reader, version);
                    break;

                default:
                    throw new FormatException(string.Format(CultureInfo.CurrentCulture, Resources.UnsupportedABRVersion, version));
                }

                if (brushes.Count == 0)
                {
                    throw new FormatException(Resources.NoSampledBrushes);
                }

                int maxWidth  = 0;
                int maxHeight = 0;
                foreach (var item in brushes)
                {
                    if (item.Surface.Width > maxWidth)
                    {
                        maxWidth = item.Surface.Width;
                    }

                    if (item.Surface.Height > maxHeight)
                    {
                        maxHeight = item.Surface.Height;
                    }
                }

                Document doc     = null;
                Document tempDoc = null;

                try
                {
                    tempDoc = new Document(maxWidth, maxHeight);

                    for (int i = 0; i < brushes.Count; i++)
                    {
                        Brush abr = brushes[i];

                        BitmapLayer layer     = null;
                        BitmapLayer tempLayer = null;
                        try
                        {
                            tempLayer = new BitmapLayer(maxWidth, maxHeight);
                            tempLayer.IsBackground = i == 0;
                            tempLayer.Name         = !string.IsNullOrEmpty(abr.Name) ? abr.Name : string.Format(CultureInfo.CurrentCulture, Resources.BrushNameFormat, i);
                            tempLayer.Metadata.SetUserValue(AbrMetadataNames.BrushSpacing, abr.Spacing.ToString(CultureInfo.InvariantCulture));

                            tempLayer.Surface.CopySurface(abr.Surface);
                            layer     = tempLayer;
                            tempLayer = null;
                        }
                        finally
                        {
                            if (tempLayer != null)
                            {
                                tempLayer.Dispose();
                                tempLayer = null;
                            }
                            abr.Dispose();
                        }

                        tempDoc.Layers.Add(layer);
                    }
                    doc     = tempDoc;
                    tempDoc = null;
                }
                finally
                {
                    if (tempDoc != null)
                    {
                        tempDoc.Dispose();
                        tempDoc = null;
                    }
                }

                return(doc);
            }
        }
Example #16
0
        private static ReadOnlyCollection <Brush> DecodeVersion6(BinaryReverseReader reader, short majorVersion)
        {
            short minorVersion = reader.ReadInt16();
            long  unusedDataLength;

            switch (minorVersion)
            {
            case 1:
                // Skip the Int16 bounds rectangle and the unknown Int16.
                unusedDataLength = 10L;
                break;

            case 2:
                // Skip the unknown bytes.
                unusedDataLength = 264L;
                break;

            default:
                throw new FormatException(string.Format(CultureInfo.CurrentCulture, Resources.UnsupportedABRSubVersion, majorVersion, minorVersion));
            }

            BrushSectionParser parser = new BrushSectionParser(reader);

            List <Brush> brushes = new List <Brush>(parser.SampledBrushes.Count);

            long sampleSectionOffset = parser.SampleSectionOffset;

            if (parser.SampledBrushes.Count > 0 && sampleSectionOffset >= 0)
            {
                reader.BaseStream.Position = sampleSectionOffset;

                uint sectionLength = reader.ReadUInt32();

                long sectionEnd = reader.BaseStream.Position + sectionLength;

                while (reader.BaseStream.Position < sectionEnd)
                {
                    uint brushLength = reader.ReadUInt32();

                    // The brush data is padded to 4 byte alignment.
                    long paddedBrushLength = ((long)brushLength + 3) & ~3;

                    long endOffset = reader.BaseStream.Position + paddedBrushLength;

                    string tag = reader.ReadPascalString();

                    // Skip the unneeded data that comes before the Int32 bounds rectangle.
                    reader.BaseStream.Position += unusedDataLength;

                    Rectangle bounds = reader.ReadInt32Rectangle();
                    if (bounds.Width <= 0 || bounds.Height <= 0)
                    {
                        // Skip any brushes that have invalid dimensions.
                        reader.BaseStream.Position += (endOffset - reader.BaseStream.Position);
                        continue;
                    }

                    short depth = reader.ReadInt16();
                    if (depth != 8 && depth != 16)
                    {
                        // Skip any brushes with an unknown bit depth.
                        reader.BaseStream.Position += (endOffset - reader.BaseStream.Position);
                        continue;
                    }

                    SampledBrush sampledBrush = parser.SampledBrushes.FindBrush(tag);
                    if (sampledBrush != null)
                    {
                        AbrImageCompression compression = (AbrImageCompression)reader.ReadByte();

                        int height = bounds.Height;
                        int width  = bounds.Width;

                        byte[] alphaData = null;

                        if (compression == AbrImageCompression.RLE)
                        {
                            short[] compressedRowLengths = new short[height];

                            for (int y = 0; y < height; y++)
                            {
                                compressedRowLengths[y] = reader.ReadInt16();
                            }

                            int alphaDataSize = width * height;
                            int bytesPerRow   = width;

                            if (depth == 16)
                            {
                                alphaDataSize *= 2;
                                bytesPerRow   *= 2;
                            }

                            alphaData = new byte[alphaDataSize];

                            for (int y = 0; y < height; y++)
                            {
                                RLEHelper.DecodedRow(reader, alphaData, y * width, bytesPerRow);
                            }
                        }
                        else
                        {
                            int alphaDataSize = width * height;

                            if (depth == 16)
                            {
                                alphaDataSize *= 2;
                            }

                            alphaData = reader.ReadBytes(alphaDataSize);
                        }

                        var brush = CreateSampledBrush(width, height, depth, alphaData, sampledBrush.Name, sampledBrush.Spacing);

                        brushes.Add(brush);

                        // Some brushes only store the largest item and scale it down.
                        var scaledBrushes = parser.SampledBrushes.Where(i => i.Tag.Equals(tag, StringComparison.Ordinal) && i.Diameter < sampledBrush.Diameter);
                        if (scaledBrushes.Any())
                        {
                            int originalWidth  = brush.Surface.Width;
                            int originalHeight = brush.Surface.Height;

                            foreach (var item in scaledBrushes.OrderByDescending(p => p.Diameter))
                            {
                                Size size = ComputeBrushSize(originalWidth, originalHeight, item.Diameter);

                                Brush scaledBrush = new Brush(size.Width, size.Height, item.Name, item.Spacing);
                                scaledBrush.Surface.FitSurface(ResamplingAlgorithm.SuperSampling, brush.Surface);

                                brushes.Add(scaledBrush);
                            }
                        }
                    }

                    long remaining = endOffset - reader.BaseStream.Position;
                    // Skip any remaining bytes until the next sampled brush.
                    if (remaining > 0)
                    {
                        reader.BaseStream.Position += remaining;
                    }
                }
            }

            return(brushes.AsReadOnly());
        }
Example #17
0
        private static ReadOnlyCollection <Brush> DecodeVersion1(BinaryReverseReader reader, short version)
        {
            short count = reader.ReadInt16();

            List <Brush> brushes = new List <Brush>(count);

            for (int i = 0; i < count; i++)
            {
                AbrBrushType type = (AbrBrushType)reader.ReadInt16();
                int          size = reader.ReadInt32();

                long endOffset = reader.BaseStream.Position + size;

#if DEBUG
                System.Diagnostics.Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Brush: {0}, type: {1}, size: {2} bytes", i, type, size));
#endif
                if (type == AbrBrushType.Computed)
                {
#if DEBUG
                    int   misc    = reader.ReadInt32();
                    short spacing = reader.ReadInt16();

                    string name = string.Empty;
                    if (version == 2)
                    {
                        name = reader.ReadUnicodeString();
                    }

                    short diameter  = reader.ReadInt16();
                    short roundness = reader.ReadInt16();
                    short angle     = reader.ReadInt16();
                    short hardness  = reader.ReadInt16();
#else
                    reader.BaseStream.Position += size;
#endif
                }
                else if (type == AbrBrushType.Sampled)
                {
                    int   misc    = reader.ReadInt32();
                    short spacing = reader.ReadInt16();

                    string name = string.Empty;
                    if (version == 2)
                    {
                        name = reader.ReadUnicodeString();
                    }

                    bool antiAlias = reader.ReadByte() != 0;

                    // Skip the Int16 bounds.
                    reader.BaseStream.Position += 8L;

                    Rectangle bounds = reader.ReadInt32Rectangle();
                    if (bounds.Width <= 0 || bounds.Height <= 0)
                    {
                        // Skip any brushes that have invalid dimensions.
                        reader.BaseStream.Position += (endOffset - reader.BaseStream.Position);
                        continue;
                    }

                    short depth = reader.ReadInt16();

                    if (depth != 8)
                    {
                        // The format specs state that brushes must be 8-bit, skip any that are not.
                        reader.BaseStream.Position += (endOffset - reader.BaseStream.Position);
                        continue;
                    }
                    int height = bounds.Height;
                    int width  = bounds.Width;

                    int rowsRemaining = height;
                    int rowsRead      = 0;

                    byte[] alphaData = new byte[width * height];
                    do
                    {
                        // Sampled brush data is broken into repeating chunks for brushes taller that 16384 pixels.
                        int chunkHeight = Math.Min(rowsRemaining, 16384);
                        // The format specs state that compression is stored as a 2-byte field, but it is written as a 1-byte field in actual files.
                        AbrImageCompression compression = (AbrImageCompression)reader.ReadByte();

                        if (compression == AbrImageCompression.RLE)
                        {
                            short[] compressedRowLengths = new short[height];

                            for (int y = 0; y < height; y++)
                            {
                                compressedRowLengths[y] = reader.ReadInt16();
                            }

                            for (int y = 0; y < chunkHeight; y++)
                            {
                                int row = rowsRead + y;
                                RLEHelper.DecodedRow(reader, alphaData, row * width, width);
                            }
                        }
                        else
                        {
                            int rowByteOffset  = rowsRead * width;
                            int numBytesToRead = chunkHeight * width;
                            int numBytesRead   = 0;
                            while (numBytesToRead > 0)
                            {
                                // Read may return anything from 0 to numBytesToRead.
                                int n = reader.Read(alphaData, rowByteOffset + numBytesRead, numBytesToRead);
                                // The end of the file is reached.
                                if (n == 0)
                                {
                                    break;
                                }
                                numBytesRead   += n;
                                numBytesToRead -= n;
                            }
                        }

                        rowsRemaining -= 16384;
                        rowsRead      += 16384;
                    } while (rowsRemaining > 0);

                    brushes.Add(CreateSampledBrush(width, height, depth, alphaData, name, spacing));
                }
                else
                {
                    // Skip any unknown brush types.
                    reader.BaseStream.Position += size;
                }
            }

            return(brushes.AsReadOnly());
        }
        private static double ParseFloat(BinaryReverseReader reader)
        {
            double value = reader.ReadDouble();

            return(value);
        }
        private static uint ParseInteger(BinaryReverseReader reader)
        {
            uint value = reader.ReadUInt32();

            return(value);
        }
        private static bool ParseBoolean(BinaryReverseReader reader)
        {
            bool value = reader.ReadByte() != 0;

            return(value);
        }
 private static string ParseString(BinaryReverseReader reader)
 {
     return(reader.ReadUnicodeString());
 }