diff --git a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs index 1cd72152b..e22e0516c 100644 --- a/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs +++ b/src/ImageSharp/Diagnostics/MemoryDiagnostics.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Diagnostics /// public static int TotalUndisposedAllocationCount => totalUndisposedAllocationCount; - internal static bool MemoryResourceLeakedSubscribed => undisposedMemoryResourceSubscriptionCounter > 0; + internal static bool MemoryResourceLeakedSubscribed => Volatile.Read(ref undisposedMemoryResourceSubscriptionCounter) > 0; internal static void IncrementTotalUndisposedAllocationCount() => Interlocked.Increment(ref totalUndisposedAllocationCount); diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs index f25286447..d7511fdda 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs @@ -280,7 +280,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } /// - /// Quantize input block, apply zig-zag ordering and store result as 16bit integers. + /// Quantize input block, transpose, apply zig-zag ordering and store as . /// /// Source block. /// Destination block. @@ -291,19 +291,19 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components if (Avx2.IsSupported) { MultiplyIntoInt16_Avx2(ref block, ref qt, ref dest); - ZigZag.ApplyZigZagOrderingAvx2(ref dest); + ZigZag.ApplyTransposingZigZagOrderingAvx2(ref dest); } else if (Ssse3.IsSupported) { MultiplyIntoInt16_Sse2(ref block, ref qt, ref dest); - ZigZag.ApplyZigZagOrderingSsse3(ref dest); + ZigZag.ApplyTransposingZigZagOrderingSsse3(ref dest); } else #endif { for (int i = 0; i < Size; i++) { - int idx = ZigZag.ZigZagOrder[i]; + int idx = ZigZag.TransposingOrder[i]; float quantizedVal = block[idx] * qt[idx]; quantizedVal += quantizedVal < 0 ? -0.5f : 0.5f; dest[i] = (short)quantizedVal; diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs index 94864005e..8acc4b626 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs @@ -29,11 +29,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { DebugGuard.IsTrue(Avx.IsSupported, "Avx support is required to execute this operation."); - // First pass - process rows - block.TransposeInplace(); + // First pass - process columns FDCT8x8_1D_Avx(ref block); - // Second pass - process columns + // Second pass - process rows block.TransposeInplace(); FDCT8x8_1D_Avx(ref block); diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs index c27ad5b82..e1bcff30f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs @@ -92,6 +92,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components tableRef = 0.125f / (tableRef * Unsafe.Add(ref multipliersRef, i)); tableRef = ref Unsafe.Add(ref tableRef, 1); } + + // Spectral macroblocks are not transposed before quantization + // Transpose is done after quantization at zig-zag stage + // so we must transpose quantization table + quantTable.TransposeInplace(); } /// @@ -133,14 +138,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } else #endif - if (Vector.IsHardwareAccelerated) { FDCT_Vector4(ref block); } - else - { - FDCT_Scalar(ref block); - } } /// @@ -217,136 +217,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components } } - /// - /// Apply 2D floating point FDCT inplace using scalar operations. - /// - /// - /// Ported from libjpeg-turbo https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/jfdctflt.c. - /// - /// Input block. - private static void FDCT_Scalar(ref Block8x8F block) - { - const int dctSize = 8; - - float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - float tmp10, tmp11, tmp12, tmp13; - float z1, z2, z3, z4, z5, z11, z13; - - // First pass - process rows - ref float blockRef = ref Unsafe.As(ref block); - for (int ctr = 7; ctr >= 0; ctr--) - { - tmp0 = Unsafe.Add(ref blockRef, 0) + Unsafe.Add(ref blockRef, 7); - tmp7 = Unsafe.Add(ref blockRef, 0) - Unsafe.Add(ref blockRef, 7); - tmp1 = Unsafe.Add(ref blockRef, 1) + Unsafe.Add(ref blockRef, 6); - tmp6 = Unsafe.Add(ref blockRef, 1) - Unsafe.Add(ref blockRef, 6); - tmp2 = Unsafe.Add(ref blockRef, 2) + Unsafe.Add(ref blockRef, 5); - tmp5 = Unsafe.Add(ref blockRef, 2) - Unsafe.Add(ref blockRef, 5); - tmp3 = Unsafe.Add(ref blockRef, 3) + Unsafe.Add(ref blockRef, 4); - tmp4 = Unsafe.Add(ref blockRef, 3) - Unsafe.Add(ref blockRef, 4); - - // Even part - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - Unsafe.Add(ref blockRef, 0) = tmp10 + tmp11; - Unsafe.Add(ref blockRef, 4) = tmp10 - tmp11; - - z1 = (tmp12 + tmp13) * 0.707106781f; - Unsafe.Add(ref blockRef, 2) = tmp13 + z1; - Unsafe.Add(ref blockRef, 6) = tmp13 - z1; - - // Odd part - tmp10 = tmp4 + tmp5; - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - z5 = (tmp10 - tmp12) * 0.382683433f; - z2 = (0.541196100f * tmp10) + z5; - z4 = (1.306562965f * tmp12) + z5; - z3 = tmp11 * 0.707106781f; - - z11 = tmp7 + z3; - z13 = tmp7 - z3; - - Unsafe.Add(ref blockRef, 5) = z13 + z2; - Unsafe.Add(ref blockRef, 3) = z13 - z2; - Unsafe.Add(ref blockRef, 1) = z11 + z4; - Unsafe.Add(ref blockRef, 7) = z11 - z4; - - blockRef = ref Unsafe.Add(ref blockRef, dctSize); - } - - // Second pass - process columns - blockRef = ref Unsafe.As(ref block); - for (int ctr = 7; ctr >= 0; ctr--) - { - tmp0 = Unsafe.Add(ref blockRef, dctSize * 0) + Unsafe.Add(ref blockRef, dctSize * 7); - tmp7 = Unsafe.Add(ref blockRef, dctSize * 0) - Unsafe.Add(ref blockRef, dctSize * 7); - tmp1 = Unsafe.Add(ref blockRef, dctSize * 1) + Unsafe.Add(ref blockRef, dctSize * 6); - tmp6 = Unsafe.Add(ref blockRef, dctSize * 1) - Unsafe.Add(ref blockRef, dctSize * 6); - tmp2 = Unsafe.Add(ref blockRef, dctSize * 2) + Unsafe.Add(ref blockRef, dctSize * 5); - tmp5 = Unsafe.Add(ref blockRef, dctSize * 2) - Unsafe.Add(ref blockRef, dctSize * 5); - tmp3 = Unsafe.Add(ref blockRef, dctSize * 3) + Unsafe.Add(ref blockRef, dctSize * 4); - tmp4 = Unsafe.Add(ref blockRef, dctSize * 3) - Unsafe.Add(ref blockRef, dctSize * 4); - - // Even part - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - Unsafe.Add(ref blockRef, dctSize * 0) = tmp10 + tmp11; - Unsafe.Add(ref blockRef, dctSize * 4) = tmp10 - tmp11; - - z1 = (tmp12 + tmp13) * 0.707106781f; - Unsafe.Add(ref blockRef, dctSize * 2) = tmp13 + z1; - Unsafe.Add(ref blockRef, dctSize * 6) = tmp13 - z1; - - // Odd part - tmp10 = tmp4 + tmp5; - tmp11 = tmp5 + tmp6; - tmp12 = tmp6 + tmp7; - - z5 = (tmp10 - tmp12) * 0.382683433f; - z2 = (0.541196100f * tmp10) + z5; - z4 = (1.306562965f * tmp12) + z5; - z3 = tmp11 * 0.707106781f; - - z11 = tmp7 + z3; - z13 = tmp7 - z3; - - Unsafe.Add(ref blockRef, dctSize * 5) = z13 + z2; - Unsafe.Add(ref blockRef, dctSize * 3) = z13 - z2; - Unsafe.Add(ref blockRef, dctSize * 1) = z11 + z4; - Unsafe.Add(ref blockRef, dctSize * 7) = z11 - z4; - - blockRef = ref Unsafe.Add(ref blockRef, 1); - } - } - /// /// Apply floating point FDCT inplace using API. /// - /// - /// This implementation must be called only if hardware supports 4 - /// floating point numbers vector. Otherwise explicit scalar - /// implementation is faster - /// because it does not rely on block transposition. - /// /// Input block. public static void FDCT_Vector4(ref Block8x8F block) { - DebugGuard.IsTrue(Vector.IsHardwareAccelerated, "Scalar implementation should be called for non-accelerated hardware."); - - // First pass - process rows - block.TransposeInplace(); + // First pass - process columns FDCT8x4_Vector4(ref block.V0L); FDCT8x4_Vector4(ref block.V0R); - // Second pass - process columns + // Second pass - process rows block.TransposeInplace(); FDCT8x4_Vector4(ref block.V0L); FDCT8x4_Vector4(ref block.V0R); diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs index 6577739c1..850de26c3 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.Intrinsic.cs @@ -3,6 +3,7 @@ #if SUPPORTS_RUNTIME_INTRINSICS using System; +using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -18,120 +19,138 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components #pragma warning restore SA1309 /// - /// Gets shuffle vectors for + /// Gets shuffle vectors for /// zig zag implementation. /// private static ReadOnlySpan SseShuffleMasks => new byte[] { - // row0 - 0, 1, 2, 3, _, _, _, _, _, _, 4, 5, 6, 7, _, _, - _, _, _, _, 0, 1, _, _, 2, 3, _, _, _, _, 4, 5, - _, _, _, _, _, _, 0, 1, _, _, _, _, _, _, _, _, - - // row1 - _, _, _, _, _, _, _, _, _, _, _, _, 8, 9, 10, 11, - 2, 3, _, _, _, _, _, _, 4, 5, _, _, _, _, _, _, - _, _, 0, 1, _, _, 2, 3, _, _, _, _, _, _, _, _, - - // row2 - _, _, _, _, _, _, 2, 3, _, _, _, _, _, _, 4, 5, - _, _, _, _, _, _, _, _, 0, 1, _, _, 2, 3, _, _, - - // row3 - _, _, _, _, _, _, 12, 13, 14, 15, _, _, _, _, _, _, - _, _, _, _, 10, 11, _, _, _, _, 12, 13, _, _, _, _, - _, _, 8, 9, _, _, _, _, _, _, _, _, 10, 11, _, _, - 6, 7, _, _, _, _, _, _, _, _, _, _, _, _, 8, 9, - - // row4 - _, _, 4, 5, _, _, _, _, _, _, _, _, 6, 7, _, _, - _, _, _, _, 2, 3, _, _, _, _, 4, 5, _, _, _, _, - _, _, _, _, _, _, 0, 1, 2, 3, _, _, _, _, _, _, - - // row5 - _, _, 12, 13, _, _, 14, 15, _, _, _, _, _, _, _, _, - 10, 11, _, _, _, _, _, _, 12, 13, _, _, _, _, _, _, - - // row6 - _, _, _, _, _, _, _, _, 12, 13, _, _, 14, 15, _, _, - _, _, _, _, _, _, 10, 11, _, _, _, _, _, _, 12, 13, - 4, 5, 6, 7, _, _, _, _, _, _, _, _, _, _, _, _, - - // row7 - 10, 11, _, _, _, _, 12, 13, _, _, 14, 15, _, _, _, _, - _, _, 8, 9, 10, 11, _, _, _, _, _, _, 12, 13, 14, 15 +#pragma warning disable SA1515 + /* row0 - A0 B0 A1 A2 B1 C0 D0 C1 */ + // A + 0, 1, _, _, 2, 3, 4, 5, _, _, _, _, _, _, _, _, + // B + _, _, 0, 1, _, _, _, _, 2, 3, _, _, _, _, _, _, + // C + _, _, _, _, _, _, _, _, _, _, 0, 1, _, _, 2, 3, + + /* row1 - B2 A3 A4 B3 C2 D1 E0 F0 */ + // A + _, _, 6, 7, 8, 9, _, _, _, _, _, _, _, _, _, _, + // B + 4, 5, _, _, _, _, 6, 7, _, _, _, _, _, _, _, _, + + /* row2 - E1 D2 C3 B4 A5 A6 B5 C4 */ + // A + _, _, _, _, _, _, _, _, 10, 11, 12, 13, _, _, _, _, + // B + _, _, _, _, _, _, 8, 9, _, _, _, _, 10, 11, _, _, + // C + _, _, _, _, 6, 7, _, _, _, _, _, _, _, _, 8, 9, + + /* row3 - D3 E2 F1 G0 H0 G1 F2 E3 */ + // E + _, _, 4, 5, _, _, _, _, _, _, _, _, _, _, 6, 7, + // F + _, _, _, _, 2, 3, _, _, _, _, _, _, 4, 5, _, _, + // G + _, _, _, _, _, _, 0, 1, _, _, 2, 3, _, _, _, _, + + /* row4 - D4 C5 B6 A7 B7 C6 D5 E4 */ + // B + _, _, _, _, 12, 13, _, _, 14, 15, _, _, _, _, _, _, + // C + _, _, 10, 11, _, _, _, _, _, _, 12, 13, _, _, _, _, + // D + 8, 9, _, _, _, _, _, _, _, _, _, _, 10, 11, _, _, + + /* row5 - F3 G2 H1 H2 G3 F4 E5 D6 */ + // F + 6, 7, _, _, _, _, _, _, _, _, 8, 9, _, _, _, _, + // G + _, _, 4, 5, _, _, _, _, 6, 7, _, _, _, _, _, _, + // H + _, _, _, _, 2, 3, 4, 5, _, _, _, _, _, _, _, _, + + /* row6 - C7 D7 E6 F5 G4 H3 H4 G5 */ + // G + _, _, _, _, _, _, _, _, 8, 9, _, _, _, _, 10, 11, + // H + _, _, _, _, _, _, _, _, _, _, 6, 7, 8, 9, _, _, + + /* row7 - F6 E7 F7 G6 H5 H6 G7 H7 */ + // F + 12, 13, _, _, 14, 15, _, _, _, _, _, _, _, _, _, _, + // G + _, _, _, _, _, _, 12, 13, _, _, _, _, 14, 15, _, _, + // H + _, _, _, _, _, _, _, _, 10, 11, 12, 13, _, _, 14, 15, +#pragma warning restore SA1515 }; /// - /// Gets shuffle vectors for + /// Gets shuffle vectors for /// zig zag implementation. /// private static ReadOnlySpan AvxShuffleMasks => new byte[] { - // 01_AB/01_EF/23_CD - cross-lane - 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, - - // 01_AB - inner-lane - 0, 1, 2, 3, 8, 9, _, _, 10, 11, 4, 5, 6, 7, 12, 13, _, _, _, _, _, _, _, _, _, _, 10, 11, 4, 5, 6, 7, - - // 01_CD/23_GH - cross-lane - 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, _, _, _, _, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, _, _, _, _, - - // 01_CD - inner-lane - _, _, _, _, _, _, 0, 1, _, _, _, _, _, _, _, _, 2, 3, 8, 9, _, _, 10, 11, 4, 5, _, _, _, _, _, _, - - // 01_EF - inner-lane - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 0, 1, _, _, _, _, _, _, _, _, _, _, - - // 23_AB/45_CD/67_EF - cross-lane - 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, _, _, _, _, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, _, _, _, _, - - // 23_AB - inner-lane - 4, 5, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 6, 7, 0, 1, 2, 3, 8, 9, _, _, _, _, - - // 23_CD - inner-lane - _, _, 6, 7, 12, 13, _, _, _, _, _, _, _, _, _, _, 10, 11, 4, 5, _, _, _, _, _, _, _, _, 6, 7, 12, 13, - - // 23_EF - inner-lane - _, _, _, _, _, _, 2, 3, 8, 9, _, _, 10, 11, 4, 5, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - - // 23_GH - inner-lane - _, _, _, _, _, _, _, _, _, _, 0, 1, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - - // 45_AB - inner-lane - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 10, 11, _, _, _, _, _, _, _, _, _, _, - - // 45_CD - inner-lane - _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 6, 7, 0, 1, _, _, 2, 3, 8, 9, _, _, _, _, _, _, - - // 45_EF - cross-lane - 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, _, _, _, _, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, - - // 45_EF - inner-lane - 2, 3, 8, 9, _, _, _, _, _, _, _, _, 10, 11, 4, 5, _, _, _, _, _, _, _, _, _, _, 2, 3, 8, 9, _, _, - - // 45_GH - inner-lane - _, _, _, _, 2, 3, 8, 9, 10, 11, 4, 5, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 6, 7, - - // 67_CD - inner-lane - _, _, _, _, _, _, _, _, _, _, 10, 11, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, - - // 67_EF - inner-lane - _, _, _, _, _, _, 6, 7, 0, 1, _, _, 2, 3, 8, 9, _, _, _, _, _, _, _, _, 10, 11, _, _, _, _, _, _, - - // 67_GH - inner-lane - 8, 9, 10, 11, 4, 5, _, _, _, _, _, _, _, _, _, _, 2, 3, 8, 9, 10, 11, 4, 5, _, _, 6, 7, 12, 13, 14, 15 +#pragma warning disable SA1515 + /* 01 */ + // [cr] crln_01_AB_CD + 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, _, _, _, _, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, + // (in) AB + 0, 1, 8, 9, 2, 3, 4, 5, 10, 11, _, _, _, _, _, _, 12, 13, 2, 3, 4, 5, 14, 15, _, _, _, _, _, _, _, _, + // (in) CD + _, _, _, _, _, _, _, _, _, _, 0, 1, 8, 9, 2, 3, _, _, _, _, _, _, _, _, 0, 1, 10, 11, _, _, _, _, + // [cr] crln_01_23_EF_23_CD + 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, + // (in) EF + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 0, 1, 8, 9, + + /* 23 */ + // [cr] crln_23_AB_23_45_GH + 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, + // (in) AB + _, _, _, _, _, _, 8, 9, 2, 3, 4, 5, 10, 11, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + // (in) CDe + _, _, 12, 13, 6, 7, _, _, _, _, _, _, _, _, 8, 9, 14, 15, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + // (in) EF + 2, 3, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 4, 5, 10, 11, _, _, _, _, _, _, 12, 13, 6, 7, + // (in) GH + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 0, 1, 8, 9, 2, 3, _, _, _, _, + + /* 45 */ + // (in) AB + _, _, _, _, 12, 13, 6, 7, 14, 15, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + // [cr] crln_45_67_CD_45_EF + 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, + // (in) CD + 8, 9, 2, 3, _, _, _, _, _, _, 4, 5, 10, 11, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 12, 13, + // (in) EF + _, _, _, _, _, _, _, _, _, _, _, _, _, _, 0, 1, 6, 7, _, _, _, _, _, _, _, _, 8, 9, 2, 3, _, _, + // (in) GH + _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, 4, 5, 10, 11, 12, 13, 6, 7, _, _, _, _, _, _, + + /* 67 */ + // (in) CD + 6, 7, 14, 15, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, + // [cr] crln_67_EF_67_GH + 2, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, _, _, _, _, + // (in) EF + _, _, _, _, 4, 5, 14, 15, _, _, _, _, _, _, _, _, 8, 9, 2, 3, 10, 11, _, _, _, _, _, _, _, _, _, _, + // (in) GH + _, _, _, _, _, _, _, _, 0, 1, 10, 11, 12, 13, 2, 3, _, _, _, _, _, _, 0, 1, 6, 7, 8, 9, 2, 3, 10, 11, +#pragma warning restore SA1515 }; /// /// Applies zig zag ordering for given 8x8 matrix using SSE cpu intrinsics. /// /// Input matrix. - public static unsafe void ApplyZigZagOrderingSsse3(ref Block8x8 block) + public static unsafe void ApplyTransposingZigZagOrderingSsse3(ref Block8x8 block) { DebugGuard.IsTrue(Ssse3.IsSupported, "Ssse3 support is required to run this operation!"); - fixed (byte* maskPtr = SseShuffleMasks) + fixed (byte* shuffleVectorsPtr = &MemoryMarshal.GetReference(SseShuffleMasks)) { Vector128 rowA = block.V0.AsByte(); Vector128 rowB = block.V1.AsByte(); @@ -142,73 +161,69 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Vector128 rowG = block.V6.AsByte(); Vector128 rowH = block.V7.AsByte(); - // row0 - A0 A1 B0 C0 B1 A2 A3 B2 - Vector128 rowA0 = Ssse3.Shuffle(rowA, Sse2.LoadVector128(maskPtr + (16 * 0))).AsInt16(); - Vector128 rowB0 = Ssse3.Shuffle(rowB, Sse2.LoadVector128(maskPtr + (16 * 1))).AsInt16(); - Vector128 row0 = Sse2.Or(rowA0, rowB0); - Vector128 rowC0 = Ssse3.Shuffle(rowC, Sse2.LoadVector128(maskPtr + (16 * 2))).AsInt16(); - row0 = Sse2.Or(row0, rowC0); - - // row1 - C1 D0 E0 D1 C2 B3 A4 A5 - Vector128 rowA1 = Ssse3.Shuffle(rowA, Sse2.LoadVector128(maskPtr + (16 * 3))).AsInt16(); - Vector128 rowC1 = Ssse3.Shuffle(rowC, Sse2.LoadVector128(maskPtr + (16 * 4))).AsInt16(); - Vector128 row1 = Sse2.Or(rowA1, rowC1); - Vector128 rowD1 = Ssse3.Shuffle(rowD, Sse2.LoadVector128(maskPtr + (16 * 5))).AsInt16(); - row1 = Sse2.Or(row1, rowD1); - row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowB.AsUInt16(), 3), 5).AsInt16(); - row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 0), 2).AsInt16(); - - // row2 - Vector128 rowE2 = Ssse3.Shuffle(rowE, Sse2.LoadVector128(maskPtr + (16 * 6))).AsInt16(); - Vector128 rowF2 = Ssse3.Shuffle(rowF, Sse2.LoadVector128(maskPtr + (16 * 7))).AsInt16(); - Vector128 row2 = Sse2.Or(rowE2, rowF2); - row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowB.AsUInt16(), 4), 0).AsInt16(); - row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowC.AsUInt16(), 3), 1).AsInt16(); - row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 2), 2).AsInt16(); - row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowG.AsUInt16(), 0), 5).AsInt16(); - - // row3 - Vector128 rowA3 = Ssse3.Shuffle(rowA, Sse2.LoadVector128(maskPtr + (16 * 8))).AsInt16().AsInt16(); - Vector128 rowB3 = Ssse3.Shuffle(rowB, Sse2.LoadVector128(maskPtr + (16 * 9))).AsInt16().AsInt16(); - Vector128 row3 = Sse2.Or(rowA3, rowB3); - Vector128 rowC3 = Ssse3.Shuffle(rowC, Sse2.LoadVector128(maskPtr + (16 * 10))).AsInt16(); - row3 = Sse2.Or(row3, rowC3); - Vector128 shuffleRowD3EF = Sse2.LoadVector128(maskPtr + (16 * 11)); - Vector128 rowD3 = Ssse3.Shuffle(rowD, shuffleRowD3EF).AsInt16(); - row3 = Sse2.Or(row3, rowD3); - - // row4 - Vector128 rowE4 = Ssse3.Shuffle(rowE, shuffleRowD3EF).AsInt16(); - Vector128 rowF4 = Ssse3.Shuffle(rowF, Sse2.LoadVector128(maskPtr + (16 * 12))).AsInt16(); - Vector128 row4 = Sse2.Or(rowE4, rowF4); - Vector128 rowG4 = Ssse3.Shuffle(rowG, Sse2.LoadVector128(maskPtr + (16 * 13))).AsInt16(); - row4 = Sse2.Or(row4, rowG4); - Vector128 rowH4 = Ssse3.Shuffle(rowH, Sse2.LoadVector128(maskPtr + (16 * 14))).AsInt16(); - row4 = Sse2.Or(row4, rowH4); - - // row5 - Vector128 rowC5 = Ssse3.Shuffle(rowC, Sse2.LoadVector128(maskPtr + (16 * 15))).AsInt16(); - Vector128 rowD5 = Ssse3.Shuffle(rowD, Sse2.LoadVector128(maskPtr + (16 * 16))).AsInt16(); - Vector128 row5 = Sse2.Or(rowC5, rowD5); - row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowB.AsUInt16(), 7), 2).AsInt16(); - row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 5), 5).AsInt16(); - row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowF.AsUInt16(), 4), 6).AsInt16(); - row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowG.AsUInt16(), 3), 7).AsInt16(); - - // row6 - Vector128 rowE6 = Ssse3.Shuffle(rowE, Sse2.LoadVector128(maskPtr + (16 * 17))).AsInt16(); - Vector128 rowF6 = Ssse3.Shuffle(rowF, Sse2.LoadVector128(maskPtr + (16 * 18))).AsInt16(); - Vector128 row6 = Sse2.Or(rowE6, rowF6); - Vector128 rowH6 = Ssse3.Shuffle(rowH, Sse2.LoadVector128(maskPtr + (16 * 19))).AsInt16(); - row6 = Sse2.Or(row6, rowH6); - row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 7), 5).AsInt16(); - row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowG.AsUInt16(), 4), 2).AsInt16(); - - // row7 - Vector128 rowG7 = Ssse3.Shuffle(rowG, Sse2.LoadVector128(maskPtr + (16 * 20))).AsInt16(); - Vector128 rowH7 = Ssse3.Shuffle(rowH, Sse2.LoadVector128(maskPtr + (16 * 21))).AsInt16(); - Vector128 row7 = Sse2.Or(rowG7, rowH7); - row7 = Sse2.Insert(row7.AsUInt16(), Sse2.Extract(rowF.AsUInt16(), 7), 4).AsInt16(); + // row0 - A0 B0 A1 A2 B1 C0 D0 C1 + Vector128 row0_A = Ssse3.Shuffle(rowA, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 0))).AsInt16(); + Vector128 row0_B = Ssse3.Shuffle(rowB, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 1))).AsInt16(); + Vector128 row0_C = Ssse3.Shuffle(rowC, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 2))).AsInt16(); + Vector128 row0 = Sse2.Or(Sse2.Or(row0_A, row0_B), row0_C); + row0 = Sse2.Insert(row0.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 0), 6).AsInt16(); + + // row1 - B2 A3 A4 B3 C2 D1 E0 F0 + Vector128 row1_A = Ssse3.Shuffle(rowA, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 3))).AsInt16(); + Vector128 row1_B = Ssse3.Shuffle(rowB, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 4))).AsInt16(); + Vector128 row1 = Sse2.Or(row1_A, row1_B); + row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowC.AsUInt16(), 2), 4).AsInt16(); + row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 1), 5).AsInt16(); + row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 0), 6).AsInt16(); + row1 = Sse2.Insert(row1.AsUInt16(), Sse2.Extract(rowF.AsUInt16(), 0), 7).AsInt16(); + + // row2 - E1 D2 C3 B4 A5 A6 B5 C4 + Vector128 row2_A = Ssse3.Shuffle(rowA, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 5))).AsInt16(); + Vector128 row2_B = Ssse3.Shuffle(rowB, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 6))).AsInt16(); + Vector128 row2_C = Ssse3.Shuffle(rowC, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 7))).AsInt16(); + Vector128 row2 = Sse2.Or(Sse2.Or(row2_A, row2_B), row2_C); + row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 2), 1).AsInt16(); + row2 = Sse2.Insert(row2.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 1), 0).AsInt16(); + + // row3 - D3 E2 F1 G0 H0 G1 F2 E3 + Vector128 row3_E = Ssse3.Shuffle(rowE, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 8))).AsInt16(); + Vector128 row3_F = Ssse3.Shuffle(rowF, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 9))).AsInt16(); + Vector128 row3_G = Ssse3.Shuffle(rowG, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 10))).AsInt16(); + Vector128 row3 = Sse2.Or(Sse2.Or(row3_E, row3_F), row3_G); + row3 = Sse2.Insert(row3.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 3), 0).AsInt16(); + row3 = Sse2.Insert(row3.AsUInt16(), Sse2.Extract(rowH.AsUInt16(), 0), 4).AsInt16(); + + // row4 - D4 C5 B6 A7 B7 C6 D5 E4 + Vector128 row4_B = Ssse3.Shuffle(rowB, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 11))).AsInt16(); + Vector128 row4_C = Ssse3.Shuffle(rowC, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 12))).AsInt16(); + Vector128 row4_D = Ssse3.Shuffle(rowD, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 13))).AsInt16(); + Vector128 row4 = Sse2.Or(Sse2.Or(row4_B, row4_C), row4_D); + row4 = Sse2.Insert(row4.AsUInt16(), Sse2.Extract(rowA.AsUInt16(), 7), 3).AsInt16(); + row4 = Sse2.Insert(row4.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 4), 7).AsInt16(); + + // row5 - F3 G2 H1 H2 G3 F4 E5 D6 + Vector128 row5_F = Ssse3.Shuffle(rowF, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 14))).AsInt16(); + Vector128 row5_G = Ssse3.Shuffle(rowG, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 15))).AsInt16(); + Vector128 row5_H = Ssse3.Shuffle(rowH, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 16))).AsInt16(); + Vector128 row5 = Sse2.Or(Sse2.Or(row5_F, row5_G), row5_H); + row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 6), 7).AsInt16(); + row5 = Sse2.Insert(row5.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 5), 6).AsInt16(); + + // row6 - C7 D7 E6 F5 G4 H3 H4 G5 + Vector128 row6_G = Ssse3.Shuffle(rowG, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 17))).AsInt16(); + Vector128 row6_H = Ssse3.Shuffle(rowH, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 18))).AsInt16(); + Vector128 row6 = Sse2.Or(row6_G, row6_H); + row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowC.AsUInt16(), 7), 0).AsInt16(); + row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowD.AsUInt16(), 7), 1).AsInt16(); + row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 6), 2).AsInt16(); + row6 = Sse2.Insert(row6.AsUInt16(), Sse2.Extract(rowF.AsUInt16(), 5), 3).AsInt16(); + + // row7 - F6 E7 F7 G6 H5 H6 G7 H7 + Vector128 row7_F = Ssse3.Shuffle(rowF, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 19))).AsInt16(); + Vector128 row7_G = Ssse3.Shuffle(rowG, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 20))).AsInt16(); + Vector128 row7_H = Ssse3.Shuffle(rowH, Sse2.LoadVector128(shuffleVectorsPtr + (16 * 21))).AsInt16(); + Vector128 row7 = Sse2.Or(Sse2.Or(row7_F, row7_G), row7_H); + row7 = Sse2.Insert(row7.AsUInt16(), Sse2.Extract(rowE.AsUInt16(), 7), 1).AsInt16(); block.V0 = row0; block.V1 = row1; @@ -225,69 +240,61 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// Applies zig zag ordering for given 8x8 matrix using AVX cpu intrinsics. /// /// Input matrix. - public static unsafe void ApplyZigZagOrderingAvx2(ref Block8x8 block) + public static unsafe void ApplyTransposingZigZagOrderingAvx2(ref Block8x8 block) { DebugGuard.IsTrue(Avx2.IsSupported, "Avx2 support is required to run this operation!"); - fixed (byte* shuffleVectorsPtr = AvxShuffleMasks) + fixed (byte* shuffleVectorsPtr = &MemoryMarshal.GetReference(AvxShuffleMasks)) { - Vector256 rowsAB = block.V01.AsByte(); - Vector256 rowsCD = block.V23.AsByte(); - Vector256 rowsEF = block.V45.AsByte(); - Vector256 rowsGH = block.V67.AsByte(); - - // rows 0 1 - Vector256 rows_AB01_EF01_CD23_shuffleMask = Avx.LoadVector256(shuffleVectorsPtr + (0 * 32)).AsInt32(); - Vector256 row01_AB = Avx2.PermuteVar8x32(rowsAB.AsInt32(), rows_AB01_EF01_CD23_shuffleMask).AsByte(); + Vector256 rowAB = block.V01.AsByte(); + Vector256 rowCD = block.V23.AsByte(); + Vector256 rowEF = block.V45.AsByte(); + Vector256 rowGH = block.V67.AsByte(); + + /* row01 - A0 B0 A1 A2 B1 C0 D0 C1 | B2 A3 A4 B3 C2 D1 E0 F0 */ + Vector256 crln_01_AB_CD = Avx.LoadVector256(shuffleVectorsPtr + (0 * 32)).AsInt32(); + Vector256 row01_AB = Avx2.PermuteVar8x32(rowAB.AsInt32(), crln_01_AB_CD).AsByte(); row01_AB = Avx2.Shuffle(row01_AB, Avx.LoadVector256(shuffleVectorsPtr + (1 * 32))).AsByte(); - - Vector256 rows_CD01_GH23_shuffleMask = Avx.LoadVector256(shuffleVectorsPtr + (2 * 32)).AsInt32(); - Vector256 row01_CD = Avx2.PermuteVar8x32(rowsCD.AsInt32(), rows_CD01_GH23_shuffleMask).AsByte(); - row01_CD = Avx2.Shuffle(row01_CD, Avx.LoadVector256(shuffleVectorsPtr + (3 * 32))).AsByte(); - - Vector256 row0123_EF = Avx2.PermuteVar8x32(rowsEF.AsInt32(), rows_AB01_EF01_CD23_shuffleMask).AsByte(); - Vector256 row01_EF = Avx2.Shuffle(row0123_EF, Avx.LoadVector256(shuffleVectorsPtr + (4 * 32))).AsByte(); - - Vector256 row01 = Avx2.Or(Avx2.Or(row01_AB, row01_CD), row01_EF); - - // rows 2 3 - Vector256 rows_AB23_CD45_EF67_shuffleMask = Avx.LoadVector256(shuffleVectorsPtr + (5 * 32)).AsInt32(); - Vector256 row2345_AB = Avx2.PermuteVar8x32(rowsAB.AsInt32(), rows_AB23_CD45_EF67_shuffleMask).AsByte(); - Vector256 row23_AB = Avx2.Shuffle(row2345_AB, Avx.LoadVector256(shuffleVectorsPtr + (6 * 32))).AsByte(); - - Vector256 row23_CD = Avx2.PermuteVar8x32(rowsCD.AsInt32(), rows_AB01_EF01_CD23_shuffleMask).AsByte(); + Vector256 row01_CD = Avx2.PermuteVar8x32(rowCD.AsInt32(), crln_01_AB_CD).AsByte(); + row01_CD = Avx2.Shuffle(row01_CD, Avx.LoadVector256(shuffleVectorsPtr + (2 * 32))).AsByte(); + Vector256 crln_01_23_EF_23_CD = Avx.LoadVector256(shuffleVectorsPtr + (3 * 32)).AsInt32(); + Vector256 row01_23_EF = Avx2.PermuteVar8x32(rowEF.AsInt32(), crln_01_23_EF_23_CD).AsByte(); + Vector256 row01_EF = Avx2.Shuffle(row01_23_EF, Avx.LoadVector256(shuffleVectorsPtr + (4 * 32))).AsByte(); + + Vector256 row01 = Avx2.Or(row01_AB, Avx2.Or(row01_CD, row01_EF)); + + /* row23 - E1 D2 C3 B4 A5 A6 B5 C4 | D3 E2 F1 G0 H0 G1 F2 E3 */ + Vector256 crln_23_AB_23_45_GH = Avx.LoadVector256(shuffleVectorsPtr + (5 * 32)).AsInt32(); + Vector256 row23_45_AB = Avx2.PermuteVar8x32(rowAB.AsInt32(), crln_23_AB_23_45_GH).AsByte(); + Vector256 row23_AB = Avx2.Shuffle(row23_45_AB, Avx.LoadVector256(shuffleVectorsPtr + (6 * 32))).AsByte(); + Vector256 row23_CD = Avx2.PermuteVar8x32(rowCD.AsInt32(), crln_01_23_EF_23_CD).AsByte(); row23_CD = Avx2.Shuffle(row23_CD, Avx.LoadVector256(shuffleVectorsPtr + (7 * 32))).AsByte(); - - Vector256 row23_EF = Avx2.Shuffle(row0123_EF, Avx.LoadVector256(shuffleVectorsPtr + (8 * 32))).AsByte(); - - Vector256 row2345_GH = Avx2.PermuteVar8x32(rowsGH.AsInt32(), rows_CD01_GH23_shuffleMask).AsByte(); - Vector256 row23_GH = Avx2.Shuffle(row2345_GH, Avx.LoadVector256(shuffleVectorsPtr + (9 * 32)).AsByte()); + Vector256 row23_EF = Avx2.Shuffle(row01_23_EF, Avx.LoadVector256(shuffleVectorsPtr + (8 * 32))).AsByte(); + Vector256 row23_45_GH = Avx2.PermuteVar8x32(rowGH.AsInt32(), crln_23_AB_23_45_GH).AsByte(); + Vector256 row23_GH = Avx2.Shuffle(row23_45_GH, Avx.LoadVector256(shuffleVectorsPtr + (9 * 32))).AsByte(); Vector256 row23 = Avx2.Or(Avx2.Or(row23_AB, row23_CD), Avx2.Or(row23_EF, row23_GH)); - // rows 4 5 - Vector256 row45_AB = Avx2.Shuffle(row2345_AB, Avx.LoadVector256(shuffleVectorsPtr + (10 * 32)).AsByte()); - Vector256 row4567_CD = Avx2.PermuteVar8x32(rowsCD.AsInt32(), rows_AB23_CD45_EF67_shuffleMask).AsByte(); - Vector256 row45_CD = Avx2.Shuffle(row4567_CD, Avx.LoadVector256(shuffleVectorsPtr + (11 * 32)).AsByte()); - - Vector256 rows_EF45_GH67_shuffleMask = Avx.LoadVector256(shuffleVectorsPtr + (12 * 32)).AsInt32(); - Vector256 row45_EF = Avx2.PermuteVar8x32(rowsEF.AsInt32(), rows_EF45_GH67_shuffleMask).AsByte(); - row45_EF = Avx2.Shuffle(row45_EF, Avx.LoadVector256(shuffleVectorsPtr + (13 * 32)).AsByte()); - - Vector256 row45_GH = Avx2.Shuffle(row2345_GH, Avx.LoadVector256(shuffleVectorsPtr + (14 * 32)).AsByte()); + /* row45 - D4 C5 B6 A7 B7 C6 D5 E4 | F3 G2 H1 H2 G3 F4 E5 D6 */ + Vector256 row45_AB = Avx2.Shuffle(row23_45_AB, Avx.LoadVector256(shuffleVectorsPtr + (10 * 32))).AsByte(); + Vector256 crln_45_67_CD_45_EF = Avx.LoadVector256(shuffleVectorsPtr + (11 * 32)).AsInt32(); + Vector256 row45_67_CD = Avx2.PermuteVar8x32(rowCD.AsInt32(), crln_45_67_CD_45_EF).AsByte(); + Vector256 row45_CD = Avx2.Shuffle(row45_67_CD, Avx.LoadVector256(shuffleVectorsPtr + (12 * 32))).AsByte(); + Vector256 row45_EF = Avx2.PermuteVar8x32(rowEF.AsInt32(), crln_45_67_CD_45_EF).AsByte(); + row45_EF = Avx2.Shuffle(row45_EF, Avx.LoadVector256(shuffleVectorsPtr + (13 * 32))).AsByte(); + Vector256 row45_GH = Avx2.Shuffle(row23_45_GH, Avx.LoadVector256(shuffleVectorsPtr + (14 * 32))).AsByte(); Vector256 row45 = Avx2.Or(Avx2.Or(row45_AB, row45_CD), Avx2.Or(row45_EF, row45_GH)); - // rows 6 7 - Vector256 row67_CD = Avx2.Shuffle(row4567_CD, Avx.LoadVector256(shuffleVectorsPtr + (15 * 32)).AsByte()); - - Vector256 row67_EF = Avx2.PermuteVar8x32(rowsEF.AsInt32(), rows_AB23_CD45_EF67_shuffleMask).AsByte(); - row67_EF = Avx2.Shuffle(row67_EF, Avx.LoadVector256(shuffleVectorsPtr + (16 * 32)).AsByte()); - - Vector256 row67_GH = Avx2.PermuteVar8x32(rowsGH.AsInt32(), rows_EF45_GH67_shuffleMask).AsByte(); - row67_GH = Avx2.Shuffle(row67_GH, Avx.LoadVector256(shuffleVectorsPtr + (17 * 32)).AsByte()); + /* row67 - C7 D7 E6 F5 G4 H3 H4 G5 | F6 E7 F7 G6 H5 H6 G7 H7 */ + Vector256 row67_CD = Avx2.Shuffle(row45_67_CD, Avx.LoadVector256(shuffleVectorsPtr + (15 * 32))).AsByte(); + Vector256 crln_67_EF_67_GH = Avx.LoadVector256(shuffleVectorsPtr + (16 * 32)).AsInt32(); + Vector256 row67_EF = Avx2.PermuteVar8x32(rowEF.AsInt32(), crln_67_EF_67_GH).AsByte(); + row67_EF = Avx2.Shuffle(row67_EF, Avx.LoadVector256(shuffleVectorsPtr + (17 * 32))).AsByte(); + Vector256 row67_GH = Avx2.PermuteVar8x32(rowGH.AsInt32(), crln_67_EF_67_GH).AsByte(); + row67_GH = Avx2.Shuffle(row67_GH, Avx.LoadVector256(shuffleVectorsPtr + (18 * 32))).AsByte(); - Vector256 row67 = Avx2.Or(Avx2.Or(row67_CD, row67_EF), row67_GH); + Vector256 row67 = Avx2.Or(row67_CD, Avx2.Or(row67_EF, row67_GH)); block.V01 = row01.AsInt16(); block.V23 = row23.AsInt16(); diff --git a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs index 000de4f88..d119d3031 100644 --- a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs @@ -10,6 +10,7 @@ namespace SixLabors.ImageSharp.Formats.Webp { /// /// Gets the webp file format used. Either lossless or lossy. + /// Defaults to lossy. /// WebpFileFormatType? FileFormat { get; } diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index 8640261b1..195fa62bd 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// Indicating what file format compression should be used. + /// Defaults to lossy. /// private readonly WebpFileFormatType? fileFormat; @@ -112,43 +113,43 @@ namespace SixLabors.ImageSharp.Formats.Webp Guard.NotNull(stream, nameof(stream)); this.configuration = image.GetConfiguration(); - bool lossy; + bool lossless; if (this.fileFormat is not null) { - lossy = this.fileFormat == WebpFileFormatType.Lossy; + lossless = this.fileFormat == WebpFileFormatType.Lossless; } else { WebpMetadata webpMetadata = image.Metadata.GetWebpMetadata(); - lossy = webpMetadata.FileFormat == WebpFileFormatType.Lossy; + lossless = webpMetadata.FileFormat == WebpFileFormatType.Lossless; } - if (lossy) + if (lossless) { - using var enc = new Vp8Encoder( + using var enc = new Vp8LEncoder( this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, - this.entropyPasses, - this.filterStrength, - this.spatialNoiseShaping); + this.transparentColorMode, + this.nearLossless, + this.nearLosslessQuality); enc.Encode(image, stream); } else { - using var enc = new Vp8LEncoder( + using var enc = new Vp8Encoder( this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, - this.transparentColorMode, - this.nearLossless, - this.nearLosslessQuality); + this.entropyPasses, + this.filterStrength, + this.spatialNoiseShaping); enc.Encode(image, stream); } } diff --git a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs index d1c39ccbf..13b339597 100644 --- a/src/ImageSharp/Memory/Buffer2DRegion{T}.cs +++ b/src/ImageSharp/Memory/Buffer2DRegion{T}.cs @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Memory /// The row index /// The span [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span GetRowSpan(int y) + public Span DangerousGetRowSpan(int y) { int yy = this.Rectangle.Y + y; int xx = this.Rectangle.X; @@ -152,7 +152,7 @@ namespace SixLabors.ImageSharp.Memory for (int y = 0; y < this.Rectangle.Height; y++) { - Span row = this.GetRowSpan(y); + Span row = this.DangerousGetRowSpan(y); row.Clear(); } } diff --git a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs index 2a41329a6..3e420ca03 100644 --- a/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/CloningImageProcessor{TPixel}.cs @@ -46,38 +46,25 @@ namespace SixLabors.ImageSharp.Processing.Processors /// Image ICloningImageProcessor.CloneAndExecute() { - try - { - Image clone = this.CreateTarget(); - this.CheckFrameCount(this.Source, clone); + Image clone = this.CreateTarget(); + this.CheckFrameCount(this.Source, clone); - Configuration configuration = this.Configuration; - this.BeforeImageApply(clone); + Configuration configuration = this.Configuration; + this.BeforeImageApply(clone); - for (int i = 0; i < this.Source.Frames.Count; i++) - { - ImageFrame sourceFrame = this.Source.Frames[i]; - ImageFrame clonedFrame = clone.Frames[i]; + for (int i = 0; i < this.Source.Frames.Count; i++) + { + ImageFrame sourceFrame = this.Source.Frames[i]; + ImageFrame clonedFrame = clone.Frames[i]; - this.BeforeFrameApply(sourceFrame, clonedFrame); - this.OnFrameApply(sourceFrame, clonedFrame); - this.AfterFrameApply(sourceFrame, clonedFrame); - } + this.BeforeFrameApply(sourceFrame, clonedFrame); + this.OnFrameApply(sourceFrame, clonedFrame); + this.AfterFrameApply(sourceFrame, clonedFrame); + } - this.AfterImageApply(clone); + this.AfterImageApply(clone); - return clone; - } -#if DEBUG - catch (Exception) - { - throw; -#else - catch (Exception ex) - { - throw new ImageProcessingException($"An error occurred when processing the image using {this.GetType().Name}. See the inner exception for more detail.", ex); -#endif - } + return clone; } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs index 77ba9582d..75f82a25a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor.cs @@ -21,6 +21,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler, nameof(sampler)); + if (TransformUtils.IsDegenerate(matrix)) + { + throw new DegenerateTransformException("Matrix is degenerate. Check input values."); + } + this.Sampler = sampler; this.TransformMatrix = matrix; this.DestinationSize = targetDimensions; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs index 640527fe7..f6fadca33 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/AffineTransformProcessor{TPixel}.cs @@ -61,10 +61,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Matrix3x2 matrix = this.transformMatrix; // Handle transforms that result in output identical to the original. - if (matrix.Equals(default) || matrix.Equals(Matrix3x2.Identity)) + // Degenerate matrices are already handled in the upstream definition. + if (matrix.Equals(Matrix3x2.Identity)) { // The clone will be blank here copy all the pixel data over - source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); + var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds()); + Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest); + Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest); + for (int y = 0; y < sourceBuffer.Height; y++) + { + sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y)); + } + return; } @@ -73,7 +81,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (sampler is NearestNeighborResampler) { - var nnOperation = new NNAffineOperation(source.PixelBuffer, destination.PixelBuffer, matrix); + var nnOperation = new NNAffineOperation( + source.PixelBuffer, + Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + destination.PixelBuffer, + matrix); + ParallelRowIterator.IterateRows( configuration, destination.Bounds(), @@ -85,6 +98,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms var operation = new AffineOperation( configuration, source.PixelBuffer, + Rectangle.Intersect(this.SourceRectangle, source.Bounds()), destination.PixelBuffer, in sampler, matrix); @@ -105,12 +119,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public NNAffineOperation( Buffer2D source, + Rectangle bounds, Buffer2D destination, Matrix3x2 matrix) { this.source = source; + this.bounds = bounds; this.destination = destination; - this.bounds = source.Bounds(); this.matrix = matrix; } @@ -138,6 +153,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { private readonly Configuration configuration; private readonly Buffer2D source; + private readonly Rectangle bounds; private readonly Buffer2D destination; private readonly TResampler sampler; private readonly Matrix3x2 matrix; @@ -148,12 +164,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public AffineOperation( Configuration configuration, Buffer2D source, + Rectangle bounds, Buffer2D destination, in TResampler sampler, Matrix3x2 matrix) { this.configuration = configuration; this.source = source; + this.bounds = bounds; this.destination = destination; this.sampler = sampler; this.matrix = matrix; @@ -182,8 +200,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms TResampler sampler = this.sampler; float yRadius = this.yRadius; float xRadius = this.xRadius; - int maxY = this.source.Height - 1; - int maxX = this.source.Width - 1; + int minY = this.bounds.Y; + int maxY = this.bounds.Bottom - 1; + int minX = this.bounds.X; + int maxX = this.bounds.Right - 1; for (int y = rows.Min; y < rows.Max; y++) { @@ -200,10 +220,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float pY = point.Y; float pX = point.X; - int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); - int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); - int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); - int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, minY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, minY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, minX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, minX, maxX); if (bottom == top || right == left) { @@ -245,8 +265,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms TResampler sampler = this.sampler; float yRadius = this.yRadius; float xRadius = this.xRadius; - int maxY = this.source.Height - 1; - int maxX = this.source.Width - 1; + int minY = this.bounds.Y; + int maxY = this.bounds.Bottom - 1; + int minX = this.bounds.X; + int maxX = this.bounds.Right - 1; for (int y = rows.Min; y < rows.Max; y++) { @@ -263,10 +285,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float pY = point.Y; float pX = point.X; - int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); - int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); - int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); - int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, minY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, minY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, minX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, minX, maxX); if (bottom == top || right == left) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs index c6168b461..fd0c7f23b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/LinearTransformUtility.cs @@ -39,11 +39,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The radius. /// The center position. + /// The min allowed amouunt. /// The max allowed amouunt. /// The . [MethodImpl(InliningOptions.ShortMethod)] - public static int GetRangeStart(float radius, float center, int max) - => Numerics.Clamp((int)MathF.Ceiling(center - radius), 0, max); + public static int GetRangeStart(float radius, float center, int min, int max) + => Numerics.Clamp((int)MathF.Ceiling(center - radius), min, max); /// /// Gets the end position (inclusive) for a sampling range given @@ -51,10 +52,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The radius. /// The center position. + /// The min allowed amouunt. /// The max allowed amouunt. /// The . [MethodImpl(InliningOptions.ShortMethod)] - public static int GetRangeEnd(float radius, float center, int max) - => Numerics.Clamp((int)MathF.Floor(center + radius), 0, max); + public static int GetRangeEnd(float radius, float center, int min, int max) + => Numerics.Clamp((int)MathF.Floor(center + radius), min, max); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs index 338489d3f..5eb89fe8a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor.cs @@ -21,6 +21,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Guard.NotNull(sampler, nameof(sampler)); Guard.MustBeValueType(sampler, nameof(sampler)); + if (TransformUtils.IsDegenerate(matrix)) + { + throw new DegenerateTransformException("Matrix is degenerate. Check input values."); + } + this.Sampler = sampler; this.TransformMatrix = matrix; this.DestinationSize = targetDimensions; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs index cf6567629..26b970d8b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Linear/ProjectiveTransformProcessor{TPixel}.cs @@ -60,10 +60,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Matrix4x4 matrix = this.transformMatrix; // Handle transforms that result in output identical to the original. - if (matrix.Equals(default) || matrix.Equals(Matrix4x4.Identity)) + // Degenerate matrices are already handled in the upstream definition. + if (matrix.Equals(Matrix4x4.Identity)) { // The clone will be blank here copy all the pixel data over - source.GetPixelMemoryGroup().CopyTo(destination.GetPixelMemoryGroup()); + var interest = Rectangle.Intersect(this.SourceRectangle, destination.Bounds()); + Buffer2DRegion sourceBuffer = source.PixelBuffer.GetRegion(interest); + Buffer2DRegion destbuffer = destination.PixelBuffer.GetRegion(interest); + for (int y = 0; y < sourceBuffer.Height; y++) + { + sourceBuffer.DangerousGetRowSpan(y).CopyTo(destbuffer.DangerousGetRowSpan(y)); + } + return; } @@ -72,7 +80,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms if (sampler is NearestNeighborResampler) { - var nnOperation = new NNProjectiveOperation(source.PixelBuffer, destination.PixelBuffer, matrix); + var nnOperation = new NNProjectiveOperation( + source.PixelBuffer, + Rectangle.Intersect(this.SourceRectangle, source.Bounds()), + destination.PixelBuffer, + matrix); + ParallelRowIterator.IterateRows( configuration, destination.Bounds(), @@ -84,6 +97,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms var operation = new ProjectiveOperation( configuration, source.PixelBuffer, + Rectangle.Intersect(this.SourceRectangle, source.Bounds()), destination.PixelBuffer, in sampler, matrix); @@ -104,12 +118,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms [MethodImpl(InliningOptions.ShortMethod)] public NNProjectiveOperation( Buffer2D source, + Rectangle bounds, Buffer2D destination, Matrix4x4 matrix) { this.source = source; + this.bounds = bounds; this.destination = destination; - this.bounds = source.Bounds(); this.matrix = matrix; } @@ -137,6 +152,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { private readonly Configuration configuration; private readonly Buffer2D source; + private readonly Rectangle bounds; private readonly Buffer2D destination; private readonly TResampler sampler; private readonly Matrix4x4 matrix; @@ -147,18 +163,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public ProjectiveOperation( Configuration configuration, Buffer2D source, + Rectangle bounds, Buffer2D destination, in TResampler sampler, Matrix4x4 matrix) { this.configuration = configuration; this.source = source; + this.bounds = bounds; this.destination = destination; this.sampler = sampler; this.matrix = matrix; - this.yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Height, destination.Height); - this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, source.Width, destination.Width); + this.yRadius = LinearTransformUtility.GetSamplingRadius(in sampler, bounds.Height, destination.Height); + this.xRadius = LinearTransformUtility.GetSamplingRadius(in sampler, bounds.Width, destination.Width); } [MethodImpl(InliningOptions.ShortMethod)] @@ -181,8 +199,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms TResampler sampler = this.sampler; float yRadius = this.yRadius; float xRadius = this.xRadius; - int maxY = this.source.Height - 1; - int maxX = this.source.Width - 1; + int minY = this.bounds.Y; + int maxY = this.bounds.Bottom - 1; + int minX = this.bounds.X; + int maxX = this.bounds.Right - 1; for (int y = rows.Min; y < rows.Max; y++) { @@ -199,10 +219,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float pY = point.Y; float pX = point.X; - int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); - int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); - int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); - int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, minY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, minY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, minX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, minX, maxX); if (bottom <= top || right <= left) { @@ -244,8 +264,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms TResampler sampler = this.sampler; float yRadius = this.yRadius; float xRadius = this.xRadius; - int maxY = this.source.Height - 1; - int maxX = this.source.Width - 1; + int minY = this.bounds.Y; + int maxY = this.bounds.Bottom - 1; + int minX = this.bounds.X; + int maxX = this.bounds.Right - 1; for (int y = rows.Min; y < rows.Max; y++) { @@ -262,10 +284,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms float pY = point.Y; float pX = point.X; - int top = LinearTransformUtility.GetRangeStart(yRadius, pY, maxY); - int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, maxY); - int left = LinearTransformUtility.GetRangeStart(xRadius, pX, maxX); - int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, maxX); + int top = LinearTransformUtility.GetRangeStart(yRadius, pY, minY, maxY); + int bottom = LinearTransformUtility.GetRangeEnd(yRadius, pY, minY, maxY); + int left = LinearTransformUtility.GetRangeStart(xRadius, pX, minX, maxX); + int right = LinearTransformUtility.GetRangeEnd(xRadius, pX, minX, maxX); if (bottom <= top || right <= left) { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index 93aeb75f4..90cbf8bda 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -172,7 +172,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = calculationInterval.Min; y < calculationInterval.Max; y++) { - Span sourceRow = this.source.GetRowSpan(y); + Span sourceRow = this.source.DangerousGetRowSpan(y); PixelOperations.Instance.ToVector4( this.configuration, diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs index ae7e81254..9576cbd3c 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // Reference implementation quantizes given block via division Block8x8 expected = default; - ReferenceImplementations.Quantize(ref source, ref expected, ref quant, ZigZag.ZigZagOrder); + ReferenceImplementations.Quantize(ref source, ref expected, ref quant, ZigZag.TransposingOrder); // Actual current implementation quantizes given block via multiplication // With quantization table reciprocal diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 36570ce55..9c467a1cc 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -135,10 +135,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg FastFloatingPointDCT.AdjustToIDCT(ref dequantMatrix); srcBlock.MultiplyInPlace(ref dequantMatrix); + // testee // IDCT implementation tranforms blocks after transposition srcBlock.TransposeInplace(); - - // IDCT calculation FastFloatingPointDCT.TransformIDCT(ref srcBlock); float[] actualDest = srcBlock.ToArray(); @@ -180,7 +179,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); // testee + // Second transpose call is done by Quantize step + // Do this manually here just to be complient to the reference implementation FastFloatingPointDCT.TransformFDCT(ref block); + block.TransposeInplace(); // Part of the IDCT calculations is fused into the quantization step // We must multiply input block with adjusted no-quantization matrix diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs index cad39224e..7043549b2 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp private static string TestImageLossyFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, Lossy.NoFilter06); [Theory] - [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] // if its not a webp input image, it should default to lossless. + [WithFile(Flag, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] // If its not a webp input image, it should default to lossy. [WithFile(Lossless.NoTransform1, PixelTypes.Rgba32, WebpFileFormatType.Lossless)] [WithFile(Lossy.Bike, PixelTypes.Rgba32, WebpFileFormatType.Lossy)] public void Encode_PreserveRatio(TestImageProvider provider, WebpFileFormatType expectedFormat) diff --git a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs index 00acce64e..7d98eff61 100644 --- a/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs +++ b/tests/ImageSharp.Tests/Memory/Allocators/UniformUnmanagedMemoryPoolTests.cs @@ -253,35 +253,40 @@ namespace SixLabors.ImageSharp.Tests.Memory.Allocators [InlineData(true)] public void RentReturnRelease_SubsequentRentReturnsDifferentHandles(bool multiple) { - var pool = new UniformUnmanagedMemoryPool(16, 16); - using var cleanup = new CleanupUtil(pool); - UnmanagedMemoryHandle b0 = pool.Rent(); - IntPtr h0 = b0.Handle; - UnmanagedMemoryHandle b1 = pool.Rent(); - IntPtr h1 = b1.Handle; - pool.Return(b0); - pool.Return(b1); - pool.Release(); + RemoteExecutor.Invoke(RunTest, multiple.ToString()).Dispose(); - // Do some unmanaged allocations to make sure new pool buffers are different: - IntPtr[] dummy = Enumerable.Range(0, 100).Select(_ => Marshal.AllocHGlobal(16)).ToArray(); - cleanup.Register(dummy); - - if (multiple) - { - UnmanagedMemoryHandle b = pool.Rent(); - cleanup.Register(b); - Assert.NotEqual(h0, b.Handle); - Assert.NotEqual(h1, b.Handle); - } - else + static void RunTest(string multipleInner) { - UnmanagedMemoryHandle[] b = pool.Rent(2); - cleanup.Register(b); - Assert.NotEqual(h0, b[0].Handle); - Assert.NotEqual(h1, b[0].Handle); - Assert.NotEqual(h0, b[1].Handle); - Assert.NotEqual(h1, b[1].Handle); + var pool = new UniformUnmanagedMemoryPool(16, 16); + using var cleanup = new CleanupUtil(pool); + UnmanagedMemoryHandle b0 = pool.Rent(); + IntPtr h0 = b0.Handle; + UnmanagedMemoryHandle b1 = pool.Rent(); + IntPtr h1 = b1.Handle; + pool.Return(b0); + pool.Return(b1); + pool.Release(); + + // Do some unmanaged allocations to make sure new pool buffers are different: + IntPtr[] dummy = Enumerable.Range(0, 100).Select(_ => Marshal.AllocHGlobal(16)).ToArray(); + cleanup.Register(dummy); + + if (bool.Parse(multipleInner)) + { + UnmanagedMemoryHandle b = pool.Rent(); + cleanup.Register(b); + Assert.NotEqual(h0, b.Handle); + Assert.NotEqual(h1, b.Handle); + } + else + { + UnmanagedMemoryHandle[] b = pool.Rent(2); + cleanup.Register(b); + Assert.NotEqual(h0, b[0].Handle); + Assert.NotEqual(h1, b[0].Handle); + Assert.NotEqual(h0, b[1].Handle); + Assert.NotEqual(h1, b[1].Handle); + } } } diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index 76e55aa3a..12b7c74ab 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests.Memory Buffer2DRegion region = buffer.GetRegion(r); - Span span = region.GetRowSpan(y); + Span span = region.DangerousGetRowSpan(y); Assert.Equal(w, span.Length); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs index 3cd8cec15..33725f7aa 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/AffineTransformTests.cs @@ -4,6 +4,7 @@ using System; using System.Numerics; using System.Reflection; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; @@ -224,6 +225,46 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms c => c.Transform(builder)); } + [Fact] + public void Issue1911() + { + using var image = new Image(100, 100); + image.Mutate(x => x = x.Transform(new Rectangle(0, 0, 99, 100), Matrix3x2.Identity, new Size(99, 100), KnownResamplers.Lanczos2)); + + Assert.Equal(99, image.Width); + Assert.Equal(100, image.Height); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void Identity(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + Matrix3x2 m = Matrix3x2.Identity; + Rectangle r = new(25, 25, 50, 50); + image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0.0001F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 57F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0F)] + public void Transform_With_Custom_Dimensions(TestImageProvider provider, float radians) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + var m = Matrix3x2.CreateRotation(radians, new Vector2(50, 50)); + Rectangle r = new(25, 25, 50, 50); + image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); + image.DebugSave(provider, testOutputDetails: radians); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); + } + private static IResampler GetResampler(string name) { PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs index ef8e03763..d841c963f 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ProjectiveTransformTests.cs @@ -147,6 +147,46 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms } } + [Fact] + public void Issue1911() + { + using var image = new Image(100, 100); + image.Mutate(x => x = x.Transform(new Rectangle(0, 0, 99, 100), Matrix4x4.Identity, new Size(99, 100), KnownResamplers.Lanczos2)); + + Assert.Equal(99, image.Width); + Assert.Equal(100, image.Height); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32)] + public void Identity(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + Matrix4x4 m = Matrix4x4.Identity; + Rectangle r = new(25, 25, 50, 50); + image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); + image.DebugSave(provider); + image.CompareToReferenceOutput(ValidatorComparer, provider); + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0.0001F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 57F)] + [WithTestPatternImages(100, 100, PixelTypes.Rgba32, 0F)] + public void Transform_With_Custom_Dimensions(TestImageProvider provider, float radians) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(); + + Matrix4x4 m = Matrix4x4.CreateRotationX(radians, new Vector3(50, 50, 1F)) * Matrix4x4.CreateRotationY(radians, new Vector3(50, 50, 1F)); + Rectangle r = new(25, 25, 50, 50); + image.Mutate(x => x.Transform(r, m, new Size(100, 100), KnownResamplers.Bicubic)); + image.DebugSave(provider, testOutputDetails: radians); + image.CompareToReferenceOutput(ValidatorComparer, provider, testOutputDetails: radians); + } + private static IResampler GetResampler(string name) { PropertyInfo property = typeof(KnownResamplers).GetTypeInfo().GetProperty(name); diff --git a/tests/ImageSharp.Tests/Quantization/PixelSamplingStrategyTests.cs b/tests/ImageSharp.Tests/Quantization/PixelSamplingStrategyTests.cs index 9a8d8351b..7529b0e5f 100644 --- a/tests/ImageSharp.Tests/Quantization/PixelSamplingStrategyTests.cs +++ b/tests/ImageSharp.Tests/Quantization/PixelSamplingStrategyTests.cs @@ -75,7 +75,7 @@ namespace SixLabors.ImageSharp.Tests.Quantization var white = new L8(255); for (int y = 0; y < region.Height; y++) { - region.GetRowSpan(y).Fill(white); + region.DangerousGetRowSpan(y).Fill(white); } } diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Identity_Rgba32_TestPattern100x100.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Identity_Rgba32_TestPattern100x100.png new file mode 100644 index 000000000..ce6e8ce9f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Identity_Rgba32_TestPattern100x100.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595 +size 3564 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png index f5af01102..53ac0ff89 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle1_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b01d54838d678b61c3b7a1c7e76ff9a60b3f5f4faef5af848231177eac956eb1 -size 1262 +oid sha256:bbe1ffaf7b801fd92724438cc810fd0c5506e0a907b970c4f0bf5bec3627ca2a +size 551 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png index e5005ac5d..2480164d6 100644 --- a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_FromSourceRectangle2_Rgba32_TestPattern96x48.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d34394771605c2a70cc23f3841592c20d22a68aaabf2ad6e8aba7348a181afb3 -size 531 +oid sha256:b45933471a1af1b6d4112240e1bc6b6187065a872043ddbf917200ce9e8cc84b +size 371 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png new file mode 100644 index 000000000..4b9953b67 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd3b29b530e221618f65cd5e493b21fe3c27804fde7664636b7bb002f72abbb2 +size 3663 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png new file mode 100644 index 000000000..ce6e8ce9f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595 +size 3564 diff --git a/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png new file mode 100644 index 000000000..5f4911e47 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/AffineTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a35757fef08a6fd9b37e719d5be7a82d5ff79f0395e082f697d9ebe9c7f03cc8 +size 5748 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Identity_Rgba32_TestPattern100x100.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Identity_Rgba32_TestPattern100x100.png new file mode 100644 index 000000000..ce6e8ce9f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Identity_Rgba32_TestPattern100x100.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595 +size 3564 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png new file mode 100644 index 000000000..e8efa8a98 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.0001.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:39c25539c3c9b8926bf65c041df693a60617bbe8653bb72357bde5ab6342c59c +size 3618 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png new file mode 100644 index 000000000..ce6e8ce9f --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da8229605bda413676a42f587df250a743540e6e00c04eacb1e622f223e19595 +size 3564 diff --git a/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png new file mode 100644 index 000000000..99a74e400 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/ProjectiveTransformTests/Transform_With_Custom_Dimensions_Rgba32_TestPattern100x100_57.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b1fc95fdf07c7443147205afffb157aa82f94818cfbb833a615c42f584fbda0 +size 5070