Browse Source

Override PhotometricInterpretation to RGB, if it's YCbCr and Jpeg compressed

pull/2124/head
Brian Popow 4 years ago
parent
commit
82e7ac14f0
  1. 2
      src/ImageSharp/Common/Helpers/Numerics.cs
  2. 6
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  3. 12
      src/ImageSharp/Formats/Tiff/PhotometricInterpretation/YCbCrConverter.cs
  4. 2
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  5. 7
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  6. 1
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  7. 1
      tests/ImageSharp.Tests/TestImages.cs
  8. 3
      tests/Images/Input/Tiff/ycbcr_jpegcompressed2.tiff

2
src/ImageSharp/Common/Helpers/Numerics.cs

@ -968,7 +968,7 @@ namespace SixLabors.ImageSharp
/// Tells whether input value is outside of the given range.
/// </summary>
/// <param name="value">Value.</param>
/// <param name="min">Mininum value, inclusive.</param>
/// <param name="min">Minimum value, inclusive.</param>
/// <param name="max">Maximum value, inclusive.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsOutOfRange(int value, int min, int max)

6
src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs

@ -65,13 +65,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
jpegDecoder.ParseStream(stream, spectralConverterGray, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
using var decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None);
using Buffer2D<L8> decompressedBuffer = spectralConverterGray.GetPixelBuffer(CancellationToken.None);
CopyImageBytesToBuffer(buffer, decompressedBuffer);
break;
}
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
case TiffPhotometricInterpretation.YCbCr:
case TiffPhotometricInterpretation.Rgb:
{
@ -82,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
jpegDecoder.ParseStream(stream, spectralConverter, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
using var decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None);
using Buffer2D<Rgb24> decompressedBuffer = spectralConverter.GetPixelBuffer(CancellationToken.None);
CopyImageBytesToBuffer(buffer, decompressedBuffer);
break;
}

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

@ -19,16 +19,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation
private static readonly Rational[] DefaultLuma =
{
new Rational(299, 1000),
new Rational(587, 1000),
new Rational(114, 1000)
new(299, 1000),
new(587, 1000),
new(114, 1000)
};
private static readonly Rational[] DefaultReferenceBlackWhite =
{
new Rational(0, 1), new Rational(255, 1),
new Rational(128, 1), new Rational(255, 1),
new Rational(128, 1), new Rational(255, 1)
new(0, 1), new(255, 1),
new(128, 1), new(255, 1),
new(128, 1), new(255, 1)
};
public YCbCrConverter(Rational[] referenceBlackAndWhite, Rational[] coefficients)

2
src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs

@ -228,7 +228,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="tags">The IFD tags.</param>
/// <param name="cancellationToken">The token to monitor cancellation.</param>
/// <returns> The tiff frame. </returns>
/// <returns>The tiff frame.</returns>
private ImageFrame<TPixel> DecodeFrame<TPixel>(ExifProfile tags, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
{

7
src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs

@ -459,6 +459,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
case TiffCompression.Jpeg:
{
options.CompressionType = TiffDecoderCompressionType.Jpeg;
if (options.PhotometricInterpretation is TiffPhotometricInterpretation.YCbCr && options.JpegTables is null)
{
// Note: Setting PhotometricInterpretation to RGB here, since the jpeg decoder will handle the conversion of the pixel data.
options.PhotometricInterpretation = TiffPhotometricInterpretation.Rgb;
}
break;
}

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

@ -642,6 +642,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[WithFile(RgbJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)]
[WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)]
[WithFile(YCbCrJpegCompressed2, PixelTypes.Rgba32)]
[WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)]
[WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)

1
tests/ImageSharp.Tests/TestImages.cs

@ -834,6 +834,7 @@ namespace SixLabors.ImageSharp.Tests
public const string RgbYCbCr888Contiguoush2v2 = "Tiff/rgb-ycbcr-contig-08_h2v2.tiff";
public const string RgbYCbCr888Contiguoush4v4 = "Tiff/rgb-ycbcr-contig-08_h4v4.tiff";
public const string YCbCrJpegCompressed = "Tiff/ycbcr_jpegcompressed.tiff";
public const string YCbCrJpegCompressed2 = "Tiff/ycbcr_jpegcompressed2.tiff";
public const string FlowerRgb444Contiguous = "Tiff/flower-rgb-contig-04.tiff";
public const string FlowerRgb444Planar = "Tiff/flower-rgb-planar-04.tiff";
public const string FlowerRgb222Contiguous = "Tiff/flower-rgb-contig-02.tiff";

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

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