diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs index 7c21d62ddf..cd05833617 100644 --- a/src/ImageSharp/Color/Color.cs +++ b/src/ImageSharp/Color/Color.cs @@ -270,8 +270,15 @@ namespace SixLabors.ImageSharp return pixel; } + if (this.boxedHighPrecisionPixel is null) + { + pixel = default; + pixel.FromRgba64(this.data); + return pixel; + } + pixel = default; - pixel.FromRgba64(this.data); + pixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4()); return pixel; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs index d71a62ec98..6acc6b6db0 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs @@ -650,6 +650,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder } this.target.Write(this.streamWriteBuffer, 0, writeIdx); + this.emitWriteIndex = this.emitBuffer.Length; } /// @@ -660,11 +661,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder /// This must be called only if is true /// only during the macroblocks encoding routine. /// - private void FlushToStream() - { + private void FlushToStream() => this.FlushToStream(this.emitWriteIndex * 4); - this.emitWriteIndex = this.emitBuffer.Length; - } /// /// Flushes final cached bits to the stream padding 1's to @@ -681,10 +679,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder // And writing only valuable count of bytes count we want to write to the output stream int valuableBytesCount = (int)Numerics.DivideCeil((uint)this.bitCount, 8); uint packedBytes = this.accumulatedBits | (uint.MaxValue >> this.bitCount); - this.emitBuffer[--this.emitWriteIndex] = packedBytes; + this.emitBuffer[this.emitWriteIndex - 1] = packedBytes; // Flush cached bytes to the output stream with padding bits - this.FlushToStream((this.emitWriteIndex * 4) - 4 + valuableBytesCount); + int lastByteIndex = (this.emitWriteIndex * 4) - valuableBytesCount; + this.FlushToStream(lastByteIndex); } } } diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index adabd0ac3f..7769a0a6c8 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -192,7 +192,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless public bool UseCrossColorTransform { get; set; } /// - /// Gets or sets a value indicating whether to use the substract green transform. + /// Gets or sets a value indicating whether to use the subtract green transform. /// public bool UseSubtractGreenTransform { get; set; } @@ -1049,7 +1049,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless return EntropyIx.Palette; } - using IMemoryOwner histoBuffer = this.memoryAllocator.Allocate((int)HistoIx.HistoTotal * 256); + using IMemoryOwner histoBuffer = this.memoryAllocator.Allocate((int)HistoIx.HistoTotal * 256, AllocationOptions.Clean); Span histo = histoBuffer.Memory.Span; uint pixPrev = bgra[0]; // Skip the first pixel. ReadOnlySpan prevRow = null; diff --git a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs index 3003265ca6..bfc290c2e5 100644 --- a/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs +++ b/tests/ImageSharp.Tests/Color/ColorTests.CastTo.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; using Xunit; @@ -90,17 +91,30 @@ namespace SixLabors.ImageSharp.Tests } [Fact] - public void GenericPixel() + public void Vector4Constructor() { - AssertGenericPixel(new RgbaVector(float.Epsilon, 2 * float.Epsilon, float.MaxValue, float.MinValue)); - AssertGenericPixel(new Rgba64(1, 2, ushort.MaxValue, ushort.MaxValue - 1)); - AssertGenericPixel(new Rgb48(1, 2, ushort.MaxValue - 1)); - AssertGenericPixel(new La32(1, ushort.MaxValue - 1)); - AssertGenericPixel(new L16(ushort.MaxValue - 1)); - AssertGenericPixel(new Rgba32(1, 2, 255, 254)); + // Act: + Color color = new(Vector4.One); + + // Assert: + Assert.Equal(new RgbaVector(1, 1, 1, 1), color.ToPixel()); + Assert.Equal(new Rgba64(65535, 65535, 65535, 65535), color.ToPixel()); + Assert.Equal(new Rgba32(255, 255, 255, 255), color.ToPixel()); + Assert.Equal(new L8(255), color.ToPixel()); + } + + [Fact] + public void GenericPixelRoundTrip() + { + AssertGenericPixelRoundTrip(new RgbaVector(float.Epsilon, 2 * float.Epsilon, float.MaxValue, float.MinValue)); + AssertGenericPixelRoundTrip(new Rgba64(1, 2, ushort.MaxValue, ushort.MaxValue - 1)); + AssertGenericPixelRoundTrip(new Rgb48(1, 2, ushort.MaxValue - 1)); + AssertGenericPixelRoundTrip(new La32(1, ushort.MaxValue - 1)); + AssertGenericPixelRoundTrip(new L16(ushort.MaxValue - 1)); + AssertGenericPixelRoundTrip(new Rgba32(1, 2, 255, 254)); } - private static void AssertGenericPixel(TPixel source) + private static void AssertGenericPixelRoundTrip(TPixel source) where TPixel : unmanaged, IPixel { // Act: @@ -110,6 +124,27 @@ namespace SixLabors.ImageSharp.Tests TPixel actual = color.ToPixel(); Assert.Equal(source, actual); } + + [Fact] + public void GenericPixelDifferentPrecision() + { + AssertGenericPixelDifferentPrecision(new RgbaVector(1, 1, 1, 1), new Rgba64(65535, 65535, 65535, 65535)); + AssertGenericPixelDifferentPrecision(new RgbaVector(1, 1, 1, 1), new Rgba32(255, 255, 255, 255)); + AssertGenericPixelDifferentPrecision(new Rgba64(65535, 65535, 65535, 65535), new Rgba32(255, 255, 255, 255)); + AssertGenericPixelDifferentPrecision(new Rgba32(255, 255, 255, 255), new L8(255)); + } + + private static void AssertGenericPixelDifferentPrecision(TPixel source, TPixel2 expected) + where TPixel : unmanaged, IPixel + where TPixel2 : unmanaged, IPixel + { + // Act: + var color = Color.FromPixel(source); + + // Assert: + TPixel2 actual = color.ToPixel(); + Assert.Equal(expected, actual); + } } } } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index 70cc487bfe..ec0040a465 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -101,6 +101,24 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp image.VerifyEncoder(provider, "webp", testOutputDetails, encoder); } + [Theory] + [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 15114)] + public void Encode_Lossless_WithBestQuality_HasExpectedSize(TestImageProvider provider, int expectedBytes) + where TPixel : unmanaged, IPixel + { + var encoder = new WebpEncoder() + { + FileFormat = WebpFileFormatType.Lossless, + Method = WebpEncodingMethod.BestQuality + }; + + using Image image = provider.GetImage(); + using var memoryStream = new MemoryStream(); + image.Save(memoryStream, encoder); + + Assert.Equal(memoryStream.Length, expectedBytes); + } + [Theory] [WithFile(RgbTestPattern100x100, PixelTypes.Rgba32, 85)] [WithFile(RgbTestPattern100x100, PixelTypes.Rgba32, 60)]