From eb2d32ff94d139f9b4396821461dad9bdefb9b1d Mon Sep 17 00:00:00 2001 From: dirk Date: Sun, 30 Oct 2016 11:34:39 +0100 Subject: [PATCH] Used the new pixel code to optimize the BMP decoder. --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 293 ++++++++----------- 1 file changed, 117 insertions(+), 176 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 0e8975d8a..e06e3aef6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -7,7 +7,6 @@ namespace ImageSharp.Formats { using System; using System.IO; - using System.Threading.Tasks; /// /// Performs the bmp decoding operation. @@ -121,39 +120,40 @@ namespace ImageSharp.Formats + $"bigger then the max allowed size '{image.MaxWidth}x{image.MaxHeight}'"); } - TColor[] imageData = new TColor[this.infoHeader.Width * this.infoHeader.Height]; + image.InitPixels(this.infoHeader.Width, this.infoHeader.Height); - switch (this.infoHeader.Compression) + using (PixelAccessor pixels = image.Lock()) { - case BmpCompression.RGB: - if (this.infoHeader.HeaderSize != 40) - { - throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); - } - - if (this.infoHeader.BitsPerPixel == 32) - { - this.ReadRgb32(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel == 24) - { - this.ReadRgb24(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel == 16) - { - this.ReadRgb16(imageData, this.infoHeader.Width, this.infoHeader.Height, inverted); - } - else if (this.infoHeader.BitsPerPixel <= 8) - { - this.ReadRgbPalette(imageData, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted); - } - - break; - default: - throw new NotSupportedException("Does not support this kind of bitmap files."); - } + switch (this.infoHeader.Compression) + { + case BmpCompression.RGB: + if (this.infoHeader.HeaderSize != 40) + { + throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); + } - image.SetPixels(this.infoHeader.Width, this.infoHeader.Height, imageData); + if (this.infoHeader.BitsPerPixel == 32) + { + this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + } + else if (this.infoHeader.BitsPerPixel == 24) + { + this.ReadRgb24(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + } + else if (this.infoHeader.BitsPerPixel == 16) + { + this.ReadRgb16(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); + } + else if (this.infoHeader.BitsPerPixel <= 8) + { + this.ReadRgbPalette(pixels, palette, this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, inverted); + } + + break; + default: + throw new NotSupportedException("Does not support this kind of bitmap files."); + } + } } catch (IndexOutOfRangeException e) { @@ -184,18 +184,30 @@ namespace ImageSharp.Formats return row; } + private static int CalculatPadding(int width, int componentCount) + { + int padding = (width * componentCount) % 4; + + if (padding != 0) + { + padding = 4 - padding; + } + + return padding; + } + /// /// Reads the color palette from the stream. /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The to assign the palette to. /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. /// The number of bits per pixel. /// Whether the bitmap is inverted. - private void ReadRgbPalette(TColor[] imageData, byte[] colors, int width, int height, int bits, bool inverted) + private void ReadRgbPalette(PixelAccessor pixels, byte[] colors, int width, int height, int bits, bool inverted) where TColor : struct, IPackedPixel where TPacked : struct { @@ -207,46 +219,40 @@ namespace ImageSharp.Formats // Bit mask int mask = 0xFF >> (8 - bits); - byte[] data = new byte[arrayWidth * height]; - - this.currentStream.Read(data, 0, data.Length); - // Rows are aligned on 4 byte boundaries - int alignment = arrayWidth % 4; - if (alignment != 0) + int padding = arrayWidth % 4; + if (padding != 0) { - alignment = 4 - alignment; + padding = 4 - padding; } - Parallel.For( - 0, - height, - Bootstrapper.Instance.ParallelOptions, - y => - { - int rowOffset = y * (arrayWidth + alignment); + byte[] row = new byte[arrayWidth + padding]; + TColor color = default(TColor); - for (int x = 0; x < arrayWidth; x++) - { - int offset = rowOffset + x; + for (int y = 0; y < height; y++) + { + int newY = Invert(y, height, inverted); - // Revert the y value, because bitmaps are saved from down to top - int row = Invert(y, height, inverted); + this.currentStream.Read(row, 0, row.Length); - int colOffset = x * ppb; + int offset = 0; + for (int x = 0; x < arrayWidth; x++) + { + int colOffset = x * ppb; - for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++) - { - int colorIndex = ((data[offset] >> (8 - bits - (shift * bits))) & mask) * 4; - int arrayOffset = (row * width) + (colOffset + shift); + for (int shift = 0; shift < ppb && (x + shift) < width; shift++) + { + int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; + int newX = colOffset + shift; - // Stored in b-> g-> r order. - TColor packed = default(TColor); - packed.PackFromVector4(new Color(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex]).ToVector4()); - imageData[arrayOffset] = packed; - } - } - }); + // Stored in b-> g-> r order. + color.PackFromBytes(colors[colorIndex + 2], colors[colorIndex + 1], colors[colorIndex], 255); + pixels[newX, newY] = color; + } + + offset++; + } + } } /// @@ -254,50 +260,43 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb16(TColor[] imageData, int width, int height, bool inverted) + private void ReadRgb16(PixelAccessor pixels, int width, int height, bool inverted) where TColor : struct, IPackedPixel where TPacked : struct { // We divide here as we will store the colors in our floating point format. const int ScaleR = 8; // 256/32 const int ScaleG = 4; // 256/64 + const int ComponentCount = 2; - int alignment; - byte[] data = this.GetImageArray(width, height, 2, out alignment); - - Parallel.For( - 0, - height, - Bootstrapper.Instance.ParallelOptions, - y => - { - int rowOffset = y * ((width * 2) + alignment); - - // Revert the y value, because bitmaps are saved from down to top - int row = Invert(y, height, inverted); - - for (int x = 0; x < width; x++) - { - int offset = rowOffset + (x * 2); + TColor color = default(TColor); + using (PixelRow row = new PixelRow(width, ComponentOrder.RGB)) + { + for (int y = 0; y < height; y++) + { + row.Read(this.currentStream); - short temp = BitConverter.ToInt16(data, offset); + int newY = Invert(y, height, inverted); - byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); - byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); - byte b = (byte)((temp & Rgb16BMask) * ScaleR); + int offset = 0; + for (int x = 0; x < width; x++) + { + short temp = BitConverter.ToInt16(row.Data, offset); - int arrayOffset = (row * width) + x; + byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR); + byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG); + byte b = (byte)((temp & Rgb16BMask) * ScaleR); - // Stored in b-> g-> r order. - TColor packed = default(TColor); - packed.PackFromVector4(new Color(r, g, b).ToVector4()); - imageData[arrayOffset] = packed; - } - }); + color.PackFromBytes(r, g, b, 255); + pixels[x, newY] = color; + offset += ComponentCount; + } + } + } } /// @@ -305,39 +304,25 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb24(TColor[] imageData, int width, int height, bool inverted) + private void ReadRgb24(PixelAccessor pixels, int width, int height, bool inverted) where TColor : struct, IPackedPixel where TPacked : struct { - int alignment; - byte[] data = this.GetImageArray(width, height, 3, out alignment); - - Parallel.For( - 0, - height, - Bootstrapper.Instance.ParallelOptions, - y => - { - int rowOffset = y * ((width * 3) + alignment); - - // Revert the y value, because bitmaps are saved from down to top - int row = Invert(y, height, inverted); - - for (int x = 0; x < width; x++) - { - int offset = rowOffset + (x * 3); - int arrayOffset = (row * width) + x; - - // Stored in b-> g-> r-> a order. - TColor packed = default(TColor); - packed.PackFromVector4(new Color(data[offset + 2], data[offset + 1], data[offset]).ToVector4()); - imageData[arrayOffset] = packed; - } - }); + int padding = CalculatPadding(width, 3); + using (PixelRow row = new PixelRow(width, ComponentOrder.BGR, padding)) + { + for (int y = 0; y < height; y++) + { + row.Read(this.currentStream); + + int newY = Invert(y, height, inverted); + pixels.CopyFrom(row, newY); + } + } } /// @@ -345,69 +330,25 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The packed format. uint, long, float. - /// The image data to assign the palette to. + /// The to assign the palette to. /// The width of the bitmap. /// The height of the bitmap. /// Whether the bitmap is inverted. - private void ReadRgb32(TColor[] imageData, int width, int height, bool inverted) + private void ReadRgb32(PixelAccessor pixels, int width, int height, bool inverted) where TColor : struct, IPackedPixel where TPacked : struct { - int alignment; - byte[] data = this.GetImageArray(width, height, 4, out alignment); - - Parallel.For( - 0, - height, - Bootstrapper.Instance.ParallelOptions, - y => - { - int rowOffset = y * ((width * 4) + alignment); - - // Revert the y value, because bitmaps are saved from down to top - int row = Invert(y, height, inverted); - - for (int x = 0; x < width; x++) - { - int offset = rowOffset + (x * 4); - int arrayOffset = (row * width) + x; - - // Stored in b-> g-> r-> a order. - TColor packed = default(TColor); - packed.PackFromVector4(new Color(data[offset + 2], data[offset + 1], data[offset], data[offset + 3]).ToVector4()); - imageData[arrayOffset] = packed; - } - }); - } - - /// - /// Returns a containing the pixels for the current bitmap. - /// - /// The width of the bitmap. - /// The height. - /// The number of bytes per pixel. - /// The alignment of the pixels. - /// - /// The containing the pixels. - /// - private byte[] GetImageArray(int width, int height, int bytes, out int alignment) - { - int dataWidth = width; - - alignment = (width * bytes) % 4; - - if (alignment != 0) + int padding = CalculatPadding(width, 4); + using (PixelRow row = new PixelRow(width, ComponentOrder.BGRA, padding)) { - alignment = 4 - alignment; - } - - int size = ((dataWidth * bytes) + alignment) * height; - - byte[] data = new byte[size]; - - this.currentStream.Read(data, 0, size); + for (int y = 0; y < height; y++) + { + row.Read(this.currentStream); - return data; + int newY = Invert(y, height, inverted); + pixels.CopyFrom(row, newY); + } + } } ///