diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs index bf04cb7024..3961cc6c57 100644 --- a/src/ImageSharp/Advanced/AotCompilerTools.cs +++ b/src/ImageSharp/Advanced/AotCompilerTools.cs @@ -7,13 +7,13 @@ using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs index f4806dd52f..ed4bfe9087 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/BackwardReferenceEncoder.cs @@ -81,7 +81,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless // Keep the best backward references. var histo = new Vp8LHistogram(worst, cacheBitsTmp); - var bitCost = histo.EstimateBits(); + double bitCost = histo.EstimateBits(); if (lz77TypeBest == 0 || bitCost < bitCostBest) { diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs index ead8137512..f3c4ad1ca0 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs @@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless /// Quality/speed trade-off (0=fast, 6=slower-better). public Vp8LEncoder(MemoryAllocator memoryAllocator, int width, int height, int quality, int method) { - var pixelCount = width * height; + int pixelCount = width * height; int initialSize = pixelCount * 2; this.quality = Numerics.Clamp(quality, 0, 100); @@ -254,7 +254,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless (crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); this.UsePredictorTransform = (crunchConfig.EntropyIdx == EntropyIx.Spatial) || (crunchConfig.EntropyIdx == EntropyIx.SpatialSubGreen); - this.UseCrossColorTransform = redAndBlueAlwaysZero ? false : this.UsePredictorTransform; + this.UseCrossColorTransform = !redAndBlueAlwaysZero && this.UsePredictorTransform; this.AllocateTransformBuffer(width, height); // 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; // 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. this.HistoBits = GetHistoBits(this.method, usePalette, width, height); this.TransformBits = GetTransformBits(this.method, this.HistoBits); // 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); bool doNotCache = false; @@ -382,7 +382,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless // Fill in the different LZ77s. foreach (CrunchConfig crunchConfig in crunchConfigs) { - for (var j = 0; j < nlz77s; ++j) + for (int j = 0; j < nlz77s; ++j) { crunchConfig.SubConfigs.Add(new CrunchSubConfig { @@ -398,7 +398,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless private void EncodeImage(Span 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); - var histogramSymbols = new ushort[histogramImageXySize]; + ushort[] histogramSymbols = new ushort[histogramImageXySize]; var huffTree = new HuffmanTree[3 * WebpConstants.CodeLengthCodes]; 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); // Create Huffman bit lengths and codes for each histogram image. - var histogramImageSize = histogramImage.Count; - var bitArraySize = 5 * histogramImageSize; + int histogramImageSize = histogramImage.Count; + int bitArraySize = 5 * histogramImageSize; var huffmanCodes = new HuffmanTreeCode[bitArraySize]; for (int i = 0; i < huffmanCodes.Length; i++) { @@ -601,7 +601,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless private void EncodeImageNoHuffman(Span bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs refsTmp1, Vp8LBackwardRefs refsTmp2, int width, int height, int quality) { 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]; 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) { int i; - var codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; - var codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes]; + byte[] codeLengthBitDepth = new byte[WebpConstants.CodeLengthCodes]; + short[] codeLengthBitDepthSymbols = new short[WebpConstants.CodeLengthCodes]; var huffmanCode = new HuffmanTreeCode { NumSymbols = WebpConstants.CodeLengthCodes, @@ -738,9 +738,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless }; this.bitWriter.PutBits(0, 1); - var numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens); - var histogram = new uint[WebpConstants.CodeLengthCodes + 1]; - var bufRle = new bool[WebpConstants.CodeLengthCodes + 1]; + int numTokens = HuffmanUtils.CreateCompressedHuffmanTree(tree, tokens); + uint[] histogram = new uint[WebpConstants.CodeLengthCodes + 1]; + bool[] bufRle = new bool[WebpConstants.CodeLengthCodes + 1]; for (i = 0; i < numTokens; i++) { histogram[tokens[i].Code]++; @@ -758,7 +758,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless int ix = tokens[i].Code; if (ix == 0 || ix == 17 || ix == 18) { - trimmedLength--; // discount trailing zeros. + trimmedLength--; // Discount trailing zeros. trailingZeroBits += codeLengthBitDepth[ix]; if (ix == 17) { @@ -775,8 +775,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } } - var writeTrimmedLength = trimmedLength > 1 && trailingZeroBits > 12; - var length = writeTrimmedLength ? trimmedLength : numTokens; + bool writeTrimmedLength = trimmedLength > 1 && trailingZeroBits > 12; + int length = writeTrimmedLength ? trimmedLength : numTokens; this.bitWriter.PutBits((uint)(writeTrimmedLength ? 1 : 0), 1); if (writeTrimmedLength) { @@ -976,8 +976,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless prevRow = currentRow; } - var entropyComp = new double[(int)HistoIx.HistoTotal]; - var entropy = new double[(int)EntropyIx.NumEntropyIx]; + double[] entropyComp = new double[(int)HistoIx.HistoTotal]; + double[] entropy = new double[(int)EntropyIx.NumEntropyIx]; int lastModeToAnalyze = usePalette ? (int)EntropyIx.Palette : (int)EntropyIx.SpatialSubGreen; // Let's add one zero to the predicted histograms. The zeros are removed @@ -1195,7 +1195,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } 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 // 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 { - var idxMap = new uint[paletteSize]; - var paletteSorted = new uint[paletteSize]; + uint[] idxMap = new uint[paletteSize]; + uint[] paletteSorted = new uint[paletteSize]; PrepareMapToPalette(palette, paletteSize, paletteSorted, idxMap); 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++) { int bitLength = huffmanCodes[i].NumSymbols; @@ -1477,7 +1477,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } // Create Huffman trees. - var bufRle = new bool[maxNumSymbols]; + bool[] bufRle = new bool[maxNumSymbols]; var huffTree = new HuffmanTree[3 * maxNumSymbols]; for (int i = 0; i < huffTree.Length; i++) { diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs index 0e6c4aece5..f5762b6f8c 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHashChain.cs @@ -60,16 +60,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless int iterMax = GetMaxItersForQuality(quality); int windowSize = GetWindowSizeForHashChain(quality, xSize); int pos; + + if (size <= 2) + { + this.OffsetLength[0] = 0; + return; + } + using IMemoryOwner hashToFirstIndexBuffer = memoryAllocator.Allocate(HashSize); Span hashToFirstIndex = hashToFirstIndexBuffer.GetSpan(); // Initialize hashToFirstIndex array to -1. hashToFirstIndex.Fill(-1); - var chain = new int[size]; + int[] chain = new int[size]; // 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;) { uint hashCode; @@ -78,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { // Consecutive pixels with the same color will share the same hash. // 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; tmp[0] = bgra[pos]; @@ -168,7 +175,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless pos = minPos - 1; } - var bestBgra = bgra.Slice(bgraStart)[bestLength]; + uint bestBgra = bgra.Slice(bgraStart)[bestLength]; 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 // to the left, we have the best matches for the left-extended pixels. - var maxBasePosition = (uint)basePosition; + uint maxBasePosition = (uint)basePosition; while (true) { this.OffsetLength[basePosition] = (bestDistance << BackwardReferenceEncoder.MaxLengthBits) | (uint)bestLength; @@ -231,16 +238,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } [MethodImpl(InliningOptions.ShortMethod)] - public int FindLength(int basePosition) - { - return (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1)); - } + public int FindLength(int basePosition) => (int)(this.OffsetLength[basePosition] & ((1U << BackwardReferenceEncoder.MaxLengthBits) - 1)); [MethodImpl(InliningOptions.ShortMethod)] - public int FindOffset(int basePosition) - { - return (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits); - } + public int FindOffset(int basePosition) => (int)(this.OffsetLength[basePosition] >> BackwardReferenceEncoder.MaxLengthBits); /// /// Calculates the hash for a pixel pair. @@ -252,7 +253,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { uint key = bgra[1] * HashMultiplierHi; key += bgra[0] * HashMultiplierLo; - key = key >> (32 - HashBits); + key >>= 32 - HashBits; return key; } @@ -263,10 +264,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless /// The quality. /// Number of hash chain lookups. [MethodImpl(InliningOptions.ShortMethod)] - private static int GetMaxItersForQuality(int quality) - { - return 8 + (quality * quality / 128); - } + private static int GetMaxItersForQuality(int quality) => 8 + (quality * quality / 128); [MethodImpl(InliningOptions.ShortMethod)] private static int GetWindowSizeForHashChain(int quality, int xSize) diff --git a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs index b785345ee3..ecc940782b 100644 --- a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs @@ -3,7 +3,6 @@ using System.IO; using System.Threading; -using System.Threading.Tasks; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossy; diff --git a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs index 39055faf50..0056a187b3 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/EncodeTiff.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs ImageCodecInfo codec = FindCodecForType("image/tiff"); 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(); @@ -73,12 +73,6 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs { 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 }; using var memoryStream = new MemoryStream(); this.core.SaveAsTiff(memoryStream, encoder); diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 11964f2535..828fc0dcd3 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.IO; using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -26,7 +27,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp }; using Image image = provider.GetImage(); - var testOutputDetails = string.Concat("lossless", "_q", quality); + string testOutputDetails = string.Concat("lossless", "_q", quality); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp }; using Image image = provider.GetImage(); - var testOutputDetails = string.Concat("lossless", "_m", method); + string testOutputDetails = string.Concat("lossless", "_m", method); image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } @@ -67,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp }; using Image 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)); } @@ -91,10 +92,22 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp }; using Image 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)); } + [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(1, 1); + var encoder = new WebpEncoder() { Lossy = false }; + using (var memStream = new MemoryStream()) + { + image.SaveAsWebp(memStream, encoder); + } + } + private static ImageComparer GetComparer(int quality) { float tolerance = 0.01f; // ~1.0% diff --git a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs index a82ea70179..2456246b67 100644 --- a/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs +++ b/tests/ImageSharp.Tests/Metadata/ImageMetadataTests.cs @@ -98,8 +98,8 @@ namespace SixLabors.ImageSharp.Tests.Metadata image.Metadata.SyncProfiles(); - Assert.Equal(400, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value).ToDouble()); - Assert.Equal(500, ((Rational)image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value).ToDouble()); + Assert.Equal(400, image.Metadata.ExifProfile.GetValue(ExifTag.XResolution).Value.ToDouble()); + Assert.Equal(500, image.Metadata.ExifProfile.GetValue(ExifTag.YResolution).Value.ToDouble()); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 2bcee62bd2..4b374b21f6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -5,12 +5,12 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Tga; using SixLabors.ImageSharp.Formats.Tiff; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; namespace SixLabors.ImageSharp.Tests diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 4ad32a3798..c836bda350 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -5,9 +5,9 @@ using System; using System.IO; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions;