Skip to content

Commit 1b6108a

Browse files
committed
Apply Icc Profile on webp
1 parent 884efe5 commit 1b6108a

File tree

9 files changed

+101
-55
lines changed

9 files changed

+101
-55
lines changed

src/ImageSharp/Formats/Png/PngDecoderCore.cs

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
2727
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
2828
using SixLabors.ImageSharp.PixelFormats;
29+
using SixLabors.ImageSharp.Processing;
2930

3031
namespace SixLabors.ImageSharp.Formats.Png;
3132

@@ -328,7 +329,7 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
328329

329330
if (this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile))
330331
{
331-
ApplyIccProfile(image, iccProfile, CompactSrgbV4Profile.Profile);
332+
image.ApplyIccProfile(iccProfile, CompactSrgbV4Profile.Profile);
332333
}
333334

334335
return image;
@@ -2161,58 +2162,4 @@ private static bool IsXmpTextData(ReadOnlySpan<byte> keywordBytes)
21612162

21622163
private void SwapScanlineBuffers()
21632164
=> (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-
22182165
}

src/ImageSharp/Formats/Webp/WebpDecoderCore.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33

44
using System.Buffers;
55
using System.Buffers.Binary;
6+
using SixLabors.ImageSharp.ColorProfiles.Icc;
67
using SixLabors.ImageSharp.Formats.Webp.Lossless;
78
using SixLabors.ImageSharp.Formats.Webp.Lossy;
89
using SixLabors.ImageSharp.IO;
910
using SixLabors.ImageSharp.Memory;
1011
using SixLabors.ImageSharp.Metadata;
12+
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
1113
using SixLabors.ImageSharp.PixelFormats;
14+
using SixLabors.ImageSharp.Processing;
1215

1316
namespace SixLabors.ImageSharp.Formats.Webp;
1417

@@ -122,6 +125,11 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
122125
this.ParseOptionalChunks(stream, metadata, this.webImageInfo.Features, buffer);
123126
}
124127

128+
if (this.Options.TryGetIccProfileForColorConversion(metadata.IccProfile, out IccProfile? iccProfile))
129+
{
130+
image.ApplyIccProfile(iccProfile, CompactSrgbV4Profile.Profile);
131+
}
132+
125133
return image;
126134
}
127135
}

src/ImageSharp/Processing/Extensions/ProcessingExtensions.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4+
using System.Buffers;
5+
using System.Numerics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
48
using SixLabors.ImageSharp.Advanced;
9+
using SixLabors.ImageSharp.ColorProfiles;
10+
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
511
using SixLabors.ImageSharp.PixelFormats;
612
using SixLabors.ImageSharp.Processing.Processors;
713

@@ -266,6 +272,64 @@ public static IImageProcessingContext ApplyProcessors(
266272
return source;
267273
}
268274

