Browse Source

Apply color conversion when decoding PNG

pull/3028/head
Socolin 2 months ago
parent
commit
e965d3fac6
  1. 62
      src/ImageSharp/Formats/Png/PngDecoderCore.cs

62
src/ImageSharp/Formats/Png/PngDecoderCore.cs

@ -7,9 +7,12 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO.Compression;
using System.IO.Hashing;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using SixLabors.ImageSharp.ColorProfiles;
using SixLabors.ImageSharp.ColorProfiles.Icc;
using SixLabors.ImageSharp.Common.Helpers;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Png.Chunks;
@ -323,6 +326,11 @@ internal sealed class PngDecoderCore : ImageDecoderCore
PngThrowHelper.ThrowNoData();
}
if (this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile))
{
ApplyIccProfile(image, iccProfile, CompactSrgbV4Profile.Profile);
}
return image;
}
catch
@ -2153,4 +2161,58 @@ internal sealed class PngDecoderCore : ImageDecoderCore
private void SwapScanlineBuffers()
=> (this.scanline, this.previousScanline) = (this.previousScanline, this.scanline);
// FIXME: Maybe this could be a .Mutate(x => x.ApplyIccProfile(destinationProfile)) ? Nothing related to png here
private static void ApplyIccProfile<TPixel>(Image<TPixel> image, IccProfile sourceProfile, IccProfile destinationProfile)
where TPixel : unmanaged, IPixel<TPixel>
{
ColorConversionOptions options = new()
{
SourceIccProfile = sourceProfile,
TargetIccProfile = destinationProfile,
};
ColorProfileConverter converter = new(options);
image.ProcessPixelRows(pixelAccessor =>
{
using IMemoryOwner<float> rgbBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width * 3);
using IMemoryOwner<float> alphaBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width);
Span<float> rgbPacked = rgbBuffer.Memory.Span;
ref float rgbPackedRef = ref MemoryMarshal.GetReference(rgbPacked);
Span<float> alphaPacked = alphaBuffer.Memory.Span;
ref float alphaPackedRef = ref MemoryMarshal.GetReference(alphaPacked);
for (int y = 0; y < pixelAccessor.Height; y++)
{
Span<TPixel> pixelsRow = pixelAccessor.GetRowSpan(y);
int rgbIdx = 0;
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
{
Vector4 rgba = pixelsRow[x].ToScaledVector4();
Unsafe.Add(ref rgbPackedRef, rgbIdx) = rgba.X;
Unsafe.Add(ref rgbPackedRef, rgbIdx + 1) = rgba.Y;
Unsafe.Add(ref rgbPackedRef, rgbIdx + 2) = rgba.Z;
Unsafe.Add(ref alphaPackedRef, x) = rgba.W;
}
Span<Rgb> source = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
converter.Convert<Rgb, Rgb>(source, destination);
rgbIdx = 0;
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
{
float r = Unsafe.Add(ref rgbPackedRef, rgbIdx);
float g = Unsafe.Add(ref rgbPackedRef, rgbIdx + 1);
float b = Unsafe.Add(ref rgbPackedRef, rgbIdx + 2);
float a = Unsafe.Add(ref alphaPackedRef, x);
pixelsRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, a));
}
}
}
);
}
}

Loading…
Cancel
Save