Skip to content

Commit 7e22a01

Browse files
Add RGB support.
1 parent 9fbe5a4 commit 7e22a01

11 files changed

Lines changed: 99 additions & 12 deletions

File tree

src/ImageSharp/Formats/DecoderOptions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ internal bool TryGetIccProfileForColorConversion(IccProfile? profile, [NotNullWh
7878
return false;
7979
}
8080

81-
if (profile.IsCanonicalSrgbMatrixTrc())
81+
if (this.ColorProfileHandling == ColorProfileHandling.Preserve)
8282
{
8383
return false;
8484
}
8585

86-
if (this.ColorProfileHandling == ColorProfileHandling.Preserve)
86+
if (profile.IsCanonicalSrgbMatrixTrc())
8787
{
8888
return false;
8989
}
@@ -99,11 +99,11 @@ internal bool CanRemoveIccProfile(IccProfile? profile)
9999
return false;
100100
}
101101

102-
if (this.ColorProfileHandling == ColorProfileHandling.Compact && profile.IsCanonicalSrgbMatrixTrc())
102+
if (this.ColorProfileHandling == ColorProfileHandling.Convert)
103103
{
104104
return true;
105105
}
106106

107-
return this.ColorProfileHandling == ColorProfileHandling.Convert;
107+
return this.ColorProfileHandling == ColorProfileHandling.Compact && profile.IsCanonicalSrgbMatrixTrc();
108108
}
109109
}

src/ImageSharp/Formats/ImageDecoder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,14 @@ private static void HandleIccProfile(DecoderOptions options, Image image)
328328
{
329329
image.Metadata.IccProfile = null;
330330
}
331+
332+
foreach (ImageFrame frame in image.Frames)
333+
{
334+
if (options.CanRemoveIccProfile(frame.Metadata.IccProfile))
335+
{
336+
frame.Metadata.IccProfile = null;
337+
}
338+
}
331339
}
332340

333341
private static void HandleIccProfile(DecoderOptions options, ImageInfo image)
@@ -336,5 +344,13 @@ private static void HandleIccProfile(DecoderOptions options, ImageInfo image)
336344
{
337345
image.Metadata.IccProfile = null;
338346
}
347+
348+
foreach (ImageFrameMetadata frame in image.FrameMetadataCollection)
349+
{
350+
if (options.CanRemoveIccProfile(frame.IccProfile))
351+
{
352+
frame.IccProfile = null;
353+
}
354+
}
339355
}
340356
}

src/ImageSharp/Formats/ImageDecoderCore.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using SixLabors.ImageSharp.ColorProfiles.Icc;
66
using SixLabors.ImageSharp.IO;
77
using SixLabors.ImageSharp.Memory;
8+
using SixLabors.ImageSharp.Metadata;
89
using SixLabors.ImageSharp.Metadata.Profiles.Icc;
910
using SixLabors.ImageSharp.PixelFormats;
1011

@@ -164,4 +165,56 @@ protected bool TryConvertIccProfile<TPixel>(Image<TPixel> image)
164165
converter.Convert(image);
165166
return true;
166167
}
168+
169+
/// <summary>
170+
/// Converts the ICC color profile of the specified image frame to the compact sRGB v4 profile if a source profile is
171+
/// available.
172+
/// </summary>
173+
/// <remarks>
174+
/// This method should only be used by decoders that gurantee that the encoded image data is in a color space
175+
/// compatible with sRGB (e.g. standard RGB, Adobe RGB, ProPhoto RGB).
176+
/// <br/>
177+
/// If the image does not have a valid ICC profile for color conversion, no changes are made.
178+
/// This operation may affect the color appearance of the image to ensure consistency with the sRGB color
179+
/// space.
180+
/// </remarks>
181+
/// <typeparam name="TPixel">The pixel format.</typeparam>
182+
/// <param name="frame">The image frame whose ICC profile will be converted to the compact sRGB v4 profile.</param>
183+
/// <returns>
184+
/// <see langword="true"/> if the conversion was performed; otherwise, <see langword="false"/>.
185+
/// </returns>
186+
protected bool TryConvertIccProfile<TPixel>(ImageFrame<TPixel> frame)
187+
where TPixel : unmanaged, IPixel<TPixel>
188+
{
189+
if (!this.Options.TryGetIccProfileForColorConversion(frame.Metadata.IccProfile, out IccProfile? profile))
190+
{
191+
return false;
192+
}
193+
194+
ColorConversionOptions options = new()
195+
{
196+
SourceIccProfile = profile,
197+
TargetIccProfile = CompactSrgbV4Profile.Profile,
198+
MemoryAllocator = frame.Configuration.MemoryAllocator,
199+
};
200+
201+
ColorProfileConverter converter = new(options);
202+
203+
ImageMetadata metadata = new()
204+
{
205+
IccProfile = frame.Metadata.IccProfile
206+
};
207+
208+
IMemoryGroup<TPixel> m = frame.PixelBuffer.MemoryGroup;
209+
210+
// Safe: ToArray only materializes the Memory<TPixel> segment list, not the underlying pixel buffers,
211+
// and Wrap(Memory<T>[]) creates a Consumed MemoryGroup that does not own the buffers (Dispose just
212+
// invalidates the view). This means no pixel data is cloned and disposing the temporary image will
213+
// not dispose or leak the frame's pixel buffer.
214+
MemoryGroup<TPixel> memorySource = MemoryGroup<TPixel>.Wrap(m.ToArray());
215+
216+
using Image<TPixel> image = new(frame.Configuration, memorySource, frame.Width, frame.Height, metadata);
217+
converter.Convert(image);
218+
return true;
219+
}
167220
}

