diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 6afe5f9336..2e8fdfc365 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -297,7 +297,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
var rowsPerStrip = new ExifLong(ExifTagValue.RowsPerStrip)
{
- // TODO: all rows in one strip for the start
+ // All rows in one strip.
Value = (uint)image.Height
};
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs
index 96db8e110d..072b548a73 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs
@@ -9,28 +9,46 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
{
+ /*
+ This implementation is a port of a java tiff encoder by Harald Kuhr: https://github.com/haraldk/TwelveMonkeys
+
+ Original licence:
+
+ BSD 3-Clause License
+
+ * Copyright (c) 2015, Harald Kuhr
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ ** Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
///
/// Encodes and compresses the image data using dynamic Lempel-Ziv compression.
///
///
- /// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. K Weiner 12/00
- ///
- /// GIFCOMPR.C - GIF Image compression routines
- ///
- ///
- /// Lempel-Ziv compression based on 'compress'. GIF modifications by
- /// David Rowley (mgardi@watdcsu.waterloo.edu)
- ///
- /// GIF Image compression - modified 'compress'
- ///
- /// Based on: compress.c - File compression ala IEEE Computer, June 1984.
- /// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
- /// Jim McKie (decvax!mcvax!jim)
- /// Steve Davies (decvax!vax135!petsd!peora!srd)
- /// Ken Turkowski (decvax!decwrl!turtlevax!ken)
- /// James A. Woods (decvax!ihnp4!ames!jaw)
- /// Joe Orost (decvax!vax135!petsd!joe)
- ///
///
/// This code is based on the used for GIF encoding. There is potential
/// for a shared implementation. Differences between the GIF and TIFF implementations of the LZW
@@ -42,173 +60,54 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
///
internal sealed class TiffLzwEncoder : IDisposable
{
- ///
- /// The end-of-file marker
- ///
- private const int Eof = -1;
-
- ///
- /// The maximum number of bits.
- ///
- private const int Bits = 12;
-
- ///
- /// 80% occupancy
- ///
- private const int HashSize = 5003;
-
- ///
- /// Mask used when shifting pixel values
- ///
- private static readonly int[] Masks =
- {
- 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF,
- 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
- };
-
- ///
- /// The working pixel array.
- ///
- private readonly IMemoryOwner pixelArray;
+ // Clear: Re-initialize tables.
+ private static readonly int ClearCode = 256;
- ///
- /// The initial code size.
- ///
- private readonly int initialCodeSize;
+ // End of Information.
+ private static readonly int EoiCode = 257;
- ///
- /// The hash table.
- ///
- private readonly IMemoryOwner hashTable;
+ private static readonly int MinBits = 9;
+ private static readonly int MaxBits = 12;
- ///
- /// The code table.
- ///
- private readonly IMemoryOwner codeTable;
+ private static readonly int TableSize = 1 << MaxBits;
- ///
- /// Define the storage for the packet accumulator.
- ///
- private readonly byte[] accumulators = new byte[256];
-
- ///
- /// A value indicating whether this instance of the given entity has been disposed.
- ///
- /// if this instance has been disposed; otherwise, .
- ///
- /// If the entity is disposed, it must not be disposed a second
- /// time. The isDisposed field is set the first time the entity
- /// is disposed. If the isDisposed field is true, then the Dispose()
- /// method will not dispose again. This help not to prolong the entity's
- /// life in the Garbage Collector.
- ///
- private bool isDisposed;
+ private readonly IMemoryOwner data;
- ///
- /// The current pixel
- ///
- private int currentPixel;
+ // A child is made up of a parent (or prefix) code plus a suffix byte
+ // and siblings are strings with a common parent(or prefix) and different suffix bytes.
+ private readonly IMemoryOwner children;
- ///
- /// Number of bits/code
- ///
- private int bitCount;
+ private readonly IMemoryOwner siblings;
- ///
- /// User settable max # bits/code
- ///
- private int maxbits = Bits;
+ private readonly IMemoryOwner suffixes;
- ///
- /// maximum code, given bitCount
- ///
- private int maxcode;
+ // Initial setup
+ private int parent;
+ private int bitsPerCode;
+ private int nextValidCode;
+ private int maxCode;
- ///
- /// should NEVER generate this code
- ///
- private int maxmaxcode = 1 << Bits;
-
- ///
- /// For dynamic table sizing
- ///
- private int hsize = HashSize;
-
- ///
- /// First unused entry
- ///
- private int freeEntry;
-
- ///
- /// Block compression parameters -- after all codes are used up,
- /// and compression rate changes, start over.
- ///
- private bool clearFlag;
-
- ///
- /// Algorithm: use open addressing double hashing (no chaining) on the
- /// prefix code / next character combination. We do a variant of Knuth's
- /// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
- /// secondary probe. Here, the modular division first probe is gives way
- /// to a faster exclusive-or manipulation. Also do block compression with
- /// an adaptive reset, whereby the code table is cleared when the compression
- /// ratio decreases, but after the table fills. The variable-length output
- /// codes are re-sized at this point, and a special CLEAR code is generated
- /// for the decompressor. Late addition: construct the table according to
- /// file size for noticeable speed improvement on small files. Please direct
- /// questions about this implementation to ames!jaw.
- ///
- private int globalInitialBits;
-
- ///
- /// The clear code.
- ///
- private int clearCode;
-
- ///
- /// The end-of-file code.
- ///
- private int eofCode;
-
- ///
- /// Output the given code.
- /// Inputs:
- /// code: A bitCount-bit integer. If == -1, then EOF. This assumes
- /// that bitCount =< wordsize - 1.
- /// Outputs:
- /// Outputs code to the file.
- /// Assumptions:
- /// Chars are 8 bits long.
- /// Algorithm:
- /// Maintain a BITS character long buffer (so that 8 codes will
- /// fit in it exactly). Use the VAX insv instruction to insert each
- /// code in turn. When the buffer fills up empty it and start over.
- ///
- private int currentAccumulator;
-
- ///
- /// The current bits.
- ///
- private int currentBits;
-
- ///
- /// Number of characters so far in this 'packet'
- ///
- private int accumulatorCount;
+ // Buffer for partial codes
+ private int bits;
+ private int bitPos;
+ private int bufferPosition;
///
/// Initializes a new instance of the class.
///
- /// The array of indexed pixels.
- /// The color depth in bits.
+ /// The data to compress.
/// The memory allocator.
- public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner indexedPixels, int colorDepth)
+ public TiffLzwEncoder(MemoryAllocator memoryAllocator, IMemoryOwner data)
{
- this.pixelArray = indexedPixels;
- this.initialCodeSize = Math.Max(2, colorDepth);
-
- this.hashTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean);
- this.codeTable = memoryAllocator.Allocate(HashSize, AllocationOptions.Clean);
+ this.data = data;
+ this.children = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean);
+ this.siblings = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean);
+ this.suffixes = memoryAllocator.Allocate(TableSize, AllocationOptions.Clean);
+
+ this.parent = -1;
+ this.bitsPerCode = MinBits;
+ this.nextValidCode = EoiCode + 1;
+ this.maxCode = (1 << this.bitsPerCode) - 1;
}
///
@@ -217,282 +116,158 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// The stream to write to.
public void Encode(Stream stream)
{
- this.currentPixel = 0;
-
- // Compress and write the pixel data
- this.Compress(this.initialCodeSize + 1, stream);
- }
-
- ///
- public void Dispose()
- {
- // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
- this.Dispose(true);
- }
-
- ///
- /// Gets the maximum code value
- ///
- /// The number of bits
- /// See
- private static int GetMaxcode(int bitCount)
- {
- return (1 << bitCount) - 1;
- }
-
- ///
- /// Add a character to the end of the current packet, and if it is 254 characters,
- /// flush the packet to disk.
- ///
- /// The character to add.
- /// The stream to write to.
- private void AddCharacter(byte c, Stream stream)
- {
- this.accumulators[this.accumulatorCount++] = c;
- if (this.accumulatorCount >= 254)
- {
- this.FlushPacket(stream);
- }
- }
+ Span childrenSpan = this.children.GetSpan();
+ Span suffixesSpan = this.suffixes.GetSpan();
+ Span siblingsSpan = this.siblings.GetSpan();
+ int length = this.data.Length();
- ///
- /// Table clear for block compress
- ///
- /// The output stream.
- private void ClearBlock(Stream stream)
- {
- this.ResetCodeTable(this.hsize);
- this.freeEntry = this.clearCode + 2;
- this.clearFlag = true;
-
- this.Output(this.clearCode, stream);
- }
-
- ///
- /// Reset the code table.
- ///
- /// The hash size.
- private void ResetCodeTable(int size)
- {
- Span hashTableSpan = this.hashTable.GetSpan();
- for (int i = 0; i < size; ++i)
+ if (length == 0)
{
- hashTableSpan[i] = -1;
+ return;
}
- }
-
- ///
- /// Compress the packets to the stream.
- ///
- /// The initial bits.
- /// The stream to write to.
- private void Compress(int intialBits, Stream stream)
- {
- int fcode;
- int c;
- int ent;
- int hsizeReg;
- int hshift;
-
- // Set up the globals: globalInitialBits - initial number of bits
- this.globalInitialBits = intialBits;
-
- // Set up the necessary values
- this.clearFlag = false;
- this.bitCount = this.globalInitialBits;
- this.maxcode = GetMaxcode(this.bitCount);
- this.clearCode = 1 << (intialBits - 1);
- this.eofCode = this.clearCode + 1;
- this.freeEntry = this.clearCode + 2;
-
- this.accumulatorCount = 0; // clear packet
-
- ent = this.NextPixel();
-
- hshift = 0;
- for (fcode = this.hsize; fcode < 65536; fcode *= 2)
+ if (this.parent == -1)
{
- ++hshift;
+ // Init stream.
+ this.WriteCode(stream, ClearCode);
+ this.parent = this.ReadNextByte() & 0xff;
}
- hshift = 8 - hshift; // set hash code range bound
-
- hsizeReg = this.hsize;
-
- this.ResetCodeTable(hsizeReg); // clear hash table
-
- this.Output(this.clearCode, stream);
-
- Span hashTableSpan = this.hashTable.GetSpan();
- Span codeTableSpan = this.codeTable.GetSpan();
- while ((c = this.NextPixel()) != Eof)
+ while (this.bufferPosition < this.data.Length())
{
- fcode = (c << this.maxbits) + ent;
- int i = (c << hshift) ^ ent /* = 0 */;
+ int value = this.ReadNextByte() & 0xff;
+ int child = childrenSpan[this.parent];
- if (hashTableSpan[i] == fcode)
+ if (child > 0)
{
- ent = codeTableSpan[i];
- continue;
- }
-
- // Non-empty slot
- if (hashTableSpan[i] >= 0)
- {
- int disp = hsizeReg - i;
- if (i == 0)
+ if (suffixesSpan[child] == value)
{
- disp = 1;
+ this.parent = child;
}
-
- do
+ else
{
- if ((i -= disp) < 0)
- {
- i += hsizeReg;
- }
+ int sibling = child;
- if (hashTableSpan[i] == fcode)
+ while (true)
{
- ent = codeTableSpan[i];
- break;
+ if (siblingsSpan[sibling] > 0)
+ {
+ sibling = siblingsSpan[sibling];
+
+ if (suffixesSpan[sibling] == value)
+ {
+ this.parent = sibling;
+ break;
+ }
+ }
+ else
+ {
+ siblingsSpan[sibling] = (short)this.nextValidCode;
+ suffixesSpan[this.nextValidCode] = (short)value;
+ this.WriteCode(stream, this.parent);
+ this.parent = value;
+ this.nextValidCode++;
+
+ this.IncreaseCodeSizeOrResetIfNeeded(stream);
+
+ break;
+ }
}
}
- while (hashTableSpan[i] >= 0);
-
- if (hashTableSpan[i] == fcode)
- {
- continue;
- }
- }
-
- this.Output(ent, stream);
- ent = c;
- if (this.freeEntry < this.maxmaxcode)
- {
- codeTableSpan[i] = this.freeEntry++; // code -> hashtable
- hashTableSpan[i] = fcode;
}
else
{
- this.ClearBlock(stream);
+ childrenSpan[this.parent] = (short)this.nextValidCode;
+ suffixesSpan[this.nextValidCode] = (short)value;
+ this.WriteCode(stream, this.parent);
+ this.parent = value;
+ this.nextValidCode++;
+
+ this.IncreaseCodeSizeOrResetIfNeeded(stream);
}
}
- // Put out the final code.
- this.Output(ent, stream);
-
- this.Output(this.eofCode, stream);
- }
+ // Write EOI when we are done.
+ this.WriteCode(stream, this.parent);
+ this.WriteCode(stream, EoiCode);
- ///
- /// Flush the packet to disk, and reset the accumulator.
- ///
- /// The output stream.
- private void FlushPacket(Stream outStream)
- {
- if (this.accumulatorCount > 0)
+ // Flush partial codes by writing 0 pad.
+ if (this.bitPos > 0)
{
- outStream.Write(this.accumulators, 0, this.accumulatorCount);
- this.accumulatorCount = 0;
+ this.WriteCode(stream, 0);
}
}
- ///
- /// Return the next pixel from the image
- ///
- ///
- /// The
- ///
- private int NextPixel()
+ ///
+ public void Dispose()
{
- if (this.currentPixel == this.pixelArray.Length())
- {
- return Eof;
- }
-
- this.currentPixel++;
- return this.pixelArray.GetSpan()[this.currentPixel - 1] & 0xff;
+ this.children.Dispose();
+ this.siblings.Dispose();
+ this.suffixes.Dispose();
}
- ///
- /// Output the current code to the stream.
- ///
- /// The code.
- /// The stream to write to.
- private void Output(int code, Stream outs)
+ private byte ReadNextByte()
{
- this.currentAccumulator &= Masks[this.currentBits];
-
- if (this.currentBits > 0)
- {
- this.currentAccumulator |= code << this.currentBits;
- }
- else
- {
- this.currentAccumulator = code;
- }
-
- this.currentBits += this.bitCount;
-
- while (this.currentBits >= 8)
- {
- this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
- this.currentAccumulator >>= 8;
- this.currentBits -= 8;
- }
+ Span dataSpan = this.data.GetSpan();
+ var nextByte = dataSpan[this.bufferPosition];
+ this.bufferPosition++;
+ return nextByte;
+ }
- // If the next entry is going to be too big for the code size,
- // then increase it, if possible.
- if (this.freeEntry > this.maxcode || this.clearFlag)
+ private void IncreaseCodeSizeOrResetIfNeeded(Stream stream)
+ {
+ if (this.nextValidCode > this.maxCode)
{
- if (this.clearFlag)
+ if (this.bitsPerCode == MaxBits)
{
- this.maxcode = GetMaxcode(this.bitCount = this.globalInitialBits);
- this.clearFlag = false;
+ // Reset stream by writing Clear code.
+ this.WriteCode(stream, ClearCode);
+
+ // Reset tables.
+ this.ResetTables();
}
else
{
- ++this.bitCount;
- this.maxcode = this.bitCount == this.maxbits
- ? this.maxmaxcode
- : GetMaxcode(this.bitCount);
+ // Increase code size.
+ this.bitsPerCode++;
+ this.maxCode = MaxValue(this.bitsPerCode);
}
}
+ }
- if (code == this.eofCode)
- {
- // At EOF, write the rest of the buffer.
- while (this.currentBits > 0)
- {
- this.AddCharacter((byte)(this.currentAccumulator & 0xff), outs);
- this.currentAccumulator >>= 8;
- this.currentBits -= 8;
- }
+ private void WriteCode(Stream stream, int code)
+ {
+ this.bits = (this.bits << this.bitsPerCode) | (code & this.maxCode);
+ this.bitPos += this.bitsPerCode;
- this.FlushPacket(outs);
+ while (this.bitPos >= 8)
+ {
+ int b = (this.bits >> (this.bitPos - 8)) & 0xff;
+ stream.WriteByte((byte)b);
+ this.bitPos -= 8;
}
+
+ this.bits &= BitmaskFor(this.bitPos);
}
- ///
- /// Disposes the object and frees resources for the Garbage Collector.
- ///
- /// If true, the object gets disposed.
- private void Dispose(bool disposing)
+ private void ResetTables()
{
- if (this.isDisposed)
- {
- return;
- }
+ this.children.GetSpan().Fill(0);
+ this.siblings.GetSpan().Fill(0);
- if (disposing)
- {
- this.hashTable.Dispose();
- this.codeTable.Dispose();
- }
+ this.bitsPerCode = MinBits;
+ this.maxCode = MaxValue(this.bitsPerCode);
+ this.nextValidCode = EoiCode + 1;
+ }
- this.isDisposed = true;
+ private static int MaxValue(int codeLen)
+ {
+ return (1 << codeLen) - 1;
+ }
+
+ private static int BitmaskFor(int bits)
+ {
+ return MaxValue(bits);
}
}
}
diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
index 6deeb0a637..09db488903 100644
--- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
+++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs
@@ -238,7 +238,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
rowSpan.CopyTo(pixels.Slice(y * image.Width * 3));
}
- using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8);
+ using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData);
lzwEncoder.Encode(memoryStream);
byte[] buffer = memoryStream.ToArray();
@@ -441,7 +441,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
}
}
- using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8);
+ using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData);
lzwEncoder.Encode(memoryStream);
byte[] buffer = memoryStream.ToArray();
@@ -584,7 +584,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
rowSpan.CopyTo(pixels.Slice(y * image.Width));
}
- using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData, 8);
+ using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData);
lzwEncoder.Encode(memoryStream);
byte[] buffer = memoryStream.ToArray();
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
index 17a18a3466..fbac89e9ac 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs
@@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
[InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes
[InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes
[InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence
- public void Decompress_ReadsData(byte[] data)
+ public void Compress_Decompress_Roundtrip_Works(byte[] data)
{
using (Stream stream = CreateCompressedStream(data))
{
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
index 1837fe98ef..79cc1b1a88 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs
@@ -15,13 +15,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
[Trait("Format", "Tiff")]
public class LzwTiffCompressionTests
{
+ [Theory]
+ [InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 }, new byte[] { 128, 0, 64, 66, 168, 36, 22, 12, 3, 2, 64, 64, 0, 0 })] // Repeated bytes
+
+ public void Compress_Works(byte[] inputData, byte[] expectedCompressedData)
+ {
+ var compressedData = new byte[expectedCompressedData.Length];
+ Stream streamData = CreateCompressedStream(inputData);
+ streamData.Read(compressedData, 0, expectedCompressedData.Length);
+
+ Assert.Equal(expectedCompressedData, compressedData);
+ }
+
[Theory]
[InlineData(new byte[] { })]
[InlineData(new byte[] { 42 })] // One byte
[InlineData(new byte[] { 42, 16, 128, 53, 96, 218, 7, 64, 3, 4, 97 })] // Random bytes
[InlineData(new byte[] { 1, 2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 3, 4 })] // Repeated bytes
[InlineData(new byte[] { 1, 2, 42, 53, 42, 53, 42, 53, 42, 53, 42, 53, 3, 4 })] // Repeated sequence
- public void Decompress_ReadsData(byte[] data)
+
+ public void Compress_Decompress_Roundtrip_Works(byte[] data)
{
using Stream stream = CreateCompressedStream(data);
var buffer = new byte[data.Length];
@@ -37,12 +50,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression
using System.Buffers.IMemoryOwner data = Configuration.Default.MemoryAllocator.Allocate(inputData.Length);
inputData.AsSpan().CopyTo(data.GetSpan());
- using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data, 8))
+ using (var encoder = new TiffLzwEncoder(Configuration.Default.MemoryAllocator, data))
{
encoder.Encode(compressedStream);
}
compressedStream.Seek(0, SeekOrigin.Begin);
+
return compressedStream;
}
}