Browse Source

Merge branch 'main' into bp/pngavgsse2

pull/2028/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
fe06e382f5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      src/ImageSharp/Formats/Gif/GifDecoder.cs
  2. 29
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/GrayJpegSpectralConverter.cs
  3. 57
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/JpegTiffCompression.cs
  4. 1
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/RgbJpegSpectralConverter.cs
  5. 7
      src/ImageSharp/Formats/Tiff/ITiffDecoderOptions.cs
  6. 2
      src/ImageSharp/Formats/Tiff/README.md
  7. 6
      src/ImageSharp/Formats/Tiff/TiffDecoder.cs
  8. 14
      src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs
  9. 13
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  10. 2
      tests/ImageSharp.Tests/TestImages.cs
  11. 3
      tests/Images/Input/Tiff/JpegCompressedGray.tiff
  12. 3
      tests/Images/Input/Tiff/SKC1H3.tiff

1
src/ImageSharp/Formats/Gif/GifDecoder.cs

@ -5,7 +5,6 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;

29
src/ImageSharp/Formats/Tiff/Compression/Decompressors/GrayJpegSpectralConverter.cs

@ -0,0 +1,29 @@
// 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 gray TIFF's which use the JPEG compression.
/// </summary>
/// <typeparam name="TPixel">The type of the pixel.</typeparam>
internal sealed class GrayJpegSpectralConverter<TPixel> : SpectralConverter<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Initializes a new instance of the <see cref="GrayJpegSpectralConverter{TPixel}"/> class.
/// </summary>
/// <param name="configuration">The configuration.</param>
public GrayJpegSpectralConverter(Configuration configuration)
: base(configuration)
{
}
/// <inheritdoc/>
protected override JpegColorConverterBase GetColorConverter(JpegFrame frame, IRawJpegData jpegData) => JpegColorConverterBase.GetConverter(JpegColorSpace.Grayscale, frame.Precision);
}
}

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

@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
@ -55,17 +54,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
using var jpegDecoder = new JpegDecoderCore(this.configuration, new JpegDecoder());
// 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) : new SpectralConverter<Rgb24>(this.configuration);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
scanDecoder.ResetInterval = 0;
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
switch (this.photometricInterpretation)
{
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
{
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, scanDecoderGray, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverterGray.GetPixelBuffer(CancellationToken.None));
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:
{
using SpectralConverter<Rgb24> spectralConverter = this.photometricInterpretation == TiffPhotometricInterpretation.YCbCr ?
new RgbJpegSpectralConverter<Rgb24>(this.configuration) : new SpectralConverter<Rgb24>(this.configuration);
var scanDecoder = new HuffmanScanDecoder(stream, spectralConverter, CancellationToken.None);
jpegDecoder.LoadTables(this.jpegTables, scanDecoder);
jpegDecoder.ParseStream(stream, scanDecoder, CancellationToken.None);
// TODO: Should we pass through the CancellationToken from the tiff decoder?
CopyImageBytesToBuffer(buffer, spectralConverter.GetPixelBuffer(CancellationToken.None));
break;
}
default:
TiffThrowHelper.ThrowNotSupported($"Jpeg compressed tiff with photometric interpretation {this.photometricInterpretation} is not supported");
break;
}
}
else
{
@ -86,6 +109,18 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
}
}
private static void CopyImageBytesToBuffer(Span<byte> buffer, Buffer2D<L8> pixelBuffer)
{
int offset = 0;
for (int y = 0; y < pixelBuffer.Height; y++)
{
Span<L8> pixelRowSpan = pixelBuffer.DangerousGetRowSpan(y);
Span<byte> rgbBytes = MemoryMarshal.AsBytes(pixelRowSpan);
rgbBytes.CopyTo(buffer.Slice(offset));
offset += rgbBytes.Length;
}
}
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{

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

@ -1,7 +1,6 @@
// 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;

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

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp.Formats.Tiff
{
/// <summary>
@ -12,5 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// Gets a value indicating whether the metadata should be ignored when the image is being decoded.
/// </summary>
bool IgnoreMetadata { get; }
/// <summary>
/// Gets the decoding mode for multi-frame images.
/// </summary>
FrameDecodingMode DecodingMode { get; }
}
}

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

