From 128202db959840999491fb197ab4877983481f4e Mon Sep 17 00:00:00 2001 From: Sawraz Date: Fri, 9 Oct 2026 09:25:14 +0600 Subject: [PATCH 1/4] DW-38: support SixLabors.ImageSharp v4.1.2 on net8.0 via conditional TFM (CVE fixes incl. CVE-2026-106114), migrate v4 API surface, and wire Six Labors license into CI build --- CI/job_templates/build_drawing_libraries.yml | 7 ++++ .../UnitTests/AnyBitmapFunctionality.cs | 8 ++++ .../UnitTests/ColorFunctionality.cs | 20 ++++++++++ .../IronSoftware.Drawing.Common/AnyBitmap.cs | 40 ++++++++++++++++++- .../IronSoftware.Drawing.Common/Color.cs | 40 +++++++++++++++++++ .../IronSoftware.Drawing.Common.csproj | 8 +++- NuGet/IronSoftware.Drawing.nuspec | 7 ++++ 7 files changed, 127 insertions(+), 3 deletions(-) diff --git a/CI/job_templates/build_drawing_libraries.yml b/CI/job_templates/build_drawing_libraries.yml index 6b770864..c2cd056b 100644 --- a/CI/job_templates/build_drawing_libraries.yml +++ b/CI/job_templates/build_drawing_libraries.yml @@ -36,6 +36,12 @@ jobs: inputs: packageType: 'sdk' version: '8.x' + # Six Labors license (required by ImageSharp v4 / ImageSharp.Drawing v3 build-time check on the net8.0 target) + - task: DownloadSecureFile@1 + name: sixLaborsLicense + displayName: 'Download Six Labors license file' + inputs: + secureFile: 'sixlabors.lic' # Build - task: DotNetCoreCLI@2 displayName: Restore Solution NuGet Packages @@ -58,6 +64,7 @@ jobs: --no-restore --verbosity normal --property:AssemblyVersion=$(finalAssemblyVersion) + --property:SixLaborsLicenseFile=$(sixLaborsLicense.secureFilePath) versioningScheme: byEnvVar versionEnvVar: AssemblyVersion # Publish Pipeline Artifacts diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs index 88e2a8cc..993103be 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs @@ -1155,7 +1155,11 @@ public void Resize_ShouldPreserveDepthForRepresentableFormats() Assert.Equal(24, new AnyBitmap(rgb24, 20, 20).BitsPerPixel); string path64 = "dw40_tmp64.png"; +#if NET8_0_OR_GREATER + using (var img64 = new Image(30, 30)) { img64.Save(path64, new SixLabors.ImageSharp.Formats.Png.PngEncoder { ColorType = SixLabors.ImageSharp.Formats.Png.PngColorType.RgbWithAlpha, BitDepth = SixLabors.ImageSharp.Formats.Png.PngBitDepth.Bit16 }); } +#else using (var img64 = new Image(30, 30)) { img64.SaveAsPng(path64); } +#endif try { var rgba64 = AnyBitmap.FromFile(path64); @@ -1296,7 +1300,11 @@ public void Redact_ShouldRedactRegionWithColor() using var image = new Image(Configuration.Default, 100, 100, Color.White); image.Save(memoryStream, new SixLabors.ImageSharp.Formats.Bmp.BmpEncoder() { +#if NET8_0_OR_GREATER + BitsPerPixel = SixLabors.ImageSharp.Formats.Bmp.BmpBitsPerPixel.Bit32, +#else BitsPerPixel = SixLabors.ImageSharp.Formats.Bmp.BmpBitsPerPixel.Pixel32, +#endif SupportTransparency = true }); diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/ColorFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/ColorFunctionality.cs index 41c1c7a1..08bdb1d5 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/ColorFunctionality.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/ColorFunctionality.cs @@ -284,14 +284,22 @@ public void Cast_ImageSharp_Color_from_Color() Assert.Equal(0, red.G); Assert.Equal(0, red.B); +#if NET8_0_OR_GREATER + imgColor = SixLabors.ImageSharp.Color.FromPixel(new SixLabors.ImageSharp.PixelFormats.Rgba32(0, 255, 0, 255)); +#else imgColor = SixLabors.ImageSharp.Color.FromRgba(0, 255, 0, 255); +#endif Color green = imgColor; Assert.Equal(255, green.A); Assert.Equal(0, green.R); Assert.Equal(255, green.G); Assert.Equal(0, green.B); +#if NET8_0_OR_GREATER + imgColor = SixLabors.ImageSharp.Color.FromPixel(new SixLabors.ImageSharp.PixelFormats.Rgb24(0, 0, 255)); +#else imgColor = SixLabors.ImageSharp.Color.FromRgb(0, 0, 255); +#endif Color blue = imgColor; Assert.Equal(255, blue.A); Assert.Equal(0, blue.R); @@ -322,7 +330,11 @@ public void Cast_ImageSharp_Color_to_Color() [FactWithAutomaticDisplayName] public void Cast_ImageSharp_Rgba32_from_Color() { +#if NET8_0_OR_GREATER + SixLabors.ImageSharp.PixelFormats.Rgba32 imgColor = SixLabors.ImageSharp.Color.Red.ToPixel(); +#else SixLabors.ImageSharp.PixelFormats.Rgba32 imgColor = SixLabors.ImageSharp.Color.Red; +#endif Color red = imgColor; Assert.Equal(255, red.A); Assert.Equal(255, red.R); @@ -367,7 +379,11 @@ public void Cast_ImageSharp_Rgba32_to_Color() [FactWithAutomaticDisplayName] public void Cast_ImageSharp_Rgb24_from_Color() { +#if NET8_0_OR_GREATER + SixLabors.ImageSharp.PixelFormats.Rgb24 imgColor = SixLabors.ImageSharp.Color.Red.ToPixel(); +#else SixLabors.ImageSharp.PixelFormats.Rgb24 imgColor = SixLabors.ImageSharp.Color.Red; +#endif Color red = imgColor; Assert.Equal(255, red.R); Assert.Equal(0, red.G); @@ -470,7 +486,11 @@ public void Cast_ImageSharp_Rgb48_to_Color() [FactWithAutomaticDisplayName] public void Cast_ImageSharp_Rgba64_from_Color() { +#if NET8_0_OR_GREATER + SixLabors.ImageSharp.PixelFormats.Rgba64 imgColor = SixLabors.ImageSharp.Color.Red.ToPixel(); +#else SixLabors.ImageSharp.PixelFormats.Rgba64 imgColor = SixLabors.ImageSharp.Color.Red; +#endif Color red = imgColor; Assert.Equal(255, red.R); Assert.Equal(0, red.G); diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs index 655c6470..a2f4b330 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs @@ -818,7 +818,11 @@ public AnyBitmap(int width, int height, Color backgroundColor = null) var image = new Image(width, height); if (backgroundColor != null) { +#if NET8_0_OR_GREATER + image.Mutate(context => context.Paint(canvas => canvas.Fill(new SolidBrush(backgroundColor)))); +#else image.Mutate(context => context.Fill(backgroundColor)); +#endif } return [image]; }); @@ -1524,7 +1528,11 @@ public static AnyBitmap Redact( Image image = Image.Load(bitmap.Binary); Rectangle rectangle = Rectangle; var brush = new SolidBrush(color); +#if NET8_0_OR_GREATER + image.Mutate(ctx => ctx.Paint(canvas => canvas.Fill(brush, rectangle))); +#else image.Mutate(ctx => ctx.Fill(brush, rectangle)); +#endif // Redact fills a region but leaves the rest of the image untouched, so it carries the // source's declared color depth as the (decoupled, in-memory) BitsPerPixel label, @@ -3735,14 +3743,14 @@ private IImageEncoder GetDefaultImageExportEncoder(ImageFormat format = ImageFor ImageFormat.Jpeg => new JpegEncoder() { Quality = lossy, -#if NET6_0_OR_GREATER +#if NET6_0_OR_GREATER && !NET8_0_OR_GREATER ColorType = JpegEncodingColor.Rgb #else ColorType = JpegColorType.Rgb #endif }, ImageFormat.Gif => new GifEncoder(), - ImageFormat.Png => new PngEncoder(), + ImageFormat.Png => GetDefaultPngEncoder(), ImageFormat.Webp => new WebpEncoder() { Quality = lossy }, ImageFormat.Tiff => new TiffEncoder() { @@ -3753,6 +3761,30 @@ private IImageEncoder GetDefaultImageExportEncoder(ImageFormat format = ImageFor }; } + private PngEncoder GetDefaultPngEncoder() + { +#if NET8_0_OR_GREATER + // ImageSharp v4's PngEncoder no longer auto-selects color type/bit depth from the + // source pixel format (it defaults to 8-bit RgbWithAlpha). Earlier versions preserved + // the image's depth on encode, which the color-depth features rely on. Re-derive the + // encoder settings from the in-memory depth so PNGs round-trip their BitsPerPixel. + int bpp = InMemoryBitsPerPixel; + return new PngEncoder + { + BitDepth = bpp >= 48 ? PngBitDepth.Bit16 : PngBitDepth.Bit8, + ColorType = bpp switch + { + 8 => PngColorType.Grayscale, + 24 => PngColorType.Rgb, + 48 => PngColorType.Rgb, + _ => PngColorType.RgbWithAlpha + } + }; +#else + return new PngEncoder(); +#endif + } + private static ImageFormat GetImageFormat(string filename) { if (string.IsNullOrEmpty(filename)) @@ -3809,7 +3841,11 @@ object ICloneable.Clone() /// private static IImageEncoder GetDefaultImageEncoder(int imageWidth, int imageHeight) { +#if NET8_0_OR_GREATER + return new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Bit32, SupportTransparency = true }; +#else return new BmpEncoder { BitsPerPixel = BmpBitsPerPixel.Pixel32, SupportTransparency = true }; +#endif } private static void InternalSaveAsMultiPageTiff(IEnumerable images, Stream stream) diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/Color.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common/Color.cs index 2bf4492e..fc36d145 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/Color.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/Color.cs @@ -1066,7 +1066,11 @@ public static implicit operator Color(SixLabors.ImageSharp.Color color) /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.Color(Color color) { +#if NET8_0_OR_GREATER + return SixLabors.ImageSharp.Color.FromPixel(new SixLabors.ImageSharp.PixelFormats.Rgba32(color.R, color.G, color.B, color.A)); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1087,7 +1091,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Rgba32 c /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Rgba32(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Rgba32(color.R, color.G, color.B, color.A); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1107,7 +1115,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Bgra32 c /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Bgra32(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Bgra32(color.R, color.G, color.B, color.A); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1127,7 +1139,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Rgb24 co /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Rgb24(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Rgb24(color.R, color.G, color.B); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1147,7 +1163,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Bgr24 co /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Bgr24(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Bgr24(color.R, color.G, color.B); +#else return SixLabors.ImageSharp.Color.FromRgb(color.R, color.G, color.B); +#endif } /// @@ -1157,7 +1177,11 @@ public static implicit operator SixLabors.ImageSharp.PixelFormats.Bgr24(Color co /// will automatically be casted to public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Rgb48 color) { +#if NET8_0_OR_GREATER + return (Color)SixLabors.ImageSharp.Color.FromPixel(new SixLabors.ImageSharp.PixelFormats.Rgb24((byte)(color.R >> 8), (byte)(color.G >> 8), (byte)(color.B >> 8))); +#else return (Color)SixLabors.ImageSharp.Color.FromRgb((byte)(color.R >> 8), (byte)(color.G >> 8), (byte)(color.B >> 8)); +#endif } /// @@ -1167,9 +1191,13 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Rgb48 co /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Rgb48(Color color) { +#if NET8_0_OR_GREATER + return SixLabors.ImageSharp.PixelFormats.Rgb48.FromRgba64((SixLabors.ImageSharp.PixelFormats.Rgba64)color); +#else var result = new SixLabors.ImageSharp.PixelFormats.Rgb48(); result.FromRgba64((SixLabors.ImageSharp.PixelFormats.Rgba64)color); return result; +#endif } /// @@ -1189,7 +1217,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Rgba64 c /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Rgba64(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Rgba64(new SixLabors.ImageSharp.PixelFormats.Rgba32(color.R, color.G, color.B, color.A)); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1209,7 +1241,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Abgr32 c /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Abgr32(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Abgr32(color.R, color.G, color.B, color.A); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// @@ -1229,7 +1265,11 @@ public static implicit operator Color(SixLabors.ImageSharp.PixelFormats.Argb32 c /// is explicitly cast to a public static implicit operator SixLabors.ImageSharp.PixelFormats.Argb32(Color color) { +#if NET8_0_OR_GREATER + return new SixLabors.ImageSharp.PixelFormats.Argb32(color.R, color.G, color.B, color.A); +#else return SixLabors.ImageSharp.Color.FromRgba(color.R, color.G, color.B, color.A); +#endif } /// diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj b/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj index c696cb3b..57622565 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj @@ -12,7 +12,7 @@ CS8002 AnyCPU true - netstandard2.0;net60 + netstandard2.0;net60;net80 true @@ -32,6 +32,12 @@ + + + + + + diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec index 0e64b704..b95c6c54 100644 --- a/NuGet/IronSoftware.Drawing.nuspec +++ b/NuGet/IronSoftware.Drawing.nuspec @@ -64,6 +64,12 @@ Bug Fixes + + + + + + @@ -73,6 +79,7 @@ Bug Fixes + \ No newline at end of file From 0e7d703a83ebf5f3c21a616177557f75a402c268 Mon Sep 17 00:00:00 2001 From: Sawraz Date: Fri, 9 Oct 2026 09:28:02 +0600 Subject: [PATCH 2/4] DW-38: support SixLabors.ImageSharp v4.1.2 on net8.0 via conditional TFM (CVE fixes incl. CVE-2026-106114), migrate v4 API surface, and wire Six Labors license into CI build --- NuGet/IronSoftware.Drawing.nuspec | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec index b95c6c54..9d06edcc 100644 --- a/NuGet/IronSoftware.Drawing.nuspec +++ b/NuGet/IronSoftware.Drawing.nuspec @@ -42,12 +42,7 @@ For general support and technical inquiries, please email us at: support@ironsof IronSoftware.System.Drawing is an open-source solution for .NET developers to replace System.Drawing.Common with a universal and flexible library. Features -- Added support for loading TIFF and BigTIFF files larger than 2 GB using a streaming loader, enabling page-by-page decoding without loading the entire file into memory -Enhancements -- AnyBitmap now preserves the original source color depth (BitsPerPixel) across derived operations, including frame extraction, cloning, rotation, redaction, and resizing -Bug Fixes -- Fixed an issue where derived AnyBitmap operations incorrectly reported a decoded 32bpp color depth instead of the original source pixel depth - +- Added a .NET 8 target using ImageSharp 4.1.2 and Drawing 3.1.2, enabling security fixes including CVE-2026-106114. Other targets remain unchanged. Copyright © Iron Software 2022-2026 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 From fb5405937c37e3e1480285d3d7c821375a32b88a Mon Sep 17 00:00:00 2001 From: Sawraz Date: Fri, 9 Oct 2026 09:57:52 +0600 Subject: [PATCH 3/4] DW-38: make net8 PNG export match ImageSharp 3 (keep source PNG color type/bit depth, per-pixel-type fallback for L16/La16/La32/A8 etc.) and add round-trip tests --- .../UnitTests/AnyBitmapFunctionality.cs | 77 +++++++++++++++++++ .../IronSoftware.Drawing.Common/AnyBitmap.cs | 40 ++++++---- NuGet/IronSoftware.Drawing.nuspec | 3 +- 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs index 993103be..1df011b9 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs @@ -1,6 +1,7 @@ using BitMiracle.LibTiff.Classic; using FluentAssertions; using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -1172,6 +1173,82 @@ public void Resize_ShouldPreserveDepthForRepresentableFormats() } } + [TheoryWithAutomaticDisplayName] + [InlineData("A8", "GrayscaleWithAlpha", "Bit8", 16)] + [InlineData("L8", "Grayscale", "Bit8", 8)] + [InlineData("L16", "Grayscale", "Bit16", 16)] + [InlineData("La16", "GrayscaleWithAlpha", "Bit8", 16)] + [InlineData("La32", "GrayscaleWithAlpha", "Bit16", 32)] + [InlineData("Rgb24", "Rgb", "Bit8", 24)] + [InlineData("Bgr24", "Rgb", "Bit8", 24)] + [InlineData("Rgb48", "Rgb", "Bit16", 48)] + [InlineData("Rgba32", "RgbWithAlpha", "Bit8", 32)] + [InlineData("Bgra32", "RgbWithAlpha", "Bit8", 32)] + [InlineData("Rgba64", "RgbWithAlpha", "Bit16", 64)] + [InlineData("RgbaVector", "RgbWithAlpha", "Bit16", 64)] + [InlineData("Bgr565", "RgbWithAlpha", "Bit8", 32)] + public void ExportPng_ShouldChooseColorTypeAndBitDepthFromPixelType( + string pixelType, string expectedColorType, string expectedBitDepth, int expectedBitsPerPixel) + { + using Image source = pixelType switch + { + "A8" => new Image(4, 4), + "L8" => new Image(4, 4), + "L16" => new Image(4, 4), + "La16" => new Image(4, 4), + "La32" => new Image(4, 4), + "Rgb24" => new Image(4, 4), + "Bgr24" => new Image(4, 4), + "Rgb48" => new Image(4, 4), + "Rgba32" => new Image(4, 4), + "Bgra32" => new Image(4, 4), + "Rgba64" => new Image(4, 4), + "RgbaVector" => new Image(4, 4), + "Bgr565" => new Image(4, 4), + _ => throw new ArgumentOutOfRangeException(nameof(pixelType)) + }; + AnyBitmap bitmap = source; + + byte[] png = bitmap.ExportBytes(AnyBitmap.ImageFormat.Png); + + using (Image reloaded = Image.Load(png)) + { + var pngMetadata = reloaded.Metadata.GetPngMetadata(); + Assert.Equal(expectedColorType, pngMetadata.ColorType.ToString()); + Assert.Equal(expectedBitDepth, pngMetadata.BitDepth.ToString()); + } + Assert.Equal(expectedBitsPerPixel, AnyBitmap.FromBytes(png).BitsPerPixel); + } + + [TheoryWithAutomaticDisplayName] + [InlineData("Palette", "Bit8")] + [InlineData("Grayscale", "Bit1")] + [InlineData("Grayscale", "Bit16")] + public void ExportPng_ShouldKeepSourcePngColorTypeAndBitDepthWhenReencoding(string colorType, string bitDepth) + { + byte[] sourcePng; + using (var source = new Image(8, 8, new Rgba32(255, 255, 255, 255))) + using (var stream = new MemoryStream()) + { + source.Save(stream, new SixLabors.ImageSharp.Formats.Png.PngEncoder + { + ColorType = (SixLabors.ImageSharp.Formats.Png.PngColorType)Enum.Parse(typeof(SixLabors.ImageSharp.Formats.Png.PngColorType), colorType), + BitDepth = (SixLabors.ImageSharp.Formats.Png.PngBitDepth)Enum.Parse(typeof(SixLabors.ImageSharp.Formats.Png.PngBitDepth), bitDepth) + }); + sourcePng = stream.ToArray(); + } + + // RotateFlip decodes and re-encodes, so the output goes through the PNG encoder + // instead of returning the original bytes. + var rotated = AnyBitmap.FromBytes(sourcePng).RotateFlip(AnyBitmap.RotateMode.Rotate180, AnyBitmap.FlipMode.None); + byte[] png = rotated.ExportBytes(AnyBitmap.ImageFormat.Png); + + using Image reloaded = Image.Load(png); + var pngMetadata = reloaded.Metadata.GetPngMetadata(); + Assert.Equal(colorType, pngMetadata.ColorType.ToString()); + Assert.Equal(bitDepth, pngMetadata.BitDepth.ToString()); + } + [FactWithAutomaticDisplayName] public void Resize_ShouldPreserveFrameCountForMultiPageTiff() { diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs index a2f4b330..f1bf65b2 100644 --- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs +++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs @@ -3764,22 +3764,32 @@ private IImageEncoder GetDefaultImageExportEncoder(ImageFormat format = ImageFor private PngEncoder GetDefaultPngEncoder() { #if NET8_0_OR_GREATER - // ImageSharp v4's PngEncoder no longer auto-selects color type/bit depth from the - // source pixel format (it defaults to 8-bit RgbWithAlpha). Earlier versions preserved - // the image's depth on encode, which the color-depth features rely on. Re-derive the - // encoder settings from the in-memory depth so PNGs round-trip their BitsPerPixel. - int bpp = InMemoryBitsPerPixel; - return new PngEncoder - { - BitDepth = bpp >= 48 ? PngBitDepth.Bit16 : PngBitDepth.Bit8, - ColorType = bpp switch - { - 8 => PngColorType.Grayscale, - 24 => PngColorType.Rgb, - 48 => PngColorType.Rgb, - _ => PngColorType.RgbWithAlpha - } + // ImageSharp 3 picked the PNG color type/bit depth as: source PngMetadata if the image + // came from a PNG, otherwise a suggestion based on the pixel type. ImageSharp 4 still + // honors the source PngMetadata, but for any other source it falls back to 8-bit + // RgbWithAlpha. Reproduce the ImageSharp 3 behavior so net8 output matches net6. + Image image = GetFirstInternalImage(); + if (image.Metadata.DecodedImageFormat is PngFormat) + { + return new PngEncoder(); + } + + // Same per-pixel-type table as ImageSharp 3's encoder (anything not listed fell back + // to 8-bit RgbWithAlpha there too). + (PngColorType colorType, PngBitDepth bitDepth) = image switch + { + Image => (PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8), + Image => (PngColorType.Grayscale, PngBitDepth.Bit8), + Image => (PngColorType.Grayscale, PngBitDepth.Bit16), + Image => (PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8), + Image => (PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit16), + Image or Image => (PngColorType.Rgb, PngBitDepth.Bit8), + Image => (PngColorType.Rgb, PngBitDepth.Bit16), + Image or Image => (PngColorType.RgbWithAlpha, PngBitDepth.Bit16), + _ => (PngColorType.RgbWithAlpha, PngBitDepth.Bit8) }; + + return new PngEncoder { ColorType = colorType, BitDepth = bitDepth }; #else return new PngEncoder(); #endif diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec index 9d06edcc..9b046212 100644 --- a/NuGet/IronSoftware.Drawing.nuspec +++ b/NuGet/IronSoftware.Drawing.nuspec @@ -42,7 +42,8 @@ For general support and technical inquiries, please email us at: support@ironsof IronSoftware.System.Drawing is an open-source solution for .NET developers to replace System.Drawing.Common with a universal and flexible library. Features -- Added a .NET 8 target using ImageSharp 4.1.2 and Drawing 3.1.2, enabling security fixes including CVE-2026-106114. Other targets remain unchanged. +- Added a .NET 8 target using ImageSharp 4.1.2 and Drawing 3.1.2, enabling security fixes including CVE-2026-106114. Other targets remain unchanged. + Copyright © Iron Software 2022-2026 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 From e75cfb2de5e13e5a4e9bb4c1cf9b384774870c77 Mon Sep 17 00:00:00 2001 From: Sawraz Date: Fri, 9 Oct 2026 17:05:13 +0600 Subject: [PATCH 4/4] add exlude for license check --- NuGet/IronSoftware.Drawing.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec index 9b046212..a164f8c9 100644 --- a/NuGet/IronSoftware.Drawing.nuspec +++ b/NuGet/IronSoftware.Drawing.nuspec @@ -62,8 +62,8 @@ Features - - + +