diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
index a31ff7a9fd..2a52e8bff5 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/BlackIsZero16TiffColor{TPixel}.cs
@@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ushort intensity = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2));
+ ushort intensity = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ushort intensity = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2));
+ ushort intensity = TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
index 4b34d5a0db..86ac94f55a 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb161616TiffColor{TPixel}.cs
@@ -41,11 +41,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong r = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2));
+ ulong r = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
- ulong g = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
- ulong b = TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
@@ -55,11 +55,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong r = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2));
+ ulong r = TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2));
offset += 2;
- ulong g = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2));
offset += 2;
- ulong b = TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromRgba64(rgba, r, g, b, color);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
index c24fada54c..20053eb8a1 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb16PlanarTiffColor{TPixel}.cs
@@ -44,9 +44,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong r = TiffUtils.ConvertToShortBigEndian(redData.Slice(offset, 2));
- ulong g = TiffUtils.ConvertToShortBigEndian(greenData.Slice(offset, 2));
- ulong b = TiffUtils.ConvertToShortBigEndian(blueData.Slice(offset, 2));
+ ulong r = TiffUtils.ConvertToUShortBigEndian(redData.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortBigEndian(greenData.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortBigEndian(blueData.Slice(offset, 2));
offset += 2;
@@ -57,9 +57,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ulong r = TiffUtils.ConvertToShortLittleEndian(redData.Slice(offset, 2));
- ulong g = TiffUtils.ConvertToShortLittleEndian(greenData.Slice(offset, 2));
- ulong b = TiffUtils.ConvertToShortLittleEndian(blueData.Slice(offset, 2));
+ ulong r = TiffUtils.ConvertToUShortLittleEndian(redData.Slice(offset, 2));
+ ulong g = TiffUtils.ConvertToUShortLittleEndian(greenData.Slice(offset, 2));
+ ulong b = TiffUtils.ConvertToUShortLittleEndian(blueData.Slice(offset, 2));
offset += 2;
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
new file mode 100644
index 0000000000..7d13fdcdf4
--- /dev/null
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/Rgb242424TiffColor{TPixel}.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using SixLabors.ImageSharp.Formats.Tiff.Utils;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
+{
+ ///
+ /// Implements the 'RGB' photometric interpretation with 16 bits for each channel.
+ ///
+ internal class Rgb242424TiffColor : TiffBaseColorDecoder
+ where TPixel : unmanaged, IPixel
+ {
+ private readonly bool isBigEndian;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true decodes the pixel data as big endian, otherwise as little endian.
+ public Rgb242424TiffColor(bool isBigEndian) => this.isBigEndian = isBigEndian;
+
+ ///
+ public override void Decode(ReadOnlySpan data, Buffer2D pixels, int left, int top, int width, int height)
+ {
+ // Note: due to an issue with netcore 2.1 and default values and unpredictable behavior with those,
+ // we define our own defaults as a workaround. See: https://github.com/dotnet/runtime/issues/55623
+ var color = default(TPixel);
+ color.FromVector4(TiffUtils.Vector4Default);
+ int offset = 0;
+ float scale = 1.0f / 0xFFFFFF;
+ byte[] buffer = new byte[4];
+ int bufferStartIdx = this.isBigEndian ? 1 : 0;
+
+ for (int y = top; y < top + height; y++)
+ {
+ Span pixelRow = pixels.GetRowSpan(y).Slice(left, width);
+
+ if (this.isBigEndian)
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong r = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong g = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong b = TiffUtils.ConvertToUIntBigEndian(buffer);
+ offset += 3;
+
+ var colorVector = new Vector4(r * scale, g * scale, b * scale, 1.0f);
+ color.FromVector4(colorVector);
+
+ pixelRow[x] = color;
+ }
+ }
+ else
+ {
+ for (int x = 0; x < pixelRow.Length; x++)
+ {
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong r = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong g = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ data.Slice(offset, 3).CopyTo(buffer.AsSpan(bufferStartIdx));
+ ulong b = TiffUtils.ConvertToUIntLittleEndian(buffer);
+ offset += 3;
+
+ var colorVector = new Vector4(r * scale, g * scale, b * scale, 1.0f);
+ color.FromVector4(colorVector);
+
+ pixelRow[x] = color;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
index 0c93998c48..ed9a14b2a5 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
@@ -136,6 +136,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb161616TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+ case TiffColorType.Rgb242424:
+ DebugGuard.IsTrue(
+ bitsPerSample.Channels == 3
+ && bitsPerSample.Channel2 == 24
+ && bitsPerSample.Channel1 == 24
+ && bitsPerSample.Channel0 == 24,
+ "bitsPerSample");
+ DebugGuard.IsTrue(colorMap == null, "colorMap");
+ return new Rgb242424TiffColor(isBigEndian: byteOrder == ByteOrder.BigEndian);
+
case TiffColorType.PaletteColor:
DebugGuard.NotNull(colorMap, "colorMap");
return new PaletteTiffColor(bitsPerSample, colorMap);
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
index 331065d273..b49bcc219e 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
@@ -103,6 +103,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
///
Rgb161616,
+ ///
+ /// RGB color image with 24 bits for each channel.
+ ///
+ Rgb242424,
+
///
/// RGB Full Color. Planar configuration of data.
///
diff --git a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
index a6b5151d70..18b5300b27 100644
--- a/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/PhotometricInterpretation/WhiteIsZero16TiffColor{TPixel}.cs
@@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortBigEndian(data.Slice(offset, 2)));
+ ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortBigEndian(data.Slice(offset, 2)));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
@@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
for (int x = 0; x < pixelRow.Length; x++)
{
- ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToShortLittleEndian(data.Slice(offset, 2)));
+ ushort intensity = (ushort)(ushort.MaxValue - TiffUtils.ConvertToUShortLittleEndian(data.Slice(offset, 2)));
offset += 2;
pixelRow[x] = TiffUtils.ColorFromL16(l16, intensity, color);
diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
index 14c527a34c..4563e23170 100644
--- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
@@ -206,6 +206,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
ushort bitsPerChannel = options.BitsPerSample.Channel0;
switch (bitsPerChannel)
{
+ case 24:
+ options.ColorType = TiffColorType.Rgb242424;
+ break;
+
case 16:
options.ColorType = TiffColorType.Rgb161616;
break;
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
index a0ba40f4e0..9514e43015 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffUtils.cs
@@ -21,12 +21,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Utils
public static L16 L16Default { get; } = new L16(0);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ushort ConvertToShortBigEndian(ReadOnlySpan buffer) =>
- BinaryPrimitives.ReadUInt16BigEndian(buffer);
+ public static ushort ConvertToUShortBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16BigEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ushort ConvertToShortLittleEndian(ReadOnlySpan buffer) =>
- BinaryPrimitives.ReadUInt16LittleEndian(buffer);
+ public static ushort ConvertToUShortLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt16LittleEndian(buffer);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint ConvertToUIntBigEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32BigEndian(buffer);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint ConvertToUIntLittleEndian(ReadOnlySpan buffer) => BinaryPrimitives.ReadUInt32LittleEndian(buffer);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TPixel ColorFromL8(L8 l8, byte intensity, TPixel color)
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
index 3bf1c25f3f..9cda1bdac8 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
@@ -173,6 +173,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_48Bit(TestImageProvider provider)
where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+ [Theory]
+ [WithFile(FlowerRgb242424Contiguous, PixelTypes.Rgba32)]
+ [WithFile(FlowerRgb242424ContiguousLittleEndian, PixelTypes.Rgba32)]
+ public void TiffDecoder_CanDecode_72Bit(TestImageProvider provider)
+ where TPixel : unmanaged, IPixel => TestTiffDecoder(provider);
+
[Theory]
[WithFile(GrayscaleDeflateMultistrip, PixelTypes.Rgba32)]
[WithFile(RgbDeflateMultistrip, PixelTypes.Rgba32)]
diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs
index 9c24184366..1f98c30a3f 100644
--- a/tests/ImageSharp.Tests/TestImages.cs
+++ b/tests/ImageSharp.Tests/TestImages.cs
@@ -561,6 +561,8 @@ namespace SixLabors.ImageSharp.Tests
public const string RgbPalette = "Tiff/rgb_palette.tiff";
public const string Rgb4BitPalette = "Tiff/bike_colorpalette_4bit.tiff";
public const string RgbPaletteDeflate = "Tiff/rgb_palette_deflate.tiff";
+ public const string FlowerRgb242424Contiguous = "Tiff/flower-rgb-contig-24.tiff";
+ public const string FlowerRgb242424ContiguousLittleEndian = "Tiff/flower-rgb-contig-24_lsb.tiff";
public const string FlowerRgb161616Contiguous = "Tiff/flower-rgb-contig-16.tiff";
public const string FlowerRgb161616ContiguousLittleEndian = "Tiff/flower-rgb-contig-16_lsb.tiff";
public const string FlowerRgb161616Planar = "Tiff/flower-rgb-planar-16.tiff";
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-24.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-24.tiff
new file mode 100644
index 0000000000..9145c21db1
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-24.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c6368a704b0a629239024f6fbfb30723fa317593ef36ddba05d76302530bd974
+size 28568
diff --git a/tests/Images/Input/Tiff/flower-rgb-contig-24_lsb.tiff b/tests/Images/Input/Tiff/flower-rgb-contig-24_lsb.tiff
new file mode 100644
index 0000000000..40cf1c9b85
--- /dev/null
+++ b/tests/Images/Input/Tiff/flower-rgb-contig-24_lsb.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bbb2b4ca6d7eeee4737c6963c99ef68fb6971cf6ccee463427a8246574bc6440
+size 28632