Browse Source

Tiif decoding fix

pull/2177/head
Dmitry Pentin 4 years ago
parent
commit
1c9777e628
  1. 15
      src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs
  2. 8
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  3. 32
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs
  4. 50
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs
  5. 3
      tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs

15
src/ImageSharp/Formats/Jpeg/JpegDecoderCore.cs

@ -503,9 +503,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
}
/// <summary>
/// Returns the correct colorspace based on the image component count and the jpeg frame component id's.
/// Returns encoded colorspace based on the adobe APP14 marker.
/// </summary>
/// <param name="componentCount">The number of components.</param>
/// <param name="componentCount">Number of components.</param>
/// <param name="adobeMarker">Parsed adobe APP14 marker.</param>
/// <returns>The <see cref="JpegColorSpace"/></returns>
internal static JpegColorSpace DeduceJpegColorSpace(byte componentCount, ref AdobeMarker adobeMarker)
@ -534,6 +534,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
return default;
}
/// <summary>
/// Returns encoded colorspace based on the component count and component ids.
/// </summary>
/// <remarks>
/// Must take into account atleast RGB component identifiers i.e. [82, 71, 66]
/// as TIFF images with jpeg encoding don't have APP14 marker.
/// </remarks>
/// <param name="componentCount">Number of components.</param>
/// <returns>The <see cref="JpegColorSpace"/></returns>
internal static JpegColorSpace DeduceJpegColorSpace(byte componentCount)
{
if (componentCount == 1)
@ -1216,7 +1225,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg
int maxH = 0;
int maxV = 0;
int index = 0;
for (int i = 0; i < componentCount; i++)
for (int i = 0; i < this.Frame.Components.Length; i++)
{
// 1 byte: component identifier
byte componentId = this.temp[index];

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

@ -59,7 +59,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
{
using SpectralConverter<L8> spectralConverterGray = new GrayJpegSpectralConverter<L8>(this.configuration);
using SpectralConverter<L8> spectralConverterGray =
new GrayJpegSpectralConverter<L8>(this.configuration);
var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoderGray);
jpegDecoder.ParseStream(stream, spectralConverterGray, CancellationToken.None);
@ -73,9 +74,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
case TiffPhotometricInterpretation.YCbCr:
case TiffPhotometricInterpretation.Rgb:
{
// The jpeg data should treated as RGB color space. If the PhotometricInterpretation is YCbCr,
// the conversion to RGB will be handled in the next step by the YCbCr color decoder.
using SpectralConverter<Rgb24> spectralConverter = new RgbJpegSpectralConverter<Rgb24>(this.configuration);
using SpectralConverter<Rgb24> spectralConverter =
new TiffJpegSpectralConverter<Rgb24>(this.configuration, this.photometricInterpretation);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
jpegDecoder.ParseStream(stream, spectralConverter, CancellationToken.None);

32
src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs

@ -1,32 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
/// <summary>
/// Spectral converter for TIFF's which use the JPEG compression.
/// The compressed jpeg data should be always treated as RGB color space.
/// If PhotometricInterpretation indicates the data is YCbCr, the color decoder will handle the conversion.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class RgbJpegSpectralConverter<TPixel> : SpectralConverter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="RgbJpegSpectralConverter{TPixel}"/> class.
/// This Spectral converter will always convert the pixel data to RGB color.
/// </summary>
/// <param name="configuration">The configuration.</param>
public RgbJpegSpectralConverter(Configuration configuration)
: base(configuration)
{
}
/// <inheritdoc/>
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.RGB, frame.Precision);
}
}

50
src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffJpegSpectralConverter{TPixel}.cs

@ -0,0 +1,50 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
/// <summary>
/// Spectral converter for YCbCr TIFF's which use the JPEG compression.
/// The jpeg data should be always treated as RGB color space.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class TiffJpegSpectralConverter<TPixel> : SpectralConverter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly TiffPhotometricInterpretation photometricInterpretation;
/// <summary>
/// Initializes a new instance of the <see cref="TiffJpegSpectralConverter{TPixel}"/> class.
/// This Spectral converter will always convert the pixel data to RGB color.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="photometricInterpretation">Tiff photometric interpretation.</param>
public TiffJpegSpectralConverter(Configuration configuration, TiffPhotometricInterpretation photometricInterpretation)
: base(configuration)
=> this.photometricInterpretation = photometricInterpretation;
/// <inheritdoc/>
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData)
{
JpegColorSpace colorSpace = GetJpegColorSpaceFromPhotometricInterpretation(this.photometricInterpretation);
return JpegColorConverterBase.GetConverter(colorSpace, frame.Precision);
}
/// <remarks>
/// This converter must be used only for RGB and YCbCr color spaces for performance reasons.
/// For grayscale images <see cref="GrayJpegSpectralConverter{TPixel}"/> must be used.
/// </remarks>
private static JpegColorSpace GetJpegColorSpaceFromPhotometricInterpretation(TiffPhotometricInterpretation interpretation)
=> interpretation switch
{
TiffPhotometricInterpretation.Rgb => JpegColorSpace.RGB,
TiffPhotometricInterpretation.YCbCr => JpegColorSpace.RGB,
_ => throw new InvalidImageContentException($"Invalid tiff photometric interpretation for jpeg encoding: {interpretation}"),
};
}
}

3
tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.Internal.cs

@ -33,8 +33,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
byte[] adobeMarkerPayload = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, adobeFlag };
ProfileResolver.AdobeMarker.CopyTo(adobeMarkerPayload);
_ = AdobeMarker.TryParse(adobeMarkerPayload, out AdobeMarker adobeMarker);
JpegColorSpace actualColorSpace = JpegDecoderCore.DeduceJpegColorSpace(componentCount, ref adobeMarker);
Assert.Equal(expectedColorSpace, actualColorSpace);
@ -46,6 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void DeduceJpegColorSpaceAdobeMarker_ShouldThrowOnUnsupportedComponentCount(byte componentCount)
{
AdobeMarker adobeMarker = default;
Assert.Throws<NotSupportedException>(() => JpegDecoderCore.DeduceJpegColorSpace(componentCount, ref adobeMarker));
}

Loading…
Cancel
Save