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