diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
index c31eb8793f..043e5b313e 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs
@@ -55,6 +55,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
///
private uint runLength;
+ ///
+ /// We keep track if its the start of the row, because each run is expected to start with a white run.
+ /// If the image row itself starts with black, a white run of zero is expected.
+ ///
+ private bool isStartOfRow;
+
private readonly int dataLength;
private const int MinCodeLength = 2;
@@ -78,14 +84,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary WhiteLen7TermCodes = new Dictionary()
{
- { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, { 0x24, 27 }, { 0x18, 28 }
+ { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 },
+ { 0x24, 27 }, { 0x18, 28 }
};
private static readonly Dictionary WhiteLen8TermCodes = new Dictionary()
{
- { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 },
- { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, { 0x5, 46 }, { 0xA, 47 }, { 0xB, 48 }, { 0x52, 49 }, { 0x53, 50 }, { 0x54, 51 }, { 0x55, 52 }, { 0x24, 53 }, { 0x25, 54 }, { 0x58, 55 },
- { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 }
+ { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 },
+ { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 },
+ { 0x5, 46 }, { 0xA, 47 }, { 0xB, 48 }, { 0x52, 49 }, { 0x53, 50 }, { 0x54, 51 }, { 0x55, 52 }, { 0x24, 53 }, { 0x25, 54 },
+ { 0x58, 55 }, { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 }
};
private static readonly Dictionary BlackLen2TermCodes = new Dictionary()
@@ -159,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary()
{
- { 0x36, 320 }, { 0x37, 348 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 }
+ { 0x36, 320 }, { 0x37, 384 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 }
};
private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary()
@@ -181,7 +189,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary()
{
- { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 }
+ { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 },
+ { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 }
};
private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary()
@@ -197,7 +206,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary()
{
{ 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 },
- { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304}, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 }
+ { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 },
+ { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 }
};
private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary()
@@ -225,6 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
this.position = 0;
this.isWhiteRun = true;
this.isFirstScanLine = true;
+ this.isStartOfRow = true;
this.terminationCodeFound = false;
this.runLength = 0;
}
@@ -313,14 +324,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
TiffThrowHelper.ThrowImageFormatException("t4 parsing error: invalid code length read");
}
+ bool isMakeupCode = this.IsMakeupCode();
+ if (isMakeupCode)
+ {
+ if (this.IsWhiteRun)
+ {
+ this.runLength += this.WhiteMakeupCodeRunLength();
+ }
+ else
+ {
+ this.runLength += this.BlackMakeupCodeRunLength();
+ }
+
+ this.isStartOfRow = false;
+ this.Reset(resetRunLength: false);
+ continue;
+ }
+
bool isTerminatingCode = this.IsTerminatingCode();
if (isTerminatingCode)
{
// Each line starts with a white run. If the image starts with black, a white run with length zero is written.
- if (this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0)
+ if (this.isStartOfRow && this.IsWhiteRun && this.WhiteTerminatingCodeRunLength() == 0)
{
this.isWhiteRun = !this.IsWhiteRun;
this.Reset();
+ this.isStartOfRow = false;
continue;
}
@@ -334,25 +363,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
this.terminationCodeFound = true;
+ this.isStartOfRow = false;
break;
}
- bool isMakeupCode = this.IsMakeupCode();
- if (isMakeupCode)
- {
- if (this.IsWhiteRun)
- {
- this.runLength += this.WhiteMakeupCodeRunLength();
- }
- else
- {
- this.runLength += this.BlackMakeupCodeRunLength();
- }
-
- this.Reset(false);
- continue;
- }
-
var currBit = this.ReadValue(1);
this.value = (this.value << 1) | currBit;
@@ -360,6 +374,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{
// Each new row starts with a white run.
this.isWhiteRun = true;
+ this.isStartOfRow = true;
}
}
while (!this.IsEndOfScanLine);
diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs
index 13f7eb7945..ae15a8b614 100644
--- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs
+++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs
@@ -36,6 +36,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{
bitReader.ReadNextRun();
+ if (bitReader.RunLength > 0)
+ {
+ if (bitReader.IsWhiteRun)
+ {
+ this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue);
+ bitsWritten += bitReader.RunLength;
+ }
+ else
+ {
+ this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue);
+ bitsWritten += bitReader.RunLength;
+ }
+ }
+
if (bitReader.IsEndOfScanLine)
{
// Write padding bytes, if necessary.
@@ -45,19 +59,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
this.WriteBits(buffer, (int)bitsWritten, pad, 0);
bitsWritten += pad;
}
-
- continue;
- }
-
- if (bitReader.IsWhiteRun)
- {
- this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue);
- bitsWritten += bitReader.RunLength;
- }
- else
- {
- this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue);
- bitsWritten += bitReader.RunLength;
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 3a40d5ce2b..5e04906bb5 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -18,6 +18,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Trait("Category", "Tiff")]
public class TiffDecoderTests
{
+ private static TiffDecoder TiffDecoder => new TiffDecoder();
+
+ private static MagickReferenceDecoder ReferenceDecoder => new MagickReferenceDecoder();
+
public static readonly string[] SingleTestImages = TestImages.Tiff.All;
public static readonly string[] MultiframeTestImages = TestImages.Tiff.Multiframes;
@@ -29,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void ThrowsNotSupported(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- Assert.Throws(() => provider.GetImage(new TiffDecoder()));
+ Assert.Throws(() => provider.GetImage(TiffDecoder));
}
[Theory]
@@ -79,10 +83,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void Decode(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- using (Image image = provider.GetImage(new TiffDecoder()))
+ using (Image image = provider.GetImage(TiffDecoder))
{
image.DebugSave(provider);
- image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder());
+ image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
}
}
@@ -91,15 +95,15 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void DecodeMultiframe(TestImageProvider provider)
where TPixel : unmanaged, IPixel
{
- using (Image image = provider.GetImage(new TiffDecoder()))
+ using (Image image = provider.GetImage(TiffDecoder))
{
Assert.True(image.Frames.Count > 1);
image.DebugSave(provider);
- image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder());
+ image.CompareToOriginal(provider, ImageComparer.Exact, ReferenceDecoder);
image.DebugSaveMultiFrame(provider);
- image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, new MagickReferenceDecoder());
+ image.CompareToOriginalMultiFrame(provider, ImageComparer.Exact, ReferenceDecoder);
}
}
}
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index abdde50b19..fa9e4e290a 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -514,7 +514,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Calliphora_Fax3Compressed = "Tiff/Calliphora_ccitt_fax3.tif";
public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tif";
- public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tif";
+ public const string CcittFax3AllMakupCodes = "Tiff/ccitt_fax3_all_makeupcodes_codes.tif";
public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff";
public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff";
diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
index d411a6fb7e..17ad3e9903 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs
@@ -87,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs
MemoryGroup framePixels = frame.PixelBuffer.FastMemoryGroup;
using IUnsafePixelCollection pixels = magicFrame.GetPixelsUnsafe();
- if (magicFrame.Depth == 8)
+ if (magicFrame.Depth == 8 || magicFrame.Depth == 1)
{
byte[] data = pixels.ToByteArray(PixelMapping.RGBA);
diff --git a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif
index 09be316550..6a1153bac7 100644
--- a/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif
+++ b/tests/Images/Input/Tiff/ccitt_fax3_all_makeupcodes_codes.tif
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3b1a0af9ace55d5d4b86225cb569a8632b6a6bb621fa1dc56a7d3d6404eba7bb
-size 360
+oid sha256:60b64bd3c24437eb90c0a17a4328e997702d7e4c0889ec90abde092ab9b490e8
+size 546