Browse Source

Allow deflate compression for paletted tiff's

pull/1570/head
Brian Popow 6 years ago
parent
commit
add82fc4b3
  1. 6
      src/ImageSharp/Formats/Tiff/README.md
  2. 8
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  3. 37
      src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
  4. 6
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderTests.cs

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

@ -40,7 +40,7 @@
| |Encoder|Decoder|Comments | | |Encoder|Decoder|Comments |
|---------------------------|:-----:|:-----:|--------------------------| |---------------------------|:-----:|:-----:|--------------------------|
|None | Y | Y | encoding only rgb so far | |None | Y | Y | |
|Ccitt1D | | Y | | |Ccitt1D | | Y | |
|PackBits | | Y | | |PackBits | | Y | |
|CcittGroup3Fax | | Y | | |CcittGroup3Fax | | Y | |
@ -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 | |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 | |Old Jpeg | | | We should not even try to support this |
|Jpeg (Technote 2) | | | | |Jpeg (Technote 2) | | | |
|Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. Deflate encoding only for RGB now gray, should we allow this for palette too? | |Deflate (Technote 2) | (Y) | Y | Based on PNG Deflate. |
|Old Deflate (Technote 2) | | Y | | |Old Deflate (Technote 2) | | Y | |
### Photometric Interpretation Formats ### Photometric Interpretation Formats
@ -75,7 +75,7 @@
|ImageWidth | Y | Y | | |ImageWidth | Y | Y | |
|ImageLength | Y | Y | | |ImageLength | Y | Y | |
|BitsPerSample | Y | Y | | |BitsPerSample | Y | Y | |
|Compression | | Y | | |Compression | Y | Y | |
|PhotometricInterpretation | Y | Y | | |PhotometricInterpretation | Y | Y | |
|Thresholding | | | | |Thresholding | | | |
|CellWidth | | | | |CellWidth | | | |

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

@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff
switch (this.Mode) switch (this.Mode)
{ {
case TiffEncodingMode.ColorPalette: case TiffEncodingMode.ColorPalette:
imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, out colorMap); imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, out colorMap);
break; break;
case TiffEncodingMode.Gray: case TiffEncodingMode.Gray:
imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType); imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType);
@ -389,6 +389,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
return (ushort)TiffCompression.Deflate; return (ushort)TiffCompression.Deflate;
} }
if (this.CompressionType == TiffEncoderCompression.Deflate &&
this.Mode == TiffEncodingMode.ColorPalette)
{
return (ushort)TiffCompression.Deflate;
}
return (ushort)TiffCompression.None; return (ushort)TiffCompression.None;
} }
} }

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

@ -201,10 +201,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff
/// <param name="padding">The padding bytes for each row.</param> /// <param name="padding">The padding bytes for each row.</param>
/// <param name="colorMap">The color map.</param> /// <param name="colorMap">The color map.</param>
/// <returns>The number of bytes written.</returns> /// <returns>The number of bytes written.</returns>
public int WritePalettedRgb<TPixel>(Image<TPixel> image, IQuantizer quantizer, int padding, out IExifValue colorMap) public int WritePalettedRgb<TPixel>(Image<TPixel> image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, out IExifValue colorMap)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int colorPaletteSize = 256 * 3 * 2; int colorPaletteSize = 256 * 3 * 2;
using var memoryStream = new MemoryStream();
using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable
using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding);
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration); using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration);
using IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); using IndexedImageFrame<TPixel> quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds());
@ -253,16 +255,41 @@ namespace SixLabors.ImageSharp.Formats.Tiff
for (int y = 0; y < image.Height; y++) for (int y = 0; y < image.Height; y++)
{ {
ReadOnlySpan<byte> pixelSpan = quantized.GetPixelRowSpan(y); ReadOnlySpan<byte> pixelSpan = quantized.GetPixelRowSpan(y);
this.output.Write(pixelSpan);
bytesWritten += pixelSpan.Length; if (compression == TiffEncoderCompression.Deflate)
{
deflateStream.Write(pixelSpan);
}
else
{
// No compression.
this.output.Write(pixelSpan);
bytesWritten += pixelSpan.Length;
}
for (int i = 0; i < padding; i++) for (int i = 0; i < padding; i++)
{ {
this.output.WriteByte(0); if (compression == TiffEncoderCompression.Deflate)
bytesWritten++; {
deflateStream.WriteByte(0);
}
else
{
// no compression.
this.output.WriteByte(0);
bytesWritten++;
}
} }
} }
if (compression == TiffEncoderCompression.Deflate)
{
deflateStream.Flush();
byte[] buffer = memoryStream.ToArray();
this.output.Write(buffer);
bytesWritten += buffer.Length;
}
return bytesWritten; return bytesWritten;
} }

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

@ -71,6 +71,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider) public void TiffEncoder_EncodeColorPalette_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette); where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette);
// 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_WithDeflateCompression_Works<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : unmanaged, IPixel<TPixel> => TestTiffEncoderCore(provider, TiffBitsPerPixel.Pixel24, TiffEncodingMode.ColorPalette, TiffEncoderCompression.Deflate);
private static void TestTiffEncoderCore<TPixel>( private static void TestTiffEncoderCore<TPixel>(
TestImageProvider<TPixel> provider, TestImageProvider<TPixel> provider,
TiffBitsPerPixel bitsPerPixel, TiffBitsPerPixel bitsPerPixel,

Loading…
Cancel
Save