diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs
new file mode 100644
index 0000000000..f3d014a46f
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs
@@ -0,0 +1,46 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using SixLabors.ImageSharp.ColorSpaces;
+using SixLabors.ImageSharp.ColorSpaces.Conversion;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements decoding pixel data with photometric interpretation of type 'CieLab'.
+ ///
+ internal class CieLabTiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly ColorSpaceConverter colorSpaceConverter = new();
+
+ private const float Inv255 = 1.0f / 255.0f;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ var color = default(TPixel);
+ int offset = 0;
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
+
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ float l = (data[offset] & 0xFF) * 100f * Inv255;
+ var lab = new CieLab(l, (sbyte)data[offset + 1], (sbyte)data[offset + 2]);
+ var rgb = this.colorSpaceConverter.ToRgb(lab);
+
+ color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
+ pixelRow[x] = color;
+
+ offset += 3;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index c95d039461..ce146c9572 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -385,8 +385,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
return new PaletteTiffColor(bitsPerSample, colorMap);
case TiffColorType.YCbCr:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 8
+ && bitsPerSample.Channel1 == 8
+ && bitsPerSample.Channel0 == 8,
+ "bitsPerSample");
return new YCbCrTiffColor(memoryAllocator, referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling);
+ case TiffColorType.CieLab:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 8
+ && bitsPerSample.Channel1 == 8
+ && bitsPerSample.Channel0 == 8,
+ "bitsPerSample");
+ return new CieLabTiffColor();
+
default:
throw TiffThrowHelper.InvalidColorType(colorType.ToString());
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
index 6b39224fbd..b5fdcbea9e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -276,6 +276,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
/// The pixels are stored in YCbCr format as planar.
///
- YCbCrPlanar
+ YCbCrPlanar,
+
+ ///
+ /// The pixels are stored in CieLab format.
+ ///
+ CieLab,
}
}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs
index 9b37cf16a5..0c9b9360b5 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs
@@ -9,6 +9,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
+ ///
+ /// Implements decoding pixel data with photometric interpretation of type 'YCbCr'.
+ ///
internal class YCbCrTiffColor : TiffBaseColorDecoder
where TPixel : unmanaged, IPixel
{
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index 6d99fed3e1..f1c04a3828 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -381,7 +381,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
options.ColorMap = exifProfile.GetValue(ExifTag.ColorMap)?.Value;
if (options.BitsPerSample.Channels != 3)
{
- TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported.");
+ TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported for YCbCr images.");
}
ushort bitsPerChannel = options.BitsPerSample.Channel0;
@@ -395,6 +395,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff
break;
}
+ case TiffPhotometricInterpretation.CieLab:
+ {
+ options.ColorType = TiffColorType.CieLab;
+
+ if (options.BitsPerSample.Channels != 3)
+ {
+ TiffThrowHelper.ThrowNotSupported("The number of samples in the TIFF BitsPerSample entry is not supported for CieLab images.");
+ }
+
+ ushort bitsPerChannel = options.BitsPerSample.Channel0;
+ if (bitsPerChannel != 8)
+ {
+ TiffThrowHelper.ThrowNotSupported("Only 8 bits per channel is supported for CieLab images.");
+ }
+
+ break;
+ }
+
default:
{
TiffThrowHelper.ThrowNotSupported($"The specified TIFF photometric interpretation is not supported: {options.PhotometricInterpretation}");