-
-
Notifications
You must be signed in to change notification settings - Fork 886
Description
Prerequisites
- I have written a descriptive issue title
- I have verified that I am running the latest version of ImageSharp
- I have verified if the problem exist in both
DEBUGandRELEASEmode - I have searched open and closed issues to ensure it has not already been reported
ImageSharp version
4.0.0-alpha.0.55
Other ImageSharp packages and versions
Drawing: 3.0.0-alpha.0.6
Environment (Operating system, version and so on)
Ubuntu 24.04
.NET Framework version
.net 9
Description
When loading a PNG image, the ICC Profile does not seems to be converted correctly.
It seems to be something missing in the PNGDecoder since I was able to manually get the right result by applying the profile manually
Also even if the profile is not applied right now, setting ColorProfileHandling = ColorProfileHandling.Convert remove the profile from the image for a png
Note: I think having a .Mutate(x => x.DrawImageAndConvertIccProfile()) could be a nice addition to make it easier to manage collage of multiple images using various ICC profile without losing quality by comparing everything to sRGB first (or the DrawImage could have an option to convert to the destination image color profile) Should I create another issue for this ?
Steps to Reproduce
For images from https://displaycal.net/icc-color-management-test/
using var pngTestImage1 = Image.Load(new DecoderOptions {ColorProfileHandling = ColorProfileHandling.Convert}, "icc-tests/ICC Rendering Intent Test.png");
using var pngTestImage2 = Image.Load(new DecoderOptions {ColorProfileHandling = ColorProfileHandling.Convert}, "icc-tests/ICC Rendering Intent Test (cLUT only).png");
using var pngTestImage3 = Image.Load(new DecoderOptions {ColorProfileHandling = ColorProfileHandling.Convert}, "icc-tests/sRGB_Gray.png");
using var testCanvas1 = new Image<Rgba32>(pngTestImage1.Width, pngTestImage1.Height);
testCanvas1.Mutate(x => x.DrawImage(pngTestImage1, 1.0f));
testCanvas1.Save("icc-test-1.png");
using var testCanvas2 = new Image<Rgba32>(pngTestImage2.Width, pngTestImage2.Height);
testCanvas2.Mutate(x => x.DrawImage(pngTestImage2, 1.0f));
testCanvas2.Save("icc-test-2.png");
using var testCanvas3 = new Image<Rgba32>(pngTestImage3.Width, pngTestImage3.Height);
testCanvas3.Mutate(x => x.DrawImage(pngTestImage3, 1.0f));
testCanvas3.Save("icc-test-3.png");For Image from: https://github.com/svgeesus/PNG-ICC-tests
using var imgReference = Image.Load("PNG-ICC-tests/tests/support/reference-circles.png");
var files = Directory.EnumerateFiles("PNG-ICC-tests/tests/support", "*.png");
foreach (var file in files)
{
using var img = Image.Load(new DecoderOptions
{
ColorProfileHandling = ColorProfileHandling.Convert,
},
file
);
using var img2 = new Image<Rgb24>(img.Width, img.Height);
img2.Mutate(x => x.DrawImage(img, 1f));
img2.Mutate(x => x.DrawImage(imgReference, 1f));
img2.Save($"test-{Path.GetFileName(file)}");
}Works with manual conversion
using var imgReference = Image.Load("PNG-ICC-tests/tests/support/reference-circles.png");
var files = Directory.EnumerateFiles("PNG-ICC-tests/tests/support", "*.png");
foreach (var file in files)
{
using var img = Image.Load<Rgba32>(new DecoderOptions
{
ColorProfileHandling = ColorProfileHandling.Preserve,
},
file
);
using var img2 = new Image<Rgba32>(img.Width, img.Height);
//img2.Mutate(x => x.DrawImage(img, 1f));
WriteImageWithIccProfile(img, img2, new IccProfile(File.ReadAllBytes("sRGB2014.icc")));
img2.Mutate(x => x.DrawImage(imgReference, 1f));
img2.Save($"test-{Path.GetFileName(file)}");
}WriteImageWithIccProfile(pngTestImage1, testCanvas1, new IccProfile(File.ReadAllBytes("sRGB2014.icc")));
testCanvas1.Save("icc-test-1.png");
using var testCanvas2 = new Image<Rgba32>(pngTestImage2.Width, pngTestImage2.Height);
WriteImageWithIccProfile(pngTestImage2, testCanvas2, new IccProfile(File.ReadAllBytes("sRGB2014.icc")));
testCanvas2.Save("icc-test-2.png");
using var testCanvas3 = new Image<Rgba32>(pngTestImage3.Width, pngTestImage3.Height);
WriteImageWithIccProfile(pngTestImage3, testCanvas3, new IccProfile(File.ReadAllBytes("sRGB2014.icc")));
testCanvas3.Save("icc-test-2.png");
// I know this is bad code, (very poor performance I think, it's just to demonstrate that the ICC profile are just ignored)
void WriteImageWithIccProfile(Image<Rgba32> source, Image<Rgba32> destination, IccProfile profile)
{
ColorConversionOptions options = new()
{
SourceIccProfile = source.Metadata.IccProfile,
TargetIccProfile = profile,
};
ColorProfileConverter converter = new(options);
source.ProcessPixelRows(destination, (sourceAccessor, targetAccessor) =>
{
for (var y = 0; y < sourceAccessor.Height; y++)
{
Span<Rgb> tmpSource = new Rgb[sourceAccessor.Width];
var sourceRow = sourceAccessor.GetRowSpan(y);
for (var index = 0; index < sourceRow.Length; index++)
{
var rgba32 = sourceRow[index];
tmpSource[index] = new Rgb(rgba32.R / 255.0f, rgba32.G / 255.0f, rgba32.B / 255.0f);
}
Span<Rgb> tmpDest = new Rgb[sourceAccessor.Width];
converter.Convert<Rgb, Rgb>(tmpSource, tmpDest);
var targetRow = targetAccessor.GetRowSpan(y);
for (var index = 0; index < targetRow.Length; index++)
{
var rgb = tmpDest[index];
targetRow[index] = new Rgba32(rgb.R, rgb.G, rgb.B);
}
}
});
}Images
No response