diff --git a/src/ImageSharp/Common/Helpers/EnumUtils.cs b/src/ImageSharp/Common/Helpers/EnumUtils.cs
index d6bead6408..a598411629 100644
--- a/src/ImageSharp/Common/Helpers/EnumUtils.cs
+++ b/src/ImageSharp/Common/Helpers/EnumUtils.cs
@@ -19,15 +19,14 @@ namespace SixLabors.ImageSharp
/// The default value to return.
/// The .
public static TEnum Parse(int value, TEnum defaultValue)
- where TEnum : Enum
+ where TEnum : struct, Enum
{
- foreach (TEnum enumValue in Enum.GetValues(typeof(TEnum)))
+ DebugGuard.IsTrue(Unsafe.SizeOf() == sizeof(int), "Only int-sized enums are supported.");
+
+ TEnum valueEnum = Unsafe.As(ref value);
+ if (Enum.IsDefined(valueEnum))
{
- TEnum current = enumValue;
- if (value == Unsafe.As(ref current))
- {
- return enumValue;
- }
+ return valueEnum;
}
return defaultValue;
@@ -41,8 +40,10 @@ namespace SixLabors.ImageSharp
/// The flag.
/// The .
public static bool HasFlag(TEnum value, TEnum flag)
- where TEnum : Enum
+ where TEnum : struct, Enum
{
+ DebugGuard.IsTrue(Unsafe.SizeOf() == sizeof(int), "Only int-sized enums are supported.");
+
uint flagValue = Unsafe.As(ref flag);
return (Unsafe.As(ref value) & flagValue) == flagValue;
}
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
index 01b68f9fa5..ceeba0faae 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -21,6 +21,10 @@ namespace SixLabors.ImageSharp
public static ReadOnlySpan PermuteMaskSwitchInnerDWords8x32 => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0 };
+ private static ReadOnlySpan MoveFirst24BytesToSeparateLanes => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 };
+
+ internal static ReadOnlySpan ExtractRgb => new byte[] { 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, 0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF };
+
private static ReadOnlySpan ShuffleMaskPad4Nx16 => new byte[] { 0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80 };
private static ReadOnlySpan ShuffleMaskSlice4Nx16 => new byte[] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80 };
@@ -962,6 +966,49 @@ namespace SixLabors.ImageSharp
blueChannel = blueChannel.Slice(slice);
destination = destination.Slice(slice);
}
+
+ internal static void UnpackToRgbPlanesAvx2Reduce(
+ ref Span redChannel,
+ ref Span greenChannel,
+ ref Span blueChannel,
+ ref ReadOnlySpan source)
+ {
+ ref Vector256 rgbByteSpan = ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+ ref Vector256 destRRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(redChannel));
+ ref Vector256 destGRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(greenChannel));
+ ref Vector256 destBRef = ref Unsafe.As>(ref MemoryMarshal.GetReference(blueChannel));
+
+ Vector256 extractToLanesMask = Unsafe.As>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes));
+ Vector256 extractRgbMask = Unsafe.As>(ref MemoryMarshal.GetReference(ExtractRgb));
+ Vector256 rgb, rg, bx;
+ Vector256 r, g, b;
+
+ const int bytesPerRgbStride = 24;
+ int count = (int)((uint)source.Length / 8);
+ for (int i = 0; i < count; i++)
+ {
+ rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref rgbByteSpan, (IntPtr)(bytesPerRgbStride * i)).AsUInt32(), extractToLanesMask).AsByte();
+
+ rgb = Avx2.Shuffle(rgb, extractRgbMask);
+
+ rg = Avx2.UnpackLow(rgb, Vector256.Zero);
+ bx = Avx2.UnpackHigh(rgb, Vector256.Zero);
+
+ r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, Vector256.Zero).AsInt32());
+ g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, Vector256.Zero).AsInt32());
+ b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, Vector256.Zero).AsInt32());
+
+ Unsafe.Add(ref destRRef, i) = r;
+ Unsafe.Add(ref destGRef, i) = g;
+ Unsafe.Add(ref destBRef, i) = b;
+ }
+
+ int sliceCount = count * 8;
+ redChannel = redChannel.Slice(sliceCount);
+ greenChannel = greenChannel.Slice(sliceCount);
+ blueChannel = blueChannel.Slice(sliceCount);
+ source = source.Slice(sliceCount);
+ }
}
}
}
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
index 3ee2086cfe..fb62cb77e5 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.Pack.cs
@@ -65,6 +65,25 @@ namespace SixLabors.ImageSharp
PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination);
}
+ [MethodImpl(InliningOptions.ShortMethod)]
+ internal static void UnpackToRgbPlanes(
+ Span redChannel,
+ Span greenChannel,
+ Span blueChannel,
+ ReadOnlySpan source)
+ {
+ DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
+ DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
+ DebugGuard.IsTrue(source.Length <= redChannel.Length, nameof(source), "'source' span should not be bigger than the destination channels!");
+
+ if (Avx2.IsSupported)
+ {
+ HwIntrinsics.UnpackToRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref source);
+ }
+
+ UnpackToRgbPlanesScalar(redChannel, greenChannel, blueChannel, source);
+ }
+
private static void PackFromRgbPlanesScalarBatchedReduce(
ref ReadOnlySpan redChannel,
ref ReadOnlySpan greenChannel,
@@ -200,5 +219,29 @@ namespace SixLabors.ImageSharp
d.A = 255;
}
}
+
+ private static void UnpackToRgbPlanesScalar(
+ Span redChannel,
+ Span greenChannel,
+ Span blueChannel,
+ ReadOnlySpan source)
+ {
+ DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!");
+ DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!");
+ DebugGuard.IsTrue(source.Length <= redChannel.Length, nameof(source), "'source' span should not be bigger than the destination channels!");
+
+ ref float r = ref MemoryMarshal.GetReference(redChannel);
+ ref float g = ref MemoryMarshal.GetReference(greenChannel);
+ ref float b = ref MemoryMarshal.GetReference(blueChannel);
+ ref Rgb24 rgb = ref MemoryMarshal.GetReference(source);
+
+ for (int i = 0; i < source.Length; i++)
+ {
+ ref Rgb24 src = ref Unsafe.Add(ref rgb, i);
+ Unsafe.Add(ref r, i) = src.R;
+ Unsafe.Add(ref g, i) = src.G;
+ Unsafe.Add(ref b, i) = src.B;
+ }
+ }
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
index d83edbd5bc..040f7092f7 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
@@ -122,6 +122,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
+ public static Block8x8 Load(ReadOnlySpan data)
+ {
+ Unsafe.SkipInit(out Block8x8 result);
+ result.LoadFrom(data);
+ return result;
+ }
+
+ public void LoadFrom(ReadOnlySpan source)
+ {
+ for (int i = 0; i < Size; i++)
+ {
+ this[i] = source[i];
+ }
+ }
+
///
/// Load raw 16bit integers from source.
///
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
index 10501bada8..4c40659261 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Generated.cs
@@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
//
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
- internal partial struct Block8x8F
+ internal partial struct Block8x8F
{
///
/// Level shift by +maximum/2, clip to [0, maximum]
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs
new file mode 100644
index 0000000000..e50175ffa0
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopy.cs
@@ -0,0 +1,159 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Numerics;
+using System.Runtime.CompilerServices;
+
+// ReSharper disable UseObjectOrCollectionInitializer
+// ReSharper disable InconsistentNaming
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal partial struct Block8x8F
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void ScaledCopyFrom(ref float areaOrigin, int areaStride) =>
+ CopyFrom1x1Scale(ref Unsafe.As(ref areaOrigin), ref Unsafe.As(ref this), areaStride);
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
+ {
+ if (horizontalScale == 1 && verticalScale == 1)
+ {
+ CopyTo1x1Scale(ref Unsafe.As(ref this), ref Unsafe.As(ref areaOrigin), areaStride);
+ return;
+ }
+
+ if (horizontalScale == 2 && verticalScale == 2)
+ {
+ this.CopyTo2x2Scale(ref areaOrigin, areaStride);
+ return;
+ }
+
+ // TODO: Optimize: implement all cases with scale-specific, loopless code!
+ this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale);
+ }
+
+ private void CopyTo2x2Scale(ref float areaOrigin, int areaStride)
+ {
+ ref Vector2 destBase = ref Unsafe.As(ref areaOrigin);
+ int destStride = (int)((uint)areaStride / 2);
+
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 0, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 1, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 2, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 3, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 4, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 5, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 6, destStride);
+ WidenCopyRowImpl2x2(ref this.V0L, ref destBase, 7, destStride);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void WidenCopyRowImpl2x2(ref Vector4 selfBase, ref Vector2 destBase, nint row, nint destStride)
+ {
+ ref Vector4 sLeft = ref Unsafe.Add(ref selfBase, 2 * row);
+ ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
+
+ nint offset = 2 * row * destStride;
+ ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset));
+ ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride));
+
+ var xyLeft = new Vector4(sLeft.X);
+ xyLeft.Z = sLeft.Y;
+ xyLeft.W = sLeft.Y;
+
+ var zwLeft = new Vector4(sLeft.Z);
+ zwLeft.Z = sLeft.W;
+ zwLeft.W = sLeft.W;
+
+ var xyRight = new Vector4(sRight.X);
+ xyRight.Z = sRight.Y;
+ xyRight.W = sRight.Y;
+
+ var zwRight = new Vector4(sRight.Z);
+ zwRight.Z = sRight.W;
+ zwRight.W = sRight.W;
+
+ dTopLeft = xyLeft;
+ Unsafe.Add(ref dTopLeft, 1) = zwLeft;
+ Unsafe.Add(ref dTopLeft, 2) = xyRight;
+ Unsafe.Add(ref dTopLeft, 3) = zwRight;
+
+ dBottomLeft = xyLeft;
+ Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
+ Unsafe.Add(ref dBottomLeft, 2) = xyRight;
+ Unsafe.Add(ref dBottomLeft, 3) = zwRight;
+ }
+ }
+
+ [MethodImpl(InliningOptions.ColdPath)]
+ private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
+ {
+ for (int y = 0; y < 8; y++)
+ {
+ int yy = y * verticalScale;
+ int y8 = y * 8;
+
+ for (int x = 0; x < 8; x++)
+ {
+ int xx = x * horizontalScale;
+
+ float value = this[y8 + x];
+ nint baseIdx = (yy * areaStride) + xx;
+
+ for (nint i = 0; i < verticalScale; i++, baseIdx += areaStride)
+ {
+ for (nint j = 0; j < horizontalScale; j++)
+ {
+ // area[xx + j, yy + i] = value;
+ Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
+ }
+ }
+ }
+ }
+ }
+
+ private static void CopyTo1x1Scale(ref byte origin, ref byte dest, int areaStride)
+ {
+ int destStride = areaStride * sizeof(float);
+
+ CopyRowImpl(ref origin, ref dest, destStride, 0);
+ CopyRowImpl(ref origin, ref dest, destStride, 1);
+ CopyRowImpl(ref origin, ref dest, destStride, 2);
+ CopyRowImpl(ref origin, ref dest, destStride, 3);
+ CopyRowImpl(ref origin, ref dest, destStride, 4);
+ CopyRowImpl(ref origin, ref dest, destStride, 5);
+ CopyRowImpl(ref origin, ref dest, destStride, 6);
+ CopyRowImpl(ref origin, ref dest, destStride, 7);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void CopyRowImpl(ref byte origin, ref byte dest, int destStride, int row)
+ {
+ origin = ref Unsafe.Add(ref origin, row * 8 * sizeof(float));
+ dest = ref Unsafe.Add(ref dest, row * destStride);
+ Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float));
+ }
+ }
+
+ private static void CopyFrom1x1Scale(ref byte origin, ref byte dest, int areaStride)
+ {
+ int destStride = areaStride * sizeof(float);
+
+ CopyRowImpl(ref origin, ref dest, destStride, 0);
+ CopyRowImpl(ref origin, ref dest, destStride, 1);
+ CopyRowImpl(ref origin, ref dest, destStride, 2);
+ CopyRowImpl(ref origin, ref dest, destStride, 3);
+ CopyRowImpl(ref origin, ref dest, destStride, 4);
+ CopyRowImpl(ref origin, ref dest, destStride, 5);
+ CopyRowImpl(ref origin, ref dest, destStride, 6);
+ CopyRowImpl(ref origin, ref dest, destStride, 7);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void CopyRowImpl(ref byte origin, ref byte dest, int sourceStride, int row)
+ {
+ origin = ref Unsafe.Add(ref origin, row * sourceStride);
+ dest = ref Unsafe.Add(ref dest, row * 8 * sizeof(float));
+ Unsafe.CopyBlock(ref dest, ref origin, 8 * sizeof(float));
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
deleted file mode 100644
index b9fb18b8be..0000000000
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.ScaledCopyTo.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Memory;
-
-// ReSharper disable UseObjectOrCollectionInitializer
-// ReSharper disable InconsistentNaming
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components
-{
- internal partial struct Block8x8F
- {
- ///
- /// Copy block data into the destination color buffer pixel area with the provided horizontal and vertical scale factors.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ScaledCopyTo(in Buffer2DRegion region, int horizontalScale, int verticalScale)
- {
- ref float areaOrigin = ref region.GetReferenceToOrigin();
- this.ScaledCopyTo(ref areaOrigin, region.Stride, horizontalScale, verticalScale);
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public void ScaledCopyTo(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
- {
- if (horizontalScale == 1 && verticalScale == 1)
- {
- this.Copy1x1Scale(ref areaOrigin, areaStride);
- return;
- }
-
- if (horizontalScale == 2 && verticalScale == 2)
- {
- this.Copy2x2Scale(ref areaOrigin, areaStride);
- return;
- }
-
- // TODO: Optimize: implement all cases with scale-specific, loopless code!
- this.CopyArbitraryScale(ref areaOrigin, areaStride, horizontalScale, verticalScale);
- }
-
- public void Copy1x1Scale(ref float areaOrigin, int areaStride)
- {
- ref byte selfBase = ref Unsafe.As(ref this);
- ref byte destBase = ref Unsafe.As(ref areaOrigin);
- int destStride = areaStride * sizeof(float);
-
- CopyRowImpl(ref selfBase, ref destBase, destStride, 0);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 1);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 2);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 3);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 4);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 5);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 6);
- CopyRowImpl(ref selfBase, ref destBase, destStride, 7);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void CopyRowImpl(ref byte selfBase, ref byte destBase, int destStride, int row)
- {
- ref byte s = ref Unsafe.Add(ref selfBase, row * 8 * sizeof(float));
- ref byte d = ref Unsafe.Add(ref destBase, row * destStride);
- Unsafe.CopyBlock(ref d, ref s, 8 * sizeof(float));
- }
-
- private void Copy2x2Scale(ref float areaOrigin, int areaStride)
- {
- ref Vector2 destBase = ref Unsafe.As(ref areaOrigin);
- int destStride = areaStride / 2;
-
- this.WidenCopyRowImpl2x2(ref destBase, 0, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 1, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 2, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 3, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 4, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 5, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 6, destStride);
- this.WidenCopyRowImpl2x2(ref destBase, 7, destStride);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WidenCopyRowImpl2x2(ref Vector2 destBase, int row, int destStride)
- {
- ref Vector4 sLeft = ref Unsafe.Add(ref this.V0L, 2 * row);
- ref Vector4 sRight = ref Unsafe.Add(ref sLeft, 1);
-
- int offset = 2 * row * destStride;
- ref Vector4 dTopLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset));
- ref Vector4 dBottomLeft = ref Unsafe.As(ref Unsafe.Add(ref destBase, offset + destStride));
-
- var xyLeft = new Vector4(sLeft.X);
- xyLeft.Z = sLeft.Y;
- xyLeft.W = sLeft.Y;
-
- var zwLeft = new Vector4(sLeft.Z);
- zwLeft.Z = sLeft.W;
- zwLeft.W = sLeft.W;
-
- var xyRight = new Vector4(sRight.X);
- xyRight.Z = sRight.Y;
- xyRight.W = sRight.Y;
-
- var zwRight = new Vector4(sRight.Z);
- zwRight.Z = sRight.W;
- zwRight.W = sRight.W;
-
- dTopLeft = xyLeft;
- Unsafe.Add(ref dTopLeft, 1) = zwLeft;
- Unsafe.Add(ref dTopLeft, 2) = xyRight;
- Unsafe.Add(ref dTopLeft, 3) = zwRight;
-
- dBottomLeft = xyLeft;
- Unsafe.Add(ref dBottomLeft, 1) = zwLeft;
- Unsafe.Add(ref dBottomLeft, 2) = xyRight;
- Unsafe.Add(ref dBottomLeft, 3) = zwRight;
- }
-
- [MethodImpl(InliningOptions.ColdPath)]
- private void CopyArbitraryScale(ref float areaOrigin, int areaStride, int horizontalScale, int verticalScale)
- {
- for (int y = 0; y < 8; y++)
- {
- int yy = y * verticalScale;
- int y8 = y * 8;
-
- for (int x = 0; x < 8; x++)
- {
- int xx = x * horizontalScale;
-
- float value = this[y8 + x];
-
- for (int i = 0; i < verticalScale; i++)
- {
- int baseIdx = ((yy + i) * areaStride) + xx;
-
- for (int j = 0; j < horizontalScale; j++)
- {
- // area[xx + j, yy + i] = value;
- Unsafe.Add(ref areaOrigin, baseIdx + j) = value;
- }
- }
- }
- }
- }
- }
-}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 6faaadac7b..d2f3e9664b 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -352,6 +352,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
}
}
+ public void DE_NormalizeColors(float maximum)
+ {
+ if (SimdUtils.HasVector8)
+ {
+ this.NormalizeColorsAndRoundInPlaceVector8(maximum);
+ }
+ else
+ {
+ this.NormalizeColorsInPlace(maximum);
+ this.RoundInPlace();
+ }
+ }
+
///
/// Rounds all values in the block.
///
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs
new file mode 100644
index 0000000000..8edaa2efe3
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykAvx.cs
@@ -0,0 +1,99 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class CmykAvx : JpegColorConverterAvx
+ {
+ public CmykAvx(int precision)
+ : base(JpegColorSpace.Cmyk, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values)
+ {
+ ref Vector256 c0Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+ ref Vector256 c1Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
+ ref Vector256 c2Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
+ ref Vector256 c3Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3));
+
+ // Used for the color conversion
+ var scale = Vector256.Create(1 / (this.MaximumValue * this.MaximumValue));
+
+ nint n = values.Component0.Length / Vector256.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ ref Vector256 c = ref Unsafe.Add(ref c0Base, i);
+ ref Vector256 m = ref Unsafe.Add(ref c1Base, i);
+ ref Vector256 y = ref Unsafe.Add(ref c2Base, i);
+ Vector256 k = Unsafe.Add(ref c3Base, i);
+
+ k = Avx.Multiply(k, scale);
+ c = Avx.Multiply(c, k);
+ m = Avx.Multiply(m, k);
+ y = Avx.Multiply(y, k);
+ }
+ }
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ => ConvertFromRgb(in values, this.MaximumValue, rLane, gLane, bLane);
+
+ public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span rLane, Span gLane, Span bLane)
+ {
+ ref Vector256 destC =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+ ref Vector256 destM =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
+ ref Vector256 destY =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
+ ref Vector256 destK =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3));
+
+ ref Vector256 srcR =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane));
+ ref Vector256 srcG =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane));
+ ref Vector256 srcB =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane));
+
+ var scale = Vector256.Create(maxValue);
+
+ nint n = values.Component0.Length / Vector256.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ Vector256 ctmp = Avx.Subtract(scale, Unsafe.Add(ref srcR, i));
+ Vector256 mtmp = Avx.Subtract(scale, Unsafe.Add(ref srcG, i));
+ Vector256 ytmp = Avx.Subtract(scale, Unsafe.Add(ref srcB, i));
+ Vector256 ktmp = Avx.Min(ctmp, Avx.Min(mtmp, ytmp));
+
+ Vector256 kMask = Avx.CompareNotEqual(ktmp, scale);
+
+ ctmp = Avx.And(Avx.Divide(Avx.Subtract(ctmp, ktmp), Avx.Subtract(scale, ktmp)), kMask);
+ mtmp = Avx.And(Avx.Divide(Avx.Subtract(mtmp, ktmp), Avx.Subtract(scale, ktmp)), kMask);
+ ytmp = Avx.And(Avx.Divide(Avx.Subtract(ytmp, ktmp), Avx.Subtract(scale, ktmp)), kMask);
+
+ Unsafe.Add(ref destC, i) = Avx.Subtract(scale, Avx.Multiply(ctmp, scale));
+ Unsafe.Add(ref destM, i) = Avx.Subtract(scale, Avx.Multiply(mtmp, scale));
+ Unsafe.Add(ref destY, i) = Avx.Subtract(scale, Avx.Multiply(ytmp, scale));
+ Unsafe.Add(ref destK, i) = Avx.Subtract(scale, ktmp);
+ }
+ }
+ }
+ }
+}
+#endif
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs
new file mode 100644
index 0000000000..70d47b9b79
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykScalar.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class CmykScalar : JpegColorConverterScalar
+ {
+ public CmykScalar(int precision)
+ : base(JpegColorSpace.Cmyk, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values) =>
+ ConvertToRgbInplace(values, this.MaximumValue);
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b)
+ => ConvertFromRgb(values, this.MaximumValue, r, g, b);
+
+ public static void ConvertToRgbInplace(in ComponentValues values, float maxValue)
+ {
+ Span c0 = values.Component0;
+ Span c1 = values.Component1;
+ Span c2 = values.Component2;
+ Span c3 = values.Component3;
+
+ float scale = 1 / (maxValue * maxValue);
+ for (int i = 0; i < c0.Length; i++)
+ {
+ float c = c0[i];
+ float m = c1[i];
+ float y = c2[i];
+ float k = c3[i];
+
+ k *= scale;
+ c0[i] = c * k;
+ c1[i] = m * k;
+ c2[i] = y * k;
+ }
+ }
+
+ public static void ConvertFromRgb(in ComponentValues values, float maxValue, Span r, Span g, Span b)
+ {
+ Span c = values.Component0;
+ Span m = values.Component1;
+ Span y = values.Component2;
+ Span k = values.Component3;
+
+ for (int i = 0; i < c.Length; i++)
+ {
+ float ctmp = 255f - r[i];
+ float mtmp = 255f - g[i];
+ float ytmp = 255f - b[i];
+ float ktmp = MathF.Min(MathF.Min(ctmp, mtmp), ytmp);
+
+ if (ktmp >= 255f)
+ {
+ ctmp = 0f;
+ mtmp = 0f;
+ ytmp = 0f;
+ }
+ else
+ {
+ ctmp = (ctmp - ktmp) / (255f - ktmp);
+ mtmp = (mtmp - ktmp) / (255f - ktmp);
+ ytmp = (ytmp - ktmp) / (255f - ktmp);
+ }
+
+ c[i] = maxValue - (ctmp * maxValue);
+ m[i] = maxValue - (mtmp * maxValue);
+ y[i] = maxValue - (ytmp * maxValue);
+ k[i] = maxValue - ktmp;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs
new file mode 100644
index 0000000000..6d7688bcd8
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.CmykVector.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class CmykVector : JpegColorConverterVector
+ {
+ public CmykVector(int precision)
+ : base(JpegColorSpace.Cmyk, precision)
+ {
+ }
+
+ ///
+ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
+ {
+ ref Vector cBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+ ref Vector mBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
+ ref Vector yBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
+ ref Vector kBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3));
+
+ var scale = new Vector(1 / (this.MaximumValue * this.MaximumValue));
+
+ nint n = values.Component0.Length / Vector.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ ref Vector c = ref Unsafe.Add(ref cBase, i);
+ ref Vector m = ref Unsafe.Add(ref mBase, i);
+ ref Vector y = ref Unsafe.Add(ref yBase, i);
+ Vector k = Unsafe.Add(ref kBase, i);
+
+ k *= scale;
+ c *= k;
+ m *= k;
+ y *= k;
+ }
+ }
+
+ ///
+ protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
+ => CmykScalar.ConvertToRgbInplace(values, this.MaximumValue);
+
+ ///
+ protected override void ConvertFromRgbVectorized(in ComponentValues values, Span r, Span g, Span b)
+ => ConvertFromRgbInplaceVectorized(in values, this.MaximumValue, r, g, b);
+
+ ///
+ protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b)
+ => ConvertFromRgbInplaceRemainder(values, this.MaximumValue, r, g, b);
+
+ public static void ConvertFromRgbInplaceVectorized(in ComponentValues values, float maxValue, Span r, Span g, Span b)
+ {
+ ref Vector destC =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+ ref Vector destM =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
+ ref Vector destY =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
+ ref Vector destK =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component3));
+
+ ref Vector srcR =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(r));
+ ref Vector srcG =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(g));
+ ref Vector srcB =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(b));
+
+ // Used for the color conversion
+ var scale = new Vector(maxValue);
+
+ nint n = values.Component0.Length / Vector.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ Vector ctmp = scale - Unsafe.Add(ref srcR, i);
+ Vector mtmp = scale - Unsafe.Add(ref srcG, i);
+ Vector ytmp = scale - Unsafe.Add(ref srcB, i);
+ Vector ktmp = Vector.Min(ctmp, Vector.Min(mtmp, ytmp));
+
+ var kMask = Vector.Equals(ktmp, scale);
+ ctmp = Vector.AndNot((ctmp - ktmp) / (scale - ktmp), kMask.As());
+ mtmp = Vector.AndNot((mtmp - ktmp) / (scale - ktmp), kMask.As());
+ ytmp = Vector.AndNot((ytmp - ktmp) / (scale - ktmp), kMask.As());
+
+ Unsafe.Add(ref destC, i) = scale - (ctmp * scale);
+ Unsafe.Add(ref destM, i) = scale - (mtmp * scale);
+ Unsafe.Add(ref destY, i) = scale - (ytmp * scale);
+ Unsafe.Add(ref destK, i) = scale - ktmp;
+ }
+ }
+
+ public static void ConvertFromRgbInplaceRemainder(in ComponentValues values, float maxValue, Span r, Span g, Span b)
+ => CmykScalar.ConvertFromRgb(values, maxValue, r, g, b);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs
new file mode 100644
index 0000000000..26e8791853
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleAvx.cs
@@ -0,0 +1,72 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using static SixLabors.ImageSharp.SimdUtils;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class GrayscaleAvx : JpegColorConverterAvx
+ {
+ public GrayscaleAvx(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values)
+ {
+ ref Vector256 c0Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+
+ // Used for the color conversion
+ var scale = Vector256.Create(1 / this.MaximumValue);
+
+ nint n = values.Component0.Length / Vector256.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i);
+ c0 = Avx.Multiply(c0, scale);
+ }
+ }
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ {
+ ref Vector256 destLuminance =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+
+ ref Vector256 srcRed =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane));
+ ref Vector256 srcGreen =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane));
+ ref Vector256 srcBlue =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane));
+
+ // Used for the color conversion
+ var f0299 = Vector256.Create(0.299f);
+ var f0587 = Vector256.Create(0.587f);
+ var f0114 = Vector256.Create(0.114f);
+
+ nint n = values.Component0.Length / Vector256.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ ref Vector256 r = ref Unsafe.Add(ref srcRed, i);
+ ref Vector256 g = ref Unsafe.Add(ref srcGreen, i);
+ ref Vector256 b = ref Unsafe.Add(ref srcBlue, i);
+
+ // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
+ Unsafe.Add(ref destLuminance, i) = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r);
+ }
+ }
+ }
+ }
+}
+#endif
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs
new file mode 100644
index 0000000000..2e5129f328
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleScalar.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class GrayscaleScalar : JpegColorConverterScalar
+ {
+ public GrayscaleScalar(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values)
+ => ConvertToRgbInplace(values.Component0, this.MaximumValue);
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b)
+ => ConvertCoreInplaceFromRgb(values, r, g, b);
+
+ internal static void ConvertToRgbInplace(Span values, float maxValue)
+ {
+ ref float valuesRef = ref MemoryMarshal.GetReference(values);
+ float scale = 1 / maxValue;
+
+ for (nint i = 0; i < values.Length; i++)
+ {
+ Unsafe.Add(ref valuesRef, i) *= scale;
+ }
+ }
+
+ internal static void ConvertCoreInplaceFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ {
+ Span c0 = values.Component0;
+
+ for (int i = 0; i < c0.Length; i++)
+ {
+ // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
+ float luma = (0.299f * rLane[i]) + (0.587f * gLane[i]) + (0.114f * bLane[i]);
+ c0[i] = luma;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs
new file mode 100644
index 0000000000..0f903c0519
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.GrayScaleVector.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class GrayScaleVector : JpegColorConverterVector
+ {
+ public GrayScaleVector(int precision)
+ : base(JpegColorSpace.Grayscale, precision)
+ {
+ }
+
+ ///
+ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
+ {
+ ref Vector cBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+
+ var scale = new Vector(1 / this.MaximumValue);
+
+ nint n = values.Component0.Length / Vector.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ ref Vector c0 = ref Unsafe.Add(ref cBase, i);
+ c0 *= scale;
+ }
+ }
+
+ ///
+ protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
+ => GrayscaleScalar.ConvertToRgbInplace(values.Component0, this.MaximumValue);
+
+ ///
+ protected override void ConvertFromRgbVectorized(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ {
+ ref Vector destLuma =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+
+ ref Vector srcR =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(rLane));
+ ref Vector srcG =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(gLane));
+ ref Vector srcB =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(bLane));
+
+ var rMult = new Vector(0.299f);
+ var gMult = new Vector(0.587f);
+ var bMult = new Vector(0.114f);
+
+ nint n = values.Component0.Length / Vector.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ Vector r = Unsafe.Add(ref srcR, i);
+ Vector g = Unsafe.Add(ref srcR, i);
+ Vector b = Unsafe.Add(ref srcR, i);
+
+ // luminocity = (0.299 * r) + (0.587 * g) + (0.114 * b)
+ Unsafe.Add(ref destLuma, i) = (rMult * r) + (gMult * g) + (bMult * b);
+ }
+ }
+
+ ///
+ protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b)
+ => GrayscaleScalar.ConvertCoreInplaceFromRgb(values, r, g, b);
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs
similarity index 75%
rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs
index 93dfe46880..5fd97e3684 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbAvx.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbAvx.cs
@@ -2,22 +2,24 @@
// Licensed under the Six Labors Split License.
#if SUPPORTS_RUNTIME_INTRINSICS
+using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal abstract partial class JpegColorConverterBase
{
- internal sealed class FromRgbAvx : JpegColorConverterAvx
+ internal sealed class RgbAvx : JpegColorConverterAvx
{
- public FromRgbAvx(int precision)
+ public RgbAvx(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
+ ///
public override void ConvertToRgbInplace(in ComponentValues values)
{
ref Vector256 rBase =
@@ -40,6 +42,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
b = Avx.Multiply(b, scale);
}
}
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ {
+ rLane.CopyTo(values.Component0);
+ gLane.CopyTo(values.Component1);
+ bLane.CopyTo(values.Component2);
+ }
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs
new file mode 100644
index 0000000000..a3de4493fd
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbScalar.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class RgbScalar : JpegColorConverterScalar
+ {
+ public RgbScalar(int precision)
+ : base(JpegColorSpace.RGB, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values)
+ => ConvertToRgbInplace(values, this.MaximumValue);
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span r, Span g, Span b)
+ => ConvertFromRgb(values, r, g, b);
+
+ internal static void ConvertToRgbInplace(ComponentValues values, float maxValue)
+ {
+ GrayscaleScalar.ConvertToRgbInplace(values.Component0, maxValue);
+ GrayscaleScalar.ConvertToRgbInplace(values.Component1, maxValue);
+ GrayscaleScalar.ConvertToRgbInplace(values.Component2, maxValue);
+ }
+
+ internal static void ConvertFromRgb(ComponentValues values, Span r, Span g, Span b)
+ {
+ r.CopyTo(values.Component0);
+ g.CopyTo(values.Component1);
+ b.CopyTo(values.Component2);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs
similarity index 56%
rename from src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
rename to src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs
index 24d430f0a6..ba36733804 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/ColorConverters/JpegColorConverter.FromRgbVector.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.RgbVector.cs
@@ -1,22 +1,24 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
internal abstract partial class JpegColorConverterBase
{
- internal sealed class FromRgbVector : JpegColorConverterVector
+ internal sealed class RgbVector : JpegColorConverterVector
{
- public FromRgbVector(int precision)
+ public RgbVector(int precision)
: base(JpegColorSpace.RGB, precision)
{
}
- protected override void ConvertCoreVectorizedInplace(in ComponentValues values)
+ ///
+ protected override void ConvertToRgbInplaceVectorized(in ComponentValues values)
{
ref Vector rBase =
ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
@@ -39,8 +41,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters
}
}
- protected override void ConvertCoreInplace(in ComponentValues values) =>
- FromRgbScalar.ConvertCoreInplace(values, this.MaximumValue);
+ ///
+ protected override void ConvertToRgbInplaceScalarRemainder(in ComponentValues values)
+ => RgbScalar.ConvertToRgbInplace(values, this.MaximumValue);
+
+ ///
+ protected override void ConvertFromRgbVectorized(in ComponentValues values, Span r, Span g, Span b)
+ {
+ r.CopyTo(values.Component0);
+ g.CopyTo(values.Component1);
+ b.CopyTo(values.Component2);
+ }
+
+ ///
+ protected override void ConvertFromRgbScalarRemainder(in ComponentValues values, Span r, Span g, Span b)
+ => RgbScalar.ConvertFromRgb(values, r, g, b);
}
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs
new file mode 100644
index 0000000000..219584b05e
--- /dev/null
+++ b/src/ImageSharp/Formats/Jpeg/Components/ColorConverters/JpegColorConverter.YCbCrAvx.cs
@@ -0,0 +1,125 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using static SixLabors.ImageSharp.SimdUtils;
+
+// ReSharper disable ImpureMethodCallOnReadonlyValueField
+namespace SixLabors.ImageSharp.Formats.Jpeg.Components
+{
+ internal abstract partial class JpegColorConverterBase
+ {
+ internal sealed class YCbCrAvx : JpegColorConverterAvx
+ {
+ public YCbCrAvx(int precision)
+ : base(JpegColorSpace.YCbCr, precision)
+ {
+ }
+
+ ///
+ public override void ConvertToRgbInplace(in ComponentValues values)
+ {
+ ref Vector256 c0Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component0));
+ ref Vector256 c1Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component1));
+ ref Vector256 c2Base =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(values.Component2));
+
+ // Used for the color conversion
+ var chromaOffset = Vector256.Create(-this.HalfValue);
+ var scale = Vector256.Create(1 / this.MaximumValue);
+ var rCrMult = Vector256.Create(YCbCrScalar.RCrMult);
+ var gCbMult = Vector256.Create(-YCbCrScalar.GCbMult);
+ var gCrMult = Vector256.Create(-YCbCrScalar.GCrMult);
+ var bCbMult = Vector256.Create(YCbCrScalar.BCbMult);
+
+ // Walking 8 elements at one step:
+ nint n = values.Component0.Length / Vector256.Count;
+ for (nint i = 0; i < n; i++)
+ {
+ // y = yVals[i];
+ // cb = cbVals[i] - 128F;
+ // cr = crVals[i] - 128F;
+ ref Vector256 c0 = ref Unsafe.Add(ref c0Base, i);
+ ref Vector256 c1 = ref Unsafe.Add(ref c1Base, i);
+ ref Vector256 c2 = ref Unsafe.Add(ref c2Base, i);
+
+ Vector256 y = c0;
+ Vector256 cb = Avx.Add(c1, chromaOffset);
+ Vector256 cr = Avx.Add(c2, chromaOffset);
+
+ // r = y + (1.402F * cr);
+ // g = y - (0.344136F * cb) - (0.714136F * cr);
+ // b = y + (1.772F * cb);
+ Vector256 r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult);
+ Vector256 g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult);
+ Vector256 b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult);
+
+ r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale);
+ g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale);
+ b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale);
+
+ c0 = r;
+ c1 = g;
+ c2 = b;
+ }
+ }
+
+ ///
+ public override void ConvertFromRgb(in ComponentValues values, Span rLane, Span gLane, Span bLane)
+ {
+ ref Vector256