From 2c2250d9d7c5205eb040992f75638df88b6e1d64 Mon Sep 17 00:00:00 2001 From: Darth Affe Date: Sun, 11 Aug 2024 21:13:27 +0200 Subject: [PATCH] Fixed potential error in in-place conversion and improved tests to cover this case --- HPPH.Test/PixelHelper/ConvertTests.cs | 92 +++++++++++++++++++ HPPH.Test/SystemDrawing/SystemDrawingTests.cs | 16 ++++ HPPH/PixelHelper.Convert.cs | 56 ++++++----- 3 files changed, 142 insertions(+), 22 deletions(-) diff --git a/HPPH.Test/PixelHelper/ConvertTests.cs b/HPPH.Test/PixelHelper/ConvertTests.cs index 3e2c125..e1c280e 100644 --- a/HPPH.Test/PixelHelper/ConvertTests.cs +++ b/HPPH.Test/PixelHelper/ConvertTests.cs @@ -33,6 +33,12 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } [TestMethod] @@ -63,6 +69,12 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } [TestMethod] @@ -93,6 +105,12 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } [TestMethod] @@ -123,6 +141,12 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } [TestMethod] @@ -153,6 +177,12 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } [TestMethod] @@ -183,6 +213,20 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + { + ColorABGR refColor = TestDataHelper.GetColorFromLocation(x, y); + ColorRGBA color = converted[x, y]; + + Assert.AreEqual(255, color.A, $"Wrong A at x: {x}, y: {y}"); + Assert.AreEqual(refColor.R, color.R, $"Wrong R at x: {x}, y: {y}"); + Assert.AreEqual(refColor.G, color.G, $"Wrong G at x: {x}, y: {y}"); + Assert.AreEqual(refColor.B, color.B, $"Wrong B at x: {x}, y: {y}"); + } } [TestMethod] @@ -213,6 +257,20 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + { + ColorABGR refColor = TestDataHelper.GetColorFromLocation(x, y); + ColorARGB color = converted[x, y]; + + Assert.AreEqual(255, color.A, $"Wrong A at x: {x}, y: {y}"); + Assert.AreEqual(refColor.R, color.R, $"Wrong R at x: {x}, y: {y}"); + Assert.AreEqual(refColor.G, color.G, $"Wrong G at x: {x}, y: {y}"); + Assert.AreEqual(refColor.B, color.B, $"Wrong B at x: {x}, y: {y}"); + } } [TestMethod] @@ -243,6 +301,20 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + { + ColorABGR refColor = TestDataHelper.GetColorFromLocation(x, y); + ColorBGRA color = converted[x, y]; + + Assert.AreEqual(255, color.A, $"Wrong A at x: {x}, y: {y}"); + Assert.AreEqual(refColor.R, color.R, $"Wrong R at x: {x}, y: {y}"); + Assert.AreEqual(refColor.G, color.G, $"Wrong G at x: {x}, y: {y}"); + Assert.AreEqual(refColor.B, color.B, $"Wrong B at x: {x}, y: {y}"); + } } [TestMethod] @@ -273,6 +345,20 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + { + ColorABGR refColor = TestDataHelper.GetColorFromLocation(x, y); + ColorABGR color = converted[x, y]; + + Assert.AreEqual(255, color.A, $"Wrong A at x: {x}, y: {y}"); + Assert.AreEqual(refColor.R, color.R, $"Wrong R at x: {x}, y: {y}"); + Assert.AreEqual(refColor.G, color.G, $"Wrong G at x: {x}, y: {y}"); + Assert.AreEqual(refColor.B, color.B, $"Wrong B at x: {x}, y: {y}"); + } } [TestMethod] @@ -303,5 +389,11 @@ public class ConvertTests } } } + + IImage converted = TestDataHelper.CreateTestImage(1920, 1080).ConvertTo(); + + for (int y = 0; y < converted.Height; y++) + for (int x = 0; x < converted.Width; x++) + Assert.AreEqual(TestDataHelper.GetColorFromLocation(x, y), converted[x, y], $"Wrong color at x: {x}, y: {y}"); } } \ No newline at end of file diff --git a/HPPH.Test/SystemDrawing/SystemDrawingTests.cs b/HPPH.Test/SystemDrawing/SystemDrawingTests.cs index 4b65a35..474b965 100644 --- a/HPPH.Test/SystemDrawing/SystemDrawingTests.cs +++ b/HPPH.Test/SystemDrawing/SystemDrawingTests.cs @@ -16,6 +16,14 @@ public class SystemDrawingTests IImage image2 = bitmap.ToImage(); Assert.AreEqual(IColorFormat.BGR, image2.ColorFormat); + for (int y = 0; y < image.Height; y++) + for (int x = 0; x < image.Width; x++) + { + Assert.AreEqual(image[x, y].A, image2[x, y].A, $"{x}-{y}"); + Assert.AreEqual(image[x, y].R, image2[x, y].R, $"{x}-{y}"); + Assert.AreEqual(image[x, y].G, image2[x, y].G, $"{x}-{y}"); + Assert.AreEqual(image[x, y].B, image2[x, y].B, $"{x}-{y}"); + } image2 = image2.ConvertTo(); @@ -30,6 +38,14 @@ public class SystemDrawingTests IImage image2 = bitmap.ToImage(); Assert.AreEqual(IColorFormat.BGRA, image2.ColorFormat); + for (int y = 0; y < image.Height; y++) + for (int x = 0; x < image.Width; x++) + { + Assert.AreEqual(image[x, y].A, image2[x, y].A); + Assert.AreEqual(image[x, y].R, image2[x, y].R); + Assert.AreEqual(image[x, y].G, image2[x, y].G); + Assert.AreEqual(image[x, y].B, image2[x, y].B); + } image2 = image2.ConvertTo(); diff --git a/HPPH/PixelHelper.Convert.cs b/HPPH/PixelHelper.Convert.cs index 4e89ba1..43f1e5b 100644 --- a/HPPH/PixelHelper.Convert.cs +++ b/HPPH/PixelHelper.Convert.cs @@ -174,12 +174,14 @@ public static unsafe partial class PixelHelper tar += bytesPerVector; } + Span buffer = stackalloc byte[missingElements * BPP]; for (int i = 0; i < missingElements; i++) { - tar[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; - tar[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; - tar[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; } + buffer.CopyTo(new Span(tar, buffer.Length)); } else { @@ -191,12 +193,14 @@ public static unsafe partial class PixelHelper byte* missingSrc = sourcePtr + (batches * batchSize * BPP); byte* missingTar = targetPtr + (batches * batchSize * BPP); + Span buffer = stackalloc byte[missing * BPP]; for (int i = 0; i < missing; i++) { - missingTar[(i * BPP) + 0] = missingSrc[(i * BPP) + maskVector[0]]; - missingTar[(i * BPP) + 1] = missingSrc[(i * BPP) + maskVector[1]]; - missingTar[(i * BPP) + 2] = missingSrc[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 0] = missingSrc[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = missingSrc[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = missingSrc[(i * BPP) + maskVector[2]]; } + buffer.CopyTo(new Span(missingTar, buffer.Length)); } void Process(int index) @@ -217,12 +221,14 @@ public static unsafe partial class PixelHelper tar += bytesPerVector; } + Span buffer = stackalloc byte[missingElements * BPP]; for (int i = 0; i < missingElements; i++) { - tar[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; - tar[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; - tar[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; } + buffer.CopyTo(new Span(tar, buffer.Length)); } } } @@ -292,13 +298,15 @@ public static unsafe partial class PixelHelper tar += bytesPerVector; } + Span buffer = stackalloc byte[missingElements * BPP]; for (int i = 0; i < missingElements; i++) { - tar[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; - tar[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; - tar[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; - tar[(i * BPP) + 3] = src[(i * BPP) + maskVector[3]]; + buffer[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 3] = src[(i * BPP) + maskVector[3]]; } + buffer.CopyTo(new Span(tar, buffer.Length)); } else { @@ -310,13 +318,15 @@ public static unsafe partial class PixelHelper byte* missingSrc = sourcePtr + (batches * batchSize * BPP); byte* missingTar = targetPtr + (batches * batchSize * BPP); + Span buffer = stackalloc byte[missing * BPP]; for (int i = 0; i < missing; i++) { - missingTar[(i * BPP) + 0] = missingSrc[(i * BPP) + maskVector[0]]; - missingTar[(i * BPP) + 1] = missingSrc[(i * BPP) + maskVector[1]]; - missingTar[(i * BPP) + 2] = missingSrc[(i * BPP) + maskVector[2]]; - missingTar[(i * BPP) + 3] = missingSrc[(i * BPP) + maskVector[3]]; + buffer[(i * BPP) + 0] = missingSrc[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = missingSrc[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = missingSrc[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 3] = missingSrc[(i * BPP) + maskVector[3]]; } + buffer.CopyTo(new Span(missingTar, buffer.Length)); } void Process(int index) @@ -337,18 +347,20 @@ public static unsafe partial class PixelHelper tar += bytesPerVector; } + Span buffer = stackalloc byte[missingElements * BPP]; for (int i = 0; i < missingElements; i++) { - tar[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; - tar[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; - tar[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; - tar[(i * BPP) + 3] = src[(i * BPP) + maskVector[3]]; + buffer[(i * BPP) + 0] = src[(i * BPP) + maskVector[0]]; + buffer[(i * BPP) + 1] = src[(i * BPP) + maskVector[1]]; + buffer[(i * BPP) + 2] = src[(i * BPP) + maskVector[2]]; + buffer[(i * BPP) + 3] = src[(i * BPP) + maskVector[3]]; } + buffer.CopyTo(new Span(tar, buffer.Length)); } } } } - + private static void ConvertWiden3To4Bytes(ReadOnlySpan source, Span target, IColorFormat sourceFormat, IColorFormat targetFormat) { const int SOURCE_BPP = 3;