Browse Source

For YCbCr Tiff's with Jpeg compression explicitly assume RGB color space

pull/1734/head
Brian Popow 5 years ago
parent
commit
1b01984138
  1. 2
      src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs
  2. 10
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs
  3. 11
      src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs
  4. 22
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  5. 33
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs
  6. 2
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  7. 3
      tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs
  8. 4
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

2
src/ImageSharp/Formats/Jpeg/Components/Decoder/HuffmanScanDecoder.cs

@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
private readonly SpectralConverter spectralConverter;
private CancellationToken cancellationToken;
private readonly CancellationToken cancellationToken;
/// <summary>
/// Initializes a new instance of the <see cref="HuffmanScanDecoder"/> class.

10
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
/// <summary>
@ -30,5 +32,13 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
/// Actual stride height depends on the subsampling factor of the given component.
/// </remarks>
public abstract void ConvertStrideBaseline();
/// <summary>
/// Gets the color converter.
/// </summary>
/// <param name="frame">The jpeg frame with the color space to convert to.</param>
/// <param name="jpegData">The raw JPEG data.</param>
/// <returns>The color converter.</returns>
public abstract JpegColorConverter GetConverter(JpegFrame frame, IRawJpegData jpegData);
}
}

11
src/ImageSharp/Formats/Jpeg/Components/Decoder/SpectralConverter{TPixel}.cs

@ -11,12 +11,12 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
{
internal sealed class SpectralConverter<TPixel> : SpectralConverter, IDisposable
internal class SpectralConverter<TPixel> : SpectralConverter, IDisposable
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly Configuration configuration;
private CancellationToken cancellationToken;
private readonly CancellationToken cancellationToken;
private JpegComponentPostProcessor[] componentProcessors;
@ -59,6 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
/// <inheritdoc/>
public override void InjectFrameData(JpegFrame frame, IRawJpegData jpegData)
{
MemoryAllocator allocator = this.configuration.MemoryAllocator;
@ -85,9 +86,10 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
this.rgbaBuffer = allocator.Allocate<Vector4>(frame.PixelWidth);
// color converter from Rgba32 to TPixel
this.colorConverter = JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
this.colorConverter = this.GetConverter(frame, jpegData);
}
/// <inheritdoc/>
public override void ConvertStrideBaseline()
{
// Convert next pixel stride using single spectral `stride'
@ -103,6 +105,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder
}
}
/// <inheritdoc/>
public override JpegColorConverter GetConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
public void Dispose()
{
if (this.componentProcessors != null)

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

@ -23,6 +23,8 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
private readonly byte[] jpegTables;
private readonly TiffPhotometricInterpretation photometricInterpretation;
/// <summary>
/// Initializes a new instance of the <see cref="JpegTiffCompression"/> class.
/// </summary>
@ -31,12 +33,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
/// <param name="width">The image width.</param>
/// <param name="bitsPerPixel">The bits per pixel.</param>
/// <param name="jpegTables">The JPEG tables containing the quantization and/or Huffman tables.</param>
/// <param name="predictor">The predictor.</param>
public JpegTiffCompression(Configuration configuration, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, byte[] jpegTables, TiffPredictor predictor = TiffPredictor.None)
: base(memoryAllocator, width, bitsPerPixel, predictor)
/// <param name="photometricInterpretation">The photometric interpretation.</param>
public JpegTiffCompression(
Configuration configuration,
MemoryAllocator memoryAllocator,
int width,
int bitsPerPixel,
byte[] jpegTables,
TiffPhotometricInterpretation photometricInterpretation)
: base(memoryAllocator, width, bitsPerPixel)
{
this.configuration = configuration;
this.jpegTables = jpegTables;
this.photometricInterpretation = photometricInterpretation;
}
/// <inheritdoc/>
@ -47,8 +56,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder());
// Should we pass through the CancellationToken from the tiff decoder?
using var spectralConverter = new SpectralConverter<Rgb24>(this.configuration, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
// 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).
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
new RgbJpegSpectralConverter<Rgb24>(this.configuration, CancellationToken.None) : new SpectralConverter<Rgb24>(this.configuration, CancellationToken.None);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
scanDecoder.ResetInterval = 0;

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

@ -0,0 +1,33 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Threading;
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 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 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>
/// <param name="cancellationToken">The cancellation token.</param>
public RgbJpegSpectralConverter(Configuration configuration, CancellationToken cancellationToken)
: base(configuration, cancellationToken)
{
}
/// <inheritdoc/>
public override JpegColorConverter GetConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(JpegColorSpace.RGB, frame.Precision);
}
}

2
src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs

@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
case TiffDecoderCompressionType.Jpeg:
DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression");
return new JpegTiffCompression(configuration, allocator, width, bitsPerPixel, jpegTables);
return new JpegTiffCompression(configuration, allocator, width, bitsPerPixel, jpegTables, photometricInterpretation);
default:
throw TiffThrowHelper.NotSupportedDecompressor(nameof(method));

3
tests/ImageSharp.Tests/Formats/Jpg/SpectralJpegTests.cs

@ -7,6 +7,7 @@ using System.Linq;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -200,6 +201,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
this.spectralData = new LibJpegTools.SpectralData(spectralComponents);
}
public override JpegColorConverter GetConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverter.GetConverter(jpegData.ColorSpace, frame.Precision);
}
}
}

4
tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

@ -290,8 +290,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory]
[WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_WithjpegCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg);
public void TiffEncoder_EncodeRgb_WithJpegCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg, useExactComparer: false, compareTolerance: 0.012f);
[Theory]
[WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)]

Loading…
Cancel
Save