diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs index 20b0b1d1d..0b3b209b1 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -22,6 +22,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// private readonly MemoryAllocator memoryAllocator; + /// + /// The global configuration. + /// + private Configuration configuration; + /// /// The quality, that will be used to encode the image. /// @@ -80,16 +85,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// Initializes a new instance of the class. /// /// The memory allocator. + /// The global configuration. /// The width of the input image. /// The height of the input image. /// The encoding quality. /// Quality/speed trade-off (0=fast, 6=slower-better). /// Number of entropy-analysis passes (in [1..10]). - public Vp8Encoder(MemoryAllocator memoryAllocator, int width, int height, int quality, int method, int entropyPasses) + public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses) { + this.memoryAllocator = memoryAllocator; + this.configuration = configuration; this.Width = width; this.Height = height; - this.memoryAllocator = memoryAllocator; this.quality = Numerics.Clamp(quality, 0, 100); this.method = Numerics.Clamp(method, 0, 6); this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10); @@ -1210,51 +1217,59 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private void ConvertRgbToYuv(Image image) where TPixel : unmanaged, IPixel { - int uvWidth = (image.Width + 1) >> 1; + int width = image.Width; + int height = image.Height; + int uvWidth = (width + 1) >> 1; // Temporary storage for accumulated R/G/B values during conversion to U/V. using IMemoryOwner tmpRgb = this.memoryAllocator.Allocate(4 * uvWidth); + using IMemoryOwner rgbaRow0Buffer = this.memoryAllocator.Allocate(width); + using IMemoryOwner rgbaRow1Buffer = this.memoryAllocator.Allocate(width); Span tmpRgbSpan = tmpRgb.GetSpan(); + Span rgbaRow0 = rgbaRow0Buffer.GetSpan(); + Span rgbaRow1 = rgbaRow1Buffer.GetSpan(); int uvRowIndex = 0; int rowIndex; bool rowsHaveAlpha = false; - for (rowIndex = 0; rowIndex < image.Height - 1; rowIndex += 2) + for (rowIndex = 0; rowIndex < 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); + PixelOperations.Instance.ToRgba32(this.configuration, rowSpan, rgbaRow0); + PixelOperations.Instance.ToRgba32(this.configuration, nextRowSpan, rgbaRow1); + + rowsHaveAlpha = YuvConversion.CheckNonOpaque(rgbaRow0) && YuvConversion.CheckNonOpaque(rgbaRow1); + + // Downsample U/V planes, two rows at a time. if (!rowsHaveAlpha) { - YuvConversion.AccumulateRgb(rowSpan, nextRowSpan, tmpRgbSpan, image.Width); + YuvConversion.AccumulateRgb(rgbaRow0, rgbaRow1, tmpRgbSpan, width); } else { - YuvConversion.AccumulateRgba(rowSpan, nextRowSpan, tmpRgbSpan, image.Width); + YuvConversion.AccumulateRgba(rgbaRow0, rgbaRow1, tmpRgbSpan, width); } YuvConversion.ConvertRgbaToUv(tmpRgbSpan, this.U.Slice(uvRowIndex * uvWidth), this.V.Slice(uvRowIndex * uvWidth), uvWidth); uvRowIndex++; - YuvConversion.ConvertRgbaToY(rowSpan, this.Y.Slice(rowIndex * image.Width), image.Width); - YuvConversion.ConvertRgbaToY(nextRowSpan, this.Y.Slice((rowIndex + 1) * image.Width), image.Width); + YuvConversion.ConvertRgbaToY(rgbaRow0, this.Y.Slice(rowIndex * width), width); + YuvConversion.ConvertRgbaToY(rgbaRow1, this.Y.Slice((rowIndex + 1) * width), width); } // Extra last row. - if ((image.Height & 1) != 0) + if ((height & 1) != 0) { - Span rowSpan = image.GetPixelRowSpan(rowIndex); if (!rowsHaveAlpha) { - YuvConversion.AccumulateRgb(rowSpan, rowSpan, tmpRgbSpan, image.Width); + YuvConversion.AccumulateRgb(rgbaRow0, rgbaRow0, tmpRgbSpan, width); } else { - YuvConversion.AccumulateRgba(rowSpan, rowSpan, tmpRgbSpan, image.Width); + YuvConversion.AccumulateRgba(rgbaRow0, rgbaRow0, tmpRgbSpan, width); } - YuvConversion.ConvertRgbaToY(rowSpan, this.Y.Slice(rowIndex * image.Width), image.Width); + YuvConversion.ConvertRgbaToY(rgbaRow0, this.Y.Slice(rowIndex * width), width); } } diff --git a/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs index 7676555a6..fbc2996fc 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs @@ -17,28 +17,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private const int YuvHalf = 1 << (YuvFix - 1); /// - /// Checks if the image is not opaque. + /// Checks if the pixel row is not opaque. /// - /// The pixel type of the image, - /// The image to check. - /// The row to start with. - /// The row to end with. + /// The row to check. /// Returns true if alpha has non-0xff values. - public static bool CheckNonOpaque(Image image, int rowIdxStart, int rowIdxEnd) - where TPixel : unmanaged, IPixel + [MethodImpl(InliningOptions.ShortMethod)] + public static bool CheckNonOpaque(Span row) { - Rgba32 rgba = default; - for (int rowIndex = rowIdxStart; rowIndex <= rowIdxEnd; rowIndex++) + for (int x = 0; x < row.Length; x++) { - Span rowSpan = image.GetPixelRowSpan(rowIndex); - for (int x = 0; x < image.Width; x++) + if (row[x].A != 255) { - TPixel color = rowSpan[x]; - color.ToRgba32(ref rgba); - if (rgba.A != 255) - { - return true; - } + return true; } } @@ -48,19 +38,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// /// 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 + [MethodImpl(InliningOptions.ShortMethod)] + public static void ConvertRgbaToY(Span rowSpan, Span y, int width) { - Rgba32 rgba = default; for (int x = 0; x < width; x++) { - TPixel color = rowSpan[x]; - color.ToRgba32(ref rgba); - y[x] = (byte)RgbToY(rgba.R, rgba.G, rgba.B, YuvHalf); + y[x] = (byte)RgbToY(rowSpan[x].R, rowSpan[x].G, rowSpan[x].B, YuvHalf); } } @@ -82,25 +68,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } } - public static void AccumulateRgb(Span rowSpan, Span nextRowSpan, Span dst, int width) - where TPixel : unmanaged, IPixel + public static void AccumulateRgb(Span rowSpan, Span nextRowSpan, Span dst, int width) { - Rgba32 rgba0 = default; - Rgba32 rgba1 = default; - Rgba32 rgba2 = default; - Rgba32 rgba3 = default; + Rgba32 rgba0; + Rgba32 rgba1; int i, j; int dstIdx = 0; for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2, dstIdx += 4) { - TPixel color = rowSpan[j]; - color.ToRgba32(ref rgba0); - color = rowSpan[j + 1]; - color.ToRgba32(ref rgba1); - color = nextRowSpan[j]; - color.ToRgba32(ref rgba2); - color = nextRowSpan[j + 1]; - color.ToRgba32(ref rgba3); + rgba0 = rowSpan[j]; + rgba1 = rowSpan[j + 1]; + Rgba32 rgba2 = nextRowSpan[j]; + Rgba32 rgba3 = nextRowSpan[j + 1]; dst[dstIdx] = (ushort)LinearToGamma( GammaToLinear(rgba0.R) + @@ -121,10 +100,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy if ((width & 1) != 0) { - TPixel color = rowSpan[j]; - color.ToRgba32(ref rgba0); - color = nextRowSpan[j]; - color.ToRgba32(ref rgba1); + rgba0 = rowSpan[j]; + rgba1 = nextRowSpan[j]; dst[dstIdx] = (ushort)LinearToGamma(GammaToLinear(rgba0.R) + GammaToLinear(rgba1.R), 1); dst[dstIdx + 1] = (ushort)LinearToGamma(GammaToLinear(rgba0.G) + GammaToLinear(rgba1.G), 1); @@ -132,25 +109,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy } } - public static void AccumulateRgba(Span rowSpan, Span nextRowSpan, Span dst, int width) - where TPixel : unmanaged, IPixel + public static void AccumulateRgba(Span rowSpan, Span nextRowSpan, Span dst, int width) { - Rgba32 rgba0 = default; - Rgba32 rgba1 = default; - Rgba32 rgba2 = default; - Rgba32 rgba3 = default; + Rgba32 rgba0; + Rgba32 rgba1; int i, j; int dstIdx = 0; for (i = 0, j = 0; i < (width >> 1); i += 1, j += 2, dstIdx += 4) { - TPixel color = rowSpan[j]; - color.ToRgba32(ref rgba0); - color = rowSpan[j + 1]; - color.ToRgba32(ref rgba1); - color = nextRowSpan[j]; - color.ToRgba32(ref rgba2); - color = nextRowSpan[j + 1]; - color.ToRgba32(ref rgba3); + rgba0 = rowSpan[j]; + rgba1 = rowSpan[j + 1]; + Rgba32 rgba2 = nextRowSpan[j]; + Rgba32 rgba3 = nextRowSpan[j + 1]; uint a = (uint)(rgba0.A + rgba1.A + rgba2.A + rgba3.A); int r, g, b; if (a == 4 * 0xff || a == 0) @@ -186,10 +156,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy if ((width & 1) != 0) { - TPixel color = rowSpan[j]; - color.ToRgba32(ref rgba0); - color = nextRowSpan[j]; - color.ToRgba32(ref rgba1); + rgba0 = rowSpan[j]; + rgba1 = nextRowSpan[j]; uint a = (uint)(2u * (rgba0.A + rgba1.A)); int r, g, b; if (a == 4 * 0xff || a == 0) diff --git a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs index ecc940782..985300a56 100644 --- a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Webp if (this.lossy) { - var enc = new Vp8Encoder(this.memoryAllocator, image.Width, image.Height, this.quality, this.method, this.entropyPasses); + var enc = new Vp8Encoder(this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, this.entropyPasses); enc.Encode(image, stream); } else