diff --git a/ImageSharp.sln b/ImageSharp.sln
index c188d93150..f16f98ac59 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.28902.138
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_root", "_root", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
@@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_root", "_root", "{C317F1B1
ci-build.ps1 = ci-build.ps1
ci-pack.ps1 = ci-pack.ps1
ci-test.ps1 = ci-test.ps1
+ codecov.yml = codecov.yml
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
LICENSE = LICENSE
diff --git a/codecov.yml b/codecov.yml
index 833fc0a51a..310eefb8c2 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -9,3 +9,14 @@ codecov:
# Avoid Report Expired
# https://docs.codecov.io/docs/codecov-yaml#section-expired-reports
max_report_age: off
+
+coverage:
+ # Use integer precision
+ # https://docs.codecov.com/docs/codecovyml-reference#coverageprecision
+ precision: 0
+
+ # Explicitly control coverage status checks
+ # https://docs.codecov.com/docs/commit-status#disabling-a-status
+ status:
+ project: on
+ patch: off
diff --git a/shared-infrastructure b/shared-infrastructure
index a042aba176..33cb12ca77 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit a042aba176cdb840d800c6ed4cfe41a54fb7b1e3
+Subproject commit 33cb12ca77f919b44de56f344d2627cc2a108c3a
diff --git a/src/ImageSharp/Color/Color.Conversions.cs b/src/ImageSharp/Color/Color.Conversions.cs
index 0455fd26a4..bf7869e53d 100644
--- a/src/ImageSharp/Color/Color.Conversions.cs
+++ b/src/ImageSharp/Color/Color.Conversions.cs
@@ -17,56 +17,118 @@ namespace SixLabors.ImageSharp
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgba64 pixel) => this.data = pixel;
+ public Color(Rgba64 pixel)
+ {
+ this.data = pixel;
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(Rgb48 pixel)
+ {
+ this.data = new Rgba64(pixel.R, pixel.G, pixel.B, ushort.MaxValue);
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(La32 pixel)
+ {
+ this.data = new Rgba64(pixel.L, pixel.L, pixel.L, pixel.A);
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The containing the color information.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public Color(L16 pixel)
+ {
+ this.data = new Rgba64(pixel.PackedValue, pixel.PackedValue, pixel.PackedValue, ushort.MaxValue);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgba32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Rgba32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Argb32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Argb32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Bgra32 pixel) => this.data = new Rgba64(pixel);
+ public Color(Bgra32 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Rgb24 pixel) => this.data = new Rgba64(pixel);
+ public Color(Rgb24 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Bgr24 pixel) => this.data = new Rgba64(pixel);
+ public Color(Bgr24 pixel)
+ {
+ this.data = new Rgba64(pixel);
+ this.boxedHighPrecisionPixel = null;
+ }
///
/// Initializes a new instance of the struct.
///
/// The containing the color information.
[MethodImpl(InliningOptions.ShortMethod)]
- public Color(Vector4 vector) => this.data = new Rgba64(vector);
+ public Color(Vector4 vector)
+ {
+ vector = Numerics.Clamp(vector, Vector4.Zero, Vector4.One);
+ this.boxedHighPrecisionPixel = new RgbaVector(vector.X, vector.Y, vector.Z, vector.W);
+ this.data = default;
+ }
///
/// Converts a to .
///
/// The .
/// The .
- public static explicit operator Vector4(Color color) => color.data.ToVector4();
+ public static explicit operator Vector4(Color color) => color.ToVector4();
///
/// Converts an to .
@@ -74,24 +136,82 @@ namespace SixLabors.ImageSharp
/// The .
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static explicit operator Color(Vector4 source) => new Color(source);
+ public static explicit operator Color(Vector4 source) => new(source);
[MethodImpl(InliningOptions.ShortMethod)]
- internal Rgba32 ToRgba32() => this.data.ToRgba32();
+ internal Rgba32 ToRgba32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToRgba32();
+ }
+
+ Rgba32 value = default;
+ this.boxedHighPrecisionPixel.ToRgba32(ref value);
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Bgra32 ToBgra32() => this.data.ToBgra32();
+ internal Bgra32 ToBgra32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToBgra32();
+ }
+
+ Bgra32 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Argb32 ToArgb32() => this.data.ToArgb32();
+ internal Argb32 ToArgb32()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToArgb32();
+ }
+
+ Argb32 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Rgb24 ToRgb24() => this.data.ToRgb24();
+ internal Rgb24 ToRgb24()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToRgb24();
+ }
+
+ Rgb24 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Bgr24 ToBgr24() => this.data.ToBgr24();
+ internal Bgr24 ToBgr24()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToBgr24();
+ }
+
+ Bgr24 value = default;
+ value.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
+ return value;
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- internal Vector4 ToVector4() => this.data.ToVector4();
+ internal Vector4 ToVector4()
+ {
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.ToScaledVector4();
+ }
+
+ return this.boxedHighPrecisionPixel.ToScaledVector4();
+ }
}
}
diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs
index d5eedc160b..7c21d62ddf 100644
--- a/src/ImageSharp/Color/Color.cs
+++ b/src/ImageSharp/Color/Color.cs
@@ -4,7 +4,6 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp
@@ -21,6 +20,7 @@ namespace SixLabors.ImageSharp
public readonly partial struct Color : IEquatable
{
private readonly Rgba64 data;
+ private readonly IPixel boxedHighPrecisionPixel;
[MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b, byte a)
@@ -30,6 +30,8 @@ namespace SixLabors.ImageSharp
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ColorNumerics.UpscaleFrom8BitTo16Bit(a));
+
+ this.boxedHighPrecisionPixel = null;
}
[MethodImpl(InliningOptions.ShortMethod)]
@@ -40,6 +42,15 @@ namespace SixLabors.ImageSharp
ColorNumerics.UpscaleFrom8BitTo16Bit(g),
ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ushort.MaxValue);
+
+ this.boxedHighPrecisionPixel = null;
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private Color(IPixel pixel)
+ {
+ this.boxedHighPrecisionPixel = pixel;
+ this.data = default;
}
///
@@ -52,13 +63,10 @@ namespace SixLabors.ImageSharp
/// otherwise, false.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public static bool operator ==(Color left, Color right)
- {
- return left.Equals(right);
- }
+ public static bool operator ==(Color left, Color right) => left.Equals(right);
///
- /// Checks whether two structures are equal.
+ /// Checks whether two structures are not equal.
///
/// The left hand operand.
/// The right hand operand.
@@ -67,10 +75,7 @@ namespace SixLabors.ImageSharp
/// otherwise, false.
///
[MethodImpl(InliningOptions.ShortMethod)]
- public static bool operator !=(Color left, Color right)
- {
- return !left.Equals(right);
- }
+ public static bool operator !=(Color left, Color right) => !left.Equals(right);
///
/// Creates a from RGBA bytes.
@@ -81,7 +86,7 @@ namespace SixLabors.ImageSharp
/// The alpha component (0-255).
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static Color FromRgba(byte r, byte g, byte b, byte a) => new Color(r, g, b, a);
+ public static Color FromRgba(byte r, byte g, byte b, byte a) => new(r, g, b, a);
///
/// Creates a from RGB bytes.
@@ -91,7 +96,46 @@ namespace SixLabors.ImageSharp
/// The blue component (0-255).
/// The .
[MethodImpl(InliningOptions.ShortMethod)]
- public static Color FromRgb(byte r, byte g, byte b) => new Color(r, g, b);
+ public static Color FromRgb(byte r, byte g, byte b) => new(r, g, b);
+
+ ///
+ /// Creates a from the given .
+ ///
+ /// The pixel to convert from.
+ /// The pixel format.
+ /// The .
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static Color FromPixel(TPixel pixel)
+ where TPixel : unmanaged, IPixel
+ {
+ // Avoid boxing in case we can convert to Rgba64 safely and efficently
+ if (typeof(TPixel) == typeof(Rgba64))
+ {
+ return new((Rgba64)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(Rgb48))
+ {
+ return new((Rgb48)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(La32))
+ {
+ return new((La32)(object)pixel);
+ }
+ else if (typeof(TPixel) == typeof(L16))
+ {
+ return new((L16)(object)pixel);
+ }
+ else if (Unsafe.SizeOf() <= Unsafe.SizeOf())
+ {
+ Rgba32 p = default;
+ pixel.ToRgba32(ref p);
+ return new(p);
+ }
+ else
+ {
+ return new(pixel);
+ }
+ }
///
/// Creates a new instance of the struct
@@ -213,7 +257,7 @@ namespace SixLabors.ImageSharp
public override string ToString() => this.ToHex();
///
- /// Converts the color instance to a specified type.
+ /// Converts the color instance to a specified type.
///
/// The pixel type to convert to.
/// The pixel value.
@@ -221,13 +265,18 @@ namespace SixLabors.ImageSharp
public TPixel ToPixel()
where TPixel : unmanaged, IPixel
{
- TPixel pixel = default;
+ if (this.boxedHighPrecisionPixel is TPixel pixel)
+ {
+ return pixel;
+ }
+
+ pixel = default;
pixel.FromRgba64(this.data);
return pixel;
}
///
- /// Bulk converts a span of to a span of a specified type.
+ /// Bulk converts a span of to a span of a specified type.
///
/// The pixel type to convert to.
/// The configuration.
@@ -240,28 +289,38 @@ namespace SixLabors.ImageSharp
Span destination)
where TPixel : unmanaged, IPixel
{
- ReadOnlySpan rgba64Span = MemoryMarshal.Cast(source);
- PixelOperations.Instance.FromRgba64(configuration, rgba64Span, destination);
+ Guard.DestinationShouldNotBeTooShort(source, destination, nameof(destination));
+ for (int i = 0; i < source.Length; i++)
+ {
+ destination[i] = source[i].ToPixel();
+ }
}
///
[MethodImpl(InliningOptions.ShortMethod)]
public bool Equals(Color other)
{
- return this.data.PackedValue == other.data.PackedValue;
+ if (this.boxedHighPrecisionPixel is null && other.boxedHighPrecisionPixel is null)
+ {
+ return this.data.PackedValue == other.data.PackedValue;
+ }
+
+ return this.boxedHighPrecisionPixel?.Equals(other.boxedHighPrecisionPixel) == true;
}
///
- public override bool Equals(object obj)
- {
- return obj is Color other && this.Equals(other);
- }
+ public override bool Equals(object obj) => obj is Color other && this.Equals(other);
///
[MethodImpl(InliningOptions.ShortMethod)]
public override int GetHashCode()
{
- return this.data.PackedValue.GetHashCode();
+ if (this.boxedHighPrecisionPixel is null)
+ {
+ return this.data.PackedValue.GetHashCode();
+ }
+
+ return this.boxedHighPrecisionPixel.GetHashCode();
}
}
}
diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
index c8c1359818..da8481c26c 100644
--- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs
@@ -1072,7 +1072,7 @@ namespace SixLabors.ImageSharp.Formats.Png
int bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
while (bytesRead != 0)
{
- uncompressedBytes.AddRange(this.buffer.AsSpan().Slice(0, bytesRead).ToArray());
+ uncompressedBytes.AddRange(this.buffer.AsSpan(0, bytesRead).ToArray());
bytesRead = inflateStream.CompressedStream.Read(this.buffer, 0, this.buffer.Length);
}
diff --git a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
index 7dbf49d45e..000de4f88c 100644
--- a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
@@ -35,6 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
/// Gets the number of entropy-analysis passes (in [1..10]).
+ /// Defaults to 1.
///
int EntropyPasses { get; }
diff --git a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
index d5db3dffa5..a10ec6eabb 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs
@@ -4,23 +4,78 @@
using System;
using System.Buffers.Binary;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
internal static class LossyUtils
{
+#if SUPPORTS_RUNTIME_INTRINSICS
+ private static readonly Vector128 Mean16x4Mask = Vector128.Create((short)0x00ff).AsByte();
+#endif
+
+ // Note: method name in libwebp reference implementation is called VP8SSE16x16.
[MethodImpl(InliningOptions.ShortMethod)]
- public static int Vp8Sse16X16(Span a, Span b) => GetSse(a, b, 16, 16);
+ public static int Vp8_Sse16X16(Span a, Span b) => Vp8_SseNxN(a, b, 16, 16);
+ // Note: method name in libwebp reference implementation is called VP8SSE16x8.
[MethodImpl(InliningOptions.ShortMethod)]
- public static int Vp8Sse16X8(Span a, Span b) => GetSse(a, b, 16, 8);
+ public static int Vp8_Sse16X8(Span a, Span b) => Vp8_SseNxN(a, b, 16, 8);
+ // Note: method name in libwebp reference implementation is called VP8SSE4x4.
[MethodImpl(InliningOptions.ShortMethod)]
- public static int Vp8Sse4X4(Span a, Span b) => GetSse(a, b, 4, 4);
+ public static int Vp8_Sse4X4(Span a, Span b)
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse2.IsSupported)
+ {
+ // Load values.
+ ref byte aRef = ref MemoryMarshal.GetReference(a);
+ Vector128 a0 = Unsafe.As>(ref aRef);
+ Vector128 a1 = Unsafe.As>(ref Unsafe.Add(ref aRef, WebpConstants.Bps));
+ Vector128 a2 = Unsafe.As>(ref Unsafe.Add(ref aRef, WebpConstants.Bps * 2));
+ Vector128 a3 = Unsafe.As>(ref Unsafe.Add(ref aRef, WebpConstants.Bps * 3));
+ ref byte bRef = ref MemoryMarshal.GetReference(b);
+ Vector128 b0 = Unsafe.As>(ref bRef);
+ Vector128 b1 = Unsafe.As>(ref Unsafe.Add(ref bRef, WebpConstants.Bps));
+ Vector128 b2 = Unsafe.As>(ref Unsafe.Add(ref bRef, WebpConstants.Bps * 2));
+ Vector128 b3 = Unsafe.As>(ref Unsafe.Add(ref bRef, WebpConstants.Bps * 3));
+
+ // Combine pair of lines.
+ Vector128 a01 = Sse2.UnpackLow(a0.AsInt32(), a1.AsInt32());
+ Vector128 a23 = Sse2.UnpackLow(a2.AsInt32(), a3.AsInt32());
+ Vector128 b01 = Sse2.UnpackLow(b0.AsInt32(), b1.AsInt32());
+ Vector128 b23 = Sse2.UnpackLow(b2.AsInt32(), b3.AsInt32());
+
+ // Convert to 16b.
+ Vector128 a01s = Sse2.UnpackLow(a01.AsByte(), Vector128.Zero);
+ Vector128 a23s = Sse2.UnpackLow(a23.AsByte(), Vector128.Zero);
+ Vector128 b01s = Sse2.UnpackLow(b01.AsByte(), Vector128.Zero);
+ Vector128 b23s = Sse2.UnpackLow(b23.AsByte(), Vector128.Zero);
+
+ // subtract, square and accumulate.
+ Vector128 d0 = Sse2.SubtractSaturate(a01s, b01s);
+ Vector128 d1 = Sse2.SubtractSaturate(a23s, b23s);
+ Vector128 e0 = Sse2.MultiplyAddAdjacent(d0.AsInt16(), d0.AsInt16());
+ Vector128 e1 = Sse2.MultiplyAddAdjacent(d1.AsInt16(), d1.AsInt16());
+ Vector128 sum = Sse2.Add(e0, e1);
+
+ return Numerics.ReduceSum(sum);
+ }
+ else
+#endif
+ {
+ return Vp8_SseNxN(a, b, 4, 4);
+ }
+ }
[MethodImpl(InliningOptions.ShortMethod)]
- public static int GetSse(Span a, Span b, int w, int h)
+ public static int Vp8_SseNxN(Span a, Span b, int w, int h)
{
int count = 0;
int aOffset = 0;
@@ -61,11 +116,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static int Vp8Disto16X16(Span a, Span b, Span w, Span scratch)
{
int d = 0;
+ int dataSize = (4 * WebpConstants.Bps) - 16;
for (int y = 0; y < 16 * WebpConstants.Bps; y += 4 * WebpConstants.Bps)
{
for (int x = 0; x < 16; x += 4)
{
- d += Vp8Disto4X4(a.Slice(x + y), b.Slice(x + y), w, scratch);
+ d += Vp8Disto4X4(a.Slice(x + y, dataSize), b.Slice(x + y, dataSize), w, scratch);
}
}
@@ -75,9 +131,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
public static int Vp8Disto4X4(Span a, Span b, Span w, Span scratch)
{
- int sum1 = TTransform(a, w, scratch);
- int sum2 = TTransform(b, w, scratch);
- return Math.Abs(sum2 - sum1) >> 5;
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse41.IsSupported)
+ {
+ int diffSum = TTransformSse41(a, b, w);
+ return Math.Abs(diffSum) >> 5;
+ }
+ else
+#endif
+ {
+ int sum1 = TTransform(a, w, scratch);
+ int sum2 = TTransform(b, w, scratch);
+ return Math.Abs(sum2 - sum1) >> 5;
+ }
}
public static void DC16(Span dst, Span yuv, int offset)
@@ -589,6 +655,122 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return sum;
}
+#if SUPPORTS_RUNTIME_INTRINSICS
+ ///
+ /// Hadamard transform
+ /// Returns the weighted sum of the absolute value of transformed coefficients.
+ /// w[] contains a row-major 4 by 4 symmetric matrix.
+ ///
+ public static int TTransformSse41(Span inputA, Span inputB, Span w)
+ {
+ // Load and combine inputs.
+ Vector128 ina0 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA));
+ Vector128 ina1 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(WebpConstants.Bps, 16)));
+ Vector128 ina2 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(WebpConstants.Bps * 2, 16)));
+ Vector128 ina3 = Unsafe.As>(ref MemoryMarshal.GetReference(inputA.Slice(WebpConstants.Bps * 3, 16))).AsInt64();
+ Vector128 inb0 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB));
+ Vector128 inb1 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(WebpConstants.Bps, 16)));
+ Vector128 inb2 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(WebpConstants.Bps * 2, 16)));
+ Vector128 inb3 = Unsafe.As>(ref MemoryMarshal.GetReference(inputB.Slice(WebpConstants.Bps * 3, 16))).AsInt64();
+
+ // Combine inA and inB (we'll do two transforms in parallel).
+ Vector128 inab0 = Sse2.UnpackLow(ina0.AsInt32(), inb0.AsInt32());
+ Vector128 inab1 = Sse2.UnpackLow(ina1.AsInt32(), inb1.AsInt32());
+ Vector128 inab2 = Sse2.UnpackLow(ina2.AsInt32(), inb2.AsInt32());
+ Vector128 inab3 = Sse2.UnpackLow(ina3.AsInt32(), inb3.AsInt32());
+ Vector128 tmp0 = Sse41.ConvertToVector128Int16(inab0.AsByte());
+ Vector128 tmp1 = Sse41.ConvertToVector128Int16(inab1.AsByte());
+ Vector128 tmp2 = Sse41.ConvertToVector128Int16(inab2.AsByte());
+ Vector128 tmp3 = Sse41.ConvertToVector128Int16(inab3.AsByte());
+
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+ // Vertical pass first to avoid a transpose (vertical and horizontal passes
+ // are commutative because w/kWeightY is symmetric) and subsequent transpose.
+ // Calculate a and b (two 4x4 at once).
+ Vector128 a0 = Sse2.Add(tmp0, tmp2);
+ Vector128 a1 = Sse2.Add(tmp1, tmp3);
+ Vector128 a2 = Sse2.Subtract(tmp1, tmp3);
+ Vector128 a3 = Sse2.Subtract(tmp0, tmp2);
+ Vector128 b0 = Sse2.Add(a0, a1);
+ Vector128 b1 = Sse2.Add(a3, a2);
+ Vector128 b2 = Sse2.Subtract(a3, a2);
+ Vector128 b3 = Sse2.Subtract(a0, a1);
+
+ // a00 a01 a02 a03 b00 b01 b02 b03
+ // a10 a11 a12 a13 b10 b11 b12 b13
+ // a20 a21 a22 a23 b20 b21 b22 b23
+ // a30 a31 a32 a33 b30 b31 b32 b33
+ // Transpose the two 4x4.
+ Vector128 transpose00 = Sse2.UnpackLow(b0, b1);
+ Vector128 transpose01 = Sse2.UnpackLow(b2, b3);
+ Vector128 transpose02 = Sse2.UnpackHigh(b0, b1);
+ Vector128 transpose03 = Sse2.UnpackHigh(b2, b3);
+
+ // a00 a10 a01 a11 a02 a12 a03 a13
+ // a20 a30 a21 a31 a22 a32 a23 a33
+ // b00 b10 b01 b11 b02 b12 b03 b13
+ // b20 b30 b21 b31 b22 b32 b23 b33
+ Vector128 transpose10 = Sse2.UnpackLow(transpose00.AsInt32(), transpose01.AsInt32());
+ Vector128 transpose11 = Sse2.UnpackLow(transpose02.AsInt32(), transpose03.AsInt32());
+ Vector128 transpose12 = Sse2.UnpackHigh(transpose00.AsInt32(), transpose01.AsInt32());
+ Vector128 transpose13 = Sse2.UnpackHigh(transpose02.AsInt32(), transpose03.AsInt32());
+
+ // a00 a10 a20 a30 a01 a11 a21 a31
+ // b00 b10 b20 b30 b01 b11 b21 b31
+ // a02 a12 a22 a32 a03 a13 a23 a33
+ // b02 b12 a22 b32 b03 b13 b23 b33
+ Vector128 output0 = Sse2.UnpackLow(transpose10.AsInt64(), transpose11.AsInt64());
+ Vector128 output1 = Sse2.UnpackHigh(transpose10.AsInt64(), transpose11.AsInt64());
+ Vector128 output2 = Sse2.UnpackLow(transpose12.AsInt64(), transpose13.AsInt64());
+ Vector128 output3 = Sse2.UnpackHigh(transpose12.AsInt64(), transpose13.AsInt64());
+
+ // a00 a10 a20 a30 b00 b10 b20 b30
+ // a01 a11 a21 a31 b01 b11 b21 b31
+ // a02 a12 a22 a32 b02 b12 b22 b32
+ // a03 a13 a23 a33 b03 b13 b23 b33
+ // Horizontal pass and difference of weighted sums.
+ Vector128 w0 = Unsafe.As>(ref MemoryMarshal.GetReference(w));
+ Vector128 w8 = Unsafe.As>(ref MemoryMarshal.GetReference(w.Slice(8, 8)));
+
+ // Calculate a and b (two 4x4 at once).
+ a0 = Sse2.Add(output0.AsInt16(), output2.AsInt16());
+ a1 = Sse2.Add(output1.AsInt16(), output3.AsInt16());
+ a2 = Sse2.Subtract(output1.AsInt16(), output3.AsInt16());
+ a3 = Sse2.Subtract(output0.AsInt16(), output2.AsInt16());
+ b0 = Sse2.Add(a0, a1);
+ b1 = Sse2.Add(a3, a2);
+ b2 = Sse2.Subtract(a3, a2);
+ b3 = Sse2.Subtract(a0, a1);
+
+ // Separate the transforms of inA and inB.
+ Vector128 ab0 = Sse2.UnpackLow(b0.AsInt64(), b1.AsInt64());
+ Vector128 ab2 = Sse2.UnpackLow(b2.AsInt64(), b3.AsInt64());
+ Vector128 bb0 = Sse2.UnpackHigh(b0.AsInt64(), b1.AsInt64());
+ Vector128 bb2 = Sse2.UnpackHigh(b2.AsInt64(), b3.AsInt64());
+
+ Vector128 ab0Abs = Ssse3.Abs(ab0.AsInt16());
+ Vector128 ab2Abs = Ssse3.Abs(ab2.AsInt16());
+ Vector128 b0Abs = Ssse3.Abs(bb0.AsInt16());
+ Vector128 bb2Abs = Ssse3.Abs(bb2.AsInt16());
+
+ // weighted sums.
+ Vector128 ab0mulw0 = Sse2.MultiplyAddAdjacent(ab0Abs.AsInt16(), w0.AsInt16());
+ Vector128 ab2mulw8 = Sse2.MultiplyAddAdjacent(ab2Abs.AsInt16(), w8.AsInt16());
+ Vector128 b0mulw0 = Sse2.MultiplyAddAdjacent(b0Abs.AsInt16(), w0.AsInt16());
+ Vector128 bb2mulw8 = Sse2.MultiplyAddAdjacent(bb2Abs.AsInt16(), w8.AsInt16());
+ Vector128 ab0ab2Sum = Sse2.Add(ab0mulw0, ab2mulw8);
+ Vector128 b0w0bb2w8Sum = Sse2.Add(b0mulw0, bb2mulw8);
+
+ // difference of weighted sums.
+ Vector128 result = Sse2.Subtract(ab0ab2Sum.AsInt32(), b0w0bb2w8Sum.AsInt32());
+
+ return Numerics.ReduceSum(result);
+ }
+#endif
+
public static void TransformTwo(Span src, Span dst, Span scratch)
{
TransformOne(src, dst, scratch);
@@ -598,7 +780,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
public static void TransformOne(Span src, Span dst, Span scratch)
{
Span tmp = scratch.Slice(0, 16);
- tmp.Clear();
int tmpOffset = 0;
for (int srcOffset = 0; srcOffset < 4; srcOffset++)
{
@@ -801,26 +982,55 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
FilterLoop24(v, offsetPlus4, 1, stride, 8, thresh, ithresh, hevThresh);
}
- [MethodImpl(InliningOptions.ShortMethod)]
- public static uint LoadUv(byte u, byte v) =>
- (uint)(u | (v << 16)); // We process u and v together stashed into 32bit(16bit each).
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void YuvToBgr(int y, int u, int v, Span bgr)
+ public static void Mean16x4(Span input, Span dc)
{
- bgr[0] = (byte)YuvToB(y, u);
- bgr[1] = (byte)YuvToG(y, u, v);
- bgr[2] = (byte)YuvToR(y, v);
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int YuvToB(int y, int u) => Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685);
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int YuvToG(int y, int u, int v) => Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708);
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Ssse3.IsSupported)
+ {
+ Vector128 a0 = Unsafe.As>(ref MemoryMarshal.GetReference(input));
+ Vector128 a1 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps, 16)));
+ Vector128 a2 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps * 2, 16)));
+ Vector128 a3 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps * 3, 16)));
+ Vector128 b0 = Sse2.ShiftRightLogical(a0.AsInt16(), 8); // hi byte
+ Vector128 b1 = Sse2.ShiftRightLogical(a1.AsInt16(), 8);
+ Vector128 b2 = Sse2.ShiftRightLogical(a2.AsInt16(), 8);
+ Vector128 b3 = Sse2.ShiftRightLogical(a3.AsInt16(), 8);
+ Vector128 c0 = Sse2.And(a0, Mean16x4Mask); // lo byte
+ Vector128 c1 = Sse2.And(a1, Mean16x4Mask);
+ Vector128 c2 = Sse2.And(a2, Mean16x4Mask);
+ Vector128 c3 = Sse2.And(a3, Mean16x4Mask);
+ Vector128 d0 = Sse2.Add(b0.AsInt32(), c0.AsInt32());
+ Vector128 d1 = Sse2.Add(b1.AsInt32(), c1.AsInt32());
+ Vector128 d2 = Sse2.Add(b2.AsInt32(), c2.AsInt32());
+ Vector128 d3 = Sse2.Add(b3.AsInt32(), c3.AsInt32());
+ Vector128 e0 = Sse2.Add(d0, d1);
+ Vector128 e1 = Sse2.Add(d2, d3);
+ Vector128 f0 = Sse2.Add(e0, e1);
+ Vector128 hadd = Ssse3.HorizontalAdd(f0.AsInt16(), f0.AsInt16());
+ Vector128 wide = Sse2.UnpackLow(hadd, Vector128.Zero).AsUInt32();
+
+ ref uint outputRef = ref MemoryMarshal.GetReference(dc);
+ Unsafe.As>(ref outputRef) = wide;
+ }
+ else
+#endif
+ {
+ for (int k = 0; k < 4; k++)
+ {
+ uint avg = 0;
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ avg += input[x + (y * WebpConstants.Bps)];
+ }
+ }
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int YuvToR(int y, int v) => Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234);
+ dc[k] = avg;
+ input = input.Slice(4); // go to next 4x4 block.
+ }
+ }
+ }
[MethodImpl(InliningOptions.ShortMethod)]
public static byte Avg2(byte a, byte b) => (byte)((a + b + 1) >> 1);
@@ -1026,9 +1236,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
return WebpLookupTables.Abs0(p1 - p0) > thresh || WebpLookupTables.Abs0(q1 - q0) > thresh;
}
- [MethodImpl(InliningOptions.ShortMethod)]
- private static int MultHi(int v, int coeff) => (v * coeff) >> 8;
-
[MethodImpl(InliningOptions.ShortMethod)]
private static void Store(Span dst, int x, int y, int v)
{
@@ -1051,13 +1258,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
[MethodImpl(InliningOptions.ShortMethod)]
private static int Mul2(int a) => (a * 35468) >> 16;
- [MethodImpl(InliningOptions.ShortMethod)]
- private static byte Clip8(int v)
- {
- int yuvMask = (256 << 6) - 1;
- return (byte)((v & ~yuvMask) == 0 ? v >> 6 : v < 0 ? 0 : 255);
- }
-
[MethodImpl(InliningOptions.ShortMethod)]
private static void Put8x8uv(byte value, Span dst)
{
diff --git a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs
index 18d7494f0f..38ed80590d 100644
--- a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs
+++ b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs
@@ -3,13 +3,18 @@
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
namespace SixLabors.ImageSharp.Formats.Webp.Lossy
{
///
/// Quantization methods.
///
- internal static class QuantEnc
+ internal static unsafe class QuantEnc
{
private static readonly byte[] Zigzag = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 };
@@ -17,6 +22,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private const int MaxLevel = 2047;
+#if SUPPORTS_RUNTIME_INTRINSICS
+ private static readonly Vector128 MaxCoeff2047 = Vector128.Create((short)MaxLevel);
+
+ private static readonly Vector128 CstLo = Vector128.Create(0, 1, 2, 3, 8, 9, 254, 255, 10, 11, 4, 5, 6, 7, 12, 13);
+
+ private static readonly Vector128 Cst7 = Vector128.Create(254, 255, 254, 255, 254, 255, 254, 255, 14, 15, 254, 255, 254, 255, 254, 255);
+
+ private static readonly Vector128 CstHi = Vector128.Create(2, 3, 8, 9, 10, 11, 4, 5, 254, 255, 6, 7, 12, 13, 14, 15);
+
+ private static readonly Vector128 Cst8 = Vector128.Create(254, 255, 254, 255, 254, 255, 0, 1, 254, 255, 254, 255, 254, 255, 254, 255);
+#endif
+
// Diffusion weights. We under-correct a bit (15/16th of the error is actually
// diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0.
private const int C1 = 7; // fraction of error sent to the 4x4 block below
@@ -49,7 +66,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode);
// Measure RD-score.
- rdCur.D = LossyUtils.Vp8Sse16X16(src, tmpDst);
+ rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst);
rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0;
rdCur.H = WebpConstants.Vp8FixedCostsI16[mode];
rdCur.R = it.GetCostLuma16(rdCur, proba, res);
@@ -143,7 +160,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode);
// Compute RD-score.
- rdTmp.D = LossyUtils.Vp8Sse4X4(src, tmpDst);
+ rdTmp.D = LossyUtils.Vp8_Sse4X4(src, tmpDst);
rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0;
rdTmp.H = modeCosts[mode];
@@ -234,7 +251,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode);
// Compute RD-score
- rdUv.D = LossyUtils.Vp8Sse16X8(src, tmpDst);
+ rdUv.D = LossyUtils.Vp8_Sse16X8(src, tmpDst);
rdUv.SD = 0; // not calling TDisto here: it tends to flatten areas.
rdUv.H = WebpConstants.Vp8FixedCostsUv[mode];
rdUv.R = it.GetCostUv(rdUv, proba, res);
@@ -298,14 +315,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
Vp8Encoding.FTransformWht(tmp, dcTmp, scratch);
- nz |= QuantizeBlock(dcTmp, rd.YDcLevels, dqm.Y2) << 24;
+ nz |= QuantizeBlock(dcTmp, rd.YDcLevels, ref dqm.Y2) << 24;
for (n = 0; n < 16; n += 2)
{
// Zero-out the first coeff, so that: a) nz is correct below, and
// b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified.
tmp[n * 16] = tmp[(n + 1) * 16] = 0;
- nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.YAcLevels.AsSpan(n * 16, 32), dqm.Y1) << n;
+ nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.YAcLevels.AsSpan(n * 16, 32), ref dqm.Y1) << n;
}
// Transform back.
@@ -323,10 +340,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
Span tmp = it.Scratch2.AsSpan(0, 16);
Span scratch = it.Scratch3.AsSpan(0, 16);
- tmp.Clear();
- scratch.Clear();
Vp8Encoding.FTransform(src, reference, tmp, scratch);
- int nz = QuantizeBlock(tmp, levels, dqm.Y1);
+ int nz = QuantizeBlock(tmp, levels, ref dqm.Y1);
Vp8Encoding.ITransform(reference, tmp, yuvOut, false, scratch);
return nz;
@@ -340,8 +355,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int n;
Span tmp = it.Scratch2.AsSpan(0, 8 * 16);
Span scratch = it.Scratch3.AsSpan(0, 16);
- tmp.Clear();
- scratch.Clear();
for (n = 0; n < 8; n += 2)
{
@@ -353,11 +366,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
scratch);
}
- CorrectDcValues(it, dqm.Uv, tmp, rd);
+ CorrectDcValues(it, ref dqm.Uv, tmp, rd);
for (n = 0; n < 8; n += 2)
{
- nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.UvLevels.AsSpan(n * 16, 32), dqm.Uv) << n;
+ nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.UvLevels.AsSpan(n * 16, 32), ref dqm.Uv) << n;
}
for (n = 0; n < 8; n += 2)
@@ -394,7 +407,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{
Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]);
- long score = (LossyUtils.Vp8Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16);
+ long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16);
if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit)
{
@@ -441,7 +454,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (mode = 0; mode < WebpConstants.NumBModes; ++mode)
{
Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
- long score = (LossyUtils.Vp8Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4);
+ long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4);
if (score < bestI4Score)
{
bestI4Mode = mode;
@@ -490,7 +503,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
{
Span reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]);
- long score = (LossyUtils.Vp8Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv);
+ long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv);
if (score < bestUvScore)
{
bestMode = mode;
@@ -508,58 +521,155 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
[MethodImpl(InliningOptions.ShortMethod)]
- public static int Quantize2Blocks(Span input, Span output, Vp8Matrix mtx)
+ public static int Quantize2Blocks(Span input, Span output, ref Vp8Matrix mtx)
{
- int nz = QuantizeBlock(input, output, mtx) << 0;
- nz |= QuantizeBlock(input.Slice(1 * 16), output.Slice(1 * 16), mtx) << 1;
+ int nz = QuantizeBlock(input.Slice(0, 16), output.Slice(0, 16), ref mtx) << 0;
+ nz |= QuantizeBlock(input.Slice(1 * 16, 16), output.Slice(1 * 16, 16), ref mtx) << 1;
return nz;
}
- public static int QuantizeBlock(Span input, Span output, Vp8Matrix mtx)
+ public static int QuantizeBlock(Span input, Span output, ref Vp8Matrix mtx)
{
- int last = -1;
- int n;
- for (n = 0; n < 16; ++n)
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse41.IsSupported)
+ {
+ // Load all inputs.
+ Vector128 input0 = Unsafe.As>(ref MemoryMarshal.GetReference(input));
+ Vector128 input8 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(8, 8)));
+ Vector128 iq0 = Unsafe.As>(ref mtx.IQ[0]);
+ Vector128 iq8 = Unsafe.As>(ref mtx.IQ[8]);
+ Vector128 q0 = Unsafe.As>(ref mtx.Q[0]);
+ Vector128 q8 = Unsafe.As>(ref mtx.Q[8]);
+
+ // coeff = abs(in)
+ Vector128 coeff0 = Ssse3.Abs(input0);
+ Vector128 coeff8 = Ssse3.Abs(input8);
+
+ // coeff = abs(in) + sharpen
+ Vector128 sharpen0 = Unsafe.As>(ref mtx.Sharpen[0]);
+ Vector128 sharpen8 = Unsafe.As>(ref mtx.Sharpen[8]);
+ Sse2.Add(coeff0.AsInt16(), sharpen0);
+ Sse2.Add(coeff8.AsInt16(), sharpen8);
+
+ // out = (coeff * iQ + B) >> QFIX
+ // doing calculations with 32b precision (QFIX=17)
+ // out = (coeff * iQ)
+ Vector128 coeffiQ0H = Sse2.MultiplyHigh(coeff0, iq0);
+ Vector128 coeffiQ0L = Sse2.MultiplyLow(coeff0, iq0);
+ Vector128 coeffiQ8H = Sse2.MultiplyHigh(coeff8, iq8);
+ Vector128 coeffiQ8L = Sse2.MultiplyLow(coeff8, iq8);
+ Vector128 out00 = Sse2.UnpackLow(coeffiQ0L, coeffiQ0H);
+ Vector128 out04 = Sse2.UnpackHigh(coeffiQ0L, coeffiQ0H);
+ Vector128 out08 = Sse2.UnpackLow(coeffiQ8L, coeffiQ8H);
+ Vector128 out12 = Sse2.UnpackHigh(coeffiQ8L, coeffiQ8H);
+
+ // out = (coeff * iQ + B)
+ Vector128 bias00 = Unsafe.As>(ref mtx.Bias[0]);
+ Vector128 bias04 = Unsafe.As>(ref mtx.Bias[4]);
+ Vector128 bias08 = Unsafe.As>(ref mtx.Bias[8]);
+ Vector128 bias12 = Unsafe.As>(ref mtx.Bias[12]);
+ out00 = Sse2.Add(out00.AsInt32(), bias00.AsInt32()).AsUInt16();
+ out04 = Sse2.Add(out04.AsInt32(), bias04.AsInt32()).AsUInt16();
+ out08 = Sse2.Add(out08.AsInt32(), bias08.AsInt32()).AsUInt16();
+ out12 = Sse2.Add(out12.AsInt32(), bias12.AsInt32()).AsUInt16();
+
+ // out = QUANTDIV(coeff, iQ, B, QFIX)
+ out00 = Sse2.ShiftRightArithmetic(out00.AsInt32(), WebpConstants.QFix).AsUInt16();
+ out04 = Sse2.ShiftRightArithmetic(out04.AsInt32(), WebpConstants.QFix).AsUInt16();
+ out08 = Sse2.ShiftRightArithmetic(out08.AsInt32(), WebpConstants.QFix).AsUInt16();
+ out12 = Sse2.ShiftRightArithmetic(out12.AsInt32(), WebpConstants.QFix).AsUInt16();
+
+ // pack result as 16b
+ Vector128 out0 = Sse2.PackSignedSaturate(out00.AsInt32(), out04.AsInt32());
+ Vector128 out8 = Sse2.PackSignedSaturate(out08.AsInt32(), out12.AsInt32());
+
+ // if (coeff > 2047) coeff = 2047
+ out0 = Sse2.Min(out0, MaxCoeff2047);
+ out8 = Sse2.Min(out8, MaxCoeff2047);
+
+ // put sign back
+ out0 = Ssse3.Sign(out0, input0);
+ out8 = Ssse3.Sign(out8, input8);
+
+ // in = out * Q
+ input0 = Sse2.MultiplyLow(out0, q0.AsInt16());
+ input8 = Sse2.MultiplyLow(out8, q8.AsInt16());
+
+ // in = out * Q
+ ref short inputRef = ref MemoryMarshal.GetReference(input);
+ Unsafe.As>(ref inputRef) = input0;
+ Unsafe.As>(ref Unsafe.Add(ref inputRef, 8)) = input8;
+
+ // zigzag the output before storing it. The re-ordering is:
+ // 0 1 2 3 4 5 6 7 | 8 9 10 11 12 13 14 15
+ // -> 0 1 4[8]5 2 3 6 | 9 12 13 10 [7]11 14 15
+ // There's only two misplaced entries ([8] and [7]) that are crossing the
+ // reg's boundaries.
+ // We use pshufb instead of pshuflo/pshufhi.
+ Vector128 tmpLo = Ssse3.Shuffle(out0.AsByte(), CstLo);
+ Vector128 tmp7 = Ssse3.Shuffle(out0.AsByte(), Cst7); // extract #7
+ Vector128 tmpHi = Ssse3.Shuffle(out8.AsByte(), CstHi);
+ Vector128 tmp8 = Ssse3.Shuffle(out8.AsByte(), Cst8); // extract #8
+ Vector128 outZ0 = Sse2.Or(tmpLo, tmp8);
+ Vector128 outZ8 = Sse2.Or(tmpHi, tmp7);
+
+ ref short outputRef = ref MemoryMarshal.GetReference(output);
+ Unsafe.As>(ref outputRef) = outZ0.AsInt16();
+ Unsafe.As>(ref Unsafe.Add(ref outputRef, 8)) = outZ8.AsInt16();
+
+ Vector128 packedOutput = Sse2.PackSignedSaturate(outZ0.AsInt16(), outZ8.AsInt16());
+
+ // Detect if all 'out' values are zeros or not.
+ Vector128 cmpeq = Sse2.CompareEqual(packedOutput, Vector128.Zero);
+ return Sse2.MoveMask(cmpeq) != 0xffff ? 1 : 0;
+ }
+ else
+#endif
{
- int j = Zigzag[n];
- bool sign = input[j] < 0;
- uint coeff = (uint)((sign ? -input[j] : input[j]) + mtx.Sharpen[j]);
- if (coeff > mtx.ZThresh[j])
+ int last = -1;
+ int n;
+ for (n = 0; n < 16; ++n)
{
- uint q = mtx.Q[j];
- uint iQ = mtx.IQ[j];
- uint b = mtx.Bias[j];
- int level = QuantDiv(coeff, iQ, b);
- if (level > MaxLevel)
+ int j = Zigzag[n];
+ bool sign = input[j] < 0;
+ uint coeff = (uint)((sign ? -input[j] : input[j]) + mtx.Sharpen[j]);
+ if (coeff > mtx.ZThresh[j])
{
- level = MaxLevel;
- }
+ uint q = mtx.Q[j];
+ uint iQ = mtx.IQ[j];
+ uint b = mtx.Bias[j];
+ int level = QuantDiv(coeff, iQ, b);
+ if (level > MaxLevel)
+ {
+ level = MaxLevel;
+ }
- if (sign)
- {
- level = -level;
- }
+ if (sign)
+ {
+ level = -level;
+ }
- input[j] = (short)(level * (int)q);
- output[n] = (short)level;
- if (level != 0)
+ input[j] = (short)(level * (int)q);
+ output[n] = (short)level;
+ if (level != 0)
+ {
+ last = n;
+ }
+ }
+ else
{
- last = n;
+ output[n] = 0;
+ input[j] = 0;
}
}
- else
- {
- output[n] = 0;
- input[j] = 0;
- }
- }
- return last >= 0 ? 1 : 0;
+ return last >= 0 ? 1 : 0;
+ }
}
// Quantize as usual, but also compute and return the quantization error.
// Error is already divided by DSHIFT.
- public static int QuantizeSingle(Span v, Vp8Matrix mtx)
+ public static int QuantizeSingle(Span