mirror of https://github.com/SixLabors/ImageSharp
Browse Source
# Conflicts: # tests/ImageSharp.Tests/TestImages.cs # tests/Images/Input/Tiff/Calliphora_ccitt_fax4.tiffpull/1734/head
29 changed files with 1045 additions and 238 deletions
@ -0,0 +1,154 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a reference scan line for CCITT 2D decoding.
|
|||
/// </summary>
|
|||
internal readonly ref struct CcittReferenceScanline |
|||
{ |
|||
private readonly ReadOnlySpan<byte> scanLine; |
|||
private readonly int width; |
|||
private readonly byte whiteByte; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittReferenceScanline"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="whiteIsZero">Indicates, if white is zero, otherwise black is zero.</param>
|
|||
/// <param name="scanLine">The scan line.</param>
|
|||
public CcittReferenceScanline(bool whiteIsZero, ReadOnlySpan<byte> scanLine) |
|||
{ |
|||
this.scanLine = scanLine; |
|||
this.width = scanLine.Length; |
|||
this.whiteByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittReferenceScanline"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="whiteIsZero">Indicates, if white is zero, otherwise black is zero.</param>
|
|||
/// <param name="width">The width of the scanline.</param>
|
|||
public CcittReferenceScanline(bool whiteIsZero, int width) |
|||
{ |
|||
this.scanLine = default; |
|||
this.width = width; |
|||
this.whiteByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
} |
|||
|
|||
public bool IsEmpty => this.scanLine.IsEmpty; |
|||
|
|||
/// <summary>
|
|||
/// Finds b1: The first changing element on the reference line to the right of a0 and of opposite color to a0.
|
|||
/// </summary>
|
|||
/// <param name="a0">The reference or starting element om the coding line.</param>
|
|||
/// <param name="a0Byte">Fill byte.</param>
|
|||
/// <returns>Position of b1.</returns>
|
|||
public int FindB1(int a0, byte a0Byte) |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return this.FindB1ForImaginaryWhiteLine(a0, a0Byte); |
|||
} |
|||
|
|||
return this.FindB1ForNormalLine(a0, a0Byte); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Finds b2: The next changing element to the right of b1 on the reference line.
|
|||
/// </summary>
|
|||
/// <param name="b1">The first changing element on the reference line to the right of a0 and opposite of color to a0.</param>
|
|||
/// <returns>Position of b1.</returns>
|
|||
public int FindB2(int b1) |
|||
{ |
|||
if (this.IsEmpty) |
|||
{ |
|||
return this.FindB2ForImaginaryWhiteLine(); |
|||
} |
|||
|
|||
return this.FindB2ForNormalLine(b1); |
|||
} |
|||
|
|||
private int FindB1ForImaginaryWhiteLine(int a0, byte a0Byte) |
|||
{ |
|||
if (a0 < 0) |
|||
{ |
|||
if (a0Byte != this.whiteByte) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
return this.width; |
|||
} |
|||
|
|||
private int FindB1ForNormalLine(int a0, byte a0Byte) |
|||
{ |
|||
int offset = 0; |
|||
if (a0 < 0) |
|||
{ |
|||
if (a0Byte != this.scanLine[0]) |
|||
{ |
|||
return 0; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
offset = a0; |
|||
} |
|||
|
|||
ReadOnlySpan<byte> searchSpace = this.scanLine.Slice(offset); |
|||
byte searchByte = (byte)~a0Byte; |
|||
int index = searchSpace.IndexOf(searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
if (index != 0) |
|||
{ |
|||
return offset + index; |
|||
} |
|||
|
|||
searchByte = (byte)~searchSpace[0]; |
|||
index = searchSpace.IndexOf(searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
searchSpace = searchSpace.Slice(index); |
|||
offset += index; |
|||
index = searchSpace.IndexOf((byte)~searchByte); |
|||
if (index < 0) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
return index + offset; |
|||
} |
|||
|
|||
private int FindB2ForImaginaryWhiteLine() => this.width; |
|||
|
|||
private int FindB2ForNormalLine(int b1) |
|||
{ |
|||
if (b1 >= this.scanLine.Length) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
byte searchByte = (byte)~this.scanLine[b1]; |
|||
int offset = b1 + 1; |
|||
ReadOnlySpan<byte> searchSpace = this.scanLine.Slice(offset); |
|||
int index = searchSpace.IndexOf(searchByte); |
|||
if (index == -1) |
|||
{ |
|||
return this.scanLine.Length; |
|||
} |
|||
|
|||
return offset + index; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Diagnostics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
[DebuggerDisplay("Type = {Type}")] |
|||
internal readonly struct CcittTwoDimensionalCode |
|||
{ |
|||
private readonly ushort value; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="CcittTwoDimensionalCode"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="type">The type.</param>
|
|||
/// <param name="bitsRequired">The bits required.</param>
|
|||
/// <param name="extensionBits">The extension bits.</param>
|
|||
public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) |
|||
=> this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the code type.
|
|||
/// </summary>
|
|||
public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111); |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Enum for the different two dimensional code words for the ccitt fax compression.
|
|||
/// </summary>
|
|||
internal enum CcittTwoDimensionalCodeType |
|||
{ |
|||
/// <summary>
|
|||
/// No valid code word was read.
|
|||
/// </summary>
|
|||
None = 0, |
|||
|
|||
/// <summary>
|
|||
/// Pass mode: This mode is identified when the position of b2 lies to the left of a1.
|
|||
/// </summary>
|
|||
Pass = 1, |
|||
|
|||
/// <summary>
|
|||
/// Indicates horizontal mode.
|
|||
/// </summary>
|
|||
Horizontal = 2, |
|||
|
|||
/// <summary>
|
|||
/// Vertical 0 code word: relative distance between a1 and b1 is 0.
|
|||
/// </summary>
|
|||
Vertical0 = 3, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r1 code word: relative distance between a1 and b1 is 1, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR1 = 4, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r2 code word: relative distance between a1 and b1 is 2, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR2 = 5, |
|||
|
|||
/// <summary>
|
|||
/// Vertical r3 code word: relative distance between a1 and b1 is 3, a1 is to the right of b1.
|
|||
/// </summary>
|
|||
VerticalR3 = 6, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l1 code word: relative distance between a1 and b1 is 1, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL1 = 7, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l2 code word: relative distance between a1 and b1 is 2, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL2 = 8, |
|||
|
|||
/// <summary>
|
|||
/// Vertical l3 code word: relative distance between a1 and b1 is 3, a1 is to the left of b1.
|
|||
/// </summary>
|
|||
VerticalL3 = 9, |
|||
|
|||
/// <summary>
|
|||
/// 1d extensions code word, extension code is used to indicate the change from the current mode to another mode, e.g., another coding scheme.
|
|||
/// Not supported.
|
|||
/// </summary>
|
|||
Extensions1D = 10, |
|||
|
|||
/// <summary>
|
|||
/// 2d extensions code word, extension code is used to indicate the change from the current mode to another mode, e.g., another coding scheme.
|
|||
/// Not supported.
|
|||
/// </summary>
|
|||
Extensions2D = 11, |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.IO; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Bit reader for data encoded with the modified huffman rle method.
|
|||
/// See TIFF 6.0 specification, section 10.
|
|||
/// </summary>
|
|||
internal sealed class ModifiedHuffmanBitReader : T4BitReader |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ModifiedHuffmanBitReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="input">The compressed input stream.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) |
|||
: base(input, fillOrder, bytesToRead, allocator) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool IsEndOfScanLine |
|||
{ |
|||
get |
|||
{ |
|||
if (this.IsWhiteRun && this.CurValueBitsRead == 12 && this.Value == 1) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (this.CurValueBitsRead == 11 && this.Value == 0) |
|||
{ |
|||
// black run.
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void StartNewRow() |
|||
{ |
|||
base.StartNewRow(); |
|||
|
|||
int remainder = this.BitsRead & 7; // bit-hack for % 8
|
|||
if (remainder != 0) |
|||
{ |
|||
// Skip padding bits, move to next byte.
|
|||
this.Position++; |
|||
this.ResetBitsRead(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No EOL is expected at the start of a run for the modified huffman encoding.
|
|||
/// </summary>
|
|||
protected override void ReadEolBeforeFirstData() |
|||
{ |
|||
// Nothing to do here.
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
|
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Bit reader for reading CCITT T6 compressed fax data.
|
|||
/// See: Facsimile Coding Schemes and Coding Control Functions for Group 4 Facsimile Apparatus, itu-t recommendation t.6
|
|||
/// </summary>
|
|||
internal sealed class T6BitReader : T4BitReader |
|||
{ |
|||
private readonly int maxCodeLength = 12; |
|||
|
|||
private static readonly CcittTwoDimensionalCode None = new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.None, 0); |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len1Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len3Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, |
|||
{ 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, |
|||
{ 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len4Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len6Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, |
|||
{ 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } |
|||
}; |
|||
|
|||
private static readonly Dictionary<uint, CcittTwoDimensionalCode> Len7Codes = new Dictionary<uint, CcittTwoDimensionalCode>() |
|||
{ |
|||
{ 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, |
|||
{ 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, |
|||
{ 0b0000001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions2D, 7) }, |
|||
{ 0b0000000, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions1D, 7) } |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T6BitReader"/> class.
|
|||
/// </summary>
|
|||
/// <param name="input">The compressed input stream.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="bytesToRead">The number of bytes to read from the stream.</param>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
public T6BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) |
|||
: base(input, fillOrder, bytesToRead, allocator) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the two dimensional code.
|
|||
/// </summary>
|
|||
public CcittTwoDimensionalCode Code { get; internal set; } |
|||
|
|||
public bool ReadNextCodeWord() |
|||
{ |
|||
this.Code = None; |
|||
this.Reset(); |
|||
uint value = this.ReadValue(1); |
|||
|
|||
do |
|||
{ |
|||
if (this.CurValueBitsRead > this.maxCodeLength) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error: invalid code length read"); |
|||
} |
|||
|
|||
switch (this.CurValueBitsRead) |
|||
{ |
|||
case 1: |
|||
if (Len1Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len1Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 3: |
|||
if (Len3Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len3Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 4: |
|||
if (Len4Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len4Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 6: |
|||
if (Len6Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len6Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
|
|||
case 7: |
|||
if (Len7Codes.ContainsKey(value)) |
|||
{ |
|||
this.Code = Len7Codes[value]; |
|||
return false; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
uint currBit = this.ReadValue(1); |
|||
value = (value << 1) | currBit; |
|||
} |
|||
while (!this.IsEndOfScanLine); |
|||
|
|||
if (this.IsEndOfScanLine) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// No EOL is expected at the start of a run.
|
|||
/// </summary>
|
|||
protected override void ReadEolBeforeFirstData() |
|||
{ |
|||
// Nothing to do here.
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Swaps the white run to black run an vise versa.
|
|||
/// </summary>
|
|||
public void SwapColor() => this.IsWhiteRun = !this.IsWhiteRun; |
|||
} |
|||
} |
|||
@ -0,0 +1,254 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using SixLabors.ImageSharp.Formats.Tiff.Constants; |
|||
using SixLabors.ImageSharp.IO; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors |
|||
{ |
|||
/// <summary>
|
|||
/// Class to handle cases where TIFF image data is compressed using CCITT T6 compression.
|
|||
/// </summary>
|
|||
internal sealed class T6TiffCompression : TiffBaseDecompressor |
|||
{ |
|||
private readonly bool isWhiteZero; |
|||
|
|||
private readonly byte whiteValue; |
|||
|
|||
private readonly byte blackValue; |
|||
|
|||
private readonly int width; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="T6TiffCompression" /> class.
|
|||
/// </summary>
|
|||
/// <param name="allocator">The memory allocator.</param>
|
|||
/// <param name="fillOrder">The logical order of bits within a byte.</param>
|
|||
/// <param name="width">The image width.</param>
|
|||
/// <param name="bitsPerPixel">The number of bits per pixel.</param>
|
|||
/// <param name="photometricInterpretation">The photometric interpretation.</param>
|
|||
public T6TiffCompression( |
|||
MemoryAllocator allocator, |
|||
TiffFillOrder fillOrder, |
|||
int width, |
|||
int bitsPerPixel, |
|||
TiffPhotometricInterpretation photometricInterpretation) |
|||
: base(allocator, width, bitsPerPixel) |
|||
{ |
|||
this.FillOrder = fillOrder; |
|||
this.width = width; |
|||
this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; |
|||
this.whiteValue = (byte)(this.isWhiteZero ? 0 : 1); |
|||
this.blackValue = (byte)(this.isWhiteZero ? 1 : 0); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the logical order of bits within a byte.
|
|||
/// </summary>
|
|||
private TiffFillOrder FillOrder { get; } |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer) |
|||
{ |
|||
int height = stripHeight; |
|||
|
|||
using System.Buffers.IMemoryOwner<byte> scanLineBuffer = this.Allocator.Allocate<byte>(this.width * 2); |
|||
Span<byte> scanLine = scanLineBuffer.GetSpan().Slice(0, this.width); |
|||
Span<byte> referenceScanLineSpan = scanLineBuffer.GetSpan().Slice(this.width, this.width); |
|||
|
|||
using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); |
|||
|
|||
var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); |
|||
uint bitsWritten = 0; |
|||
for (int y = 0; y < height; y++) |
|||
{ |
|||
scanLine.Fill(0); |
|||
Decode2DScanline(bitReader, this.isWhiteZero, referenceScanLine, scanLine); |
|||
|
|||
bitsWritten = this.WriteScanLine(buffer, scanLine, bitsWritten); |
|||
|
|||
scanLine.CopyTo(referenceScanLineSpan); |
|||
referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, referenceScanLineSpan); |
|||
} |
|||
} |
|||
|
|||
private uint WriteScanLine(Span<byte> buffer, Span<byte> scanLine, uint bitsWritten) |
|||
{ |
|||
byte white = (byte)(this.isWhiteZero ? 0 : 255); |
|||
for (int i = 0; i < scanLine.Length; i++) |
|||
{ |
|||
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, 1, scanLine[i] == white ? this.whiteValue : this.blackValue); |
|||
bitsWritten++; |
|||
} |
|||
|
|||
// Write padding bytes, if necessary.
|
|||
uint remainder = bitsWritten % 8; |
|||
if (remainder != 0) |
|||
{ |
|||
uint padding = 8 - remainder; |
|||
BitWriterUtils.WriteBits(buffer, (int)bitsWritten, padding, 0); |
|||
bitsWritten += padding; |
|||
} |
|||
|
|||
return bitsWritten; |
|||
} |
|||
|
|||
private static void Decode2DScanline(T6BitReader bitReader, bool whiteIsZero, CcittReferenceScanline referenceScanline, Span<byte> scanline) |
|||
{ |
|||
int width = scanline.Length; |
|||
bitReader.StartNewRow(); |
|||
|
|||
// 2D Encoding variables.
|
|||
int a0 = -1; |
|||
byte fillByte = whiteIsZero ? (byte)0 : (byte)255; |
|||
|
|||
// Process every code word in this scanline.
|
|||
int unpacked = 0; |
|||
while (true) |
|||
{ |
|||
// Read next code word and advance pass it.
|
|||
bool isEol = bitReader.ReadNextCodeWord(); |
|||
|
|||
// Special case handling for EOL.
|
|||
if (isEol) |
|||
{ |
|||
// If a TIFF reader encounters EOFB before the expected number of lines has been extracted,
|
|||
// it is appropriate to assume that the missing rows consist entirely of white pixels.
|
|||
scanline.Fill(whiteIsZero ? (byte)0 : (byte)255); |
|||
break; |
|||
} |
|||
|
|||
// Update 2D Encoding variables.
|
|||
int b1 = referenceScanline.FindB1(a0, fillByte); |
|||
|
|||
// Switch on the code word.
|
|||
int a1; |
|||
switch (bitReader.Code.Type) |
|||
{ |
|||
case CcittTwoDimensionalCodeType.None: |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, could not read a valid code word."); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.Pass: |
|||
int b2 = referenceScanline.FindB2(b1); |
|||
scanline.Slice(unpacked, b2 - unpacked).Fill(fillByte); |
|||
unpacked = b2; |
|||
a0 = b2; |
|||
break; |
|||
case CcittTwoDimensionalCodeType.Horizontal: |
|||
// Decode M(a0a1)
|
|||
bitReader.ReadNextRun(); |
|||
int runLength = (int)bitReader.RunLength; |
|||
if (runLength > (uint)(scanline.Length - unpacked)) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); |
|||
} |
|||
|
|||
scanline.Slice(unpacked, runLength).Fill(fillByte); |
|||
unpacked += runLength; |
|||
fillByte = (byte)~fillByte; |
|||
|
|||
// Decode M(a1a2)
|
|||
bitReader.ReadNextRun(); |
|||
runLength = (int)bitReader.RunLength; |
|||
if (runLength > (uint)(scanline.Length - unpacked)) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error"); |
|||
} |
|||
|
|||
scanline.Slice(unpacked, runLength).Fill(fillByte); |
|||
unpacked += runLength; |
|||
fillByte = (byte)~fillByte; |
|||
|
|||
// Prepare next a0
|
|||
a0 = unpacked; |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.Vertical0: |
|||
a1 = b1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR1: |
|||
a1 = b1 + 1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR2: |
|||
a1 = b1 + 2; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalR3: |
|||
a1 = b1 + 3; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL1: |
|||
a1 = b1 - 1; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL2: |
|||
a1 = b1 - 2; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
case CcittTwoDimensionalCodeType.VerticalL3: |
|||
a1 = b1 - 3; |
|||
scanline.Slice(unpacked, a1 - unpacked).Fill(fillByte); |
|||
unpacked = a1; |
|||
a0 = a1; |
|||
fillByte = (byte)~fillByte; |
|||
bitReader.SwapColor(); |
|||
break; |
|||
|
|||
default: |
|||
throw new NotSupportedException("ccitt extensions are not supported."); |
|||
} |
|||
|
|||
// This line is fully unpacked. Should exit and process next line.
|
|||
if (unpacked == width) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (unpacked > width) |
|||
{ |
|||
TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, unpacked data > width"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,3 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:3e968748833a239d06879ecf100681f5f93c8c3830558c438b684d78dd5faefa |
|||
size 4258 |
|||
oid sha256:118e55fe0f0dbb5d2712337ec9456a27407f11c0eb9f7e7e81700d4d84b7db09 |
|||
size 4378 |
|||
|
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:185ae3c4174b323adcf811d125cd77b71768406845923f50395c0baebff57b7c |
|||
size 282 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:a19eb117f194718575681a81a4fbe7fe4a1b82b99113707295194090fb935784 |
|||
size 282 |
|||
@ -0,0 +1,3 @@ |
|||
version https://git-lfs.github.com/spec/v1 |
|||
oid sha256:af0d8f3c18f96228aa369bc295201a1bfe1b044c23991ff168401adc5402ebb6 |
|||
size 308 |
|||
Loading…
Reference in new issue