@ -3,7 +3,6 @@
using System.Buffers ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
using SixLabors.ImageSharp.IO ;
using SixLabors.ImageSharp.Memory ;
@ -37,22 +36,22 @@ internal sealed class LzwDecoder : IDisposable
/// <summary>
/// The prefix buffer.
/// </summary>
private readonly IMemoryOwner < int > prefix ;
private readonly IMemoryOwner < int > prefixOwner ;
/// <summary>
/// The suffix buffer.
/// </summary>
private readonly IMemoryOwner < int > suffix ;
private readonly IMemoryOwner < int > suffixOwner ;
/// <summary>
/// The scratch buffer for reading data blocks.
/// </summary>
private readonly IMemoryOwner < byte > scratchBuff er;
private readonly IMemoryOwner < byte > bufferOwn er;
/// <summary>
/// The pixel stack buffer.
/// </summary>
private readonly IMemoryOwner < int > pixelStack ;
private readonly IMemoryOwner < int > pixelStackOwner ;
private readonly int minCodeSize ;
private readonly int clearCode ;
private readonly int endCode ;
@ -79,11 +78,12 @@ internal sealed class LzwDecoder : IDisposable
public LzwDecoder ( MemoryAllocator memoryAllocator , BufferedReadStream stream , int minCodeSize )
{
this . stream = stream ? ? throw new ArgumentNullException ( nameof ( stream ) ) ;
Guard . IsTrue ( IsValidMinCodeSize ( minCodeSize ) , nameof ( minCodeSize ) , "Invalid minimum code size." ) ;
this . prefix = memoryAllocator . Allocate < int > ( MaxStackSize , AllocationOptions . Clean ) ;
this . suffix = memoryAllocator . Allocate < int > ( MaxStackSize , AllocationOptions . Clean ) ;
this . pixelStack = memoryAllocator . Allocate < int > ( MaxStackSize + 1 , AllocationOptions . Clean ) ;
this . scratchBuff er = memoryAllocator . Allocate < byte > ( byte . MaxValue , AllocationOptions . None ) ;
this . prefixOwner = memoryAllocator . Allocate < int > ( MaxStackSize , AllocationOptions . Clean ) ;
this . suffixOwner = memoryAllocator . Allocate < int > ( MaxStackSize , AllocationOptions . Clean ) ;
this . pixelStackOwner = memoryAllocator . Allocate < int > ( MaxStackSize + 1 , AllocationOptions . Clean ) ;
this . bufferOwn er = memoryAllocator . Allocate < byte > ( byte . MaxValue , AllocationOptions . None ) ;
this . minCodeSize = minCodeSize ;
// Calculate the clear code. The value of the clear code is 2 ^ minCodeSize
@ -93,11 +93,15 @@ internal sealed class LzwDecoder : IDisposable
this . endCode = this . clearCode + 1 ;
this . availableCode = this . clearCode + 2 ;
ref int suffixRef = ref MemoryMarshal . GetReference ( this . suffix . GetSpan ( ) ) ;
for ( this . code = 0 ; this . code < this . clearCode ; this . code + + )
// Fill the suffix buffer with the initial values represented by the number of colors.
Span < int > suffix = this . suffixOwner . GetSpan ( ) [ . . this . clearCode ] ;
int i ;
for ( i = 0 ; i < suffix . Length ; i + + )
{
Unsafe . Add ( ref suffixRef , ( uint ) this . code ) = ( byte ) this . code ;
suffix [ i ] = i ;
}
this . code = i ;
}
/// <summary>
@ -112,8 +116,7 @@ internal sealed class LzwDecoder : IDisposable
// It is possible to specify a larger LZW minimum code size than the palette length in bits
// which may leave a gap in the codes where no colors are assigned.
// http://www.matthewflickinger.com/lab/whatsinagif/lzw_image_data.asp#lzw_compression
int clearCode = 1 < < minCodeSize ;
if ( minCodeSize < 2 | | minCodeSize > MaximumLzwBits | | clearCode > MaxStackSize )
if ( minCodeSize < 2 | | minCodeSize > MaximumLzwBits | | 1 < < minCodeSize > MaxStackSize )
{
// Don't attempt to decode the frame indices.
// Theoretically we could determine a min code size from the length of the provided
@ -132,112 +135,139 @@ internal sealed class LzwDecoder : IDisposable
{
indices . Clear ( ) ;
ref byte pixelsRowRef = ref MemoryMarshal . GetReference ( indices ) ;
ref int prefixRef = ref MemoryMarshal . GetReference ( this . prefix . GetSpan ( ) ) ;
ref int suffixRef = ref MemoryMarshal . GetReference ( this . suffix . GetSpan ( ) ) ;
ref int pixelStackRef = ref MemoryMarshal . GetReference ( this . pixelStack . GetSpan ( ) ) ;
Span < byte > buffer = this . scratchBuffer . GetSpan ( ) ;
int x = 0 ;
int xyz = 0 ;
while ( xyz < indices . Length )
// Get span values from the owners.
Span < int > prefix = this . prefixOwner . GetSpan ( ) ;
Span < int > suffix = this . suffixOwner . GetSpan ( ) ;
Span < int > pixelStack = this . pixelStackOwner . GetSpan ( ) ;
Span < byte > buffer = this . bufferOwner . GetSpan ( ) ;
// Cache frequently accessed instance fields into locals.
// This helps avoid repeated field loads inside the tight loop.
BufferedReadStream stream = this . stream ;
int top = this . top ;
int bits = this . bits ;
int codeSize = this . codeSize ;
int codeMask = this . codeMask ;
int minCodeSize = this . minCodeSize ;
int availableCode = this . availableCode ;
int oldCode = this . oldCode ;
int first = this . first ;
int data = this . data ;
int count = this . count ;
int bufferIndex = this . bufferIndex ;
int code = this . code ;
int clearCode = this . clearCode ;
int endCode = this . endCode ;
int i = 0 ;
while ( i < indices . Length )
{
if ( this . top = = 0 )
if ( top = = 0 )
{
if ( this . bits < this . codeSize )
if ( bits < codeSize )
{
// Load bytes until there are enough bits for a code.
if ( this . count = = 0 )
if ( count = = 0 )
{
// Read a new data block.
this . count = this . ReadBlock ( buffer ) ;
if ( this . count = = 0 )
count = ReadBlock ( stream , buffer ) ;
if ( count = = 0 )
{
break ;
}
this . bufferIndex = 0 ;
bufferIndex = 0 ;
}
this . data + = buffer [ this . bufferIndex ] < < this . bits ;
this . bits + = 8 ;
this . bufferIndex + + ;
this . count - - ;
data + = buffer [ bufferIndex ] < < bits ;
bits + = 8 ;
bufferIndex + + ;
count - - ;
continue ;
}
// Get the next code
this . code = this . data & this . codeMask ;
this . data > > = this . codeSize ;
this . bits - = this . codeSize ;
code = data & codeMask ;
data > > = codeSize ;
bits - = codeSize ;
// Interpret the code
if ( this . code > this . availableCode | | this . code = = this . endCode )
if ( code > availableCode | | code = = endCode )
{
break ;
}
if ( this . code = = this . clearCode )
if ( code = = clearCode )
{
// Reset the decoder
this . codeSize = this . minCodeSize + 1 ;
this . codeMask = ( 1 < < this . codeSize ) - 1 ;
this . availableCode = this . clearCode + 2 ;
this . oldCode = NullCode ;
codeSize = minCodeSize + 1 ;
codeMask = ( 1 < < codeSize ) - 1 ;
availableCode = clearCode + 2 ;
oldCode = NullCode ;
continue ;
}
if ( this . oldCode = = NullCode )
if ( oldCode = = NullCode )
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . oldCode = this . code ;
this . first = this . code ;
pixelStack [ top + + ] = suffix [ code ] ;
oldCode = code ;
first = code ;
continue ;
}
int inCode = this . code ;
if ( this . code = = this . availableCode )
int inCode = code ;
if ( code = = availableCode )
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = ( byte ) this . first ;
this . code = this . oldCode ;
pixelStack [ top + + ] = first ;
code = oldCode ;
}
while ( this . code > this . clearCod e)
while ( code > clearCode & & top < MaxStackSiz e)
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . code = Unsafe . Add ( ref prefixRef , ( uint ) this . code ) ;
pixelStack [ top + + ] = suffix [ code ] ;
code = prefix [ code ] ;
}
int suffixCode = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . first = suffixCode ;
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = suffixCode ;
int suffixCode = suffix [ code ] ;
first = suffixCode ;
pixelStack [ top + + ] = suffixCode ;
// Fix for Gifs that have "deferred clear code" as per here :
// Fix for GIFs that have "deferred clear code" as per :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if ( this . availableCode < MaxStackSize )
if ( availableCode < MaxStackSize )
{
Unsafe . Add ( ref prefixRef , ( uint ) this . availableCode ) = this . oldCode ;
Unsafe . Add ( ref suffixRef , ( uint ) this . availableCode ) = this . first ;
this . availableCode + + ;
if ( this . availableCode = = this . codeMask + 1 & & this . availableCode < MaxStackSize )
prefix [ availableCode ] = oldCode ;
suffix [ availableCode ] = first ;
availableCode + + ;
if ( availableCode = = codeMask + 1 & & availableCode < MaxStackSize )
{
this . codeSize + + ;
this . codeMask = ( 1 < < this . codeSize ) - 1 ;
codeSize + + ;
codeMask = ( 1 < < codeSize ) - 1 ;
}
}
this . oldCode = inCode ;
oldCode = inCode ;
}
// Pop a pixel off the pixel stack.
this . top - - ;
top - - ;
// Clear missing pixels
xyz + + ;
Unsafe . Add ( ref pixelsRowRef , ( uint ) x + + ) = ( byte ) Unsafe . Add ( ref pixelStackRef , ( uint ) this . top ) ;
// Clear missing pixels.
indices [ i + + ] = ( byte ) pixelStack [ top ] ;
}
// Write back the local values to the instance fields.
this . top = top ;
this . bits = bits ;
this . codeSize = codeSize ;
this . codeMask = codeMask ;
this . availableCode = availableCode ;
this . oldCode = oldCode ;
this . first = first ;
this . data = data ;
this . count = count ;
this . bufferIndex = bufferIndex ;
this . code = code ;
}
/// <summary>
@ -246,130 +276,161 @@ internal sealed class LzwDecoder : IDisposable
/// <param name="length">The resulting index table length.</param>
public void SkipIndices ( int length )
{
ref int prefixRef = ref MemoryMarshal . GetReference ( this . prefix . GetSpan ( ) ) ;
ref int suffixRef = ref MemoryMarshal . GetReference ( this . suffix . GetSpan ( ) ) ;
ref int pixelStackRef = ref MemoryMarshal . GetReference ( this . pixelStack . GetSpan ( ) ) ;
Span < byte > buffer = this . scratchBuffer . GetSpan ( ) ;
int xyz = 0 ;
while ( xyz < length )
// Get span values from the owners.
Span < int > prefix = this . prefixOwner . GetSpan ( ) ;
Span < int > suffix = this . suffixOwner . GetSpan ( ) ;
Span < int > pixelStack = this . pixelStackOwner . GetSpan ( ) ;
Span < byte > buffer = this . bufferOwner . GetSpan ( ) ;
// Cache frequently accessed instance fields into locals.
// This helps avoid repeated field loads inside the tight loop.
BufferedReadStream stream = this . stream ;
int top = this . top ;
int bits = this . bits ;
int codeSize = this . codeSize ;
int codeMask = this . codeMask ;
int minCodeSize = this . minCodeSize ;
int availableCode = this . availableCode ;
int oldCode = this . oldCode ;
int first = this . first ;
int data = this . data ;
int count = this . count ;
int bufferIndex = this . bufferIndex ;
int code = this . code ;
int clearCode = this . clearCode ;
int endCode = this . endCode ;
int i = 0 ;
while ( i < length )
{
if ( this . top = = 0 )
if ( top = = 0 )
{
if ( this . bits < this . codeSize )
if ( bits < codeSize )
{
// Load bytes until there are enough bits for a code.
if ( this . count = = 0 )
if ( count = = 0 )
{
// Read a new data block.
this . count = this . ReadBlock ( buffer ) ;
if ( this . count = = 0 )
count = ReadBlock ( stream , buffer ) ;
if ( count = = 0 )
{
break ;
}
this . bufferIndex = 0 ;
bufferIndex = 0 ;
}
this . data + = buffer [ this . bufferIndex ] < < this . bits ;
this . bits + = 8 ;
this . bufferIndex + + ;
this . count - - ;
data + = buffer [ bufferIndex ] < < bits ;
bits + = 8 ;
bufferIndex + + ;
count - - ;
continue ;
}
// Get the next code
this . code = this . data & this . codeMask ;
this . data > > = this . codeSize ;
this . bits - = this . codeSize ;
code = data & codeMask ;
data > > = codeSize ;
bits - = codeSize ;
// Interpret the code
if ( this . code > this . availableCode | | this . code = = this . endCode )
if ( code > availableCode | | code = = endCode )
{
break ;
}
if ( this . code = = this . clearCode )
if ( code = = clearCode )
{
// Reset the decoder
this . codeSize = this . minCodeSize + 1 ;
this . codeMask = ( 1 < < this . codeSize ) - 1 ;
this . availableCode = this . clearCode + 2 ;
this . oldCode = NullCode ;
codeSize = minCodeSize + 1 ;
codeMask = ( 1 < < codeSize ) - 1 ;
availableCode = clearCode + 2 ;
oldCode = NullCode ;
continue ;
}
if ( this . oldCode = = NullCode )
if ( oldCode = = NullCode )
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . oldCode = this . code ;
this . first = this . code ;
pixelStack [ top + + ] = suffix [ code ] ;
oldCode = code ;
first = code ;
continue ;
}
int inCode = this . code ;
if ( this . code = = this . availableCode )
int inCode = code ;
if ( code = = availableCode )
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = ( byte ) this . first ;
this . code = this . oldCode ;
pixelStack [ top + + ] = first ;
code = oldCode ;
}
while ( this . code > this . clearCod e)
while ( code > clearCode & & top < MaxStackSiz e)
{
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . code = Unsafe . Add ( ref prefixRef , ( uint ) this . code ) ;
pixelStack [ top + + ] = suffix [ code ] ;
code = prefix [ code ] ;
}
int suffixCode = Unsafe . Add ( ref suffixRef , ( uint ) this . code ) ;
this . first = suffixCode ;
Unsafe . Add ( ref pixelStackRef , ( uint ) this . top + + ) = suffixCode ;
int suffixCode = suffix [ code ] ;
first = suffixCode ;
pixelStack [ top + + ] = suffixCode ;
// Fix for Gifs that have "deferred clear code" as per here :
// Fix for GIFs that have "deferred clear code" as per :
// https://bugzilla.mozilla.org/show_bug.cgi?id=55918
if ( this . availableCode < MaxStackSize )
if ( availableCode < MaxStackSize )
{
Unsafe . Add ( ref prefixRef , ( uint ) this . availableCode ) = this . oldCode ;
Unsafe . Add ( ref suffixRef , ( uint ) this . availableCode ) = this . first ;
this . availableCode + + ;
if ( this . availableCode = = this . codeMask + 1 & & this . availableCode < MaxStackSize )
prefix [ availableCode ] = oldCode ;
suffix [ availableCode ] = first ;
availableCode + + ;
if ( availableCode = = codeMask + 1 & & availableCode < MaxStackSize )
{
this . codeSize + + ;
this . codeMask = ( 1 < < this . codeSize ) - 1 ;
codeSize + + ;
codeMask = ( 1 < < codeSize ) - 1 ;
}
}
this . oldCode = inCode ;
oldCode = inCode ;
}
// Pop a pixel off the pixel stack.
this . top - - ;
top - - ;
// Clear missing pixels
xyz + + ;
// Skip missing pixels.
i + + ;
}
// Write back the local values to the instance fields.
this . top = top ;
this . bits = bits ;
this . codeSize = codeSize ;
this . codeMask = codeMask ;
this . availableCode = availableCode ;
this . oldCode = oldCode ;
this . first = first ;
this . data = data ;
this . count = count ;
this . bufferIndex = bufferIndex ;
this . code = code ;
}
/// <summary>
/// Reads the next data block from the stream. A data block begins with a byte,
/// which defines the size of the block, followed by the block itself.
/// </summary>
/// <param name="stream">The stream to read from.</param>
/// <param name="buffer">The buffer to store the block in.</param>
/// <returns>
/// The <see cref="int"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int ReadBlock ( Span < byte > buffer )
private static int ReadBlock ( BufferedReadStream stream , Span < byte > buffer )
{
int bufferSize = this . stream . ReadByte ( ) ;
int bufferSize = stream . ReadByte ( ) ;
if ( bufferSize < 1 )
{
return 0 ;
}
int count = this . stream . Read ( buffer , 0 , bufferSize ) ;
int count = stream . Read ( buffer , 0 , bufferSize ) ;
return count ! = bufferSize ? 0 : bufferSize ;
}
@ -377,9 +438,9 @@ internal sealed class LzwDecoder : IDisposable
/// <inheritdoc />
public void Dispose ( )
{
this . prefix . Dispose ( ) ;
this . suffix . Dispose ( ) ;
this . pixelStack . Dispose ( ) ;
this . scratchBuff er. Dispose ( ) ;
this . prefixOwner . Dispose ( ) ;
this . suffixOwner . Dispose ( ) ;
this . pixelStackOwner . Dispose ( ) ;
this . bufferOwn er. Dispose ( ) ;
}
}