Skip to content

Commit 5b2413a

Browse files
Merge branch 'main' into bp/tiffonlyfirstframe
2 parents ca7e70c + 739aa39 commit 5b2413a

File tree

6 files changed

+80
-12
lines changed

6 files changed

+80
-12
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
5+
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
6+
using SixLabors.ImageSharp.PixelFormats;
7+
8+
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
9+
{
10+
/// <summary>
11+
/// Spectral converter for gray TIFF's which use the JPEG compression.
12+
/// </summary>
13+
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
14+
internal sealed class GrayJpegSpectralConverter<TPixel> : SpectralConverter<TPixel>
15+
where TPixel : unmanaged, IPixel<TPixel>
16+
{
17+
/// <summary>
18+
/// Initializes a new instance of the <see cref="GrayJpegSpectralConverter{TPixel}"/> class.
19+
/// </summary>
20+
/// <param name="configuration">The configuration.</param>
21+
public GrayJpegSpectralConverter(Configuration configuration)
22+
: base(configuration)
23+
{
24+
}
25+
26+
/// <inheritdoc/>
27+
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, frame.Precision);
28+
}
29+
}

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

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using SixLabors.ImageSharp.Formats.Tiff.Constants;
1010
using SixLabors.ImageSharp.IO;
1111
using SixLabors.ImageSharp.Memory;
12-
using SixLabors.ImageSharp.Metadata;
1312
using SixLabors.ImageSharp.PixelFormats;
1413

1514
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
@@ -55,17 +54,41 @@ protected override void Decompress(BufferedReadStream stream, int byteCount, int
5554
{
5655
using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder());
5756

58-
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
59-
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
60-
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
61-
new RgbJpegSpectralConverter<Rgb24>(this.configuration) : new SpectralConverter<Rgb24>(this.configuration);
62-
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
63-
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
64-
scanDecoder.ResetInterval = 0;
65-
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
57+
switch (this.photometricInterpretation)
58+
{
59+
case TiffPhotometricInterpretation.BlackIsZero:
60+
case TiffPhotometricInterpretation.WhiteIsZero:
61+
{
62+
using SpectralConverter<L8> spectralConverterGray = new GrayJpegSpectralConverter<L8>(this.configuration);
63+
var scanDecoderGray = new HuffmanScanDecoder(stream, spectralConverterGray, CancellationToken.None);
64+
jpegDecoder.LoadTables(this.jpegTables, scanDecoderGray);
65+
jpegDecoder.ParseStream(stream, scanDecoderGray, CancellationToken.None);
6666

67-
// TODO: Should we pass through the CancellationToken from the tiff decoder?
68-
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
67+
// TODO: Should we pass through the CancellationToken from the tiff decoder?
68+
CopyImageBytesToBuffer(buffer, spectralConverterGray.GetPixelBuffer(CancellationToken.None));
69+
break;
70+
}
71+
72+
// If the PhotometricInterpretation is YCbCr we explicitly assume the JPEG data is in RGB color space.
73+
// There seems no other way to determine that the JPEG data is RGB colorspace (no APP14 marker, componentId's are not RGB).
74+
case TiffPhotometricInterpretation.YCbCr:
75+
case TiffPhotometricInterpretation.Rgb:
76+
{
77+
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
78+
new RgbJpegSpectralConverter<Rgb24>(this.configuration) : new SpectralConverter<Rgb24>(this.configuration);
79+
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
80+
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
81+
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
82+
83+
// TODO: Should we pass through the CancellationToken from the tiff decoder?
84+
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
85+
break;
86+
}
87+
88+
default:
89+
TiffThrowHelper.ThrowNotSupported($"Jpeg compressed tiff with photometric interpretation {this.photometricInterpretation} is not supported");
90+
break;
91+
}
6992
}
7093
else
7194
{
@@ -86,6 +109,18 @@ private static void CopyImageBytesToBuffer(Span<byte> buffer, Buffer2D<Rgb24> pi
86109
}
87110
}
88111

112+
private static void CopyImageBytesToBuffer(Span<byte> buffer, Buffer2D<L8> pixelBuffer)
113+
{
114+
int offset = 0;
115+
for (int y = 0; y < pixelBuffer.Height; y++)
116+
{
117+
Span<L8> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y);
118+
Span<byte> rgbBytes = MemoryMarshal.AsBytes(pixelRowSpan);
119+
rgbBytes.CopyTo(buffer.Slice(offset));
120+
offset += rgbBytes.Length;
121+
}
122+
}
123+
89124
/// <inheritdoc/>
90125
protected override void Dispose(bool disposing)
91126
{

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4-
using System.Threading;
54
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
65
using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters;
76
using SixLabors.ImageSharp.PixelFormats;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ public void CanDecodeJustOneFrame<TPixel>(TestImageProvider<TPixel> provider)
382382
[WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)]
383383
[WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)]
384384
[WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)]
385+
[WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)]
385386
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
386387
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false);
387388

tests/ImageSharp.Tests/TestImages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,7 @@ public static class Tiff
772772

773773
public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff";
774774
public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff";
775+
public const string GrayscaleJpegCompressed = "Tiff/JpegCompressedGray.tiff";
775776
public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff";
776777
public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff";
777778
public const string RgbDeflate = "Tiff/rgb_deflate.tiff";
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:868afd018d025ed7636f1155c1b1f64ba8a36153b56c7598e8dee18ce770cd5a
3+
size 539660

0 commit comments

Comments
 (0)