Browse Source

Merge pull request #2128 from SixLabors/bp/tiffwebp

Add support for decoding tiff with webp compressed data
pull/2183/head
James Jackson-South 4 years ago
committed by GitHub
parent
commit
4c822ada95
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 55
      src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs
  2. 5
      src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs
  3. 4
      src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs
  4. 8
      src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs
  5. 5
      src/ImageSharp/Formats/Tiff/README.md
  6. 6
      src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs
  7. 11
      tests/ImageSharp.Tests/Formats/Tiff/TiffDecoderTests.cs
  8. 2
      tests/ImageSharp.Tests/TestImages.cs
  9. 3
      tests/Images/Input/Tiff/webp_compressed.tiff

55
src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs

@ -0,0 +1,55 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors
{
/// <summary>
/// Class to handle cases where TIFF image data is compressed as a webp stream.
/// </summary>
internal class WebpTiffCompression : TiffBaseDecompressor
{
/// <summary>
/// Initializes a new instance of the <see cref="WebpTiffCompression"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The width of the image.</param>
/// <param name="bitsPerPixel">The bits per pixel.</param>
/// <param name="predictor">The predictor.</param>
public WebpTiffCompression(MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None)
: base(memoryAllocator, width, bitsPerPixel, predictor)
{
}
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer)
{
using var image = Image.Load<Rgb24>(stream, new WebpDecoder());
CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer);
}
private static void CopyImageBytesToBuffer(Span<byte> buffer, Buffer2D<Rgb24> pixelBuffer)
{
int offset = 0;
for (int y = 0; y < pixelBuffer.Height; y++)
{
Span<Rgb24> 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)
{
}
}
}

5
src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs

@ -47,5 +47,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression
/// The image data is compressed as a JPEG stream.
/// </summary>
Jpeg = 7,
/// <summary>
/// The image data is compressed as a WEBP stream.
/// </summary>
Webp = 8,
}
}

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

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

8
src/ImageSharp/Formats/Tiff/Constants/TiffCompression.cs

@ -103,5 +103,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Constants
/// if this is chosen.
/// </summary>
OldDeflate = 32946,
/// <summary>
/// Pixel data is compressed with webp encoder.
///
/// Note: The TIFF encoder does not support this compression and will default to use no compression instead,
/// if this is chosen.
/// </summary>
Webp = 50001,
}
}

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

@ -37,11 +37,12 @@
|PackBits | Y | Y | |
|CcittGroup3Fax | Y | Y | |
|CcittGroup4Fax | Y | Y | |
|Lzw | Y | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case |
|Old Jpeg | | | We should not even try to support this |
|Lzw | Y | Y | Based on ImageSharp GIF LZW implementation - this code could be modified to be (i) shared, or (ii) optimised for each case. |
|Old Jpeg | | | We should not even try to support this. |
|Jpeg (Technote 2) | Y | Y | |
|Deflate (Technote 2) | Y | Y | Based on PNG Deflate. |
|Old Deflate (Technote 2) | | Y | |
|Webp | | Y | |
### Photometric Interpretation Formats

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

@ -488,6 +488,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
break;
}
case TiffCompression.Webp:
{
options.CompressionType = TiffDecoderCompressionType.Webp;
break;
}
default:
{
TiffThrowHelper.ThrowNotSupported($"The specified TIFF compression format '{compression}' is not supported");

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

@ -667,6 +667,17 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffDecoder_CanDecode_JpegCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffDecoder(provider, useExactComparer: false);
[Theory]
[WithFile(WebpCompressed, PixelTypes.Rgba32)]
public void TiffDecoder_CanDecode_WebpCompressed<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel>
{
if (TestEnvironment.IsWindows)
{
TestTiffDecoder(provider, useExactComparer: false);
}
}
// https://github.com/SixLabors/ImageSharp/issues/1891
[Theory]
[WithFile(Issues1891, PixelTypes.Rgba32)]

2
tests/ImageSharp.Tests/TestImages.cs

@ -763,8 +763,8 @@ namespace SixLabors.ImageSharp.Tests
public const string Fax4Compressed = "Tiff/basi3p02_fax4.tiff";
public const string Fax4Compressed2 = "Tiff/CCITTGroup4.tiff";
public const string Fax4CompressedLowerOrderBitsFirst = "Tiff/basi3p02_fax4_lowerOrderBitsFirst.tiff";
public const string WebpCompressed = "Tiff/webp_compressed.tiff";
public const string Fax4CompressedMinIsBlack = "Tiff/CCITTGroup4_minisblack.tiff";
public const string CcittFax3AllTermCodes = "Tiff/ccitt_fax3_all_terminating_codes.tiff";
public const string CcittFax3AllMakeupCodes = "Tiff/ccitt_fax3_all_makeup_codes.tiff";
public const string HuffmanRleAllTermCodes = "Tiff/huffman_rle_all_terminating_codes.tiff";

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

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