Browse Source

Add support for encoding deflate compressed tiff's

pull/1570/head
Brian Popow 6 years ago
parent
commit
4e5be46960
  1. 5
      src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs
  2. 23
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  3. 29
      src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
  4. 24
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

5
src/ImageSharp/Formats/Tiff/TiffEncoderCompression.cs

@ -12,5 +12,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// No compression is used. /// No compression is used.
/// </summary> /// </summary>
None, None,
/// <summary>
/// Use zlib compression.
/// </summary>
Deflate
} }
} }

23
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -150,16 +150,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
// Write the image bytes to the steam. // Write the image bytes to the steam.
var imageDataStart = (uint)writer.Position; var imageDataStart = (uint)writer.Position;
int imageDataBytes; int imageDataBytes;
switch (this.PhotometricInterpretation) switch (this.Mode)
{ {
case TiffPhotometricInterpretation.PaletteColor: case TiffEncodingMode.ColorPalette:
imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap); imageDataBytes = writer.WritePalettedRgbImageData(image, this.quantizer, this.padding, out colorMap);
break; break;
case TiffPhotometricInterpretation.BlackIsZero: case TiffEncodingMode.Gray:
imageDataBytes = writer.WriteGrayImageData(image, this.padding); imageDataBytes = writer.WriteGrayImageData(image, this.padding);
break; break;
default: default:
imageDataBytes = writer.WriteRgbImageData(image, this.padding); imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType);
break; break;
} }
@ -260,10 +260,10 @@ namespace SixLabors.ImageSharp.Formats.Tiff
Value = bitsPerSampleValue Value = bitsPerSampleValue
}; };
ushort compressionType = this.GetCompressionType();
var compression = new ExifShort(ExifTagValue.Compression) var compression = new ExifShort(ExifTagValue.Compression)
{ {
// TODO: for the start, no compression is used. Value = compressionType
Value = (ushort)TiffCompression.None
}; };
var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation) var photometricInterpretation = new ExifShort(ExifTagValue.PhotometricInterpretation)
@ -374,5 +374,16 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return new ushort[] { 8, 8, 8 }; return new ushort[] { 8, 8, 8 };
} }
} }
private ushort GetCompressionType()
{
if (this.CompressionType == TiffEncoderCompression.Deflate &&
this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb)
{
return (ushort)TiffCompression.Deflate;
}
return (ushort)TiffCompression.None;
}
} }
} }

29
src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs

@ -6,6 +6,9 @@ using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Png.Zlib;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -133,13 +136,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <typeparam name="TPixel">The pixel data.</typeparam> /// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param> /// <param name="image">The image to write to the stream.</param>
/// <param name="padding">The padding bytes for each row.</param> /// <param name="padding">The padding bytes for each row.</param>
/// <param name="compression">The compression to use.</param>
/// <returns>The number of bytes written.</returns> /// <returns>The number of bytes written.</returns>
public int WriteRgbImageData<TPixel>(Image<TPixel> image, int padding) public int WriteRgbImageData<TPixel>(Image<TPixel> image, int padding, TiffEncoderCompression compression)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
int bytesWritten = 0; int bytesWritten = 0;
if (compression == TiffEncoderCompression.Deflate)
{
using var memoryStream = new MemoryStream();
// TODO: move zlib compression from png to a common place?
using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable
for (int y = 0; y < image.Height; y++)
{
Span<TPixel> pixelRow = image.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
deflateStream.Write(rowSpan);
}
deflateStream.Flush();
byte[] buffer = memoryStream.ToArray();
this.output.Write(buffer);
bytesWritten += buffer.Length;
return bytesWritten;
}
// No compression.
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
Span<TPixel> pixelRow = image.GetPixelRowSpan(y); Span<TPixel> pixelRow = image.GetPixelRowSpan(y);

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

@ -3,10 +3,11 @@
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Tiff; using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit; using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff namespace SixLabors.ImageSharp.Tests.Formats.Tiff
@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Trait("Category", "Tiff")] [Trait("Category", "Tiff")]
public class TiffEncoderTests public class TiffEncoderTests
{ {
private static TiffDecoder referenceDecoder = new TiffDecoder(); private static readonly IImageDecoder ReferenceDecoder = new MagickReferenceDecoder();
public static readonly TheoryData<string, TiffBitsPerPixel> TiffBitsPerPixelFiles = public static readonly TheoryData<string, TiffBitsPerPixel> TiffBitsPerPixelFiles =
new TheoryData<string, TiffBitsPerPixel> new TheoryData<string, TiffBitsPerPixel>
@ -45,18 +46,23 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
[Theory] [Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeRgb_Works<TPixel>(TestImageProvider<TPixel> provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.Rgb) public void TiffEncoder_EncodeRgb_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, bitsPerPixel, mode); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb);
[Theory] [Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel8, TiffEncodingMode mode = TiffEncodingMode.Gray) public void TiffEncoder_EncodeRgb_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, bitsPerPixel, mode); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.Rgb, TiffEncoderCompression.Deflate);
[Theory]
[WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray);
[Theory] [Theory]
[WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)] [WithFile(TestImages.Tiff.RgbUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider, TiffBitsPerPixel bitsPerPixel = TiffBitsPerPixel.Pixel24, TiffEncodingMode mode = TiffEncodingMode.ColorPalette) public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, bitsPerPixel, mode); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette);
private static void TestTiffEncoderCore<TPixel>( private static void TestTiffEncoderCore<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
@ -71,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
var encoder = new TiffEncoder { Mode = mode, Compression = compression }; var encoder = new TiffEncoder { Mode = mode, Compression = compression };
// Does DebugSave & load reference CompareToReferenceInput(): // Does DebugSave & load reference CompareToReferenceInput():
image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: referenceDecoder); image.VerifyEncoder(provider, "tiff", bitsPerPixel, encoder, useExactComparer ? ImageComparer.Exact : ImageComparer.Tolerant(compareTolerance), referenceDecoder: ReferenceDecoder);
} }
} }
} }

Loading…
Cancel
Save