From 769f601fa233b955b9147f6e51cb0bf80c9bcc8d Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Sat, 21 Nov 2020 18:19:42 +0100 Subject: [PATCH] Use memory allocator in t4 bitreader --- .../Formats/Tiff/Compression/T4BitReader.cs | 66 ++++++++++++------- .../Tiff/Compression/T4TiffCompression.cs | 7 +- src/ImageSharp/Formats/Tiff/README.md | 3 +- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs index c37de8031a..b09645a4b3 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs @@ -2,15 +2,17 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.IO; using System.Linq; +using SixLabors.ImageSharp.Memory; namespace SixLabors.ImageSharp.Formats.Tiff.Compression { /// /// Bitreader for reading compressed CCITT T4 1D data. /// - internal class T4BitReader + internal class T4BitReader : IDisposable { /// /// Number of bits read. @@ -42,6 +44,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private bool isFirstScanLine; + /// + /// Indicates whether we have found a termination code which signals the end of a run. + /// private bool terminationCodeFound; /// @@ -49,16 +54,24 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// private uint runLength; + private readonly int dataLength; + private const int MinCodeLength = 2; private const int MaxCodeLength = 13; - public T4BitReader(Stream input, int bytesToRead) + /// + /// Initializes a new instance of the class. + /// + /// The compressed input stream. + /// The number of bytes to read from the stream. + /// The memory allocator. + public T4BitReader(Stream input, int bytesToRead, MemoryAllocator allocator) { - // TODO: use memory allocator - this.Data = new byte[bytesToRead]; - this.ReadImageDataFromStream(input, bytesToRead); + this.Data = allocator.Allocate(bytesToRead); + this.ReadImageDataFromStream(input, bytesToRead, allocator); + this.dataLength = bytesToRead; this.bitsRead = 0; this.value = 0; this.curValueBitsRead = 0; @@ -72,7 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression /// /// Gets the compressed image data. /// - public byte[] Data { get; } + public IMemoryOwner Data { get; } /// /// Gets a value indicating whether there is more data to read left. @@ -81,7 +94,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { get { - return this.position < (ulong)this.Data.Length - 1; + return this.position < (ulong)this.dataLength - 1; } } @@ -207,6 +220,12 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.isFirstScanLine = false; } + /// + public void Dispose() + { + this.Data.Dispose(); + } + private uint WhiteTerminatingCodeRunLength() { switch (this.curValueBitsRead) @@ -401,6 +420,7 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression case 0x2: return 3; } + break; } @@ -959,31 +979,31 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { case 2: { - uint[] codes = {0x3, 0x2}; + uint[] codes = { 0x3, 0x2 }; return codes.Contains(this.value); } case 3: { - uint[] codes = {0x02, 0x03}; + uint[] codes = { 0x02, 0x03 }; return codes.Contains(this.value); } case 4: { - uint[] codes = {0x03, 0x02}; + uint[] codes = { 0x03, 0x02 }; return codes.Contains(this.value); } case 5: { - uint[] codes = {0x03}; + uint[] codes = { 0x03 }; return codes.Contains(this.value); } case 6: { - uint[] codes = {0x5, 0x4}; + uint[] codes = { 0x5, 0x4 }; return codes.Contains(this.value); } @@ -1067,8 +1087,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.LoadNewByte(); } + Span dataSpan = this.Data.GetSpan(); int shift = 8 - this.bitsRead - 1; - var bit = (uint)((this.Data[this.position] & (1 << shift)) != 0 ? 1 : 0); + var bit = (uint)((dataSpan[(int)this.position] & (1 << shift)) != 0 ? 1 : 0); this.bitsRead++; return bit; @@ -1079,24 +1100,23 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression this.position++; this.bitsRead = 0; - if (this.position >= (ulong)this.Data.Length) + if (this.position >= (ulong)this.dataLength) { TiffThrowHelper.ThrowImageFormatException("tiff image has invalid t4 compressed data"); } } - private void ReadImageDataFromStream(Stream input, int bytesToRead) + private void ReadImageDataFromStream(Stream input, int bytesToRead, MemoryAllocator allocator) { - var buffer = new byte[4096]; - - Span bufferSpan = buffer.AsSpan(); - Span dataSpan = this.Data.AsSpan(); + int bufferLength = 4096; + IMemoryOwner buffer = allocator.Allocate(bufferLength); + Span bufferSpan = buffer.GetSpan(); + Span dataSpan = this.Data.GetSpan(); int read; - while (bytesToRead > 0 && - (read = input.Read(buffer, 0, Math.Min(bufferSpan.Length, bytesToRead))) > 0) + while (bytesToRead > 0 && (read = input.Read(bufferSpan, 0, Math.Min(bufferLength, bytesToRead))) > 0) { - buffer.AsSpan(0, read).CopyTo(dataSpan); + buffer.Slice(0, read).CopyTo(dataSpan); bytesToRead -= read; dataSpan = dataSpan.Slice(read); } @@ -1106,5 +1126,5 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression TiffThrowHelper.ThrowImageFormatException("tiff image file has insufficient data"); } } -} + } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs index 52b11613b6..13f7eb7945 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs @@ -29,10 +29,9 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression int whiteValue = isWhiteZero ? 0 : 1; int blackValue = isWhiteZero ? 1 : 0; - var bitReader = new T4BitReader(stream, byteCount); + using var bitReader = new T4BitReader(stream, byteCount, this.Allocator); uint bitsWritten = 0; - uint pixels = 0; while (bitReader.HasMoreData) { bitReader.ReadNextRun(); @@ -54,17 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Tiff.Compression { this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, whiteValue); bitsWritten += bitReader.RunLength; - pixels += bitReader.RunLength; } else { this.WriteBits(buffer, (int)bitsWritten, bitReader.RunLength, blackValue); bitsWritten += bitReader.RunLength; - pixels += bitReader.RunLength; } } - - int foo = 0; } private void WriteBits(Span buffer, int pos, uint count, int value) diff --git a/src/ImageSharp/Formats/Tiff/README.md b/src/ImageSharp/Formats/Tiff/README.md index 2eed880b62..ca9078ae1b 100644 --- a/src/ImageSharp/Formats/Tiff/README.md +++ b/src/ImageSharp/Formats/Tiff/README.md @@ -10,6 +10,7 @@ - [TIFF/EP Extension (Wikipedia)](https://en.wikipedia.org/wiki/TIFF/EP) - [Adobe TIFF Pages](http://partners.adobe.com/public/developer/tiff/index.html) - [Unofficial TIFF FAQ](http://www.awaresystems.be/imaging/tiff/faq.html) + - [CCITT T.4 Compression](https://www.itu.int/rec/T-REC-T.4-198811-S/_page.print) - DNG - [Adobe DNG Pages](https://helpx.adobe.com/photoshop/digital-negative.html) @@ -41,7 +42,7 @@ |None | | Y | | |Ccitt1D | | | | |PackBits | | Y | | -|CcittGroup3Fax | | | | +|CcittGroup3Fax | | Y | | |CcittGroup4Fax | | | | |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 | | | |