/// <summary> /// Decodes the descriptor-based brushes in version 6 and later. /// </summary> /// <param name="reader">The reader.</param> /// <param name="majorVersion">The major version.</param> /// <returns>An <see cref="AbrBrushCollection"/> containing the brushes.</returns> /// <exception cref="FormatException">The ABR version is not supported.</exception> private static AbrBrushCollection DecodeVersion6(BigEndianBinaryReader 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, Localization.Strings.AbrUnsupportedMinorVersionFormat, majorVersion, minorVersion)); } BrushSectionParser parser = new BrushSectionParser(reader); List <AbrBrush> brushes = new List <AbrBrush>(parser.SampledBrushes.Count); long sampleSectionOffset = parser.SampleSectionOffset; if (parser.SampledBrushes.Count > 0 && sampleSectionOffset >= 0) { reader.Position = sampleSectionOffset; uint sectionLength = reader.ReadUInt32(); long sectionEnd = reader.Position + sectionLength; while (reader.Position < sectionEnd) { uint brushLength = reader.ReadUInt32(); // The brush data is padded to 4 byte alignment. long paddedBrushLength = ((long)brushLength + 3) & ~3; long endOffset = reader.Position + paddedBrushLength; string tag = reader.ReadPascalString(); // Skip the unneeded data that comes before the Int32 bounds rectangle. reader.Position += unusedDataLength; Rectangle bounds = reader.ReadInt32Rectangle(); if (bounds.Width <= 0 || bounds.Height <= 0) { // Skip any brushes that have invalid dimensions. reader.Position += (endOffset - reader.Position); continue; } short depth = reader.ReadInt16(); if (depth != 8 && depth != 16) { // Skip any brushes with an unknown bit depth. reader.Position += (endOffset - reader.Position); continue; } SampledBrush sampledBrush = parser.SampledBrushes.FindLargestBrush(tag); if (sampledBrush != null) { BrushCompression compression = (BrushCompression)reader.ReadByte(); int height = bounds.Height; int width = bounds.Width; byte[] alphaData = null; if (compression == BrushCompression.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 if (compression == BrushCompression.None) { int alphaDataSize = width * height; if (depth == 16) { alphaDataSize *= 2; } alphaData = reader.ReadBytes(alphaDataSize); } else { // Skip any brushes with an unknown compression type. reader.Position += (endOffset - reader.Position); continue; } AbrBrush brush = CreateSampledBrush(width, height, depth, alphaData, sampledBrush.Name); 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.Image.Width; int originalHeight = brush.Image.Height; foreach (SampledBrush item in scaledBrushes.OrderByDescending(p => p.Diameter)) { Size size = Utils.ComputeBrushSize(originalWidth, originalHeight, item.Diameter); AbrBrush scaledBrush = new AbrBrush(size.Width, size.Height, item.Name); using (Graphics gr = Graphics.FromImage(scaledBrush.Image)) { gr.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; gr.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; gr.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; gr.DrawImage(brush.Image, 0, 0, size.Width, size.Height); } brushes.Add(scaledBrush); } } } long remaining = endOffset - reader.Position; // Skip any remaining bytes until the next sampled brush. if (remaining > 0) { reader.Position += remaining; } } } return(new AbrBrushCollection(brushes)); }
/// <summary> /// Decodes the version 1 and 2 brushes. /// </summary> /// <param name="reader">The reader.</param> /// <param name="majorVersion">The major version.</param> /// <returns>An <see cref="AbrBrushCollection"/> containing the brushes.</returns> private static AbrBrushCollection DecodeVersion1(BigEndianBinaryReader reader, short version) { short count = reader.ReadInt16(); List <AbrBrush> brushes = new List <AbrBrush>(count); for (int i = 0; i < count; i++) { BrushType type = (BrushType)reader.ReadInt16(); int size = reader.ReadInt32(); long endOffset = reader.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 == BrushType.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.Position += size; #endif } else if (type == BrushType.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.Position += 8L; Rectangle bounds = reader.ReadInt32Rectangle(); if (bounds.Width <= 0 || bounds.Height <= 0) { // Skip any brushes that have invalid dimensions. reader.Position += (endOffset - reader.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.Position += (endOffset - reader.Position); continue; } int height = bounds.Height; int width = bounds.Width; int rowsRemaining = height; int rowsRead = 0; byte[] alphaData = new byte[width * height]; bool unknownCompressionType = false; 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. BrushCompression compression = (BrushCompression)reader.ReadByte(); if (compression == BrushCompression.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 if (compression == BrushCompression.None) { int numBytesToRead = chunkHeight * width; int numBytesRead = rowsRead * width; while (numBytesToRead > 0) { // Read may return anything from 0 to numBytesToRead. int n = reader.Read(alphaData, numBytesRead, numBytesToRead); // The end of the file is reached. if (n == 0) { break; } numBytesRead += n; numBytesToRead -= n; } } else { // The format specs state that the brush data can be either uncompressed or RLE compressed with PackBits. // Ignore any brushes with an unknown compression type. unknownCompressionType = true; break; } rowsRead += 16384; rowsRemaining -= 16384; } while (rowsRemaining > 0); if (unknownCompressionType) { reader.Position += (endOffset - reader.Position); continue; } else { brushes.Add(CreateSampledBrush(width, height, depth, alphaData, name)); } } else { // Skip any unknown brush types. reader.Position += size; } } return(new AbrBrushCollection(brushes)); }