diff --git a/CI/job_templates/test_drawing_libraries.yml b/CI/job_templates/test_drawing_libraries.yml
index ac8c71e..d6a6b42 100644
--- a/CI/job_templates/test_drawing_libraries.yml
+++ b/CI/job_templates/test_drawing_libraries.yml
@@ -20,6 +20,14 @@ jobs:
buildType: 'current'
artifactName: 'IronDrawingDataTests'
targetPath: '$(Agent.BuildDirectory)/Data'
+ - ${{ if or(eq(parameters.OSPlatform, 'Ubuntu'), eq(parameters.OSPlatform, 'Linux')) }}:
+ - task: Bash@3
+ displayName: 'Install GDI+ dependencies'
+ inputs:
+ targetType: 'inline'
+ script: |
+ sudo apt-get update
+ sudo apt-get install -y libgdiplus libc6-dev
- ${{ if eq(parameters.framework, 'netcoreapp3.1') }}:
- task: UseDotNet@2
displayName: 'Install .Netcoreapp3.1 Core sdk'
diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs
index b563d9b..83f30a5 100644
--- a/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs
+++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common.Tests/UnitTests/AnyBitmapFunctionality.cs
@@ -942,6 +942,26 @@ public void LoadImage_TiffImage_ShouldLoadWithoutThumbnail()
bitmap.FrameCount.Should().Be(1);
}
+ [TheoryWithAutomaticDisplayName]
+ [InlineData("24_bit.png")]
+ [InlineData("checkmark.jpg")]
+ [InlineData("DW-26 Jpg72Input.jpg")]
+ [InlineData("DW-26 Jpg300Input.jpg")]
+ [InlineData("mountainclimbers.jpg")]
+ public void LoadImage_SetPreserveOriginalFormat_ShouldReturnCorrectBitPerPixel(string imageFileName)
+ {
+ // Arrange
+ string imagePath = GetRelativeFilePath(imageFileName);
+
+ // Act
+ var preserve = new AnyBitmap(imagePath, true);
+ var notPreserve = new AnyBitmap(imagePath, false);
+
+ // Assert
+ Assert.Equal(24, preserve.BitsPerPixel);
+ Assert.Equal(32, notPreserve.BitsPerPixel);
+ }
+
#if !NET7_0
[FactWithAutomaticDisplayName]
public void CastAnyBitmap_from_SixLabors()
diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs
index 94503d4..5f3a22d 100644
--- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs
+++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/AnyBitmap.cs
@@ -52,6 +52,7 @@ public partial class AnyBitmap : IDisposable, IAnyImage
private byte[] Binary { get; set; }
private IImageFormat Format { get; set; }
private TiffCompression TiffCompression { get; set; } = TiffCompression.Lzw;
+ private bool PreserveOriginalFormat { get; set; } = true;
///
/// Width of the image.
@@ -138,7 +139,7 @@ public MemoryStream GetStream()
///
public AnyBitmap Clone()
{
- return new AnyBitmap(Binary);
+ return new AnyBitmap(Binary, PreserveOriginalFormat);
}
///
@@ -462,87 +463,99 @@ public T ToBitmap()
/// Create a new Bitmap from a a Byte Span.
///
/// A Byte Span of image data in any common format.
- public static AnyBitmap FromSpan(ReadOnlySpan span)
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ public static AnyBitmap FromSpan(ReadOnlySpan span, bool preserveOriginalFormat = true)
{
- return new AnyBitmap(span);
+ return new AnyBitmap(span, preserveOriginalFormat);
}
///
/// Create a new Bitmap from a a Byte Array.
///
/// A ByteArray of image data in any common format.
- public static AnyBitmap FromBytes(byte[] bytes)
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ public static AnyBitmap FromBytes(byte[] bytes, bool preserveOriginalFormat = true)
{
- return new AnyBitmap(bytes);
+ return new AnyBitmap(bytes, preserveOriginalFormat);
}
///
/// Create a new Bitmap from a (bytes).
///
- /// A of image data in any
- /// common format.
- ///
+ /// A of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ ///
///
- public static AnyBitmap FromStream(MemoryStream stream)
+ public static AnyBitmap FromStream(MemoryStream stream, bool preserveOriginalFormat = true)
{
- return new AnyBitmap(stream);
+ return new AnyBitmap(stream, preserveOriginalFormat);
}
///
/// Create a new Bitmap from a (bytes).
///
- /// A of image data in any
- /// common format.
- ///
+ /// A of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ ///
///
- public static AnyBitmap FromStream(Stream stream)
+ public static AnyBitmap FromStream(Stream stream, bool preserveOriginalFormat = true)
{
- return new AnyBitmap(stream);
+ return new AnyBitmap(stream, preserveOriginalFormat);
}
///
/// Construct a new Bitmap from binary data (byte span).
///
/// A byte span of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
- public AnyBitmap(ReadOnlySpan span)
+ public AnyBitmap(ReadOnlySpan span, bool preserveOriginalFormat = true)
{
- LoadImage(span);
+ LoadImage(span, preserveOriginalFormat);
}
///
/// Construct a new Bitmap from binary data (bytes).
///
/// A ByteArray of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
- public AnyBitmap(byte[] bytes)
+ public AnyBitmap(byte[] bytes, bool preserveOriginalFormat = true)
{
- LoadImage(bytes);
+ LoadImage(bytes, preserveOriginalFormat);
}
///
/// Construct a new Bitmap from a (bytes).
///
- /// A of image data in any
- /// common format.
- ///
+ /// A of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ ///
///
- public AnyBitmap(MemoryStream stream)
+ public AnyBitmap(MemoryStream stream, bool preserveOriginalFormat = true)
{
- LoadImage(stream.ToArray());
+ LoadImage(stream.ToArray(), preserveOriginalFormat);
}
///
/// Construct a new Bitmap from a (bytes).
///
- /// A of image data in any
- /// common format.
- ///
+ /// A of image data in any common format.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
+ ///
///
- public AnyBitmap(Stream stream)
+ public AnyBitmap(Stream stream, bool preserveOriginalFormat = true)
{
- LoadImage(stream);
+ LoadImage(stream, preserveOriginalFormat);
}
///
@@ -561,25 +574,29 @@ public AnyBitmap(AnyBitmap original, int width, int height)
/// Construct a new Bitmap from a file.
///
/// A fully qualified file path./
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
- public AnyBitmap(string file)
+ public AnyBitmap(string file, bool preserveOriginalFormat = true)
{
- LoadImage(File.ReadAllBytes(file));
+ LoadImage(File.ReadAllBytes(file), preserveOriginalFormat);
}
///
/// Construct a new Bitmap from a Uri
///
/// The uri of the image.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
- public AnyBitmap(Uri uri)
+ public AnyBitmap(Uri uri, bool preserveOriginalFormat = true)
{
try
{
using Stream stream = LoadUriAsync(uri).GetAwaiter().GetResult();
- LoadImage(stream);
+ LoadImage(stream, preserveOriginalFormat);
}
catch (Exception e)
{
@@ -602,17 +619,19 @@ public AnyBitmap(int width, int height, Color backgroundColor = null)
/// Create a new Bitmap from a file.
///
/// A fully qualified file path.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
- public static AnyBitmap FromFile(string file)
+ public static AnyBitmap FromFile(string file, bool preserveOriginalFormat = true)
{
if (file.ToLower().EndsWith(".svg"))
{
- return LoadSVGImage(file);
+ return LoadSVGImage(file, preserveOriginalFormat);
}
else
{
- return new AnyBitmap(file);
+ return new AnyBitmap(file, preserveOriginalFormat);
}
}
@@ -620,16 +639,18 @@ public static AnyBitmap FromFile(string file)
/// Construct a new Bitmap from a Uri
///
/// The uri of the image.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
///
///
- public static async Task FromUriAsync(Uri uri)
+ public static async Task FromUriAsync(Uri uri, bool preserveOriginalFormat = true)
{
try
{
using Stream stream = await LoadUriAsync(uri);
- return new AnyBitmap(stream);
+ return new AnyBitmap(stream, preserveOriginalFormat);
}
catch (Exception e)
{
@@ -641,13 +662,15 @@ public static async Task FromUriAsync(Uri uri)
/// Construct a new Bitmap from a Uri
///
/// The uri of the image.
+ /// Determine whether to load as its original pixel format or Rgba32.
+ /// Default is true. Set to false to load as Rgba32.
///
///
///
#if NET6_0_OR_GREATER
[Obsolete("FromUri(Uri) is obsolete for net60 or greater because it uses WebClient which is obsolete. Consider using FromUriAsync(Uri) method.")]
#endif
- public static AnyBitmap FromUri(Uri uri)
+ public static AnyBitmap FromUri(Uri uri, bool preserveOriginalFormat = true)
{
try
{
@@ -2017,19 +2040,33 @@ private void CreateNewImageInstance(int width, int height, Color backgroundColor
Binary = stream.ToArray();
}
- private void LoadImage(ReadOnlySpan bytes)
+ private void LoadImage(ReadOnlySpan bytes, bool preserveOriginalFormat)
{
Format = Image.DetectFormat(bytes);
try
{
if (Format is TiffFormat)
OpenTiffToImageSharp(bytes);
-
else
{
Binary = bytes.ToArray();
- Image = Image.Load(bytes);
-
+
+ if (preserveOriginalFormat)
+ Image = Image.Load(bytes);
+ else
+ {
+ PreserveOriginalFormat = preserveOriginalFormat;
+ Image = Image.Load(bytes);
+
+ // .png image pre-processing
+ if (Format.Name == "PNG")
+ Image.Mutate(img => img.BackgroundColor(SixLabors.ImageSharp.Color.White));
+ }
+
+ // Fix if the input image is auto-rotated; this issue is acknowledged by SixLabors.ImageSharp community
+ // ref: https://github.com/SixLabors/ImageSharp/discussions/2685
+ Image.Mutate(x => x.AutoOrient());
+
var resolutionUnit = this.Image.Metadata.ResolutionUnits;
var horizontal = this.Image.Metadata.HorizontalResolution;
var vertical = this.Image.Metadata.VerticalResolution;
@@ -2067,7 +2104,7 @@ private void LoadImage(ReadOnlySpan bytes)
}
}
- private void LoadImage(Stream stream)
+ private void LoadImage(Stream stream, bool preserveOriginalFormat)
{
byte[] buffer = new byte[16 * 1024];
using MemoryStream ms = new();
@@ -2077,17 +2114,17 @@ private void LoadImage(Stream stream)
ms.Write(buffer, 0, read);
}
- LoadImage(ms.ToArray());
+ LoadImage(ms.ToArray(), preserveOriginalFormat);
}
- private static AnyBitmap LoadSVGImage(string file)
+ private static AnyBitmap LoadSVGImage(string file, bool preserveOriginalFormat)
{
try
{
return new AnyBitmap(
DecodeSVG(file).Encode(SKEncodedImageFormat.Png, 100)
- .ToArray());
+ .ToArray(), preserveOriginalFormat);
}
catch (DllNotFoundException e)
{
@@ -2233,6 +2270,21 @@ private static SKBitmap OpenTiffToSKBitmap(AnyBitmap anyBitmap)
}
}
+ ///
+ /// Disable warning message written to console by BitMiracle.LibTiff.NET.
+ ///
+ private class DisableErrorHandler : TiffErrorHandler
+ {
+ public override void WarningHandler(Tiff tif, string method, string format, params object[] args)
+ {
+ // do nothing, ie, do not write warnings to console
+ }
+ public override void WarningHandlerExt(Tiff tif, object clientData, string method, string format, params object[] args)
+ {
+ // do nothing ie, do not write warnings to console
+ }
+ }
+
private void OpenTiffToImageSharp(ReadOnlySpan bytes)
{
try
@@ -2246,6 +2298,9 @@ private void OpenTiffToImageSharp(ReadOnlySpan bytes)
// create a memory stream out of them
using MemoryStream tiffStream = new(bytes.ToArray());
+ // Disable warning messages
+ Tiff.SetErrorHandler(new DisableErrorHandler());
+
// open a TIFF stored in the stream
using (Tiff tiff = Tiff.ClientOpen("in-memory", "r", tiffStream, new TiffStream()))
{
diff --git a/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj b/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj
index af58cb4..ec774df 100644
--- a/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj
+++ b/IronSoftware.Drawing/IronSoftware.Drawing.Common/IronSoftware.Drawing.Common.csproj
@@ -1,4 +1,4 @@
-
+
IronSoftware.Drawing.Common.snk
@@ -25,7 +25,7 @@
-
+
diff --git a/NuGet/IronSoftware.Drawing.nuspec b/NuGet/IronSoftware.Drawing.nuspec
index 730882b..0946583 100644
--- a/NuGet/IronSoftware.Drawing.nuspec
+++ b/NuGet/IronSoftware.Drawing.nuspec
@@ -39,23 +39,23 @@ 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.
- - Fixes incorrect HorizontalResolution and VerticalResolution of AnyBitmap images.
- - Improves library's internal functionality and performance.
+ - Updates internal dependencies.
+ - Disable warning messages from BitMiracle.LibTiff.NET.
Copyright © Iron Software 2022-2025
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
-
+
-
-
-
+
+
+