From dd35b743c58c3a3ff5f72d96813d807325f0155a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 18 May 2022 15:05:12 +0200 Subject: [PATCH 01/62] Used inline vectors if they are constants --- .../Jpeg/Components/Block8x8F.Intrinsic.cs | 5 +- .../FastFloatingPointDCT.Intrinsic.cs | 26 +--- .../Webp/Lossless/ColorSpaceTransformUtils.cs | 92 +++++------ .../Formats/Webp/Lossless/LosslessUtils.cs | 84 ++++------ .../Formats/Webp/Lossy/LossyUtils.cs | 147 +++++++++--------- src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs | 38 ++--- .../Formats/Webp/Lossy/Vp8Encoding.cs | 101 +++++------- .../Formats/Webp/Lossy/Vp8Histogram.cs | 6 +- .../Formats/Webp/Lossy/Vp8Residual.cs | 10 +- .../Formats/Webp/Lossy/YuvConversion.cs | 89 ++++------- .../Formats/Webp/WebpCommonUtils.cs | 43 +++-- 11 files changed, 249 insertions(+), 392 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs index 0971ccdca0..879d64458d 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.Intrinsic.cs @@ -35,8 +35,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components [FieldOffset(224)] public Vector256 V7; - private static readonly Vector256 MultiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7); - private static unsafe void MultiplyIntoInt16_Avx2(ref Block8x8F a, ref Block8x8F b, ref Block8x8 dest) { DebugGuard.IsTrue(Avx2.IsSupported, "Avx2 support is required to run this operation!"); @@ -45,6 +43,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components ref Vector256 bBase = ref b.V0; ref Vector256 destRef = ref dest.V01; + Vector256 multiplyIntoInt16ShuffleMask = Vector256.Create(0, 1, 4, 5, 2, 3, 6, 7); for (nint i = 0; i < 8; i += 2) { @@ -52,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Vector256 row1 = Avx.ConvertToVector256Int32(Avx.Multiply(Unsafe.Add(ref aBase, i + 1), Unsafe.Add(ref bBase, i + 1))); Vector256 row = Avx2.PackSignedSaturate(row0, row1); - row = Avx2.PermuteVar8x32(row.AsInt32(), MultiplyIntoInt16ShuffleMask).AsInt16(); + row = Avx2.PermuteVar8x32(row.AsInt32(), multiplyIntoInt16ShuffleMask).AsInt16(); Unsafe.Add(ref destRef, (IntPtr)((uint)i / 2)) = row; } diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs index 8acc4b6269..86640842e1 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.Intrinsic.cs @@ -9,18 +9,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components { internal static partial class FastFloatingPointDCT { -#pragma warning disable SA1310, SA1311, IDE1006 // naming rule violation warnings - private static readonly Vector256 mm256_F_0_7071 = Vector256.Create(0.707106781f); - private static readonly Vector256 mm256_F_0_3826 = Vector256.Create(0.382683433f); - private static readonly Vector256 mm256_F_0_5411 = Vector256.Create(0.541196100f); - private static readonly Vector256 mm256_F_1_3065 = Vector256.Create(1.306562965f); - - private static readonly Vector256 mm256_F_1_4142 = Vector256.Create(1.414213562f); - private static readonly Vector256 mm256_F_1_8477 = Vector256.Create(1.847759065f); - private static readonly Vector256 mm256_F_n1_0823 = Vector256.Create(-1.082392200f); - private static readonly Vector256 mm256_F_n2_6131 = Vector256.Create(-2.613125930f); -#pragma warning restore SA1310, SA1311, IDE1006 - /// /// Apply floating point FDCT inplace using simd operations. /// @@ -57,6 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components block.V0 = Avx.Add(tmp10, tmp11); block.V4 = Avx.Subtract(tmp10, tmp11); + Vector256 mm256_F_0_7071 = Vector256.Create(0.707106781f); Vector256 z1 = Avx.Multiply(Avx.Add(tmp12, tmp13), mm256_F_0_7071); block.V2 = Avx.Add(tmp13, z1); block.V6 = Avx.Subtract(tmp13, z1); @@ -66,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components tmp11 = Avx.Add(tmp5, tmp6); tmp12 = Avx.Add(tmp6, tmp7); - Vector256 z5 = Avx.Multiply(Avx.Subtract(tmp10, tmp12), mm256_F_0_3826); - Vector256 z2 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, mm256_F_0_5411, tmp10); - Vector256 z4 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, mm256_F_1_3065, tmp12); + Vector256 z5 = Avx.Multiply(Avx.Subtract(tmp10, tmp12), Vector256.Create(0.382683433f)); // mm256_F_0_3826 + Vector256 z2 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, Vector256.Create(0.541196100f), tmp10); // mm256_F_0_5411 + Vector256 z4 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, Vector256.Create(1.306562965f), tmp12); // mm256_F_1_3065 Vector256 z3 = Avx.Multiply(tmp11, mm256_F_0_7071); Vector256 z11 = Avx.Add(tmp7, z3); @@ -109,6 +98,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components Vector256 tmp10 = Avx.Add(z5, tmp2); Vector256 tmp11 = Avx.Subtract(z5, tmp2); + Vector256 mm256_F_1_4142 = Vector256.Create(1.414213562f); Vector256 tmp13 = Avx.Add(tmp1, tmp3); Vector256 tmp12 = SimdUtils.HwIntrinsics.MultiplySubstract(tmp13, Avx.Subtract(tmp1, tmp3), mm256_F_1_4142); @@ -131,10 +121,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components tmp7 = Avx.Add(z11, z13); tmp11 = Avx.Multiply(Avx.Subtract(z11, z13), mm256_F_1_4142); - z5 = Avx.Multiply(Avx.Add(z10, z12), mm256_F_1_8477); + z5 = Avx.Multiply(Avx.Add(z10, z12), Vector256.Create(1.847759065f)); // mm256_F_1_8477 - tmp10 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z12, mm256_F_n1_0823); - tmp12 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z10, mm256_F_n2_6131); + tmp10 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z12, Vector256.Create(-1.082392200f)); // mm256_F_n1_0823 + tmp12 = SimdUtils.HwIntrinsics.MultiplyAdd(z5, z10, Vector256.Create(-2.613125930f)); // mm256_F_n2_6131 tmp6 = Avx.Subtract(tmp12, tmp7); tmp5 = Avx.Subtract(tmp11, tmp6); diff --git a/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs index 71f3c5ca9e..6a7f4c9b0e 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/ColorSpaceTransformUtils.cs @@ -13,36 +13,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { internal static class ColorSpaceTransformUtils { -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector128 CollectColorRedTransformsGreenMask = Vector128.Create(0x00ff00).AsByte(); - - private static readonly Vector128 CollectColorRedTransformsAndMask = Vector128.Create((short)0xff).AsByte(); - - private static readonly Vector256 CollectColorRedTransformsGreenMask256 = Vector256.Create(0x00ff00).AsByte(); - - private static readonly Vector256 CollectColorRedTransformsAndMask256 = Vector256.Create((short)0xff).AsByte(); - - private static readonly Vector128 CollectColorBlueTransformsGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - - private static readonly Vector128 CollectColorBlueTransformsGreenBlueMask = Vector128.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0); - - private static readonly Vector128 CollectColorBlueTransformsBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); - - private static readonly Vector128 CollectColorBlueTransformsShuffleLowMask = Vector128.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255); - - private static readonly Vector128 CollectColorBlueTransformsShuffleHighMask = Vector128.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14); - - private static readonly Vector256 CollectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255); - - private static readonly Vector256 CollectColorBlueTransformsShuffleHighMask256 = Vector256.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30); - - private static readonly Vector256 CollectColorBlueTransformsGreenBlueMask256 = Vector256.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0); - - private static readonly Vector256 CollectColorBlueTransformsBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); - - private static readonly Vector256 CollectColorBlueTransformsGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); -#endif - public static void CollectColorBlueTransforms(Span bgra, int stride, int tileWidth, int tileHeight, int greenToBlue, int redToBlue, Span histo) { #if SUPPORTS_RUNTIME_INTRINSICS @@ -50,8 +20,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { const int span = 16; Span values = stackalloc ushort[span]; - var multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue)); - var multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue)); + Vector256 collectColorBlueTransformsShuffleLowMask256 = Vector256.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30, 255, 255, 255, 255, 255, 255, 255, 255); + Vector256 collectColorBlueTransformsShuffleHighMask256 = Vector256.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255, 255, 18, 255, 22, 255, 26, 255, 30); + Vector256 collectColorBlueTransformsGreenBlueMask256 = Vector256.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0); + Vector256 collectColorBlueTransformsGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector256 collectColorBlueTransformsBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); + Vector256 multsr = Vector256.Create(LosslessUtils.Cst5b(redToBlue)); + Vector256 multsg = Vector256.Create(LosslessUtils.Cst5b(greenToBlue)); for (int y = 0; y < tileHeight; y++) { Span srcSpan = bgra.Slice(y * stride); @@ -62,18 +37,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint input1Idx = x + (span / 2); Vector256 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector256 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); - Vector256 r0 = Avx2.Shuffle(input0, CollectColorBlueTransformsShuffleLowMask256); - Vector256 r1 = Avx2.Shuffle(input1, CollectColorBlueTransformsShuffleHighMask256); + Vector256 r0 = Avx2.Shuffle(input0, collectColorBlueTransformsShuffleLowMask256); + Vector256 r1 = Avx2.Shuffle(input1, collectColorBlueTransformsShuffleHighMask256); Vector256 r = Avx2.Or(r0, r1); - Vector256 gb0 = Avx2.And(input0, CollectColorBlueTransformsGreenBlueMask256); - Vector256 gb1 = Avx2.And(input1, CollectColorBlueTransformsGreenBlueMask256); + Vector256 gb0 = Avx2.And(input0, collectColorBlueTransformsGreenBlueMask256); + Vector256 gb1 = Avx2.And(input1, collectColorBlueTransformsGreenBlueMask256); Vector256 gb = Avx2.PackUnsignedSaturate(gb0.AsInt32(), gb1.AsInt32()); - Vector256 g = Avx2.And(gb.AsByte(), CollectColorBlueTransformsGreenMask256); + Vector256 g = Avx2.And(gb.AsByte(), collectColorBlueTransformsGreenMask256); Vector256 a = Avx2.MultiplyHigh(r.AsInt16(), multsr); Vector256 b = Avx2.MultiplyHigh(g.AsInt16(), multsg); Vector256 c = Avx2.Subtract(gb.AsByte(), b.AsByte()); Vector256 d = Avx2.Subtract(c, a.AsByte()); - Vector256 e = Avx2.And(d, CollectColorBlueTransformsBlueMask256); + Vector256 e = Avx2.And(d, collectColorBlueTransformsBlueMask256); ref ushort outputRef = ref MemoryMarshal.GetReference(values); Unsafe.As>(ref outputRef) = e.AsUInt16(); @@ -95,8 +70,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { const int span = 8; Span values = stackalloc ushort[span]; - var multsr = Vector128.Create(LosslessUtils.Cst5b(redToBlue)); - var multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue)); + Vector128 collectColorBlueTransformsShuffleLowMask = Vector128.Create(255, 2, 255, 6, 255, 10, 255, 14, 255, 255, 255, 255, 255, 255, 255, 255); + Vector128 collectColorBlueTransformsShuffleHighMask = Vector128.Create(255, 255, 255, 255, 255, 255, 255, 255, 255, 2, 255, 6, 255, 10, 255, 14); + Vector128 collectColorBlueTransformsGreenBlueMask = Vector128.Create(255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0); + Vector128 collectColorBlueTransformsGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector128 collectColorBlueTransformsBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); + Vector128 multsr = Vector128.Create(LosslessUtils.Cst5b(redToBlue)); + Vector128 multsg = Vector128.Create(LosslessUtils.Cst5b(greenToBlue)); for (int y = 0; y < tileHeight; y++) { Span srcSpan = bgra.Slice(y * stride); @@ -107,18 +87,18 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint input1Idx = x + (span / 2); Vector128 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector128 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); - Vector128 r0 = Ssse3.Shuffle(input0, CollectColorBlueTransformsShuffleLowMask); - Vector128 r1 = Ssse3.Shuffle(input1, CollectColorBlueTransformsShuffleHighMask); + Vector128 r0 = Ssse3.Shuffle(input0, collectColorBlueTransformsShuffleLowMask); + Vector128 r1 = Ssse3.Shuffle(input1, collectColorBlueTransformsShuffleHighMask); Vector128 r = Sse2.Or(r0, r1); - Vector128 gb0 = Sse2.And(input0, CollectColorBlueTransformsGreenBlueMask); - Vector128 gb1 = Sse2.And(input1, CollectColorBlueTransformsGreenBlueMask); + Vector128 gb0 = Sse2.And(input0, collectColorBlueTransformsGreenBlueMask); + Vector128 gb1 = Sse2.And(input1, collectColorBlueTransformsGreenBlueMask); Vector128 gb = Sse41.PackUnsignedSaturate(gb0.AsInt32(), gb1.AsInt32()); - Vector128 g = Sse2.And(gb.AsByte(), CollectColorBlueTransformsGreenMask); + Vector128 g = Sse2.And(gb.AsByte(), collectColorBlueTransformsGreenMask); Vector128 a = Sse2.MultiplyHigh(r.AsInt16(), multsr); Vector128 b = Sse2.MultiplyHigh(g.AsInt16(), multsg); Vector128 c = Sse2.Subtract(gb.AsByte(), b.AsByte()); Vector128 d = Sse2.Subtract(c, a.AsByte()); - Vector128 e = Sse2.And(d, CollectColorBlueTransformsBlueMask); + Vector128 e = Sse2.And(d, collectColorBlueTransformsBlueMask); ref ushort outputRef = ref MemoryMarshal.GetReference(values); Unsafe.As>(ref outputRef) = e.AsUInt16(); @@ -163,7 +143,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless #if SUPPORTS_RUNTIME_INTRINSICS if (Avx2.IsSupported && tileWidth >= 16) { - var multsg = Vector256.Create(LosslessUtils.Cst5b(greenToRed)); + Vector256 collectColorRedTransformsGreenMask256 = Vector256.Create(0x00ff00).AsByte(); + Vector256 collectColorRedTransformsAndMask256 = Vector256.Create((short)0xff).AsByte(); + Vector256 multsg = Vector256.Create(LosslessUtils.Cst5b(greenToRed)); const int span = 16; Span values = stackalloc ushort[span]; for (int y = 0; y < tileHeight; y++) @@ -176,15 +158,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint input1Idx = x + (span / 2); Vector256 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector256 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); - Vector256 g0 = Avx2.And(input0, CollectColorRedTransformsGreenMask256); // 0 0 | g 0 - Vector256 g1 = Avx2.And(input1, CollectColorRedTransformsGreenMask256); + Vector256 g0 = Avx2.And(input0, collectColorRedTransformsGreenMask256); // 0 0 | g 0 + Vector256 g1 = Avx2.And(input1, collectColorRedTransformsGreenMask256); Vector256 g = Avx2.PackUnsignedSaturate(g0.AsInt32(), g1.AsInt32()); // g 0 Vector256 a0 = Avx2.ShiftRightLogical(input0.AsInt32(), 16); // 0 0 | x r Vector256 a1 = Avx2.ShiftRightLogical(input1.AsInt32(), 16); Vector256 a = Avx2.PackUnsignedSaturate(a0, a1); // x r Vector256 b = Avx2.MultiplyHigh(g.AsInt16(), multsg); // x dr Vector256 c = Avx2.Subtract(a.AsByte(), b.AsByte()); // x r' - Vector256 d = Avx2.And(c, CollectColorRedTransformsAndMask256); // 0 r' + Vector256 d = Avx2.And(c, collectColorRedTransformsAndMask256); // 0 r' ref ushort outputRef = ref MemoryMarshal.GetReference(values); Unsafe.As>(ref outputRef) = d.AsUInt16(); @@ -204,7 +186,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } else if (Sse41.IsSupported) { - var multsg = Vector128.Create(LosslessUtils.Cst5b(greenToRed)); + Vector128 collectColorRedTransformsGreenMask = Vector128.Create(0x00ff00).AsByte(); + Vector128 collectColorRedTransformsAndMask = Vector128.Create((short)0xff).AsByte(); + Vector128 multsg = Vector128.Create(LosslessUtils.Cst5b(greenToRed)); const int span = 8; Span values = stackalloc ushort[span]; for (int y = 0; y < tileHeight; y++) @@ -217,15 +201,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint input1Idx = x + (span / 2); Vector128 input0 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input0Idx)).AsByte(); Vector128 input1 = Unsafe.As>(ref Unsafe.Add(ref inputRef, input1Idx)).AsByte(); - Vector128 g0 = Sse2.And(input0, CollectColorRedTransformsGreenMask); // 0 0 | g 0 - Vector128 g1 = Sse2.And(input1, CollectColorRedTransformsGreenMask); + Vector128 g0 = Sse2.And(input0, collectColorRedTransformsGreenMask); // 0 0 | g 0 + Vector128 g1 = Sse2.And(input1, collectColorRedTransformsGreenMask); Vector128 g = Sse41.PackUnsignedSaturate(g0.AsInt32(), g1.AsInt32()); // g 0 Vector128 a0 = Sse2.ShiftRightLogical(input0.AsInt32(), 16); // 0 0 | x r Vector128 a1 = Sse2.ShiftRightLogical(input1.AsInt32(), 16); Vector128 a = Sse41.PackUnsignedSaturate(a0, a1); // x r Vector128 b = Sse2.MultiplyHigh(g.AsInt16(), multsg); // x dr Vector128 c = Sse2.Subtract(a.AsByte(), b.AsByte()); // x r' - Vector128 d = Sse2.And(c, CollectColorRedTransformsAndMask); // 0 r' + Vector128 d = Sse2.And(c, collectColorRedTransformsAndMask); // 0 r' ref ushort outputRef = ref MemoryMarshal.GetReference(values); Unsafe.As>(ref outputRef) = d.AsUInt16(); diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index e7782b0ef4..e86ef0f2ad 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -27,36 +27,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless private const double Log2Reciprocal = 1.44269504088896338700465094007086; -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector256 AddGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); - - private static readonly Vector128 AddGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); - - private static readonly byte AddGreenToBlueAndRedShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); - - private static readonly Vector256 SubtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); - - private static readonly Vector128 SubtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); - - private static readonly byte SubtractGreenFromBlueAndRedShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); - - private static readonly Vector128 TransformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - - private static readonly Vector256 TransformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - - private static readonly Vector128 TransformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); - - private static readonly Vector256 TransformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); - - private static readonly byte TransformColorShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); - - private static readonly Vector128 TransformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - - private static readonly Vector256 TransformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); - - private static readonly byte TransformColorInverseShuffleMask = SimdUtils.Shuffle.MmShuffle(2, 2, 0, 0); -#endif - /// /// Returns the exact index where array1 and array2 are different. For an index /// inferior or equal to bestLenMatch, the return value just has to be strictly @@ -129,13 +99,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless #if SUPPORTS_RUNTIME_INTRINSICS if (Avx2.IsSupported) { + Vector256 addGreenToBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); int numPixels = pixelData.Length; nint i; for (i = 0; i <= numPixels - 8; i += 8) { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector256 input = Unsafe.As>(ref pos).AsByte(); - Vector256 in0g0g = Avx2.Shuffle(input, AddGreenToBlueAndRedMaskAvx2); + Vector256 in0g0g = Avx2.Shuffle(input, addGreenToBlueAndRedMaskAvx2); Vector256 output = Avx2.Add(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -147,13 +118,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } else if (Ssse3.IsSupported) { + Vector128 addGreenToBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); int numPixels = pixelData.Length; nint i; for (i = 0; i <= numPixels - 4; i += 4) { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); - Vector128 in0g0g = Ssse3.Shuffle(input, AddGreenToBlueAndRedMaskSsse3); + Vector128 in0g0g = Ssse3.Shuffle(input, addGreenToBlueAndRedMaskSsse3); Vector128 output = Sse2.Add(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -172,8 +144,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, AddGreenToBlueAndRedShuffleMask); - Vector128 c = Sse2.ShuffleHigh(b, AddGreenToBlueAndRedShuffleMask); // 0g0g + Vector128 b = Sse2.ShuffleLow(a, 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 c = Sse2.ShuffleHigh(b, 0xA0); // MmShuffle(2, 2, 0, 0) 0g0g Vector128 output = Sse2.Add(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -209,13 +181,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless #if SUPPORTS_RUNTIME_INTRINSICS if (Avx2.IsSupported) { + Vector256 subtractGreenFromBlueAndRedMaskAvx2 = Vector256.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255, 17, 255, 17, 255, 21, 255, 21, 255, 25, 255, 25, 255, 29, 255, 29, 255); int numPixels = pixelData.Length; nint i; for (i = 0; i <= numPixels - 8; i += 8) { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector256 input = Unsafe.As>(ref pos).AsByte(); - Vector256 in0g0g = Avx2.Shuffle(input, SubtractGreenFromBlueAndRedMaskAvx2); + Vector256 in0g0g = Avx2.Shuffle(input, subtractGreenFromBlueAndRedMaskAvx2); Vector256 output = Avx2.Subtract(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -227,13 +200,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } else if (Ssse3.IsSupported) { + Vector128 subtractGreenFromBlueAndRedMaskSsse3 = Vector128.Create(1, 255, 1, 255, 5, 255, 5, 255, 9, 255, 9, 255, 13, 255, 13, 255); int numPixels = pixelData.Length; nint i; for (i = 0; i <= numPixels - 4; i += 4) { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); - Vector128 in0g0g = Ssse3.Shuffle(input, SubtractGreenFromBlueAndRedMaskSsse3); + Vector128 in0g0g = Ssse3.Shuffle(input, subtractGreenFromBlueAndRedMaskSsse3); Vector128 output = Sse2.Subtract(input, in0g0g); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -252,8 +226,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, SubtractGreenFromBlueAndRedShuffleMask); - Vector128 c = Sse2.ShuffleHigh(b, SubtractGreenFromBlueAndRedShuffleMask); // 0g0g + Vector128 b = Sse2.ShuffleLow(a, 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 c = Sse2.ShuffleHigh(b, 0xA0); // MmShuffle(2, 2, 0, 0) 0g0g Vector128 output = Sse2.Subtract(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -400,6 +374,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless #if SUPPORTS_RUNTIME_INTRINSICS if (Avx2.IsSupported && numPixels >= 8) { + Vector256 transformColorAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector256 transformColorRedBlueMask256 = Vector256.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); Vector256 multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); @@ -408,15 +384,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); - Vector256 a = Avx2.And(input.AsByte(), TransformColorAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), TransformColorShuffleMask); - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), TransformColorShuffleMask); + Vector256 a = Avx2.And(input.AsByte(), transformColorAlphaGreenMask256); + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.ShiftLeftLogical(input.AsInt16(), 8); Vector256 f = Avx2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); Vector256 g = Avx2.ShiftRightLogical(f.AsInt32(), 16); Vector256 h = Avx2.Add(g.AsByte(), d.AsByte()); - Vector256 i = Avx2.And(h, TransformColorRedBlueMask256); + Vector256 i = Avx2.And(h, transformColorRedBlueMask256); Vector256 output = Avx2.Subtract(input.AsByte(), i); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -428,6 +404,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } else if (Sse2.IsSupported) { + Vector128 transformColorAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); + Vector128 transformColorRedBlueMask = Vector128.Create(255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0); Vector128 multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); nint idx; @@ -435,15 +413,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); - Vector128 a = Sse2.And(input.AsByte(), TransformColorAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), TransformColorShuffleMask); - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), TransformColorShuffleMask); + Vector128 a = Sse2.And(input.AsByte(), transformColorAlphaGreenMask); + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.ShiftLeftLogical(input.AsInt16(), 8); Vector128 f = Sse2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); Vector128 g = Sse2.ShiftRightLogical(f.AsInt32(), 16); Vector128 h = Sse2.Add(g.AsByte(), d.AsByte()); - Vector128 i = Sse2.And(h, TransformColorRedBlueMask); + Vector128 i = Sse2.And(h, transformColorRedBlueMask); Vector128 output = Sse2.Subtract(input.AsByte(), i); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -488,6 +466,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless #if SUPPORTS_RUNTIME_INTRINSICS if (Avx2.IsSupported && pixelData.Length >= 8) { + Vector256 transformColorInverseAlphaGreenMask256 = Vector256.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); Vector256 multsrb = MkCst32(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector256 multsb2 = MkCst32(Cst5b(m.RedToBlue), 0); nint idx; @@ -495,9 +474,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); - Vector256 a = Avx2.And(input.AsByte(), TransformColorInverseAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), TransformColorInverseShuffleMask); - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), TransformColorInverseShuffleMask); + Vector256 a = Avx2.And(input.AsByte(), transformColorInverseAlphaGreenMask256); + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.Add(input.AsByte(), d.AsByte()); Vector256 f = Avx2.ShiftLeftLogical(e.AsInt16(), 8); @@ -516,6 +495,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless } else if (Sse2.IsSupported) { + Vector128 transformColorInverseAlphaGreenMask = Vector128.Create(0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255, 0, 255); Vector128 multsrb = MkCst16(Cst5b(m.GreenToRed), Cst5b(m.GreenToBlue)); Vector128 multsb2 = MkCst16(Cst5b(m.RedToBlue), 0); @@ -524,9 +504,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless { ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); - Vector128 a = Sse2.And(input.AsByte(), TransformColorInverseAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), TransformColorInverseShuffleMask); - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), TransformColorInverseShuffleMask); + Vector128 a = Sse2.And(input.AsByte(), transformColorInverseAlphaGreenMask); + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.Add(input.AsByte(), d.AsByte()); Vector128 f = Sse2.ShiftLeftLogical(e.AsInt16(), 8); diff --git a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs index 8938ede0a1..2e00bfb7e1 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/LossyUtils.cs @@ -15,29 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy { internal static class LossyUtils { -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector128 Mean16x4Mask = Vector128.Create((short)0x00ff).AsByte(); - - private static readonly Vector128 SignBit = Vector128.Create((byte)0x80); - - private static readonly Vector128 Three = Vector128.Create((byte)3).AsSByte(); - - private static readonly Vector128 FourShort = Vector128.Create((short)4); - - private static readonly Vector128 FourSByte = Vector128.Create((byte)4).AsSByte(); - - private static readonly Vector128 Nine = Vector128.Create((short)0x0900).AsSByte(); - - private static readonly Vector128 SixtyThree = Vector128.Create((short)63).AsSByte(); - - private static readonly Vector128 SixtyFour = Vector128.Create((byte)64).AsSByte(); - - private static readonly Vector128 K1 = Vector128.Create((short)20091); - - private static readonly Vector128 K2 = Vector128.Create((short)-30068); - -#endif - // Note: method name in libwebp reference implementation is called VP8SSE16x16. [MethodImpl(InliningOptions.ShortMethod)] public static int Vp8_Sse16X16(Span a, Span b) @@ -1025,16 +1002,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 a = Sse2.Add(in0.AsInt16(), in2.AsInt16()); Vector128 b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); + Vector128 k1 = Vector128.Create((short)20091); + Vector128 k2 = Vector128.Create((short)-30068); + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 - Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), K2); - Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), K1); + Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2); + Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), k1); Vector128 c3 = Sse2.Subtract(in1.AsInt16(), in3.AsInt16()); Vector128 c4 = Sse2.Subtract(c1, c2); Vector128 c = Sse2.Add(c3.AsInt16(), c4); // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 - Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), K1); - Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), K2); + Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), k1); + Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), k2); Vector128 d3 = Sse2.Add(in1.AsInt16(), in3.AsInt16()); Vector128 d4 = Sse2.Add(d1, d2); Vector128 d = Sse2.Add(d3, d4); @@ -1050,20 +1030,20 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Horizontal pass and subsequent transpose. // First pass, c and d calculations are longer because of the "trick" multiplications. - Vector128 dc = Sse2.Add(t0.AsInt16(), FourShort); + Vector128 dc = Sse2.Add(t0.AsInt16(), Vector128.Create((short)4)); a = Sse2.Add(dc, t2.AsInt16()); b = Sse2.Subtract(dc, t2.AsInt16()); // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 - c1 = Sse2.MultiplyHigh(t1.AsInt16(), K2); - c2 = Sse2.MultiplyHigh(t3.AsInt16(), K1); + c1 = Sse2.MultiplyHigh(t1.AsInt16(), k2); + c2 = Sse2.MultiplyHigh(t3.AsInt16(), k1); c3 = Sse2.Subtract(t1.AsInt16(), t3.AsInt16()); c4 = Sse2.Subtract(c1, c2); c = Sse2.Add(c3, c4); // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 - d1 = Sse2.MultiplyHigh(t1.AsInt16(), K1); - d2 = Sse2.MultiplyHigh(t3.AsInt16(), K2); + d1 = Sse2.MultiplyHigh(t1.AsInt16(), k1); + d2 = Sse2.MultiplyHigh(t3.AsInt16(), k2); d3 = Sse2.Add(t1.AsInt16(), t3.AsInt16()); d4 = Sse2.Add(d1, d2); d = Sse2.Add(d3, d4); @@ -1146,16 +1126,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 a = Sse2.Add(in0.AsInt16(), in2.AsInt16()); Vector128 b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); + Vector128 k1 = Vector128.Create((short)20091); + Vector128 k2 = Vector128.Create((short)-30068); + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 - Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), K2); - Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), K1); + Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2); + Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), k1); Vector128 c3 = Sse2.Subtract(in1.AsInt16(), in3.AsInt16()); Vector128 c4 = Sse2.Subtract(c1, c2); Vector128 c = Sse2.Add(c3.AsInt16(), c4); // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 - Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), K1); - Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), K2); + Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), k1); + Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), k2); Vector128 d3 = Sse2.Add(in1.AsInt16(), in3.AsInt16()); Vector128 d4 = Sse2.Add(d1, d2); Vector128 d = Sse2.Add(d3, d4); @@ -1171,20 +1154,20 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Horizontal pass and subsequent transpose. // First pass, c and d calculations are longer because of the "trick" multiplications. - Vector128 dc = Sse2.Add(t0.AsInt16(), FourShort); + Vector128 dc = Sse2.Add(t0.AsInt16(), Vector128.Create((short)4)); a = Sse2.Add(dc, t2.AsInt16()); b = Sse2.Subtract(dc, t2.AsInt16()); // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 - c1 = Sse2.MultiplyHigh(t1.AsInt16(), K2); - c2 = Sse2.MultiplyHigh(t3.AsInt16(), K1); + c1 = Sse2.MultiplyHigh(t1.AsInt16(), k2); + c2 = Sse2.MultiplyHigh(t3.AsInt16(), k1); c3 = Sse2.Subtract(t1.AsInt16(), t3.AsInt16()); c4 = Sse2.Subtract(c1, c2); c = Sse2.Add(c3, c4); // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 - d1 = Sse2.MultiplyHigh(t1.AsInt16(), K1); - d2 = Sse2.MultiplyHigh(t3.AsInt16(), K2); + d1 = Sse2.MultiplyHigh(t1.AsInt16(), k1); + d2 = Sse2.MultiplyHigh(t3.AsInt16(), k2); d3 = Sse2.Add(t1.AsInt16(), t3.AsInt16()); d4 = Sse2.Add(d1, d2); d = Sse2.Add(d3, d4); @@ -1810,6 +1793,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy #if SUPPORTS_RUNTIME_INTRINSICS if (Ssse3.IsSupported) { + Vector128 mean16x4Mask = Vector128.Create((short)0x00ff).AsByte(); + Vector128 a0 = Unsafe.As>(ref MemoryMarshal.GetReference(input)); Vector128 a1 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps, 16))); Vector128 a2 = Unsafe.As>(ref MemoryMarshal.GetReference(input.Slice(WebpConstants.Bps * 2, 16))); @@ -1818,10 +1803,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 b1 = Sse2.ShiftRightLogical(a1.AsInt16(), 8); Vector128 b2 = Sse2.ShiftRightLogical(a2.AsInt16(), 8); Vector128 b3 = Sse2.ShiftRightLogical(a3.AsInt16(), 8); - Vector128 c0 = Sse2.And(a0, Mean16x4Mask); // lo byte - Vector128 c1 = Sse2.And(a1, Mean16x4Mask); - Vector128 c2 = Sse2.And(a2, Mean16x4Mask); - Vector128 c3 = Sse2.And(a3, Mean16x4Mask); + Vector128 c0 = Sse2.And(a0, mean16x4Mask); // lo byte + Vector128 c1 = Sse2.And(a1, mean16x4Mask); + Vector128 c2 = Sse2.And(a2, mean16x4Mask); + Vector128 c3 = Sse2.And(a3, mean16x4Mask); Vector128 d0 = Sse2.Add(b0.AsInt32(), c0.AsInt32()); Vector128 d1 = Sse2.Add(b1.AsInt32(), c1.AsInt32()); Vector128 d2 = Sse2.Add(b2.AsInt32(), c2.AsInt32()); @@ -1978,14 +1963,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Applies filter on 2 pixels (p0 and q0) private static void DoFilter2Sse2(ref Vector128 p1, ref Vector128 p0, ref Vector128 q0, ref Vector128 q1, int thresh) { + Vector128 signBit = Vector128.Create((byte)0x80); + // Convert p1/q1 to byte (for GetBaseDelta). - Vector128 p1s = Sse2.Xor(p1, SignBit); - Vector128 q1s = Sse2.Xor(q1, SignBit); + Vector128 p1s = Sse2.Xor(p1, signBit); + Vector128 q1s = Sse2.Xor(q1, signBit); Vector128 mask = NeedsFilter(p1, p0, q0, q1, thresh); // Flip sign. - p0 = Sse2.Xor(p0, SignBit); - q0 = Sse2.Xor(q0, SignBit); + p0 = Sse2.Xor(p0, signBit); + q0 = Sse2.Xor(q0, signBit); Vector128 a = GetBaseDelta(p1s.AsSByte(), p0.AsSByte(), q0.AsSByte(), q1s.AsSByte()).AsByte(); @@ -1995,8 +1982,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy DoSimpleFilterSse2(ref p0, ref q0, a); // Flip sign. - p0 = Sse2.Xor(p0, SignBit); - q0 = Sse2.Xor(q0, SignBit); + p0 = Sse2.Xor(p0, signBit); + q0 = Sse2.Xor(q0, signBit); } // Applies filter on 4 pixels (p1, p0, q0 and q1) @@ -2005,11 +1992,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Compute hev mask. Vector128 notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh); + Vector128 signBit = Vector128.Create((byte)0x80); + // Convert to signed values. - p1 = Sse2.Xor(p1, SignBit); - p0 = Sse2.Xor(p0, SignBit); - q0 = Sse2.Xor(q0, SignBit); - q1 = Sse2.Xor(q1, SignBit); + p1 = Sse2.Xor(p1, signBit); + p0 = Sse2.Xor(p0, signBit); + q0 = Sse2.Xor(q0, signBit); + q1 = Sse2.Xor(q1, signBit); Vector128 t1 = Sse2.SubtractSaturate(p1.AsSByte(), q1.AsSByte()); // p1 - q1 t1 = Sse2.AndNot(notHev, t1.AsByte()).AsSByte(); // hev(p1 - q1) @@ -2019,25 +2008,25 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy t1 = Sse2.AddSaturate(t1, t2); // hev(p1 - q1) + 3 * (q0 - p0) t1 = Sse2.And(t1.AsByte(), mask).AsSByte(); // mask filter values we don't care about. - t2 = Sse2.AddSaturate(t1, Three); // 3 * (q0 - p0) + hev(p1 - q1) + 3 - Vector128 t3 = Sse2.AddSaturate(t1, FourSByte); // 3 * (q0 - p0) + hev(p1 - q1) + 4 + t2 = Sse2.AddSaturate(t1, Vector128.Create((byte)3).AsSByte()); // 3 * (q0 - p0) + hev(p1 - q1) + 3 + Vector128 t3 = Sse2.AddSaturate(t1, Vector128.Create((byte)4).AsSByte()); // 3 * (q0 - p0) + hev(p1 - q1) + 4 t2 = SignedShift8b(t2.AsByte()); // (3 * (q0 - p0) + hev(p1 - q1) + 3) >> 3 t3 = SignedShift8b(t3.AsByte()); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 3 p0 = Sse2.AddSaturate(p0.AsSByte(), t2).AsByte(); // p0 += t2 q0 = Sse2.SubtractSaturate(q0.AsSByte(), t3).AsByte(); // q0 -= t3 - p0 = Sse2.Xor(p0, SignBit); - q0 = Sse2.Xor(q0, SignBit); + p0 = Sse2.Xor(p0, signBit); + q0 = Sse2.Xor(q0, signBit); // This is equivalent to signed (a + 1) >> 1 calculation. - t2 = Sse2.Add(t3, SignBit.AsSByte()); + t2 = Sse2.Add(t3, signBit.AsSByte()); t3 = Sse2.Average(t2.AsByte(), Vector128.Zero).AsSByte(); - t3 = Sse2.Subtract(t3, SixtyFour); + t3 = Sse2.Subtract(t3, Vector128.Create((sbyte)64)); t3 = Sse2.And(notHev, t3.AsByte()).AsSByte(); // if !hev q1 = Sse2.SubtractSaturate(q1.AsSByte(), t3).AsByte(); // q1 -= t3 p1 = Sse2.AddSaturate(p1.AsSByte(), t3).AsByte(); // p1 += t3 - p1 = Sse2.Xor(p1.AsByte(), SignBit); - q1 = Sse2.Xor(q1.AsByte(), SignBit); + p1 = Sse2.Xor(p1.AsByte(), signBit); + q1 = Sse2.Xor(q1.AsByte(), signBit); } // Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2) @@ -2047,12 +2036,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 notHev = GetNotHev(ref p1, ref p0, ref q0, ref q1, tresh); // Convert to signed values. - p1 = Sse2.Xor(p1, SignBit); - p0 = Sse2.Xor(p0, SignBit); - q0 = Sse2.Xor(q0, SignBit); - q1 = Sse2.Xor(q1, SignBit); - p2 = Sse2.Xor(p2, SignBit); - q2 = Sse2.Xor(q2, SignBit); + Vector128 signBit = Vector128.Create((byte)0x80); + p1 = Sse2.Xor(p1, signBit); + p0 = Sse2.Xor(p0, signBit); + q0 = Sse2.Xor(q0, signBit); + q1 = Sse2.Xor(q1, signBit); + p2 = Sse2.Xor(p2, signBit); + q2 = Sse2.Xor(q2, signBit); Vector128 a = GetBaseDelta(p1.AsSByte(), p0.AsSByte(), q0.AsSByte(), q1.AsSByte()); @@ -2067,11 +2057,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 flow = Sse2.UnpackLow(Vector128.Zero, f); Vector128 fhigh = Sse2.UnpackHigh(Vector128.Zero, f); - Vector128 f9Low = Sse2.MultiplyHigh(flow.AsInt16(), Nine.AsInt16()); // Filter (lo) * 9 - Vector128 f9High = Sse2.MultiplyHigh(fhigh.AsInt16(), Nine.AsInt16()); // Filter (hi) * 9 + Vector128 nine = Vector128.Create((short)0x0900); + Vector128 f9Low = Sse2.MultiplyHigh(flow.AsInt16(), nine); // Filter (lo) * 9 + Vector128 f9High = Sse2.MultiplyHigh(fhigh.AsInt16(), nine); // Filter (hi) * 9 - Vector128 a2Low = Sse2.Add(f9Low, SixtyThree.AsInt16()); // Filter * 9 + 63 - Vector128 a2High = Sse2.Add(f9High, SixtyThree.AsInt16()); // Filter * 9 + 63 + Vector128 sixtyThree = Vector128.Create((short)63); + Vector128 a2Low = Sse2.Add(f9Low, sixtyThree); // Filter * 9 + 63 + Vector128 a2High = Sse2.Add(f9High, sixtyThree); // Filter * 9 + 63 Vector128 a1Low = Sse2.Add(a2Low, f9Low); // Filter * 18 + 63 Vector128 a1High = Sse2.Add(a2High, f9High); // // Filter * 18 + 63 @@ -2086,8 +2078,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private static void DoSimpleFilterSse2(ref Vector128 p0, ref Vector128 q0, Vector128 fl) { - Vector128 v3 = Sse2.AddSaturate(fl.AsSByte(), Three); - Vector128 v4 = Sse2.AddSaturate(fl.AsSByte(), FourSByte); + Vector128 v3 = Sse2.AddSaturate(fl.AsSByte(), Vector128.Create((byte)3).AsSByte()); + Vector128 v4 = Sse2.AddSaturate(fl.AsSByte(), Vector128.Create((byte)4).AsSByte()); v4 = SignedShift8b(v4.AsByte()).AsSByte(); // v4 >> 3 v3 = SignedShift8b(v3.AsByte()).AsSByte(); // v3 >> 3 @@ -2353,13 +2345,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // Pixels 'pi' and 'qi' are int8_t on input, uint8_t on output (sign flip). private static void Update2Pixels(ref Vector128 pi, ref Vector128 qi, Vector128 a0Low, Vector128 a0High) { + Vector128 signBit = Vector128.Create((byte)0x80); Vector128 a1Low = Sse2.ShiftRightArithmetic(a0Low, 7); Vector128 a1High = Sse2.ShiftRightArithmetic(a0High, 7); Vector128 delta = Sse2.PackSignedSaturate(a1Low, a1High); pi = Sse2.AddSaturate(pi.AsSByte(), delta).AsByte(); qi = Sse2.SubtractSaturate(qi.AsSByte(), delta).AsByte(); - pi = Sse2.Xor(pi, SignBit.AsByte()); - qi = Sse2.Xor(qi, SignBit.AsByte()); + pi = Sse2.Xor(pi, signBit.AsByte()); + qi = Sse2.Xor(qi, signBit.AsByte()); } [MethodImpl(InliningOptions.ShortMethod)] diff --git a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs index b6703d7390..e1c1ee557e 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs @@ -20,24 +20,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private const int MaxLevel = 2047; -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector128 MaxCoeff2047 = Vector128.Create((short)MaxLevel); - - private static readonly Vector256 MaxCoeff2047Vec256 = Vector256.Create((short)MaxLevel); - - private static readonly Vector256 Cst256 = Vector256.Create(0, 1, 2, 3, 8, 9, 254, 255, 10, 11, 4, 5, 6, 7, 12, 13, 2, 3, 8, 9, 10, 11, 4, 5, 254, 255, 6, 7, 12, 13, 14, 15); - - private static readonly Vector256 Cst78 = Vector256.Create(254, 255, 254, 255, 254, 255, 254, 255, 14, 15, 254, 255, 254, 255, 254, 255, 254, 255, 254, 255, 254, 255, 0, 1, 254, 255, 254, 255, 254, 255, 254, 255); - - private static readonly Vector128 CstLo = Vector128.Create(0, 1, 2, 3, 8, 9, 254, 255, 10, 11, 4, 5, 6, 7, 12, 13); - - private static readonly Vector128 Cst7 = Vector128.Create(254, 255, 254, 255, 254, 255, 254, 255, 14, 15, 254, 255, 254, 255, 254, 255); - - private static readonly Vector128 CstHi = Vector128.Create(2, 3, 8, 9, 10, 11, 4, 5, 254, 255, 6, 7, 12, 13, 14, 15); - - private static readonly Vector128 Cst8 = Vector128.Create(254, 255, 254, 255, 254, 255, 0, 1, 254, 255, 254, 255, 254, 255, 254, 255); -#endif - // Diffusion weights. We under-correct a bit (15/16th of the error is actually // diffused) to avoid 'rainbow' chessboard pattern of blocks at q~=0. private const int C1 = 7; // fraction of error sent to the 4x4 block below @@ -574,8 +556,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector256 out0 = Avx2.PackSignedSaturate(out00.AsInt32(), out08.AsInt32()); // if (coeff > 2047) coeff = 2047 - out0 = Avx2.Min(out0, MaxCoeff2047Vec256); - + out0 = Avx2.Min(out0, Vector256.Create((short)MaxLevel)); // Put the sign back. out0 = Avx2.Sign(out0, input0); @@ -585,8 +566,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Unsafe.As>(ref inputRef) = input0; // zigzag the output before storing it. - Vector256 tmp256 = Avx2.Shuffle(out0.AsByte(), Cst256); - Vector256 tmp78 = Avx2.Shuffle(out0.AsByte(), Cst78); + Vector256 tmp256 = Avx2.Shuffle(out0.AsByte(), Vector256.Create(0, 1, 2, 3, 8, 9, 254, 255, 10, 11, 4, 5, 6, 7, 12, 13, 2, 3, 8, 9, 10, 11, 4, 5, 254, 255, 6, 7, 12, 13, 14, 15)); // Cst256 + Vector256 tmp78 = Avx2.Shuffle(out0.AsByte(), Vector256.Create(254, 255, 254, 255, 254, 255, 254, 255, 14, 15, 254, 255, 254, 255, 254, 255, 254, 255, 254, 255, 254, 255, 0, 1, 254, 255, 254, 255, 254, 255, 254, 255)); // Cst78 // Reverse the order of the 16-byte lanes. Vector256 tmp87 = Avx2.Permute2x128(tmp78, tmp78, 1); @@ -654,8 +635,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 out8 = Sse2.PackSignedSaturate(out08.AsInt32(), out12.AsInt32()); // if (coeff > 2047) coeff = 2047 - out0 = Sse2.Min(out0, MaxCoeff2047); - out8 = Sse2.Min(out8, MaxCoeff2047); + Vector128 maxCoeff2047 = Vector128.Create((short)MaxLevel); + out0 = Sse2.Min(out0, maxCoeff2047); + out8 = Sse2.Min(out8, maxCoeff2047); // Put the sign back. out0 = Ssse3.Sign(out0, input0); @@ -676,10 +658,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // There's only two misplaced entries ([8] and [7]) that are crossing the // reg's boundaries. // We use pshufb instead of pshuflo/pshufhi. - Vector128 tmpLo = Ssse3.Shuffle(out0.AsByte(), CstLo); - Vector128 tmp7 = Ssse3.Shuffle(out0.AsByte(), Cst7); // extract #7 - Vector128 tmpHi = Ssse3.Shuffle(out8.AsByte(), CstHi); - Vector128 tmp8 = Ssse3.Shuffle(out8.AsByte(), Cst8); // extract #8 + Vector128 tmpLo = Ssse3.Shuffle(out0.AsByte(), Vector128.Create(0, 1, 2, 3, 8, 9, 254, 255, 10, 11, 4, 5, 6, 7, 12, 13)); + Vector128 tmp7 = Ssse3.Shuffle(out0.AsByte(), Vector128.Create(254, 255, 254, 255, 254, 255, 254, 255, 14, 15, 254, 255, 254, 255, 254, 255)); // extract #7 + Vector128 tmpHi = Ssse3.Shuffle(out8.AsByte(), Vector128.Create(2, 3, 8, 9, 10, 11, 4, 5, 254, 255, 6, 7, 12, 13, 14, 15)); + Vector128 tmp8 = Ssse3.Shuffle(out8.AsByte(), Vector128.Create(254, 255, 254, 255, 254, 255, 0, 1, 254, 255, 254, 255, 254, 255, 254, 255)); // extract #8 Vector128 outZ0 = Sse2.Or(tmpLo, tmp8); Vector128 outZ8 = Sse2.Or(tmpHi, tmp7); diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs index 65b1d07d3d..eb2ac3bcbd 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private const int KC2 = 35468; - private static readonly byte[] Clip1 = new byte[255 + 510 + 1]; // clips [-255,510] to [0,255] + private static readonly byte[] Clip1 = GetClip1(); // clips [-255,510] to [0,255] private const int I16DC16 = 0 * 16 * WebpConstants.Bps; @@ -65,48 +65,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy public static readonly int[] Vp8I4ModeOffsets = { I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4 }; -#if SUPPORTS_RUNTIME_INTRINSICS -#pragma warning disable SA1310 // Field names should not contain underscore - private static readonly Vector128 K1 = Vector128.Create((short)20091).AsInt16(); - - private static readonly Vector128 K2 = Vector128.Create((short)-30068).AsInt16(); - - private static readonly Vector128 Four = Vector128.Create((short)4); - - private static readonly Vector128 Seven = Vector128.Create((short)7); - - private static readonly Vector128 K88p = Vector128.Create(8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0).AsInt16(); - - private static readonly Vector128 K88m = Vector128.Create(8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255).AsInt16(); - - private static readonly Vector128 K5352_2217p = Vector128.Create(232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8).AsInt16(); - - private static readonly Vector128 K5352_2217m = Vector128.Create(169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235).AsInt16(); - - private static readonly Vector128 K937 = Vector128.Create(937); - - private static readonly Vector128 K1812 = Vector128.Create(1812); - - private static readonly Vector128 K5352_2217 = Vector128.Create(169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20).AsInt16(); - - private static readonly Vector128 K2217_5352 = Vector128.Create(24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8).AsInt16(); - - private static readonly Vector128 K12000PlusOne = Vector128.Create(12000 + (1 << 16)); - - private static readonly Vector128 K51000 = Vector128.Create(51000); - - private static readonly byte MmShuffle2301 = SimdUtils.Shuffle.MmShuffle(2, 3, 0, 1); - - private static readonly byte MmShuffle1032 = SimdUtils.Shuffle.MmShuffle(1, 0, 3, 2); -#pragma warning restore SA1310 // Field names should not contain underscore -#endif - - static Vp8Encoding() + private static byte[] GetClip1() { + byte[] clip1 = new byte[255 + 510 + 1]; + for (int i = -255; i <= 255 + 255; i++) { - Clip1[255 + i] = Clip8b(i); + clip1[255 + i] = Clip8b(i); } + + return clip1; } // Transforms (Paragraph 14.4) @@ -347,16 +315,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 a = Sse2.Add(in0.AsInt16(), in2.AsInt16()); Vector128 b = Sse2.Subtract(in0.AsInt16(), in2.AsInt16()); + Vector128 k1 = Vector128.Create((short)20091).AsInt16(); + Vector128 k2 = Vector128.Create((short)-30068).AsInt16(); + // c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3 - Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), K2); - Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), K1); + Vector128 c1 = Sse2.MultiplyHigh(in1.AsInt16(), k2); + Vector128 c2 = Sse2.MultiplyHigh(in3.AsInt16(), k1); Vector128 c3 = Sse2.Subtract(in1.AsInt16(), in3.AsInt16()); Vector128 c4 = Sse2.Subtract(c1, c2); Vector128 c = Sse2.Add(c3, c4); // d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3 - Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), K1); - Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), K2); + Vector128 d1 = Sse2.MultiplyHigh(in1.AsInt16(), k1); + Vector128 d2 = Sse2.MultiplyHigh(in3.AsInt16(), k2); Vector128 d3 = Sse2.Add(in1.AsInt16(), in3.AsInt16()); Vector128 d4 = Sse2.Add(d1, d2); Vector128 d = Sse2.Add(d3, d4); @@ -370,20 +341,23 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private static void InverseTransformHorizontalPass(Vector128 t0, Vector128 t2, Vector128 t1, Vector128 t3, out Vector128 shifted0, out Vector128 shifted1, out Vector128 shifted2, out Vector128 shifted3) { - Vector128 dc = Sse2.Add(t0.AsInt16(), Four); + Vector128 dc = Sse2.Add(t0.AsInt16(), Vector128.Create((short)4)); Vector128 a = Sse2.Add(dc, t2.AsInt16()); Vector128 b = Sse2.Subtract(dc, t2.AsInt16()); + Vector128 k1 = Vector128.Create((short)20091).AsInt16(); + Vector128 k2 = Vector128.Create((short)-30068).AsInt16(); + // c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3 - Vector128 c1 = Sse2.MultiplyHigh(t1.AsInt16(), K2); - Vector128 c2 = Sse2.MultiplyHigh(t3.AsInt16(), K1); + Vector128 c1 = Sse2.MultiplyHigh(t1.AsInt16(), k2); + Vector128 c2 = Sse2.MultiplyHigh(t3.AsInt16(), k1); Vector128 c3 = Sse2.Subtract(t1.AsInt16(), t3.AsInt16()); Vector128 c4 = Sse2.Subtract(c1, c2); Vector128 c = Sse2.Add(c3, c4); // d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3 - Vector128 d1 = Sse2.MultiplyHigh(t1.AsInt16(), K1); - Vector128 d2 = Sse2.MultiplyHigh(t3.AsInt16(), K2); + Vector128 d1 = Sse2.MultiplyHigh(t1.AsInt16(), k1); + Vector128 d2 = Sse2.MultiplyHigh(t3.AsInt16(), k2); Vector128 d3 = Sse2.Add(t1.AsInt16(), t3.AsInt16()); Vector128 d4 = Sse2.Add(d1, d2); Vector128 d = Sse2.Add(d3, d4); @@ -561,8 +535,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy { // *in01 = 00 01 10 11 02 03 12 13 // *in23 = 20 21 30 31 22 23 32 33 - Vector128 shuf01_p = Sse2.ShuffleHigh(row01, MmShuffle2301); - Vector128 shuf32_p = Sse2.ShuffleHigh(row23, MmShuffle2301); + Vector128 shuf01_p = Sse2.ShuffleHigh(row01, 0xB1); // MmShuffle(2, 3, 0, 1) + Vector128 shuf32_p = Sse2.ShuffleHigh(row23, 0xB1); // MmShuffle(2, 3, 0, 1) // 00 01 10 11 03 02 13 12 // 20 21 30 31 23 22 33 32 @@ -576,12 +550,15 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // [d0 + d3 | d1 + d2 | ...] = [a0 a1 | a0' a1' | ... ] // [d0 - d3 | d1 - d2 | ...] = [a3 a2 | a3' a2' | ... ] - Vector128 tmp0 = Sse2.MultiplyAddAdjacent(a01, K88p); // [ (a0 + a1) << 3, ... ] - Vector128 tmp2 = Sse2.MultiplyAddAdjacent(a01, K88m); // [ (a0 - a1) << 3, ... ] - Vector128 tmp11 = Sse2.MultiplyAddAdjacent(a32, K5352_2217p); - Vector128 tmp31 = Sse2.MultiplyAddAdjacent(a32, K5352_2217m); - Vector128 tmp12 = Sse2.Add(tmp11, K1812); - Vector128 tmp32 = Sse2.Add(tmp31, K937); + + // [ (a0 + a1) << 3, ... ] + Vector128 tmp0 = Sse2.MultiplyAddAdjacent(a01, Vector128.Create(8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0).AsInt16()); // K88p + // [ (a0 - a1) << 3, ... ] + Vector128 tmp2 = Sse2.MultiplyAddAdjacent(a01, Vector128.Create(8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255).AsInt16()); // K88m + Vector128 tmp11 = Sse2.MultiplyAddAdjacent(a32, Vector128.Create(232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8).AsInt16()); // K5352_2217p + Vector128 tmp31 = Sse2.MultiplyAddAdjacent(a32, Vector128.Create(169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235).AsInt16()); // K5352_2217m + Vector128 tmp12 = Sse2.Add(tmp11, Vector128.Create(1812)); + Vector128 tmp32 = Sse2.Add(tmp31, Vector128.Create(937)); Vector128 tmp1 = Sse2.ShiftRightArithmetic(tmp12, 9); Vector128 tmp3 = Sse2.ShiftRightArithmetic(tmp32, 9); Vector128 s03 = Sse2.PackSignedSaturate(tmp0, tmp2); @@ -590,7 +567,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 shi = Sse2.UnpackHigh(s03, s12); // 2 3 2 3 2 3 Vector128 v23 = Sse2.UnpackHigh(slo.AsInt32(), shi.AsInt32()); out01 = Sse2.UnpackLow(slo.AsInt32(), shi.AsInt32()); - out32 = Sse2.Shuffle(v23, MmShuffle1032); + out32 = Sse2.Shuffle(v23, 0x4E); // MmShuffle(1, 0, 3, 2) } public static void FTransformPass2SSE2(Vector128 v01, Vector128 v32, Span output) @@ -602,10 +579,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 a22 = Sse2.UnpackHigh(a32.AsInt64(), a32.AsInt64()); Vector128 b23 = Sse2.UnpackLow(a22.AsInt16(), a32.AsInt16()); - Vector128 c1 = Sse2.MultiplyAddAdjacent(b23, K5352_2217); - Vector128 c3 = Sse2.MultiplyAddAdjacent(b23, K2217_5352); - Vector128 d1 = Sse2.Add(c1, K12000PlusOne); - Vector128 d3 = Sse2.Add(c3, K51000); + Vector128 c1 = Sse2.MultiplyAddAdjacent(b23, Vector128.Create(169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20).AsInt16()); // K5352_2217 + Vector128 c3 = Sse2.MultiplyAddAdjacent(b23, Vector128.Create(24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8, 24, 235, 169, 8).AsInt16()); // K2217_5352 + Vector128 d1 = Sse2.Add(c1, Vector128.Create(12000 + (1 << 16))); // K12000PlusOne + Vector128 d3 = Sse2.Add(c3, Vector128.Create(51000)); Vector128 e1 = Sse2.ShiftRightArithmetic(d1, 16); Vector128 e3 = Sse2.ShiftRightArithmetic(d3, 16); @@ -623,7 +600,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // a0 = v0 + v3 // a1 = v1 + v2 Vector128 a01 = Sse2.Add(v01.AsInt16(), v32.AsInt16()); - Vector128 a01Plus7 = Sse2.Add(a01.AsInt16(), Seven); + Vector128 a01Plus7 = Sse2.Add(a01.AsInt16(), Vector128.Create((short)7)); Vector128 a11 = Sse2.UnpackHigh(a01.AsInt64(), a01.AsInt64()).AsInt16(); Vector128 c0 = Sse2.Add(a01Plus7, a11); Vector128 c2 = Sse2.Subtract(a01Plus7, a11); diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs index f679fcb136..f0aeff642f 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Histogram.cs @@ -24,10 +24,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// private const int MaxCoeffThresh = 31; -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector256 MaxCoeffThreshVec = Vector256.Create((short)MaxCoeffThresh); -#endif - private int maxValue; private int lastNonZero; @@ -73,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector256 v0 = Avx2.ShiftRightArithmetic(abs0.AsInt16(), 3); // bin = min(v, MAX_COEFF_THRESH) - Vector256 min0 = Avx2.Min(v0, MaxCoeffThreshVec); + Vector256 min0 = Avx2.Min(v0, Vector256.Create((short)MaxCoeffThresh)); // Store. Unsafe.As>(ref outputRef) = min0; diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs index 5e45918943..5b4e2c746a 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Residual.cs @@ -16,12 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy /// internal class Vp8Residual { -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector256 Cst2 = Vector256.Create((byte)2); - - private static readonly Vector256 Cst67 = Vector256.Create((byte)67); -#endif - private readonly byte[] scratch = new byte[32]; private readonly ushort[] scratchUShort = new ushort[16]; @@ -182,8 +176,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector256 d0 = Avx2.Subtract(Vector256.Zero, c0); Vector256 e0 = Avx2.Max(c0, d0); // abs(v), 16b Vector256 f = Avx2.PackSignedSaturate(e0, e0); - Vector256 g = Avx2.Min(f.AsByte(), Cst2); - Vector256 h = Avx2.Min(f.AsByte(), Cst67); // clampLevel in [0..67] + Vector256 g = Avx2.Min(f.AsByte(), Vector256.Create((byte)2)); + Vector256 h = Avx2.Min(f.AsByte(), Vector256.Create((byte)67)); // clampLevel in [0..67] ref byte ctxsRef = ref MemoryMarshal.GetReference(ctxs); ref byte levelsRef = ref MemoryMarshal.GetReference(levels); diff --git a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs index d0374d637d..d7c1904480 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/YuvConversion.cs @@ -23,49 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy private const int YuvHalf = 1 << (YuvFix - 1); -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector128 One = Vector128.Create((byte)1); - - // These constants are 14b fixed-point version of ITU-R BT.601 constants. - // R = (19077 * y + 26149 * v - 14234) >> 6 - // G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 - // B = (19077 * y + 33050 * u - 17685) >> 6 - private static readonly Vector128 K19077 = Vector128.Create((short)19077).AsByte(); - - private static readonly Vector128 K26149 = Vector128.Create((short)26149).AsByte(); - - private static readonly Vector128 K14234 = Vector128.Create((short)14234).AsByte(); - - // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic - private static readonly Vector128 K33050 = Vector128.Create(26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129); - - private static readonly Vector128 K17685 = Vector128.Create((short)17685).AsByte(); - - private static readonly Vector128 K6419 = Vector128.Create((short)6419).AsByte(); - - private static readonly Vector128 K13320 = Vector128.Create((short)13320).AsByte(); - - private static readonly Vector128 K8708 = Vector128.Create((short)8708).AsByte(); - - private static readonly Vector128 PlanarTo24Shuffle0 = Vector128.Create(0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255, 255, 5); - - private static readonly Vector128 PlanarTo24Shuffle1 = Vector128.Create(255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255, 10, 255); - - private static readonly Vector128 PlanarTo24Shuffle2 = Vector128.Create(255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15, 255, 255); - - private static readonly Vector128 PlanarTo24Shuffle3 = Vector128.Create(255, 0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255, 255); - - private static readonly Vector128 PlanarTo24Shuffle4 = Vector128.Create(5, 255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255, 10); - - private static readonly Vector128 PlanarTo24Shuffle5 = Vector128.Create(255, 255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15, 255); - - private static readonly Vector128 PlanarTo24Shuffle6 = Vector128.Create(255, 255, 0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255); - - private static readonly Vector128 PlanarTo24Shuffle7 = Vector128.Create(255, 5, 255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255); - - private static readonly Vector128 PlanarTo24Shuffle8 = Vector128.Create(10, 255, 255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15); -#endif - // UpSample from YUV to RGB. // Given samples laid out in a square as: // [a b] @@ -250,7 +207,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 t1 = Sse2.Or(ad, bc); // (a^d) | (b^c) Vector128 t2 = Sse2.Or(t1, st); // (a^d) | (b^c) | (s^t) - Vector128 t3 = Sse2.And(t2, One); // (a^d) | (b^c) | (s^t) & 1 + Vector128 t3 = Sse2.And(t2, Vector128.Create((byte)1)); // (a^d) | (b^c) | (s^t) & 1 Vector128 t4 = Sse2.Average(s, t); Vector128 k = Sse2.Subtract(t4, t3); // k = (a + b + c + d) / 4 @@ -289,7 +246,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 tmp1 = Sse2.And(ij, st); // (ij) & (s^t) Vector128 tmp2 = Sse2.Xor(k, input); // (k^in) Vector128 tmp3 = Sse2.Or(tmp1, tmp2); // ((ij) & (s^t)) | (k^in) - Vector128 tmp4 = Sse2.And(tmp3, One); // & 1 -> lsb_correction + Vector128 tmp4 = Sse2.And(tmp3, Vector128.Create((byte)1)); // & 1 -> lsb_correction return Sse2.Subtract(tmp0, tmp4); // (k + in + 1) / 2 - lsb_correction } @@ -668,9 +625,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy ChannelMixing( input0, input1, - PlanarTo24Shuffle0, - PlanarTo24Shuffle1, - PlanarTo24Shuffle2, + Vector128.Create(0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255, 255, 5), // PlanarTo24Shuffle0 + Vector128.Create(255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255, 10, 255), // PlanarTo24Shuffle1 + Vector128.Create(255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15, 255, 255), // PlanarTo24Shuffle2 out Vector128 r0, out Vector128 r1, out Vector128 r2, @@ -683,9 +640,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy ChannelMixing( input2, input3, - PlanarTo24Shuffle3, - PlanarTo24Shuffle4, - PlanarTo24Shuffle5, + Vector128.Create(255, 0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255, 255), // PlanarTo24Shuffle3 + Vector128.Create(5, 255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255, 10), // PlanarTo24Shuffle4 + Vector128.Create(255, 255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15, 255), // PlanarTo24Shuffle5 out Vector128 g0, out Vector128 g1, out Vector128 g2, @@ -697,9 +654,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy ChannelMixing( input4, input5, - PlanarTo24Shuffle6, - PlanarTo24Shuffle7, - PlanarTo24Shuffle8, + Vector128.Create(255, 255, 0, 255, 255, 1, 255, 255, 2, 255, 255, 3, 255, 255, 4, 255), // PlanarTo24Shuffle6 + Vector128.Create(255, 5, 255, 255, 6, 255, 255, 7, 255, 255, 8, 255, 255, 9, 255, 255), // PlanarTo24Shuffle7 + Vector128.Create(10, 255, 255, 11, 255, 255, 12, 255, 255, 13, 255, 255, 14, 255, 255, 15), // PlanarTo24Shuffle8 out Vector128 b0, out Vector128 b1, out Vector128 b2, @@ -757,21 +714,29 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy u0 = Sse2.UnpackLow(Vector128.Zero, u0); v0 = Sse2.UnpackLow(Vector128.Zero, v0); - Vector128 y1 = Sse2.MultiplyHigh(y0.AsUInt16(), K19077.AsUInt16()); - Vector128 r0 = Sse2.MultiplyHigh(v0.AsUInt16(), K26149.AsUInt16()); - Vector128 g0 = Sse2.MultiplyHigh(u0.AsUInt16(), K6419.AsUInt16()); - Vector128 g1 = Sse2.MultiplyHigh(v0.AsUInt16(), K13320.AsUInt16()); + // These constants are 14b fixed-point version of ITU-R BT.601 constants. + // R = (19077 * y + 26149 * v - 14234) >> 6 + // G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 + // B = (19077 * y + 33050 * u - 17685) >> 6 + Vector128 k19077 = Vector128.Create((ushort)19077); + Vector128 k26149 = Vector128.Create((ushort)26149); + Vector128 k14234 = Vector128.Create((ushort)14234); + + Vector128 y1 = Sse2.MultiplyHigh(y0.AsUInt16(), k19077); + Vector128 r0 = Sse2.MultiplyHigh(v0.AsUInt16(), k26149); + Vector128 g0 = Sse2.MultiplyHigh(u0.AsUInt16(), Vector128.Create((ushort)6419)); + Vector128 g1 = Sse2.MultiplyHigh(v0.AsUInt16(), Vector128.Create((ushort)13320)); - Vector128 r1 = Sse2.Subtract(y1.AsUInt16(), K14234.AsUInt16()); + Vector128 r1 = Sse2.Subtract(y1.AsUInt16(), k14234); Vector128 r2 = Sse2.Add(r1, r0); - Vector128 g2 = Sse2.Add(y1.AsUInt16(), K8708.AsUInt16()); + Vector128 g2 = Sse2.Add(y1.AsUInt16(), Vector128.Create((ushort)8708)); Vector128 g3 = Sse2.Add(g0, g1); Vector128 g4 = Sse2.Subtract(g2, g3); - Vector128 b0 = Sse2.MultiplyHigh(u0.AsUInt16(), K33050.AsUInt16()); + Vector128 b0 = Sse2.MultiplyHigh(u0.AsUInt16(), Vector128.Create(26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129, 26, 129).AsUInt16()); Vector128 b1 = Sse2.AddSaturate(b0, y1); - Vector128 b2 = Sse2.SubtractSaturate(b1, K17685.AsUInt16()); + Vector128 b2 = Sse2.SubtractSaturate(b1, Vector128.Create((ushort)17685)); // Use logical shift for B2, which can be larger than 32767. r = Sse2.ShiftRightArithmetic(r2.AsInt16(), 6); // range: [-14234, 30815] diff --git a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs index 4251af7428..3350497bb3 100644 --- a/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs +++ b/src/ImageSharp/Formats/Webp/WebpCommonUtils.cs @@ -16,16 +16,6 @@ namespace SixLabors.ImageSharp.Formats.Webp /// internal static class WebpCommonUtils { -#if SUPPORTS_RUNTIME_INTRINSICS - private static readonly Vector256 AlphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); - - private static readonly Vector256 All0x80Vector256 = Vector256.Create((byte)0x80).AsByte(); - - private static readonly Vector128 AlphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); - - private static readonly Vector128 All0x80 = Vector128.Create((byte)0x80).AsByte(); -#endif - /// /// Checks if the pixel row is not opaque. /// @@ -41,20 +31,23 @@ namespace SixLabors.ImageSharp.Formats.Webp int length = (row.Length * 4) - 3; fixed (byte* src = rowBytes) { + Vector256 alphaMaskVector256 = Vector256.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); + Vector256 all0x80Vector256 = Vector256.Create((byte)0x80).AsByte(); + for (; i + 128 <= length; i += 128) { Vector256 a0 = Avx.LoadVector256(src + i).AsByte(); Vector256 a1 = Avx.LoadVector256(src + i + 32).AsByte(); Vector256 a2 = Avx.LoadVector256(src + i + 64).AsByte(); Vector256 a3 = Avx.LoadVector256(src + i + 96).AsByte(); - Vector256 b0 = Avx2.And(a0, AlphaMaskVector256).AsInt32(); - Vector256 b1 = Avx2.And(a1, AlphaMaskVector256).AsInt32(); - Vector256 b2 = Avx2.And(a2, AlphaMaskVector256).AsInt32(); - Vector256 b3 = Avx2.And(a3, AlphaMaskVector256).AsInt32(); + Vector256 b0 = Avx2.And(a0, alphaMaskVector256).AsInt32(); + Vector256 b1 = Avx2.And(a1, alphaMaskVector256).AsInt32(); + Vector256 b2 = Avx2.And(a2, alphaMaskVector256).AsInt32(); + Vector256 b3 = Avx2.And(a3, alphaMaskVector256).AsInt32(); Vector256 c0 = Avx2.PackSignedSaturate(b0, b1).AsInt16(); Vector256 c1 = Avx2.PackSignedSaturate(b2, b3).AsInt16(); Vector256 d = Avx2.PackSignedSaturate(c0, c1).AsByte(); - Vector256 bits = Avx2.CompareEqual(d, All0x80Vector256); + Vector256 bits = Avx2.CompareEqual(d, all0x80Vector256); int mask = Avx2.MoveMask(bits); if (mask != -1) { @@ -137,18 +130,20 @@ namespace SixLabors.ImageSharp.Formats.Webp #if SUPPORTS_RUNTIME_INTRINSICS private static unsafe bool IsNoneOpaque64Bytes(byte* src, int i) { + Vector128 alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); + Vector128 a0 = Sse2.LoadVector128(src + i).AsByte(); Vector128 a1 = Sse2.LoadVector128(src + i + 16).AsByte(); Vector128 a2 = Sse2.LoadVector128(src + i + 32).AsByte(); Vector128 a3 = Sse2.LoadVector128(src + i + 48).AsByte(); - Vector128 b0 = Sse2.And(a0, AlphaMask).AsInt32(); - Vector128 b1 = Sse2.And(a1, AlphaMask).AsInt32(); - Vector128 b2 = Sse2.And(a2, AlphaMask).AsInt32(); - Vector128 b3 = Sse2.And(a3, AlphaMask).AsInt32(); + Vector128 b0 = Sse2.And(a0, alphaMask).AsInt32(); + Vector128 b1 = Sse2.And(a1, alphaMask).AsInt32(); + Vector128 b2 = Sse2.And(a2, alphaMask).AsInt32(); + Vector128 b3 = Sse2.And(a3, alphaMask).AsInt32(); Vector128 c0 = Sse2.PackSignedSaturate(b0, b1).AsInt16(); Vector128 c1 = Sse2.PackSignedSaturate(b2, b3).AsInt16(); Vector128 d = Sse2.PackSignedSaturate(c0, c1).AsByte(); - Vector128 bits = Sse2.CompareEqual(d, All0x80); + Vector128 bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte()); int mask = Sse2.MoveMask(bits); if (mask != 0xFFFF) { @@ -160,13 +155,15 @@ namespace SixLabors.ImageSharp.Formats.Webp private static unsafe bool IsNoneOpaque32Bytes(byte* src, int i) { + Vector128 alphaMask = Vector128.Create(0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255); + Vector128 a0 = Sse2.LoadVector128(src + i).AsByte(); Vector128 a1 = Sse2.LoadVector128(src + i + 16).AsByte(); - Vector128 b0 = Sse2.And(a0, AlphaMask).AsInt32(); - Vector128 b1 = Sse2.And(a1, AlphaMask).AsInt32(); + Vector128 b0 = Sse2.And(a0, alphaMask).AsInt32(); + Vector128 b1 = Sse2.And(a1, alphaMask).AsInt32(); Vector128 c = Sse2.PackSignedSaturate(b0, b1).AsInt16(); Vector128 d = Sse2.PackSignedSaturate(c, c).AsByte(); - Vector128 bits = Sse2.CompareEqual(d, All0x80); + Vector128 bits = Sse2.CompareEqual(d, Vector128.Create((byte)0x80).AsByte()); int mask = Sse2.MoveMask(bits); if (mask != 0xFFFF) { From 482ef61b5e5fe05b01ff15f9d82d6f5d0fdda36c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 18 May 2022 15:50:26 +0200 Subject: [PATCH 02/62] Fixed single line comment rule violation by inserting a blank line --- src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs | 1 + src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs index e1c1ee557e..0938ecec30 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/QuantEnc.cs @@ -557,6 +557,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // if (coeff > 2047) coeff = 2047 out0 = Avx2.Min(out0, Vector256.Create((short)MaxLevel)); + // Put the sign back. out0 = Avx2.Sign(out0, input0); diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs index eb2ac3bcbd..4e6e8fd8a2 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs @@ -553,6 +553,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy // [ (a0 + a1) << 3, ... ] Vector128 tmp0 = Sse2.MultiplyAddAdjacent(a01, Vector128.Create(8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0, 8, 0).AsInt16()); // K88p + // [ (a0 - a1) << 3, ... ] Vector128 tmp2 = Sse2.MultiplyAddAdjacent(a01, Vector128.Create(8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255, 8, 0, 248, 255).AsInt16()); // K88m Vector128 tmp11 = Sse2.MultiplyAddAdjacent(a32, Vector128.Create(232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8, 232, 20, 169, 8).AsInt16()); // K5352_2217p From e3655a993a688487255bdaefafbc5a6d658853f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 18 May 2022 16:33:49 +0200 Subject: [PATCH 03/62] Switched to const by MMShuffle-pattern instead of the comment approach Cf. https://github.com/SixLabors/ImageSharp/issues/2121#issuecomment-1130056699 --- .../Formats/Webp/Lossless/LosslessUtils.cs | 36 ++++++++++++------- .../Formats/Webp/Lossy/Vp8Encoding.cs | 9 +++-- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs index e86ef0f2ad..1405b390b6 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/LosslessUtils.cs @@ -141,11 +141,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint i; for (i = 0; i <= numPixels - 4; i += 4) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, 0xA0); // MmShuffle(2, 2, 0, 0) - Vector128 c = Sse2.ShuffleHigh(b, 0xA0); // MmShuffle(2, 2, 0, 0) 0g0g + Vector128 b = Sse2.ShuffleLow(a, mmShuffle_2200); + Vector128 c = Sse2.ShuffleHigh(b, mmShuffle_2200); // 0g0g Vector128 output = Sse2.Add(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -223,11 +225,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint i; for (i = 0; i <= numPixels - 4; i += 4) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), i); Vector128 input = Unsafe.As>(ref pos).AsByte(); Vector128 a = Sse2.ShiftRightLogical(input.AsUInt16(), 8); // 0 a 0 g - Vector128 b = Sse2.ShuffleLow(a, 0xA0); // MmShuffle(2, 2, 0, 0) - Vector128 c = Sse2.ShuffleHigh(b, 0xA0); // MmShuffle(2, 2, 0, 0) 0g0g + Vector128 b = Sse2.ShuffleLow(a, mmShuffle_2200); + Vector128 c = Sse2.ShuffleHigh(b, mmShuffle_2200); // 0g0g Vector128 output = Sse2.Subtract(input.AsByte(), c.AsByte()); Unsafe.As>(ref pos) = output.AsUInt32(); } @@ -382,11 +386,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint idx; for (idx = 0; idx <= numPixels - 8; idx += 8) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); Vector256 a = Avx2.And(input.AsByte(), transformColorAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), mmShuffle_2200); + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.ShiftLeftLogical(input.AsInt16(), 8); Vector256 f = Avx2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); @@ -411,11 +417,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint idx; for (idx = 0; idx <= numPixels - 4; idx += 4) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); Vector128 a = Sse2.And(input.AsByte(), transformColorAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), mmShuffle_2200); + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.ShiftLeftLogical(input.AsInt16(), 8); Vector128 f = Sse2.MultiplyHigh(e.AsInt16(), multsb2.AsInt16()); @@ -472,11 +480,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint idx; for (idx = 0; idx <= pixelData.Length - 8; idx += 8) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector256 input = Unsafe.As>(ref pos); Vector256 a = Avx2.And(input.AsByte(), transformColorInverseAlphaGreenMask256); - Vector256 b = Avx2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) - Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector256 b = Avx2.ShuffleLow(a.AsInt16(), mmShuffle_2200); + Vector256 c = Avx2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); Vector256 d = Avx2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector256 e = Avx2.Add(input.AsByte(), d.AsByte()); Vector256 f = Avx2.ShiftLeftLogical(e.AsInt16(), 8); @@ -502,11 +512,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless nint idx; for (idx = 0; idx <= pixelData.Length - 4; idx += 4) { + const byte mmShuffle_2200 = 0b_10_10_00_00; + ref uint pos = ref Unsafe.Add(ref MemoryMarshal.GetReference(pixelData), idx); Vector128 input = Unsafe.As>(ref pos); Vector128 a = Sse2.And(input.AsByte(), transformColorInverseAlphaGreenMask); - Vector128 b = Sse2.ShuffleLow(a.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) - Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), 0xA0); // MmShuffle(2, 2, 0, 0) + Vector128 b = Sse2.ShuffleLow(a.AsInt16(), mmShuffle_2200); + Vector128 c = Sse2.ShuffleHigh(b.AsInt16(), mmShuffle_2200); Vector128 d = Sse2.MultiplyHigh(c.AsInt16(), multsrb.AsInt16()); Vector128 e = Sse2.Add(input.AsByte(), d.AsByte()); Vector128 f = Sse2.ShiftLeftLogical(e.AsInt16(), 8); diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs index 4e6e8fd8a2..b10b3c600c 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoding.cs @@ -535,8 +535,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy { // *in01 = 00 01 10 11 02 03 12 13 // *in23 = 20 21 30 31 22 23 32 33 - Vector128 shuf01_p = Sse2.ShuffleHigh(row01, 0xB1); // MmShuffle(2, 3, 0, 1) - Vector128 shuf32_p = Sse2.ShuffleHigh(row23, 0xB1); // MmShuffle(2, 3, 0, 1) + const byte mmShuffle_2301 = 0b_10_11_00_01; + Vector128 shuf01_p = Sse2.ShuffleHigh(row01, mmShuffle_2301); + Vector128 shuf32_p = Sse2.ShuffleHigh(row23, mmShuffle_2301); // 00 01 10 11 03 02 13 12 // 20 21 30 31 23 22 33 32 @@ -568,7 +569,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy Vector128 shi = Sse2.UnpackHigh(s03, s12); // 2 3 2 3 2 3 Vector128 v23 = Sse2.UnpackHigh(slo.AsInt32(), shi.AsInt32()); out01 = Sse2.UnpackLow(slo.AsInt32(), shi.AsInt32()); - out32 = Sse2.Shuffle(v23, 0x4E); // MmShuffle(1, 0, 3, 2) + + const byte mmShuffle_1032 = 0b_01_00_11_10; + out32 = Sse2.Shuffle(v23, mmShuffle_1032); } public static void FTransformPass2SSE2(Vector128 v01, Vector128 v32, Span output) From f15b336f862360a61a3ee96c8f2e499662e8ff2a Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 20 May 2022 10:45:17 +0200 Subject: [PATCH 04/62] Fix issue disposing tmp buffer too early --- .../PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs index 52cc1f0f17..9b37cf16a5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs @@ -39,8 +39,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation Span tmpBufferSpan = tmpBuffer.GetSpan(); ReverseChromaSubSampling(width, height, this.ycbcrSubSampling[0], this.ycbcrSubSampling[1], data, tmpBufferSpan); ycbcrData = tmpBufferSpan; + this.DecodeYCbCrData(pixels, left, top, width, height, ycbcrData); + return; } + this.DecodeYCbCrData(pixels, left, top, width, height, ycbcrData); + } + + private void DecodeYCbCrData(Buffer2D pixels, int left, int top, int width, int height, ReadOnlySpan ycbcrData) + { var color = default(TPixel); int offset = 0; int widthPadding = 0; From 233dfe12c56876ccfdb5754cf19965232891bce5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 20 May 2022 11:12:00 +0200 Subject: [PATCH 05/62] Update Magick reference to 11.1.2 --- tests/Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Directory.Build.targets b/tests/Directory.Build.targets index 5258601897..4e1b9503e1 100644 --- a/tests/Directory.Build.targets +++ b/tests/Directory.Build.targets @@ -21,7 +21,7 @@ - + From b6e26ede089103003a580d1f72b611b47246c18e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 20 May 2022 11:48:11 +0200 Subject: [PATCH 06/62] Remove invalid test image: not actually ycbcr --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 - tests/ImageSharp.Tests/TestImages.cs | 1 - tests/Images/Input/Tiff/rgb-ycbcr-contig-08_h2v1.tiff | 3 --- 3 files changed, 5 deletions(-) delete mode 100644 tests/Images/Input/Tiff/rgb-ycbcr-contig-08_h2v1.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index ceded79cc2..bb8e2e5153 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -303,7 +303,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(FlowerYCbCr888Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerYCbCr888Planar, PixelTypes.Rgba32)] - [WithFile(RgbYCbCr888Contiguoush1v1, PixelTypes.Rgba32)] [WithFile(RgbYCbCr888Contiguoush2v1, PixelTypes.Rgba32)] [WithFile(RgbYCbCr888Contiguoush2v2, PixelTypes.Rgba32)] [WithFile(RgbYCbCr888Contiguoush4v4, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index d36cec630d..013332be9d 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -831,7 +831,6 @@ namespace SixLabors.ImageSharp.Tests public const string FlowerYCbCr888Contiguoush2v1 = "Tiff/flower-ycbcr-contig-08_h2v1.tiff"; public const string FlowerYCbCr888Contiguoush2v2 = "Tiff/flower-ycbcr-contig-08_h2v2.tiff"; public const string FlowerYCbCr888Contiguoush4v4 = "Tiff/flower-ycbcr-contig-08_h4v4.tiff"; - public const string RgbYCbCr888Contiguoush1v1 = "Tiff/rgb-ycbcr-contig-08_h1v1.tiff"; public const string RgbYCbCr888Contiguoush2v1 = "Tiff/rgb-ycbcr-contig-08_h2v1.tiff"; public const string RgbYCbCr888Contiguoush2v2 = "Tiff/rgb-ycbcr-contig-08_h2v2.tiff"; public const string RgbYCbCr888Contiguoush4v4 = "Tiff/rgb-ycbcr-contig-08_h4v4.tiff"; diff --git a/tests/Images/Input/Tiff/rgb-ycbcr-contig-08_h2v1.tiff b/tests/Images/Input/Tiff/rgb-ycbcr-contig-08_h2v1.tiff deleted file mode 100644 index 82350e5b29..0000000000 --- a/tests/Images/Input/Tiff/rgb-ycbcr-contig-08_h2v1.tiff +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:95b1ba4ff48ea2263041eca4ada44d009277297bb3b3a185d48580bdf3f7caaf -size 81382 From 9268701336fbf7391638ab7baf79bc3ae92ccdd2 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 20 May 2022 20:18:56 +0200 Subject: [PATCH 07/62] Another attempt: Remove invalid test image, not actually ycbcr --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 - tests/ImageSharp.Tests/TestImages.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index bb8e2e5153..01715cfc72 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -303,7 +303,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(FlowerYCbCr888Contiguous, PixelTypes.Rgba32)] [WithFile(FlowerYCbCr888Planar, PixelTypes.Rgba32)] - [WithFile(RgbYCbCr888Contiguoush2v1, PixelTypes.Rgba32)] [WithFile(RgbYCbCr888Contiguoush2v2, PixelTypes.Rgba32)] [WithFile(RgbYCbCr888Contiguoush4v4, PixelTypes.Rgba32)] [WithFile(FlowerYCbCr888Contiguoush2v1, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 013332be9d..8426011440 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -831,7 +831,6 @@ namespace SixLabors.ImageSharp.Tests public const string FlowerYCbCr888Contiguoush2v1 = "Tiff/flower-ycbcr-contig-08_h2v1.tiff"; public const string FlowerYCbCr888Contiguoush2v2 = "Tiff/flower-ycbcr-contig-08_h2v2.tiff"; public const string FlowerYCbCr888Contiguoush4v4 = "Tiff/flower-ycbcr-contig-08_h4v4.tiff"; - public const string RgbYCbCr888Contiguoush2v1 = "Tiff/rgb-ycbcr-contig-08_h2v1.tiff"; public const string RgbYCbCr888Contiguoush2v2 = "Tiff/rgb-ycbcr-contig-08_h2v2.tiff"; public const string RgbYCbCr888Contiguoush4v4 = "Tiff/rgb-ycbcr-contig-08_h4v4.tiff"; public const string YCbCrJpegCompressed = "Tiff/ycbcr_jpegcompressed.tiff"; From 82e7ac14f00c2ed2b05797913905e7e40cc96c0b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 20 May 2022 20:42:07 +0200 Subject: [PATCH 08/62] Override PhotometricInterpretation to RGB, if it's YCbCr and Jpeg compressed --- src/ImageSharp/Common/Helpers/Numerics.cs | 2 +- .../Compression/Decompressors/JpegTiffCompression.cs | 6 ++---- .../Tiff/PhotometricInterpretation/YCbCrConverter.cs | 12 ++++++------ src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Formats/Tiff/TiffDecoderOptionsParser.cs | 7 +++++++ .../Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff | 3 +++ 8 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index 7de838bc94..bfbaa1b31c 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -968,7 +968,7 @@ namespace SixLabors.ImageSharp /// Tells whether input value is outside of the given range. /// /// Value. - /// Mininum value, inclusive. + /// Minimum value, inclusive. /// Maximum value, inclusive. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsOutOfRange(int value, int min, int max) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs index 88dbcb8828..3b2c765b38 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs @@ -65,13 +65,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors jpegDecoder.ParseStream(stream, spectralConverterGray, CancellationToken.None); // TODO: Should we pass through the CancellationToken from the tiff decoder? - using var decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None); + using Buffer2D decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None); CopyImageBytesToBuffer(buffer, decompressedBuffer); break; } - // If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space. - // There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB). case TiffPhotometricInterpretation.YCbCr: case TiffPhotometricInterpretation.Rgb: { @@ -82,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors jpegDecoder.ParseStream(stream, spectralConverter, CancellationToken.None); // TODO: Should we pass through the CancellationToken from the tiff decoder? - using var decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None); + using Buffer2D decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None); CopyImageBytesToBuffer(buffer, decompressedBuffer); break; } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs index c6594f9084..41c3facd19 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs @@ -19,16 +19,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private static readonly Rational[] DefaultLuma = { - new Rational(299, 1000), - new Rational(587, 1000), - new Rational(114, 1000) + new(299, 1000), + new(587, 1000), + new(114, 1000) }; private static readonly Rational[] DefaultReferenceBlackWhite = { - new Rational(0, 1), new Rational(255, 1), - new Rational(128, 1), new Rational(255, 1), - new Rational(128, 1), new Rational(255, 1) + new(0, 1), new(255, 1), + new(128, 1), new(255, 1), + new(128, 1), new(255, 1) }; public YCbCrConverter(Rational[] referenceBlackAndWhite, Rational[] coefficients) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 1cd3d2c0c1..c595246a5b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff /// The pixel format. /// The IFD tags. /// The token to monitor cancellation. - /// The tiff frame. + /// The tiff frame. private ImageFrame DecodeFrame(ExifProfile tags, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 6baf71466a..206f068437 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -459,6 +459,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompression.Jpeg: { options.CompressionType = TiffDecoderCompressionType.Jpeg; + + if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) + { + // Note: Setting PhotometricInterpretation to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + } + break; } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 01715cfc72..837ee0613b 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -642,6 +642,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(RgbJpegCompressed, PixelTypes.Rgba32)] [WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)] [WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)] + [WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)] [WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_JpegCompressed(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 8426011440..e638180c7c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -834,6 +834,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbYCbCr888Contiguoush2v2 = "Tiff/rgb-ycbcr-contig-08_h2v2.tiff"; public const string RgbYCbCr888Contiguoush4v4 = "Tiff/rgb-ycbcr-contig-08_h4v4.tiff"; public const string YCbCrJpegCompressed = "Tiff/ycbcr_jpegcompressed.tiff"; + public const string YCbCrJpegCompressed2 = "Tiff/ycbcr_jpegcompressed2.tiff"; public const string FlowerRgb444Contiguous = "Tiff/flower-rgb-contig-04.tiff"; public const string FlowerRgb444Planar = "Tiff/flower-rgb-planar-04.tiff"; public const string FlowerRgb222Contiguous = "Tiff/flower-rgb-contig-02.tiff"; diff --git a/tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff b/tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff new file mode 100644 index 0000000000..26495d577d --- /dev/null +++ b/tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:533f92b6a45c2de4dc0f3cdb8debf45dcfe84790cfa652404b2f44e15f06e44f +size 38816 From c1c4f528c7d2dc4ef99b5c8fa7762a0e7e68508b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 21:09:10 +0200 Subject: [PATCH 09/62] If component Id's are 1-3 for a 3-channel image, then the image is assumed to be YCbCr --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index a07db1c952..7247f16489 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -520,11 +520,18 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. + // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color if (this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82) { return JpegColorSpace.RGB; } + // If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr. + if (this.Components[2].Id == 3 && this.Components[1].Id == 2 && this.Components[0].Id == 1) + { + return JpegColorSpace.YCbCr; + } + // Some images are poorly encoded and contain incorrect colorspace transform metadata. // We ignore that and always fall back to the default colorspace. return JpegColorSpace.YCbCr; From 89668a6e4d6d7d1a33f05f53b40458937b62130c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 21:12:49 +0200 Subject: [PATCH 10/62] 3-channel non-subsampled images are assumed to be RGB. --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 7247f16489..c852d957c2 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -532,6 +532,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.YCbCr; } + // 3-channel non-subsampled images are assumed to be RGB. + if (this.Components[2].VerticalSamplingFactor == 1 && this.Components[1].VerticalSamplingFactor == 1 && this.Components[0].VerticalSamplingFactor == 1 && + this.Components[2].HorizontalSamplingFactor == 1 && this.Components[1].HorizontalSamplingFactor == 1 && this.Components[0].HorizontalSamplingFactor == 1) + { + return JpegColorSpace.RGB; + } + // Some images are poorly encoded and contain incorrect colorspace transform metadata. // We ignore that and always fall back to the default colorspace. return JpegColorSpace.YCbCr; From afc13e04f2ccfabcd6f7959079be42fb34ad79b3 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 21:17:43 +0200 Subject: [PATCH 11/62] Add test for jpeg compressed tiff, which is actually RGB colorspace --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + .../Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/Images/Input/Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 837ee0613b..4c89c00b63 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -640,6 +640,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(RgbJpegCompressed, PixelTypes.Rgba32)] + [WithFile(RgbJpegCompressed2, PixelTypes.Rgba32)] [WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)] [WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)] [WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 0ac0944514..daf0c1f78f 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -780,6 +780,7 @@ namespace SixLabors.ImageSharp.Tests public const string RgbDeflatePredictor = "Tiff/rgb_deflate_predictor.tiff"; public const string RgbDeflateMultistrip = "Tiff/rgb_deflate_multistrip.tiff"; public const string RgbJpegCompressed = "Tiff/rgb_jpegcompression.tiff"; + public const string RgbJpegCompressed2 = "Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff"; public const string RgbWithStripsJpegCompressed = "Tiff/rgb_jpegcompressed_stripped.tiff"; public const string RgbJpegCompressedNoJpegTable = "Tiff/rgb_jpegcompressed_nojpegtable.tiff"; public const string RgbLzwPredictor = "Tiff/rgb_lzw_predictor.tiff"; diff --git a/tests/Images/Input/Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff b/tests/Images/Input/Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff new file mode 100644 index 0000000000..67c5bcf9a6 --- /dev/null +++ b/tests/Images/Input/Tiff/twain-rgb-jpeg-with-bogus-ycbcr-subsampling.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7a559d36e3852265ab4f82e43d28cc0bfc310813a5ced08e51c1366d8e323f9 +size 146853 From 30e43549857ed27d296e3c12e2870f3c5427d7b0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 22:23:22 +0200 Subject: [PATCH 12/62] JFIF implies YCbCr --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index c852d957c2..e1e6d5d6e9 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -519,6 +519,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } + if (!this.jFif.Equals(default)) + { + // JFIF implies YCbCr. + return JpegColorSpace.YCbCr; + } + // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color if (this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82) From 44c1ad58b19c25934fdb0b58db63709bda3f27f9 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 22:31:18 +0200 Subject: [PATCH 13/62] Use flag to indicate if JFIF marker is present (invalid markers also count) --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index e1e6d5d6e9..1e7f5a92e3 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -90,6 +90,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// private JFifMarker jFif; + /// + /// Whether the image has a JFIF marker. This is needed to determine, if the colorspace is YCbCr. + /// + private bool hasJFif; + /// /// Contains information about the Adobe marker. /// @@ -519,7 +524,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } - if (!this.jFif.Equals(default)) + if (this.hasJFif) { // JFIF implies YCbCr. return JpegColorSpace.YCbCr; @@ -721,6 +726,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// The remaining bytes in the segment block. private void ProcessApplicationHeaderMarker(BufferedReadStream stream, int remaining) { + this.hasJFif = true; + // We can only decode JFif identifiers. // Some images contain multiple JFIF markers (Issue 1932) so we check to see // if it's already been read. From 535372da099204758283cac57d4ce727c17a380c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 May 2022 22:57:29 +0200 Subject: [PATCH 14/62] Another attempt to get deducing jpeg color space right --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 1e7f5a92e3..b3d7f60f51 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -524,9 +524,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } - if (this.hasJFif) + if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) { - // JFIF implies YCbCr. return JpegColorSpace.YCbCr; } @@ -537,6 +536,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } + if (this.hasJFif) + { + // JFIF implies YCbCr. + return JpegColorSpace.YCbCr; + } + // If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr. if (this.Components[2].Id == 3 && this.Components[1].Id == 2 && this.Components[0].Id == 1) { From 58927f327bbcd8aeb7cf369ae31ba60bf0473f51 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 May 2022 14:33:29 +0200 Subject: [PATCH 15/62] Also set color type to rgb, when compression is jpeg --- src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 206f068437..6d99fed3e1 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -462,8 +462,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null) { - // Note: Setting PhotometricInterpretation to RGB here, since the jpeg decoder will handle the conversion of the pixel data. + // Note: Setting PhotometricInterpretation and color type to RGB here, since the jpeg decoder will handle the conversion of the pixel data. options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb; + options.ColorType = TiffColorType.Rgb; } break; From d652a4afa5688e70f0e6614c92decb9bd497eb60 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 22 May 2022 17:20:24 +0200 Subject: [PATCH 16/62] Add test case for replicating issue #2123 --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/Issues/Issue2123.tiff | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/Images/Input/Tiff/Issues/Issue2123.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 4c89c00b63..5311fe7eee 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -646,6 +646,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)] [WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)] [WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)] + [WithFile(Issues2123, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_JpegCompressed(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffDecoder(provider, useExactComparer: false); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index daf0c1f78f..023efd7076 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -912,6 +912,7 @@ namespace SixLabors.ImageSharp.Tests public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string Issues1891 = "Tiff/Issues/Issue1891.tiff"; + public const string Issues2123 = "Tiff/Issues/Issue2123.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; diff --git a/tests/Images/Input/Tiff/Issues/Issue2123.tiff b/tests/Images/Input/Tiff/Issues/Issue2123.tiff new file mode 100644 index 0000000000..a21bffca12 --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Issue2123.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5663288720203035c454c61c529222c2170df21dcde1a89f1f30e3b668020d6f +size 3805 From e42fdd09f2714946e6f177ff283f290958d817a1 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 23 May 2022 14:40:54 +0200 Subject: [PATCH 17/62] Change deducing color space according to review --- .../Formats/Jpeg/JpegDecoderCore.cs | 53 ++++++++++++------- .../Formats/Jpeg/JpegThrowHelper.cs | 3 ++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index b3d7f60f51..84a679620c 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -519,21 +519,21 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (componentCount == 3) { - if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) + // We prioritize adobe marker over jfif marker, if somebody really encoded this image with redundant adobe marker, + // then it's most likely an adobe jfif image. + if (!this.adobe.Equals(default)) { - return JpegColorSpace.RGB; - } + if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) + { + return JpegColorSpace.YCbCr; + } - if (!this.adobe.Equals(default) && this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYCbCr) - { - return JpegColorSpace.YCbCr; - } + if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) + { + return JpegColorSpace.RGB; + } - // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. - // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color - if (this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82) - { - return JpegColorSpace.RGB; + JpegThrowHelper.ThrowNotSupportedColorSpace(); } if (this.hasJFif) @@ -542,10 +542,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.YCbCr; } - // If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr. - if (this.Components[2].Id == 3 && this.Components[1].Id == 2 && this.Components[0].Id == 1) + // Fallback to the id color deduction. + // If the component Id's are R, G, B in ASCII the colorspace is RGB and not YCbCr. + // See: https://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color + if (this.Components[2].Id == 66 && this.Components[1].Id == 71 && this.Components[0].Id == 82) { - return JpegColorSpace.YCbCr; + return JpegColorSpace.RGB; } // 3-channel non-subsampled images are assumed to be RGB. @@ -562,9 +564,24 @@ namespace SixLabors.ImageSharp.Formats.Jpeg if (componentCount == 4) { - return this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck - ? JpegColorSpace.Ycck - : JpegColorSpace.Cmyk; + // jfif images doesn't not support 4 component images, so we only check adobe. + if (!this.adobe.Equals(default)) + { + if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformYcck) + { + return JpegColorSpace.Ycck; + } + + if (this.adobe.ColorTransform == JpegConstants.Adobe.ColorTransformUnknown) + { + return JpegColorSpace.Cmyk; + } + + JpegThrowHelper.ThrowNotSupportedColorSpace(); + } + + // Fallback to cmyk as neither of cmyk nor ycck have 'special' component ids. + return JpegColorSpace.Cmyk; } JpegThrowHelper.ThrowNotSupportedComponentCount(componentCount); diff --git a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs index 1073ffff78..0dc412a6f9 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegThrowHelper.cs @@ -51,5 +51,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg [MethodImpl(InliningOptions.ColdPath)] public static void ThrowNotSupportedComponentCount(int componentCount) => throw new NotSupportedException($"Images with {componentCount} components are not supported."); + + [MethodImpl(InliningOptions.ColdPath)] + public static void ThrowNotSupportedColorSpace() => throw new NotSupportedException("Image color space could not be deduced."); } } From bc733221a447ee22027e288858294352e4a9752f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 23 May 2022 14:45:19 +0200 Subject: [PATCH 18/62] Fix jpeg compressed tiff without jpeg table --- tests/Images/Input/Tiff/rgb_jpegcompressed_nojpegtable.tiff | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Images/Input/Tiff/rgb_jpegcompressed_nojpegtable.tiff b/tests/Images/Input/Tiff/rgb_jpegcompressed_nojpegtable.tiff index b97ab8830d..a10b0dfa55 100644 --- a/tests/Images/Input/Tiff/rgb_jpegcompressed_nojpegtable.tiff +++ b/tests/Images/Input/Tiff/rgb_jpegcompressed_nojpegtable.tiff @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6b81013d7b0a29ed1ac9c33e175e0c0e69494b93b2b65b692f16d9ea042b9d5d -size 7759 +oid sha256:3e889209fc31702aaa7c966c1b5370cc0904cbfbcfd17718977045049cc1bfd9 +size 5904 From 4e84800728b7cdd8196b9d887091456c0a0ccc98 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 23 May 2022 14:54:19 +0200 Subject: [PATCH 19/62] Fallback to id color deduction for adobe marker --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 84a679620c..99039567ae 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -533,6 +533,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } + // Fallback to the id color deduction: If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr. + if (this.Components[2].Id == 3 && this.Components[1].Id == 2 && this.Components[0].Id == 1) + { + return JpegColorSpace.YCbCr; + } + JpegThrowHelper.ThrowNotSupportedColorSpace(); } From c167062d84aef34edcf4996d78c4ec221338bfed Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 24 May 2022 23:54:01 +0200 Subject: [PATCH 20/62] Add compare to reference output for decoding tiff images with YCbCr data --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 1 + ...anDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h1v1.png | 3 +++ ...anDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v1.png | 3 +++ ...anDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v2.png | 3 +++ ...anDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h4v4.png | 3 +++ ...anDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-planar-08_h1v1.png | 3 +++ ...r_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h2v2.png | 3 +++ ...r_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h4v4.png | 3 +++ 8 files changed, 22 insertions(+) create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h1v1.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v1.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v2.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h4v4.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-planar-08_h1v1.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h2v2.png create mode 100644 tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h4v4.png diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 5311fe7eee..b9569c7a46 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -315,6 +315,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff // converting the pixel data from Magick.NET to our format with YCbCr? using Image image = provider.GetImage(); image.DebugSave(provider); + image.CompareToReferenceOutput(ImageComparer.Exact, provider); } [Theory] diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h1v1.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h1v1.png new file mode 100644 index 0000000000..3c9cbce8f6 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h1v1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f43aec94a8febc4174d1c3b0637b9e613781acccc1dc988cb62f521e26c4038 +size 9775 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v1.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v1.png new file mode 100644 index 0000000000..9edccbed24 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f874b0a5172d494a8c8e9760fe87e02d138d78e15de637506ff46334ad7d0629 +size 9792 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v2.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v2.png new file mode 100644 index 0000000000..d8fa6791a7 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h2v2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa07f9f6a85a87e145224a6e2d42c7ecc26b9128d01eee3f45fc4333f05d560c +size 9808 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h4v4.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h4v4.png new file mode 100644 index 0000000000..78fba45962 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-contig-08_h4v4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c34d570b0e6d7d9fe830e4696c6acb279929b86e6f4b9f572d4b379fee383315 +size 9504 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-planar-08_h1v1.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-planar-08_h1v1.png new file mode 100644 index 0000000000..3c9cbce8f6 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_flower-ycbcr-planar-08_h1v1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f43aec94a8febc4174d1c3b0637b9e613781acccc1dc988cb62f521e26c4038 +size 9775 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h2v2.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h2v2.png new file mode 100644 index 0000000000..a1d71cbaff --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h2v2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:77a101bcc2059d8ca98ac7b1c4fe67a286c33f0d9fa7d37bb4a5073377f70c62 +size 91016 diff --git a/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h4v4.png b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h4v4.png new file mode 100644 index 0000000000..cb1f0ea692 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_YCbCr_24Bit_Rgba32_rgb-ycbcr-contig-08_h4v4.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cbe7c1ab6862e2f6ad5ad5f0a02634a2a3e99fbf1a404168b1fbcd7aafb27884 +size 84732 From dc9bceed233c2263add4c94d11bcd26e5a6fd328 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 08:55:00 +0200 Subject: [PATCH 21/62] Remove old frameworks --- tests/ImageSharp.Benchmarks/Config.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Config.cs b/tests/ImageSharp.Benchmarks/Config.cs index 60d0e76613..34978b594a 100644 --- a/tests/ImageSharp.Benchmarks/Config.cs +++ b/tests/ImageSharp.Benchmarks/Config.cs @@ -32,17 +32,13 @@ namespace SixLabors.ImageSharp.Benchmarks public class MultiFramework : Config { public MultiFramework() => this.AddJob( - Job.Default.WithRuntime(ClrRuntime.Net472), - Job.Default.WithRuntime(CoreRuntime.Core31), - Job.Default.WithRuntime(CoreRuntime.Core50).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + Job.Default.WithRuntime(CoreRuntime.Core60).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } public class ShortMultiFramework : Config { public ShortMultiFramework() => this.AddJob( - Job.Default.WithRuntime(ClrRuntime.Net472).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Default.WithRuntime(CoreRuntime.Core31).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3), - Job.Default.WithRuntime(CoreRuntime.Core50).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); + Job.Default.WithRuntime(CoreRuntime.Core60).WithLaunchCount(1).WithWarmupCount(3).WithIterationCount(3).WithArguments(new Argument[] { new MsBuildArgument("/p:DebugType=portable") })); } public class ShortCore31 : Config From 54ff65c7cff6b1739fde3c06adafe983773cc276 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 08:59:22 +0200 Subject: [PATCH 22/62] Add fax4 image for benchmarks --- tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/CCITTGroup4.tiff | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 tests/Images/Input/Tiff/CCITTGroup4.tiff diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs index db94fb1214..b34640bb14 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/DecodeTiff.cs @@ -46,6 +46,7 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Params( TestImages.Tiff.CcittFax3AllTermCodes, + TestImages.Tiff.Fax4Compressed2, TestImages.Tiff.HuffmanRleAllMakeupCodes, TestImages.Tiff.Calliphora_GrayscaleUncompressed, TestImages.Tiff.Calliphora_RgbPaletteLzw_Predictor, diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 023efd7076..e83d3d1562 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -759,6 +759,7 @@ namespace SixLabors.ImageSharp.Tests public const string Calliphora_HuffmanCompressed = "Tiff/Calliphora_huffman_rle.tiff"; public const string Calliphora_BiColorUncompressed = "Tiff/Calliphora_bicolor_uncompressed.tiff"; public const string Fax4Compressed = "Tiff/basi3p02_fax4.tiff"; + public const string Fax4Compressed2 = "Tiff/CCITTGroup4.tiff"; public const string Fax4CompressedLowerOrderBitsFirst = "Tiff/basi3p02_fax4_lowerOrderBitsFirst.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; diff --git a/tests/Images/Input/Tiff/CCITTGroup4.tiff b/tests/Images/Input/Tiff/CCITTGroup4.tiff new file mode 100644 index 0000000000..20ffcd5d60 --- /dev/null +++ b/tests/Images/Input/Tiff/CCITTGroup4.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:120ad0814f207c45d968b05f7435034ecfee8ac1a0958cd984a070dad31f66f3 +size 11082 From 0cf4d9172f64ad67b06d458df1d257bb62dddfd8 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 09:01:42 +0200 Subject: [PATCH 23/62] Add InliningOptions.ShortMethod for WriteBit methods --- src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 08d1475268..c78862d865 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { @@ -44,8 +45,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } } + [MethodImpl(InliningOptions.ShortMethod)] public static void WriteBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); + [MethodImpl(InliningOptions.ShortMethod)] public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); } } From 8459828aad3bf6cdbac4dcc76faeb8d657120e83 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 09:18:10 +0200 Subject: [PATCH 24/62] Use GetRowSpan to access pixel data --- .../BlackIsZero1TiffColor{TPixel}.cs | 13 ++++++------- .../WhiteIsZero1TiffColor{TPixel}.cs | 13 ++++++------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index eb749efe62..6cb9c61761 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -17,14 +17,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; + var colorBlack = default(TPixel); + var colorWhite = default(TPixel); - Color black = Color.Black; - Color white = Color.White; + colorBlack.FromRgba32(Color.Black); + colorWhite.FromRgba32(Color.White); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width; x += 8) { byte b = data[offset++]; @@ -34,9 +35,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { int bit = (b >> (7 - shift)) & 1; - color.FromRgba32(bit == 0 ? black : white); - - pixels[x + shift, y] = color; + pixelRowSpan[x + shift] = bit == 0 ? colorBlack : colorWhite; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index 5f1afe46ff..41152ac727 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -16,14 +16,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - var color = default(TPixel); - int offset = 0; + var colorBlack = default(TPixel); + var colorWhite = default(TPixel); - Color black = Color.Black; - Color white = Color.White; + colorBlack.FromRgba32(Color.Black); + colorWhite.FromRgba32(Color.White); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width; x += 8) { byte b = data[offset++]; @@ -33,9 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { int bit = (b >> (7 - shift)) & 1; - color.FromRgba32(bit == 0 ? white : black); - - pixels[x + shift, y] = color; + pixelRowSpan[x + shift] = bit == 0 ? colorWhite : colorBlack; } } } From c0059fc599d40dbb556fd983ad52635afa786099 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 10:03:04 +0200 Subject: [PATCH 25/62] Avoid calculating bit position multiple times --- .../Compression/Decompressors/T4BitReader.cs | 54 +++++++++---------- .../Decompressors/T6TiffCompression.cs | 21 +++++++- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 9925d5a194..122316f94f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -52,28 +52,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// private readonly int maxCodeLength = 13; - private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen4TermCodes = new() { { 0x7, 2 }, { 0x8, 3 }, { 0xB, 4 }, { 0xC, 5 }, { 0xE, 6 }, { 0xF, 7 } }; - private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen5TermCodes = new() { { 0x13, 8 }, { 0x14, 9 }, { 0x7, 10 }, { 0x8, 11 } }; - private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen6TermCodes = new() { { 0x7, 1 }, { 0x8, 12 }, { 0x3, 13 }, { 0x34, 14 }, { 0x35, 15 }, { 0x2A, 16 }, { 0x2B, 17 } }; - private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen7TermCodes = new() { { 0x27, 18 }, { 0xC, 19 }, { 0x8, 20 }, { 0x17, 21 }, { 0x3, 22 }, { 0x4, 23 }, { 0x28, 24 }, { 0x2B, 25 }, { 0x13, 26 }, { 0x24, 27 }, { 0x18, 28 } }; - private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen8TermCodes = new() { { 0x35, 0 }, { 0x2, 29 }, { 0x3, 30 }, { 0x1A, 31 }, { 0x1B, 32 }, { 0x12, 33 }, { 0x13, 34 }, { 0x14, 35 }, { 0x15, 36 }, { 0x16, 37 }, { 0x17, 38 }, { 0x28, 39 }, { 0x29, 40 }, { 0x2A, 41 }, { 0x2B, 42 }, { 0x2C, 43 }, { 0x2D, 44 }, { 0x4, 45 }, @@ -81,57 +81,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x58, 55 }, { 0x59, 56 }, { 0x5A, 57 }, { 0x5B, 58 }, { 0x4A, 59 }, { 0x4B, 60 }, { 0x32, 61 }, { 0x33, 62 }, { 0x34, 63 } }; - private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + private static readonly Dictionary BlackLen2TermCodes = new() { { 0x3, 2 }, { 0x2, 3 } }; - private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + private static readonly Dictionary BlackLen3TermCodes = new() { { 0x2, 1 }, { 0x3, 4 } }; - private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + private static readonly Dictionary BlackLen4TermCodes = new() { { 0x3, 5 }, { 0x2, 6 } }; - private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + private static readonly Dictionary BlackLen5TermCodes = new() { { 0x3, 7 } }; - private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + private static readonly Dictionary BlackLen6TermCodes = new() { { 0x5, 8 }, { 0x4, 9 } }; - private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + private static readonly Dictionary BlackLen7TermCodes = new() { { 0x4, 10 }, { 0x5, 11 }, { 0x7, 12 } }; - private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + private static readonly Dictionary BlackLen8TermCodes = new() { { 0x4, 13 }, { 0x7, 14 } }; - private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + private static readonly Dictionary BlackLen9TermCodes = new() { { 0x18, 15 } }; - private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + private static readonly Dictionary BlackLen10TermCodes = new() { { 0x37, 0 }, { 0x17, 16 }, { 0x18, 17 }, { 0x8, 18 } }; - private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + private static readonly Dictionary BlackLen11TermCodes = new() { { 0x67, 19 }, { 0x68, 20 }, { 0x6C, 21 }, { 0x37, 22 }, { 0x28, 23 }, { 0x17, 24 }, { 0x18, 25 } }; - private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + private static readonly Dictionary BlackLen12TermCodes = new() { { 0xCA, 26 }, { 0xCB, 27 }, { 0xCC, 28 }, { 0xCD, 29 }, { 0x68, 30 }, { 0x69, 31 }, { 0x6A, 32 }, { 0x6B, 33 }, { 0xD2, 34 }, { 0xD3, 35 }, { 0xD4, 36 }, { 0xD5, 37 }, { 0xD6, 38 }, { 0xD7, 39 }, { 0x6C, 40 }, { 0x6D, 41 }, { 0xDA, 42 }, { 0xDB, 43 }, @@ -140,62 +140,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x66, 62 }, { 0x67, 63 } }; - private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen5MakeupCodes = new() { { 0x1B, 64 }, { 0x12, 128 } }; - private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen6MakeupCodes = new() { { 0x17, 192 }, { 0x18, 1664 } }; - private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen8MakeupCodes = new() { { 0x36, 320 }, { 0x37, 384 }, { 0x64, 448 }, { 0x65, 512 }, { 0x68, 576 }, { 0x67, 640 } }; - private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen7MakeupCodes = new() { { 0x37, 256 } }; - private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen9MakeupCodes = new() { { 0xCC, 704 }, { 0xCD, 768 }, { 0xD2, 832 }, { 0xD3, 896 }, { 0xD4, 960 }, { 0xD5, 1024 }, { 0xD6, 1088 }, { 0xD7, 1152 }, { 0xD8, 1216 }, { 0xD9, 1280 }, { 0xDA, 1344 }, { 0xDB, 1408 }, { 0x98, 1472 }, { 0x99, 1536 }, { 0x9A, 1600 }, { 0x9B, 1728 } }; - private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen11MakeupCodes = new() { { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } }; - private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen12MakeupCodes = new() { { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; - private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen10MakeupCodes = new() { { 0xF, 64 } }; - private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen11MakeupCodes = new() { { 0x8, 1792 }, { 0xC, 1856 }, { 0xD, 1920 } }; - private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen12MakeupCodes = new() { { 0xC8, 128 }, { 0xC9, 192 }, { 0x5B, 256 }, { 0x33, 320 }, { 0x34, 384 }, { 0x35, 448 }, { 0x12, 1984 }, { 0x13, 2048 }, { 0x14, 2112 }, { 0x15, 2176 }, { 0x16, 2240 }, { 0x17, 2304 }, { 0x1C, 2368 }, { 0x1D, 2432 }, { 0x1E, 2496 }, { 0x1F, 2560 } }; - private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen13MakeupCodes = new() { { 0x6C, 512 }, { 0x6D, 576 }, { 0x4A, 640 }, { 0x4B, 704 }, { 0x4C, 768 }, { 0x4D, 832 }, { 0x72, 896 }, { 0x73, 960 }, { 0x74, 1024 }, { 0x75, 1088 }, { 0x76, 1152 }, { 0x77, 1216 }, { 0x52, 1280 }, { 0x53, 1344 }, diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 972f4d8ff1..71d7c4d556 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,10 +77,27 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) { byte white = (byte)(this.isWhiteZero ? 0 : 255); + int bitPos = (int)(bitsWritten % 8); + int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, 1, scanLine[i] == white ? this.whiteValue : this.blackValue); + if (scanLine[i] == white) + { + BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); + } + else + { + BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); + } + + bitPos++; bitsWritten++; + + if (bitPos >= 8) + { + bitPos = 0; + bufferPos++; + } } // Write padding bytes, if necessary. @@ -122,7 +139,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } else { - scanline.Fill((byte)255); + scanline.Fill(255); } break; From cecd7f29d77b8d09888437e9e0fc992085e1829f Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 10:17:15 +0200 Subject: [PATCH 26/62] Add MethodImpl(InliningOptions.ShortMethod) for SwapColor --- .../Tiff/Compression/Decompressors/T6BitReader.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index 6b9939b175..e747718a44 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; @@ -16,32 +17,32 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly int maxCodeLength = 12; - private static readonly CcittTwoDimensionalCode None = new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.None, 0); + private static readonly CcittTwoDimensionalCode None = new(CcittTwoDimensionalCodeType.None, 0); - private static readonly Dictionary Len1Codes = new Dictionary() + private static readonly Dictionary Len1Codes = new() { { 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } }; - private static readonly Dictionary Len3Codes = new Dictionary() + private static readonly Dictionary Len3Codes = new() { { 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, { 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, { 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } }; - private static readonly Dictionary Len4Codes = new Dictionary() + private static readonly Dictionary Len4Codes = new() { { 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } }; - private static readonly Dictionary Len6Codes = new Dictionary() + private static readonly Dictionary Len6Codes = new() { { 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, { 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } }; - private static readonly Dictionary Len7Codes = new Dictionary() + private static readonly Dictionary Len7Codes = new() { { 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, { 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, @@ -154,6 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Swaps the white run to black run an vise versa. /// + [MethodImpl(InliningOptions.ShortMethod)] public void SwapColor() => this.IsWhiteRun = !this.IsWhiteRun; } } From 0ae59fdba44e300963b18cee7285bb7691f17388 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 14:01:47 +0200 Subject: [PATCH 27/62] Avoid bounds checks in GetBit() --- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 4 +++- .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 6 ------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 122316f94f..15e2b3c307 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -6,6 +6,7 @@ using System.Buffers; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.Memory; @@ -815,7 +816,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.BitsRead - 1; - uint bit = (uint)((dataSpan[(int)this.Position] & (1 << shift)) != 0 ? 1 : 0); + ref byte dataAtPosition = ref Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); + uint bit = (uint)((dataAtPosition & (1 << shift)) != 0 ? 1 : 0); this.BitsRead++; return bit; diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 71d7c4d556..c769d4d685 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -15,10 +15,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly bool isWhiteZero; - private readonly byte whiteValue; - - private readonly byte blackValue; - private readonly int width; /// @@ -40,8 +36,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.FillOrder = fillOrder; this.width = width; this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; - this.whiteValue = (byte)(this.isWhiteZero ? 0 : 1); - this.blackValue = (byte)(this.isWhiteZero ? 1 : 0); } /// From dd6b4c11fd7b8e28dd941bf06f0b325fa82d5110 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 15:28:22 +0200 Subject: [PATCH 28/62] Deduce color space as YCbCr if component id's are 1, 2, 3 --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 99039567ae..a6ccb1b644 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -556,6 +556,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return JpegColorSpace.RGB; } + // If these values are 1-3 for a 3-channel image, then the image is assumed to be YCbCr. + if (this.Components[2].Id == 3 && this.Components[1].Id == 2 && this.Components[0].Id == 1) + { + return JpegColorSpace.YCbCr; + } + // 3-channel non-subsampled images are assumed to be RGB. if (this.Components[2].VerticalSamplingFactor == 1 && this.Components[1].VerticalSamplingFactor == 1 && this.Components[0].VerticalSamplingFactor == 1 && this.Components[2].HorizontalSamplingFactor == 1 && this.Components[1].HorizontalSamplingFactor == 1 && this.Components[0].HorizontalSamplingFactor == 1) From 9d04ae47014e457503f79c72aee652c29480b83c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 15:31:45 +0200 Subject: [PATCH 29/62] Add test case for #2133 --- .../Formats/Jpg/JpegDecoderTests.cs | 13 +++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Jpg/issues/Issue2133.jpg | 3 +++ 3 files changed, 17 insertions(+) create mode 100644 tests/Images/Input/Jpg/issues/Issue2133.jpg diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index e39aaa323e..b105677bec 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -218,6 +218,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + // https://github.com/SixLabors/ImageSharp/issues/2133 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2133DeduceColorSpace, PixelTypes.Rgba32)] + public void Issue2133_DeduceColorSpace(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(JpegDecoder)) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + // DEBUG ONLY! // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" // into "\tests\Images\ActualOutput\JpegDecoderTests\" diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 023efd7076..403b42b254 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -272,6 +272,7 @@ namespace SixLabors.ImageSharp.Tests public const string Issue2057App1Parsing = "Jpg/issues/Issue2057-App1Parsing.jpg"; public const string ExifNullArrayTag = "Jpg/issues/issue-2056-exif-null-array.jpg"; public const string ValidExifArgumentNullExceptionOnEncode = "Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg"; + public const string Issue2133DeduceColorSpace = "Jpg/issues/Issue2133.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/Issue2133.jpg b/tests/Images/Input/Jpg/issues/Issue2133.jpg new file mode 100644 index 0000000000..c51962a607 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue2133.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7be837f931a350906f04542a868c3654a35b4f463ec814939aed3d134c7e56fe +size 1140 From 07d2e79c384a45eb9dc26b658a6e1e80ea264713 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:15:28 +0200 Subject: [PATCH 30/62] Avoid more bounds checks --- .../Formats/Tiff/Compression/BitWriterUtils.cs | 13 +++++++++++-- .../Compression/Decompressors/T6TiffCompression.cs | 4 +++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index c78862d865..7394577a52 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -3,6 +3,7 @@ using System; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { @@ -46,9 +47,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos)); + public static void WriteBit(Span buffer, int bufferPos, int bitPos) + { + ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); + b |= (byte)(1 << (7 - bitPos)); + } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos))); + public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) + { + ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); + b = (byte)(b & ~(1 << (7 - bitPos))); + } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index c769d4d685..1ce7f02d1e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -75,7 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - if (scanLine[i] == white) + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) == white) { BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); } From 57349fd0aea4f3258f7dde682b2776ae05b35137 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:16:56 +0200 Subject: [PATCH 31/62] Avoid writing zero bit, we already have a clean buffer --- .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 1ce7f02d1e..f8333cf338 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,11 +77,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors int bufferPos = (int)(bitsWritten / 8); for (int i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) == white) - { - BitWriterUtils.WriteZeroBit(buffer, bufferPos, bitPos); - } - else + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From c7aaf10411f23a70d2162721259bea6e076ff861 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 17:43:38 +0200 Subject: [PATCH 32/62] Use GetRowSpan --- .../BlackIsZero4TiffColor{TPixel}.cs | 7 ++++--- .../WhiteIsZero4TiffColor{TPixel}.cs | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs index 2e66bb6d70..79247d8ed0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero4TiffColor{TPixel}.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; @@ -32,13 +33,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; byte intensity2 = (byte)((byteData & 0x0F) * 17); l8.PackedValue = intensity2; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; } if (isOddWidth) @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[left + width - 1, y] = color; + pixelRowSpan[left + width - 1] = color; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs index a4650af5ea..8b635043ec 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero4TiffColor{TPixel}.cs @@ -24,6 +24,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation var l8 = default(L8); for (int y = top; y < top + height; y++) { + Span pixelRowSpan = pixels.DangerousGetRowSpan(y); for (int x = left; x < left + width - 1;) { byte byteData = data[offset++]; @@ -32,13 +33,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; byte intensity2 = (byte)((15 - (byteData & 0x0F)) * 17); l8.PackedValue = intensity2; color.FromL8(l8); - pixels[x++, y] = color; + pixelRowSpan[x++] = color; } if (isOddWidth) @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation l8.PackedValue = intensity1; color.FromL8(l8); - pixels[left + width - 1, y] = color; + pixelRowSpan[left + width - 1] = color; } } } From 432c0d7262e66b71dd1e49a241c8ca4d1a2979dd Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 18:05:39 +0200 Subject: [PATCH 33/62] Avoid bounds checks in BlackIsZero and WhiteIsZero --- .../BlackIsZero1TiffColor{TPixel}.cs | 9 +++++++-- .../WhiteIsZero1TiffColor{TPixel}.cs | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 6cb9c61761..2d8e21d6cf 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -23,19 +25,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); + ref byte dataRef = ref MemoryMarshal.GetReference(data); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); for (int x = left; x < left + width; x += 8) { - byte b = data[offset++]; + byte b = Unsafe.Add(ref dataRef, offset++); int maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { int bit = (b >> (7 - shift)) & 1; - pixelRowSpan[x + shift] = bit == 0 ? colorBlack : colorWhite; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorBlack : colorWhite; } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index 41152ac727..ad7d6c08a9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -22,19 +24,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); + ref byte dataRef = ref MemoryMarshal.GetReference(data); for (int y = top; y < top + height; y++) { Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); for (int x = left; x < left + width; x += 8) { - byte b = data[offset++]; + byte b = Unsafe.Add(ref dataRef, offset++); int maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { int bit = (b >> (7 - shift)) & 1; - pixelRowSpan[x + shift] = bit == 0 ? colorWhite : colorBlack; + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorWhite : colorBlack; } } } From 2876f2f44d0bc363450eda0f81da6db9da7fbb4d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 29 May 2022 23:37:28 +0200 Subject: [PATCH 34/62] Use nint, determine what's white only once --- .../Compression/Decompressors/CcittReferenceScanline.cs | 2 ++ .../Tiff/Compression/Decompressors/T6TiffCompression.cs | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs index 0aec2361c3..5b0c64cf18 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittReferenceScanline.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -130,6 +131,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return index + offset; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int FindB2ForImaginaryWhiteLine() => this.width; private int FindB2ForNormalLine(int b1) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index f8333cf338..e9921418d5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private readonly int width; + private readonly byte white; + /// /// Initializes a new instance of the class. /// @@ -38,6 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.FillOrder = fillOrder; this.width = width; this.isWhiteZero = photometricInterpretation == TiffPhotometricInterpretation.WhiteIsZero; + this.white = (byte)(this.isWhiteZero ? 0 : 255); } /// @@ -72,12 +75,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) { - byte white = (byte)(this.isWhiteZero ? 0 : 255); int bitPos = (int)(bitsWritten % 8); int bufferPos = (int)(bitsWritten / 8); - for (int i = 0; i < scanLine.Length; i++) + for (nint i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != white) + if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From a26bfe01236444c9b2129bb53d2df3f03a0b1a45 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 09:01:28 +0200 Subject: [PATCH 35/62] Access data span only for new byte, not for every bit --- .../Decompressors/ModifiedHuffmanBitReader.cs | 3 +- .../Compression/Decompressors/T4BitReader.cs | 36 ++++++++++++++----- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index 89cdf7ea2b..4a87e2951e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -57,8 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (remainder != 0) { // Skip padding bits, move to next byte. - this.Position++; - this.ResetBitsRead(); + this.AdvancePosition(); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 15e2b3c307..1053ca107b 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -229,12 +229,20 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.RunLength = 0; this.eolPadding = eolPadding; + Span dataSpan = this.Data.GetSpan(); + this.DataAtPosition = dataSpan[(int)this.Position]; + if (this.eolPadding) { this.maxCodeLength = 24; } } + /// + /// Gets or sets the byte at the given position. + /// + private byte DataAtPosition { get; set; } + /// /// Gets the current value. /// @@ -453,6 +461,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return v; } + /// + /// Advances the position by one byte. + /// + /// True, if data could be advanced by one byte. + protected bool AdvancePosition() + { + this.LoadNewByte(); + if (this.Position < (ulong)this.DataLength) + { + Span dataSpan = this.Data.GetSpan(); + this.DataAtPosition = Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); + return true; + } + + return false; + } + private uint WhiteTerminatingCodeRunLength() { switch (this.CurValueBitsRead) @@ -811,13 +836,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (this.BitsRead >= 8) { - this.LoadNewByte(); + this.AdvancePosition(); } - Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.BitsRead - 1; - ref byte dataAtPosition = ref Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); - uint bit = (uint)((dataAtPosition & (1 << shift)) != 0 ? 1 : 0); + uint bit = (uint)((this.DataAtPosition & (1 << shift)) != 0 ? 1 : 0); this.BitsRead++; return bit; @@ -827,11 +850,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { this.Position++; this.ResetBitsRead(); - - if (this.Position >= (ulong)this.DataLength) - { - TiffThrowHelper.ThrowImageFormatException("tiff image has invalid ccitt compressed data"); - } } private void ReadImageDataFromStream(Stream input, int bytesToRead) From f0f08e83b497769faf1a97480b9b27fde58e4c61 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 09:27:56 +0200 Subject: [PATCH 36/62] Use Numerics.Modulo8 --- .../Formats/Tiff/Compression/BitWriterUtils.cs | 6 +++--- .../ModifiedHuffmanTiffCompression.cs | 12 ++++++------ .../Compression/Decompressors/T4TiffCompression.cs | 6 +++--- .../Compression/Decompressors/T6TiffCompression.cs | 14 +++++++------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 7394577a52..2a9efa59bb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -9,12 +9,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { - public static void WriteBits(Span buffer, int pos, uint count, byte value) + public static void WriteBits(Span buffer, int pos, int count, byte value) { - int bitPos = pos % 8; + int bitPos = Numerics.Modulo8(pos); int bufferPos = pos / 8; int startIdx = bufferPos + bitPos; - int endIdx = (int)(startIdx + count); + int endIdx = startIdx + count; if (value == 1) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 453f7d10dd..c45587a728 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator); buffer.Clear(); - uint bitsWritten = 0; + int bitsWritten = 0; uint pixelsWritten = 0; while (bitReader.HasMoreData) { @@ -55,14 +55,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.blackValue); } - bitsWritten += bitReader.RunLength; + bitsWritten += (int)bitReader.RunLength; pixelsWritten += bitReader.RunLength; } @@ -72,10 +72,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors pixelsWritten = 0; // Write padding bits, if necessary. - uint pad = 8 - (bitsWritten % 8); + int pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 158cac9471..9bb53e29ae 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors uint pad = 8 - (bitsWritten % 8); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, pad, 0); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)pad, 0); bitsWritten += pad; } @@ -104,11 +104,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.blackValue); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index e9921418d5..6a0bb00fc4 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); - uint bitsWritten = 0; + int bitsWritten = 0; for (int y = 0; y < height; y++) { scanLine.Clear(); @@ -73,10 +73,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private uint WriteScanLine(Span buffer, Span scanLine, uint bitsWritten) + private int WriteScanLine(Span buffer, Span scanLine, int bitsWritten) { - int bitPos = (int)(bitsWritten % 8); - int bufferPos = (int)(bitsWritten / 8); + int bitPos = Numerics.Modulo8(bitsWritten); + int bufferPos = bitsWritten / 8; for (nint i = 0; i < scanLine.Length; i++) { if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) @@ -95,11 +95,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Write padding bytes, if necessary. - uint remainder = bitsWritten % 8; + int remainder = bitsWritten % 8; if (remainder != 0) { - uint padding = 8 - remainder; - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, padding, 0); + int padding = 8 - remainder; + BitWriterUtils.WriteBits(buffer, bitsWritten, padding, 0); bitsWritten += padding; } From 62de4589d54cf3498cc72c1c6a158edbccda7474 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 13:38:16 +0200 Subject: [PATCH 37/62] Stream -> BufferedReadStream --- .../Compression/Decompressors/ModifiedHuffmanBitReader.cs | 4 ++-- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 3 ++- .../Formats/Tiff/Compression/Decompressors/T6BitReader.cs | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index 4a87e2951e..de3f3569ea 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -1,8 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.IO; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The logical order of bits within a byte. /// The number of bytes to read from the stream. /// The memory allocator. - public ModifiedHuffmanBitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) + public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 1053ca107b..89dd7a50ec 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -8,6 +8,7 @@ using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -211,7 +212,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The number of bytes to read from the stream. /// The memory allocator. /// Indicates, if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary. Defaults to false. - public T4BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) + public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) { this.fillOrder = fillOrder; this.Data = allocator.Allocate(bytesToRead); diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index e747718a44..160dbd9fcb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -2,9 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System.Collections.Generic; -using System.IO; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The logical order of bits within a byte. /// The number of bytes to read from the stream. /// The memory allocator. - public T6BitReader(Stream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) + public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) : base(input, fillOrder, bytesToRead, allocator) { } From 962466144ae3e4822679be6281dd6f9c637cd845 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 19:59:30 +0200 Subject: [PATCH 38/62] Avoid reading all compressed data at once: instead read byte by byte from the stream to avoid allocation --- .../Decompressors/ModifiedHuffmanBitReader.cs | 10 ++- .../ModifiedHuffmanTiffCompression.cs | 12 +++- .../Compression/Decompressors/T4BitReader.cs | 70 +++++++++---------- .../Decompressors/T4TiffCompression.cs | 31 ++++---- .../Compression/Decompressors/T6BitReader.cs | 6 +- .../Decompressors/T6TiffCompression.cs | 2 +- 6 files changed, 69 insertions(+), 62 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs index de3f3569ea..8306e91a34 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanBitReader.cs @@ -3,7 +3,6 @@ using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -19,14 +18,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. - public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) - : base(input, fillOrder, bytesToRead, allocator) + public ModifiedHuffmanBitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead) + : base(input, fillOrder, bytesToRead) { } /// - public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); + public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < 6; /// public override bool IsEndOfScanLine @@ -53,7 +51,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { base.StartNewRow(); - int remainder = this.BitsRead & 7; // bit-hack for % 8 + int remainder = Numerics.Modulo8(this.BitsRead); if (remainder != 0) { // Skip padding bits, move to next byte. diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index c45587a728..4ec989742c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -42,11 +42,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { - using var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount, this.Allocator); + var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount); buffer.Clear(); int bitsWritten = 0; uint pixelsWritten = 0; + nint rowsWritten = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (pixelsWritten == this.Width) { - bitReader.StartNewRow(); + rowsWritten++; pixelsWritten = 0; // Write padding bits, if necessary. @@ -78,6 +79,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } + + if (rowsWritten >= stripHeight) + { + break; + } + + bitReader.StartNewRow(); } if (pixelsWritten > this.Width) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 89dd7a50ec..c46066a3a8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -1,22 +1,17 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; using System.Collections.Generic; -using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { /// /// Bitreader for reading compressed CCITT T4 1D data. /// - internal class T4BitReader : IDisposable + internal class T4BitReader { /// /// The logical order of bits within a byte. @@ -204,20 +199,22 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { 0x54, 1408 }, { 0x55, 1472 }, { 0x5A, 1536 }, { 0x5B, 1600 }, { 0x64, 1664 }, { 0x65, 1728 } }; + /// + /// The compressed input stream. + /// + private readonly BufferedReadStream stream; + /// /// Initializes a new instance of the class. /// /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. /// Indicates, if fill bits have been added as necessary before EOL codes such that EOL always ends on a byte boundary. Defaults to false. - public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator, bool eolPadding = false) + public T4BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, bool eolPadding = false) { + this.stream = input; this.fillOrder = fillOrder; - this.Data = allocator.Allocate(bytesToRead); - this.ReadImageDataFromStream(input, bytesToRead); - this.DataLength = bytesToRead; this.BitsRead = 0; this.Value = 0; @@ -230,8 +227,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.RunLength = 0; this.eolPadding = eolPadding; - Span dataSpan = this.Data.GetSpan(); - this.DataAtPosition = dataSpan[(int)this.Position]; + this.ReadNextByte(); if (this.eolPadding) { @@ -269,11 +265,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// protected ulong Position { get; set; } - /// - /// Gets the compressed image data. - /// - public IMemoryOwner Data { get; } - /// /// Gets a value indicating whether there is more data to read left. /// @@ -400,9 +391,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors this.terminationCodeFound = false; } - /// - public void Dispose() => this.Data.Dispose(); - /// /// An EOL is expected before the first data. /// @@ -465,14 +453,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Advances the position by one byte. /// - /// True, if data could be advanced by one byte. + /// True, if data could be advanced by one byte, otherwise false. protected bool AdvancePosition() { - this.LoadNewByte(); - if (this.Position < (ulong)this.DataLength) + if (this.LoadNewByte()) { - Span dataSpan = this.Data.GetSpan(); - this.DataAtPosition = Unsafe.Add(ref MemoryMarshal.GetReference(dataSpan), (int)this.Position); return true; } @@ -833,6 +818,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return false; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private uint GetBit() { if (this.BitsRead >= 8) @@ -847,24 +833,34 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors return bit; } - private void LoadNewByte() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool LoadNewByte() { + if (this.Position < (ulong)this.DataLength) + { + this.ReadNextByte(); + this.Position++; + return true; + } + this.Position++; - this.ResetBitsRead(); + this.DataAtPosition = 0; + return false; } - private void ReadImageDataFromStream(Stream input, int bytesToRead) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ReadNextByte() { - Span dataSpan = this.Data.GetSpan(); - input.Read(dataSpan, 0, bytesToRead); - - if (this.fillOrder == TiffFillOrder.LeastSignificantBitFirst) + int nextByte = this.stream.ReadByte(); + if (nextByte == -1) { - for (int i = 0; i < dataSpan.Length; i++) - { - dataSpan[i] = ReverseBits(dataSpan[i]); - } + TiffThrowHelper.ThrowImageFormatException("Tiff fax compression error: not enough data."); } + + this.ResetBitsRead(); + this.DataAtPosition = this.fillOrder == TiffFillOrder.LeastSignificantBitFirst + ? ReverseBits((byte)nextByte) + : (byte)nextByte; } // http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 9bb53e29ae..254cb2ab02 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -61,11 +61,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } bool eolPadding = this.faxCompressionOptions.HasFlag(FaxCompressionOptions.EolPadding); - using var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, this.Allocator, eolPadding); + var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, eolPadding); buffer.Clear(); - uint bitsWritten = 0; - uint pixelWritten = 0; + int bitsWritten = 0; + uint pixelsWritten = 0; + nint rowsWritten = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -74,41 +75,47 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { this.WritePixelRun(buffer, bitReader, bitsWritten); - bitsWritten += bitReader.RunLength; - pixelWritten += bitReader.RunLength; + bitsWritten += (int)bitReader.RunLength; + pixelsWritten += bitReader.RunLength; } if (bitReader.IsEndOfScanLine) { // Write padding bytes, if necessary. - uint pad = 8 - (bitsWritten % 8); + int pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)pad, 0); + BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); bitsWritten += pad; } - pixelWritten = 0; + pixelsWritten = 0; + rowsWritten++; + + if (rowsWritten >= stripHeight) + { + break; + } } } // Edge case for when we are at the last byte, but there are still some unwritten pixels left. - if (pixelWritten > 0 && pixelWritten < this.width) + if (pixelsWritten > 0 && pixelsWritten < this.width) { bitReader.ReadNextRun(); this.WritePixelRun(buffer, bitReader, bitsWritten); } } - private void WritePixelRun(Span buffer, T4BitReader bitReader, uint bitsWritten) + private void WritePixelRun(Span buffer, T4BitReader bitReader, int bitsWritten) { if (bitReader.IsWhiteRun) { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.whiteValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.whiteValue); } else { - BitWriterUtils.WriteBits(buffer, (int)bitsWritten, (int)bitReader.RunLength, this.blackValue); + BitWriterUtils.WriteBits(buffer, bitsWritten, (int)bitReader.RunLength, this.blackValue); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index 160dbd9fcb..a3ac0ca2b5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; -using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { @@ -56,9 +55,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// The compressed input stream. /// The logical order of bits within a byte. /// The number of bytes to read from the stream. - /// The memory allocator. - public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead, MemoryAllocator allocator) - : base(input, fillOrder, bytesToRead, allocator) + public T6BitReader(BufferedReadStream input, TiffFillOrder fillOrder, int bytesToRead) + : base(input, fillOrder, bytesToRead) { } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 6a0bb00fc4..495fba0370 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors Span scanLine = scanLineBuffer.GetSpan().Slice(0, this.width); Span referenceScanLineSpan = scanLineBuffer.GetSpan().Slice(this.width, this.width); - using var bitReader = new T6BitReader(stream, this.FillOrder, byteCount, this.Allocator); + var bitReader = new T6BitReader(stream, this.FillOrder, byteCount); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); int bitsWritten = 0; From 98b96fe0a9610f81549273d922b93f67d13ab242 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 20:29:52 +0200 Subject: [PATCH 39/62] Use nint --- src/ImageSharp/Common/Helpers/Numerics.cs | 6 +++++ .../Tiff/Compression/BitWriterUtils.cs | 22 +++++++++---------- .../ModifiedHuffmanTiffCompression.cs | 10 ++++----- .../Decompressors/T4TiffCompression.cs | 10 ++++----- .../Decompressors/T6TiffCompression.cs | 12 +++++----- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs index bfbaa1b31c..301bef297f 100644 --- a/src/ImageSharp/Common/Helpers/Numerics.cs +++ b/src/ImageSharp/Common/Helpers/Numerics.cs @@ -75,6 +75,12 @@ namespace SixLabors.ImageSharp [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Modulo8(int x) => x & 7; + /// + /// Calculates % 8 + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint Modulo8(nint x) => x & 7; + /// /// Fast (x mod m) calculator, with the restriction that /// should be power of 2. diff --git a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs index 2a9efa59bb..37e2a6efcb 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs @@ -9,16 +9,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { internal static class BitWriterUtils { - public static void WriteBits(Span buffer, int pos, int count, byte value) + public static void WriteBits(Span buffer, nint pos, nint count, byte value) { - int bitPos = Numerics.Modulo8(pos); - int bufferPos = pos / 8; - int startIdx = bufferPos + bitPos; - int endIdx = startIdx + count; + nint bitPos = Numerics.Modulo8(pos); + nint bufferPos = pos / 8; + nint startIdx = bufferPos + bitPos; + nint endIdx = startIdx + count; if (value == 1) { - for (int i = startIdx; i < endIdx; i++) + for (nint i = startIdx; i < endIdx; i++) { WriteBit(buffer, bufferPos, bitPos); @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } else { - for (int i = startIdx; i < endIdx; i++) + for (nint i = startIdx; i < endIdx; i++) { WriteZeroBit(buffer, bufferPos, bitPos); @@ -47,17 +47,17 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteBit(Span buffer, int bufferPos, int bitPos) + public static void WriteBit(Span buffer, nint bufferPos, nint bitPos) { ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); - b |= (byte)(1 << (7 - bitPos)); + b |= (byte)(1 << (int)(7 - bitPos)); } [MethodImpl(InliningOptions.ShortMethod)] - public static void WriteZeroBit(Span buffer, int bufferPos, int bitPos) + public static void WriteZeroBit(Span buffer, nint bufferPos, nint bitPos) { ref byte b = ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), bufferPos); - b = (byte)(b & ~(1 << (7 - bitPos))); + b = (byte)(b & ~(1 << (int)(7 - bitPos))); } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 4ec989742c..54c94525c5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -45,8 +45,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new ModifiedHuffmanBitReader(stream, this.FillOrder, byteCount); buffer.Clear(); - int bitsWritten = 0; - uint pixelsWritten = 0; + nint bitsWritten = 0; + nuint pixelsWritten = 0; nint rowsWritten = 0; while (bitReader.HasMoreData) { @@ -67,13 +67,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors pixelsWritten += bitReader.RunLength; } - if (pixelsWritten == this.Width) + if (pixelsWritten == (ulong)this.Width) { rowsWritten++; pixelsWritten = 0; // Write padding bits, if necessary. - int pad = 8 - Numerics.Modulo8(bitsWritten); + nint pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); @@ -88,7 +88,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors bitReader.StartNewRow(); } - if (pixelsWritten > this.Width) + if (pixelsWritten > (ulong)this.Width) { TiffThrowHelper.ThrowImageFormatException("ccitt compression parsing error, decoded more pixels then image width"); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 254cb2ab02..7b59e71173 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -64,8 +64,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new T4BitReader(stream, this.FillOrder, byteCount, eolPadding); buffer.Clear(); - int bitsWritten = 0; - uint pixelsWritten = 0; + nint bitsWritten = 0; + nuint pixelsWritten = 0; nint rowsWritten = 0; while (bitReader.HasMoreData) { @@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors if (bitReader.IsEndOfScanLine) { // Write padding bytes, if necessary. - int pad = 8 - Numerics.Modulo8(bitsWritten); + nint pad = 8 - Numerics.Modulo8(bitsWritten); if (pad != 8) { BitWriterUtils.WriteBits(buffer, bitsWritten, pad, 0); @@ -100,14 +100,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Edge case for when we are at the last byte, but there are still some unwritten pixels left. - if (pixelsWritten > 0 && pixelsWritten < this.width) + if (pixelsWritten > 0 && pixelsWritten < (ulong)this.width) { bitReader.ReadNextRun(); this.WritePixelRun(buffer, bitReader, bitsWritten); } } - private void WritePixelRun(Span buffer, T4BitReader bitReader, int bitsWritten) + private void WritePixelRun(Span buffer, T4BitReader bitReader, nint bitsWritten) { if (bitReader.IsWhiteRun) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 495fba0370..890eecbffd 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -60,7 +60,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors var bitReader = new T6BitReader(stream, this.FillOrder, byteCount); var referenceScanLine = new CcittReferenceScanline(this.isWhiteZero, this.width); - int bitsWritten = 0; + nint bitsWritten = 0; for (int y = 0; y < height; y++) { scanLine.Clear(); @@ -73,10 +73,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private int WriteScanLine(Span buffer, Span scanLine, int bitsWritten) + private nint WriteScanLine(Span buffer, Span scanLine, nint bitsWritten) { - int bitPos = Numerics.Modulo8(bitsWritten); - int bufferPos = bitsWritten / 8; + nint bitPos = Numerics.Modulo8(bitsWritten); + nint bufferPos = bitsWritten / 8; for (nint i = 0; i < scanLine.Length; i++) { if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) @@ -95,10 +95,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } // Write padding bytes, if necessary. - int remainder = bitsWritten % 8; + nint remainder = Numerics.Modulo8(bitsWritten); if (remainder != 0) { - int padding = 8 - remainder; + nint padding = 8 - remainder; BitWriterUtils.WriteBits(buffer, bitsWritten, padding, 0); bitsWritten += padding; } From d5a3de1184f3efe8befa2e9ac1ac62c39025b235 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 30 May 2022 21:43:28 +0200 Subject: [PATCH 40/62] Avoid using Dictionary's --- .../Decompressors/CcittTwoDimensionalCode.cs | 12 ++- .../Compression/Decompressors/T6BitReader.cs | 98 +++++++++++-------- .../Decompressors/T6TiffCompression.cs | 3 +- 3 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs index 74a17b9075..9543499d7f 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/CcittTwoDimensionalCode.cs @@ -13,15 +13,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// Initializes a new instance of the struct. /// - /// The type. + /// The code word. + /// The type of the code. /// The bits required. /// The extension bits. - public CcittTwoDimensionalCode(CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) - => this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); + public CcittTwoDimensionalCode(int code, CcittTwoDimensionalCodeType type, int bitsRequired, int extensionBits = 0) + { + this.Code = code; + this.value = (ushort)((byte)type | ((bitsRequired & 0b1111) << 8) | ((extensionBits & 0b111) << 11)); + } /// /// Gets the code type. /// public CcittTwoDimensionalCodeType Type => (CcittTwoDimensionalCodeType)(this.value & 0b11111111); + + public int Code { get; } } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs index a3ac0ca2b5..0d068bb6fd 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6BitReader.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using System.Collections.Generic; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.IO; @@ -16,38 +15,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { private readonly int maxCodeLength = 12; - private static readonly CcittTwoDimensionalCode None = new(CcittTwoDimensionalCodeType.None, 0); + private static readonly CcittTwoDimensionalCode None = new(0, CcittTwoDimensionalCodeType.None, 0); - private static readonly Dictionary Len1Codes = new() - { - { 0b1, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Vertical0, 1) } - }; + private static readonly CcittTwoDimensionalCode Len1Code1 = new(0b1, CcittTwoDimensionalCodeType.Vertical0, 1); - private static readonly Dictionary Len3Codes = new() - { - { 0b001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Horizontal, 3) }, - { 0b010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL1, 3) }, - { 0b011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR1, 3) } - }; + private static readonly CcittTwoDimensionalCode Len3Code001 = new(0b001, CcittTwoDimensionalCodeType.Horizontal, 3); + private static readonly CcittTwoDimensionalCode Len3Code010 = new(0b010, CcittTwoDimensionalCodeType.VerticalL1, 3); + private static readonly CcittTwoDimensionalCode Len3Code011 = new(0b011, CcittTwoDimensionalCodeType.VerticalR1, 3); - private static readonly Dictionary Len4Codes = new() - { - { 0b0001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Pass, 4) } - }; + private static readonly CcittTwoDimensionalCode Len4Code0001 = new(0b0001, CcittTwoDimensionalCodeType.Pass, 4); - private static readonly Dictionary Len6Codes = new() - { - { 0b000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR2, 6) }, - { 0b000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL2, 6) } - }; + private static readonly CcittTwoDimensionalCode Len6Code000011 = new(0b000011, CcittTwoDimensionalCodeType.VerticalR2, 6); + private static readonly CcittTwoDimensionalCode Len6Code000010 = new(0b000010, CcittTwoDimensionalCodeType.VerticalL2, 6); - private static readonly Dictionary Len7Codes = new() - { - { 0b0000011, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalR3, 7) }, - { 0b0000010, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.VerticalL3, 7) }, - { 0b0000001, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions2D, 7) }, - { 0b0000000, new CcittTwoDimensionalCode(CcittTwoDimensionalCodeType.Extensions1D, 7) } - }; + private static readonly CcittTwoDimensionalCode Len7Code0000011 = new(0b0000011, CcittTwoDimensionalCodeType.VerticalR3, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000010 = new(0b0000010, CcittTwoDimensionalCodeType.VerticalL3, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000001 = new(0b0000001, CcittTwoDimensionalCodeType.Extensions2D, 7); + private static readonly CcittTwoDimensionalCode Len7Code0000000 = new(0b0000000, CcittTwoDimensionalCodeType.Extensions1D, 7); /// /// Initializes a new instance of the class. @@ -61,7 +45,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } /// - public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || ((uint)(this.BitsRead - 1) < (7 - 1)); + public override bool HasMoreData => this.Position < (ulong)this.DataLength - 1 || (uint)(this.BitsRead - 1) < (7 - 1); /// /// Gets or sets the two dimensional code. @@ -84,45 +68,81 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors switch (this.CurValueBitsRead) { case 1: - if (Len1Codes.ContainsKey(value)) + if (value == Len1Code1.Code) { - this.Code = Len1Codes[value]; + this.Code = Len1Code1; return false; } break; case 3: - if (Len3Codes.ContainsKey(value)) + if (value == Len3Code001.Code) { - this.Code = Len3Codes[value]; + this.Code = Len3Code001; + return false; + } + + if (value == Len3Code010.Code) + { + this.Code = Len3Code010; + return false; + } + + if (value == Len3Code011.Code) + { + this.Code = Len3Code011; return false; } break; case 4: - if (Len4Codes.ContainsKey(value)) + if (value == Len4Code0001.Code) { - this.Code = Len4Codes[value]; + this.Code = Len4Code0001; return false; } break; case 6: - if (Len6Codes.ContainsKey(value)) + if (value == Len6Code000010.Code) { - this.Code = Len6Codes[value]; + this.Code = Len6Code000010; + return false; + } + + if (value == Len6Code000011.Code) + { + this.Code = Len6Code000011; return false; } break; case 7: - if (Len7Codes.ContainsKey(value)) + if (value == Len7Code0000000.Code) + { + this.Code = Len7Code0000000; + return false; + } + + if (value == Len7Code0000001.Code) + { + this.Code = Len7Code0000001; + return false; + } + + if (value == Len7Code0000011.Code) + { + this.Code = Len7Code0000011; + return false; + } + + if (value == Len7Code0000010.Code) { - this.Code = Len7Codes[value]; + this.Code = Len7Code0000010; return false; } diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 890eecbffd..7cfcfe12b6 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -77,9 +77,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors { nint bitPos = Numerics.Modulo8(bitsWritten); nint bufferPos = bitsWritten / 8; + ref byte scanLineRef = ref MemoryMarshal.GetReference(scanLine); for (nint i = 0; i < scanLine.Length; i++) { - if (Unsafe.Add(ref MemoryMarshal.GetReference(scanLine), i) != this.white) + if (Unsafe.Add(ref scanLineRef, i) != this.white) { BitWriterUtils.WriteBit(buffer, bufferPos, bitPos); } From 188b5cd2c66a45e9b4b5eb5a345227a64f9080cb Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 10:24:09 +0200 Subject: [PATCH 41/62] Use more nint --- .../BlackIsZero1TiffColor{TPixel}.cs | 10 +++++----- .../WhiteIsZero1TiffColor{TPixel}.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index 2d8e21d6cf..dd4cf873b9 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -19,21 +19,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - int offset = 0; + nint offset = 0; var colorBlack = default(TPixel); var colorWhite = default(TPixel); colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (int y = top; y < top + height; y++) + for (nint y = top; y < top + height; y++) { - Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (int x = left; x < left + width; x += 8) + for (nint x = left; x < left + width; x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - int maxShift = Math.Min(left + width - x, 8); + nint maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index ad7d6c08a9..ed8548d41d 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -18,21 +18,21 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - int offset = 0; + nint offset = 0; var colorBlack = default(TPixel); var colorWhite = default(TPixel); colorBlack.FromRgba32(Color.Black); colorWhite.FromRgba32(Color.White); ref byte dataRef = ref MemoryMarshal.GetReference(data); - for (int y = top; y < top + height; y++) + for (nint y = top; y < top + height; y++) { - Span pixelRowSpan = pixels.DangerousGetRowSpan(y); + Span pixelRowSpan = pixels.DangerousGetRowSpan((int)y); ref TPixel pixelRowRef = ref MemoryMarshal.GetReference(pixelRowSpan); - for (int x = left; x < left + width; x += 8) + for (nint x = left; x < left + width; x += 8) { byte b = Unsafe.Add(ref dataRef, offset++); - int maxShift = Math.Min(left + width - x, 8); + nint maxShift = Math.Min(left + width - x, 8); for (int shift = 0; shift < maxShift; shift++) { From 29b4647cf0f24e7de620bbf1e965e772f330997d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 14:16:51 +0200 Subject: [PATCH 42/62] Remove no longer needed Vector4.Zero workaround for netcore2.1 --- .../BlackIsZero16TiffColor{TPixel}.cs | 5 ++--- .../BlackIsZero24TiffColor{TPixel}.cs | 3 ++- .../BlackIsZero32FloatTiffColor{TPixel}.cs | 5 +---- .../BlackIsZero32TiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs | 5 ++--- .../Rgb16PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs | 5 ++--- .../Rgb24PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs | 5 ++--- .../Rgb32PlanarTiffColor{TPixel}.cs | 5 ++--- .../RgbFloat323232TiffColor{TPixel}.cs | 2 +- .../Rgba16161616TiffColor{TPixel}.cs | 4 +--- .../Rgba16PlanarTiffColor{TPixel}.cs | 5 ++--- .../Rgba24242424TiffColor{TPixel}.cs | 5 ++--- .../Rgba24PlanarTiffColor{TPixel}.cs | 5 ++--- .../Rgba32323232TiffColor{TPixel}.cs | 5 ++--- .../Rgba32PlanarTiffColor{TPixel}.cs | 5 ++--- .../PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs | 2 +- .../RgbaFloat32323232TiffColor{TPixel}.cs | 2 +- .../WhiteIsZero16TiffColor{TPixel}.cs | 3 ++- .../WhiteIsZero24TiffColor{TPixel}.cs | 3 ++- .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 2 +- .../WhiteIsZero32TiffColor{TPixel}.cs | 3 ++- src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs | 2 -- 24 files changed, 40 insertions(+), 56 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs index e605629122..6f3189e706 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,11 +33,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index 7d230dfd5e..1c942fa9b2 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs index c43b121caf..fc526a86f5 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32FloatTiffColor{TPixel}.cs @@ -3,7 +3,6 @@ using System; using System.Numerics; -using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +25,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs index 00e4caef79..b2ab127003 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero32TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs index 6093690117..0527eaaf0f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,11 +33,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs index 76fed3c93e..fc5dc82aab 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,11 +27,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs index addf576e95..4fc25f2dd0 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs index 2eda3b5af7..59bc94802f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb24PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs index 02319bfa66..b8f9da72ff 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb323232TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -25,10 +26,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs index 26f75bfcf8..8903b8a40f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb32PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -26,10 +27,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs index 7fd98dd504..c2590286bd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs index 0340438cbf..d1164b67a3 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16161616TiffColor{TPixel}.cs @@ -42,11 +42,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs index 856d810d31..51e6e21dfa 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba16PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,11 +34,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 Rgba64 rgba = TiffUtils.Rgba64Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs index 2ce30252f5..ff35a8d0dd 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24242424TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,10 +33,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs index 89172cfe72..1a3459fcb6 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba24PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,10 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs index 8ee9eb0bf9..378338de6e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32323232TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -32,10 +33,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs index c98ac1cf00..af3a888a79 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba32PlanarTiffColor{TPixel}.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -33,10 +34,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(IMemoryOwner[] data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); Span redData = data[0].GetSpan(); Span greenData = data[1].GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs index 967a68ad0c..24cf8019ce 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgba8888TiffColor{TPixel}.cs @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation bool hasAssociatedAlpha = this.extraSamplesType.HasValue && this.extraSamplesType == TiffExtraSampleType.AssociatedAlphaData; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); using IMemoryOwner vectors = hasAssociatedAlpha ? this.memoryAllocator.Allocate(width) : null; Span vectorsSpan = hasAssociatedAlpha ? vectors.GetSpan() : Span.Empty; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index f95045ec5a..f273485866 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index d509776d7d..02e95b5421 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); int offset = 0; for (int y = top; y < top + height; y++) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index fbf8130789..af66609799 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; const uint maxValue = 0xFFFFFF; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 40d1541c51..59017a6c0c 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs index fd908c1e9f..1d7ac881cb 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Numerics; using SixLabors.ImageSharp.Formats.Tiff.Utils; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); - color.FromScaledVector4(TiffUtils.Vector4Default); + color.FromScaledVector4(Vector4.Zero); const uint maxValue = 0xFFFFFFFF; int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs index 532423c4f1..2f1ddcf21c 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs @@ -18,8 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils private const float Scale32Bit = 1.0f / 0xFFFFFFFF; - public static Vector4 Vector4Default { get; } = new(0.0f, 0.0f, 0.0f, 0.0f); - public static Rgba64 Rgba64Default { get; } = new(0, 0, 0, 0); public static L16 L16Default { get; } = new(0); From affb1430f23cc1b13e7f5ddc607f5a74ddc0d76c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 14:53:30 +0200 Subject: [PATCH 43/62] Remove no longer valid comments --- .../PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs | 2 -- .../RgbFloat323232TiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs | 2 -- .../WhiteIsZero32FloatTiffColor{TPixel}.cs | 2 -- .../PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs | 2 -- 6 files changed, 12 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index 1c942fa9b2..f384705267 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs index c2590286bd..2aa810623e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbFloat323232TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs index 02e95b5421..092ba68e2e 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 L16 l16 = TiffUtils.L16Default; var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index af66609799..73c168a698 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs index 59017a6c0c..f3b72edb5f 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32FloatTiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); byte[] buffer = new byte[4]; diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs index 1d7ac881cb..48e24dedae 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero32TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); const uint maxValue = 0xFFFFFFFF; From 10ff24fa8754981d80462fa99d00117b05550bb0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 17:14:52 +0200 Subject: [PATCH 44/62] Loop unroll --- .../BlackIsZero1TiffColor{TPixel}.cs | 45 +++++++++++++++++-- .../WhiteIsZero1TiffColor{TPixel}.cs | 45 +++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs index dd4cf873b9..cf8f8fb734 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero1TiffColor{TPixel}.cs @@ -35,12 +35,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation byte b = Unsafe.Add(ref dataRef, offset++); nint maxShift = Math.Min(left + width - x, 8); - for (int shift = 0; shift < maxShift; shift++) + if (maxShift == 8) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> 7) & 1; + ref TPixel pixel0 = ref Unsafe.Add(ref pixelRowRef, x); + pixel0 = bit == 0 ? colorBlack : colorWhite; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); - pixel = bit == 0 ? colorBlack : colorWhite; + bit = (b >> 6) & 1; + ref TPixel pixel1 = ref Unsafe.Add(ref pixelRowRef, x + 1); + pixel1 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 5) & 1; + ref TPixel pixel2 = ref Unsafe.Add(ref pixelRowRef, x + 2); + pixel2 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 4) & 1; + ref TPixel pixel3 = ref Unsafe.Add(ref pixelRowRef, x + 3); + pixel3 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 3) & 1; + ref TPixel pixel4 = ref Unsafe.Add(ref pixelRowRef, x + 4); + pixel4 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 2) & 1; + ref TPixel pixel5 = ref Unsafe.Add(ref pixelRowRef, x + 5); + pixel5 = bit == 0 ? colorBlack : colorWhite; + + bit = (b >> 1) & 1; + ref TPixel pixel6 = ref Unsafe.Add(ref pixelRowRef, x + 6); + pixel6 = bit == 0 ? colorBlack : colorWhite; + + bit = b & 1; + ref TPixel pixel7 = ref Unsafe.Add(ref pixelRowRef, x + 7); + pixel7 = bit == 0 ? colorBlack : colorWhite; + } + else + { + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorBlack : colorWhite; + } } } } diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs index ed8548d41d..e3e95d9e12 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero1TiffColor{TPixel}.cs @@ -34,12 +34,49 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation byte b = Unsafe.Add(ref dataRef, offset++); nint maxShift = Math.Min(left + width - x, 8); - for (int shift = 0; shift < maxShift; shift++) + if (maxShift == 8) { - int bit = (b >> (7 - shift)) & 1; + int bit = (b >> 7) & 1; + ref TPixel pixel0 = ref Unsafe.Add(ref pixelRowRef, x); + pixel0 = bit == 0 ? colorWhite : colorBlack; - ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); - pixel = bit == 0 ? colorWhite : colorBlack; + bit = (b >> 6) & 1; + ref TPixel pixel1 = ref Unsafe.Add(ref pixelRowRef, x + 1); + pixel1 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 5) & 1; + ref TPixel pixel2 = ref Unsafe.Add(ref pixelRowRef, x + 2); + pixel2 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 4) & 1; + ref TPixel pixel3 = ref Unsafe.Add(ref pixelRowRef, x + 3); + pixel3 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 3) & 1; + ref TPixel pixel4 = ref Unsafe.Add(ref pixelRowRef, x + 4); + pixel4 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 2) & 1; + ref TPixel pixel5 = ref Unsafe.Add(ref pixelRowRef, x + 5); + pixel5 = bit == 0 ? colorWhite : colorBlack; + + bit = (b >> 1) & 1; + ref TPixel pixel6 = ref Unsafe.Add(ref pixelRowRef, x + 6); + pixel6 = bit == 0 ? colorWhite : colorBlack; + + bit = b & 1; + ref TPixel pixel7 = ref Unsafe.Add(ref pixelRowRef, x + 7); + pixel7 = bit == 0 ? colorWhite : colorBlack; + } + else + { + for (int shift = 0; shift < maxShift; shift++) + { + int bit = (b >> (7 - shift)) & 1; + + ref TPixel pixel = ref Unsafe.Add(ref pixelRowRef, x + shift); + pixel = bit == 0 ? colorWhite : colorBlack; + } } } } From 50c1aab54fef01b1e21da4cbb156a6a9da4a438c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 31 May 2022 20:22:56 +0200 Subject: [PATCH 45/62] Use stackalloc --- .../Formats/Tiff/Compression/Decompressors/T4BitReader.cs | 1 + .../BlackIsZero24TiffColor{TPixel}.cs | 4 ++-- .../WhiteIsZero24TiffColor{TPixel}.cs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index c46066a3a8..226bfe5dad 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -434,6 +434,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors /// /// The number of bits to read. /// The value read. + [MethodImpl(InliningOptions.ShortMethod)] protected uint ReadValue(int nBits) { DebugGuard.MustBeGreaterThan(nBits, 0, nameof(nBits)); diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs index f384705267..9be8dd7741 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero24TiffColor{TPixel}.cs @@ -28,10 +28,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; - Span bufferSpan = buffer.AsSpan(bufferStartIdx); + Span bufferSpan = buffer.Slice(bufferStartIdx); int offset = 0; for (int y = top; y < top + height; y++) { diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs index 73c168a698..d483d7faf8 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero24TiffColor{TPixel}.cs @@ -28,11 +28,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation { var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); - byte[] buffer = new byte[4]; + Span buffer = stackalloc byte[4]; int bufferStartIdx = this.isBigEndian ? 1 : 0; const uint maxValue = 0xFFFFFF; - Span bufferSpan = buffer.AsSpan(bufferStartIdx); + Span bufferSpan = buffer.Slice(bufferStartIdx); int offset = 0; for (int y = top; y < top + height; y++) { From 8ff29a1a3fd9fbef47153ea3bdd42d471ecc482b Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Wed, 1 Jun 2022 22:54:30 +0200 Subject: [PATCH 46/62] PackBitsTiffCompression: Use Slice.Fill to repeat the data --- .../Decompressors/PackBitsTiffCompression.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 4093d89871..d7bba88fd9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors byte repeatData = compressedData[compressedOffset + 1]; int repeatLength = 257 - headerByte; - ArrayCopyRepeat(repeatData, buffer, decompressedOffset, repeatLength); + buffer.Slice(decompressedOffset, repeatLength).Fill(repeatData); compressedOffset += 2; decompressedOffset += repeatLength; @@ -81,14 +81,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors } } - private static void ArrayCopyRepeat(byte value, Span destinationArray, int destinationIndex, int length) - { - for (int i = 0; i < length; i++) - { - destinationArray[i + destinationIndex] = value; - } - } - /// protected override void Dispose(bool disposing) => this.compressedDataMemory?.Dispose(); } From 7434643eeed5493e6d2aab23751d8d1626fa2d77 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Thu, 2 Jun 2022 00:37:09 +0200 Subject: [PATCH 47/62] Remove no longer valid comment Co-authored-by: Berkan Diler --- .../RgbaFloat32323232TiffColor{TPixel}.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs index f273485866..317c2db3b3 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/RgbaFloat32323232TiffColor{TPixel}.cs @@ -26,8 +26,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation /// public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height) { - // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those, - // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623 var color = default(TPixel); color.FromScaledVector4(Vector4.Zero); int offset = 0; From 1b7f800c074deebb62d580d203b228ef235874a0 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Jun 2022 11:45:54 +0200 Subject: [PATCH 48/62] Minor: Another instance of use Numerics.Modulo8 instead of % 8 --- .../Tiff/Compression/Compressors/TiffCcittCompressor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs index 3166106216..052b01f279 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs @@ -442,16 +442,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors } /// - /// Pads output to the next byte + /// Pads output to the next byte. /// /// /// If the output is not currently on a byte boundary, - /// zero-pad it to the next byte + /// zero-pad it to the next byte. /// protected void PadByte() { // Check if padding is necessary. - if (this.bitPosition % 8 != 0) + if (Numerics.Modulo8(this.bitPosition) != 0) { // Skip padding bits, move to next byte. this.bytePosition++; From 34584b9b3f2f3732bc1cd7cc4c11b8be27cff265 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 2 Jun 2022 12:07:56 +0200 Subject: [PATCH 49/62] Add another test case for Fax4 compressed with min is black --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 2 ++ tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index b9569c7a46..7987d76a3f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -611,6 +611,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [Theory] [WithFile(Fax4Compressed, PixelTypes.Rgba32)] + [WithFile(Fax4Compressed2, PixelTypes.Rgba32)] + [WithFile(Fax4CompressedMinIsBlack, PixelTypes.Rgba32)] [WithFile(Fax4CompressedLowerOrderBitsFirst, PixelTypes.Rgba32)] [WithFile(Calliphora_Fax4Compressed, PixelTypes.Rgba32)] public void TiffDecoder_CanDecode_Fax4Compressed(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1f6da39b29..ace20c2e4e 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -762,6 +762,7 @@ namespace SixLabors.ImageSharp.Tests public const string Fax4Compressed = "Tiff/basi3p02_fax4.tiff"; public const string Fax4Compressed2 = "Tiff/CCITTGroup4.tiff"; public const string Fax4CompressedLowerOrderBitsFirst = "Tiff/basi3p02_fax4_lowerOrderBitsFirst.tiff"; + public const string Fax4CompressedMinIsBlack = "Tiff/CCITTGroup4_minisblack.tiff"; public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff"; public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff"; diff --git a/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff b/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff new file mode 100644 index 0000000000..8ae4647537 --- /dev/null +++ b/tests/Images/Input/Tiff/CCITTGroup4_minisblack.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c12c96059a6214739fe836572bce6912dcf4a0d2ff389c840f0d2daa4465f55 +size 11637 From 035c31e826d8e0d7199e9124bae46c668fd4eda5 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:31:16 +0200 Subject: [PATCH 50/62] Make sure 1 Bit compression is only used with 1 bit pixel type --- .../Compressors/TiffCcittCompressor.cs | 54 +++++++++---------- .../Formats/Tiff/TiffEncoderCore.cs | 31 +++++++---- .../Formats/Tiff/TiffEncoderTests.cs | 22 ++++++++ 3 files changed, 71 insertions(+), 36 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs index 052b01f279..f92cf1822a 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffCcittCompressor.cs @@ -23,28 +23,28 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors 64, 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1088, 1152, 1216, 1280, 1344, 1408, 1472, 1536, 1600, 1664, 1728, 1792, 1856, 1920, 1984, 2048, 2112, 2176, 2240, 2304, 2368, 2432, 2496, 2560 }; - private static readonly Dictionary WhiteLen4TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen4TermCodes = new() { { 2, 0x7 }, { 3, 0x8 }, { 4, 0xB }, { 5, 0xC }, { 6, 0xE }, { 7, 0xF } }; - private static readonly Dictionary WhiteLen5TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen5TermCodes = new() { { 8, 0x13 }, { 9, 0x14 }, { 10, 0x7 }, { 11, 0x8 } }; - private static readonly Dictionary WhiteLen6TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen6TermCodes = new() { { 1, 0x7 }, { 12, 0x8 }, { 13, 0x3 }, { 14, 0x34 }, { 15, 0x35 }, { 16, 0x2A }, { 17, 0x2B } }; - private static readonly Dictionary WhiteLen7TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen7TermCodes = new() { { 18, 0x27 }, { 19, 0xC }, { 20, 0x8 }, { 21, 0x17 }, { 22, 0x3 }, { 23, 0x4 }, { 24, 0x28 }, { 25, 0x2B }, { 26, 0x13 }, { 27, 0x24 }, { 28, 0x18 } }; - private static readonly Dictionary WhiteLen8TermCodes = new Dictionary() + private static readonly Dictionary WhiteLen8TermCodes = new() { { 0, WhiteZeroRunTermCode }, { 29, 0x2 }, { 30, 0x3 }, { 31, 0x1A }, { 32, 0x1B }, { 33, 0x12 }, { 34, 0x13 }, { 35, 0x14 }, { 36, 0x15 }, { 37, 0x16 }, { 38, 0x17 }, { 39, 0x28 }, { 40, 0x29 }, { 41, 0x2A }, { 42, 0x2B }, { 43, 0x2C }, { 44, 0x2D }, @@ -53,57 +53,57 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { 63, 0x34 } }; - private static readonly Dictionary BlackLen2TermCodes = new Dictionary() + private static readonly Dictionary BlackLen2TermCodes = new() { { 2, 0x3 }, { 3, 0x2 } }; - private static readonly Dictionary BlackLen3TermCodes = new Dictionary() + private static readonly Dictionary BlackLen3TermCodes = new() { { 1, 0x2 }, { 4, 0x3 } }; - private static readonly Dictionary BlackLen4TermCodes = new Dictionary() + private static readonly Dictionary BlackLen4TermCodes = new() { { 5, 0x3 }, { 6, 0x2 } }; - private static readonly Dictionary BlackLen5TermCodes = new Dictionary() + private static readonly Dictionary BlackLen5TermCodes = new() { { 7, 0x3 } }; - private static readonly Dictionary BlackLen6TermCodes = new Dictionary() + private static readonly Dictionary BlackLen6TermCodes = new() { { 8, 0x5 }, { 9, 0x4 } }; - private static readonly Dictionary BlackLen7TermCodes = new Dictionary() + private static readonly Dictionary BlackLen7TermCodes = new() { { 10, 0x4 }, { 11, 0x5 }, { 12, 0x7 } }; - private static readonly Dictionary BlackLen8TermCodes = new Dictionary() + private static readonly Dictionary BlackLen8TermCodes = new() { { 13, 0x4 }, { 14, 0x7 } }; - private static readonly Dictionary BlackLen9TermCodes = new Dictionary() + private static readonly Dictionary BlackLen9TermCodes = new() { { 15, 0x18 } }; - private static readonly Dictionary BlackLen10TermCodes = new Dictionary() + private static readonly Dictionary BlackLen10TermCodes = new() { { 0, BlackZeroRunTermCode }, { 16, 0x17 }, { 17, 0x18 }, { 18, 0x8 } }; - private static readonly Dictionary BlackLen11TermCodes = new Dictionary() + private static readonly Dictionary BlackLen11TermCodes = new() { { 19, 0x67 }, { 20, 0x68 }, { 21, 0x6C }, { 22, 0x37 }, { 23, 0x28 }, { 24, 0x17 }, { 25, 0x18 } }; - private static readonly Dictionary BlackLen12TermCodes = new Dictionary() + private static readonly Dictionary BlackLen12TermCodes = new() { { 26, 0xCA }, { 27, 0xCB }, { 28, 0xCC }, { 29, 0xCD }, { 30, 0x68 }, { 31, 0x69 }, { 32, 0x6A }, { 33, 0x6B }, { 34, 0xD2 }, { 35, 0xD3 }, { 36, 0xD4 }, { 37, 0xD5 }, { 38, 0xD6 }, { 39, 0xD7 }, { 40, 0x6C }, { 41, 0x6D }, { 42, 0xDA }, { 43, 0xDB }, @@ -112,62 +112,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors { 62, 0x66 }, { 63, 0x67 } }; - private static readonly Dictionary WhiteLen5MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen5MakeupCodes = new() { { 64, 0x1B }, { 128, 0x12 } }; - private static readonly Dictionary WhiteLen6MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen6MakeupCodes = new() { { 192, 0x17 }, { 1664, 0x18 } }; - private static readonly Dictionary WhiteLen8MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen8MakeupCodes = new() { { 320, 0x36 }, { 384, 0x37 }, { 448, 0x64 }, { 512, 0x65 }, { 576, 0x68 }, { 640, 0x67 } }; - private static readonly Dictionary WhiteLen7MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen7MakeupCodes = new() { { 256, 0x37 } }; - private static readonly Dictionary WhiteLen9MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen9MakeupCodes = new() { { 704, 0xCC }, { 768, 0xCD }, { 832, 0xD2 }, { 896, 0xD3 }, { 960, 0xD4 }, { 1024, 0xD5 }, { 1088, 0xD6 }, { 1152, 0xD7 }, { 1216, 0xD8 }, { 1280, 0xD9 }, { 1344, 0xDA }, { 1408, 0xDB }, { 1472, 0x98 }, { 1536, 0x99 }, { 1600, 0x9A }, { 1728, 0x9B } }; - private static readonly Dictionary WhiteLen11MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen11MakeupCodes = new() { { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } }; - private static readonly Dictionary WhiteLen12MakeupCodes = new Dictionary() + private static readonly Dictionary WhiteLen12MakeupCodes = new() { { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } }; - private static readonly Dictionary BlackLen10MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen10MakeupCodes = new() { { 64, 0xF } }; - private static readonly Dictionary BlackLen11MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen11MakeupCodes = new() { { 1792, 0x8 }, { 1856, 0xC }, { 1920, 0xD } }; - private static readonly Dictionary BlackLen12MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen12MakeupCodes = new() { { 128, 0xC8 }, { 192, 0xC9 }, { 256, 0x5B }, { 320, 0x33 }, { 384, 0x34 }, { 448, 0x35 }, { 1984, 0x12 }, { 2048, 0x13 }, { 2112, 0x14 }, { 2176, 0x15 }, { 2240, 0x16 }, { 2304, 0x17 }, { 2368, 0x1C }, { 2432, 0x1D }, { 2496, 0x1E }, { 2560, 0x1F } }; - private static readonly Dictionary BlackLen13MakeupCodes = new Dictionary() + private static readonly Dictionary BlackLen13MakeupCodes = new() { { 512, 0x6C }, { 576, 0x6D }, { 640, 0x4A }, { 704, 0x4B }, { 768, 0x4C }, { 832, 0x4D }, { 896, 0x72 }, { 960, 0x73 }, { 1024, 0x74 }, { 1088, 0x75 }, { 1152, 0x76 }, { 1216, 0x77 }, { 1280, 0x52 }, { 1344, 0x53 }, diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 3409b3dd8a..9a953a2695 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -343,7 +343,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff switch (bitsPerPixel) { case TiffBitsPerPixel.Bit1: - if (compression == TiffCompression.Ccitt1D || compression == TiffCompression.CcittGroup3Fax || compression == TiffCompression.CcittGroup4Fax) + if (IsOneBitCompression(compression)) { // The “normal” PhotometricInterpretation for bilevel CCITT compressed data is WhiteIsZero. this.SetEncoderOptions(bitsPerPixel, TiffPhotometricInterpretation.WhiteIsZero, compression, TiffPredictor.None); @@ -375,6 +375,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff break; } + // Make sure 1 Bit compression is only used with 1 bit pixel type. + if (IsOneBitCompression(this.CompressionType) && this.BitsPerPixel != TiffBitsPerPixel.Bit1) + { + // Invalid compression / bits per pixel combination, fallback to no compression. + this.CompressionType = DefaultCompression; + } + return; } @@ -396,18 +403,14 @@ namespace SixLabors.ImageSharp.Formats.Tiff { case TiffPhotometricInterpretation.BlackIsZero: case TiffPhotometricInterpretation.WhiteIsZero: - if (this.CompressionType == TiffCompression.Ccitt1D || - this.CompressionType == TiffCompression.CcittGroup3Fax || - this.CompressionType == TiffCompression.CcittGroup4Fax) + if (IsOneBitCompression(this.CompressionType)) { this.SetEncoderOptions(TiffBitsPerPixel.Bit1, photometricInterpretation, compression, TiffPredictor.None); return; } - else - { - this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); - return; - } + + this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); + return; case TiffPhotometricInterpretation.PaletteColor: this.SetEncoderOptions(TiffBitsPerPixel.Bit8, photometricInterpretation, compression, predictor); @@ -428,5 +431,15 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.CompressionType = compression; this.HorizontalPredictor = predictor; } + + public static bool IsOneBitCompression(TiffCompression? compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) + { + return true; + } + + return false; + } } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 93ca611c9e..d5f6df4a5f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -95,6 +95,28 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff Assert.Equal(TiffBitsPerPixel.Bit24, frameMetaData.BitsPerPixel); } + [Theory] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Ccitt1D)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.CcittGroup3Fax)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.CcittGroup4Fax)] + public void EncoderOptions_WithInvalidCompressionAndPixelTypeCombination_DefaultsToRgb(TiffPhotometricInterpretation photometricInterpretation, TiffCompression compression) + { + // arrange + var tiffEncoder = new TiffEncoder { PhotometricInterpretation = photometricInterpretation, Compression = compression }; + using Image input = new Image(10, 10); + using var memStream = new MemoryStream(); + + // act + input.Save(memStream, tiffEncoder); + + // assert + memStream.Position = 0; + using var output = Image.Load(memStream); + + TiffFrameMetadata frameMetaData = output.Frames.RootFrame.Metadata.GetTiffMetadata(); + Assert.Equal(TiffBitsPerPixel.Bit24, frameMetaData.BitsPerPixel); + } + [Theory] [InlineData(null, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Deflate, TiffBitsPerPixel.Bit24, TiffCompression.Deflate)] From 5675aa82ab14ac37e620a5fb0eef4ad5df81db2e Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:38:41 +0200 Subject: [PATCH 51/62] Fix encode tiff benchmarks - Deflate and packbits are not supported by System.Drawing - When 1d Compression is chosen, TiffPhotometricInterpretation should be WhiteIsZero --- .../Codecs/Tiff/EncodeTiff.cs | 39 +++++++++++++------ 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs index e9c61e729e..da2d94a2a2 100644 --- a/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs +++ b/tests/ImageSharp.Benchmarks/Codecs/Tiff/EncodeTiff.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using System.Drawing.Imaging; using System.IO; using BenchmarkDotNet.Attributes; @@ -9,6 +10,7 @@ using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff.Constants; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests; +using SDImage = System.Drawing.Image; namespace SixLabors.ImageSharp.Benchmarks.Codecs { @@ -17,11 +19,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Config(typeof(Config.ShortMultiFramework))] public class EncodeTiff { - private System.Drawing.Image drawing; + private Stream stream; + private SDImage drawing; private Image core; - private Configuration configuration; - private string TestImageFullPath => Path.Combine(TestEnvironment.InputImagesDirectoryFullPath, this.TestImage); [Params(TestImages.Tiff.Calliphora_RgbUncompressed)] @@ -29,9 +30,11 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Params( TiffCompression.None, - TiffCompression.Deflate, + + // System.Drawing does not support Deflate or PackBits + // TiffCompression.Deflate, + // TiffCompression.PackBits, TiffCompression.Lzw, - TiffCompression.PackBits, TiffCompression.CcittGroup3Fax, TiffCompression.Ccitt1D)] public TiffCompression Compression { get; set; } @@ -39,11 +42,12 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [GlobalSetup] public void ReadImages() { - if (this.core == null) + if (this.stream == null) { - this.configuration = new Configuration(); - this.core = Image.Load(this.configuration, this.TestImageFullPath); - this.drawing = System.Drawing.Image.FromFile(this.TestImageFullPath); + this.stream = File.OpenRead(this.TestImageFullPath); + this.core = Image.Load(this.stream); + this.stream.Position = 0; + this.drawing = SDImage.FromStream(this.stream); } } @@ -70,7 +74,10 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs [Benchmark(Description = "ImageSharp Tiff")] public void TiffCore() { - TiffPhotometricInterpretation photometricInterpretation = TiffPhotometricInterpretation.Rgb; + TiffPhotometricInterpretation photometricInterpretation = + IsOneBitCompression(this.Compression) ? + TiffPhotometricInterpretation.WhiteIsZero : + TiffPhotometricInterpretation.Rgb; var encoder = new TiffEncoder() { Compression = this.Compression, PhotometricInterpretation = photometricInterpretation }; using var memoryStream = new MemoryStream(); @@ -109,8 +116,18 @@ namespace SixLabors.ImageSharp.Benchmarks.Codecs return EncoderValue.CompressionLZW; default: - throw new System.NotSupportedException(compression.ToString()); + throw new NotSupportedException(compression.ToString()); + } + } + + public static bool IsOneBitCompression(TiffCompression compression) + { + if (compression is TiffCompression.Ccitt1D or TiffCompression.CcittGroup3Fax or TiffCompression.CcittGroup4Fax) + { + return true; } + + return false; } } } From 3af2dd7720ca35060348ea0a50e4e430b9015358 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Fri, 3 Jun 2022 15:39:59 +0200 Subject: [PATCH 52/62] Multiply by inv max value instead of dividing in GeneratePalette --- .../PaletteTiffColor{TPixel}.cs | 8 +++++--- .../BlackIsZeroTiffColorTests.cs | 19 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs index ad5793084b..d0ab2383d7 100644 --- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs +++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/PaletteTiffColor{TPixel}.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation private readonly TPixel[] palette; + private const float InvMax = 1.0f / 65535F; + /// The number of bits per sample for each pixel. /// The RGB color lookup table to use for decoding the image. public PaletteTiffColor(TiffBitsPerSample bitsPerSample, ushort[] colorMap) @@ -56,9 +58,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation for (int i = 0; i < palette.Length; i++) { - float r = colorMap[rOffset + i] / 65535F; - float g = colorMap[gOffset + i] / 65535F; - float b = colorMap[bOffset + i] / 65535F; + float r = colorMap[rOffset + i] * InvMax; + float g = colorMap[gOffset + i] * InvMax; + float b = colorMap[bOffset + i] * InvMax; palette[i].FromScaledVector4(new Vector4(r, g, b, 1.0f)); } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs index 38611c6f37..d4964cf778 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/PhotometricInterpretation/BlackIsZeroTiffColorTests.cs @@ -13,14 +13,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation [Trait("Format", "Tiff")] public class BlackIsZeroTiffColorTests : PhotometricInterpretationTestBase { - private static readonly Rgba32 Gray000 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Gray128 = new Rgba32(128, 128, 128, 255); - private static readonly Rgba32 Gray255 = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Gray0 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Gray8 = new Rgba32(136, 136, 136, 255); - private static readonly Rgba32 GrayF = new Rgba32(255, 255, 255, 255); - private static readonly Rgba32 Bit0 = new Rgba32(0, 0, 0, 255); - private static readonly Rgba32 Bit1 = new Rgba32(255, 255, 255, 255); + private static readonly Rgba32 Gray000 = new(0, 0, 0, 255); + private static readonly Rgba32 Gray128 = new(128, 128, 128, 255); + private static readonly Rgba32 Gray255 = new(255, 255, 255, 255); + private static readonly Rgba32 Gray0 = new(0, 0, 0, 255); + private static readonly Rgba32 Gray8 = new(136, 136, 136, 255); + private static readonly Rgba32 GrayF = new(255, 255, 255, 255); + private static readonly Rgba32 Bit0 = new(0, 0, 0, 255); + private static readonly Rgba32 Bit1 = new(255, 255, 255, 255); private static readonly byte[] BilevelBytes4X4 = { @@ -30,8 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.PhotometricInterpretation 0b10010000 }; - private static readonly Rgba32[][] BilevelResult4X4 = new[] - { + private static readonly Rgba32[][] BilevelResult4X4 = { new[] { Bit0, Bit1, Bit0, Bit1 }, new[] { Bit1, Bit1, Bit1, Bit1 }, new[] { Bit0, Bit1, Bit1, Bit1 }, From cf86a975feb59447670bb80ffb9916c94fee15c5 Mon Sep 17 00:00:00 2001 From: Dirk Lemstra Date: Sat, 4 Jun 2022 18:08:37 +0200 Subject: [PATCH 53/62] Added dependabot config to keep github actions up to date. --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..5ace4600a1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From 08448a98ade54e13ff98479585b7d567b0d11ecf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:09:11 +0000 Subject: [PATCH 54/62] Bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 4 ++-- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 16f8ebb065..f53c650dc0 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -58,7 +58,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive @@ -148,7 +148,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 3f8a820313..9ce4c1f7a5 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -24,7 +24,7 @@ jobs: git config --global core.longpaths true - name: Git Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 submodules: recursive From 60b815761a29339c11653f4e773531a6556256ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:09:06 +0000 Subject: [PATCH 55/62] Bump actions/setup-dotnet from 1 to 2 Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 1 to 2. - [Release notes](https://github.com/actions/setup-dotnet/releases) - [Commits](https://github.com/actions/setup-dotnet/compare/v1...v2) --- updated-dependencies: - dependency-name: actions/setup-dotnet dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 2 +- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index f53c650dc0..d15b834068 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -89,7 +89,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget- - name: DotNet Setup - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: include-prerelease: true dotnet-version: | diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 9ce4c1f7a5..31ddaa6c3a 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -55,7 +55,7 @@ jobs: restore-keys: ${{ runner.os }}-nuget- - name: DotNet Setup - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: dotnet-version: | 6.0.x From a59e0f281a7a7c1934a8d48676d7e640d364774d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:09:03 +0000 Subject: [PATCH 56/62] Bump actions/cache from 2 to 3 Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 6 +++--- .github/workflows/code-coverage.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d15b834068..933e46608a 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -68,7 +68,7 @@ jobs: run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - name: Git Setup LFS Cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: lfs-cache with: path: .git/lfs @@ -81,7 +81,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: nuget-cache with: path: ~/.nuget @@ -157,7 +157,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: nuget-cache with: path: ~/.nuget diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 31ddaa6c3a..0922a9e824 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -34,7 +34,7 @@ jobs: run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id - name: Git Setup LFS Cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: lfs-cache with: path: .git/lfs @@ -47,7 +47,7 @@ jobs: uses: NuGet/setup-nuget@v1 - name: NuGet Setup Cache - uses: actions/cache@v2 + uses: actions/cache@v3 id: nuget-cache with: path: ~/.nuget From c1bb294d50d4b9175652b9033c42d70a9962f46a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:09:14 +0000 Subject: [PATCH 57/62] Bump actions/upload-artifact from 2 to 3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 2 to 3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build-and-test.yml | 2 +- .github/workflows/code-coverage.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 933e46608a..4ffbbb5440 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -127,7 +127,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 0922a9e824..91369d7aa8 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -74,7 +74,7 @@ jobs: XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit - name: Export Failed Output - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip From 3609507a70a4750a7730d68d58e1d72284739b8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 4 Jun 2022 16:09:09 +0000 Subject: [PATCH 58/62] Bump codecov/codecov-action from 1 to 3 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 1 to 3. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v1...v3) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/code-coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-coverage.yml b/.github/workflows/code-coverage.yml index 91369d7aa8..85ff42b74b 100644 --- a/.github/workflows/code-coverage.yml +++ b/.github/workflows/code-coverage.yml @@ -81,7 +81,7 @@ jobs: path: tests/Images/ActualOutput/ - name: Codecov Update - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v3 if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors') with: flags: unittests From 943cc3439398079e7eff22ac124f63339be8ea6e Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sun, 5 Jun 2022 20:59:10 +0300 Subject: [PATCH 59/62] Fixed --- .../Jpeg/Components/Decoder/JpegBitReader.cs | 7 +-- .../Formats/Jpeg/JpegDecoderCore.cs | 54 +++++++++---------- .../Formats/Jpg/JpegDecoderTests.cs | 15 +++++- tests/ImageSharp.Tests/TestImages.cs | 3 +- ...ssue2136-scan-segment-extraneous-bytes.jpg | 3 ++ 5 files changed, 49 insertions(+), 33 deletions(-) create mode 100644 tests/Images/Input/Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg diff --git a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs index 84013319e1..70968d5194 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/Decoder/JpegBitReader.cs @@ -155,12 +155,14 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder c = this.ReadStream(); } + // Found a marker // We accept multiple FF bytes followed by a 0 as meaning a single FF data byte. - // This data pattern is not valid according to the standard. + // even though it's considered 'invalid' according to the specs. if (c != 0) { - this.Marker = (byte)c; + // It's a trick so we won't read past actual marker this.badData = true; + this.Marker = (byte)c; this.MarkerPosition = this.stream.Position - 2; } } @@ -199,7 +201,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder if (b != 0) { this.Marker = (byte)b; - this.badData = true; this.MarkerPosition = this.stream.Position - 2; return true; } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index a6ccb1b644..73254ace12 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -164,38 +164,38 @@ namespace SixLabors.ImageSharp.Formats.Jpeg /// /// Finds the next file marker within the byte stream. /// - /// The buffer to read file markers to. /// The input stream. - /// The - public static JpegFileMarker FindNextFileMarker(byte[] marker, BufferedReadStream stream) + /// The . + public static JpegFileMarker FindNextFileMarker(BufferedReadStream stream) { - int value = stream.Read(marker, 0, 2); - - if (value == 0) + while (true) { - return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); - } + int b = stream.ReadByte(); + if (b == -1) + { + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + } - if (marker[0] == JpegConstants.Markers.XFF) - { - // According to Section B.1.1.2: - // "Any marker may optionally be preceded by any number of fill bytes, which are bytes assigned code 0xFF." - int m = marker[1]; - while (m == JpegConstants.Markers.XFF) + // Found a marker. + if (b == JpegConstants.Markers.XFF) { - int suffix = stream.ReadByte(); - if (suffix == -1) + while (b == JpegConstants.Markers.XFF) { - return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + // Loop here to discard any padding FF bytes on terminating marker. + b = stream.ReadByte(); + if (b == -1) + { + return new JpegFileMarker(JpegConstants.Markers.EOI, stream.Length - 2); + } } - m = suffix; + // Found a valid marker. Exit loop + if (b is not 0 and (< JpegConstants.Markers.RST0 or > JpegConstants.Markers.RST7)) + { + return new JpegFileMarker((byte)(uint)b, stream.Position - 2); + } } - - return new JpegFileMarker((byte)m, stream.Position - 2); } - - return new JpegFileMarker(marker[1], stream.Position - 2, true); } /// @@ -331,15 +331,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg JpegThrowHelper.ThrowInvalidImageContentException("Missing SOI marker."); } - stream.Read(this.markerBuffer, 0, 2); - byte marker = this.markerBuffer[1]; - fileMarker = new JpegFileMarker(marker, (int)stream.Position - 2); + fileMarker = FindNextFileMarker(stream); this.QuantizationTables ??= new Block8x8F[4]; // Break only when we discover a valid EOI marker. // https://github.com/SixLabors/ImageSharp/issues/695 - while (fileMarker.Marker != JpegConstants.Markers.EOI - || (fileMarker.Marker == JpegConstants.Markers.EOI && fileMarker.Invalid)) + while (fileMarker.Marker != JpegConstants.Markers.EOI) { cancellationToken.ThrowIfCancellationRequested(); @@ -491,7 +488,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg } // Read on. - fileMarker = FindNextFileMarker(this.markerBuffer, stream); + fileMarker = FindNextFileMarker(stream); + Console.WriteLine($"Found marker: {fileMarker.Marker} at {fileMarker.Position}"); } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index b105677bec..2b24597e35 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // https://github.com/SixLabors/ImageSharp/issues/2133 [Theory] - [WithFile(TestImages.Jpeg.Issues.Issue2133DeduceColorSpace, PixelTypes.Rgba32)] + [WithFile(TestImages.Jpeg.Issues.Issue2133_DeduceColorSpace, PixelTypes.Rgba32)] public void Issue2133_DeduceColorSpace(TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -231,6 +231,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } } + // https://github.com/SixLabors/ImageSharp/issues/2133 + [Theory] + [WithFile(TestImages.Jpeg.Issues.Issue2136_ScanMarkerExtraneousBytes, PixelTypes.Rgba32)] + public void Issue2136_DecodeWorks(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(JpegDecoder)) + { + image.DebugSave(provider); + image.CompareToOriginal(provider); + } + } + // DEBUG ONLY! // The PDF.js output should be saved by "tests\ImageSharp.Tests\Formats\Jpg\pdfjs\jpeg-converter.htm" // into "\tests\Images\ActualOutput\JpegDecoderTests\" diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 403b42b254..ec65e2c65b 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -272,7 +272,8 @@ namespace SixLabors.ImageSharp.Tests public const string Issue2057App1Parsing = "Jpg/issues/Issue2057-App1Parsing.jpg"; public const string ExifNullArrayTag = "Jpg/issues/issue-2056-exif-null-array.jpg"; public const string ValidExifArgumentNullExceptionOnEncode = "Jpg/issues/Issue2087-exif-null-reference-on-encode.jpg"; - public const string Issue2133DeduceColorSpace = "Jpg/issues/Issue2133.jpg"; + public const string Issue2133_DeduceColorSpace = "Jpg/issues/Issue2133.jpg"; + public const string Issue2136_ScanMarkerExtraneousBytes = "Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg"; public static class Fuzz { diff --git a/tests/Images/Input/Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg b/tests/Images/Input/Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg new file mode 100644 index 0000000000..c759b93ce6 --- /dev/null +++ b/tests/Images/Input/Jpg/issues/Issue2136-scan-segment-extraneous-bytes.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b40cd36423a0602515abf411944016cc43169423b31b347953739dee91e15d38 +size 826538 From ef9830fd69b7b03ada0869a8654f5242640105b1 Mon Sep 17 00:00:00 2001 From: Dmitry Pentin Date: Sun, 5 Jun 2022 21:18:00 +0300 Subject: [PATCH 60/62] Removed debug console log --- src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs index 73254ace12..58c85bd34e 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs @@ -489,7 +489,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg // Read on. fileMarker = FindNextFileMarker(stream); - Console.WriteLine($"Found marker: {fileMarker.Marker} at {fileMarker.Position}"); } } From 323f24e8e556e4a866433fde46c57e8df3fea801 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 12 Jun 2022 13:32:33 +0200 Subject: [PATCH 61/62] Add test case for #2149 --- tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs | 6 ++++++ tests/ImageSharp.Tests/TestImages.cs | 1 + .../Input/Tiff/Issues/Group4CompressionWithStrips.tiff | 3 +++ 3 files changed, 10 insertions(+) create mode 100644 tests/Images/Input/Tiff/Issues/Group4CompressionWithStrips.tiff diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs index 7987d76a3f..90e97824e8 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs @@ -665,6 +665,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff } }); + // https://github.com/SixLabors/ImageSharp/issues/2149 + [Theory] + [WithFile(Issues2149, PixelTypes.Rgba32)] + public void TiffDecoder_CanDecode_Fax4CompressedWithStrips(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffDecoder(provider); + [Theory] [WithFileCollection(nameof(MultiframeTestImages), PixelTypes.Rgba32)] public void DecodeMultiframe(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9c8fdd8d05..26101a17ae 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -917,6 +917,7 @@ namespace SixLabors.ImageSharp.Tests public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff"; public const string Issues1891 = "Tiff/Issues/Issue1891.tiff"; public const string Issues2123 = "Tiff/Issues/Issue2123.tiff"; + public const string Issues2149 = "Tiff/Issues/Group4CompressionWithStrips.tiff"; public const string SmallRgbDeflate = "Tiff/rgb_small_deflate.tiff"; public const string SmallRgbLzw = "Tiff/rgb_small_lzw.tiff"; diff --git a/tests/Images/Input/Tiff/Issues/Group4CompressionWithStrips.tiff b/tests/Images/Input/Tiff/Issues/Group4CompressionWithStrips.tiff new file mode 100644 index 0000000000..16d8030a4f --- /dev/null +++ b/tests/Images/Input/Tiff/Issues/Group4CompressionWithStrips.tiff @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:141320615d58b2971aedfeba8d408c38c0b4ab43024678254cecaebf0ed7edb0 +size 4440 From e84100a05e2b20ff1d77f406c6c10dbf6e5938c7 Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sun, 12 Jun 2022 13:34:08 +0200 Subject: [PATCH 62/62] Fix issue #2149 --- .../Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs index 7cfcfe12b6..8a94ead1e6 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T6TiffCompression.cs @@ -52,6 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer) { int height = stripHeight; + buffer.Clear(); using System.Buffers.IMemoryOwner scanLineBuffer = this.Allocator.Allocate(this.width * 2); Span scanLine = scanLineBuffer.GetSpan().Slice(0, this.width);