@ -25,7 +25,7 @@
## Implementation Status
- The Decoder currently only supports a single frame per image.
- The Decoder currently only supports decoding multiframe images, which have the same dimensions.
- Some compression formats are not yet supported. See the list below.
### Deviations from the TIFF spec (to be fixed)

6
src/ImageSharp/Formats/Tiff/TiffDecoder.cs

@ -4,6 +4,7 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff
@ -18,6 +19,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
public bool IgnoreMetadata { get; set; }
/// <summary>
/// Gets or sets the decoding mode for multi-frame images.
/// </summary>
public FrameDecodingMode DecodingMode { get; set; }
/// <inheritdoc/>
public Image<TPixel> Decode<TPixel>(Configuration configuration, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>

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

@ -13,7 +13,6 @@ using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.Metadata.Profiles.Xmp;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff
@ -33,6 +32,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// </summary>
private readonly bool ignoreMetadata;
/// <summary>
/// Gets the decoding mode for multi-frame images
/// </summary>
private FrameDecodingMode decodingMode;
/// <summary>
/// The stream to decode from.
/// </summary>
@ -59,6 +63,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
this.Configuration = configuration ?? Configuration.Default;
this.ignoreMetadata = options.IgnoreMetadata;
this.decodingMode = options.DecodingMode;
this.memoryAllocator = this.Configuration.MemoryAllocator;
}
@ -160,11 +165,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
cancellationToken.ThrowIfCancellationRequested();
ImageFrame<TPixel> frame = this.DecodeFrame<TPixel>(ifd, cancellationToken);
frames.Add(frame);
if (this.decodingMode is FrameDecodingMode.First)
{
break;
}
}
ImageMetadata metadata = TiffDecoderMetadataCreator.Create(frames, this.ignoreMetadata, reader.ByteOrder, reader.IsBigTiff);
// TODO: Tiff frames can have different sizes
// TODO: Tiff frames can have different sizes.
ImageFrame<TPixel> root = frames[0];
this.Dimensions = root.Size();
foreach (ImageFrame<TPixel> frame in frames)

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

@ -4,6 +4,7 @@
// ReSharper disable InconsistentNaming
using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -365,11 +366,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_PackBitsCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider);
[Theory]
[WithFile(MultiFrameMipMap, PixelTypes.Rgba32)]
public void CanDecodeJustOneFrame<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new TiffDecoder() { DecodingMode = FrameDecodingMode.First }))
{
Assert.Equal(1, image.Frames.Count);
}
}
[Theory]
[WithFile(RgbJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbWithStripsJpegCompressed, PixelTypes.Rgba32)]
[WithFile(YCbCrJpegCompressed, PixelTypes.Rgba32)]
[WithFile(RgbJpegCompressedNoJpegTable, PixelTypes.Rgba32)]
[WithFile(GrayscaleJpegCompressed, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false);

2
tests/ImageSharp.Tests/TestImages.cs

@ -766,6 +766,7 @@ namespace SixLabors.ImageSharp.Tests
public const string GrayscaleDeflateMultistrip = "Tiff/grayscale_deflate_multistrip.tiff";
public const string GrayscaleUncompressed = "Tiff/grayscale_uncompressed.tiff";
public const string GrayscaleJpegCompressed = "Tiff/JpegCompressedGray.tiff";
public const string PaletteDeflateMultistrip = "Tiff/palette_grayscale_deflate_multistrip.tiff";
public const string PaletteUncompressed = "Tiff/palette_uncompressed.tiff";
public const string RgbDeflate = "Tiff/rgb_deflate.tiff";
@ -867,6 +868,7 @@ namespace SixLabors.ImageSharp.Tests
public const string MultiframeDeflateWithPreview = "Tiff/multipage_deflate_withPreview.tiff";
public const string MultiframeDifferentSize = "Tiff/multipage_differentSize.tiff";
public const string MultiframeDifferentVariants = "Tiff/multipage_differentVariants.tiff";
public const string MultiFrameMipMap = "Tiff/SKC1H3.tiff";
public const string LittleEndianByteOrder = "Tiff/little_endian.tiff";

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

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:868afd018d025ed7636f1155c1b1f64ba8a36153b56c7598e8dee18ce770cd5a
size 539660

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

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