From 5154c2a4902066f177edac96a6b021b7afc16544 Mon Sep 17 00:00:00 2001 From: ikkyu Date: Thu, 2 May 2024 11:42:05 +0700 Subject: [PATCH 1/7] add test CastAnyBitmap_from_SixLabors --- IronSoftware.Drawing/.gitattributes | 1 + .../Data/RenderedFromChrome.bmp | 3 +++ .../UnitTests/AnyBitmapFunctionality.cs | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 IronSoftware.Drawing/.gitattributes create mode 100644 IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/Data/RenderedFromChrome.bmp diff --git a/IronSoftware.Drawing/.gitattributes b/IronSoftware.Drawing/.gitattributes new file mode 100644 index 0000000..209dc1d --- /dev/null +++ b/IronSoftware.Drawing/.gitattributes @@ -0,0 +1 @@ +IronSoftware.Drawing.Common.Tests/Data/RenderedFromChrome.bmp filter=lfs diff=lfs merge=lfs -text diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/Data/RenderedFromChrome.bmp b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/Data/RenderedFromChrome.bmp new file mode 100644 index 0000000..1df9441 --- /dev/null +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/Data/RenderedFromChrome.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02c9d5fb29cd2882a4da202ac4f5657a2d3b18df0b2c587d9d545099a9ad7b6f +size 139141366 diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs index 18bb26e..6973da5 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs @@ -866,5 +866,21 @@ public void LoadImage_TiffImage_ShouldLoadWithoutThumbnail() bitmap.FrameCount.Should().Be(1); } + + [FactWithAutomaticDisplayName] + public void CastAnyBitmap_from_SixLabors() + { + //This test throw System.OutOfMemoryException in x86 + + var image = Image.Load(GetRelativeFilePath("RenderedFromChrome.bmp")); + + var anyBitmap = (AnyBitmap)image; + + image.Save("expected.bmp"); + anyBitmap.SaveAs("result.bmp"); + + AssertImageAreEqual("expected.bmp", "result.bmp", true); + } + } } From 4f2a99a0f00bf4cf5c759e3e8a26de7f2fcf8769 Mon Sep 17 00:00:00 2001 From: Meee Date: Thu, 2 May 2024 15:40:15 +0700 Subject: [PATCH 2/7] PR Validation: Add test NET472 on x86 --- CI/stage_templates/run_tests_on_pool.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CI/stage_templates/run_tests_on_pool.yml b/CI/stage_templates/run_tests_on_pool.yml index 58cb4e1..56354b2 100644 --- a/CI/stage_templates/run_tests_on_pool.yml +++ b/CI/stage_templates/run_tests_on_pool.yml @@ -16,10 +16,18 @@ stages: - script: echo "Skip Unit tests" - ${{ if eq(parameters.runUnitTests, true) }}: - ${{ if eq(parameters.OSPlatform, 'Windows') }}: + # Windows .NET 472 x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}net472x86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'net472' + architecture: '.x86' + buildConfiguration: $(Configuration) # Windows .NET 472 x64 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: - name: UnitTest${{ parameters.OSPlatform }}net472 + name: UnitTest${{ parameters.OSPlatform }}net472x64 OSPlatform: ${{ parameters.OSPlatform }} framework: 'net472' architecture: '' From b28a3664decaa17a5ee2281fc5879bc9b3de0270 Mon Sep 17 00:00:00 2001 From: Meee Date: Tue, 7 May 2024 12:54:56 +0700 Subject: [PATCH 3/7] PR Validation: Add tests for NET60 and NET70 x86 --- CI/stage_templates/run_tests_on_pool.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/CI/stage_templates/run_tests_on_pool.yml b/CI/stage_templates/run_tests_on_pool.yml index 56354b2..f1959eb 100644 --- a/CI/stage_templates/run_tests_on_pool.yml +++ b/CI/stage_templates/run_tests_on_pool.yml @@ -56,7 +56,7 @@ stages: framework: 'net50' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET 6.0 Tests + # Windows .NET 6.0 x64 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: name: UnitTest${{ parameters.OSPlatform }}net60 @@ -64,7 +64,15 @@ stages: framework: 'net60' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET 7.0 Tests + # Windows .NET 6.0 x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}net60x86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'net60' + architecture: '.x86' + buildConfiguration: $(Configuration) + # Windows .NET 7.0 x64 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: name: UnitTest${{ parameters.OSPlatform }}net70 @@ -72,3 +80,11 @@ stages: framework: 'net70' architecture: '' buildConfiguration: $(Configuration) + # Windows .NET 7.0 x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}net70x86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'net70' + architecture: '.x86' + buildConfiguration: $(Configuration) From 0b1b8c9629fae62ca149b532bac1ad8b491a4ee0 Mon Sep 17 00:00:00 2001 From: Meee Date: Tue, 7 May 2024 13:07:28 +0700 Subject: [PATCH 4/7] PR Validation: Tests x86 arc only on Windows --- CI/stage_templates/run_tests_on_pool.yml | 70 ++++++++++++------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/CI/stage_templates/run_tests_on_pool.yml b/CI/stage_templates/run_tests_on_pool.yml index f1959eb..898be51 100644 --- a/CI/stage_templates/run_tests_on_pool.yml +++ b/CI/stage_templates/run_tests_on_pool.yml @@ -32,23 +32,39 @@ stages: framework: 'net472' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET Core x64 Tests - - template: ../job_templates/test_drawing_libraries.yml - parameters: - name: UnitTest${{ parameters.OSPlatform }}netcore - OSPlatform: ${{ parameters.OSPlatform }} - framework: 'netcoreapp3.1' - architecture: '' - buildConfiguration: $(Configuration) - # Windows .NET Core x86 Tests - - template: ../job_templates/test_drawing_libraries.yml - parameters: - name: UnitTest${{ parameters.OSPlatform }}netcorex86 - OSPlatform: ${{ parameters.OSPlatform }} - framework: 'netcoreapp3.1' - architecture: '.x86' - buildConfiguration: $(Configuration) - # Windows .NET 5.0 Tests + # Windows .NET Core x64 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}netcore + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'netcoreapp3.1' + architecture: '' + buildConfiguration: $(Configuration) + # Windows .NET Core x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}netcorex86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'netcoreapp3.1' + architecture: '.x86' + buildConfiguration: $(Configuration) + # Windows .NET 6.0 x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}net60x86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'net60' + architecture: '.x86' + buildConfiguration: $(Configuration) + # Windows .NET 7.0 x86 Tests + - template: ../job_templates/test_drawing_libraries.yml + parameters: + name: UnitTest${{ parameters.OSPlatform }}net70x86 + OSPlatform: ${{ parameters.OSPlatform }} + framework: 'net70' + architecture: '.x86' + buildConfiguration: $(Configuration) + # .NET 5.0 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: name: UnitTest${{ parameters.OSPlatform }}net50 @@ -56,7 +72,7 @@ stages: framework: 'net50' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET 6.0 x64 Tests + # .NET 6.0 x64 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: name: UnitTest${{ parameters.OSPlatform }}net60 @@ -64,15 +80,7 @@ stages: framework: 'net60' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET 6.0 x86 Tests - - template: ../job_templates/test_drawing_libraries.yml - parameters: - name: UnitTest${{ parameters.OSPlatform }}net60x86 - OSPlatform: ${{ parameters.OSPlatform }} - framework: 'net60' - architecture: '.x86' - buildConfiguration: $(Configuration) - # Windows .NET 7.0 x64 Tests + # .NET 7.0 x64 Tests - template: ../job_templates/test_drawing_libraries.yml parameters: name: UnitTest${{ parameters.OSPlatform }}net70 @@ -80,11 +88,3 @@ stages: framework: 'net70' architecture: '' buildConfiguration: $(Configuration) - # Windows .NET 7.0 x86 Tests - - template: ../job_templates/test_drawing_libraries.yml - parameters: - name: UnitTest${{ parameters.OSPlatform }}net70x86 - OSPlatform: ${{ parameters.OSPlatform }} - framework: 'net70' - architecture: '.x86' - buildConfiguration: $(Configuration) From 77373a91714bc8c026328674adde832a44effcea Mon Sep 17 00:00:00 2001 From: Meee Date: Tue, 7 May 2024 13:18:56 +0700 Subject: [PATCH 5/7] AnyBitmap: Use stream to save image instead of byte[] to fix `OutOfMemoryException` `OutOfMemoryException` is causing by the giant array of byte which memory on x86 can't hold it. --- .../UnitTests/AnyBitmapFunctionality.cs | 2 +- .../UnitTests/Compare.cs | 83 +++++++++++++------ .../IronSoftware.Drawing.Common/AnyBitmap.cs | 13 +-- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs index 6973da5..f9221b9 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs @@ -879,7 +879,7 @@ public void CastAnyBitmap_from_SixLabors() image.Save("expected.bmp"); anyBitmap.SaveAs("result.bmp"); - AssertImageAreEqual("expected.bmp", "result.bmp", true); + AssertLargeImageAreEqual("expected.bmp", "result.bmp", true); } } diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/Compare.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/Compare.cs index aae1840..bc4a4a2 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/Compare.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/Compare.cs @@ -1,4 +1,5 @@ using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using System; using System.IO; using System.Linq; @@ -14,43 +15,74 @@ protected Compare(ITestOutputHelper output) : base(output) { } - protected static void AssertImageAreEqual(string expectedImagePath, string resultImagePath, bool isCleanAll = false) + protected static void AssertLargeImageAreEqual(string expectedImagePath, string resultImagePath, bool isCleanAll = false) { - string assertName = "AssertImage.AreEqual"; + using SixLabors.ImageSharp.Image expectedImageSharp = SixLabors.ImageSharp.Image.Load(expectedImagePath); + using SixLabors.ImageSharp.Image actualImageSharp = SixLabors.ImageSharp.Image.Load(resultImagePath); - var expected = AnyBitmap.FromFile(expectedImagePath); - var actual = AnyBitmap.FromFile(resultImagePath); + CleanCompareResultFile(expectedImagePath, resultImagePath, isCleanAll); - if (isCleanAll) - { - CleanResultFile(expectedImagePath); - } + expectedImageSharp.Mutate(x => x.Resize(100, 100)); + actualImageSharp.Mutate(x => x.Resize(100, 100)); - CleanResultFile(resultImagePath); + AssertImageAreEqual(expectedImageSharp, actualImageSharp); + } - //Test to see if we have the same size of image - if (expected.Width != actual.Width || expected.Height != actual.Height) - { - throw new AssertActualExpectedException($"Expected:.", $"Actual:.", $"{assertName} failed."); - } + protected static void AssertImageAreEqual(string expectedImagePath, string resultImagePath, bool isCleanAll = false) + { + using var expected = AnyBitmap.FromFile(expectedImagePath); + using var actual = AnyBitmap.FromFile(resultImagePath); - //Convert each image to a byte array - byte[] btImageExpected = expected.ExportBytes(); - byte[] btImageActual = expected.ExportBytes(); + CleanCompareResultFile(expectedImagePath, resultImagePath, isCleanAll); - //Compute a hash for each image - var shaM = SHA256.Create(); - byte[] hash1 = shaM.ComputeHash(btImageExpected); - byte[] hash2 = shaM.ComputeHash(btImageActual); + AssertImageAreEqual(expected, actual); + } - //Compare the hash values - for (int i = 0; i < hash1.Length && i < hash2.Length; i++) + protected static void AssertImageAreEqual(AnyBitmap expected, AnyBitmap actual) + { + try { - if (hash1[i] != hash2[i]) + string assertName = "AssertImage.AreEqual"; + + //Test to see if we have the same size of image + if (expected.Width != actual.Width || expected.Height != actual.Height) { - throw new AssertActualExpectedException($"Expected:.", $"Actual:.", $"{assertName} failed."); + throw new AssertActualExpectedException($"Expected:.", $"Actual:.", $"{assertName} failed."); } + + //Convert each image to a byte array + byte[] btImageExpected = expected.ExportBytes(); + byte[] btImageActual = expected.ExportBytes(); + + //Compute a hash for each image + var shaM = SHA256.Create(); + byte[] hash1 = shaM.ComputeHash(btImageExpected); + byte[] hash2 = shaM.ComputeHash(btImageActual); + + //Compare the hash values + for (int i = 0; i < hash1.Length && i < hash2.Length; i++) + { + if (hash1[i] != hash2[i]) + { + throw new AssertActualExpectedException($"Expected:.", $"Actual:.", $"{assertName} failed."); + } + } + } + finally + { + expected?.Dispose(); + actual?.Dispose(); + } + } + + private static void CleanCompareResultFile(string expectedImagePath, string resultImagePath, bool isCleanAll) + { + if (isCleanAll) + { + CleanResultFile(expectedImagePath); } + + CleanResultFile(resultImagePath); } protected static void CleanResultFile(string filename) @@ -60,6 +92,7 @@ protected static void CleanResultFile(string filename) File.Delete(filename); } } + protected static void AssertStreamAreEqual(MemoryStream expected, MemoryStream actual) { string assertName = "AssertStream.AreEqual"; diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs index 043a3b7..1382195 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs @@ -174,7 +174,7 @@ public AnyBitmap Clone(Rectangle rectangle) public byte[] ExportBytes( ImageFormat format = ImageFormat.Default, int lossy = 100) { - MemoryStream mem = new(); + using MemoryStream mem = new(); ExportStream(mem, format, lossy); byte[] byteArray = mem.ToArray(); @@ -202,13 +202,7 @@ public void ExportFile( ImageFormat format = ImageFormat.Default, int lossy = 100) { - using (MemoryStream mem = new()) - { - ExportStream(mem, format, lossy); - byte[] byteArray = mem.ToArray(); - - File.WriteAllBytes(file, byteArray); - } + SaveAs(file, format, lossy); } /// @@ -346,7 +340,8 @@ public void SaveAs(string file) /// public void SaveAs(string file, ImageFormat format, int lossy = 100) { - File.WriteAllBytes(file, ExportBytes(format, lossy)); + using var fileStream = new FileStream(file, FileMode.Create); + ExportStream(fileStream, format, lossy); } /// From fab7991165e69ad866a35639e632f765359c7889 Mon Sep 17 00:00:00 2001 From: Meee Date: Tue, 7 May 2024 13:29:54 +0700 Subject: [PATCH 6/7] PR Validation: Install missing NET7 SDK on x86 - Set env.PROCESSOR_ARCHITECTURE to x86 to install on x86 arch - Install .NET7 only on x86 arch it already install for x64 PR Validation: Add missing `.` when checking architecture --- CI/job_templates/test_drawing_libraries.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CI/job_templates/test_drawing_libraries.yml b/CI/job_templates/test_drawing_libraries.yml index 8f35846..ac8c71e 100644 --- a/CI/job_templates/test_drawing_libraries.yml +++ b/CI/job_templates/test_drawing_libraries.yml @@ -32,6 +32,15 @@ jobs: inputs: packageType: 'sdk' version: '5.x' + # .NET 7.0 need to install only on x86 + - ${{ if and(eq(parameters.framework, 'net70'), eq(parameters.architecture, '.x86')) }}: + - task: UseDotNet@2 + displayName: 'Install .NET7 sdk' + inputs: + packageType: 'sdk' + version: '7.x' + env: + PROCESSOR_ARCHITECTURE: x86 - ${{ if ne(parameters.framework, 'net60') }}: - task: DotNetCoreCLI@2 displayName: Execute ${{ parameters.OSPlatform }} ${{ parameters.framework}} ${{ parameters.architecture }} Tests From e863e40f23d9884baa4f8d38299ff59275660fa5 Mon Sep 17 00:00:00 2001 From: first-ironsoftware Date: Mon, 20 May 2024 12:54:43 +0700 Subject: [PATCH 7/7] Update release note --- NuGet/IronSoftware.Drawing.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec index 7979c9c..02d42b3 100644 --- a/NuGet/IronSoftware.Drawing.nuspec +++ b/NuGet/IronSoftware.Drawing.nuspec @@ -39,7 +39,7 @@ Supports: For general support and technical inquiries, please email us at: support@ironsoftware.com IronSoftware.System.Drawing is an open-source solution for .NET developers to replace System.Drawing.Common with a universal and flexible library. - - Update SixLabors.ImageSharp & SixLabors.ImageSharp.Drawing to address known vulnerabilities. + - Improves memory management when loading large image input Copyright © Iron Software 2022-2024 Images, Bitmap, SkiaSharp, SixLabors, BitMiracle, Maui, SVG, TIFF, TIF, GIF, JPEG, PNG, Color, Rectangle, Drawing, C#, VB.NET, ASPX, create, render, generate, standard, netstandard2.0, core, netcore