Browse Source

Allow deflate compression for gray tiff

pull/1570/head
Brian Popow 5 years ago
parent
commit
d0d57ca61a
  1. 2
      src/ImageSharp/Formats/Tiff/README.md
  2. 10
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  3. 40
      src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
  4. 7
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

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

@ -48,7 +48,7 @@
|Lzw | | 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) | | | |
|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now, should we allow this for the gray and palette too? |
|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? |
|Old Deflate (Technote 2) | | Y | |
### Photometric Interpretation Formats

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

@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap);
break;
case TiffEncodingMode.Gray:
imageDataBytes = writer.WriteGray(image, this.padding);
imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType);
break;
default:
imageDataBytes = writer.WriteRgbImageData(image, this.padding, this.CompressionType);
@ -378,7 +378,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff
private ushort GetCompressionType()
{
if (this.CompressionType == TiffEncoderCompression.Deflate &&
this.PhotometricInterpretation == TiffPhotometricInterpretation.Rgb)
this.Mode == TiffEncodingMode.Rgb)
{
return (ushort)TiffCompression.Deflate;
}
if (this.CompressionType == TiffEncoderCompression.Deflate &&
this.Mode == TiffEncodingMode.Gray)
{
return (ushort)TiffCompression.Deflate;
}

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

@ -272,12 +272,19 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</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>
public int WriteGray<TPixel>(Image<TPixel> image, int padding)
public int WriteGray<TPixel>(Image<TPixel> image, int padding, TiffEncoderCompression compression)
where TPixel : unmanaged, IPixel<TPixel>
{
using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding);
Span<byte> rowSpan = row.GetSpan();
if (compression == TiffEncoderCompression.Deflate)
{
return this.WriteGrayDeflateCompressed(image, rowSpan);
}
int bytesWritten = 0;
for (int y = 0; y < image.Height; y++)
{
@ -290,6 +297,37 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return bytesWritten;
}
/// <summary>
/// Writes the image data as 8 bit gray with deflate compression to the stream.
/// </summary>
/// <param name="image">The image to write to the stream.</param>
/// <param name="rowSpan">A span of a pixel row.</param>
/// <returns>The number of bytes written.</returns>
private int WriteGrayDeflateCompressed<TPixel>(Image<TPixel> image, Span<byte> rowSpan)
where TPixel : unmanaged, IPixel<TPixel>
{
int bytesWritten = 0;
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.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length);
deflateStream.Write(rowSpan);
}
deflateStream.Flush();
byte[] buffer = memoryStream.ToArray();
this.output.Write(buffer);
bytesWritten += buffer.Length;
return bytesWritten;
}
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel, int padding) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, padding);
/// <summary>

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

@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff
@ -59,6 +60,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeGray_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray);
[Theory]
[WithFile(TestImages.Tiff.GrayscaleUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeGray_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel8, TiffEncodingMode.Gray, TiffEncoderCompression.Deflate);
// TODO: this test fails, but the output looks correct. I thinks its due to the fact that a quantizer is used to create the palette.
[Theory]
[WithFile(TestImages.Tiff.Calliphora_PaletteUncompressed, PixelTypes.Rgba32)]
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)

Loading…
Cancel
Save