Skip to content

Commit c7e8302

Browse files
committed
Apply color conversion when decoding PNG
1 parent 8df5efe commit c7e8302

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@
77
using System.Globalization;
88
using System.IO.Compression;
99
using System.IO.Hashing;
10+
using System.Numerics;
1011
using System.Runtime.CompilerServices;
1112
using System.Runtime.InteropServices;
1213
using System.Text;
14+
using SixLabors.ImageSharp.ColorProfiles;
15+
using SixLabors.ImageSharp.ColorProfiles.Icc;
1316
using SixLabors.ImageSharp.Common.Helpers;
1417
using SixLabors.ImageSharp.Compression.Zlib;
1518
using SixLabors.ImageSharp.Formats.Png.Chunks;
@@ -323,6 +326,11 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
323326
PngThrowHelper.ThrowNoData();
324327
}
325328

329+
if (this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile))
330+
{
331+
ApplyIccProfile(image, iccProfile, CompactSrgbV4Profile.Profile);
332+
}
333+
326334
return image;
327335
}
328336
catch
@@ -2153,4 +2161,58 @@ private static bool IsXmpTextData(ReadOnlySpan<byte> keywordBytes)
21532161

21542162
private void SwapScanlineBuffers()
21552163
=> (this.scanline, this.previousScanline) = (this.previousScanline, this.scanline);
2164+
2165+
// FIXME: Maybe this could be a .Mutate(x => x.ApplyIccProfile(destinationProfile)) ? Nothing related to png here
2166+
private static void ApplyIccProfile<TPixel>(Image<TPixel> image, IccProfile sourceProfile, IccProfile destinationProfile)
2167+
where TPixel : unmanaged, IPixel<TPixel>
2168+
{
2169+
ColorConversionOptions options = new()
2170+
{
2171+
SourceIccProfile = sourceProfile,
2172+
TargetIccProfile = destinationProfile,
2173+
};
2174+
2175+
ColorProfileConverter converter = new(options);
2176+
2177+
image.ProcessPixelRows(pixelAccessor =>
2178+
{
2179+
using IMemoryOwner<float> rgbBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width * 3);
2180+
using IMemoryOwner<float> alphaBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width);
2181+
Span<float> rgbPacked = rgbBuffer.Memory.Span;
2182+
ref float rgbPackedRef = ref MemoryMarshal.GetReference(rgbPacked);
2183+
Span<float> alphaPacked = alphaBuffer.Memory.Span;
2184+
ref float alphaPackedRef = ref MemoryMarshal.GetReference(alphaPacked);
2185+
2186+
for (int y = 0; y < pixelAccessor.Height; y++)
2187+
{
2188+
Span<TPixel> pixelsRow = pixelAccessor.GetRowSpan(y);
2189+
int rgbIdx = 0;
2190+
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
2191+
{
2192+
Vector4 rgba = pixelsRow[x].ToScaledVector4();
2193+
Unsafe.Add(ref rgbPackedRef, rgbIdx) = rgba.X;
2194+
Unsafe.Add(ref rgbPackedRef, rgbIdx + 1) = rgba.Y;
2195+
Unsafe.Add(ref rgbPackedRef, rgbIdx + 2) = rgba.Z;
2196+
Unsafe.Add(ref alphaPackedRef, x) = rgba.W;
2197+
}
2198+
2199+
Span<Rgb> source = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
2200+
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
2201+
converter.Convert<Rgb, Rgb>(source, destination);
2202+
2203+
rgbIdx = 0;
2204+
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
2205+
{
2206+
float r = Unsafe.Add(ref rgbPackedRef, rgbIdx);
2207+
float g = Unsafe.Add(ref rgbPackedRef, rgbIdx + 1);
2208+
float b = Unsafe.Add(ref rgbPackedRef, rgbIdx + 2);
2209+
float a = Unsafe.Add(ref alphaPackedRef, x);
2210+
2211+
pixelsRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, a));
2212+
}
2213+
}
2214+
}
2215+
);
2216+
}
2217+
21562218
}

0 commit comments

Comments
 (0)