Browse Source

Add makeup codes for run length above 63

pull/1570/head
Brian Popow 5 years ago
parent
commit
6ea7675336
  1. 301
      src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs
  2. 20
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

301
src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs

@ -15,6 +15,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
/// </summary>
internal class T4BitWriter
{
private const uint WhiteZeroRunTermCode = 0x35;
private const uint BlackZeroRunTermCode = 0x37;
private static readonly List<uint> MakeupRunLength = new List<uint>()
{
64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560
};
private static readonly Dictionary<uint, uint> WhiteLen4TermCodes = new Dictionary<uint, uint>()
{
{ 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF }
@ -38,10 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary<uint, uint> WhiteLen8TermCodes = new Dictionary<uint, uint>()
{
{ 0, 0x35 }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, { 36, 0x15 },
{ 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, { 45, 0x4 },
{ 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 }, { 54, 0x25 },
{ 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 }, { 63, 0x34 }
{ 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 },
{ 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D },
{ 45, 0x4 }, { 46, 0x5 }, { 47, 0xA }, { 48, 0xB }, { 49, 0x52 }, { 50, 0x53 }, { 51, 0x54 }, { 52, 0x55 }, { 53, 0x24 },
{ 54, 0x25 }, { 55, 0x58 }, { 56, 0x59 }, { 57, 0x5A }, { 58, 0x5B }, { 59, 0x4A }, { 60, 0x4B }, { 61, 0x32 }, { 62, 0x33 },
{ 63, 0x34 }
};
private static readonly Dictionary<uint, uint> BlackLen2TermCodes = new Dictionary<uint, uint>()
@ -86,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
private static readonly Dictionary<uint, uint> BlackLen10TermCodes = new Dictionary<uint, uint>()
{
{ 0, 0x37 }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 }
{ 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 }
};
private static readonly Dictionary<uint, uint> BlackLen11TermCodes = new Dictionary<uint, uint>()
@ -103,13 +113,75 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{ 62, 0x66 }, { 63, 0x67 }
};
private static readonly Dictionary<uint, uint> WhiteLen5MakeupCodes = new Dictionary<uint, uint>()
{
{ 64, 0x1B }, { 128, 0x12 }
};
private static readonly Dictionary<uint, uint> WhiteLen6MakeupCodes = new Dictionary<uint, uint>()
{
{ 192, 0x17 }, { 1664, 0x18 }
};
private static readonly Dictionary<uint, uint> WhiteLen8MakeupCodes = new Dictionary<uint, uint>()
{
{ 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 }
};
private static readonly Dictionary<uint, uint> WhiteLen7MakeupCodes = new Dictionary<uint, uint>()
{
{ 256, 0x37 }
};
private static readonly Dictionary<uint, uint> WhiteLen9MakeupCodes = new Dictionary<uint, uint>()
{
{ 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 },
{ 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 },
{ 1600, 0x9A }, { 1728, 0x9B }
};
private static readonly Dictionary<uint, uint> WhiteLen11MakeupCodes = new Dictionary<uint, uint>()
{
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD }
};
private static readonly Dictionary<uint, uint> WhiteLen12MakeupCodes = new Dictionary<uint, uint>()
{
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C },
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F }
};
private static readonly Dictionary<uint, uint> BlackLen10MakeupCodes = new Dictionary<uint, uint>()
{
{ 64, 0xF }
};
private static readonly Dictionary<uint, uint> BlackLen11MakeupCodes = new Dictionary<uint, uint>()
{
{ 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD }
};
private static readonly Dictionary<uint, uint> BlackLen12MakeupCodes = new Dictionary<uint, uint>()
{
{ 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 },
{ 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C },
{ 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F }
};
private static readonly Dictionary<uint, uint> BlackLen13MakeupCodes = new Dictionary<uint, uint>()
{
{ 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 },
{ 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 },
{ 1408, 0x54 }, { 1472, 0x55 }, { 1536, 0x5A }, { 1600, 0x5B }, { 1664, 0x64 }, { 1728, 0x65 }
};
private readonly MemoryAllocator memoryAllocator;
private readonly Configuration configuration;
private int bytePosition = 0;
private int bytePosition;
private byte bitPosition = 0;
private byte bitPosition;
/// <summary>
/// Initializes a new instance of the <see cref="T4BitWriter" /> class.
@ -149,13 +221,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
for (int y = 0; y < image.Height; y++)
{
bool isWhiteRun = true;
bool isStartOrRow = true;
Span<TPixel> pixelRow = image.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGray);
int x = 0;
while (x < image.Width)
{
uint runLength = 0;
for (int i = x; i < pixelRow.Length; i++)
for (int i = x; i < image.Width; i++)
{
if (isWhiteRun && pixelRowAsGray[i].PackedValue != 255)
{
@ -179,16 +252,51 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
bool gotTermCode = this.GetTermCode(runLength, out var code, out var codeLength, isWhiteRun);
if (isStartOrRow && runLength == 0)
{
this.WriteCode(8, WhiteZeroRunTermCode, compressedData);
this.WriteCode(codeLength, code, compressedData);
isWhiteRun = false;
isStartOrRow = false;
continue;
}
x += (int)runLength;
uint code;
uint codeLength;
if (runLength <= 63)
{
code = this.GetTermCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
x += (int)runLength;
}
else
{
runLength = this.GetBestFittingMakeupRunLength(runLength);
code = this.GetMakeupCode(runLength, out codeLength, isWhiteRun);
this.WriteCode(codeLength, code, compressedData);
x += (int)runLength;
// If we are at the end of the line with a makeup code, we need to write a final term code with a length of zero.
if (x == image.Width)
{
if (isWhiteRun)
{
this.WriteCode(8, WhiteZeroRunTermCode, compressedData);
}
else
{
this.WriteCode(10, BlackZeroRunTermCode, compressedData);
}
}
continue;
}
isStartOrRow = false;
isWhiteRun = !isWhiteRun;
}
// Write EOL
// Write EOL.
this.WriteCode(12, 1, compressedData);
}
@ -202,7 +310,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
{
while (codeLength > 0)
{
var bitNumber = (int) codeLength;
var bitNumber = (int)codeLength;
var bit = (code & (1 << (bitNumber - 1))) != 0;
if (bit)
{
@ -224,142 +332,227 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
}
}
private bool GetTermCode(uint runLength, out uint code, out uint codeLength, bool isWhiteRun)
private uint GetBestFittingMakeupRunLength(uint runLength)
{
for (int i = 0; i < MakeupRunLength.Count - 1; i++)
{
if (MakeupRunLength[i] <= runLength && MakeupRunLength[i + 1] > runLength)
{
return MakeupRunLength[i];
}
}
return MakeupRunLength[MakeupRunLength.Count - 1];
}
private uint GetTermCode(uint runLength, out uint codeLength, bool isWhiteRun)
{
if (isWhiteRun)
{
return this.GetWhiteTermCode(runLength, out codeLength);
}
return this.GetBlackTermCode(runLength, out codeLength);
}
private uint GetMakeupCode(uint runLength, out uint codeLength, bool isWhiteRun)
{
if (isWhiteRun)
{
return this.GetWhiteTermCode(runLength, out code, out codeLength);
return this.GetWhiteMakeupCode(runLength, out codeLength);
}
return this.GetBlackMakeupCode(runLength, out codeLength);
}
private uint GetWhiteMakeupCode(uint runLength, out uint codeLength)
{
codeLength = 0;
if (WhiteLen5MakeupCodes.ContainsKey(runLength))
{
codeLength = 5;
return WhiteLen5MakeupCodes[runLength];
}
if (WhiteLen6MakeupCodes.ContainsKey(runLength))
{
codeLength = 6;
return WhiteLen6MakeupCodes[runLength];
}
if (WhiteLen7MakeupCodes.ContainsKey(runLength))
{
codeLength = 7;
return WhiteLen7MakeupCodes[runLength];
}
if (WhiteLen8MakeupCodes.ContainsKey(runLength))
{
codeLength = 8;
return WhiteLen8MakeupCodes[runLength];
}
if (WhiteLen9MakeupCodes.ContainsKey(runLength))
{
codeLength = 9;
return WhiteLen9MakeupCodes[runLength];
}
if (WhiteLen11MakeupCodes.ContainsKey(runLength))
{
codeLength = 11;
return WhiteLen11MakeupCodes[runLength];
}
if (WhiteLen12MakeupCodes.ContainsKey(runLength))
{
codeLength = 12;
return WhiteLen12MakeupCodes[runLength];
}
return 0;
}
private uint GetBlackMakeupCode(uint runLength, out uint codeLength)
{
codeLength = 0;
if (BlackLen10MakeupCodes.ContainsKey(runLength))
{
codeLength = 10;
return BlackLen10MakeupCodes[runLength];
}
if (BlackLen11MakeupCodes.ContainsKey(runLength))
{
codeLength = 11;
return BlackLen11MakeupCodes[runLength];
}
if (BlackLen12MakeupCodes.ContainsKey(runLength))
{
codeLength = 12;
return BlackLen12MakeupCodes[runLength];
}
if (BlackLen13MakeupCodes.ContainsKey(runLength))
{
codeLength = 13;
return BlackLen13MakeupCodes[runLength];
}
return this.GetBlackTermCode(runLength, out code, out codeLength);
return 0;
}
private bool GetWhiteTermCode(uint runLength, out uint code, out uint codeLength)
private uint GetWhiteTermCode(uint runLength, out uint codeLength)
{
code = 0;
codeLength = 0;
if (WhiteLen4TermCodes.ContainsKey(runLength))
{
code = WhiteLen4TermCodes[runLength];
codeLength = 4;
return true;
return WhiteLen4TermCodes[runLength];
}
if (WhiteLen5TermCodes.ContainsKey(runLength))
{
code = WhiteLen5TermCodes[runLength];
codeLength = 5;
return true;
return WhiteLen5TermCodes[runLength];
}
if (WhiteLen6TermCodes.ContainsKey(runLength))
{
code = WhiteLen6TermCodes[runLength];
codeLength = 6;
return true;
return WhiteLen6TermCodes[runLength];
}
if (WhiteLen7TermCodes.ContainsKey(runLength))
{
code = WhiteLen7TermCodes[runLength];
codeLength = 7;
return true;
return WhiteLen7TermCodes[runLength];
}
if (WhiteLen8TermCodes.ContainsKey(runLength))
{
code = WhiteLen8TermCodes[runLength];
codeLength = 8;
return true;
return WhiteLen8TermCodes[runLength];
}
return false;
return 0;
}
private bool GetBlackTermCode(uint runLength, out uint code, out uint codeLength)
private uint GetBlackTermCode(uint runLength, out uint codeLength)
{
code = 0;
codeLength = 0;
if (BlackLen2TermCodes.ContainsKey(runLength))
{
code = BlackLen2TermCodes[runLength];
codeLength = 2;
return true;
return BlackLen2TermCodes[runLength];
}
if (BlackLen3TermCodes.ContainsKey(runLength))
{
code = BlackLen3TermCodes[runLength];
codeLength = 3;
return true;
return BlackLen3TermCodes[runLength];
}
if (BlackLen4TermCodes.ContainsKey(runLength))
{
code = BlackLen4TermCodes[runLength];
codeLength = 4;
return true;
return BlackLen4TermCodes[runLength];
}
if (BlackLen5TermCodes.ContainsKey(runLength))
{
code = BlackLen5TermCodes[runLength];
codeLength = 5;
return true;
return BlackLen5TermCodes[runLength];
}
if (BlackLen6TermCodes.ContainsKey(runLength))
{
code = BlackLen6TermCodes[runLength];
codeLength = 6;
return true;
return BlackLen6TermCodes[runLength];
}
if (BlackLen7TermCodes.ContainsKey(runLength))
{
code = BlackLen7TermCodes[runLength];
codeLength = 7;
return true;
return BlackLen7TermCodes[runLength];
}
if (BlackLen8TermCodes.ContainsKey(runLength))
{
code = BlackLen8TermCodes[runLength];
codeLength = 8;
return true;
return BlackLen8TermCodes[runLength];
}
if (BlackLen9TermCodes.ContainsKey(runLength))
{
code = BlackLen9TermCodes[runLength];
codeLength = 9;
return true;
return BlackLen9TermCodes[runLength];
}
if (BlackLen10TermCodes.ContainsKey(runLength))
{
code = BlackLen10TermCodes[runLength];
codeLength = 10;
return true;
return BlackLen10TermCodes[runLength];
}
if (BlackLen11TermCodes.ContainsKey(runLength))
{
code = BlackLen11TermCodes[runLength];
codeLength = 11;
return true;
return BlackLen11TermCodes[runLength];
}
if (BlackLen12TermCodes.ContainsKey(runLength))
{
code = BlackLen12TermCodes[runLength];
codeLength = 12;
return true;
return BlackLen12TermCodes[runLength];
}
return false;
return 0;
}
}
}

20
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -341,6 +341,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.PhotometricInterpretation = TiffPhotometricInterpretation.PaletteColor;
break;
case TiffEncodingMode.BiColor:
if (this.CompressionType == TiffEncoderCompression.CcittGroup3Fax)
{
// The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero.
this.PhotometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
}
else
{
this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
}
break;
case TiffEncodingMode.Gray:
this.PhotometricInterpretation = TiffPhotometricInterpretation.BlackIsZero;
break;
@ -358,6 +370,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return 3;
case TiffPhotometricInterpretation.PaletteColor:
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
return 1;
default:
return 3;
@ -372,6 +385,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return new ushort[] { 8 };
case TiffPhotometricInterpretation.Rgb:
return new ushort[] { 8, 8, 8 };
case TiffPhotometricInterpretation.WhiteIsZero:
if (this.Mode == TiffEncodingMode.BiColor)
{
return new ushort[] { 1 };
}
return new ushort[] { 8 };
case TiffPhotometricInterpretation.BlackIsZero:
if (this.Mode == TiffEncodingMode.BiColor)
{

Loading…
Cancel
Save