|
7 | 7 | using System.Globalization; |
8 | 8 | using System.IO.Compression; |
9 | 9 | using System.IO.Hashing; |
| 10 | +using System.Numerics; |
10 | 11 | using System.Runtime.CompilerServices; |
11 | 12 | using System.Runtime.InteropServices; |
12 | 13 | using System.Text; |
| 14 | +using SixLabors.ImageSharp.ColorProfiles; |
| 15 | +using SixLabors.ImageSharp.ColorProfiles.Icc; |
13 | 16 | using SixLabors.ImageSharp.Common.Helpers; |
14 | 17 | using SixLabors.ImageSharp.Compression.Zlib; |
15 | 18 | using SixLabors.ImageSharp.Formats.Png.Chunks; |
@@ -323,6 +326,11 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance |
323 | 326 | PngThrowHelper.ThrowNoData(); |
324 | 327 | } |
325 | 328 |
|
| 329 | + if (this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile)) |
| 330 | + { |
| 331 | + ApplyIccProfile(image, iccProfile, CompactSrgbV4Profile.Profile); |
| 332 | + } |
| 333 | + |
326 | 334 | return image; |
327 | 335 | } |
328 | 336 | catch |
@@ -2153,4 +2161,58 @@ private static bool IsXmpTextData(ReadOnlySpan<byte> keywordBytes) |
2153 | 2161 |
|
2154 | 2162 | private void SwapScanlineBuffers() |
2155 | 2163 | => (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 | + |
2156 | 2218 | } |
0 commit comments