From 4e64aabc1dcc52c5b63c8e6677de95affcbd1f65 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 30 Jan 2021 18:11:01 +0300 Subject: [PATCH 1/4] Split TiffWriter to the specialized classes --- .../Formats/Tiff/Utils/TiffWriter.cs | 732 +----------------- .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 206 +++++ .../Formats/Tiff/Writers/TiffGrayWriter.cs | 169 ++++ .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 240 ++++++ .../Formats/Tiff/Writers/TiffRgbWriter.cs | 172 ++++ 5 files changed, 811 insertions(+), 708 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index ebfd119f4a..190f7af083 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -2,19 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; using System.IO; -using System.Runtime.InteropServices; - -using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; -using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Dithering; -using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils { @@ -23,25 +12,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// internal class TiffWriter : IDisposable { - private readonly Stream output; - - private readonly MemoryAllocator memoryAllocator; - - private readonly Configuration configuration; - - private readonly byte[] paddingBytes = new byte[4]; + private static readonly byte[] PaddingBytes = new byte[4]; /// /// Initializes a new instance of the class. /// /// The output stream. - /// The memory allocator. + /// The memory allocator. /// The configuration. - public TiffWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) + public TiffWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration) { - this.output = output; - this.memoryAllocator = memoryMemoryAllocator; - this.configuration = configuration; + this.Output = output; + this.MemoryAllocator = memoryAllocator; + this.Configuration = configuration; } /// @@ -52,7 +35,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// Gets the current position within the stream. /// - public long Position => this.output.Position; + public long Position => this.Output.Position; + + protected Stream Output { get; } + + protected MemoryAllocator MemoryAllocator { get; } + + protected Configuration Configuration { get; } /// /// Writes an empty four bytes to the stream, returning the offset to be written later. @@ -60,7 +49,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The offset to be written later public long PlaceMarker() { - long offset = this.output.Position; + long offset = this.Output.Position; this.Write(0u); return offset; } @@ -69,13 +58,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// Writes an array of bytes to the current stream. /// /// The bytes to write. - public void Write(byte[] value) => this.output.Write(value, 0, value.Length); + public void Write(byte[] value) => this.Output.Write(value, 0, value.Length); /// /// Writes a byte to the current stream. /// /// The byte to write. - public void Write(byte value) => this.output.Write(new[] { value }, 0, 1); + public void Write(byte value) => this.Output.Write(new byte[] { value }, 0, 1); /// /// Writes a two-byte unsigned integer to the current stream. @@ -84,7 +73,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils public void Write(ushort value) { byte[] bytes = BitConverter.GetBytes(value); - this.output.Write(bytes, 0, 2); + this.Output.Write(bytes, 0, 2); } /// @@ -94,7 +83,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils public void Write(uint value) { byte[] bytes = BitConverter.GetBytes(value); - this.output.Write(bytes, 0, 4); + this.Output.Write(bytes, 0, 4); } /// @@ -103,11 +92,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The bytes to write. public void WritePadded(byte[] value) { - this.output.Write(value, 0, value.Length); + this.Output.Write(value, 0, value.Length); if (value.Length < 4) { - this.output.Write(this.paddingBytes, 0, 4 - value.Length); + this.Output.Write(PaddingBytes, 0, 4 - value.Length); } } @@ -118,688 +107,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The four-byte unsigned integer to write. public void WriteMarker(long offset, uint value) { - long currentOffset = this.output.Position; - this.output.Seek(offset, SeekOrigin.Begin); + long currentOffset = this.Output.Position; + this.Output.Seek(offset, SeekOrigin.Begin); this.Write(value); - this.output.Seek(currentOffset, SeekOrigin.Begin); - } - - /// - /// Writes the image data as RGB to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. - /// The number of bytes written. - public int WriteRgb(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width * 3); - Span rowSpan = row.GetSpan(); - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteDeflateCompressedRgb(image, rowSpan, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteLzwCompressedRgb(image, rowSpan, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteRgbPackBitsCompressed(image, rowSpan); - } - - // No compression. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - this.output.Write(rowSpan); - bytesWritten += rowSpan.Length; - } - - return bytesWritten; - } - - /// - /// Writes the image data as RGB compressed with zlib to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. - /// The number of bytes written. - private int WriteDeflateCompressedRgb(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); - } - - deflateStream.Write(rowSpan); - } - - deflateStream.Flush(); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as RGB compressed with lzw to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteLzwCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - - IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height * 3); - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); - } - - rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); - } - - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as RGB with packed bits compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A Span for a pixel row. - /// The number of bytes written. - private int WriteRgbPackBitsCompressed(Image image, Span rowSpan) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; - using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - int bytesWritten = 0; - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToRgb24Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); - this.output.Write(compressedRow.Slice(0, size)); - bytesWritten += size; - compressedRowSpan.Clear(); - } - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantizer to use. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. - /// The entries collector. - /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, TiffEncoderEntriesCollector entriesCollector) - where TPixel : unmanaged, IPixel - { - int colorsPerChannel = 256; - int colorPaletteSize = colorsPerChannel * 3; - int colorPaletteBytes = colorPaletteSize * 2; - using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); - using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.configuration); - using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); - using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.AllocateManagedByteBuffer(colorPaletteBytes); - Span colorPalette = colorPaletteBuffer.GetSpan(); - - ReadOnlySpan quantizedColors = quantized.Palette.Span; - int quantizedColorBytes = quantizedColors.Length * 3 * 2; - - // In the ColorMap, black is represented by 0,0,0 and white is represented by 65535, 65535, 65535. - Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); - PixelOperations.Instance.ToRgb48(this.configuration, quantizedColors, quantizedColorRgb48); - - // It can happen that the quantized colors are less than the expected 256 per channel. - var diffToMaxColors = colorsPerChannel - quantizedColors.Length; - - // In a TIFF ColorMap, all the Red values come first, followed by the Green values, - // then the Blue values. Convert the quantized palette to this format. - var palette = new ushort[colorPaletteSize]; - int paletteIdx = 0; - for (int i = 0; i < quantizedColors.Length; i++) - { - palette[paletteIdx++] = quantizedColorRgb48[i].R; - } - - paletteIdx += diffToMaxColors; - - for (int i = 0; i < quantizedColors.Length; i++) - { - palette[paletteIdx++] = quantizedColorRgb48[i].G; - } - - paletteIdx += diffToMaxColors; - - for (int i = 0; i < quantizedColors.Length; i++) - { - palette[paletteIdx++] = quantizedColorRgb48[i].B; - } - - var colorMap = new ExifShortArray(ExifTagValue.ColorMap) - { - Value = palette - }; - - entriesCollector.Add(colorMap); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteLzwCompressedPalettedRgb(image, quantized, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WritePackBitsCompressedPalettedRgb(image, quantized); - } - - // No compression. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - this.output.Write(pixelSpan); - bytesWritten += pixelSpan.Length; - } - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelRow = quantized.GetPixelRowSpan(y); - if (useHorizontalPredictor) - { - // We need a writable Span here. - Span pixelRowCopy = tmpBuffer.GetSpan(); - pixelRow.CopyTo(pixelRowCopy); - HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); - deflateStream.Write(pixelRowCopy); - } - else - { - deflateStream.Write(pixelRow); - } - } - - deflateStream.Flush(); - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with lzw compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - public int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height); - using IManagedByteBuffer tmpBuffer = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); - using var memoryStream = new MemoryStream(); - - int bytesWritten = 0; - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan indexedPixelRow = quantized.GetPixelRowSpan(y); - - if (useHorizontalPredictor) - { - // We need a writable Span here. - Span pixelRowCopy = tmpBuffer.GetSpan(); - indexedPixelRow.CopyTo(pixelRowCopy); - HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); - pixelRowCopy.CopyTo(pixels.Slice(y * image.Width)); - } - else - { - indexedPixelRow.CopyTo(pixels.Slice(y * image.Width)); - } - } - - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - - return bytesWritten; - } - - /// - /// Writes the image data as indices into a color map compressed with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The quantized frame. - /// The number of bytes written. - public int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width * 3 / 127) + 1; - using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); - - int size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); - this.output.Write(compressedRowSpan.Slice(0, size)); - bytesWritten += size; - } - - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The compression to use. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. - /// The number of bytes written. - public int WriteGray(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(image.Width); - Span rowSpan = row.GetSpan(); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteGrayDeflateCompressed(image, rowSpan, compressionLevel, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.Lzw) - { - return this.WriteGrayLzwCompressed(image, rowSpan, useHorizontalPredictor); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteGrayPackBitsCompressed(image, rowSpan); - } - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - this.output.Write(rowSpan); - bytesWritten += rowSpan.Length; - } - - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray with deflate compression to the stream. - /// - /// The image to write to the stream. - /// A span of a row of pixels. - /// The compression level for deflate compression. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteGrayDeflateCompressed(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); - - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); - } - - deflateStream.Write(rowSpan); - } - - deflateStream.Flush(); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray with lzw compression to the stream. - /// - /// The image to write to the stream. - /// A span of a row of pixels. - /// Indicates if horizontal prediction should be used. - /// The number of bytes written. - private int WriteGrayLzwCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel - { - int bytesWritten = 0; - using var memoryStream = new MemoryStream(); - - IMemoryOwner pixelData = this.memoryAllocator.Allocate(image.Width * image.Height); - Span pixels = pixelData.GetSpan(); - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - if (useHorizontalPredictor) - { - HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); - } - - rowSpan.CopyTo(pixels.Slice(y * image.Width)); - } - - using var lzwEncoder = new TiffLzwEncoder(this.memoryAllocator, pixelData); - lzwEncoder.Encode(memoryStream); - - byte[] buffer = memoryStream.ToArray(); - this.output.Write(buffer); - bytesWritten += buffer.Length; - return bytesWritten; - } - - /// - /// Writes the image data as 8 bit gray to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span of a row of pixels. - /// The number of bytes written. - private int WriteGrayPackBitsCompressed(Image image, Span rowSpan) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = (image.Width / 127) + 1; - using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(image.Width + additionalBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.Instance.ToL8Bytes(this.configuration, pixelRow, rowSpan, pixelRow.Length); - int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); - this.output.Write(compressedRow.Slice(0, size)); - bytesWritten += size; - compressedRowSpan.Clear(); - } - - return bytesWritten; - } - - /// - /// Writes the image data as 1 bit black and white to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// The compression to use. - /// The compression level for deflate compression. - /// The number of bytes written. - public int WriteBiColor(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) - where TPixel : unmanaged, IPixel - { - int padding = image.Width % 8 == 0 ? 0 : 1; - int bytesPerRow = (image.Width / 8) + padding; - using IMemoryOwner pixelRowAsGray = this.memoryAllocator.Allocate(image.Width); - using IManagedByteBuffer row = this.memoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); - Span outputRow = row.GetSpan(); - Span pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); - - // Convert image to black and white. - // TODO: Should we allow to skip this by the user, if its known to be black and white already? - using Image imageBlackWhite = image.Clone(); - imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); - - if (compression == TiffEncoderCompression.Deflate) - { - return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow, compressionLevel); - } - - if (compression == TiffEncoderCompression.PackBits) - { - return this.WriteBiColorPackBits(imageBlackWhite, pixelRowAsGraySpan, outputRow); - } - - if (compression == TiffEncoderCompression.CcittGroup3Fax) - { - var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output); - } - - if (compression == TiffEncoderCompression.ModifiedHuffman) - { - var bitWriter = new T4BitWriter(this.memoryAllocator, this.configuration, useModifiedHuffman: true); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.output); - } - - // Write image uncompressed. - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = imageBlackWhite.GetPixelRowSpan(y); - PixelOperations.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; - } - } - - this.output.Write(row); - bytesWritten += row.Length(); - - row.Clear(); - } - - return bytesWritten; - } - - /// - /// Writes the image data as 1 bit black and white with deflate compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span for converting a pixel row to gray. - /// A span which will be used to store the output pixels. - /// The compression level for deflate compression. - /// The number of bytes written. - public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow, DeflateCompressionLevel compressionLevel) - where TPixel : unmanaged, IPixel - { - using var memoryStream = new MemoryStream(); - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.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; - } - - /// - /// Writes the image data as 1 bit black and white with pack bits compression to the stream. - /// - /// The pixel data. - /// The image to write to the stream. - /// A span for converting a pixel row to gray. - /// A span which will be used to store the output pixels. - /// The number of bytes written. - public int WriteBiColorPackBits(Image image, Span pixelRowAsGraySpan, Span outputRow) - where TPixel : unmanaged, IPixel - { - // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits. - int additionalBytes = (image.Width / 127) + 2; - int compressedRowBytes = (image.Width / 8) + additionalBytes; - using IManagedByteBuffer compressedRow = this.memoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean); - Span compressedRowSpan = compressedRow.GetSpan(); - - int bytesWritten = 0; - for (int y = 0; y < image.Height; y++) - { - int bitIndex = 0; - int byteIndex = 0; - Span pixelRow = image.GetPixelRowSpan(y); - PixelOperations.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; - } - } - - var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan); - this.output.Write(compressedRowSpan.Slice(0, size)); - bytesWritten += size; - - outputRow.Clear(); - } - - return bytesWritten; + this.Output.Seek(currentOffset, SeekOrigin.Begin); } /// /// Disposes instance, ensuring any unwritten data is flushed. /// - public void Dispose() => this.output.Flush(); + public void Dispose() => this.Output.Flush(); } } diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs new file mode 100644 index 0000000000..60df87da30 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -0,0 +1,206 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; + +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Dithering; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +{ + /// + /// Utility class for writing TIFF data to a . + /// + internal class TiffBiColorWriter : TiffWriter + { + public TiffBiColorWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration) + : base(output, memoryAllocator, configuration) + { + } + + /// + /// Writes the image data as 1 bit black and white to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The compression to use. + /// The compression level for deflate compression. + /// The number of bytes written. + public int WriteBiColor(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) + where TPixel : unmanaged, IPixel + { + int padding = image.Width % 8 == 0 ? 0 : 1; + int bytesPerRow = (image.Width / 8) + padding; + using IMemoryOwner pixelRowAsGray = this.MemoryAllocator.Allocate(image.Width); + using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(bytesPerRow, AllocationOptions.Clean); + Span outputRow = row.GetSpan(); + Span pixelRowAsGraySpan = pixelRowAsGray.GetSpan(); + + // Convert image to black and white. + // TODO: Should we allow to skip this by the user, if its known to be black and white already? + using Image imageBlackWhite = image.Clone(); + imageBlackWhite.Mutate(img => img.BinaryDither(default(ErrorDither))); + + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow, compressionLevel); + } + + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteBiColorPackBits(imageBlackWhite, pixelRowAsGraySpan, outputRow); + } + + if (compression == TiffEncoderCompression.CcittGroup3Fax) + { + var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output); + } + + if (compression == TiffEncoderCompression.ModifiedHuffman) + { + var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration, useModifiedHuffman: true); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output); + } + + // Write image uncompressed. + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = imageBlackWhite.GetPixelRowSpan(y); + PixelOperations.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; + } + } + + this.Output.Write(row); + bytesWritten += row.Length(); + + row.Clear(); + } + + return bytesWritten; + } + + /// + /// Writes the image data as 1 bit black and white with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span for converting a pixel row to gray. + /// A span which will be used to store the output pixels. + /// The compression level for deflate compression. + /// The number of bytes written. + public int WriteBiColorDeflate(Image image, Span pixelRowAsGraySpan, Span outputRow, DeflateCompressionLevel compressionLevel) + where TPixel : unmanaged, IPixel + { + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.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; + } + + /// + /// Writes the image data as 1 bit black and white with pack bits compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span for converting a pixel row to gray. + /// A span which will be used to store the output pixels. + /// The number of bytes written. + public int WriteBiColorPackBits(Image image, Span pixelRowAsGraySpan, Span outputRow) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bits. + int additionalBytes = (image.Width / 127) + 2; + int compressedRowBytes = (image.Width / 8) + additionalBytes; + using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer(compressedRowBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + int bitIndex = 0; + int byteIndex = 0; + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.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; + } + } + + var size = PackBitsWriter.PackBits(outputRow, compressedRowSpan); + this.Output.Write(compressedRowSpan.Slice(0, size)); + bytesWritten += size; + + outputRow.Clear(); + } + + return bytesWritten; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs new file mode 100644 index 0000000000..a611b88326 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -0,0 +1,169 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; + +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +{ + /// + /// Utility class for writing TIFF data to a . + /// + internal class TiffGrayWriter : TiffWriter + { + public TiffGrayWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) + : base(output, memoryMemoryAllocator, configuration) + { + } + + /// + /// Writes the image data as 8 bit gray to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The compression to use. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. + /// The number of bytes written. + public int WriteGray(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); + Span rowSpan = row.GetSpan(); + + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteGrayDeflateCompressed(image, rowSpan, compressionLevel, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteGrayLzwCompressed(image, rowSpan, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteGrayPackBitsCompressed(image, rowSpan); + } + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + this.Output.Write(rowSpan); + bytesWritten += rowSpan.Length; + } + + return bytesWritten; + } + + /// + /// Writes the image data as 8 bit gray with deflate compression to the stream. + /// + /// The image to write to the stream. + /// A span of a row of pixels. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteGrayDeflateCompressed(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); + } + + deflateStream.Write(rowSpan); + } + + deflateStream.Flush(); + + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + + /// + /// Writes the image data as 8 bit gray with lzw compression to the stream. + /// + /// The image to write to the stream. + /// A span of a row of pixels. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteGrayLzwCompressed(Image image, Span rowSpan, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height); + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + if (useHorizontalPredictor) + { + HorizontalPredictor.ApplyHorizontalPrediction8Bit(rowSpan); + } + + rowSpan.CopyTo(pixels.Slice(y * image.Width)); + } + + using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + + /// + /// Writes the image data as 8 bit gray to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A span of a row of pixels. + /// The number of bytes written. + private int WriteGrayPackBitsCompressed(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = (image.Width / 127) + 1; + using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width + additionalBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToL8Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); + this.Output.Write(compressedRow.Slice(0, size)); + bytesWritten += size; + compressedRowSpan.Clear(); + } + + return bytesWritten; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs new file mode 100644 index 0000000000..bef5fe1bc1 --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -0,0 +1,240 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +{ + /// + /// Utility class for writing TIFF data to a . + /// + internal class TiffPaletteWriter : TiffWriter + { + /// + /// Initializes a new instance of the class. + /// + /// The output stream. + /// The memory allocator. + /// The configuration. + public TiffPaletteWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) + : base(output, memoryMemoryAllocator, configuration) + { + } + + /// + /// Writes the image data as indices into a color map to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantizer to use. + /// The compression to use. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. + /// The entries collector. + /// The number of bytes written. + public int WritePalettedRgb(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, TiffEncoderEntriesCollector entriesCollector) + where TPixel : unmanaged, IPixel + { + int colorsPerChannel = 256; + int colorPaletteSize = colorsPerChannel * 3; + int colorPaletteBytes = colorPaletteSize * 2; + using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); + using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds()); + using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(colorPaletteBytes); + Span colorPalette = colorPaletteBuffer.GetSpan(); + + ReadOnlySpan quantizedColors = quantized.Palette.Span; + int quantizedColorBytes = quantizedColors.Length * 3 * 2; + + // In the ColorMap, black is represented by 0,0,0 and white is represented by 65535, 65535, 65535. + Span quantizedColorRgb48 = MemoryMarshal.Cast(colorPalette.Slice(0, quantizedColorBytes)); + PixelOperations.Instance.ToRgb48(this.Configuration, quantizedColors, quantizedColorRgb48); + + // It can happen that the quantized colors are less than the expected 256 per channel. + var diffToMaxColors = colorsPerChannel - quantizedColors.Length; + + // In a TIFF ColorMap, all the Red values come first, followed by the Green values, + // then the Blue values. Convert the quantized palette to this format. + var palette = new ushort[colorPaletteSize]; + int paletteIdx = 0; + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].R; + } + + paletteIdx += diffToMaxColors; + + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].G; + } + + paletteIdx += diffToMaxColors; + + for (int i = 0; i < quantizedColors.Length; i++) + { + palette[paletteIdx++] = quantizedColorRgb48[i].B; + } + + var colorMap = new ExifShortArray(ExifTagValue.ColorMap) + { + Value = palette + }; + + entriesCollector.Add(colorMap); + + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteDeflateCompressedPalettedRgb(image, quantized, compressionLevel, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteLzwCompressedPalettedRgb(image, quantized, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.PackBits) + { + return this.WritePackBitsCompressedPalettedRgb(image, quantized); + } + + // No compression. + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + this.Output.Write(pixelSpan); + bytesWritten += pixelSpan.Length; + } + + return bytesWritten; + } + + /// + /// Writes the image data as indices into a color map compressed with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer tmpBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelRow = quantized.GetPixelRowSpan(y); + if (useHorizontalPredictor) + { + // We need a writable Span here. + Span pixelRowCopy = tmpBuffer.GetSpan(); + pixelRow.CopyTo(pixelRowCopy); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); + deflateStream.Write(pixelRowCopy); + } + else + { + deflateStream.Write(pixelRow); + } + } + + deflateStream.Flush(); + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + + return bytesWritten; + } + + /// + /// Writes the image data as indices into a color map compressed with lzw compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteLzwCompressedPalettedRgb(Image image, IndexedImageFrame quantized, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height); + using IManagedByteBuffer tmpBuffer = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); + using var memoryStream = new MemoryStream(); + + int bytesWritten = 0; + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan indexedPixelRow = quantized.GetPixelRowSpan(y); + + if (useHorizontalPredictor) + { + // We need a writable Span here. + Span pixelRowCopy = tmpBuffer.GetSpan(); + indexedPixelRow.CopyTo(pixelRowCopy); + HorizontalPredictor.ApplyHorizontalPrediction8Bit(pixelRowCopy); + pixelRowCopy.CopyTo(pixels.Slice(y * image.Width)); + } + else + { + indexedPixelRow.CopyTo(pixels.Slice(y * image.Width)); + } + } + + using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + + return bytesWritten; + } + + /// + /// Writes the image data as indices into a color map compressed with deflate compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The quantized frame. + /// The number of bytes written. + private int WritePackBitsCompressedPalettedRgb(Image image, IndexedImageFrame quantized) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = ((image.Width * 3) / 127) + 1; + using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + ReadOnlySpan pixelSpan = quantized.GetPixelRowSpan(y); + + int size = PackBitsWriter.PackBits(pixelSpan, compressedRowSpan); + this.Output.Write(compressedRowSpan.Slice(0, size)); + bytesWritten += size; + } + + return bytesWritten; + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs new file mode 100644 index 0000000000..b72e923b4c --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -0,0 +1,172 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.IO; + +using SixLabors.ImageSharp.Compression.Zlib; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Tiff.Compression; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +{ + /// + /// Utility class for writing TIFF data to a . + /// + internal class TiffRgbWriter : TiffWriter + { + public TiffRgbWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) + : base(output, memoryMemoryAllocator, configuration) + { + } + + /// + /// Writes the image data as RGB to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// The compression to use. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// The number of bytes written. + public int WriteRgb(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width * 3); + Span rowSpan = row.GetSpan(); + if (compression == TiffEncoderCompression.Deflate) + { + return this.WriteDeflateCompressedRgb(image, rowSpan, compressionLevel, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.Lzw) + { + return this.WriteLzwCompressedRgb(image, rowSpan, useHorizontalPredictor); + } + + if (compression == TiffEncoderCompression.PackBits) + { + return this.WriteRgbPackBitsCompressed(image, rowSpan); + } + + // No compression. + int bytesWritten = 0; + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + this.Output.Write(rowSpan); + bytesWritten += rowSpan.Length; + } + + return bytesWritten; + } + + /// + /// Writes the image data as RGB compressed with zlib to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// The compression level for deflate compression. + /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. + /// The number of bytes written. + private int WriteDeflateCompressedRgb(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + using var deflateStream = new ZlibDeflateStream(this.MemoryAllocator, memoryStream, compressionLevel); + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); + } + + deflateStream.Write(rowSpan); + } + + deflateStream.Flush(); + + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + + /// + /// Writes the image data as RGB compressed with lzw to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// Indicates if horizontal prediction should be used. + /// The number of bytes written. + private int WriteLzwCompressedRgb(Image image, Span rowSpan, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel + { + int bytesWritten = 0; + using var memoryStream = new MemoryStream(); + + IMemoryOwner pixelData = this.MemoryAllocator.Allocate(image.Width * image.Height * 3); + Span pixels = pixelData.GetSpan(); + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + + if (useHorizontalPredictor) + { + HorizontalPredictor.ApplyHorizontalPrediction24Bit(rowSpan); + } + + rowSpan.CopyTo(pixels.Slice(y * image.Width * 3)); + } + + using var lzwEncoder = new TiffLzwEncoder(this.MemoryAllocator, pixelData); + lzwEncoder.Encode(memoryStream); + + byte[] buffer = memoryStream.ToArray(); + this.Output.Write(buffer); + bytesWritten += buffer.Length; + return bytesWritten; + } + + /// + /// Writes the image data as RGB with packed bits compression to the stream. + /// + /// The pixel data. + /// The image to write to the stream. + /// A Span for a pixel row. + /// The number of bytes written. + private int WriteRgbPackBitsCompressed(Image image, Span rowSpan) + where TPixel : unmanaged, IPixel + { + // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. + int additionalBytes = ((image.Width * 3) / 127) + 1; + using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); + Span compressedRowSpan = compressedRow.GetSpan(); + int bytesWritten = 0; + + for (int y = 0; y < image.Height; y++) + { + Span pixelRow = image.GetPixelRowSpan(y); + PixelOperations.Instance.ToRgb24Bytes(this.Configuration, pixelRow, rowSpan, pixelRow.Length); + int size = PackBitsWriter.PackBits(rowSpan, compressedRowSpan); + this.Output.Write(compressedRow.Slice(0, size)); + bytesWritten += size; + compressedRowSpan.Clear(); + } + + return bytesWritten; + } + } +} From 2c1111945f6519c03bbc8b84a402ccff005b6e5b Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sat, 30 Jan 2021 19:18:04 +0300 Subject: [PATCH 2/4] Add color factory, update decoder methods and tests --- .../Formats/Tiff/TiffEncoderCore.cs | 36 ++++++--------- .../Tiff/Writers/TiffBaseColorWriter.cs | 44 ++++++++++++++++++ .../Formats/Tiff/Writers/TiffBiColorWriter.cs | 28 +++++++----- .../Tiff/Writers/TiffColorWriterFactory.cs | 25 +++++++++++ .../Formats/Tiff/Writers/TiffGrayWriter.cs | 18 +++++--- .../Formats/Tiff/Writers/TiffPaletteWriter.cs | 24 +++++----- .../Formats/Tiff/Writers/TiffRgbWriter.cs | 18 +++++--- .../TiffStreamWriter.cs} | 45 +++++++++---------- .../Formats/Tiff/TiffEncoderHeaderTests.cs | 5 ++- .../Formats/Tiff/Utils/TiffWriterTests.cs | 17 +++---- 10 files changed, 165 insertions(+), 95 deletions(-) create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs create mode 100644 src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs rename src/ImageSharp/Formats/Tiff/{Utils/TiffWriter.cs => Writers/TiffStreamWriter.cs} (66%) diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index d206a2d23a..b6125ee767 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -9,7 +9,7 @@ using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.SetPhotometricInterpretation(); - using (var writer = new TiffWriter(stream, this.memoryAllocator, this.configuration)) + using (var writer = new TiffStreamWriter(stream)) { long firstIfdMarker = this.WriteHeader(writer); @@ -134,9 +134,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Writes the TIFF file header. /// - /// The to write data to. - /// The marker to write the first IFD offset. - public long WriteHeader(TiffWriter writer) + /// The to write data to. + /// + /// The marker to write the first IFD offset. + /// + public long WriteHeader(TiffStreamWriter writer) { writer.Write(ByteOrderMarker); writer.Write(TiffConstants.HeaderMagicNumber); @@ -153,29 +155,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// The to encode from. /// The marker to write this IFD offset. /// The marker to write the next IFD offset (if present). - public long WriteImage(TiffWriter writer, Image image, long ifdOffset) + public long WriteImage(TiffStreamWriter writer, Image image, long ifdOffset) where TPixel : unmanaged, IPixel { var entriesCollector = new TiffEncoderEntriesCollector(); // Write the image bytes to the steam. var imageDataStart = (uint)writer.Position; - int imageDataBytes; - switch (this.Mode) - { - case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor, entriesCollector); - break; - case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor); - break; - case TiffEncodingMode.BiColor: - imageDataBytes = writer.WriteBiColor(image, this.CompressionType, this.compressionLevel); - break; - default: - imageDataBytes = writer.WriteRgb(image, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor); - break; - } + + TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(this.Mode, writer, this.memoryAllocator, this.configuration, entriesCollector); + + int imageDataBytes = colorWriter.Write(image, this.quantizer, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor); this.AddStripTags(image, entriesCollector, imageDataStart, imageDataBytes); entriesCollector.ProcessImageFormat(this); @@ -193,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// The to write data to. /// The IFD entries to write to the file. /// The marker to write the next IFD offset (if present). - public long WriteIfd(TiffWriter writer, List entries) + public long WriteIfd(TiffStreamWriter writer, List entries) { if (entries.Count == 0) { diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs new file mode 100644 index 0000000000..d16d8c321c --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -0,0 +1,44 @@ +// 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.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +{ + /// + /// Utility class for writing TIFF data to a . + /// + internal abstract class TiffBaseColorWriter + { + /// + /// Initializes a new instance of the class. + /// + /// The output stream. + /// The memory allocator. + /// The configuration. + /// The entries collector. + public TiffBaseColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + { + this.Output = output; + this.MemoryAllocator = memoryAllocator; + this.Configuration = configuration; + this.EntriesCollector = entriesCollector; + } + + protected TiffStreamWriter Output { get; } + + protected MemoryAllocator MemoryAllocator { get; } + + protected Configuration Configuration { get; } + + protected TiffEncoderEntriesCollector EntriesCollector { get; } + + public abstract int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) + where TPixel : unmanaged, IPixel; + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 60df87da30..5636fa0629 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -11,16 +11,17 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Dithering; +using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { /// /// Utility class for writing TIFF data to a . /// - internal class TiffBiColorWriter : TiffWriter + internal class TiffBiColorWriter : TiffBaseColorWriter { - public TiffBiColorWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration) - : base(output, memoryAllocator, configuration) + public TiffBiColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(output, memoryAllocator, configuration, entriesCollector) { } @@ -29,11 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The pixel data. /// The image to write to the stream. + /// The quantizer. /// The compression to use. /// The compression level for deflate compression. - /// The number of bytes written. - public int WriteBiColor(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) - where TPixel : unmanaged, IPixel + /// if set to true [use horizontal predictor]. + /// + /// The number of bytes written. + /// + public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) { int padding = image.Width % 8 == 0 ? 0 : 1; int bytesPerRow = (image.Width / 8) + padding; @@ -60,13 +64,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.CcittGroup3Fax) { var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output.BaseStream); } if (compression == TiffEncoderCompression.ModifiedHuffman) { var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration, useModifiedHuffman: true); - return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output); + return bitWriter.CompressImage(imageBlackWhite, pixelRowAsGraySpan, this.Output.BaseStream); } // Write image uncompressed. @@ -93,10 +97,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils } } - this.Output.Write(row); - bytesWritten += row.Length(); + this.Output.Write(outputRow); + bytesWritten += outputRow.Length; - row.Clear(); + outputRow.Clear(); } return bytesWritten; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs new file mode 100644 index 0000000000..6c378f28dc --- /dev/null +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs @@ -0,0 +1,25 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Memory; + +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers +{ + internal static class TiffColorWriterFactory + { + public static TiffBaseColorWriter Create(TiffEncodingMode mode, TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + { + switch (mode) + { + case TiffEncodingMode.ColorPalette: + return new TiffPaletteWriter(output, memoryAllocator, configuration, entriesCollector); + case TiffEncodingMode.Gray: + return new TiffGrayWriter(output, memoryAllocator, configuration, entriesCollector); + case TiffEncodingMode.BiColor: + return new TiffBiColorWriter(output, memoryAllocator, configuration, entriesCollector); + default: + return new TiffRgbWriter(output, memoryAllocator, configuration, entriesCollector); + } + } + } +} diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index a611b88326..d2435cc9f7 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -7,19 +7,21 @@ using System.IO; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { /// /// Utility class for writing TIFF data to a . /// - internal class TiffGrayWriter : TiffWriter + internal class TiffGrayWriter : TiffBaseColorWriter { - public TiffGrayWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) - : base(output, memoryMemoryAllocator, configuration) + public TiffGrayWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(output, memoryAllocator, configuration, entriesCollector) { } @@ -28,12 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The pixel data. /// The image to write to the stream. + /// The quantizer. /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression. - /// The number of bytes written. - public int WriteGray(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel + /// + /// The number of bytes written. + /// + public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) { using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); Span rowSpan = row.GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index bef5fe1bc1..0d103f2a10 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -8,27 +8,29 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { /// /// Utility class for writing TIFF data to a . /// - internal class TiffPaletteWriter : TiffWriter + internal class TiffPaletteWriter : TiffBaseColorWriter { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The output stream. - /// The memory allocator. + /// The memory allocator. /// The configuration. - public TiffPaletteWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) - : base(output, memoryMemoryAllocator, configuration) + /// The entries collector. + public TiffPaletteWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(output, memoryAllocator, configuration, entriesCollector) { } @@ -41,10 +43,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression. - /// The entries collector. - /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, TiffEncoderEntriesCollector entriesCollector) - where TPixel : unmanaged, IPixel + /// + /// The number of bytes written. + /// + public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) { int colorsPerChannel = 256; int colorPaletteSize = colorsPerChannel * 3; @@ -93,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils Value = palette }; - entriesCollector.Add(colorMap); + this.EntriesCollector.Add(colorMap); if (compression == TiffEncoderCompression.Deflate) { diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index b72e923b4c..e127706f31 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -7,19 +7,21 @@ using System.IO; using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Quantization; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { /// /// Utility class for writing TIFF data to a . /// - internal class TiffRgbWriter : TiffWriter + internal class TiffRgbWriter : TiffBaseColorWriter { - public TiffRgbWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) - : base(output, memoryMemoryAllocator, configuration) + public TiffRgbWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + : base(output, memoryAllocator, configuration, entriesCollector) { } @@ -28,12 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// The pixel data. /// The image to write to the stream. + /// The quantizer. /// The compression to use. /// The compression level for deflate compression. /// Indicates if horizontal prediction should be used. Should only be used with deflate compression. - /// The number of bytes written. - public int WriteRgb(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) - where TPixel : unmanaged, IPixel + /// + /// The number of bytes written. + /// + public override int Write(Image image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) { using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width * 3); Span rowSpan = row.GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs similarity index 66% rename from src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs rename to src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 190f7af083..8de67d633d 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -5,27 +5,20 @@ using System; using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers { /// /// Utility class for writing TIFF data to a . /// - internal class TiffWriter : IDisposable + internal class TiffStreamWriter : IDisposable { private static readonly byte[] PaddingBytes = new byte[4]; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The output stream. - /// The memory allocator. - /// The configuration. - public TiffWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration) - { - this.Output = output; - this.MemoryAllocator = memoryAllocator; - this.Configuration = configuration; - } + public TiffStreamWriter(Stream output) => this.BaseStream = output; /// /// Gets a value indicating whether the architecture is little-endian. @@ -35,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// Gets the current position within the stream. /// - public long Position => this.Output.Position; + public long Position => this.BaseStream.Position; - protected Stream Output { get; } + public Stream BaseStream { get; } protected MemoryAllocator MemoryAllocator { get; } @@ -49,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The offset to be written later public long PlaceMarker() { - long offset = this.Output.Position; + long offset = this.BaseStream.Position; this.Write(0u); return offset; } @@ -58,13 +51,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// Writes an array of bytes to the current stream. /// /// The bytes to write. - public void Write(byte[] value) => this.Output.Write(value, 0, value.Length); + public void Write(byte[] value) => this.BaseStream.Write(value, 0, value.Length); + + public void Write(ReadOnlySpan value) => this.BaseStream.Write(value); /// /// Writes a byte to the current stream. /// /// The byte to write. - public void Write(byte value) => this.Output.Write(new byte[] { value }, 0, 1); + public void Write(byte value) => this.BaseStream.Write(new byte[] { value }, 0, 1); /// /// Writes a two-byte unsigned integer to the current stream. @@ -73,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils public void Write(ushort value) { byte[] bytes = BitConverter.GetBytes(value); - this.Output.Write(bytes, 0, 2); + this.BaseStream.Write(bytes, 0, 2); } /// @@ -83,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils public void Write(uint value) { byte[] bytes = BitConverter.GetBytes(value); - this.Output.Write(bytes, 0, 4); + this.BaseStream.Write(bytes, 0, 4); } /// @@ -92,11 +87,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The bytes to write. public void WritePadded(byte[] value) { - this.Output.Write(value, 0, value.Length); + this.BaseStream.Write(value, 0, value.Length); if (value.Length < 4) { - this.Output.Write(PaddingBytes, 0, 4 - value.Length); + this.BaseStream.Write(PaddingBytes, 0, 4 - value.Length); } } @@ -107,15 +102,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The four-byte unsigned integer to write. public void WriteMarker(long offset, uint value) { - long currentOffset = this.Output.Position; - this.Output.Seek(offset, SeekOrigin.Begin); + long currentOffset = this.BaseStream.Position; + this.BaseStream.Seek(offset, SeekOrigin.Begin); this.Write(value); - this.Output.Seek(currentOffset, SeekOrigin.Begin); + this.BaseStream.Seek(currentOffset, SeekOrigin.Begin); } /// - /// Disposes instance, ensuring any unwritten data is flushed. + /// Disposes instance, ensuring any unwritten data is flushed. /// - public void Dispose() => this.Output.Flush(); + public void Dispose() => this.BaseStream.Flush(); } } diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs index e3f75ed8b9..d7c066ec2f 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs @@ -5,6 +5,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; using Xunit; @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var stream = new MemoryStream(); var encoder = new TiffEncoderCore(Options, MemoryAllocator); - using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) + using (var writer = new TiffStreamWriter(stream)) { long firstIfdMarker = encoder.WriteHeader(writer); } @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff using var stream = new MemoryStream(); var encoder = new TiffEncoderCore(Options, MemoryAllocator); - using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) + using (var writer = new TiffStreamWriter(stream)) { long firstIfdMarker = encoder.WriteHeader(writer); Assert.Equal(4, firstIfdMarker); diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs index bd9ce37ca7..8d86482ecc 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs @@ -4,6 +4,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers; using SixLabors.ImageSharp.Memory; using Xunit; @@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void IsLittleEndian_IsTrueOnWindows() { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); Assert.True(writer.IsLittleEndian); } @@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.Write(data); Assert.Equal(writer.Position, expectedResult); } @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void Write_WritesByte() { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.Write((byte)42); Assert.Equal(new byte[] { 42 }, stream.ToArray()); @@ -50,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void Write_WritesByteArray() { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.Write(new byte[] { 2, 4, 6, 8 }); Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray()); @@ -60,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void Write_WritesUInt16() { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.Write((ushort)1234); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void Write_WritesUInt32() { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.Write(12345678U); Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); @@ -86,7 +87,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult) { using var stream = new MemoryStream(); - using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); + using var writer = new TiffStreamWriter(stream); writer.WritePadded(bytes); Assert.Equal(expectedResult, stream.ToArray()); @@ -97,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils { using var stream = new MemoryStream(); - using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) + using (var writer = new TiffStreamWriter(stream)) { writer.Write(0x11111111); long marker = writer.PlaceMarker(); From 98dde8e863c7db81d57078d3ffbd5442876e3f97 Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Sun, 31 Jan 2021 16:15:55 +0300 Subject: [PATCH 3/4] Rename decompressors classes --- .../Tiff/Compression/{ => Compressors}/PackBitsWriter.cs | 2 +- .../Tiff/Compression/{ => Compressors}/T4BitWriter.cs | 2 +- .../{Utils => Compression/Compressors}/TiffLzwEncoder.cs | 0 .../{ => Decompressors}/DeflateTiffCompression.cs | 2 +- .../{ => Decompressors}/FaxCompressionOptions.cs | 2 +- .../Compression/{ => Decompressors}/LzwTiffCompression.cs | 3 +-- .../{ => Decompressors}/ModifiedHuffmanTiffCompression.cs | 2 +- .../Compression/{ => Decompressors}/NoneTiffCompression.cs | 7 ++----- .../{ => Decompressors}/PackBitsTiffCompression.cs | 2 +- .../Tiff/Compression/{ => Decompressors}/T4BitReader.cs | 2 +- .../Compression/{ => Decompressors}/T4TiffCompression.cs | 2 +- .../Compression/{ => Decompressors}/TiffBaseCompression.cs | 2 +- .../{ => Decompressors}/TiffDecoderCompressionType.cs | 2 +- .../TiffDecompressorsFactory.cs} | 4 ++-- .../{Utils => Compression/Decompressors}/TiffLzwDecoder.cs | 2 +- src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs | 7 +++---- src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs | 2 +- src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs | 2 +- src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs | 2 +- src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs | 4 ++-- src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs | 4 ++-- .../Tiff/Compression/DeflateTiffCompressionTests.cs | 2 +- .../Formats/Tiff/Compression/LzwTiffCompressionTests.cs | 3 +-- .../Formats/Tiff/Compression/NoneTiffCompressionTests.cs | 2 +- .../Tiff/Compression/PackBitsTiffCompressionTests.cs | 4 ++-- 25 files changed, 31 insertions(+), 37 deletions(-) rename src/ImageSharp/Formats/Tiff/Compression/{ => Compressors}/PackBitsWriter.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Compressors}/T4BitWriter.cs (99%) rename src/ImageSharp/Formats/Tiff/{Utils => Compression/Compressors}/TiffLzwEncoder.cs (100%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/DeflateTiffCompression.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/FaxCompressionOptions.cs (98%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/LzwTiffCompression.cs (96%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/ModifiedHuffmanTiffCompression.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/NoneTiffCompression.cs (88%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/PackBitsTiffCompression.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/T4BitReader.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/T4TiffCompression.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/TiffBaseCompression.cs (99%) rename src/ImageSharp/Formats/Tiff/Compression/{ => Decompressors}/TiffDecoderCompressionType.cs (98%) rename src/ImageSharp/Formats/Tiff/Compression/{TiffCompressionFactory.cs => Decompressors/TiffDecompressorsFactory.cs} (96%) rename src/ImageSharp/Formats/Tiff/{Utils => Compression/Decompressors}/TiffLzwDecoder.cs (98%) diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs rename to src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs index d70c9a370c..67ff3eba8d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/PackBitsWriter.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { /// /// Pack Bits compression for tiff images. See Tiff Spec v6, section 9. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs rename to src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs index 4041a57c32..99e2a148c8 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Compressors/T4BitWriter.cs @@ -9,7 +9,7 @@ using System.IO; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors { /// /// Bitwriter for writing compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs similarity index 100% rename from src/ImageSharp/Formats/Tiff/Utils/TiffLzwEncoder.cs rename to src/ImageSharp/Formats/Tiff/Compression/Compressors/TiffLzwEncoder.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs index 73669d3cb4..8c0dbee95e 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/DeflateTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/DeflateTiffCompression.cs @@ -10,7 +10,7 @@ using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using Deflate compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs similarity index 98% rename from src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs index d5171db657..c98ad0387c 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/FaxCompressionOptions.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/FaxCompressionOptions.cs @@ -3,7 +3,7 @@ using System; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Fax compression options, see TIFF spec page 51f (T4Options). diff --git a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs similarity index 96% rename from src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs index 5e5cdd3791..98aecd1732 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/LzwTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/LzwTiffCompression.cs @@ -4,12 +4,11 @@ using System; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using LZW compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs index 539fc83dce..1664cbebb0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/ModifiedHuffmanTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/ModifiedHuffmanTiffCompression.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using Modified Huffman Compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs similarity index 88% rename from src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs index d1b0c79b31..9840300d91 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/NoneTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/NoneTiffCompression.cs @@ -6,7 +6,7 @@ using System; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is not compressed. @@ -23,9 +23,6 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression } /// - protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) - { - _ = stream.Read(buffer, 0, byteCount); - } + protected override void Decompress(BufferedReadStream stream, int byteCount, Span buffer) => _ = stream.Read(buffer, 0, byteCount); } } diff --git a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs index 181c8e558d..6fd939e612 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/PackBitsTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/PackBitsTiffCompression.cs @@ -7,7 +7,7 @@ using System.Buffers; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using PackBits compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs index 35dd829ffc..8aac1d91b9 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4BitReader.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4BitReader.cs @@ -8,7 +8,7 @@ using System.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Bitreader for reading compressed CCITT T4 1D data. diff --git a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs index 2d0d7f5753..005b5132a5 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/T4TiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/T4TiffCompression.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Class to handle cases where TIFF image data is compressed using CCITT T4 compression. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs similarity index 99% rename from src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs index 7046f0fe11..7262b483b0 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffBaseCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffBaseCompression.cs @@ -8,7 +8,7 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Base tiff decompressor class. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs similarity index 98% rename from src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs index 247d91e63e..8ec11c3605 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecoderCompressionType.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecoderCompressionType.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Provides enumeration of the various TIFF compression types the decoder can handle. diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs similarity index 96% rename from src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs index 55cd64b777..2f78405bb7 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffCompressionFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffDecompressorsFactory.cs @@ -4,9 +4,9 @@ using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { - internal static class TiffCompressionFactory + internal static class TiffDecompressorsFactory { public static TiffBaseCompression Create( TiffDecoderCompressionType compressionType, diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs similarity index 98% rename from src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs rename to src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs index 1ce99980a5..d8150bea77 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffLzwDecoder.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/TiffLzwDecoder.cs @@ -7,7 +7,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.Memory; -namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils +namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors { /// /// Decompresses and decodes data using the dynamic LZW algorithms. diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs index 9b7afe0052..fa4cc31c3e 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderCore.cs @@ -3,8 +3,7 @@ using System.Collections.Generic; using System.Threading; - -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; @@ -251,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff stripBuffers[stripIndex] = this.memoryAllocator.AllocateManagedByteBuffer(uncompressedStripSize); } - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + TiffBaseCompression decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); RgbPlanarTiffColor colorDecoder = TiffColorDecoderFactory.CreatePlanar(this.ColorType, this.BitsPerSample, this.ColorMap); @@ -294,7 +293,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff Buffer2D pixels = frame.PixelBuffer; - TiffBaseCompression decompressor = TiffCompressionFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); + TiffBaseCompression decompressor = TiffDecompressorsFactory.Create(this.CompressionType, this.memoryAllocator, this.PhotometricInterpretation, width, bitsPerPixel, this.Predictor, this.FaxCompressionOptions); TiffBaseColorDecoder colorDecoder = TiffColorDecoderFactory.Create(this.ColorType, this.BitsPerSample, this.ColorMap); diff --git a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs index 2c632b36e4..625af123df 100644 --- a/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs +++ b/src/ImageSharp/Formats/Tiff/TiffDecoderOptionsParser.cs @@ -1,7 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Metadata.Profiles.Exif; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs index 5636fa0629..24c2f08daf 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs index d2435cc9f7..7161254f83 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs index 0d103f2a10..55a2efc9a9 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs @@ -7,7 +7,7 @@ using System.IO; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; @@ -222,7 +222,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = ((image.Width * 3) / 127) + 1; + int additionalBytes = (image.Width * 3 / 127) + 1; using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs index e127706f31..26f2d82d86 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs @@ -6,7 +6,7 @@ using System.Buffers; using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Memory; @@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers where TPixel : unmanaged, IPixel { // Worst case is that the actual compressed data is larger then the input data. In this case we need 1 additional byte per 127 bytes. - int additionalBytes = ((image.Width * 3) / 127) + 1; + int additionalBytes = (image.Width * 3 / 127) + 1; using IManagedByteBuffer compressedRow = this.MemoryAllocator.AllocateManagedByteBuffer((image.Width * 3) + additionalBytes, AllocationOptions.Clean); Span compressedRowSpan = compressedRow.GetSpan(); int bytesWritten = 0; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 18025eecbd..f1de0c9715 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -3,7 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Compression.Zlib; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.IO; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs index 6ba1b32261..94835962da 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/LzwTiffCompressionTests.cs @@ -3,8 +3,7 @@ using System; using System.IO; - -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.IO; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs index dcdd327c27..b366694578 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/NoneTiffCompressionTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System.IO; -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.IO; using Xunit; diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs index f272c7cb9d..6fcaa24d26 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/PackBitsTiffCompressionTests.cs @@ -3,8 +3,8 @@ using System; using System.IO; - -using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Compressors; +using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression.Decompressors; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; From 8d01c602d2c89d85f9b1c5ed3ffe54883aeee77a Mon Sep 17 00:00:00 2001 From: Ildar Khayrutdinov Date: Mon, 1 Feb 2021 14:41:28 +0300 Subject: [PATCH 4/4] Cleanup --- .../Formats/Tiff/Writers/TiffBaseColorWriter.cs | 2 +- .../Formats/Tiff/Writers/TiffStreamWriter.cs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs index d16d8c321c..23b4e329df 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The memory allocator. /// The configuration. /// The entries collector. - public TiffBaseColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) + protected TiffBaseColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector) { this.Output = output; this.MemoryAllocator = memoryAllocator; diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs index 8de67d633d..5b971962a4 100644 --- a/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs @@ -30,12 +30,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// public long Position => this.BaseStream.Position; + /// + /// Gets the base stream. + /// public Stream BaseStream { get; } - protected MemoryAllocator MemoryAllocator { get; } - - protected Configuration Configuration { get; } - /// /// Writes an empty four bytes to the stream, returning the offset to be written later. /// @@ -53,6 +52,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers /// The bytes to write. public void Write(byte[] value) => this.BaseStream.Write(value, 0, value.Length); + /// + /// Writes the specified value. + /// + /// The bytes to write. public void Write(ReadOnlySpan value) => this.BaseStream.Write(value); ///