Browse Source

Add color factory, update decoder methods and tests

pull/1570/head
Ildar Khayrutdinov 5 years ago
parent
commit
2c1111945f
  1. 36
      src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
  2. 44
      src/ImageSharp/Formats/Tiff/Writers/TiffBaseColorWriter.cs
  3. 28
      src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs
  4. 25
      src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
  5. 18
      src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs
  6. 24
      src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs
  7. 18
      src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs
  8. 45
      src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs
  9. 5
      tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
  10. 17
      tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs

36
src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs

@ -9,7 +9,7 @@ using System.Threading;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; 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.Memory;
using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
@ -122,7 +122,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
this.SetPhotometricInterpretation(); this.SetPhotometricInterpretation();
using (var writer = new TiffWriter(stream, this.memoryAllocator, this.configuration)) using (var writer = new TiffStreamWriter(stream))
{ {
long firstIfdMarker = this.WriteHeader(writer); long firstIfdMarker = this.WriteHeader(writer);
@ -134,9 +134,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// <summary> /// <summary>
/// Writes the TIFF file header. /// Writes the TIFF file header.
/// </summary> /// </summary>
/// <param name="writer">The <see cref="TiffWriter"/> to write data to.</param> /// <param name="writer">The <see cref="TiffStreamWriter" /> to write data to.</param>
/// <returns>The marker to write the first IFD offset.</returns> /// <returns>
public long WriteHeader(TiffWriter writer) /// The marker to write the first IFD offset.
/// </returns>
public long WriteHeader(TiffStreamWriter writer)
{ {
writer.Write(ByteOrderMarker); writer.Write(ByteOrderMarker);
writer.Write(TiffConstants.HeaderMagicNumber); writer.Write(TiffConstants.HeaderMagicNumber);
@ -153,29 +155,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param> /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param>
/// <param name="ifdOffset">The marker to write this IFD offset.</param> /// <param name="ifdOffset">The marker to write this IFD offset.</param>
/// <returns>The marker to write the next IFD offset (if present).</returns> /// <returns>The marker to write the next IFD offset (if present).</returns>
public long WriteImage<TPixel>(TiffWriter writer, Image<TPixel> image, long ifdOffset) public long WriteImage<TPixel>(TiffStreamWriter writer, Image<TPixel> image, long ifdOffset)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
var entriesCollector = new TiffEncoderEntriesCollector(); var entriesCollector = new TiffEncoderEntriesCollector();
// Write the image bytes to the steam. // Write the image bytes to the steam.
var imageDataStart = (uint)writer.Position; var imageDataStart = (uint)writer.Position;
int imageDataBytes;
switch (this.Mode) TiffBaseColorWriter colorWriter = TiffColorWriterFactory.Create(this.Mode, writer, this.memoryAllocator, this.configuration, entriesCollector);
{
case TiffEncodingMode.ColorPalette: int imageDataBytes = colorWriter.Write(image, this.quantizer, this.CompressionType, this.compressionLevel, this.UseHorizontalPredictor);
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;
}
this.AddStripTags(image, entriesCollector, imageDataStart, imageDataBytes); this.AddStripTags(image, entriesCollector, imageDataStart, imageDataBytes);
entriesCollector.ProcessImageFormat(this); entriesCollector.ProcessImageFormat(this);
@ -193,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff
/// <param name="writer">The <see cref="BinaryWriter"/> to write data to.</param> /// <param name="writer">The <see cref="BinaryWriter"/> to write data to.</param>
/// <param name="entries">The IFD entries to write to the file.</param> /// <param name="entries">The IFD entries to write to the file.</param>
/// <returns>The marker to write the next IFD offset (if present).</returns> /// <returns>The marker to write the next IFD offset (if present).</returns>
public long WriteIfd(TiffWriter writer, List<IExifValue> entries) public long WriteIfd(TiffStreamWriter writer, List<IExifValue> entries)
{ {
if (entries.Count == 0) if (entries.Count == 0)
{ {

44
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
{
/// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary>
internal abstract class TiffBaseColorWriter
{
/// <summary>
/// Initializes a new instance of the <see cref="TiffBaseColorWriter" /> class.
/// </summary>
/// <param name="output">The output stream.</param>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="configuration">The configuration.</param>
/// <param name="entriesCollector">The entries collector.</param>
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<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor)
where TPixel : unmanaged, IPixel<TPixel>;
}
}

28
src/ImageSharp/Formats/Tiff/Writers/TiffBiColorWriter.cs

@ -11,16 +11,17 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Dithering; 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
{ {
/// <summary> /// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>. /// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary> /// </summary>
internal class TiffBiColorWriter : TiffWriter internal class TiffBiColorWriter : TiffBaseColorWriter
{ {
public TiffBiColorWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration) public TiffBiColorWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
: base(output, memoryAllocator, configuration) : base(output, memoryAllocator, configuration, entriesCollector)
{ {
} }
@ -29,11 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam> /// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param> /// <param name="image">The image to write to the stream.</param>
/// <param name="quantizer">The quantizer.</param>
/// <param name="compression">The compression to use.</param> /// <param name="compression">The compression to use.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param> /// <param name="compressionLevel">The compression level for deflate compression.</param>
/// <returns>The number of bytes written.</returns> /// <param name="useHorizontalPredictor">if set to <c>true</c> [use horizontal predictor].</param>
public int WriteBiColor<TPixel>(Image<TPixel> image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) /// <returns>
where TPixel : unmanaged, IPixel<TPixel> /// The number of bytes written.
/// </returns>
public override int Write<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor)
{ {
int padding = image.Width % 8 == 0 ? 0 : 1; int padding = image.Width % 8 == 0 ? 0 : 1;
int bytesPerRow = (image.Width / 8) + padding; int bytesPerRow = (image.Width / 8) + padding;
@ -60,13 +64,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
if (compression == TiffEncoderCompression.CcittGroup3Fax) if (compression == TiffEncoderCompression.CcittGroup3Fax)
{ {
var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration); 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) if (compression == TiffEncoderCompression.ModifiedHuffman)
{ {
var bitWriter = new T4BitWriter(this.MemoryAllocator, this.Configuration, useModifiedHuffman: true); 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. // Write image uncompressed.
@ -93,10 +97,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
} }
} }
this.Output.Write(row); this.Output.Write(outputRow);
bytesWritten += row.Length(); bytesWritten += outputRow.Length;
row.Clear(); outputRow.Clear();
} }
return bytesWritten; return bytesWritten;

25
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);
}
}
}
}

18
src/ImageSharp/Formats/Tiff/Writers/TiffGrayWriter.cs

@ -7,19 +7,21 @@ using System.IO;
using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils;
using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
{ {
/// <summary> /// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>. /// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary> /// </summary>
internal class TiffGrayWriter : TiffWriter internal class TiffGrayWriter : TiffBaseColorWriter
{ {
public TiffGrayWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) public TiffGrayWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
: base(output, memoryMemoryAllocator, configuration) : base(output, memoryAllocator, configuration, entriesCollector)
{ {
} }
@ -28,12 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam> /// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param> /// <param name="image">The image to write to the stream.</param>
/// <param name="quantizer">The quantizer.</param>
/// <param name="compression">The compression to use.</param> /// <param name="compression">The compression to use.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param> /// <param name="compressionLevel">The compression level for deflate compression.</param>
/// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression.</param> /// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used with deflate or lzw compression.</param>
/// <returns>The number of bytes written.</returns> /// <returns>
public int WriteGray<TPixel>(Image<TPixel> image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) /// The number of bytes written.
where TPixel : unmanaged, IPixel<TPixel> /// </returns>
public override int Write<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor)
{ {
using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width); using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();

24
src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter.cs

@ -8,27 +8,29 @@ using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils;
using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
{ {
/// <summary> /// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>. /// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary> /// </summary>
internal class TiffPaletteWriter : TiffWriter internal class TiffPaletteWriter : TiffBaseColorWriter
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TiffPaletteWriter"/> class. /// Initializes a new instance of the <see cref="TiffPaletteWriter" /> class.
/// </summary> /// </summary>
/// <param name="output">The output stream.</param> /// <param name="output">The output stream.</param>
/// <param name="memoryMemoryAllocator">The memory allocator.</param> /// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="configuration">The configuration.</param> /// <param name="configuration">The configuration.</param>
public TiffPaletteWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) /// <param name="entriesCollector">The entries collector.</param>
: base(output, memoryMemoryAllocator, configuration) 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
/// <param name="compression">The compression to use.</param> /// <param name="compression">The compression to use.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param> /// <param name="compressionLevel">The compression level for deflate compression.</param>
/// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression.</param> /// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used in combination with deflate or LZW compression.</param>
/// <param name="entriesCollector">The entries collector.</param> /// <returns>
/// <returns>The number of bytes written.</returns> /// The number of bytes written.
public int WritePalettedRgb<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, TiffEncoderEntriesCollector entriesCollector) /// </returns>
where TPixel : unmanaged, IPixel<TPixel> public override int Write<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor)
{ {
int colorsPerChannel = 256; int colorsPerChannel = 256;
int colorPaletteSize = colorsPerChannel * 3; int colorPaletteSize = colorsPerChannel * 3;
@ -93,7 +95,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
Value = palette Value = palette
}; };
entriesCollector.Add(colorMap); this.EntriesCollector.Add(colorMap);
if (compression == TiffEncoderCompression.Deflate) if (compression == TiffEncoderCompression.Deflate)
{ {

18
src/ImageSharp/Formats/Tiff/Writers/TiffRgbWriter.cs

@ -7,19 +7,21 @@ using System.IO;
using SixLabors.ImageSharp.Compression.Zlib; using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Compression;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils;
using SixLabors.ImageSharp.Formats.Tiff.Compression; using SixLabors.ImageSharp.Formats.Tiff.Compression;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
{ {
/// <summary> /// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>. /// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary> /// </summary>
internal class TiffRgbWriter : TiffWriter internal class TiffRgbWriter : TiffBaseColorWriter
{ {
public TiffRgbWriter(Stream output, MemoryAllocator memoryMemoryAllocator, Configuration configuration) public TiffRgbWriter(TiffStreamWriter output, MemoryAllocator memoryAllocator, Configuration configuration, TiffEncoderEntriesCollector entriesCollector)
: base(output, memoryMemoryAllocator, configuration) : base(output, memoryAllocator, configuration, entriesCollector)
{ {
} }
@ -28,12 +30,14 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// </summary> /// </summary>
/// <typeparam name="TPixel">The pixel data.</typeparam> /// <typeparam name="TPixel">The pixel data.</typeparam>
/// <param name="image">The image to write to the stream.</param> /// <param name="image">The image to write to the stream.</param>
/// <param name="quantizer">The quantizer.</param>
/// <param name="compression">The compression to use.</param> /// <param name="compression">The compression to use.</param>
/// <param name="compressionLevel">The compression level for deflate compression.</param> /// <param name="compressionLevel">The compression level for deflate compression.</param>
/// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used with deflate compression.</param> /// <param name="useHorizontalPredictor">Indicates if horizontal prediction should be used. Should only be used with deflate compression.</param>
/// <returns>The number of bytes written.</returns> /// <returns>
public int WriteRgb<TPixel>(Image<TPixel> image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) /// The number of bytes written.
where TPixel : unmanaged, IPixel<TPixel> /// </returns>
public override int Write<TPixel>(Image<TPixel> image, IQuantizer quantizer, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor)
{ {
using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width * 3); using IManagedByteBuffer row = this.MemoryAllocator.AllocateManagedByteBuffer(image.Width * 3);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();

45
src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs → src/ImageSharp/Formats/Tiff/Writers/TiffStreamWriter.cs

@ -5,27 +5,20 @@ using System;
using System.IO; using System.IO;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers
{ {
/// <summary> /// <summary>
/// Utility class for writing TIFF data to a <see cref="Stream"/>. /// Utility class for writing TIFF data to a <see cref="Stream"/>.
/// </summary> /// </summary>
internal class TiffWriter : IDisposable internal class TiffStreamWriter : IDisposable
{ {
private static readonly byte[] PaddingBytes = new byte[4]; private static readonly byte[] PaddingBytes = new byte[4];
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TiffWriter"/> class. /// Initializes a new instance of the <see cref="TiffStreamWriter"/> class.
/// </summary> /// </summary>
/// <param name="output">The output stream.</param> /// <param name="output">The output stream.</param>
/// <param name="memoryAllocator">The memory allocator.</param> public TiffStreamWriter(Stream output) => this.BaseStream = output;
/// <param name="configuration">The configuration.</param>
public TiffWriter(Stream output, MemoryAllocator memoryAllocator, Configuration configuration)
{
this.Output = output;
this.MemoryAllocator = memoryAllocator;
this.Configuration = configuration;
}
/// <summary> /// <summary>
/// Gets a value indicating whether the architecture is little-endian. /// Gets a value indicating whether the architecture is little-endian.
@ -35,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// <summary> /// <summary>
/// Gets the current position within the stream. /// Gets the current position within the stream.
/// </summary> /// </summary>
public long Position => this.Output.Position; public long Position => this.BaseStream.Position;
protected Stream Output { get; } public Stream BaseStream { get; }
protected MemoryAllocator MemoryAllocator { get; } protected MemoryAllocator MemoryAllocator { get; }
@ -49,7 +42,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// <returns>The offset to be written later</returns> /// <returns>The offset to be written later</returns>
public long PlaceMarker() public long PlaceMarker()
{ {
long offset = this.Output.Position; long offset = this.BaseStream.Position;
this.Write(0u); this.Write(0u);
return offset; return offset;
} }
@ -58,13 +51,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// Writes an array of bytes to the current stream. /// Writes an array of bytes to the current stream.
/// </summary> /// </summary>
/// <param name="value">The bytes to write.</param> /// <param name="value">The bytes to write.</param>
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<byte> value) => this.BaseStream.Write(value);
/// <summary> /// <summary>
/// Writes a byte to the current stream. /// Writes a byte to the current stream.
/// </summary> /// </summary>
/// <param name="value">The byte to write.</param> /// <param name="value">The byte to write.</param>
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);
/// <summary> /// <summary>
/// Writes a two-byte unsigned integer to the current stream. /// 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) public void Write(ushort value)
{ {
byte[] bytes = BitConverter.GetBytes(value); byte[] bytes = BitConverter.GetBytes(value);
this.Output.Write(bytes, 0, 2); this.BaseStream.Write(bytes, 0, 2);
} }
/// <summary> /// <summary>
@ -83,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
public void Write(uint value) public void Write(uint value)
{ {
byte[] bytes = BitConverter.GetBytes(value); byte[] bytes = BitConverter.GetBytes(value);
this.Output.Write(bytes, 0, 4); this.BaseStream.Write(bytes, 0, 4);
} }
/// <summary> /// <summary>
@ -92,11 +87,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils
/// <param name="value">The bytes to write.</param> /// <param name="value">The bytes to write.</param>
public void WritePadded(byte[] value) public void WritePadded(byte[] value)
{ {
this.Output.Write(value, 0, value.Length); this.BaseStream.Write(value, 0, value.Length);
if (value.Length < 4) 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
/// <param name="value">The four-byte unsigned integer to write.</param> /// <param name="value">The four-byte unsigned integer to write.</param>
public void WriteMarker(long offset, uint value) public void WriteMarker(long offset, uint value)
{ {
long currentOffset = this.Output.Position; long currentOffset = this.BaseStream.Position;
this.Output.Seek(offset, SeekOrigin.Begin); this.BaseStream.Seek(offset, SeekOrigin.Begin);
this.Write(value); this.Write(value);
this.Output.Seek(currentOffset, SeekOrigin.Begin); this.BaseStream.Seek(currentOffset, SeekOrigin.Begin);
} }
/// <summary> /// <summary>
/// Disposes <see cref="TiffWriter"/> instance, ensuring any unwritten data is flushed. /// Disposes <see cref="TiffStreamWriter"/> instance, ensuring any unwritten data is flushed.
/// </summary> /// </summary>
public void Dispose() => this.Output.Flush(); public void Dispose() => this.BaseStream.Flush();
} }
} }

5
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;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Xunit; using Xunit;
@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
using var stream = new MemoryStream(); using var stream = new MemoryStream();
var encoder = new TiffEncoderCore(Options, MemoryAllocator); 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); long firstIfdMarker = encoder.WriteHeader(writer);
} }
@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff
using var stream = new MemoryStream(); using var stream = new MemoryStream();
var encoder = new TiffEncoderCore(Options, MemoryAllocator); 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); long firstIfdMarker = encoder.WriteHeader(writer);
Assert.Equal(4, firstIfdMarker); Assert.Equal(4, firstIfdMarker);

17
tests/ImageSharp.Tests/Formats/Tiff/Utils/TiffWriterTests.cs

@ -4,6 +4,7 @@
using System.IO; using System.IO;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils;
using SixLabors.ImageSharp.Formats.Experimental.Tiff.Writers;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Xunit; using Xunit;
@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
public void IsLittleEndian_IsTrueOnWindows() public void IsLittleEndian_IsTrueOnWindows()
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
Assert.True(writer.IsLittleEndian); Assert.True(writer.IsLittleEndian);
} }
@ -31,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult) public void Position_EqualsTheStreamPosition(byte[] data, long expectedResult)
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
writer.Write(data); writer.Write(data);
Assert.Equal(writer.Position, expectedResult); Assert.Equal(writer.Position, expectedResult);
} }
@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
public void Write_WritesByte() public void Write_WritesByte()
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
writer.Write((byte)42); writer.Write((byte)42);
Assert.Equal(new byte[] { 42 }, stream.ToArray()); Assert.Equal(new byte[] { 42 }, stream.ToArray());
@ -50,7 +51,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
public void Write_WritesByteArray() public void Write_WritesByteArray()
{ {
using var stream = new MemoryStream(); 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 }); writer.Write(new byte[] { 2, 4, 6, 8 });
Assert.Equal(new byte[] { 2, 4, 6, 8 }, stream.ToArray()); 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() public void Write_WritesUInt16()
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
writer.Write((ushort)1234); writer.Write((ushort)1234);
Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray()); Assert.Equal(new byte[] { 0xD2, 0x04 }, stream.ToArray());
@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
public void Write_WritesUInt32() public void Write_WritesUInt32()
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
writer.Write(12345678U); writer.Write(12345678U);
Assert.Equal(new byte[] { 0x4E, 0x61, 0xBC, 0x00 }, stream.ToArray()); 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) public void WritePadded_WritesByteArray(byte[] bytes, byte[] expectedResult)
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using var writer = new TiffWriter(stream, MemoryAllocator, Configuration); using var writer = new TiffStreamWriter(stream);
writer.WritePadded(bytes); writer.WritePadded(bytes);
Assert.Equal(expectedResult, stream.ToArray()); Assert.Equal(expectedResult, stream.ToArray());
@ -97,7 +98,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Utils
{ {
using var stream = new MemoryStream(); using var stream = new MemoryStream();
using (var writer = new TiffWriter(stream, MemoryAllocator, Configuration)) using (var writer = new TiffStreamWriter(stream))
{ {
writer.Write(0x11111111); writer.Write(0x11111111);
long marker = writer.PlaceMarker(); long marker = writer.PlaceMarker();

Loading…
Cancel
Save