@ -1,20 +1,17 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System ;
using System.Buffers ;
using System.Collections.Generic ;
using System.IO ;
using System.Runtime.CompilerServices ;
using SixLabors.ImageSharp.Formats.Tiff.Constants ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.IO ;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
/// <summary>
/// Bitreader for reading compressed CCITT T4 1D data.
/// </summary>
internal class T4BitReader : IDisposable
internal class T4BitReader
{
/// <summary>
/// The logical order of bits within a byte.
@ -52,28 +49,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// </summary>
private readonly int maxCodeLength = 1 3 ;
private static readonly Dictionary < uint , uint > WhiteLen4TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen4TermCodes = new ( )
{
{ 0x7 , 2 } , { 0x8 , 3 } , { 0xB , 4 } , { 0xC , 5 } , { 0xE , 6 } , { 0xF , 7 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen5TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen5TermCodes = new ( )
{
{ 0x13 , 8 } , { 0x14 , 9 } , { 0x7 , 1 0 } , { 0x8 , 1 1 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen6TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen6TermCodes = new ( )
{
{ 0x7 , 1 } , { 0x8 , 1 2 } , { 0x3 , 1 3 } , { 0x34 , 1 4 } , { 0x35 , 1 5 } , { 0x2A , 1 6 } , { 0x2B , 1 7 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen7TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen7TermCodes = new ( )
{
{ 0x27 , 1 8 } , { 0xC , 1 9 } , { 0x8 , 2 0 } , { 0x17 , 2 1 } , { 0x3 , 2 2 } , { 0x4 , 2 3 } , { 0x28 , 2 4 } , { 0x2B , 2 5 } , { 0x13 , 2 6 } ,
{ 0x24 , 2 7 } , { 0x18 , 2 8 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen8TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen8TermCodes = new ( )
{
{ 0x35 , 0 } , { 0x2 , 2 9 } , { 0x3 , 3 0 } , { 0x1A , 3 1 } , { 0x1B , 3 2 } , { 0x12 , 3 3 } , { 0x13 , 3 4 } , { 0x14 , 3 5 } , { 0x15 , 3 6 } ,
{ 0x16 , 3 7 } , { 0x17 , 3 8 } , { 0x28 , 3 9 } , { 0x29 , 4 0 } , { 0x2A , 4 1 } , { 0x2B , 4 2 } , { 0x2C , 4 3 } , { 0x2D , 4 4 } , { 0x4 , 4 5 } ,
@ -81,57 +78,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{ 0x58 , 5 5 } , { 0x59 , 5 6 } , { 0x5A , 5 7 } , { 0x5B , 5 8 } , { 0x4A , 5 9 } , { 0x4B , 6 0 } , { 0x32 , 6 1 } , { 0x33 , 6 2 } , { 0x34 , 6 3 }
} ;
private static readonly Dictionary < uint , uint > BlackLen2TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen2TermCodes = new ( )
{
{ 0x3 , 2 } , { 0x2 , 3 }
} ;
private static readonly Dictionary < uint , uint > BlackLen3TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen3TermCodes = new ( )
{
{ 0x2 , 1 } , { 0x3 , 4 }
} ;
private static readonly Dictionary < uint , uint > BlackLen4TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen4TermCodes = new ( )
{
{ 0x3 , 5 } , { 0x2 , 6 }
} ;
private static readonly Dictionary < uint , uint > BlackLen5TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen5TermCodes = new ( )
{
{ 0x3 , 7 }
} ;
private static readonly Dictionary < uint , uint > BlackLen6TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen6TermCodes = new ( )
{
{ 0x5 , 8 } , { 0x4 , 9 }
} ;
private static readonly Dictionary < uint , uint > BlackLen7TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen7TermCodes = new ( )
{
{ 0x4 , 1 0 } , { 0x5 , 1 1 } , { 0x7 , 1 2 }
} ;
private static readonly Dictionary < uint , uint > BlackLen8TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen8TermCodes = new ( )
{
{ 0x4 , 1 3 } , { 0x7 , 1 4 }
} ;
private static readonly Dictionary < uint , uint > BlackLen9TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen9TermCodes = new ( )
{
{ 0x18 , 1 5 }
} ;
private static readonly Dictionary < uint , uint > BlackLen10TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen10TermCodes = new ( )
{
{ 0x37 , 0 } , { 0x17 , 1 6 } , { 0x18 , 1 7 } , { 0x8 , 1 8 }
} ;
private static readonly Dictionary < uint , uint > BlackLen11TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen11TermCodes = new ( )
{
{ 0x67 , 1 9 } , { 0x68 , 2 0 } , { 0x6C , 2 1 } , { 0x37 , 2 2 } , { 0x28 , 2 3 } , { 0x17 , 2 4 } , { 0x18 , 2 5 }
} ;
private static readonly Dictionary < uint , uint > BlackLen12TermCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen12TermCodes = new ( )
{
{ 0xCA , 2 6 } , { 0xCB , 2 7 } , { 0xCC , 2 8 } , { 0xCD , 2 9 } , { 0x68 , 3 0 } , { 0x69 , 3 1 } , { 0x6A , 3 2 } , { 0x6B , 3 3 } , { 0xD2 , 3 4 } ,
{ 0xD3 , 3 5 } , { 0xD4 , 3 6 } , { 0xD5 , 3 7 } , { 0xD6 , 3 8 } , { 0xD7 , 3 9 } , { 0x6C , 4 0 } , { 0x6D , 4 1 } , { 0xDA , 4 2 } , { 0xDB , 4 3 } ,
@ -140,82 +137,84 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{ 0x66 , 6 2 } , { 0x67 , 6 3 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen5MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen5MakeupCodes = new ( )
{
{ 0x1B , 6 4 } , { 0x12 , 1 2 8 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen6MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen6MakeupCodes = new ( )
{
{ 0x17 , 1 9 2 } , { 0x18 , 1 6 6 4 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen8MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen8MakeupCodes = new ( )
{
{ 0x36 , 3 2 0 } , { 0x37 , 3 8 4 } , { 0x64 , 4 4 8 } , { 0x65 , 5 1 2 } , { 0x68 , 5 7 6 } , { 0x67 , 6 4 0 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen7MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen7MakeupCodes = new ( )
{
{ 0x37 , 2 5 6 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen9MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen9MakeupCodes = new ( )
{
{ 0xCC , 7 0 4 } , { 0xCD , 7 6 8 } , { 0xD2 , 8 3 2 } , { 0xD3 , 8 9 6 } , { 0xD4 , 9 6 0 } , { 0xD5 , 1 0 2 4 } , { 0xD6 , 1 0 8 8 } ,
{ 0xD7 , 1 1 5 2 } , { 0xD8 , 1 2 1 6 } , { 0xD9 , 1 2 8 0 } , { 0xDA , 1 3 4 4 } , { 0xDB , 1 4 0 8 } , { 0x98 , 1 4 7 2 } , { 0x99 , 1 5 3 6 } ,
{ 0x9A , 1 6 0 0 } , { 0x9B , 1 7 2 8 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen11MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen11MakeupCodes = new ( )
{
{ 0x8 , 1 7 9 2 } , { 0xC , 1 8 5 6 } , { 0xD , 1 9 2 0 }
} ;
private static readonly Dictionary < uint , uint > WhiteLen12MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > WhiteLen12MakeupCodes = new ( )
{
{ 0x12 , 1 9 8 4 } , { 0x13 , 2 0 4 8 } , { 0x14 , 2 1 1 2 } , { 0x15 , 2 1 7 6 } , { 0x16 , 2 2 4 0 } , { 0x17 , 2 3 0 4 } , { 0x1C , 2 3 6 8 } ,
{ 0x1D , 2 4 3 2 } , { 0x1E , 2 4 9 6 } , { 0x1F , 2 5 6 0 }
} ;
private static readonly Dictionary < uint , uint > BlackLen10MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen10MakeupCodes = new ( )
{
{ 0xF , 6 4 }
} ;
private static readonly Dictionary < uint , uint > BlackLen11MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen11MakeupCodes = new ( )
{
{ 0x8 , 1 7 9 2 } , { 0xC , 1 8 5 6 } , { 0xD , 1 9 2 0 }
} ;
private static readonly Dictionary < uint , uint > BlackLen12MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen12MakeupCodes = new ( )
{
{ 0xC8 , 1 2 8 } , { 0xC9 , 1 9 2 } , { 0x5B , 2 5 6 } , { 0x33 , 3 2 0 } , { 0x34 , 3 8 4 } , { 0x35 , 4 4 8 } ,
{ 0x12 , 1 9 8 4 } , { 0x13 , 2 0 4 8 } , { 0x14 , 2 1 1 2 } , { 0x15 , 2 1 7 6 } , { 0x16 , 2 2 4 0 } , { 0x17 , 2 3 0 4 } , { 0x1C , 2 3 6 8 } ,
{ 0x1D , 2 4 3 2 } , { 0x1E , 2 4 9 6 } , { 0x1F , 2 5 6 0 }
} ;
private static readonly Dictionary < uint , uint > BlackLen13MakeupCodes = new Dictionary < uint , uint > ( )
private static readonly Dictionary < uint , uint > BlackLen13MakeupCodes = new ( )
{
{ 0x6C , 5 1 2 } , { 0x6D , 5 7 6 } , { 0x4A , 6 4 0 } , { 0x4B , 7 0 4 } , { 0x4C , 7 6 8 } , { 0x4D , 8 3 2 } , { 0x72 , 8 9 6 } ,
{ 0x73 , 9 6 0 } , { 0x74 , 1 0 2 4 } , { 0x75 , 1 0 8 8 } , { 0x76 , 1 1 5 2 } , { 0x77 , 1 2 1 6 } , { 0x52 , 1 2 8 0 } , { 0x53 , 1 3 4 4 } ,
{ 0x54 , 1 4 0 8 } , { 0x55 , 1 4 7 2 } , { 0x5A , 1 5 3 6 } , { 0x5B , 1 6 0 0 } , { 0x64 , 1 6 6 4 } , { 0x65 , 1 7 2 8 }
} ;
/// <summary>
/// The compressed input stream.
/// </summary>
private readonly BufferedReadStream stream ;
/// <summary>
/// Initializes a new instance of the <see cref="T4BitReader" /> 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>
/// <param name="eolPadding">Indicates, if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary. Defaults to false.</param>
public T4BitReader ( Stream input , TiffFillOrder fillOrder , int bytesToRead , MemoryAllocator allocator , bool eolPadding = false )
public T4BitReader ( BufferedRead Stream input , TiffFillOrder fillOrder , int bytesToRead , bool eolPadding = false )
{
this . stream = input ;
this . fillOrder = fillOrder ;
this . Data = allocator . Allocate < byte > ( bytesToRead ) ;
this . ReadImageDataFromStream ( input , bytesToRead ) ;
this . DataLength = bytesToRead ;
this . BitsRead = 0 ;
this . Value = 0 ;
@ -228,12 +227,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
this . RunLength = 0 ;
this . eolPadding = eolPadding ;
this . ReadNextByte ( ) ;
if ( this . eolPadding )
{
this . maxCodeLength = 2 4 ;
}
}
/// <summary>
/// Gets or sets the byte at the given position.
/// </summary>
private byte DataAtPosition { get ; set ; }
/// <summary>
/// Gets the current value.
/// </summary>
@ -259,11 +265,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// </summary>
protected ulong Position { get ; set ; }
/// <summary>
/// Gets the compressed image data.
/// </summary>
public IMemoryOwner < byte > Data { get ; }
/// <summary>
/// Gets a value indicating whether there is more data to read left.
/// </summary>
@ -390,9 +391,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
this . terminationCodeFound = false ;
}
/// <inheritdoc/>
public void Dispose ( ) = > this . Data . Dispose ( ) ;
/// <summary>
/// An EOL is expected before the first data.
/// </summary>
@ -436,6 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// </summary>
/// <param name="nBits">The number of bits to read.</param>
/// <returns>The value read.</returns>
[MethodImpl(InliningOptions.ShortMethod)]
protected uint ReadValue ( int nBits )
{
DebugGuard . MustBeGreaterThan ( nBits , 0 , nameof ( nBits ) ) ;
@ -452,6 +451,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
return v ;
}
/// <summary>
/// Advances the position by one byte.
/// </summary>
/// <returns>True, if data could be advanced by one byte, otherwise false.</returns>
protected bool AdvancePosition ( )
{
if ( this . LoadNewByte ( ) )
{
return true ;
}
return false ;
}
private uint WhiteTerminatingCodeRunLength ( )
{
switch ( this . CurValueBitsRead )
@ -806,44 +819,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
return false ;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint GetBit ( )
{
if ( this . BitsRead > = 8 )
{
this . LoadNewByte ( ) ;
this . AdvancePosition ( ) ;
}
Span < byte > dataSpan = this . Data . GetSpan ( ) ;
int shift = 8 - this . BitsRead - 1 ;
uint bit = ( uint ) ( ( dataSpan [ ( int ) this . Position ] & ( 1 < < shift ) ) ! = 0 ? 1 : 0 ) ;
uint bit = ( uint ) ( ( this . DataAt Position & ( 1 < < shift ) ) ! = 0 ? 1 : 0 ) ;
this . BitsRead + + ;
return bit ;
}
private void LoadNewByte ( )
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool LoadNewByte ( )
{
this . Position + + ;
this . ResetBitsRead ( ) ;
if ( this . Position > = ( ulong ) this . DataLength )
if ( this . Position < ( ulong ) this . DataLength )
{
TiffThrowHelper . ThrowImageFormatException ( "tiff image has invalid ccitt compressed data" ) ;
this . ReadNextByte ( ) ;
this . Position + + ;
return true ;
}
this . Position + + ;
this . DataAtPosition = 0 ;
return false ;
}
private void ReadImageDataFromStream ( Stream input , int bytesToRead )
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadNextByte ( )
{
Span < byte > dataSpan = this . Data . GetSpan ( ) ;
input . Read ( dataSpan , 0 , bytesToRead ) ;
if ( this . fillOrder = = TiffFillOrder . LeastSignificantBitFirst )
int nextByte = this . stream . ReadByte ( ) ;
if ( nextByte = = - 1 )
{
for ( int i = 0 ; i < dataSpan . Length ; i + + )
{
dataSpan [ i ] = ReverseBits ( dataSpan [ i ] ) ;
}
TiffThrowHelper . ThrowImageFormatException ( "Tiff fax compression error: not enough data." ) ;
}
this . ResetBitsRead ( ) ;
this . DataAtPosition = this . fillOrder = = TiffFillOrder . LeastSignificantBitFirst
? ReverseBits ( ( byte ) nextByte )
: ( byte ) nextByte ;
}
// http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits