Browse Source

Merge branch 'main' into bp/tiffwebp

pull/2128/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
0cde5ef46c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs
  2. 49
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs
  3. 46
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs
  4. 18
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorDecoderFactory{TPixel}.cs
  5. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs
  6. 3
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs
  7. 3
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs
  8. 2
      src/ImageSharp/Formats/Tiff/README.md
  9. 20
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  10. 14
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  11. 5
      tests/ImageSharp.Tests/TestImages.cs
  12. 3
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLab.png
  13. 3
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLabPlanar.png
  14. 3
      tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLab_lzwcompressed_predictor.png
  15. 3
      tests/Images/Input/Tiff/CieLab.tiff
  16. 3
      tests/Images/Input/Tiff/CieLabPlanar.tiff
  17. 3
      tests/Images/Input/Tiff/CieLab_lzwcompressed_predictor.tiff

1
src/ImageSharp/Formats/Tiff/Compression/HorizontalPredictor.cs

@ -41,6 +41,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
UndoGray32Bit(pixelBytes, width, isBigEndian);
break;
case TiffColorType.Rgb888:
case TiffColorType.CieLab:
UndoRgb24Bit(pixelBytes, width);
break;
case TiffColorType.Rgba8888:

49
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabPlanarTiffColor{TPixel}.cs

@ -0,0 +1,49 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Buffers;
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
{
/// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'CieLab' with the planar configuration.
/// </summary>
internal class CieLabPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private static readonly ColorSpaceConverter ColorSpaceConverter = new();
private const float Inv255 = 1.0f / 255.0f;
/// <inheritdoc/>
public override void Decode(IMemoryOwner<byte>[] data, Buffer2D<TPixel> pixels, int left, int top, int width, int height)
{
Span<byte> l = data[0].GetSpan();
Span<byte> a = data[1].GetSpan();
Span<byte> b = data[2].GetSpan();
var color = default(TPixel);
int offset = 0;
for (int y = top; y < top + height; y++)
{
Span<TPixel> pixelRow = pixels.DangerousGetRowSpan(y).Slice(left, width);
for (int x = 0; x < pixelRow.Length; x++)
{
var lab = new CieLab((l[offset] & 0xFF) * 100f * Inv255, (sbyte)a[offset], (sbyte)b[offset]);
var rgb = ColorSpaceConverter.ToRgb(lab);
color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = color;
offset++;
}
}
}
}
}

46
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/CieLabTiffColor{TPixel}.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
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
{
/// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'CieLab'.
/// </summary>
internal class CieLabTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private static readonly ColorSpaceConverter ColorSpaceConverter = new();
private const float Inv255 = 1.0f / 255.0f;
/// <inheritdoc/>
public override void Decode(ReadOnlySpan<byte> data, Buffer2D<TPixel> 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<TPixel> 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 = ColorSpaceConverter.ToRgb(lab);
color.FromVector4(new Vector4(rgb.R, rgb.G, rgb.B, 1.0f));
pixelRow[x] = color;
offset += 3;
}
}
}
}
}

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

@ -385,8 +385,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
return new PaletteTiffColor<TPixel>(bitsPerSample, colorMap);
case TiffColorType.YCbCr:
DebugGuard.IsTrue(
bitsPerSample.Channels == 3
&& bitsPerSample.Channel2 == 8
&& bitsPerSample.Channel1 == 8
&& bitsPerSample.Channel0 == 8,
"bitsPerSample");
return new YCbCrTiffColor<TPixel>(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<TPixel>();
default:
throw TiffThrowHelper.InvalidColorType(colorType.ToString());
}
@ -415,6 +430,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
case TiffColorType.YCbCrPlanar:
return new YCbCrPlanarTiffColor<TPixel>(referenceBlackAndWhite, ycbcrCoefficients, ycbcrSubSampling);
case TiffColorType.CieLabPlanar:
return new CieLabPlanarTiffColor<TPixel>();
case TiffColorType.Rgb161616Planar:
DebugGuard.IsTrue(colorMap == null, "colorMap");
return new Rgb16PlanarTiffColor<TPixel>(byteOrder == ByteOrder.BigEndian);

12
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/TiffColorType.cs

@ -276,6 +276,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
/// <summary>
/// The pixels are stored in YCbCr format as planar.
/// </summary>
YCbCrPlanar
YCbCrPlanar,
/// <summary>
/// The pixels are stored in CieLab format.
/// </summary>
CieLab,
/// <summary>
/// The pixels are stored in CieLab format as planar.
/// </summary>
CieLabPlanar,
}
}

3
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrPlanarTiffColor{TPixel}.cs

@ -9,6 +9,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
/// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'YCbCr' with the planar configuration.
/// </summary>
internal class YCbCrPlanarTiffColor<TPixel> : TiffBasePlanarColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{

3
src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrTiffColor{TPixel}.cs

@ -9,6 +9,9 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
{
/// <summary>
/// Implements decoding pixel data with photometric interpretation of type 'YCbCr'.
/// </summary>
internal class YCbCrTiffColor<TPixel> : TiffBaseColorDecoder<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{

2
src/ImageSharp/Formats/Tiff/README.md

@ -56,7 +56,7 @@
|TransparencyMask | | | |
|Separated (TIFF Extension) | | | |
|YCbCr (TIFF Extension) | | Y | |
|CieLab (TIFF Extension) | | | |
|CieLab (TIFF Extension) | | Y | |
|IccLab (TechNote 1) | | | |
### Baseline TIFF Tags

20
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:
{
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.");
}
options.ColorType = options.PlanarConfiguration == TiffPlanarConfiguration.Chunky ? TiffColorType.CieLab : TiffColorType.CieLabPlanar;
break;
}
default:
{
TiffThrowHelper.ThrowNotSupported($"The specified TIFF photometric interpretation is not supported: {options.PhotometricInterpretation}");

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

@ -318,6 +318,20 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
}
[Theory]
[WithFile(CieLab, PixelTypes.Rgba32)]
[WithFile(CieLabPlanar, PixelTypes.Rgba32)]
[WithFile(CieLabLzwPredictor, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_CieLab<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
// Note: The image from MagickReferenceDecoder does not look right, maybe we are doing something wrong
// converting the pixel data from Magick.NET to our format with CieLab?
using Image<TPixel> image = provider.GetImage();
image.DebugSave(provider);
image.CompareToReferenceOutput(ImageComparer.Exact, provider);
}
[Theory]
[WithFile(FlowerRgb101010Contiguous, PixelTypes.Rgba32)]
[WithFile(FlowerRgb101010Planar, PixelTypes.Rgba32)]

5
tests/ImageSharp.Tests/TestImages.cs

@ -914,6 +914,11 @@ namespace SixLabors.ImageSharp.Tests
public const string Rgba32BitPlanarUnassociatedAlphaBigEndian = "Tiff/RgbaUnassociatedAlphaPlanar32bit_msb.tiff";
public const string Rgba32BitPlanarUnassociatedAlphaLittleEndian = "Tiff/RgbaUnassociatedAlphaPlanar32bit_lsb.tiff";
// Cie Lab color space.
public const string CieLab = "Tiff/CieLab.tiff";
public const string CieLabPlanar = "Tiff/CieLabPlanar.tiff";
public const string CieLabLzwPredictor = "Tiff/CieLab_lzwcompressed_predictor.tiff";
public const string Issues1716Rgb161616BitLittleEndian = "Tiff/Issues/Issue1716.tiff";
public const string Issues1891 = "Tiff/Issues/Issue1891.tiff";
public const string Issues2123 = "Tiff/Issues/Issue2123.tiff";

3
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLab.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:13bc9da102f85124855217fad757ca907f5d68442e54e3b7039ac048d7b2ad3f
size 25791

3
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLabPlanar.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:13bc9da102f85124855217fad757ca907f5d68442e54e3b7039ac048d7b2ad3f
size 25791

3
tests/Images/External/ReferenceOutput/TiffDecoderTests/TiffDecoder_CanDecode_CieLab_Rgba32_CieLab_lzwcompressed_predictor.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f9481c91c58ca7bbab9de4b9ae95fe4a2197ae4b6ef6b15b72d4858aba3a1a4
size 25782

3
tests/Images/Input/Tiff/CieLab.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7542b5b3abe049614f2ddaf78ffe995edac13e768f0b2fc9f324c6ef43b379eb
size 1312046

3
tests/Images/Input/Tiff/CieLabPlanar.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:28592d9da8d51f60700b7136369d2d6bd40550d5f8c7758e570b5e624c71a3e4
size 1307488

3
tests/Images/Input/Tiff/CieLab_lzwcompressed_predictor.tiff

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6affced5550e51441c4cde7f1770d4e57cfa594bd271a12f9571359733c2185d
size 55346
Loading…
Cancel
Save