From fd957fae842ed9de6b79f626deb9cfcca6ed97df Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Thu, 12 Aug 2021 07:12:00 +0200 Subject: [PATCH] Add option to encode Tiff's with jpeg compression --- .../Compression/Compressors/PackBitsWriter.cs | 2 +- .../Compressors/TiffJpegCompressor.cs | 49 +++++++++++++++++++ .../Tiff/Compression/TiffCompressorFactory.cs | 6 ++- .../Compression/TiffDecompressorsFactory.cs | 2 +- .../Formats/Tiff/TiffDecoderCore.cs | 2 +- .../Tiff/TiffEncoderEntriesCollector.cs | 3 ++ .../Formats/Tiff/TiffEncoderTests.cs | 7 ++- 7 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs index 30d21e54ce..f456324e53 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors private static bool IsReplicateRun(ReadOnlySpan rowSpan, int startPos) { // We consider run which has at least 3 same consecutive bytes a candidate for a run. - var startByte = rowSpan[startPos]; + byte startByte = rowSpan[startPos]; int count = 0; for (int i = startPos + 1; i < rowSpan.Length; i++) { diff --git a/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs new file mode 100644 index 0000000000..1098c3b292 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffJpegCompressor.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Tiff.Constants; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Tiff.Compression.Compressors +{ + internal class TiffJpegCompressor : TiffBaseCompressor + { + public TiffJpegCompressor(Stream output, MemoryAllocator memoryAllocator, int width, int bitsPerPixel, TiffPredictor predictor = TiffPredictor.None) + : base(output, memoryAllocator, width, bitsPerPixel, predictor) + { + } + + /// + public override TiffCompression Method => TiffCompression.Jpeg; + + /// + public override void Initialize(int rowsPerStrip) + { + } + + /// + public override void CompressStrip(Span rows, int height) + { + int pixelCount = rows.Length / 3; + int width = pixelCount / height; + + using var memoryStream = new MemoryStream(); + var image = Image.LoadPixelData(rows, width, height); + image.Save(memoryStream, new JpegEncoder() + { + Subsample = JpegSubsample.Rgb + }); + memoryStream.Position = 0; + memoryStream.WriteTo(this.Output); + } + + /// + protected override void Dispose(bool disposing) + { + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs index 14a0c6e9d7..db2b935b74 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffCompressorFactory.cs @@ -25,7 +25,6 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression // The following compression types are not implemented in the encoder and will default to no compression instead. case TiffCompression.ItuTRecT43: case TiffCompression.ItuTRecT82: - case TiffCompression.Jpeg: case TiffCompression.OldJpeg: case TiffCompression.OldDeflate: case TiffCompression.None: @@ -34,6 +33,11 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression return new NoCompressor(output, allocator, width, bitsPerPixel); + case TiffCompression.Jpeg: + DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); + DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); + return new TiffJpegCompressor(output, allocator, width, bitsPerPixel); + case TiffCompression.PackBits: DebugGuard.IsTrue(compressionLevel == DeflateCompressionLevel.DefaultCompression, "No deflate compression level is expected to be set"); DebugGuard.IsTrue(predictor == TiffPredictor.None, "Predictor should only be used with lzw or deflate compression"); diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index ee44a70219..04f38c6be2 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression TiffColorType colorType, TiffPredictor predictor, FaxCompressionOptions faxOptions, - byte[] jpegTables, + byte[] jpegTables, TiffFillOrder fillOrder, ByteOrder byteOrder) { diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 5b8c974b46..b0acbf39d4 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -366,7 +366,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff this.ColorType, this.Predictor, this.FaxCompressionOptions, - this.JpegTables, + this.JpegTables, this.FillOrder, this.byteOrder); diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs index 15694978fc..55dd7d3973 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderEntriesCollector.cs @@ -396,6 +396,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff case TiffCompression.Ccitt1D: return (ushort)TiffCompression.Ccitt1D; + + case TiffCompression.Jpeg: + return (ushort)TiffCompression.Jpeg; } return (ushort)TiffCompression.None; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs index 712c8502ab..1a201dd096 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs @@ -114,7 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff [InlineData(TiffPhotometricInterpretation.BlackIsZero, TiffCompression.Ccitt1D, TiffBitsPerPixel.Bit1, TiffCompression.Ccitt1D)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT43, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.ItuTRecT82, TiffBitsPerPixel.Bit24, TiffCompression.None)] - [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] + [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg, TiffBitsPerPixel.Bit24, TiffCompression.Jpeg)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldDeflate, TiffBitsPerPixel.Bit24, TiffCompression.None)] [InlineData(TiffPhotometricInterpretation.Rgb, TiffCompression.OldJpeg, TiffBitsPerPixel.Bit24, TiffCompression.None)] public void EncoderOptions_SetPhotometricInterpretationAndCompression_Works( @@ -288,6 +288,11 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff public void TiffEncoder_EncodeRgb_WithPackBitsCompression_Works(TestImageProvider provider) where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.PackBits); + [Theory] + [WithFile(Calliphora_RgbUncompressed, PixelTypes.Rgba32)] + public void TiffEncoder_EncodeRgb_WithjpegCompression_Works(TestImageProvider provider) + where TPixel : unmanaged, IPixel => TestTiffEncoderCore(provider, TiffBitsPerPixel.Bit24, TiffPhotometricInterpretation.Rgb, TiffCompression.Jpeg); + [Theory] [WithFile(Calliphora_GrayscaleUncompressed, PixelTypes.Rgba32)] public void TiffEncoder_EncodeGray_Works(TestImageProvider provider)