275+
/// <summary>
276+
/// Convert color accordinlgy to the IccProfile. Both Icc profile need to be Rgb.
277+
/// </summary>
278+
/// <typeparam name="TPixel">The pixel format.</typeparam>
279+
/// <param name="image">The image processing context.</param>
280+
/// <param name="sourceProfile">The source profile (Probably image.Metadata.IccProfile.</param>
281+
/// <param name="destinationProfile">The desired profile.</param>
282+
public static void ApplyIccProfile<TPixel>(this Image<TPixel> image, IccProfile sourceProfile, IccProfile destinationProfile)
283+
where TPixel : unmanaged, IPixel<TPixel>
284+
{
285+
ColorConversionOptions options = new()
286+
{
287+
SourceIccProfile = sourceProfile,
288+
TargetIccProfile = destinationProfile,
289+
};
290+
291+
ColorProfileConverter converter = new(options);
292+
293+
image.ProcessPixelRows(pixelAccessor =>
294+
{
295+
using IMemoryOwner<float> rgbBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width * 3);
296+
using IMemoryOwner<float> alphaBuffer = image.Configuration.MemoryAllocator.Allocate<float>(pixelAccessor.Width);
297+
Span<float> rgbPacked = rgbBuffer.Memory.Span;
298+
ref float rgbPackedRef = ref MemoryMarshal.GetReference(rgbPacked);
299+
Span<float> alphaPacked = alphaBuffer.Memory.Span;
300+
ref float alphaPackedRef = ref MemoryMarshal.GetReference(alphaPacked);
301+
302+
for (int y = 0; y < pixelAccessor.Height; y++)
303+
{
304+
Span<TPixel> pixelsRow = pixelAccessor.GetRowSpan(y);
305+
int rgbIdx = 0;
306+
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
307+
{
308+
Vector4 rgba = pixelsRow[x].ToScaledVector4();
309+
Unsafe.Add(ref rgbPackedRef, rgbIdx) = rgba.X;
310+
Unsafe.Add(ref rgbPackedRef, rgbIdx + 1) = rgba.Y;
311+
Unsafe.Add(ref rgbPackedRef, rgbIdx + 2) = rgba.Z;
312+
Unsafe.Add(ref alphaPackedRef, x) = rgba.W;
313+
}
314+
315+
Span<Rgb> source = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
316+
Span<Rgb> destination = MemoryMarshal.Cast<float, Rgb>(rgbPacked);
317+
converter.Convert<Rgb, Rgb>(source, destination);
318+
319+
rgbIdx = 0;
320+
for (int x = 0; x < pixelsRow.Length; x++, rgbIdx += 3)
321+
{
322+
float r = Unsafe.Add(ref rgbPackedRef, rgbIdx);
323+
float g = Unsafe.Add(ref rgbPackedRef, rgbIdx + 1);
324+
float b = Unsafe.Add(ref rgbPackedRef, rgbIdx + 2);
325+
float a = Unsafe.Add(ref alphaPackedRef, x);
326+
327+
pixelsRow[x] = TPixel.FromScaledVector4(new Vector4(r, g, b, a));
328+
}
329+
}
330+
});
331+
}
332+
269333
private class ProcessingVisitor : IImageVisitor
270334
{
271335
private readonly Configuration configuration;

tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ public void WebpDecoder_CanDecode_Lossy_VerySmall<TPixel>(TestImageProvider<TPix
117117
image.CompareToOriginal(provider, ReferenceDecoder);
118118
}
119119

120+
[Theory]
121+
[WithFile(Lossy.LosslessSRgbGray, PixelTypes.Rgba32)]
122+
[WithFile(Lossy.LossySRgbGray, PixelTypes.Rgba32)]
123+
public void WebpDecoder_CanDecode_WithIccProfile<TPixel>(TestImageProvider<TPixel> provider)
124+
where TPixel : unmanaged, IPixel<TPixel>
125+
{
126+
using Image<TPixel> image = provider.GetImage(WebpDecoder.Instance, new DecoderOptions { ColorProfileHandling = ColorProfileHandling.Convert });
127+
image.DebugSave(provider);
128+
image.CompareToReferenceOutput(provider, ImageComparer.Exact);
129+
}
130+
120131
[Theory]
121132
[WithFile(Lossy.SegmentationNoFilter04, PixelTypes.Rgba32)]
122133
[WithFile(Lossy.SegmentationNoFilter05, PixelTypes.Rgba32)]

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,10 @@ public static class Lossy
875875
public const string AlphaThinkingSmiley = "Webp/1602311202.webp";
876876
public const string AlphaSticker = "Webp/sticker.webp";
877877

878+
// Icc test
879+
public const string LosslessSRgbGray = "Webp/lossless_sRGB_Gray.webp";
880+
public const string LossySRgbGray = "Webp/lossy_sRGB_Gray.webp";
881+
878882
// Issues
879883
public const string Issue1594 = "Webp/issues/Issue1594.webp";
880884
public const string Issue2243 = "Webp/issues/Issue2243.webp";
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)