Browse Source

Fix issue with encoding 1 by 1 pixel lossless image

pull/1552/head
Brian Popow 5 years ago
parent
commit
c4a1b994e6
  1. 2
      src/ImageSharp/Advanced/AotCompilerTools.cs
  2. 2
      src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs
  3. 48
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  4. 34
      src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs
  5. 1
      src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
  6. 8
      tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs
  7. 21
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
  8. 4
      tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs
  9. 2
      tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs
  10. 2
      tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs

2
src/ImageSharp/Advanced/AotCompilerTools.cs

@ -7,13 +7,13 @@ using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;

2
src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs

@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Keep the best backward references. // Keep the best backward references.
var histo = new Vp8LHistogram(worst, cacheBitsTmp); var histo = new Vp8LHistogram(worst, cacheBitsTmp);
var bitCost = histo.EstimateBits(); double bitCost = histo.EstimateBits();
if (lz77TypeBest == 0 || bitCost < bitCostBest) if (lz77TypeBest == 0 || bitCost < bitCostBest)
{ {

48
src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param> /// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
public Vp8LEncoder(MemoryAllocator memoryAllocator, int width, int height, int quality, int method) public Vp8LEncoder(MemoryAllocator memoryAllocator, int width, int height, int quality, int method)
{ {
var pixelCount = width * height; int pixelCount = width * height;
int initialSize = pixelCount * 2; int initialSize = pixelCount * 2;
this.quality = Numerics.Clamp(quality, 0, 100); this.quality = Numerics.Clamp(quality, 0, 100);
@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
(crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); (crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen);
this.UsePredictorTransform = (crunchConfig.EntropyIdx == EntropyIx.Spatial) || this.UsePredictorTransform = (crunchConfig.EntropyIdx == EntropyIx.Spatial) ||
(crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); (crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen);
this.UseCrossColorTransform = redAndBlueAlwaysZero ? false : this.UsePredictorTransform; this.UseCrossColorTransform = !redAndBlueAlwaysZero && this.UsePredictorTransform;
this.AllocateTransformBuffer(width, height); this.AllocateTransformBuffer(width, height);
// Reset any parameter in the encoder that is set in the previous iteration. // Reset any parameter in the encoder that is set in the previous iteration.
@ -335,14 +335,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int height = image.Height; int height = image.Height;
// Check if we only deal with a small number of colors and should use a palette. // Check if we only deal with a small number of colors and should use a palette.
var usePalette = this.AnalyzeAndCreatePalette(image); bool usePalette = this.AnalyzeAndCreatePalette(image);
// Empirical bit sizes. // Empirical bit sizes.
this.HistoBits = GetHistoBits(this.method, usePalette, width, height); this.HistoBits = GetHistoBits(this.method, usePalette, width, height);
this.TransformBits = GetTransformBits(this.method, this.HistoBits); this.TransformBits = GetTransformBits(this.method, this.HistoBits);
// Try out multiple LZ77 on images with few colors. // Try out multiple LZ77 on images with few colors.
var nlz77s = (this.PaletteSize > 0 && this.PaletteSize <= 16) ? 2 : 1; int nlz77s = (this.PaletteSize > 0 && this.PaletteSize <= 16) ? 2 : 1;
EntropyIx entropyIdx = this.AnalyzeEntropy(image, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero); EntropyIx entropyIdx = this.AnalyzeEntropy(image, usePalette, this.PaletteSize, this.TransformBits, out redAndBlueAlwaysZero);
bool doNotCache = false; bool doNotCache = false;
@ -382,7 +382,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// Fill in the different LZ77s. // Fill in the different LZ77s.
foreach (CrunchConfig crunchConfig in crunchConfigs) foreach (CrunchConfig crunchConfig in crunchConfigs)
{ {
for (var j = 0; j < nlz77s; ++j) for (int j = 0; j < nlz77s; ++j)
{ {
crunchConfig.SubConfigs.Add(new CrunchSubConfig crunchConfig.SubConfigs.Add(new CrunchSubConfig
{ {
@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, bool useCache, CrunchConfig config, int cacheBits, int histogramBits) private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, bool useCache, CrunchConfig config, int cacheBits, int histogramBits)
{ {
int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits); int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits);
var histogramSymbols = new ushort[histogramImageXySize]; ushort[] histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes]; var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {
@ -452,8 +452,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
HistogramEncoder.GetHistoImageSymbols(width, height, refsBest, this.quality, histogramBits, cacheBits, histogramImage, tmpHisto, histogramSymbols); HistogramEncoder.GetHistoImageSymbols(width, height, refsBest, this.quality, histogramBits, cacheBits, histogramImage, tmpHisto, histogramSymbols);
// Create Huffman bit lengths and codes for each histogram image. // Create Huffman bit lengths and codes for each histogram image.
var histogramImageSize = histogramImage.Count; int histogramImageSize = histogramImage.Count;
var bitArraySize = 5 * histogramImageSize; int bitArraySize = 5 * histogramImageSize;
var huffmanCodes = new HuffmanTreeCode[bitArraySize]; var huffmanCodes = new HuffmanTreeCode[bitArraySize];
for (int i = 0; i < huffmanCodes.Length; i++) for (int i = 0; i < huffmanCodes.Length; i++)
{ {
@ -601,7 +601,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality) private void EncodeImageNoHuffman(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality)
{ {
int cacheBits = 0; int cacheBits = 0;
var histogramSymbols = new ushort[1]; // Only one tree, one symbol. ushort[] histogramSymbols = new ushort[1]; // Only one tree, one symbol.
var huffmanCodes = new HuffmanTreeCode[5]; var huffmanCodes = new HuffmanTreeCode[5];
for (int i = 0; i < huffmanCodes.Length; i++) for (int i = 0; i < huffmanCodes.Length; i++)
@ -728,8 +728,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree) private void StoreFullHuffmanCode(HuffmanTree[] huffTree, HuffmanTreeToken[] tokens, HuffmanTreeCode tree)
{ {
int i; int i;
var codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes];
var codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes]; short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes];
var huffmanCode = new HuffmanTreeCode var huffmanCode = new HuffmanTreeCode
{ {
NumSymbols = WebpConstants.CodeLengthCodes, NumSymbols = WebpConstants.CodeLengthCodes,
@ -738,9 +738,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
}; };
this.bitWriter.PutBits(0, 1); this.bitWriter.PutBits(0, 1);
var numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens); int numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens);
var histogram = new uint[WebpConstants.CodeLengthCodes + 1]; uint[] histogram = new uint[WebpConstants.CodeLengthCodes + 1];
var bufRle = new bool[WebpConstants.CodeLengthCodes + 1]; bool[] bufRle = new bool[WebpConstants.CodeLengthCodes + 1];
for (i = 0; i < numTokens; i++) for (i = 0; i < numTokens; i++)
{ {
histogram[tokens[i].Code]++; histogram[tokens[i].Code]++;
@ -758,7 +758,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int ix = tokens[i].Code; int ix = tokens[i].Code;
if (ix == 0 || ix == 17 || ix == 18) if (ix == 0 || ix == 17 || ix == 18)
{ {
trimmedLength--; // discount trailing zeros. trimmedLength--; // Discount trailing zeros.
trailingZeroBits += codeLengthBitDepth[ix]; trailingZeroBits += codeLengthBitDepth[ix];
if (ix == 17) if (ix == 17)
{ {
@ -775,8 +775,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
var writeTrimmedLength = trimmedLength > 1 && trailingZeroBits > 12; bool writeTrimmedLength = trimmedLength > 1 && trailingZeroBits > 12;
var length = writeTrimmedLength ? trimmedLength : numTokens; int length = writeTrimmedLength ? trimmedLength : numTokens;
this.bitWriter.PutBits((uint)(writeTrimmedLength ? 1 : 0), 1); this.bitWriter.PutBits((uint)(writeTrimmedLength ? 1 : 0), 1);
if (writeTrimmedLength) if (writeTrimmedLength)
{ {
@ -976,8 +976,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
prevRow = currentRow; prevRow = currentRow;
} }
var entropyComp = new double[(int)HistoIx.HistoTotal]; double[] entropyComp = new double[(int)HistoIx.HistoTotal];
var entropy = new double[(int)EntropyIx.NumEntropyIx]; double[] entropy = new double[(int)EntropyIx.NumEntropyIx];
int lastModeToAnalyze = usePalette ? (int)EntropyIx.Palette : (int)EntropyIx.SpatialSubGreen; int lastModeToAnalyze = usePalette ? (int)EntropyIx.Palette : (int)EntropyIx.SpatialSubGreen;
// Let's add one zero to the predicted histograms. The zeros are removed // Let's add one zero to the predicted histograms. The zeros are removed
@ -1195,7 +1195,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
else else
{ {
var buffer = new uint[PaletteInvSize]; uint[] buffer = new uint[PaletteInvSize];
// Try to find a perfect hash function able to go from a color to an index // Try to find a perfect hash function able to go from a color to an index
// within 1 << PaletteInvSize in order to build a hash map to go from color to index in palette. // within 1 << PaletteInvSize in order to build a hash map to go from color to index in palette.
@ -1246,8 +1246,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
else else
{ {
var idxMap = new uint[paletteSize]; uint[] idxMap = new uint[paletteSize];
var paletteSorted = new uint[paletteSize]; uint[] paletteSorted = new uint[paletteSize];
PrepareMapToPalette(palette, paletteSize, paletteSorted, idxMap); PrepareMapToPalette(palette, paletteSize, paletteSorted, idxMap);
ApplyPaletteForWithIdxMap(width, height, palette, src, srcStride, dst, dstStride, tmpRow, idxMap, xBits, paletteSorted, paletteSize); ApplyPaletteForWithIdxMap(width, height, palette, src, srcStride, dst, dstStride, tmpRow, idxMap, xBits, paletteSorted, paletteSize);
} }
@ -1464,7 +1464,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
} }
var end = 5 * histogramImage.Count; int end = 5 * histogramImage.Count;
for (int i = 0; i < end; i++) for (int i = 0; i < end; i++)
{ {
int bitLength = huffmanCodes[i].NumSymbols; int bitLength = huffmanCodes[i].NumSymbols;
@ -1477,7 +1477,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
// Create Huffman trees. // Create Huffman trees.
var bufRle = new bool[maxNumSymbols]; bool[] bufRle = new bool[maxNumSymbols];
var huffTree = new HuffmanTree[3 * maxNumSymbols]; var huffTree = new HuffmanTree[3 * maxNumSymbols];
for (int i = 0; i < huffTree.Length; i++) for (int i = 0; i < huffTree.Length; i++)
{ {

34
src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs

@ -60,16 +60,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
int iterMax = GetMaxItersForQuality(quality); int iterMax = GetMaxItersForQuality(quality);
int windowSize = GetWindowSizeForHashChain(quality, xSize); int windowSize = GetWindowSizeForHashChain(quality, xSize);
int pos; int pos;
if (size <= 2)
{
this.OffsetLength[0] = 0;
return;
}
using IMemoryOwner<int> hashToFirstIndexBuffer = memoryAllocator.Allocate<int>(HashSize); using IMemoryOwner<int> hashToFirstIndexBuffer = memoryAllocator.Allocate<int>(HashSize);
Span<int> hashToFirstIndex = hashToFirstIndexBuffer.GetSpan(); Span<int> hashToFirstIndex = hashToFirstIndexBuffer.GetSpan();
// Initialize hashToFirstIndex array to -1. // Initialize hashToFirstIndex array to -1.
hashToFirstIndex.Fill(-1); hashToFirstIndex.Fill(-1);
var chain = new int[size]; int[] chain = new int[size];
// Fill the chain linking pixels with the same hash. // Fill the chain linking pixels with the same hash.
var bgraComp = bgra[0] == bgra[1]; bool bgraComp = bgra.Length > 1 && bgra[0] == bgra[1];
for (pos = 0; pos < size - 2;) for (pos = 0; pos < size - 2;)
{ {
uint hashCode; uint hashCode;
@ -78,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
// Consecutive pixels with the same color will share the same hash. // Consecutive pixels with the same color will share the same hash.
// We therefore use a different hash: the color and its repetition length. // We therefore use a different hash: the color and its repetition length.
var tmp = new uint[2]; uint[] tmp = new uint[2];
uint len = 1; uint len = 1;
tmp[0] = bgra[pos]; tmp[0] = bgra[pos];
@ -168,7 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
pos = minPos - 1; pos = minPos - 1;
} }
var bestBgra = bgra.Slice(bgraStart)[bestLength]; uint bestBgra = bgra.Slice(bgraStart)[bestLength];
for (; pos >= minPos && (--iter > 0); pos = chain[pos]) for (; pos >= minPos && (--iter > 0); pos = chain[pos])
{ {
@ -194,7 +201,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
// We have the best match but in case the two intervals continue matching // We have the best match but in case the two intervals continue matching
// to the left, we have the best matches for the left-extended pixels. // to the left, we have the best matches for the left-extended pixels.
var maxBasePosition = (uint)basePosition; uint maxBasePosition = (uint)basePosition;
while (true) while (true)
{ {
this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength; this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength;
@ -231,16 +238,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
} }
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public int FindLength(int basePosition) public int FindLength(int basePosition) => (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
{
return (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1));
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public int FindOffset(int basePosition) public int FindOffset(int basePosition) => (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
{
return (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits);
}
/// <summary> /// <summary>
/// Calculates the hash for a pixel pair. /// Calculates the hash for a pixel pair.
@ -252,7 +253,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{ {
uint key = bgra[1] * HashMultiplierHi; uint key = bgra[1] * HashMultiplierHi;
key += bgra[0] * HashMultiplierLo; key += bgra[0] * HashMultiplierLo;
key = key >> (32 - HashBits); key >>= 32 - HashBits;
return key; return key;
} }
@ -263,10 +264,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="quality">The quality.</param> /// <param name="quality">The quality.</param>
/// <returns>Number of hash chain lookups.</returns> /// <returns>Number of hash chain lookups.</returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetMaxItersForQuality(int quality) private static int GetMaxItersForQuality(int quality) => 8 + (quality * quality / 128);
{
return 8 + (quality * quality / 128);
}
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
private static int GetWindowSizeForHashChain(int quality, int xSize) private static int GetWindowSizeForHashChain(int quality, int xSize)

1
src/ImageSharp/Formats/WebP/WebpEncoderCore.cs

@ -3,7 +3,6 @@
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Formats.Webp.Lossy;

8
tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs

@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
ImageCodecInfo codec = FindCodecForType("image/tiff"); ImageCodecInfo codec = FindCodecForType("image/tiff");
using var parameters = new EncoderParameters(1) using var parameters = new EncoderParameters(1)
{ {
Param = {[0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression))} Param = { [0] = new EncoderParameter(Encoder.Compression, (long)Cast(this.Compression)) }
}; };
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
@ -73,12 +73,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs
{ {
TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb; TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb;
// Workaround for 1-bit bug
if (this.Compression == TiffCompression.CcittGroup3Fax || this.Compression == TiffCompression.Ccitt1D)
{
photometricInterpretation = TiffPhotometricInterpretation.WhiteIsZero;
}
var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation };
using var memoryStream = new MemoryStream(); using var memoryStream = new MemoryStream();
this.core.SaveAsTiff(memoryStream, encoder); this.core.SaveAsTiff(memoryStream, encoder);

21
tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

@ -1,6 +1,7 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}; };
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
var testOutputDetails = string.Concat("lossless", "_q", quality); string testOutputDetails = string.Concat("lossless", "_q", quality);
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder);
} }
@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}; };
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
var testOutputDetails = string.Concat("lossless", "_m", method); string testOutputDetails = string.Concat("lossless", "_m", method);
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder);
} }
@ -67,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}; };
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
var testOutputDetails = string.Concat("lossy", "_q", quality); string testOutputDetails = string.Concat("lossy", "_q", quality);
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality));
} }
@ -91,10 +92,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
}; };
using Image<TPixel> image = provider.GetImage(); using Image<TPixel> image = provider.GetImage();
var testOutputDetails = string.Concat("lossy", "_m", method); string testOutputDetails = string.Concat("lossy", "_m", method);
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality)); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality));
} }
[Fact]
public void Encode_Lossless_OneByOnePixel_Works()
{
// Just make sure, encoding 1 pixel by 1 pixel does not throw an exception.
using var image = new Image<Rgba32>(1, 1);
var encoder = new WebpEncoder() { Lossy = false };
using (var memStream = new MemoryStream())
{
image.SaveAsWebp(memStream, encoder);
}
}
private static ImageComparer GetComparer(int quality) private static ImageComparer GetComparer(int quality)
{ {
float tolerance = 0.01f; // ~1.0% float tolerance = 0.01f; // ~1.0%

4
tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs

@ -98,8 +98,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata
image.Metadata.SyncProfiles(); image.Metadata.SyncProfiles();
Assert.Equal(400, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble());
Assert.Equal(500, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble());
} }
} }
} }

2
tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs

@ -5,12 +5,12 @@ using System;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
namespace SixLabors.ImageSharp.Tests namespace SixLabors.ImageSharp.Tests

2
tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs

@ -5,9 +5,9 @@ using System;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;

Loading…
Cancel
Save