diff --git a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs index fab6788061..7562c47558 100644 --- a/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs +++ b/src/ImageSharp/Formats/Png/Filters/PaethFilter.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -82,6 +83,43 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters sum += Numerics.Abs(unchecked((sbyte)res)); } +#if SUPPORTS_RUNTIME_INTRINSICS + if (Vector.IsHardwareAccelerated) + { + Vector sumAccumulator = Vector.Zero; + + for (int xLeft = x - bytesPerPixel; x + Vector.Count <= scanline.Length; xLeft += Vector.Count) + { + var scan = new Vector(scanline.Slice(x)); + var left = new Vector(scanline.Slice(xLeft)); + var above = new Vector(previousScanline.Slice(x)); + var upperLeft = new Vector(previousScanline.Slice(xLeft)); + + Vector res = scan - PaethPredictor(left, above, upperLeft); + res.CopyTo(result.Slice(x + 1)); // + 1 to skip filter type + x += Vector.Count; + + Vector.Widen( + Vector.Abs(Vector.AsVectorSByte(res)), + out Vector shortLow, + out Vector shortHigh); + + Vector.Widen(shortLow, out Vector intLow, out Vector intHigh); + sumAccumulator += intLow; + sumAccumulator += intHigh; + + Vector.Widen(shortHigh, out intLow, out intHigh); + sumAccumulator += intLow; + sumAccumulator += intHigh; + } + + for (int i = 0; i < Vector.Count; i++) + { + sum += sumAccumulator[i]; + } + } +#endif + for (int xLeft = x - bytesPerPixel; x < scanline.Length; ++xLeft /* Note: ++x happens in the body to avoid one add operation */) { byte scan = Unsafe.Add(ref scanBaseRef, x); @@ -127,5 +165,36 @@ namespace SixLabors.ImageSharp.Formats.Png.Filters return upperLeft; } + + private static Vector PaethPredictor(Vector left, Vector above, Vector upperLeft) + { + Vector.Widen(left, out Vector a1, out Vector a2); + Vector.Widen(above, out Vector b1, out Vector b2); + Vector.Widen(upperLeft, out Vector c1, out Vector c2); + + Vector p1 = PaethPredictor(Vector.AsVectorInt16(a1), Vector.AsVectorInt16(b1), Vector.AsVectorInt16(c1)); + Vector p2 = PaethPredictor(Vector.AsVectorInt16(a2), Vector.AsVectorInt16(b2), Vector.AsVectorInt16(c2)); + return Vector.AsVectorByte(Vector.Narrow(p1, p2)); + } + + private static Vector PaethPredictor(Vector left, Vector above, Vector upperLeft) + { + Vector p = left + above - upperLeft; + var pa = Vector.Abs(p - left); + var pb = Vector.Abs(p - above); + var pc = Vector.Abs(p - upperLeft); + + var pa_pb = Vector.LessThanOrEqual(pa, pb); + var pa_pc = Vector.LessThanOrEqual(pa, pc); + var pb_pc = Vector.LessThanOrEqual(pb, pc); + + return Vector.ConditionalSelect( + condition: Vector.BitwiseAnd(pa_pb, pa_pc), + left: left, + right: Vector.ConditionalSelect( + condition: pb_pc, + left: above, + right: upperLeft)); + } } }