Skip to content

Commit

Permalink
Merge pull request #1537 from IldarKhayrutdinov/tiff-format
Browse files Browse the repository at this point in the history
#12 Support multi strip encoding. Improve performance and memory usage.
  • Loading branch information
brianpopow committed Feb 15, 2021
2 parents 0e1e8fe + 20726c3 commit 2ec4787
Show file tree
Hide file tree
Showing 49 changed files with 1,106 additions and 1,127 deletions.
1 change: 0 additions & 1 deletion src/ImageSharp/Formats/ImageExtensions.Save.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.Formats.Tiff;

namespace SixLabors.ImageSharp
{
Expand Down
38 changes: 21 additions & 17 deletions src/ImageSharp/Formats/Tiff/Compression/BitWriterUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,38 @@ public static void WriteBits(Span<byte> buffer, int pos, uint count, byte value)
int startIdx = bufferPos + bitPos;
int endIdx = (int)(startIdx + count);

for (int i = startIdx; i < endIdx; i++)
if (value == 1)
{
if (value == 1)
for (int i = startIdx; i < endIdx; i++)
{
WriteBit(buffer, bufferPos, bitPos);

bitPos++;
if (bitPos >= 8)
{
bitPos = 0;
bufferPos++;
}
}
else
}
else
{
for (int i = startIdx; i < endIdx; i++)
{
WriteZeroBit(buffer, bufferPos, bitPos);
}

bitPos++;
if (bitPos >= 8)
{
bitPos = 0;
bufferPos++;
bitPos++;
if (bitPos >= 8)
{
bitPos = 0;
bufferPos++;
}
}
}
}

public static void WriteBit(Span<byte> buffer, int bufferPos, int bitPos)
{
buffer[bufferPos] |= (byte)(1 << (7 - bitPos));
}
public static void WriteBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] |= (byte)(1 << (7 - bitPos));

public static void WriteZeroBit(Span<byte> buffer, int bufferPos, int bitPos)
{
buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos)));
}
public static void WriteZeroBit(Span<byte> buffer, int bufferPos, int bitPos) => buffer[bufferPos] = (byte)(buffer[bufferPos] & ~(1 << (7 - bitPos)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
internal class DeflateCompressor : TiffBaseCompressor
{
private readonly DeflateCompressionLevel compressionLevel;

private readonly MemoryStream memoryStream = new MemoryStream();

public DeflateCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor, DeflateCompressionLevel compressionLevel)
: base(output, allocator, width, bitsPerPixel, predictor)
=> this.compressionLevel = compressionLevel;

/// <inheritdoc/>
public override TiffEncoderCompression Method => TiffEncoderCompression.Deflate;

/// <inheritdoc/>
public override void Initialize(int rowsPerStrip)
{
}

/// <inheritdoc/>
public override void CompressStrip(Span<byte> rows, int height)
{
this.memoryStream.Seek(0, SeekOrigin.Begin);
using var stream = new ZlibDeflateStream(this.Allocator, this.memoryStream, this.compressionLevel);

if (this.Predictor == TiffPredictor.Horizontal)
{
HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel);
}

stream.Write(rows);

stream.Flush();
stream.Dispose();

int size = (int)this.memoryStream.Position;

#if !NETSTANDARD1_3
byte[] buffer = this.memoryStream.GetBuffer();
this.Output.Write(buffer, 0, size);
#else
this.memoryStream.SetLength(size);
this.memoryStream.Position = 0;
this.memoryStream.CopyTo(this.Output);
#endif
}

/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
internal class LzwCompressor : TiffBaseCompressor
{
private TiffLzwEncoder lzwEncoder;

public LzwCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel, TiffPredictor predictor)
: base(output, allocator, width, bitsPerPixel, predictor)
{
}

/// <inheritdoc/>
public override TiffEncoderCompression Method => TiffEncoderCompression.Lzw;

/// <inheritdoc/>
public override void Initialize(int rowsPerStrip) => this.lzwEncoder = new TiffLzwEncoder(this.Allocator);

/// <inheritdoc/>
public override void CompressStrip(Span<byte> rows, int height)
{
if (this.Predictor == TiffPredictor.Horizontal)
{
HorizontalPredictor.ApplyHorizontalPrediction(rows, this.BytesPerRow, this.BitsPerPixel);
}

this.lzwEncoder.Encode(rows, this.Output);
}

/// <inheritdoc/>
protected override void Dispose(bool disposing) => this.lzwEncoder?.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;

namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
internal class NoCompressor : TiffBaseCompressor
{
public NoCompressor(Stream output)
: base(output, default, default, default)
{
}

/// <inheritdoc/>
public override TiffEncoderCompression Method => TiffEncoderCompression.None;

/// <inheritdoc/>
public override void Initialize(int rowsPerStrip)
{
}

/// <inheritdoc/>
public override void CompressStrip(Span<byte> rows, int height) => this.Output.Write(rows);

/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.

using System;
using System.IO;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors
{
internal class PackBitsCompressor : TiffBaseCompressor
{
private IManagedByteBuffer pixelData;

public PackBitsCompressor(Stream output, MemoryAllocator allocator, int width, int bitsPerPixel)
: base(output, allocator, width, bitsPerPixel)
{
}

/// <inheritdoc/>
public override TiffEncoderCompression Method => TiffEncoderCompression.PackBits;

/// <inheritdoc/>
public override void Initialize(int rowsPerStrip)
{
int additionalBytes = ((this.BytesPerRow + 126) / 127) + 1;
this.pixelData = this.Allocator.AllocateManagedByteBuffer(this.BytesPerRow + additionalBytes);
}

/// <inheritdoc/>
public override void CompressStrip(Span<byte> rows, int height)
{
DebugGuard.IsTrue(rows.Length % height == 0, "Invalid height");
DebugGuard.IsTrue(this.BytesPerRow == rows.Length / height, "The widths must match");

Span<byte> span = this.pixelData.GetSpan();
for (int i = 0; i < height; i++)
{
Span<byte> row = rows.Slice(i * this.BytesPerRow, this.BytesPerRow);
int size = PackBitsWriter.PackBits(row, span);
this.Output.Write(span.Slice(0, size));
}
}

/// <inheritdoc/>
protected override void Dispose(bool disposing) => this.pixelData?.Dispose();
}
}
Loading

0 comments on commit 2ec4787

Please sign in to comment.