diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs index c22cd92a51..20b0b1d1d3 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -791,7 +791,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy m.Uv.Q[0] = WebpLookupTables.DcTable[Clip(q + this.DqUvDc, 0, 117)]; m.Uv.Q[1] = WebpLookupTables.AcTable[Clip(q + this.DqUvAc, 0, 127)]; - var qi4 = m.Y1.Expand(0); + int qi4 = m.Y1.Expand(0); m.Y2.Expand(1); // qi16 m.Uv.Expand(2); // quv @@ -808,7 +808,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy do { it.Import(y, u, v, yStride, uvStride, width, height, true); - int bestAlpha = this.MbAnalyze(it, alphas, out var bestUvAlpha); + int bestAlpha = this.MbAnalyze(it, alphas, out int bestUvAlpha); // Accumulate for later complexity analysis. alpha += bestAlpha; @@ -1211,19 +1211,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy where TPixel : unmanaged, IPixel { int uvWidth = (image.Width + 1) >> 1; - bool hasAlpha = YuvConversion.CheckNonOpaque(image); // Temporary storage for accumulated R/G/B values during conversion to U/V. using IMemoryOwner tmpRgb = this.memoryAllocator.Allocate(4 * uvWidth); Span tmpRgbSpan = tmpRgb.GetSpan(); int uvRowIndex = 0; int rowIndex; + bool rowsHaveAlpha = false; for (rowIndex = 0; rowIndex < image.Height - 1; rowIndex += 2) { + rowsHaveAlpha = YuvConversion.CheckNonOpaque(image, rowIndex, rowIndex + 1); + // Downsample U/V planes, two rows at a time. Span rowSpan = image.GetPixelRowSpan(rowIndex); Span nextRowSpan = image.GetPixelRowSpan(rowIndex + 1); - if (!hasAlpha) + if (!rowsHaveAlpha) { YuvConversion.AccumulateRgb(rowSpan, nextRowSpan, tmpRgbSpan, image.Width); } @@ -1243,7 +1245,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy if ((image.Height & 1) != 0) { Span rowSpan = image.GetPixelRowSpan(rowIndex); - if (!hasAlpha) + if (!rowsHaveAlpha) { YuvConversion.AccumulateRgb(rowSpan, rowSpan, tmpRgbSpan, image.Width); } @@ -1269,6 +1271,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy [MethodImpl(InliningOptions.ShortMethod)] private static int Vp8Sse16X16(Span a, Span b) => GetSse(a, b, 16, 16); + [MethodImpl(InliningOptions.ShortMethod)] private static int Vp8Sse16X8(Span a, Span b) => GetSse(a, b, 16, 8); [MethodImpl(InliningOptions.ShortMethod)] @@ -1319,6 +1322,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// is around q=75. Internally, our "good" middle is around c=50. So we /// map accordingly using linear piece-wise function /// + [MethodImpl(InliningOptions.ShortMethod)] private static double QualityToCompression(double c) { double linearC = (c < 0.75) ? c * (2.0d / 3.0d) : (2.0d * c) - 1.0d; @@ -1334,6 +1338,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy return v; } + [MethodImpl(InliningOptions.ShortMethod)] private int FilterStrengthFromDelta(int sharpness, int delta) { int pos = (delta < WebpConstants.MaxDelzaSize) ? delta : WebpConstants.MaxDelzaSize - 1; diff --git a/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs index afcd13ff94..7676555a62 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs @@ -21,12 +21,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// /// The pixel type of the image, /// The image to check. + /// The row to start with. + /// The row to end with. /// Returns true if alpha has non-0xff values. - public static bool CheckNonOpaque(Image image) + public static bool CheckNonOpaque(Image image, int rowIdxStart, int rowIdxEnd) where TPixel : unmanaged, IPixel { Rgba32 rgba = default; - for (int rowIndex = 0; rowIndex < image.Height; rowIndex++) + for (int rowIndex = rowIdxStart; rowIndex <= rowIdxEnd; rowIndex++) { Span rowSpan = image.GetPixelRowSpan(rowIndex); for (int x = 0; x < image.Width; x++) @@ -43,6 +45,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy return false; } + /// + /// Converts a rgba pixel row to Y. + /// + /// The type of the pixel. + /// The row span to convert. + /// The destination span for y. + /// The width. public static void ConvertRgbaToY(Span rowSpan, Span y, int width) where TPixel : unmanaged, IPixel { @@ -55,6 +64,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } } + /// + /// Converts a rgb row of pixels to UV. + /// + /// The RGB pixel row. + /// The destination span for u. + /// The destination span for v. + /// The width. public static void ConvertRgbaToUv(Span rgb, Span u, Span v, int width) { int rgbIdx = 0; @@ -184,9 +200,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } else { - r = LinearToGammaWeighted(rgba0.R, rgba1.R, rgba2.R, rgba3.R, rgba0.A, rgba1.A, rgba2.A, rgba3.A, a); - g = LinearToGammaWeighted(rgba0.G, rgba1.G, rgba2.G, rgba3.G, rgba0.A, rgba1.A, rgba2.A, rgba3.A, a); - b = LinearToGammaWeighted(rgba0.B, rgba1.B, rgba2.B, rgba3.B, rgba0.A, rgba1.A, rgba2.A, rgba3.A, a); + r = LinearToGammaWeighted(rgba0.R, rgba1.R, rgba0.R, rgba1.R, rgba0.A, rgba1.A, rgba0.A, rgba1.A, a); + g = LinearToGammaWeighted(rgba0.G, rgba1.G, rgba0.G, rgba1.G, rgba0.A, rgba1.A, rgba0.A, rgba1.A, a); + b = LinearToGammaWeighted(rgba0.B, rgba1.B, rgba0.B, rgba1.B, rgba0.A, rgba1.A, rgba0.A, rgba1.A, a); } dst[dstIdx] = (ushort)r; @@ -196,6 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } } + [MethodImpl(InliningOptions.ShortMethod)] private static int LinearToGammaWeighted(byte rgb0, byte rgb1, byte rgb2, byte rgb3, byte a0, byte a1, byte a2, byte a3, uint totalA) { uint sum = (a0 * GammaToLinear(rgb0)) + (a1 * GammaToLinear(rgb1)) + (a2 * GammaToLinear(rgb2)) + (a3 * GammaToLinear(rgb3)); @@ -212,10 +229,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } [MethodImpl(InliningOptions.ShortMethod)] - private static uint GammaToLinear(byte v) - { - return WebpLookupTables.GammaToLinearTab[v]; - } + private static uint GammaToLinear(byte v) => WebpLookupTables.GammaToLinearTab[v]; [MethodImpl(InliningOptions.ShortMethod)] private static int Interpolate(int v) diff --git a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs index b5a5df4e8f..421015d1f9 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs @@ -13,7 +13,7 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.WebP { - [Trait("Format", "WebpLossless")] + [Trait("Format", "Webp")] public class PredictorEncoderTests { [Fact]