/// <summary> /// Creates a brush with the given dimensions given bit depth, alpha /// data, and name. /// </summary> /// <param name="width">Desired width.</param> /// <param name="height">Desired height.</param> /// <param name="bitDepth">Bit depth of the data.</param> /// <param name="alphaData"> /// The pixel data, stored only as an alpha channel and flattened to a /// 1-d array. /// </param> /// <param name="name">Desired brush name.</param> /// <returns> /// An AbrBrush with a bitmap constructed from the alpha data. /// </returns> private static unsafe AbrBrush CreateSampledBrush( int width, int height, int bitDepth, byte[] alphaData, string name) { AbrBrush brush = new AbrBrush(width, height, name); BitmapData bd = brush.Image.LockBits( new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppPArgb); try { fixed(byte *ptr = alphaData) { byte *scan0 = (byte *)bd.Scan0.ToPointer(); int stride = bd.Stride; if (bitDepth == 16) { int srcStride = width * 2; for (int y = 0; y < height; y++) { byte *src = ptr + (y * srcStride); byte *dst = scan0 + (y * stride); for (int x = 0; x < width; x++) { ushort val = (ushort)((src[0] << 8) | src[1]); dst[0] = dst[1] = dst[2] = 0; dst[3] = (byte)((val * 10) / 1285); src += 2; dst += 4; } } } else { for (int y = 0; y < height; y++) { byte *src = ptr + (y * width); byte *dst = scan0 + (y * stride); for (int x = 0; x < width; x++) { dst[0] = dst[1] = dst[2] = 0; dst[3] = *src; src++; dst += 4; } } } } } finally { brush.Image.UnlockBits(bd); } return(brush); }
/// <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)); }