@ -6,7 +6,9 @@ using System.Buffers;
using System.Numerics ;
using System.Numerics ;
using System.Runtime.CompilerServices ;
using System.Runtime.CompilerServices ;
using System.Runtime.InteropServices ;
using System.Runtime.InteropServices ;
using SixLabors.ImageSharp.Common.Helpers ;
using SixLabors.ImageSharp.Formats.Webp.BitWriter ;
using SixLabors.ImageSharp.Formats.Webp.BitWriter ;
using SixLabors.ImageSharp.Formats.Webp.Chunks ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.Metadata ;
using SixLabors.ImageSharp.Metadata ;
using SixLabors.ImageSharp.Metadata.Profiles.Exif ;
using SixLabors.ImageSharp.Metadata.Profiles.Exif ;
@ -235,26 +237,60 @@ internal class Vp8LEncoder : IDisposable
/// </summary>
/// </summary>
public Vp8LHashChain HashChain { get ; }
public Vp8LHashChain HashChain { get ; }
/// <summary>
public void EncodeHeader < TPixel > ( Image < TPixel > image , Stream stream , bool hasAnimation )
/// Encodes the image as lossless webp to the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode < TPixel > ( Image < TPixel > image , Stream stream )
where TPixel : unmanaged , IPixel < TPixel >
where TPixel : unmanaged , IPixel < TPixel >
{
{
int width = image . Width ;
// Write bytes from the bitwriter buffer to the stream.
int height = image . Height ;
ImageMetadata metadata = image . Metadata ;
ImageMetadata metadata = image . Metadata ;
metadata . SyncProfiles ( ) ;
metadata . SyncProfiles ( ) ;
ExifProfile exifProfile = this . skipMetadata ? null : metadata . ExifProfile ;
ExifProfile exifProfile = this . skipMetadata ? null : metadata . ExifProfile ;
XmpProfile xmpProfile = this . skipMetadata ? null : metadata . XmpProfile ;
XmpProfile xmpProfile = this . skipMetadata ? null : metadata . XmpProfile ;
BitWriterBase . WriteTrunksBeforeData (
stream ,
( uint ) image . Width ,
( uint ) image . Height ,
exifProfile ,
xmpProfile ,
metadata . IccProfile ,
false ,
hasAnimation ) ;
if ( hasAnimation )
{
WebpMetadata webpMetadata = metadata . GetWebpMetadata ( ) ;
BitWriterBase . WriteAnimationParameter ( stream , webpMetadata . AnimationBackground , webpMetadata . AnimationLoopCount ) ;
}
}
public void EncodeFooter < TPixel > ( Image < TPixel > image , Stream stream )
where TPixel : unmanaged , IPixel < TPixel >
{
// Write bytes from the bitwriter buffer to the stream.
ImageMetadata metadata = image . Metadata ;
ExifProfile exifProfile = this . skipMetadata ? null : metadata . ExifProfile ;
XmpProfile xmpProfile = this . skipMetadata ? null : metadata . XmpProfile ;
BitWriterBase . WriteTrunksAfterData ( stream , exifProfile , xmpProfile ) ;
}
/// <summary>
/// Encodes the image as lossless webp to the specified stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="frame">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
/// <param name="hasAnimation">Flag indicating, if an animation parameter is present.</param>
public void Encode < TPixel > ( ImageFrame < TPixel > frame , Stream stream , bool hasAnimation )
where TPixel : unmanaged , IPixel < TPixel >
{
int width = frame . Width ;
int height = frame . Height ;
// Convert image pixels to bgra array.
// Convert image pixels to bgra array.
bool hasAlpha = this . ConvertPixelsToBgra ( image , width , height ) ;
bool hasAlpha = this . ConvertPixelsToBgra ( fram e, width , height ) ;
// Write the image size.
// Write the image size.
this . WriteImageSize ( width , height ) ;
this . WriteImageSize ( width , height ) ;
@ -263,35 +299,60 @@ internal class Vp8LEncoder : IDisposable
this . WriteAlphaAndVersion ( hasAlpha ) ;
this . WriteAlphaAndVersion ( hasAlpha ) ;
// Encode the main image stream.
// Encode the main image stream.
this . EncodeStream ( image ) ;
this . EncodeStream ( frame ) ;
this . bitWriter . Finish ( ) ;
long prevPosition = 0 ;
if ( hasAnimation )
{
WebpFrameMetadata frameMetadata = frame . Metadata . GetWebpMetadata ( ) ;
// TODO: If we can clip the indexed frame for transparent bounds we can set properties here.
prevPosition = new WebpFrameData (
0 ,
0 ,
( uint ) frame . Width ,
( uint ) frame . Height ,
frameMetadata . FrameDelay ,
frameMetadata . BlendMethod ,
frameMetadata . DisposalMethod )
. WriteHeaderTo ( stream ) ;
}
// Write bytes from the bitwriter buffer to the stream.
// Write bytes from the bitwriter buffer to the stream.
this . bitWriter . WriteEncodedImageToStream ( stream , exifProfile , xmpProfile , metadata . IccProfile , ( uint ) width , ( uint ) height , hasAlpha ) ;
this . bitWriter . WriteEncodedImageToStream ( stream ) ;
if ( hasAnimation )
{
RiffHelper . EndWriteChunk ( stream , prevPosition ) ;
}
}
}
/// <summary>
/// <summary>
/// Encodes the alpha image data using the webp lossless compression.
/// Encodes the alpha image data using the webp lossless compression.
/// </summary>
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="frame">The <see cref="ImageFram e{TPixel}"/> to encode from.</param>
/// <param name="alphaData">The destination buffer to write the encoded alpha data to.</param>
/// <param name="alphaData">The destination buffer to write the encoded alpha data to.</param>
/// <returns>The size of the compressed data in bytes.
/// <returns>The size of the compressed data in bytes.
/// If the size of the data is the same as the pixel count, the compression would not yield in smaller data and is left uncompressed.
/// If the size of the data is the same as the pixel count, the compression would not yield in smaller data and is left uncompressed.
/// </returns>
/// </returns>
public int EncodeAlphaImageData < TPixel > ( Image < TPixel > image , IMemoryOwner < byte > alphaData )
public int EncodeAlphaImageData < TPixel > ( ImageFrame < TPixel > fram e, IMemoryOwner < byte > alphaData )
where TPixel : unmanaged , IPixel < TPixel >
where TPixel : unmanaged , IPixel < TPixel >
{
{
int width = image . Width ;
int width = fram e. Width ;
int height = image . Height ;
int height = fram e. Height ;
int pixelCount = width * height ;
int pixelCount = width * height ;
// Convert image pixels to bgra array.
// Convert image pixels to bgra array.
this . ConvertPixelsToBgra ( imag e, width , height ) ;
this . ConvertPixelsToBgra ( fram e, width , height ) ;
// The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
// The image-stream will NOT contain any headers describing the image dimension, the dimension is already known.
this . EncodeStream ( imag e) ;
this . EncodeStream ( fram e) ;
this . bitWriter . Finish ( ) ;
this . bitWriter . Finish ( ) ;
int size = this . bitWriter . NumBytes ( ) ;
int size = this . bitWriter . NumBytes ;
if ( size > = pixelCount )
if ( size > = pixelCount )
{
{
// Compressing would not yield in smaller data -> leave the data uncompressed.
// Compressing would not yield in smaller data -> leave the data uncompressed.
@ -333,12 +394,12 @@ internal class Vp8LEncoder : IDisposable
/// Encodes the image stream using lossless webp format.
/// Encodes the image stream using lossless webp format.
/// </summary>
/// </summary>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <param name="image">The imag e to encode.</param>
/// <param name="frame">The fram e to encode.</param>
private void EncodeStream < TPixel > ( Image < TPixel > imag e)
private void EncodeStream < TPixel > ( ImageFrame < TPixel > fram e)
where TPixel : unmanaged , IPixel < TPixel >
where TPixel : unmanaged , IPixel < TPixel >
{
{
int width = imag e. Width ;
int width = fram e. Width ;
int height = imag e. Height ;
int height = fram e. Height ;
Span < uint > bgra = this . Bgra . GetSpan ( ) ;
Span < uint > bgra = this . Bgra . GetSpan ( ) ;
Span < uint > encodedData = this . EncodedData . GetSpan ( ) ;
Span < uint > encodedData = this . EncodedData . GetSpan ( ) ;
@ -425,9 +486,9 @@ internal class Vp8LEncoder : IDisposable
lowEffort ) ;
lowEffort ) ;
// If we are better than what we already have.
// If we are better than what we already have.
if ( isFirstConfig | | this . bitWriter . NumBytes ( ) < bestSize )
if ( isFirstConfig | | this . bitWriter . NumBytes < bestSize )
{
{
bestSize = this . bitWriter . NumBytes ( ) ;
bestSize = this . bitWriter . NumBytes ;
BitWriterSwap ( ref this . bitWriter , ref bitWriterBest ) ;
BitWriterSwap ( ref this . bitWriter , ref bitWriterBest ) ;
}
}
@ -447,14 +508,14 @@ internal class Vp8LEncoder : IDisposable
/// Converts the pixels of the image to bgra.
/// Converts the pixels of the image to bgra.
/// </summary>
/// </summary>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <typeparam name="TPixel">The type of the pixels.</typeparam>
/// <param name="image">The imag e to convert.</param>
/// <param name="frame">The fram e to convert.</param>
/// <param name="width">The width of the image.</param>
/// <param name="width">The width of the image.</param>
/// <param name="height">The height of the image.</param>
/// <param name="height">The height of the image.</param>
/// <returns>true, if the image is non opaque.</returns>
/// <returns>true, if the image is non opaque.</returns>
private bool ConvertPixelsToBgra < TPixel > ( Image < TPixel > imag e, int width , int height )
private bool ConvertPixelsToBgra < TPixel > ( ImageFrame < TPixel > fram e, int width , int height )
where TPixel : unmanaged , IPixel < TPixel >
where TPixel : unmanaged , IPixel < TPixel >
{
{
Buffer2D < TPixel > imageBuffer = image . Frames . RootF rame. PixelBuffer ;
Buffer2D < TPixel > imageBuffer = f rame. PixelBuffer ;
bool nonOpaque = false ;
bool nonOpaque = false ;
Span < uint > bgra = this . Bgra . GetSpan ( ) ;
Span < uint > bgra = this . Bgra . GetSpan ( ) ;
Span < byte > bgraBytes = MemoryMarshal . Cast < uint , byte > ( bgra ) ;
Span < byte > bgraBytes = MemoryMarshal . Cast < uint , byte > ( bgra ) ;
@ -589,15 +650,21 @@ internal class Vp8LEncoder : IDisposable
Vp8LBackwardRefs refsTmp = this . Refs [ refsBest . Equals ( this . Refs [ 0 ] ) ? 1 : 0 ] ;
Vp8LBackwardRefs refsTmp = this . Refs [ refsBest . Equals ( this . Refs [ 0 ] ) ? 1 : 0 ] ;
this . bitWriter . Reset ( bwInit ) ;
this . bitWriter . Reset ( bwInit ) ;
Vp8LHistogram tmpHisto = new ( cacheBits ) ;
using OwnedVp8LHistogram tmpHisto = OwnedVp8LHistogram . Create ( this . memoryAllocator , cacheBits ) ;
List < Vp8LHistogram > histogramImage = new ( histogramImageXySize ) ;
using Vp8LHistogramSet histogramImage = new ( this . memoryAllocator , histogramImageXySize , cacheBits ) ;
for ( int i = 0 ; i < histogramImageXySize ; i + + )
{
histogramImage . Add ( new Vp8LHistogram ( cacheBits ) ) ;
}
// Build histogram image and symbols from backward references.
// Build histogram image and symbols from backward references.
HistogramEncoder . GetHistoImageSymbols ( width , height , refsBest , this . quality , this . HistoBits , cacheBits , histogramImage , tmpHisto , histogramSymbols ) ;
HistogramEncoder . GetHistoImageSymbols (
this . memoryAllocator ,
width ,
height ,
refsBest ,
this . quality ,
this . HistoBits ,
cacheBits ,
histogramImage ,
tmpHisto ,
histogramSymbols ) ;
// Create Huffman bit lengths and codes for each histogram image.
// Create Huffman bit lengths and codes for each histogram image.
int histogramImageSize = histogramImage . Count ;
int histogramImageSize = histogramImage . Count ;
@ -676,11 +743,9 @@ internal class Vp8LEncoder : IDisposable
this . StoreImageToBitMask ( width , this . HistoBits , refsBest , histogramSymbols , huffmanCodes ) ;
this . StoreImageToBitMask ( width , this . HistoBits , refsBest , histogramSymbols , huffmanCodes ) ;
// Keep track of the smallest image so far.
// Keep track of the smallest image so far.
if ( isFirstIteration | | ( bitWriterBest ! = null & & this . bitWriter . NumBytes ( ) < bitWriterBest . NumBytes ( ) ) )
if ( isFirstIteration | | ( bitWriterBest ! = null & & this . bitWriter . NumBytes < bitWriterBest . NumBytes ) )
{
{
Vp8LBitWriter tmp = this . bitWriter ;
( bitWriterBest , this . bitWriter ) = ( this . bitWriter , bitWriterBest ) ;
this . bitWriter = bitWriterBest ;
bitWriterBest = tmp ;
}
}
isFirstIteration = false ;
isFirstIteration = false ;
@ -787,13 +852,8 @@ internal class Vp8LEncoder : IDisposable
refsTmp1 ,
refsTmp1 ,
refsTmp2 ) ;
refsTmp2 ) ;
List < Vp8LHistogram > histogramImage = new ( )
{
new ( cacheBits )
} ;
// Build histogram image and symbols from backward references.
// Build histogram image and symbols from backward references.
histogramImage [ 0 ] . StoreRefs ( ref s) ;
using Vp8LHistogramSet histogramImage = new ( this . memoryAllocator , refs , 1 , cacheBits ) ;
// Create Huffman bit lengths and codes for each histogram image.
// Create Huffman bit lengths and codes for each histogram image.
GetHuffBitLengthsAndCodes ( histogramImage , huffmanCodes ) ;
GetHuffBitLengthsAndCodes ( histogramImage , huffmanCodes ) ;
@ -833,7 +893,7 @@ internal class Vp8LEncoder : IDisposable
private void StoreHuffmanCode ( Span < HuffmanTree > huffTree , HuffmanTreeToken [ ] tokens , HuffmanTreeCode huffmanCode )
private void StoreHuffmanCode ( Span < HuffmanTree > huffTree , HuffmanTreeToken [ ] tokens , HuffmanTreeCode huffmanCode )
{
{
int count = 0 ;
int count = 0 ;
Span < int > symbols = this . scratch . Span . Slice ( 0 , 2 ) ;
Span < int > symbols = this . scratch . Span [ . . 2 ] ;
symbols . Clear ( ) ;
symbols . Clear ( ) ;
const int maxBits = 8 ;
const int maxBits = 8 ;
const int maxSymbol = 1 < < maxBits ;
const int maxSymbol = 1 < < maxBits ;
@ -886,6 +946,7 @@ internal class Vp8LEncoder : IDisposable
private void StoreFullHuffmanCode ( Span < HuffmanTree > huffTree , HuffmanTreeToken [ ] tokens , HuffmanTreeCode tree )
private void StoreFullHuffmanCode ( Span < HuffmanTree > huffTree , HuffmanTreeToken [ ] tokens , HuffmanTreeCode tree )
{
{
// TODO: Allocations. This method is called in a loop.
int i ;
int i ;
byte [ ] codeLengthBitDepth = new byte [ WebpConstants . CodeLengthCodes ] ;
byte [ ] codeLengthBitDepth = new byte [ WebpConstants . CodeLengthCodes ] ;
short [ ] codeLengthBitDepthSymbols = new short [ WebpConstants . CodeLengthCodes ] ;
short [ ] codeLengthBitDepthSymbols = new short [ WebpConstants . CodeLengthCodes ] ;
@ -996,7 +1057,12 @@ internal class Vp8LEncoder : IDisposable
}
}
}
}
private void StoreImageToBitMask ( int width , int histoBits , Vp8LBackwardRefs backwardRefs , Span < ushort > histogramSymbols , HuffmanTreeCode [ ] huffmanCodes )
private void StoreImageToBitMask (
int width ,
int histoBits ,
Vp8LBackwardRefs backwardRefs ,
Span < ushort > histogramSymbols ,
HuffmanTreeCode [ ] huffmanCodes )
{
{
int histoXSize = histoBits > 0 ? LosslessUtils . SubSampleSize ( width , histoBits ) : 1 ;
int histoXSize = histoBits > 0 ? LosslessUtils . SubSampleSize ( width , histoBits ) : 1 ;
int tileMask = histoBits = = 0 ? 0 : - ( 1 < < histoBits ) ;
int tileMask = histoBits = = 0 ? 0 : - ( 1 < < histoBits ) ;
@ -1008,10 +1074,10 @@ internal class Vp8LEncoder : IDisposable
int tileY = y & tileMask ;
int tileY = y & tileMask ;
int histogramIx = histogramSymbols [ 0 ] ;
int histogramIx = histogramSymbols [ 0 ] ;
Span < HuffmanTreeCode > codes = huffmanCodes . AsSpan ( 5 * histogramIx ) ;
Span < HuffmanTreeCode > codes = huffmanCodes . AsSpan ( 5 * histogramIx ) ;
using List < PixOrCopy > . Enumerator c = backwardRefs . Refs . GetEnumerator ( ) ;
while ( c . MoveNext ( ) )
for ( int i = 0 ; i < backwardRefs . Refs . Count ; i + + )
{
{
PixOrCopy v = c . Current ;
PixOrCopy v = backwardRefs . Refs [ i ] ;
if ( tileX ! = ( x & tileMask ) | | tileY ! = ( y & tileMask ) )
if ( tileX ! = ( x & tileMask ) | | tileY ! = ( y & tileMask ) )
{
{
tileX = x & tileMask ;
tileX = x & tileMask ;
@ -1024,7 +1090,7 @@ internal class Vp8LEncoder : IDisposable
{
{
for ( int k = 0 ; k < 4 ; k + + )
for ( int k = 0 ; k < 4 ; k + + )
{
{
int code = ( int ) v . Literal ( Order [ k ] ) ;
int code = v . Literal ( Order [ k ] ) ;
this . bitWriter . WriteHuffmanCode ( codes [ k ] , code ) ;
this . bitWriter . WriteHuffmanCode ( codes [ k ] , code ) ;
}
}
}
}
@ -1149,35 +1215,41 @@ internal class Vp8LEncoder : IDisposable
entropyComp [ j ] = bitEntropy . BitsEntropyRefine ( ) ;
entropyComp [ j ] = bitEntropy . BitsEntropyRefine ( ) ;
}
}
entropy [ ( int ) EntropyIx . Direct ] = entropyComp [ ( int ) HistoIx . HistoAlpha ] +
entropy [ ( int ) EntropyIx . Direct ] =
entropyComp [ ( int ) HistoIx . HistoRed ] +
entropyComp [ ( int ) HistoIx . HistoAlpha ] +
entropyComp [ ( int ) HistoIx . HistoGreen ] +
entropyComp [ ( int ) HistoIx . HistoRed ] +
entropyComp [ ( int ) HistoIx . HistoBlue ] ;
entropyComp [ ( int ) HistoIx . HistoGreen ] +
entropy [ ( int ) EntropyIx . Spatial ] = entropyComp [ ( int ) HistoIx . HistoAlphaPred ] +
entropyComp [ ( int ) HistoIx . HistoBlue ] ;
entropyComp [ ( int ) HistoIx . HistoRedPred ] +
entropy [ ( int ) EntropyIx . Spatial ] =
entropyComp [ ( int ) HistoIx . HistoGreenPred ] +
entropyComp [ ( int ) HistoIx . HistoAlphaPred ] +
entropyComp [ ( int ) HistoIx . HistoBluePred ] ;
entropyComp [ ( int ) HistoIx . HistoRedPred ] +
entropy [ ( int ) EntropyIx . SubGreen ] = entropyComp [ ( int ) HistoIx . HistoAlpha ] +
entropyComp [ ( int ) HistoIx . HistoGreenPred ] +
entropyComp [ ( int ) HistoIx . HistoRedSubGreen ] +
entropyComp [ ( int ) HistoIx . HistoBluePred ] ;
entropyComp [ ( int ) HistoIx . HistoGreen ] +
entropy [ ( int ) EntropyIx . SubGreen ] =
entropyComp [ ( int ) HistoIx . HistoBlueSubGreen ] ;
entropyComp [ ( int ) HistoIx . HistoAlpha ] +
entropy [ ( int ) EntropyIx . SpatialSubGreen ] = entropyComp [ ( int ) HistoIx . HistoAlphaPred ] +
entropyComp [ ( int ) HistoIx . HistoRedSubGreen ] +
entropyComp [ ( int ) HistoIx . HistoRedPredSubGreen ] +
entropyComp [ ( int ) HistoIx . HistoGreen ] +
entropyComp [ ( int ) HistoIx . HistoGreenPred ] +
entropyComp [ ( int ) HistoIx . HistoBlueSubGreen ] ;
entropyComp [ ( int ) HistoIx . HistoBluePredSubGreen ] ;
entropy [ ( int ) EntropyIx . SpatialSubGreen ] =
entropyComp [ ( int ) HistoIx . HistoAlphaPred ] +
entropyComp [ ( int ) HistoIx . HistoRedPredSubGreen ] +
entropyComp [ ( int ) HistoIx . HistoGreenPred ] +
entropyComp [ ( int ) HistoIx . HistoBluePredSubGreen ] ;
entropy [ ( int ) EntropyIx . Palette ] = entropyComp [ ( int ) HistoIx . HistoPalette ] ;
entropy [ ( int ) EntropyIx . Palette ] = entropyComp [ ( int ) HistoIx . HistoPalette ] ;
// When including transforms, there is an overhead in bits from
// When including transforms, there is an overhead in bits from
// storing them. This overhead is small but matters for small images.
// storing them. This overhead is small but matters for small images.
// For spatial, there are 14 transformations.
// For spatial, there are 14 transformations.
entropy [ ( int ) EntropyIx . Spatial ] + = LosslessUtils . SubSampleSize ( width , transformBits ) *
entropy [ ( int ) EntropyIx . Spatial ] + =
LosslessUtils . SubSampleSize ( height , transformBits ) *
LosslessUtils . SubSampleSize ( width , transformBits ) *
LosslessUtils . FastLog2 ( 1 4 ) ;
LosslessUtils . SubSampleSize ( height , transformBits ) *
LosslessUtils . FastLog2 ( 1 4 ) ;
// For color transforms: 24 as only 3 channels are considered in a ColorTransformElement.
// For color transforms: 24 as only 3 channels are considered in a ColorTransformElement.
entropy [ ( int ) EntropyIx . SpatialSubGreen ] + = LosslessUtils . SubSampleSize ( width , transformBits ) *
entropy [ ( int ) EntropyIx . SpatialSubGreen ] + =
LosslessUtils . SubSampleSize ( height , transformBits ) *
LosslessUtils . SubSampleSize ( width , transformBits ) *
LosslessUtils . FastLog2 ( 2 4 ) ;
LosslessUtils . SubSampleSize ( height , transformBits ) *
LosslessUtils . FastLog2 ( 2 4 ) ;
// For palettes, add the cost of storing the palette.
// For palettes, add the cost of storing the palette.
// We empirically estimate the cost of a compressed entry as 8 bits.
// We empirically estimate the cost of a compressed entry as 8 bits.
@ -1379,10 +1451,8 @@ internal class Vp8LEncoder : IDisposable
useLut = false ;
useLut = false ;
break ;
break ;
}
}
else
{
buffer [ ind ] = ( uint ) j ;
buffer [ ind ] = ( uint ) j ;
}
}
}
if ( useLut )
if ( useLut )
@ -1591,14 +1661,12 @@ internal class Vp8LEncoder : IDisposable
}
}
// Swap color(palette[bestIdx], palette[i]);
// Swap color(palette[bestIdx], palette[i]);
uint best = palette [ bestIdx ] ;
( palette [ i ] , palette [ bestIdx ] ) = ( palette [ bestIdx ] , palette [ i ] ) ;
palette [ bestIdx ] = palette [ i ] ;
palette [ i ] = best ;
predict = palette [ i ] ;
predict = palette [ i ] ;
}
}
}
}
private static void GetHuffBitLengthsAndCodes ( List < Vp8LHistogram > histogramImage , HuffmanTreeCode [ ] huffmanCodes )
private static void GetHuffBitLengthsAndCodes ( Vp8LHistogramSet histogramImage , HuffmanTreeCode [ ] huffmanCodes )
{
{
int maxNumSymbols = 0 ;
int maxNumSymbols = 0 ;
@ -1609,13 +1677,25 @@ internal class Vp8LEncoder : IDisposable
int startIdx = 5 * i ;
int startIdx = 5 * i ;
for ( int k = 0 ; k < 5 ; k + + )
for ( int k = 0 ; k < 5 ; k + + )
{
{
int numSymbols =
int numSymbols ;
k = = 0 ? histo . NumCodes ( ) :
if ( k = = 0 )
k = = 4 ? WebpConstants . NumDistanceCodes : 2 5 6 ;
{
numSymbols = histo . NumCodes ( ) ;
}
else if ( k = = 4 )
{
numSymbols = WebpConstants . NumDistanceCodes ;
}
else
{
numSymbols = 2 5 6 ;
}
huffmanCodes [ startIdx + k ] . NumSymbols = numSymbols ;
huffmanCodes [ startIdx + k ] . NumSymbols = numSymbols ;
}
}
}
}
// TODO: Allocations.
int end = 5 * histogramImage . Count ;
int end = 5 * histogramImage . Count ;
for ( int i = 0 ; i < end ; i + + )
for ( int i = 0 ; i < end ; i + + )
{
{
@ -1629,8 +1709,9 @@ internal class Vp8LEncoder : IDisposable
}
}
// Create Huffman trees.
// Create Huffman trees.
// TODO: Allocations.
bool [ ] bufRle = new bool [ maxNumSymbols ] ;
bool [ ] bufRle = new bool [ maxNumSymbols ] ;
Span < HuffmanTree > huffTree = stackalloc HuffmanTree [ 3 * maxNumSymbols ] ;
HuffmanTree [ ] huffTree = new HuffmanTree [ 3 * maxNumSymbols ] ;
for ( int i = 0 ; i < histogramImage . Count ; i + + )
for ( int i = 0 ; i < histogramImage . Count ; i + + )
{
{
@ -1682,8 +1763,18 @@ internal class Vp8LEncoder : IDisposable
histoBits + + ;
histoBits + + ;
}
}
return histoBits < WebpConstants . MinHuffmanBits ? WebpConstants . MinHuffmanBits :
if ( histoBits < WebpConstants . MinHuffmanBits )
histoBits > WebpConstants . MaxHuffmanBits ? WebpConstants . MaxHuffmanBits : histoBits ;
{
return WebpConstants . MinHuffmanBits ;
}
else if ( histoBits > WebpConstants . MaxHuffmanBits )
{
return WebpConstants . MaxHuffmanBits ;
}
else
{
return histoBits ;
}
}
}
/// <summary>
/// <summary>
@ -1720,11 +1811,7 @@ internal class Vp8LEncoder : IDisposable
[MethodImpl(InliningOptions.ShortMethod)]
[MethodImpl(InliningOptions.ShortMethod)]
private static void BitWriterSwap ( ref Vp8LBitWriter src , ref Vp8LBitWriter dst )
private static void BitWriterSwap ( ref Vp8LBitWriter src , ref Vp8LBitWriter dst )
{
= > ( dst , src ) = ( src , dst ) ;
Vp8LBitWriter tmp = src ;
src = dst ;
dst = tmp ;
}
/// <summary>
/// <summary>
/// Calculates the bits used for the transformation.
/// Calculates the bits used for the transformation.
@ -1732,9 +1819,21 @@ internal class Vp8LEncoder : IDisposable
[MethodImpl(InliningOptions.ShortMethod)]
[MethodImpl(InliningOptions.ShortMethod)]
private static int GetTransformBits ( WebpEncodingMethod method , int histoBits )
private static int GetTransformBits ( WebpEncodingMethod method , int histoBits )
{
{
int maxTransformBits = ( int ) method < 4 ? 6 : method > WebpEncodingMethod . Level4 ? 4 : 5 ;
int maxTransformBits ;
int res = histoBits > maxTransformBits ? maxTransformBits : histoBits ;
if ( ( int ) method < 4 )
return res ;
{
maxTransformBits = 6 ;
}
else if ( method > WebpEncodingMethod . Level4 )
{
maxTransformBits = 4 ;
}
else
{
maxTransformBits = 5 ;
}
return histoBits > maxTransformBits ? maxTransformBits : histoBits ;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
[MethodImpl(InliningOptions.ShortMethod)]
@ -1812,9 +1911,9 @@ internal class Vp8LEncoder : IDisposable
/// </summary>
/// </summary>
public void ClearRefs ( )
public void ClearRefs ( )
{
{
for ( int i = 0 ; i < this . Refs . Length ; i + + )
foreach ( Vp8LBackwardRefs t in this . Refs )
{
{
this . Refs [ i ] . Refs . Clear ( ) ;
t . Refs . Clear ( ) ;
}
}
}
}
@ -1823,9 +1922,9 @@ internal class Vp8LEncoder : IDisposable
{
{
this . Bgra . Dispose ( ) ;
this . Bgra . Dispose ( ) ;
this . EncodedData . Dispose ( ) ;
this . EncodedData . Dispose ( ) ;
this . BgraScratch . Dispose ( ) ;
this . BgraScratch ? . Dispose ( ) ;
this . Palette . Dispose ( ) ;
this . Palette . Dispose ( ) ;
this . TransformData . Dispose ( ) ;
this . TransformData ? . Dispose ( ) ;
this . HashChain . Dispose ( ) ;
this . HashChain . Dispose ( ) ;
}
}