Browse Source

Fix issue with AccumulateRgba in the last row

pull/1552/head
Brian Popow 5 years ago
parent
commit
a534513ee9
  1. 15
      src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
  2. 32
      src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs
  3. 2
      tests/ImageSharp.Tests/Formats/WebP/PredictorEncoderTests.cs

15
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<TPixel>
{
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<ushort> tmpRgb = this.memoryAllocator.Allocate<ushort>(4 * uvWidth);
Span<ushort> 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<TPixel> rowSpan = image.GetPixelRowSpan(rowIndex);
Span<TPixel> 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<TPixel> 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<byte> a, Span<byte> b) => GetSse(a, b, 16, 16);
[MethodImpl(InliningOptions.ShortMethod)]
private static int Vp8Sse16X8(Span<byte> a, Span<byte> 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
/// </summary>
[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;

32
src/ImageSharp/Formats/WebP/Lossy/YuvConversion.cs

@ -21,12 +21,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// </summary>
/// <typeparam name="TPixel">The pixel type of the image,</typeparam>
/// <param name="image">The image to check.</param>
/// <param name="rowIdxStart">The row to start with.</param>
/// <param name="rowIdxEnd">The row to end with.</param>
/// <returns>Returns true if alpha has non-0xff values.</returns>
public static bool CheckNonOpaque<TPixel>(Image<TPixel> image)
public static bool CheckNonOpaque<TPixel>(Image<TPixel> image, int rowIdxStart, int rowIdxEnd)
where TPixel : unmanaged, IPixel<TPixel>
{
Rgba32 rgba = default;
for (int rowIndex = 0; rowIndex < image.Height; rowIndex++)
for (int rowIndex = rowIdxStart; rowIndex <= rowIdxEnd; rowIndex++)
{
Span<TPixel> rowSpan = image.GetPixelRowSpan(rowIndex);
for (int x = 0; x < image.Width; x++)
@ -43,6 +45,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return false;
}
/// <summary>
/// Converts a rgba pixel row to Y.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
/// <param name="rowSpan">The row span to convert.</param>
/// <param name="y">The destination span for y.</param>
/// <param name="width">The width.</param>
public static void ConvertRgbaToY<TPixel>(Span<TPixel> rowSpan, Span<byte> y, int width)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -55,6 +64,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
}
/// <summary>
/// Converts a rgb row of pixels to UV.
/// </summary>
/// <param name="rgb">The RGB pixel row.</param>
/// <param name="u">The destination span for u.</param>
/// <param name="v">The destination span for v.</param>
/// <param name="width">The width.</param>
public static void ConvertRgbaToUv(Span<ushort> rgb, Span<byte> u, Span<byte> 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)

2
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]

Loading…
Cancel
Save