From f93127b005d9dddc250ba4321b24e870c70dbef7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 4 Jun 2019 19:15:19 +0200 Subject: [PATCH 01/15] Add bitmap decoder option, how to treat skipped pixels for RLE --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 7 +- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 118 ++++++++++++++---- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 9 ++ .../Formats/Bmp/IBmpDecoderOptions.cs | 5 +- .../Formats/Bmp/RleSkippedPixelHandling.cs | 26 ++++ .../Formats/Bmp/BmpDecoderTests.cs | 62 ++++++++- tests/ImageSharp.Tests/TestImages.cs | 22 +++- tests/Images/Input/Bmp/pal4rlecut.bmp | Bin 0 -> 3610 bytes tests/Images/Input/Bmp/pal4rletrns.bmp | Bin 0 -> 4326 bytes tests/Images/Input/Bmp/pal8rlecut.bmp | Bin 0 -> 7980 bytes tests/Images/Input/Bmp/pal8rletrns.bmp | Bin 0 -> 9212 bytes 11 files changed, 219 insertions(+), 30 deletions(-) create mode 100644 src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs create mode 100644 tests/Images/Input/Bmp/pal4rlecut.bmp create mode 100644 tests/Images/Input/Bmp/pal4rletrns.bmp create mode 100644 tests/Images/Input/Bmp/pal8rlecut.bmp create mode 100644 tests/Images/Input/Bmp/pal8rletrns.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index ebb7ffdf3c..96f74b0455 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -14,13 +14,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// JPG /// PNG - /// RLE4 + /// Some OS/2 specific subtypes like: Bitmap Array, Color Icon, Color Pointer, Icon, Pointer. /// /// Formats will be supported in a later releases. We advise always /// to use only 24 Bit Windows bitmaps. /// public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { + /// + /// Gets or sets a value indicating how to deal with undefined pixels, which can occur during decoding run length encoded bitmaps. + /// + public RleSkippePixelHandling RleUndefinedPixelHandling { get; set; } = RleSkippePixelHandling.Black; + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 0cbc4fca1b..322a9cbcee 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -69,7 +69,7 @@ internal sealed class BmpDecoderCore private ImageMetadata metadata; /// - /// The bmp specific metadata. + /// The bitmap specific metadata. /// private BmpMetadata bmpMetadata; @@ -83,10 +83,21 @@ internal sealed class BmpDecoderCore /// private BmpInfoHeader infoHeader; + /// + /// The global configuration. + /// private readonly Configuration configuration; + /// + /// Used for allocating memory during processing operations. + /// private readonly MemoryAllocator memoryAllocator; + /// + /// The bitmap decoder options. + /// + private readonly IBmpDecoderOptions options; + /// /// Initializes a new instance of the class. /// @@ -96,6 +107,7 @@ public BmpDecoderCore(Configuration configuration, IBmpDecoderOptions options) { this.configuration = configuration; this.memoryAllocator = configuration.MemoryAllocator; + this.options = options; } /// @@ -207,7 +219,7 @@ public IImageInfo Identify(Stream stream) /// The image width. /// The pixel component count. /// - /// The . + /// The padding. /// private static int CalculatePadding(int width, int componentCount) { @@ -222,7 +234,7 @@ private static int CalculatePadding(int width, int componentCount) } /// - /// Decodes a bitmap containing BITFIELDS Compression type. For each color channel, there will be bitmask + /// Decodes a bitmap containing the BITFIELDS Compression type. For each color channel, there will be a bitmask /// which will be used to determine which bits belong to that channel. /// /// The pixel format. @@ -258,8 +270,8 @@ private void ReadBitFields(Buffer2D pixels, bool inverted) /// /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. - /// Compressed RLE8 stream is uncompressed by - /// Compressed RLE4 stream is uncompressed by + /// Compressed RLE8 stream is uncompressed by + /// Compressed RLE4 stream is uncompressed by /// /// The pixel format. /// The compression type. Either RLE4 or RLE8. @@ -273,14 +285,15 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels { TPixel color = default; using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (Buffer2D undefinedPixels = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) { if (compression == BmpCompression.RLE8) { - this.UncompressRle8(width, buffer.GetSpan()); + this.UncompressRle8(width, buffer.GetSpan(), undefinedPixels.GetSpan()); } else { - this.UncompressRle4(width, buffer.GetSpan()); + this.UncompressRle4(width, buffer.GetSpan(), undefinedPixels.GetSpan()); } for (int y = 0; y < height; y++) @@ -291,7 +304,28 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels for (int x = 0; x < width; x++) { - color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); + byte colorIdx = bufferRow[x]; + if (undefinedPixels[x, y]) + { + switch (this.options.RleUndefinedPixelHandling) + { + case RleSkippePixelHandling.FirstColorOfPalette: + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + break; + case RleSkippePixelHandling.Transparent: + color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); + break; + case RleSkippePixelHandling.Black: + default: + color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + break; + } + } + else + { + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + } + pixelRow[x] = color; } } @@ -308,7 +342,8 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels /// /// The width of the bitmap. /// Buffer for uncompressed data. - private void UncompressRle4(int w, Span buffer) + /// Keeps track over skipped and therefore undefined pixels. + private void UncompressRle4(int w, Span buffer, Span undefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -329,13 +364,25 @@ private void UncompressRle4(int w, Span buffer) switch (cmd[1]) { case RleEndOfBitmap: + int skipEoB = buffer.Length - count; + for (int i = count; i < count + skipEoB; i++) + { + undefinedPixels[i] = true; + } + return; case RleEndOfLine: int extra = count % w; if (extra > 0) { - count += w - extra; + int skipEoL = w - extra; + for (int i = count; i < count + skipEoL; i++) + { + undefinedPixels[i] = true; + } + + count += skipEoL; } break; @@ -343,6 +390,12 @@ private void UncompressRle4(int w, Span buffer) case RleDelta: int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); + int skipDelta = (w * dy) + dx; + for (int i = count; i < count + skipDelta; i++) + { + undefinedPixels[i] = true; + } + count += (w * dy) + dx; break; @@ -374,7 +427,7 @@ private void UncompressRle4(int w, Span buffer) } } - // Absolute mode data is aligned to two-byte word-boundary + // Absolute mode data is aligned to two-byte word-boundary. int padding = bytesToRead & 1; this.stream.Skip(padding); @@ -418,7 +471,8 @@ private void UncompressRle4(int w, Span buffer) /// /// The width of the bitmap. /// Buffer for uncompressed data. - private void UncompressRle8(int w, Span buffer) + /// Keeps track over skipped and therefore undefined pixels. + private void UncompressRle8(int w, Span buffer, Span undefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -439,13 +493,25 @@ private void UncompressRle8(int w, Span buffer) switch (cmd[1]) { case RleEndOfBitmap: + int skipEoB = buffer.Length - count; + for (int i = count; i < count + skipEoB; i++) + { + undefinedPixels[i] = true; + } + return; case RleEndOfLine: int extra = count % w; if (extra > 0) { - count += w - extra; + int skipEoL = w - extra; + for (int i = count; i < count + skipEoL; i++) + { + undefinedPixels[i] = true; + } + + count += skipEoL; } break; @@ -453,7 +519,13 @@ private void UncompressRle8(int w, Span buffer) case RleDelta: int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); - count += (w * dy) + dx; + int skipDelta = (w * dy) + dx; + for (int idx = count; idx < count + skipDelta; idx++) + { + undefinedPixels[idx] = true; + } + + count += skipDelta; break; @@ -481,11 +553,11 @@ private void UncompressRle8(int w, Span buffer) else { int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0] - byte cmd1 = cmd[1]; // store the value to avoid the repeated indexer access inside the loop + byte colorIdx = cmd[1]; // store the value to avoid the repeated indexer access inside the loop for (; count < max; count++) { - buffer[count] = cmd1; + buffer[count] = colorIdx; } } } @@ -506,7 +578,7 @@ private void UncompressRle8(int w, Span buffer) private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) where TPixel : struct, IPixel { - // Pixels per byte (bits per pixel) + // Pixels per byte (bits per pixel). int ppb = 8 / bitsPerPixel; int arrayWidth = (width + ppb - 1) / ppb; @@ -514,7 +586,7 @@ private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int // Bit mask int mask = 0xFF >> (8 - bitsPerPixel); - // Rows are aligned on 4 byte boundaries + // Rows are aligned on 4 byte boundaries. int padding = arrayWidth % 4; if (padding != 0) { @@ -910,7 +982,9 @@ private void ReadInfoHeader() #else byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize]; #endif - this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); // read the header size + + // Read the header size. + this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); if (headerSize < BmpInfoHeader.CoreSize) @@ -925,7 +999,7 @@ private void ReadInfoHeader() headerSize = BmpInfoHeader.MaxHeaderSize; } - // read the rest of the header + // Read the rest of the header. this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); BmpInfoHeaderType infoHeaderType = BmpInfoHeaderType.WinVersion2; @@ -1021,7 +1095,7 @@ private void ReadInfoHeader() this.bmpMetadata = this.metadata.GetFormatMetadata(BmpFormat.Instance); this.bmpMetadata.InfoHeaderType = infoHeaderType; - // We can only encode at these bit rates so far. + // We can only encode at these bit rates so far (1 bit and 4 bit are still missing). if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel8) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel16) || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) @@ -1030,7 +1104,7 @@ private void ReadInfoHeader() this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; } - // skip the remaining header because we can't read those parts + // Skip the remaining header because we can't read those parts. this.stream.Skip(skipAmount); } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index 9fbd0b5adb..4e6ec45021 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -51,10 +51,19 @@ internal sealed class BmpEncoderCore /// private const int ColorPaletteSize8Bit = 1024; + /// + /// Used for allocating memory during processing operations. + /// private readonly MemoryAllocator memoryAllocator; + /// + /// The global configuration. + /// private Configuration configuration; + /// + /// The color depth, in number of bits per pixel. + /// private BmpBitsPerPixel? bitsPerPixel; /// diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index 219d37ca62..18f1b7fba5 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -8,6 +8,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// internal interface IBmpDecoderOptions { - // added this for consistency so we can add stuff as required, no options currently available + /// + /// Gets the value indicating how to deal with undefined pixels, which can occur during decoding run length encoded bitmaps. + /// + RleSkippePixelHandling RleUndefinedPixelHandling { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs new file mode 100644 index 0000000000..33bb3a1932 --- /dev/null +++ b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Bmp +{ + /// + /// Defines possible options, how skipped pixels during decoding of run length encoded bitmaps should be treated. + /// + public enum RleSkippePixelHandling : int + { + /// + /// Undefined pixels should be black. This is how System.Drawing handles undefined pixels. + /// + Black = 0, + + /// + /// Undefined pixels should be transparent. + /// + Transparent = 1, + + /// + /// Undefined pixels should have the first color of the palette. + /// + FirstColorOfPalette = 2 + } +} diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e615dbe568..df16e1dc74 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -24,12 +24,16 @@ public class BmpDecoderTests public static readonly string[] BitfieldsBmpFiles = BitFields; + public static readonly string[] Rle4Bitmaps = Rle4Set; + + public static readonly string[] Rle8Bitmaps = Rle8Set; + public static readonly TheoryData RatioFiles = new TheoryData { - { TestImages.Bmp.Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, - { TestImages.Bmp.RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } + { Car, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { V5Header, 3780, 3780 , PixelResolutionUnit.PixelsPerMeter }, + { RLE8, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; [Theory] @@ -60,6 +64,56 @@ public void BmpDecoder_CanDecodeBitfields(TestImageProvider prov } } + [Theory] + [WithFile(RLE4cut, PixelTypes.Rgba32)] + [WithFile(RLE4delta, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(RLE4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(RLE8cut, PixelTypes.Rgba32)] + [WithFile(RLE8delta, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(RLE8, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) @@ -223,6 +277,8 @@ public void BmpDecoder_CanDecode4BytePerEntryPalette(TestImageProviderX+S^gx2FHJ-vOJH{Unk%+sA8$~lVv zT6p~$<7bT97;}^&1Mf!*L{3%@Oyj>ZdNQ8H(y9c>VL>YL=h?m1b?1fZ& zuT9q8XuY}hkgRRG?m07-M{EwW%N@7lb`Rz^$vi$vGudr!=X1-nwAW^buR5?bn%|)6 zc1up4SP2dUfhA{q1G0}D#@XB^J4QiSH})(zGE8PWUsx6_vfgkfXi8IfG#YrE?|K>! zGtI;27?C_{dY56U08@aIqXTP`HlxAuu`4TLZLw#2kK!E-H@aha1?@Y+}a-effSGMxwwcG+mFfKu&eYC2b{Rdxu^Ix3JsW~cXQ*;1b2OvYTSznNU6C0ELYpplHcoYl4h z=R{^_=cV}?EI1rY*OJlt;zEXK)xlxf%FFx9`{%0YbI<5P_N5AP zBZ>*T(cm>LvVn>XdN%k9o;`cS2bh_`E%5ivhQHx&x4g1gZDi*vX9?81ZV#1DYJYDZ zPs!p+Uhl>7Xs)O)Am5rJnl&U3Y zN7W@>9HKP1)gCb0-$Mt`-aI$_A+{EE2lGTM6TT#5%@<-lLYKFMmW1z* zXKpMbk<+-s)9AsPZDGTkV#Cv@4OV?bV^u0p)>VRj!w?iAAc;US2xih8gnE|c5K8-L zqAu+v&)4d+b+4Jq>2GD&3rCm~*ZVZD*KJPpRNyO6l8Wz)gq;d%sS&slqCqf7IlhG0 zk__00LubwA(m2Dho(fVYPM%bVjuel%gY!zl@r}H`tz8Fj*dnv-r(hg4SfUan8#dto<28)TTIwJ|2}4Aq;~L9z|E&kwBuAN zjSARlgguo+3QfNB);(Wq5;a!`;o^Oyf5lG|?nWy%2c3A2!D*k=&X4-Jjy}1=XY`XD zF@^{=-h`Dj7}Qw6AY~hIWFrpIzy|AtE^NLTWz5&H&FRncTz^{BXD2H*Cwv+_?^CK3 z`_^&MN`VXb0kY=Ry?RZ@FLg7cZ4sN~^9uL-MTrb^n``=1Fcoe^+wTEa__5eKkmz#% z#&0y{SLb5HE4o^a{r=NpN#c%sT*76tFU7uHHzMdSvO;8R1wreu*J=s+ad%8=Kv&|d z0^?5XM2K`w6zdyLtsnx<6$&I$JOs)*6bg8PQeL7H%XbBpbbD zqKKStyv(eV8Qm>tB-svrkAyv}x4uC9c7bZ#Xg0mD86HNbr-OT*w#bY6QU9bzb`eZ= zx?L~qB8TtaKkOlMOCca61CdOsBkDmt=BuOhJ+h;F(cPR+OBw}!hoNENX?1633Ws1y zu-Jkn`kSoN#dRxE&nLY>P%Nx-B)Ld;q+3Y*=n!$XCF3fbdkr_M)k3v!+SG#_?jGv;$B4d$;age^*7eCf{dcDhu~C|>V+FO0BsB4z8}82zpm}U`U8LH51*6T zrCRavV*oq^#NUTIDA;Kei2p;bs^%Sgi_{-)PskcB-nZVV;pMra)z-9jF~`jXFB$RD he;a!t^=|kMv&^pfYvz#DpM5A{NY`focy)m=_Ya3`k@o-q literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Bmp/pal4rletrns.bmp b/tests/Images/Input/Bmp/pal4rletrns.bmp new file mode 100644 index 0000000000000000000000000000000000000000..58994e92baf9812eb805c04807ca946b07f11185 GIT binary patch literal 4326 zcmb_e%}*mo7JqcRtNjs(%MBPivtjH-+EtVTRs)hvOFPCsMYGaK$Tu!4A`Xbd&dDN* zl=!rW6P6E=M{{sbkHm~Mhl#8lR?LB@ffdky$Nn4a?{&4mAem&fOHuCb>h7xd`*^Q< z>#zS#Q~0-!-#?>&hyDb8hGL}Q_$}^RDb;6j&3||FeR$-v@br5e@lSVm>VV%nJaUI| z458mqO6n!kCea!ElZ>+kX|%-AaICibH5q%$7Lkzg66u|jxlQfISt(mu%LqqmPv$o0 z@owHi%Z(aQ0<$J~*6tvjl|<{PMaI!^yjI`E%(QGSlAENTy^l*W zTe6j&TcX9ImbB!&g||HYVNVita zl4c8wI~lgciwjAqQn)rFO-RyP>C6(NOBNYWBxa z7l7Ku%KnM5Nt^!Q?5xBi+*;h;96b-8sFwUE#s>Y#-`(4DOB1c_t}5&wSCf{fDYx*} zZ>c+0me2ktbr+70|fN#)xIIHhdMDxRp-@WclF@oExh+iklizgOdBdsi{;r_V33iBvi2eX!RC$m-C|NMc1^w`4Us^yoCmyYk{ z=^t#Z101;mv}4D49e;363sOT(aC;_l!+rDSxjey{?yiE*FE*SFXK&xmb1M%UrkKpl zMMzO9^*HCm@zF8ON@Sv>n7wQ6F$ywdAz|K z$dsjOIvU@g6mrD%PDEZSt4^_UgngK+JPXQO9L%d3aCUrzilb(;OW|y|kJL5xa?C@_ z=GZ?GA_>Gsz(eJbFdOy+ z8@58NNzs46tdQYeq1;#~z>V{S4DdXVi%>Z>RvnzhrR%A)l=R@vzmFZM=1$aCxueOYh$Hpwppc9hwr#a~47)2wasN2tYM3`*D}xosSrub6V#%`}A2xX7Gu%W>@S=S>fb-oXQ!tD%?Py$bLV|2ykx7 zvRW04dUb#A3bZhfhO|#Oy18G{!5`_MG#m1dIyxtv{%JPHafekk;yijTCLOb)`Eajq zcpNo?R$;G|BlM$s9Ayw^IPx@%7jcca9(pfr6I4?iPr4Z{k4wu3ni{pLq%$)! zJWI%AlnGP(fx(bJWZRsyP$csn8GRC2ndLk(m@eMW=n?KlY zY^gTBvb-L~_df?BGec!rt<`L==5_ts+rhT2ns57kzkkysGYkIPosR8wp#0U(Xc4;%%-{j0}}2bPqd7cr)i%;Bhfi%&g4U z*XnCuRo*$lhlO<#{*6z@4gIB=ZDtR1-Ez15g6|!akslL%brPZXF@*VFM@UAoh^kzK ziglXx>KDOna84FSvF($2Lq@ZS`%S%VbaB6X9bDl)stfN6dS^8eq8`?VU)L2GKgu2f zQ~F*H;fo?wGAksd z%sPqJ7XTbAG>l6Tq?-BUsD|VRZIsn`P?z@ zx#9tlBwyDR6S6@`#4_Rn(Inq?f02!H$c9xcZWUi`z1q6yC7+TWoW~moqH+0bZig0w Xe>D%A-yn!@Ur_QT6@%bGM$h~Q4Wl~I literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Bmp/pal8rlecut.bmp b/tests/Images/Input/Bmp/pal8rlecut.bmp new file mode 100644 index 0000000000000000000000000000000000000000..840d31cce63f6cf51773e03008d7f8ed18c0cf9b GIT binary patch literal 7980 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 Nll3QGs>B9m{5Mp_Pk;ab literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Bmp/pal8rletrns.bmp b/tests/Images/Input/Bmp/pal8rletrns.bmp new file mode 100644 index 0000000000000000000000000000000000000000..a2af88d87cbb042f233e1ace72d69d5fc7b7a82f GIT binary patch literal 9212 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 Date: Tue, 4 Jun 2019 20:11:55 +0200 Subject: [PATCH 02/15] Refactored bitmap tests into smaller tests, instead of just one test which goes through all bitmap files --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 13 ++- .../Formats/Bmp/BmpDecoderTests.cs | 81 ++++++++++++++++++- tests/ImageSharp.Tests/TestImages.cs | 29 +------ 3 files changed, 84 insertions(+), 39 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 322a9cbcee..de02e0b1b9 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -315,7 +315,8 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels case RleSkippePixelHandling.Transparent: color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); break; - case RleSkippePixelHandling.Black: + + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). default: color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); break; @@ -882,11 +883,7 @@ private void ReadRgb32BitFields(Buffer2D pixels, int width, int uint maxValueAlpha = 0xFFFFFFFF >> (32 - bitsAlphaMask); float invMaxValueAlpha = 1.0f / maxValueAlpha; - bool unusualBitMask = false; - if (bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8) - { - unusualBitMask = true; - } + bool unusualBitMask = bitsRedMask > 8 || bitsGreenMask > 8 || bitsBlueMask > 8 || invMaxValueAlpha > 8; using (IManagedByteBuffer buffer = this.memoryAllocator.AllocateManagedByteBuffer(stride)) { @@ -1027,7 +1024,7 @@ private void ReadInfoHeader() { byte[] bitfieldsBuffer = new byte[12]; this.stream.Read(bitfieldsBuffer, 0, 12); - Span data = bitfieldsBuffer.AsSpan(); + Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); @@ -1036,7 +1033,7 @@ private void ReadInfoHeader() { byte[] bitfieldsBuffer = new byte[16]; this.stream.Read(bitfieldsBuffer, 0, 16); - Span data = bitfieldsBuffer.AsSpan(); + Span data = bitfieldsBuffer.AsSpan(); this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index df16e1dc74..07edf558df 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -24,10 +24,6 @@ public class BmpDecoderTests public static readonly string[] BitfieldsBmpFiles = BitFields; - public static readonly string[] Rle4Bitmaps = Rle4Set; - - public static readonly string[] Rle8Bitmaps = Rle8Set; - public static readonly TheoryData RatioFiles = new TheoryData { @@ -64,6 +60,80 @@ public void BmpDecoder_CanDecodeBitfields(TestImageProvider prov } } + [Theory] + [WithFile(Bit16Inverted, PixelTypes.Rgba32)] + [WithFile(Bit8Inverted, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_Inverted(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit1, PixelTypes.Rgba32)] + [WithFile(Bit1Pal1, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit8, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_8Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit16, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_16Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit32Rgb, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_32Bit(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(RLE4cut, PixelTypes.Rgba32)] [WithFile(RLE4delta, PixelTypes.Rgba32)] @@ -104,6 +174,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestIma [Theory] [WithFile(RLE8, PixelTypes.Rgba32)] + [WithFile(RLE8Inverted, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider) where TPixel : struct, IPixel { @@ -160,6 +231,7 @@ public void BmpDecoder_CanDecodeBitfields_WithUnusualBitmasks(TestImageP [Theory] [WithFile(WinBmpv2, PixelTypes.Rgba32)] + [WithFile(CoreHeader, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) where TPixel : struct, IPixel { @@ -220,6 +292,7 @@ public void BmpDecoder_CanDecodeBmpv4(TestImageProvider provider [Theory] [WithFile(WinBmpv5, PixelTypes.Rgba32)] + [WithFile(V5Header, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBmpv5(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 773fff2159..81b7d60d48 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -232,10 +232,10 @@ public static class Bmp public const string RLE8 = "Bmp/RunLengthEncoded.bmp"; public const string RLE8cut = "Bmp/pal8rlecut.bmp"; public const string RLE8delta = "Bmp/pal8rletrns.bmp"; + public const string RLE8Inverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string RLE4 = "Bmp/pal4rle.bmp"; public const string RLE4cut = "Bmp/pal4rlecut.bmp"; public const string RLE4delta = "Bmp/pal4rletrns.bmp"; - public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; @@ -281,36 +281,11 @@ public static readonly string[] BitFields Issue735, }; - public static readonly string[] Rle4Set - = { - RLE4, - RLE4cut, - RLE4delta, - }; - - public static readonly string[] Rle8Set - = { - RLE8cut, - RLE8delta, - RLE8, - RLEInverted, - }; - public static readonly string[] All = { Car, F, - NegHeight, - CoreHeader, - V5Header, - Bit1, - Bit1Pal1, - Bit4, - Bit8, - Bit8Inverted, - Bit16, - Bit16Inverted, - Bit32Rgb + NegHeight }; } From d41b1fffdd2d5a1b0202686e63993d235abe062f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 4 Jun 2019 20:19:35 +0200 Subject: [PATCH 03/15] Add another adobe v3 header bitmap testcase --- .../Formats/Bmp/BmpDecoderTests.cs | 3 ++- tests/ImageSharp.Tests/TestImages.cs | 3 ++- tests/Images/Input/Bmp/rgb32h52.bmp | Bin 0 -> 32578 bytes 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Bmp/rgb32h52.bmp diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 07edf558df..4812db6df0 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -267,7 +267,8 @@ public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 81b7d60d48..25b25728b6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -267,7 +267,8 @@ public static class Bmp public const string Rgb16565 = "Bmp/rgb16-565.bmp"; public const string Rgb16565pal = "Bmp/rgb16-565pal.bmp"; public const string Issue735 = "Bmp/issue735.bmp"; - public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; + public const string Rgba32bf56AdobeV3 = "Bmp/rgba32h56.bmp"; + public const string Rgb32h52AdobeV3 = "Bmp/rgb32h52.bmp"; public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp"; diff --git a/tests/Images/Input/Bmp/rgb32h52.bmp b/tests/Images/Input/Bmp/rgb32h52.bmp new file mode 100644 index 0000000000000000000000000000000000000000..db6e4538ef84f48baf706603bd919223d372444b GIT binary patch literal 32578 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 B3Bv#Y literal 0 HcmV?d00001 From 3c1eef2f7fdc7b15434cec8d3469797978cda2c0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 5 Jun 2019 19:13:42 +0200 Subject: [PATCH 04/15] Using the constant from BmpConstants to Identify bitmaps --- src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs index c0814b1dfc..4f862d9295 100644 --- a/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs +++ b/src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers.Binary; namespace SixLabors.ImageSharp.Formats.Bmp { @@ -21,10 +22,7 @@ public IImageFormat DetectFormat(ReadOnlySpan header) private bool IsSupportedFileFormat(ReadOnlySpan header) { - // TODO: This should be in constants - return header.Length >= this.HeaderSize - && header[0] == 0x42 // B - && header[1] == 0x4D; // M + return header.Length >= this.HeaderSize && BinaryPrimitives.ReadInt16LittleEndian(header) == BmpConstants.TypeMarkers.Bitmap; } } } \ No newline at end of file From 26ce2fcfe3a46c1929b8aa5148214032b9c47bd4 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 5 Jun 2019 20:07:37 +0200 Subject: [PATCH 05/15] Bitmap decoder now can handle oversized palette's --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 10 ++++++---- .../Formats/Bmp/BmpDecoderTests.cs | 13 +++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 2 ++ tests/Images/Input/Bmp/pal8oversizepal.bmp | Bin 0 -> 9446 bytes tests/Images/Input/Bmp/rgb24largepal.bmp | Bin 0 -> 25830 bytes 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal8oversizepal.bmp create mode 100644 tests/Images/Input/Bmp/rgb24largepal.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index de02e0b1b9..e5461c734c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1176,10 +1176,12 @@ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palett if (colorMapSize > 0) { - // 256 * 4 - if (colorMapSize > 1024) + // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. + // Make sure, that we will not read pass the bitmap offset (starting position of image data). + if ((this.stream.Position + colorMapSize) > this.fileHeader.Offset) { - BmpThrowHelper.ThrowImageFormatException($"Invalid bmp colormap size '{colorMapSize}'"); + BmpThrowHelper.ThrowImageFormatException( + $"Reading the color map would read beyond the bitmap offset. Either the color map size '{colorMapSize}' is invalid or the bitmap offset."); } palette = new byte[colorMapSize]; @@ -1192,7 +1194,7 @@ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palett int skipAmount = this.fileHeader.Offset - (int)this.stream.Position; if ((skipAmount + (int)this.stream.Position) > this.stream.Length) { - BmpThrowHelper.ThrowImageFormatException($"Invalid fileheader offset found. Offset is greater than the stream length."); + BmpThrowHelper.ThrowImageFormatException("Invalid fileheader offset found. Offset is greater than the stream length."); } if (skipAmount > 0) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 4812db6df0..5d6fe6ff82 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -266,6 +266,19 @@ public void BmpDecoder_CanDecodeLessThanFullPalette(TestImageProvider(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 25b25728b6..9de24db0f3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -259,6 +259,8 @@ public static class Bmp public const string Os2v2 = "Bmp/pal8os2v2.bmp"; public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; + public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; + public const string Rgb24LargePalette = "Bmp/rgb24largepal.bmp"; // Bitmap images with compression type BITFIELDS public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; diff --git a/tests/Images/Input/Bmp/pal8oversizepal.bmp b/tests/Images/Input/Bmp/pal8oversizepal.bmp new file mode 100644 index 0000000000000000000000000000000000000000..93b8187ca1351aa20edad1a8c1da1c8a1bf74631 GIT binary patch literal 9446 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 EADP{TeE Date: Wed, 5 Jun 2019 21:04:23 +0200 Subject: [PATCH 06/15] Add test for invalid palette size --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- tests/ImageSharp.Tests/FileTestBase.cs | 2 +- .../Formats/Bmp/BmpDecoderTests.cs | 15 ++++++++--- .../JpegProfilingBenchmarks.cs | 2 +- tests/ImageSharp.Tests/TestImages.cs | 25 ++++++++++++++++-- tests/Images/Input/Bmp/invalidPaletteSize.bmp | Bin 0 -> 9270 bytes 6 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tests/Images/Input/Bmp/invalidPaletteSize.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index e5461c734c..8725062eec 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1181,7 +1181,7 @@ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palett if ((this.stream.Position + colorMapSize) > this.fileHeader.Offset) { BmpThrowHelper.ThrowImageFormatException( - $"Reading the color map would read beyond the bitmap offset. Either the color map size '{colorMapSize}' is invalid or the bitmap offset."); + $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSize}' is invalid or the bitmap offset."); } palette = new byte[colorMapSize]; diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index a056bc474e..4f8475738b 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -28,7 +28,7 @@ public abstract class FileTestBase /// /// A collection of all the bmp test images /// - public static IEnumerable AllBmpFiles = TestImages.Bmp.All; + public static IEnumerable AllBmpFiles = TestImages.Bmp.Benchmark; /// /// A collection of all the jpeg test images diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 5d6fe6ff82..d2b49015d6 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; @@ -20,7 +21,7 @@ public class BmpDecoderTests { public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; - public static readonly string[] AllBmpFiles = All; + public static readonly string[] MiscBmpFiles = Miscellaneous; public static readonly string[] BitfieldsBmpFiles = BitFields; @@ -33,8 +34,8 @@ public class BmpDecoderTests }; [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] - public void DecodeBmp(TestImageProvider provider) + [WithFileCollection(nameof(MiscBmpFiles), PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_MiscellaneousBitmaps(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new BmpDecoder())) @@ -279,6 +280,14 @@ public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Throws( () => { using (Image image = provider.GetImage(new BmpDecoder())) { } }); + } + [Theory] [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs index 65989556d2..95a47fd7cc 100644 --- a/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/ProfilingBenchmarks/JpegProfilingBenchmarks.cs @@ -82,7 +82,7 @@ public void EncodeJpeg(int executionCount, int quality, JpegSubsample subsample) return; } - string[] testFiles = TestImages.Bmp.All + string[] testFiles = TestImages.Bmp.Benchmark .Concat(new[] { TestImages.Jpeg.Baseline.Calliphora, TestImages.Jpeg.Baseline.Cmyk }).ToArray(); Image[] testImages = testFiles.Select( diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9de24db0f3..3d0d291446 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -261,8 +261,9 @@ public static class Bmp public const string Pal8Offset = "Bmp/pal8offs.bmp"; public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; public const string Rgb24LargePalette = "Bmp/rgb24largepal.bmp"; + public const string InvalidPaletteSize = "Bmp/invalidPaletteSize.bmp"; - // Bitmap images with compression type BITFIELDS + // Bitmap images with compression type BITFIELDS. public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; public const string Rgb32bf = "Bmp/rgb32bf.bmp"; public const string Rgb16bfdef = "Bmp/rgb16bfdef.bmp"; @@ -284,12 +285,32 @@ public static readonly string[] BitFields Issue735, }; - public static readonly string[] All + public static readonly string[] Miscellaneous = { Car, F, NegHeight }; + + public static readonly string[] Benchmark + = { + Car, + F, + NegHeight, + CoreHeader, + V5Header, + RLE4, + RLE8, + RLE8Inverted, + Bit1, + Bit1Pal1, + Bit4, + Bit8, + Bit8Inverted, + Bit16, + Bit16Inverted, + Bit32Rgb + }; } public static class Gif diff --git a/tests/Images/Input/Bmp/invalidPaletteSize.bmp b/tests/Images/Input/Bmp/invalidPaletteSize.bmp new file mode 100644 index 0000000000000000000000000000000000000000..afb120bbf956e58ac817c15b379cf9076ccd6678 GIT binary patch literal 9270 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 literal 0 HcmV?d00001 From abbe8e35a216fa690f4fc331ec76c0c4e9318ebe Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 5 Jun 2019 21:17:11 +0200 Subject: [PATCH 07/15] Renamed RleUndefinedPixelHandling to RleSkippedPixelHandling --- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 4 ++-- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 6 +++--- src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs | 4 ++-- src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs | 2 +- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 96f74b0455..a404ab418b 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -22,9 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp public sealed class BmpDecoder : IImageDecoder, IBmpDecoderOptions, IImageInfoDetector { /// - /// Gets or sets a value indicating how to deal with undefined pixels, which can occur during decoding run length encoded bitmaps. + /// Gets or sets a value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. /// - public RleSkippePixelHandling RleUndefinedPixelHandling { get; set; } = RleSkippePixelHandling.Black; + public RleSkippedPixelHandling RleSkippedPixelHandling { get; set; } = RleSkippedPixelHandling.Black; /// public Image Decode(Configuration configuration, Stream stream) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 8725062eec..c99997e084 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -307,12 +307,12 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels byte colorIdx = bufferRow[x]; if (undefinedPixels[x, y]) { - switch (this.options.RleUndefinedPixelHandling) + switch (this.options.RleSkippedPixelHandling) { - case RleSkippePixelHandling.FirstColorOfPalette: + case RleSkippedPixelHandling.FirstColorOfPalette: color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); break; - case RleSkippePixelHandling.Transparent: + case RleSkippedPixelHandling.Transparent: color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); break; diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs index 18f1b7fba5..f456f2ba3a 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs @@ -9,8 +9,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp internal interface IBmpDecoderOptions { /// - /// Gets the value indicating how to deal with undefined pixels, which can occur during decoding run length encoded bitmaps. + /// Gets the value indicating how to deal with skipped pixels, which can occur during decoding run length encoded bitmaps. /// - RleSkippePixelHandling RleUndefinedPixelHandling { get; } + RleSkippedPixelHandling RleSkippedPixelHandling { get; } } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs index 33bb3a1932..fe1a0aa790 100644 --- a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs +++ b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Defines possible options, how skipped pixels during decoding of run length encoded bitmaps should be treated. /// - public enum RleSkippePixelHandling : int + public enum RleSkippedPixelHandling : int { /// /// Undefined pixels should be black. This is how System.Drawing handles undefined pixels. diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index d2b49015d6..b63a7f9aa7 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -141,7 +141,7 @@ public void BmpDecoder_CanDecode_32Bit(TestImageProvider provide public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); image.CompareToOriginal(provider); @@ -153,7 +153,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); image.CompareToOriginal(provider); @@ -166,7 +166,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); image.CompareToOriginal(provider); @@ -179,7 +179,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestIma public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new BmpDecoder() { RleUndefinedPixelHandling = RleSkippePixelHandling.Black })) + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); image.CompareToOriginal(provider); From bd24596ce34bf9eeae349c9aa4f07bad99d16c35 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 6 Jun 2019 19:59:18 +0200 Subject: [PATCH 08/15] Explicitly using SystemDrawingReferenceDecoder in some BitmapDecoder tests --- .../Formats/Bmp/BmpDecoderTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index b63a7f9aa7..9159858fe5 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -83,7 +83,7 @@ public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -144,7 +144,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -156,7 +156,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -169,7 +169,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestIma using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -182,7 +182,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -276,7 +276,7 @@ public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } @@ -433,4 +433,4 @@ public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider p } } } -} \ No newline at end of file +} From 22be7ea5a9b4753a68dcb09ecd7a00ccd79f104c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 6 Jun 2019 20:07:37 +0200 Subject: [PATCH 09/15] Add test cases for unsupported bitmaps --- .../Formats/Bmp/BmpDecoderTests.cs | 11 ++++++++++- tests/ImageSharp.Tests/TestImages.cs | 2 ++ tests/Images/Input/Bmp/rgb24jpeg.bmp | Bin 0 -> 2457 bytes tests/Images/Input/Bmp/rgb24png.bmp | Bin 0 -> 1210 bytes 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Bmp/rgb24jpeg.bmp create mode 100644 tests/Images/Input/Bmp/rgb24png.bmp diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 9159858fe5..b83f64c90d 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -282,12 +282,21 @@ public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider(TestImageProvider provider) + public void BmpDecoder_ThrowsImageFormatException_OnInvalidPaletteSize(TestImageProvider provider) where TPixel : struct, IPixel { Assert.Throws( () => { using (Image image = provider.GetImage(new BmpDecoder())) { } }); } + [Theory] + [WithFile(Rgb24jpeg, PixelTypes.Rgba32)] + [WithFile(Rgb24png, PixelTypes.Rgba32)] + public void BmpDecoder_ThrowsNotSupportedException_OnUnsupportedBitmaps(TestImageProvider provider) + where TPixel : struct, IPixel + { + Assert.Throws(() => { using (Image image = provider.GetImage(new BmpDecoder())) { } }); + } + [Theory] [WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d679688d20..fe4c6c180b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -263,6 +263,8 @@ public static class Bmp public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; public const string Rgb24LargePalette = "Bmp/rgb24largepal.bmp"; public const string InvalidPaletteSize = "Bmp/invalidPaletteSize.bmp"; + public const string Rgb24jpeg = "Bmp/rgb24jpeg.bmp"; + public const string Rgb24png = "Bmp/rgb24png.bmp"; // Bitmap images with compression type BITFIELDS. public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; diff --git a/tests/Images/Input/Bmp/rgb24jpeg.bmp b/tests/Images/Input/Bmp/rgb24jpeg.bmp new file mode 100644 index 0000000000000000000000000000000000000000..87d73d75b84793d8366c0bdf7eba713fa80026bf GIT binary patch literal 2457 zcmbW23piBk8pqd|VT?=DxP=&ZLR799QzW^y<0+BLs9nfq_Do^iCYnh1K}PP`LJV7? z7@`t#%`GGv*9_W2F|#8zE_0UJr}jC|e$I2wdDrt_&%56Dd%pGk)>_|xSslti0AJ%e zc#Z-^0c8ma0tyBS3HBvLzX`nj+pw}d67si=|4`hR`>(+t;!gmZ9c=7v00;yE*ntPY z9|raV!a_oDAwgj{9F9N;i-<{yi;0Sg$w^2er4;3r(2DX33d-vGTFR>1)f5!AnQCv} zxy#7NNJ-0V&u#;(zM+x9#v~8~0wE?UCMzy3YoMZ_V(>o)zaBsdgU^Kmg{T4oCuD+)D2Gs$09^zw#drKYiMe1)7CLEHZk38hS_g@z{b|j z-og2(i|a8rcMoqL-_w5n0f8Z;(6I1`Nb<$FOP8-)y>^|Jd@Ch2Ej=SMFaK^qA-$;h zw}%y#jH>EKHMNb;o0?l%+uC3B^}iZ;Jvj7+`F4^uH9hn0clIpzkB^H>pO!y=;cak1 z0O&WY?_~eRg#x(*U@#~QzQF|%2nPj4!30%y2}$fb3_lqpscH}-EM=9H`=9}#W_W}n zeJZ$DL`L1{tp;}k?JL=T2NwH(k$ngI!Nml`pb+roK~aDOFthjubIglw-}flm#4%-% z-A5`zQ;~g!3O4V83fuc?KY-QeVD2n&rKL?Jh+p?>P7`hJ~hE9uiMINlHQ8no1c`bQH>+{A`v8~{G zLYbs&-7Eb*Y_G6vEeCEDNh=(gHk_r^1w9C3uh;l=9Xa6 zazVWNY(CY8rh7eJhw^dZ7Aw~(nGdAlo(&fFjz!$(i668~v@OTn#!y3k>b0hKNA&Id zLX|KWwl}xy@P@($L-GS(*Je|Dp9@k?fOlVI~ zG^A=#9;Fd-I&HB0?wHQDhfH_7+++vWm5NUKMYA?-xs{7ECvS_e&#O?{t+~r<hsrCdONUtWUvcy@+>|u!jM}J{Wvf)#tk;MYtNiJ)~nv1UT!0jDdT$agr#=M=}~`*SS8-erty&exp7&&QAYgpQzw*d%d_b-^}FSiL!DRm z@6qS6uW4j2KAx=tzu_m=k;E)5hq+2{^P#?OGmK+OgAF`CebUrC9?ZK6mYQ>7%U~GB zSc-H{;+W`-63=Alc0Hx8G)1`Fy9CGwPY+VIPF=>`&xPIM4vphZoP_+svB2P2ZFHGf zg;cn#0;Fww& zirJeW$^Qa?kuw^0FgqwN?VDxUIUPX7=#Ka|hFz9N*#bC_9dH zXUzT%@mJI>cQl8!q)#-MnDBC*k7Ae;ZI=|MuKQ>C+-%c0^-yh-OU&<-!oc{FK(Z?% zFR)f*=nK>1MN(@7E_YLw#g*ikU)Qto-i;<9{yf3bxP`3;WmePMbvu$;a7+y zGahcs<+ARWo{V!Dv?mdl}}0RP%yD$DfW=3 zTf4MJ{b|_UrJD+!iv#7$;z;SCYkr3+(8!8M+Vn9al^pY6OIGe;k3emfk^m0Jq9i== zH(VQa;{#md_K3h9T(Vbr-9VP(ANPzR7K;RPqnlANu7pwE6g6z}so#bXIvaLsIi9FZ z{ndsOzl;qOH+Ee+v#{d~LwCwaPoE)rIn&x{zIYE@Yn`{3kzZE=FBm^zFDcQ@GaZOt zJX1QU_TpbnKHwqgUQs=(GiYme3+S738(k#$3;1gG?A{noMuJ*M{RcmG7lGN+pE*VN zXExDz*un+0@wp#8J+Mdg07vnnG$h}jH8R1nepo!{=Cz=km&Jg$%0a-wdG;HgulHAv dm;C1OR)2c@v!AXtxsTiM%If05O+w+n`3K0WQrG|h literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Bmp/rgb24png.bmp b/tests/Images/Input/Bmp/rgb24png.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e87ec7addaf1ce3ab12cd8e93bf8fb75e7165904 GIT binary patch literal 1210 zcmZ?r-NnKH23F!Z|4~L+;$Lg_#Y~pQ`$H&C#AV}O^=Ue))J|y3q5!gQZquXur#&IabPu^ zI_pBn+7}E#ty+eU7&}XvCJJub(5l;$;;o_+%kF+`F0Z<>c58=1WF@VLGE>{5JZl-i{psQSL7S zll8@xU7o+fLePKz@ei|Rp0_ZW<$C$^NJKKA4?Wy)hisg6q@2+-v-=&o!9ofDtbKVzrt9RQkh)=PfdHMbv z#>Mh(ukBxjy^YFUYFo8Uu&qBQ+d|3T{a2OOd#;XJh9{yk>lQP(EuNUP=zV9M+GNSF zUz$!8MX}a9?p&9V1}gCA+F@GKT9K!B_4slAzxUtYKDctXR&;XZ_BRpCGE!=4WsSwE zFB!itIR1Ctp2IBLckR73v!8M5TdxY^oU;tO+dh8GJ@)DT?*CE6-VskY|9I!uRX$i7 zd^XJ4S}~hH&q-4H^tN!t1=GcTt<}xFpv?bJ?DW)cW#4vu+rZYq_+9#Ym^Oof(WZmj z*M3P}c$JGGC+{6c&AH3}W3KP(%8K6-V|9F|QKKHfUD-U}O%O|HxC)U{?z3IH6fc3*R#{IDC0^YOc<|+OuEv zCFcqBEj})DbX)6H96V8@3^{=b2l*8GkCiCxvX Date: Thu, 6 Jun 2019 20:38:28 +0200 Subject: [PATCH 10/15] Comparing RLE test images to reference decoder only on windows --- .../Formats/Bmp/BmpDecoderTests.cs | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index b83f64c90d..11477636c1 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -144,7 +144,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -156,7 +159,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -169,7 +175,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestIma using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -182,7 +191,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -276,7 +288,10 @@ public void BmpDecoder_CanDecodeOversizedPalette(TestImageProvider image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } From 42676f8bce2d53c56ab1d49ca3e9b899c273fd69 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 6 Jun 2019 21:16:47 +0200 Subject: [PATCH 11/15] Add test case for decoding winv4 fast path --- .../Formats/Bmp/BmpDecoderTests.cs | 18 ++++++++++++++++-- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Bmp/rgba32v4.bmp | Bin 0 -> 32634 bytes 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 tests/Images/Input/Bmp/rgba32v4.bmp diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 11477636c1..0c8943d4c2 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -41,7 +41,6 @@ public void BmpDecoder_CanDecode_MiscellaneousBitmaps(TestImageProvider< using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - if (TestEnvironment.IsWindows) { image.CompareToOriginal(provider); @@ -95,7 +94,10 @@ public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); - image.CompareToOriginal(provider); + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -135,6 +137,18 @@ public void BmpDecoder_CanDecode_32Bit(TestImageProvider provide } } + [Theory] + [WithFile(Rgba32v4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_32BitV4Header_Fast(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(RLE4cut, PixelTypes.Rgba32)] [WithFile(RLE4delta, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index fe4c6c180b..488d4d5804 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -265,6 +265,7 @@ public static class Bmp public const string InvalidPaletteSize = "Bmp/invalidPaletteSize.bmp"; public const string Rgb24jpeg = "Bmp/rgb24jpeg.bmp"; public const string Rgb24png = "Bmp/rgb24png.bmp"; + public const string Rgba32v4 = "Bmp/rgba32v4.bmp"; // Bitmap images with compression type BITFIELDS. public const string Rgb32bfdef = "Bmp/rgb32bfdef.bmp"; diff --git a/tests/Images/Input/Bmp/rgba32v4.bmp b/tests/Images/Input/Bmp/rgba32v4.bmp new file mode 100644 index 0000000000000000000000000000000000000000..9d644a8b1e3ca6fa444eaa00ff952f19f858b043 GIT binary patch literal 32634 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 u)GMv)V|}7ewV}`SxxUb*zSLLxT3h Date: Sat, 8 Jun 2019 12:31:04 +0200 Subject: [PATCH 12/15] Add another 8 Bit RLE test with magick reference decoder --- .../Formats/Bmp/BmpDecoderTests.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 0c8943d4c2..ba75c2d0c2 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -94,6 +94,7 @@ public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. if (TestEnvironment.IsWindows) { image.CompareToOriginal(provider); @@ -158,6 +159,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit_WithDelta(TestIma using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. if (TestEnvironment.IsWindows) { image.CompareToOriginal(provider); @@ -173,6 +175,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); + // The Magick Reference Decoder can not decode 4-Bit bitmaps, so only execute this on windows. if (TestEnvironment.IsWindows) { image.CompareToOriginal(provider); @@ -183,7 +186,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider [Theory] [WithFile(RLE8cut, PixelTypes.Rgba32)] [WithFile(RLE8delta, PixelTypes.Rgba32)] - public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestImageProvider provider) + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRefDecoder(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) @@ -191,11 +194,24 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta(TestIma image.DebugSave(provider); if (TestEnvironment.IsWindows) { - image.CompareToOriginal(provider); + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } } + [Theory] + [WithFile(RLE8cut, PixelTypes.Rgba32)] + [WithFile(RLE8delta, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(RLE8, PixelTypes.Rgba32)] [WithFile(RLE8Inverted, PixelTypes.Rgba32)] @@ -205,10 +221,7 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) { image.DebugSave(provider); - if (TestEnvironment.IsWindows) - { - image.CompareToOriginal(provider); - } + image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } } From 3d350661aebf5b86da6b4065ee70a9ec6b5ebd35 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 8 Jun 2019 20:19:18 +0200 Subject: [PATCH 13/15] Optimize RLE skipped pixel handling --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 140 +++++++++++++----- .../Formats/Bmp/RleSkippedPixelHandling.cs | 4 +- .../Formats/Bmp/BmpDecoderTests.cs | 15 +- tests/ImageSharp.Tests/TestImages.cs | 11 +- tests/Images/Input/Bmp/rle4-delta-320x240.bmp | Bin 0 -> 3686 bytes tests/Images/Input/Bmp/rle8-blank-160x120.bmp | Bin 0 -> 1080 bytes tests/Images/Input/Bmp/rle8-delta-320x240.bmp | Bin 0 -> 4646 bytes 7 files changed, 119 insertions(+), 51 deletions(-) create mode 100644 tests/Images/Input/Bmp/rle4-delta-320x240.bmp create mode 100644 tests/Images/Input/Bmp/rle8-blank-160x120.bmp create mode 100644 tests/Images/Input/Bmp/rle8-delta-320x240.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index c99997e084..8812efffa2 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; @@ -270,8 +270,8 @@ private void ReadBitFields(Buffer2D pixels, bool inverted) /// /// Looks up color values and builds the image from de-compressed RLE8 or RLE4 data. - /// Compressed RLE8 stream is uncompressed by - /// Compressed RLE4 stream is uncompressed by + /// Compressed RLE8 stream is uncompressed by + /// Compressed RLE4 stream is uncompressed by /// /// The pixel format. /// The compression type. Either RLE4 or RLE8. @@ -286,14 +286,16 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels TPixel color = default; using (Buffer2D buffer = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) using (Buffer2D undefinedPixels = this.memoryAllocator.Allocate2D(width, height, AllocationOptions.Clean)) + using (IMemoryOwner rowsWithUndefinedPixels = this.memoryAllocator.Allocate(height, AllocationOptions.Clean)) { + Span rowsWithUndefinedPixelsSpan = rowsWithUndefinedPixels.Memory.Span; if (compression == BmpCompression.RLE8) { - this.UncompressRle8(width, buffer.GetSpan(), undefinedPixels.GetSpan()); + this.UncompressRle8(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); } else { - this.UncompressRle4(width, buffer.GetSpan(), undefinedPixels.GetSpan()); + this.UncompressRle4(width, buffer.GetSpan(), undefinedPixels.GetSpan(), rowsWithUndefinedPixelsSpan); } for (int y = 0; y < height; y++) @@ -302,32 +304,46 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels Span bufferRow = buffer.GetRowSpan(y); Span pixelRow = pixels.GetRowSpan(newY); - for (int x = 0; x < width; x++) + bool rowHasUndefinedPixels = rowsWithUndefinedPixelsSpan[y]; + if (rowHasUndefinedPixels) { - byte colorIdx = bufferRow[x]; - if (undefinedPixels[x, y]) + // Slow path with undefined pixels. + for (int x = 0; x < width; x++) { - switch (this.options.RleSkippedPixelHandling) + byte colorIdx = bufferRow[x]; + if (undefinedPixels[x, y]) + { + switch (this.options.RleSkippedPixelHandling) + { + case RleSkippedPixelHandling.FirstColorOfPalette: + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + break; + case RleSkippedPixelHandling.Transparent: + color.FromVector4(Vector4.Zero); + break; + + // Default handling for skipped pixels is black (which is what System.Drawing is also doing). + default: + color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); + break; + } + } + else { - case RleSkippedPixelHandling.FirstColorOfPalette: - color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); - break; - case RleSkippedPixelHandling.Transparent: - color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); - break; - - // Default handling for skipped pixels is black (which is what System.Drawing is also doing). - default: - color.FromVector4(new Vector4(0.0f, 0.0f, 0.0f, 1.0f)); - break; + color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); } + + pixelRow[x] = color; } - else + } + else + { + // Fast path without any undefined pixels. + for (int x = 0; x < width; x++) { - color.FromBgr24(Unsafe.As(ref colors[colorIdx * 4])); + color.FromBgr24(Unsafe.As(ref colors[bufferRow[x] * 4])); + pixelRow[x] = color; } - - pixelRow[x] = color; } } } @@ -344,7 +360,8 @@ private void ReadRle(BmpCompression compression, Buffer2D pixels /// The width of the bitmap. /// Buffer for uncompressed data. /// Keeps track over skipped and therefore undefined pixels. - private void UncompressRle4(int w, Span buffer, Span undefinedPixels) + /// Keeps track of rows, which have undefined pixels. + private void UncompressRle4(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -365,19 +382,31 @@ private void UncompressRle4(int w, Span buffer, Span undefinedPixels switch (cmd[1]) { case RleEndOfBitmap: + { int skipEoB = buffer.Length - count; for (int i = count; i < count + skipEoB; i++) { undefinedPixels[i] = true; } + int skippedRowIdx = count / w; + int skippedRows = (skipEoB / w) - 1; + int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + return; + } case RleEndOfLine: - int extra = count % w; - if (extra > 0) + { + rowsWithUndefinedPixels[count / w] = true; + int remainingPixelsInRow = count % w; + if (remainingPixelsInRow > 0) { - int skipEoL = w - extra; + int skipEoL = w - remainingPixelsInRow; for (int i = count; i < count + skipEoL; i++) { undefinedPixels[i] = true; @@ -387,8 +416,10 @@ private void UncompressRle4(int w, Span buffer, Span undefinedPixels } break; + } case RleDelta: + { int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); int skipDelta = (w * dy) + dx; @@ -397,9 +428,17 @@ private void UncompressRle4(int w, Span buffer, Span undefinedPixels undefinedPixels[i] = true; } - count += (w * dy) + dx; + int skippedRowIdx = count / w; + int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + + count += skipDelta; break; + } default: // If the second byte > 2, we are in 'absolute mode'. @@ -472,8 +511,9 @@ private void UncompressRle4(int w, Span buffer, Span undefinedPixels /// /// The width of the bitmap. /// Buffer for uncompressed data. - /// Keeps track over skipped and therefore undefined pixels. - private void UncompressRle8(int w, Span buffer, Span undefinedPixels) + /// Keeps track of skipped and therefore undefined pixels. + /// Keeps track of rows, which have undefined pixels. + private void UncompressRle8(int w, Span buffer, Span undefinedPixels, Span rowsWithUndefinedPixels) { #if NETCOREAPP2_1 Span cmd = stackalloc byte[2]; @@ -494,19 +534,31 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels switch (cmd[1]) { case RleEndOfBitmap: + { int skipEoB = buffer.Length - count; for (int i = count; i < count + skipEoB; i++) { undefinedPixels[i] = true; } + int skippedRowIdx = count / w; + int skippedRows = skipEoB / w; + int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + return; + } case RleEndOfLine: - int extra = count % w; - if (extra > 0) + { + rowsWithUndefinedPixels[count / w] = true; + int remainingPixelsInRow = count % w; + if (remainingPixelsInRow > 0) { - int skipEoL = w - extra; + int skipEoL = w - remainingPixelsInRow; for (int i = count; i < count + skipEoL; i++) { undefinedPixels[i] = true; @@ -516,8 +568,10 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels } break; + } case RleDelta: + { int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); int skipDelta = (w * dy) + dx; @@ -526,13 +580,21 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels undefinedPixels[idx] = true; } + int skippedRowIdx = count / w; + int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + count += skipDelta; break; + } default: - // If the second byte > 2, we are in 'absolute mode' - // Take this number of bytes from the stream as uncompressed data + // If the second byte > 2, we are in 'absolute mode'. + // Take this number of bytes from the stream as uncompressed data. int length = cmd[1]; byte[] run = new byte[length]; @@ -543,7 +605,7 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels count += run.Length; - // Absolute mode data is aligned to two-byte word-boundary + // Absolute mode data is aligned to two-byte word-boundary. int padding = length & 1; this.stream.Skip(padding); @@ -554,7 +616,7 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels else { int max = count + cmd[0]; // as we start at the current count in the following loop, max is count + cmd[0] - byte colorIdx = cmd[1]; // store the value to avoid the repeated indexer access inside the loop + byte colorIdx = cmd[1]; // store the value to avoid the repeated indexer access inside the loop. for (; count < max; count++) { @@ -1205,4 +1267,4 @@ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palett return bytesPerColorMapEntry; } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs index fe1a0aa790..493fe366ad 100644 --- a/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs +++ b/src/ImageSharp/Formats/Bmp/RleSkippedPixelHandling.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. namespace SixLabors.ImageSharp.Formats.Bmp @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp public enum RleSkippedPixelHandling : int { /// - /// Undefined pixels should be black. This is how System.Drawing handles undefined pixels. + /// Undefined pixels should be black. This is the default behavior and equal to how System.Drawing handles undefined pixels. /// Black = 0, diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index ba75c2d0c2..1cc873717b 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -151,8 +151,9 @@ public void BmpDecoder_CanDecode_32BitV4Header_Fast(TestImageProvider(TestImageProvider provider) where TPixel : struct, IPixel { @@ -184,8 +185,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_4Bit(TestImageProvider } [Theory] - [WithFile(RLE8cut, PixelTypes.Rgba32)] - [WithFile(RLE8delta, PixelTypes.Rgba32)] + [WithFile(RLE8Cut, PixelTypes.Rgba32)] + [WithFile(RLE8Delta, PixelTypes.Rgba32)] + [WithFile(Rle8Delta320240, PixelTypes.Rgba32)] + [WithFile(Rle8Blank160120, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRefDecoder(TestImageProvider provider) where TPixel : struct, IPixel { @@ -200,8 +203,8 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_SystemDrawingRe } [Theory] - [WithFile(RLE8cut, PixelTypes.Rgba32)] - [WithFile(RLE8delta, PixelTypes.Rgba32)] + [WithFile(RLE8Cut, PixelTypes.Rgba32)] + [WithFile(RLE8Delta, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecoder(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 488d4d5804..d041f48544 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -231,12 +231,15 @@ public static class Bmp public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; public const string RLE8 = "Bmp/RunLengthEncoded.bmp"; - public const string RLE8cut = "Bmp/pal8rlecut.bmp"; - public const string RLE8delta = "Bmp/pal8rletrns.bmp"; + public const string RLE8Cut = "Bmp/pal8rlecut.bmp"; + public const string RLE8Delta = "Bmp/pal8rletrns.bmp"; + public const string Rle8Delta320240 = "Bmp/rle8-delta-320x240.bmp"; + public const string Rle8Blank160120 = "Bmp/rle8-blank-160x120.bmp"; public const string RLE8Inverted = "Bmp/RunLengthEncoded-inverted.bmp"; public const string RLE4 = "Bmp/pal4rle.bmp"; - public const string RLE4cut = "Bmp/pal4rlecut.bmp"; - public const string RLE4delta = "Bmp/pal4rletrns.bmp"; + public const string RLE4Cut = "Bmp/pal4rlecut.bmp"; + public const string RLE4Delta = "Bmp/pal4rletrns.bmp"; + public const string Rle4Delta320240 = "Bmp/rle4-delta-320x240.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; diff --git a/tests/Images/Input/Bmp/rle4-delta-320x240.bmp b/tests/Images/Input/Bmp/rle4-delta-320x240.bmp new file mode 100644 index 0000000000000000000000000000000000000000..78a09278704143f846a944af2d59fe8f6d75fcf5 GIT binary patch literal 3686 zcmeHKF%H5o40N2dr2~k3fr*ik5g}H_d;y_8fLHb*jQv44C(Y0fkb%)oFRm*my;^Bb zx!xZ;O7=p31TKbXf;Z%d;ioOp+h*($i$%0-@cE(`BaP!&1x#!*n4SmXfrcCAV~w;D zx(f7v(7ix+MO6epOg})TnSOb?Wf!dwrMno={9XB6|LT^42_rk{_6{6BkJVDX7pm8SSC9vRSQn Date: Sat, 8 Jun 2019 20:40:53 +0200 Subject: [PATCH 14/15] Refactor RLE decoding to eliminate code duplication --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 183 ++++++++++--------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 8812efffa2..294b49ed7e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -382,63 +382,22 @@ private void UncompressRle4(int w, Span buffer, Span undefinedPixels switch (cmd[1]) { case RleEndOfBitmap: - { int skipEoB = buffer.Length - count; - for (int i = count; i < count + skipEoB; i++) - { - undefinedPixels[i] = true; - } - - int skippedRowIdx = count / w; - int skippedRows = (skipEoB / w) - 1; - int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); - for (int i = skippedRowIdx; i <= lastSkippedRow; i++) - { - rowsWithUndefinedPixels[i] = true; - } + RleSkipEndOfBitmap(count, w, skipEoB, undefinedPixels, rowsWithUndefinedPixels); return; - } case RleEndOfLine: - { - rowsWithUndefinedPixels[count / w] = true; - int remainingPixelsInRow = count % w; - if (remainingPixelsInRow > 0) - { - int skipEoL = w - remainingPixelsInRow; - for (int i = count; i < count + skipEoL; i++) - { - undefinedPixels[i] = true; - } - - count += skipEoL; - } + count += RleSkipEndOfLine(count, w, undefinedPixels, rowsWithUndefinedPixels); break; - } case RleDelta: - { int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); - int skipDelta = (w * dy) + dx; - for (int i = count; i < count + skipDelta; i++) - { - undefinedPixels[i] = true; - } - - int skippedRowIdx = count / w; - int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); - for (int i = skippedRowIdx; i <= lastSkippedRow; i++) - { - rowsWithUndefinedPixels[i] = true; - } - - count += skipDelta; + count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; - } default: // If the second byte > 2, we are in 'absolute mode'. @@ -534,63 +493,22 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels switch (cmd[1]) { case RleEndOfBitmap: - { int skipEoB = buffer.Length - count; - for (int i = count; i < count + skipEoB; i++) - { - undefinedPixels[i] = true; - } - - int skippedRowIdx = count / w; - int skippedRows = skipEoB / w; - int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); - for (int i = skippedRowIdx; i <= lastSkippedRow; i++) - { - rowsWithUndefinedPixels[i] = true; - } + RleSkipEndOfBitmap(count, w, skipEoB, undefinedPixels, rowsWithUndefinedPixels); return; - } case RleEndOfLine: - { - rowsWithUndefinedPixels[count / w] = true; - int remainingPixelsInRow = count % w; - if (remainingPixelsInRow > 0) - { - int skipEoL = w - remainingPixelsInRow; - for (int i = count; i < count + skipEoL; i++) - { - undefinedPixels[i] = true; - } - - count += skipEoL; - } + count += RleSkipEndOfLine(count, w, undefinedPixels, rowsWithUndefinedPixels); break; - } case RleDelta: - { int dx = this.stream.ReadByte(); int dy = this.stream.ReadByte(); - int skipDelta = (w * dy) + dx; - for (int idx = count; idx < count + skipDelta; idx++) - { - undefinedPixels[idx] = true; - } - - int skippedRowIdx = count / w; - int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); - for (int i = skippedRowIdx; i <= lastSkippedRow; i++) - { - rowsWithUndefinedPixels[i] = true; - } - - count += skipDelta; + count += RleSkipDelta(count, w, dx, dy, undefinedPixels, rowsWithUndefinedPixels); break; - } default: // If the second byte > 2, we are in 'absolute mode'. @@ -626,6 +544,95 @@ private void UncompressRle8(int w, Span buffer, Span undefinedPixels } } + /// + /// Keeps track of skipped / undefined pixels, when EndOfBitmap command occurs. + /// + /// The already processed pixel count. + /// The width of the image. + /// The skipped pixel count. + /// The undefined pixels. + /// Rows with undefined pixels. + private static void RleSkipEndOfBitmap( + int count, + int w, + int skipPixelCount, + Span undefinedPixels, + Span rowsWithUndefinedPixels) + { + for (int i = count; i < count + skipPixelCount; i++) + { + undefinedPixels[i] = true; + } + + int skippedRowIdx = count / w; + int skippedRows = (skipPixelCount / w) - 1; + int lastSkippedRow = Math.Min(skippedRowIdx + skippedRows, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + } + + /// + /// Keeps track of undefined / skipped pixels, when the EndOfLine command occurs. + /// + /// The already processed pixel count. + /// The width of image. + /// The undefined pixels. + /// The rows with undefined pixels. + /// The number of skipped pixels. + private static int RleSkipEndOfLine(int count, int w, Span undefinedPixels, Span rowsWithUndefinedPixels) + { + rowsWithUndefinedPixels[count / w] = true; + int remainingPixelsInRow = count % w; + if (remainingPixelsInRow > 0) + { + int skipEoL = w - remainingPixelsInRow; + for (int i = count; i < count + skipEoL; i++) + { + undefinedPixels[i] = true; + } + + return skipEoL; + } + + return 0; + } + + /// + /// Keeps track of undefined / skipped pixels, when the delta command occurs. + /// + /// The count. + /// The width of the image. + /// Delta skip in x direction. + /// Delta skip in y direction. + /// The undefined pixels. + /// The rows with undefined pixels. + /// The number of skipped pixels. + private static int RleSkipDelta( + int count, + int w, + int dx, + int dy, + Span undefinedPixels, + Span rowsWithUndefinedPixels) + { + int skipDelta = (w * dy) + dx; + for (int i = count; i < count + skipDelta; i++) + { + undefinedPixels[i] = true; + } + + int skippedRowIdx = count / w; + int lastSkippedRow = Math.Min(skippedRowIdx + dy, rowsWithUndefinedPixels.Length - 1); + for (int i = skippedRowIdx; i <= lastSkippedRow; i++) + { + rowsWithUndefinedPixels[i] = true; + } + + return skipDelta; + } + /// /// Reads the color palette from the stream. /// From d79fe53f63c0843135859734b9683fbac573c6e9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 8 Jun 2019 21:30:47 +0200 Subject: [PATCH 15/15] Using MagickReferenceDecoder for the 8-Bit RLE test --- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 1cc873717b..c4dfa724cc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -221,10 +221,10 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit_WithDelta_MagickRefDecode public void BmpDecoder_CanDecode_RunLengthEncoded_8Bit(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.Black })) + using (Image image = provider.GetImage(new BmpDecoder() { RleSkippedPixelHandling = RleSkippedPixelHandling.FirstColorOfPalette })) { image.DebugSave(provider); - image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); } }