|
|
|
@ -369,30 +369,33 @@ namespace SixLabors.ImageSharp.Formats.Tiff |
|
|
|
{ |
|
|
|
int padding = image.Width % 8 == 0 ? 0 : 1; |
|
|
|
int bytesPerRow = (image.Width / 8) + padding; |
|
|
|
using IMemoryOwner<L8> rowL8 = this.memoryAllocator.Allocate<L8>(image.Width); |
|
|
|
using IMemoryOwner<L8> pixelRowAsGray = this.memoryAllocator.Allocate<L8>(image.Width); |
|
|
|
using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); |
|
|
|
using var memoryStream = new MemoryStream(); |
|
|
|
using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable
|
|
|
|
Span<byte> rowSpan = row.GetSpan(); |
|
|
|
Span<L8> rowL8Span = rowL8.GetSpan(); |
|
|
|
Span<byte> outputRow = row.GetSpan(); |
|
|
|
Span<L8> pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); |
|
|
|
|
|
|
|
// Convert image to black and white.
|
|
|
|
using Image<TPixel> imageClone = image.Clone(); |
|
|
|
imageClone.Mutate(img => img.BinaryDither(default(ErrorDither))); |
|
|
|
|
|
|
|
if (compression == TiffEncoderCompression.Deflate) |
|
|
|
{ |
|
|
|
return this.WriteBiColorDeflate(image, pixelRowAsGraySpan, outputRow); |
|
|
|
} |
|
|
|
|
|
|
|
int bytesWritten = 0; |
|
|
|
for (int y = 0; y < image.Height; y++) |
|
|
|
{ |
|
|
|
int bitIndex = 0; |
|
|
|
int byteIndex = 0; |
|
|
|
Span<TPixel> pixelRow = imageClone.GetPixelRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToL8(this.configuration, pixelRow, rowL8Span); |
|
|
|
PixelOperations<TPixel>.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); |
|
|
|
for (int x = 0; x < pixelRow.Length; x++) |
|
|
|
{ |
|
|
|
int shift = 7 - bitIndex; |
|
|
|
if (rowL8Span[x].PackedValue == 255) |
|
|
|
if (pixelRowAsGraySpan[x].PackedValue == 255) |
|
|
|
{ |
|
|
|
rowSpan[byteIndex] |= (byte)(1 << shift); |
|
|
|
outputRow[byteIndex] |= (byte)(1 << shift); |
|
|
|
} |
|
|
|
|
|
|
|
bitIndex++; |
|
|
|
@ -403,27 +406,62 @@ namespace SixLabors.ImageSharp.Formats.Tiff |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (compression == TiffEncoderCompression.Deflate) |
|
|
|
{ |
|
|
|
deflateStream.Write(row); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.output.Write(row); |
|
|
|
bytesWritten += row.Length(); |
|
|
|
} |
|
|
|
this.output.Write(row); |
|
|
|
bytesWritten += row.Length(); |
|
|
|
|
|
|
|
row.Clear(); |
|
|
|
} |
|
|
|
|
|
|
|
if (compression == TiffEncoderCompression.Deflate) |
|
|
|
return bytesWritten; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Writes the image data as 1 bit black and white with deflate compression to the stream.
|
|
|
|
/// </summary>
|
|
|
|
/// <typeparam name="TPixel">The pixel data.</typeparam>
|
|
|
|
/// <param name="image">The image to write to the stream.</param>
|
|
|
|
/// <param name="pixelRowAsGraySpan">A span for converting a pixel row to gray.</param>
|
|
|
|
/// <param name="outputRow">A span which will be used to store the output pixels.</param>
|
|
|
|
/// <returns>The number of bytes written.</returns>
|
|
|
|
public int WriteBiColorDeflate<TPixel>(Image<TPixel> image, Span<L8> pixelRowAsGraySpan, Span<byte> outputRow) |
|
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
|
{ |
|
|
|
using var memoryStream = new MemoryStream(); |
|
|
|
using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable
|
|
|
|
|
|
|
|
int bytesWritten = 0; |
|
|
|
for (int y = 0; y < image.Height; y++) |
|
|
|
{ |
|
|
|
deflateStream.Flush(); |
|
|
|
byte[] buffer = memoryStream.ToArray(); |
|
|
|
this.output.Write(buffer); |
|
|
|
bytesWritten += buffer.Length; |
|
|
|
int bitIndex = 0; |
|
|
|
int byteIndex = 0; |
|
|
|
Span<TPixel> pixelRow = image.GetPixelRowSpan(y); |
|
|
|
PixelOperations<TPixel>.Instance.ToL8(this.configuration, pixelRow, pixelRowAsGraySpan); |
|
|
|
for (int x = 0; x < pixelRow.Length; x++) |
|
|
|
{ |
|
|
|
int shift = 7 - bitIndex; |
|
|
|
if (pixelRowAsGraySpan[x].PackedValue == 255) |
|
|
|
{ |
|
|
|
outputRow[byteIndex] |= (byte)(1 << shift); |
|
|
|
} |
|
|
|
|
|
|
|
bitIndex++; |
|
|
|
if (bitIndex == 8) |
|
|
|
{ |
|
|
|
byteIndex++; |
|
|
|
bitIndex = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
deflateStream.Write(outputRow); |
|
|
|
|
|
|
|
outputRow.Clear(); |
|
|
|
} |
|
|
|
|
|
|
|
deflateStream.Flush(); |
|
|
|
byte[] buffer = memoryStream.ToArray(); |
|
|
|
this.output.Write(buffer); |
|
|
|
bytesWritten += buffer.Length; |
|
|
|
|
|
|
|
return bytesWritten; |
|
|
|
} |
|
|
|
|
|
|
|
|