src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,6 @@ public static TiffBaseColorDecoder<TPixel> Create(
429429
}
430430
}
431431

432-
#pragma warning disable IDE0060 // Remove unused parameter
433432
public static TiffBasePlanarColorDecoder<TPixel> CreatePlanar(
434433
ImageFrameMetadata metadata,
435434
DecoderOptions options,

src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ internal class TiffDecoderCore : ImageDecoderCore
5252
/// </summary>
5353
private ByteOrder byteOrder;
5454

55-
/// <summary>
56-
/// Indicating whether is BigTiff format.
57-
/// </summary>
58-
private bool isBigTiff;
59-
6055
/// <summary>
6156
/// Initializes a new instance of the <see cref="TiffDecoderCore" /> class.
6257
/// </summary>
@@ -167,7 +162,6 @@ protected override Image<TPixel> Decode<TPixel>(BufferedReadStream stream, Cance
167162

168163
IList<ExifProfile> directories = reader.Read();
169164
this.byteOrder = reader.ByteOrder;
170-
this.isBigTiff = reader.IsBigTiff;
171165

172166
Size? size = null;
173167
uint frameCount = 0;
@@ -273,6 +267,15 @@ private ImageFrame<TPixel> DecodeFrame<TPixel>(ExifProfile tags, Size? size, Can
273267
this.DecodeImageWithStrips(tags, frame, width, height, cancellationToken);
274268
}
275269

270+
// Only RGB-compatible color types can be converted here because the TPixel-based ICC profile conversion
271+
// expects RGB-like pixel data; other photometric interpretations (YCbCr, CMYK, Lab, etc.) would require
272+
// dedicated transforms. We do this once at the frame level to avoid duplicating conversion logic
273+
// across all color decoders and to keep their decode paths focused on raw pixel unpacking.
274+
if (this.ColorType is >= TiffColorType.PaletteColor and <= TiffColorType.Rgba32323232Planar)
275+
{
276+
_ = this.TryConvertIccProfile(frame);
277+
}
278+
276279
return frame;
277280
}
278281

tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class TiffDecoderTests : TiffDecoderBaseTester
2323
public static readonly string[] MultiframeTestImages = Multiframes;
2424

2525
[Theory]
26-
// [WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)]
26+
[WithFile(MultiframeDifferentVariants, PixelTypes.Rgba32)]
2727
[WithFile(Cmyk64BitDeflate, PixelTypes.Rgba32)]
2828
public void ThrowsNotSupported<TPixel>(TestImageProvider<TPixel> provider)
2929
where TPixel : unmanaged, IPixel<TPixel> => Assert.Throws<NotSupportedException>(() => provider.GetImage(TiffDecoder.Instance));
@@ -355,6 +355,8 @@ public void TiffDecoder_CanDecode_Cmyk<TPixel>(TestImageProvider<TPixel> provide
355355
[Theory]
356356
[WithFile(Icc.PerceptualCmyk, PixelTypes.Rgba32)]
357357
[WithFile(Icc.PerceptualCieLab, PixelTypes.Rgba32)]
358+
[WithFile(Icc.PerceptualRgb8, PixelTypes.Rgba32)]
359+
[WithFile(Icc.PerceptualRgb16, PixelTypes.Rgba32)]
358360
public void Decode_WhenColorProfileHandlingIsConvert_ApplyIccProfile<TPixel>(TestImageProvider<TPixel> provider)
359361
where TPixel : unmanaged, IPixel<TPixel>
360362
{

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,8 @@ public static class Icc
11991199
{
12001200
public const string PerceptualCmyk = "Tiff/icc-profiles/Perceptual_CMYK.tiff";
12011201
public const string PerceptualCieLab = "Tiff/icc-profiles/Perceptual_CIELAB.tiff";
1202+
public const string PerceptualRgb8 = "Tiff/icc-profiles/Perceptual_RGB8.tiff";
1203+
public const string PerceptualRgb16 = "Tiff/icc-profiles/Perceptual_RGB16.tiff";
12021204
}
12031205
}
12041206

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:3b0e89ac971c23c12c9edff6f9e9dd066fe99f36e28e89972343d50562dd7dbd
3+
size 65260

0 commit comments

Comments
 (0)