diff --git a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs b/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs similarity index 97% rename from src/ImageSharp/Formats/Png/PngCompressionLevel.cs rename to src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs index 7516e0987..9421bb19b 100644 --- a/src/ImageSharp/Formats/Png/PngCompressionLevel.cs +++ b/src/ImageSharp/Formats/Png/DeflateCompressionLevel.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Provides enumeration of available PNG compression levels. /// - public enum PngCompressionLevel + public enum DeflateCompressionLevel { /// /// Level 0. Equivalent to . diff --git a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs index 2c05019ed..3a453d916 100644 --- a/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/IPngEncoderOptions.cs @@ -28,9 +28,9 @@ namespace SixLabors.ImageSharp.Formats.Png /// /// Gets the compression level 1-9. - /// Defaults to . + /// Defaults to . /// - PngCompressionLevel CompressionLevel { get; } + DeflateCompressionLevel CompressionLevel { get; } /// /// Gets the threshold of characters in text metadata, when compression should be used. diff --git a/src/ImageSharp/Formats/Png/PngEncoder.cs b/src/ImageSharp/Formats/Png/PngEncoder.cs index e72e8d3d5..b6cac806d 100644 --- a/src/ImageSharp/Formats/Png/PngEncoder.cs +++ b/src/ImageSharp/Formats/Png/PngEncoder.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; set; } /// - public PngCompressionLevel CompressionLevel { get; set; } = PngCompressionLevel.DefaultCompression; + public DeflateCompressionLevel CompressionLevel { get; set; } = DeflateCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; set; } = 1024; diff --git a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs index 3c17c2463..2b8a6b192 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderOptions.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderOptions.cs @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Png public PngFilterMethod? FilterMethod { get; } /// - public PngCompressionLevel CompressionLevel { get; } = PngCompressionLevel.DefaultCompression; + public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; /// public int TextCompressionThreshold { get; } diff --git a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs index 06c6e3dea..5ebbdf1f1 100644 --- a/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs +++ b/src/ImageSharp/Formats/Png/Zlib/ZlibDeflateStream.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Formats.Png.Zlib /// The memory allocator to use for buffer allocations. /// The stream to compress. /// The compression level. - public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, PngCompressionLevel level) + public ZlibDeflateStream(MemoryAllocator memoryAllocator, Stream stream, DeflateCompressionLevel level) { int compressionLevel = (int)level; this.rawStream = stream; diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs index 78cf553d3..c3df04186 100644 --- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs +++ b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Formats.Experimental.Tiff @@ -15,6 +16,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// TiffEncoderCompression Compression { get; } + /// + /// Gets the compression level 1-9 for the deflate compression mode. + /// Defaults to . + /// + DeflateCompressionLevel CompressionLevel { get; } + /// /// Gets the encoding mode to use. RGB, RGB with color palette or gray. /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs index 0d1821704..479ff470b 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs @@ -4,7 +4,9 @@ using System.IO; using System.Threading; using System.Threading.Tasks; + using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Quantization; @@ -15,25 +17,19 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// public class TiffEncoder : IImageEncoder, ITiffEncoderOptions { - /// - /// Gets or sets a value indicating which compression to use. - /// + /// public TiffEncoderCompression Compression { get; set; } = TiffEncoderCompression.None; - /// - /// Gets or sets the encoding mode to use. RGB, RGB with a color palette or gray. - /// + /// + public DeflateCompressionLevel CompressionLevel { get; } = DeflateCompressionLevel.DefaultCompression; + + /// public TiffEncodingMode Mode { get; set; } - /// - /// Gets or sets a value indicating whether to use horizontal prediction. This can improve the compression ratio with deflate or lzw compression. - /// + /// public bool UseHorizontalPredictor { get; set; } - /// - /// Gets or sets the quantizer for color images with a palette. - /// Defaults to OctreeQuantizer. - /// + /// public IQuantizer Quantizer { get; set; } /// diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs index 93deca380..277b833a6 100644 --- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs +++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs @@ -9,6 +9,7 @@ using System.Threading; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Constants; using SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils; +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.Metadata.Profiles.Exif; @@ -51,7 +52,12 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff /// /// Indicating whether to use horizontal prediction. This can improve the compression ratio with deflate compression. /// - private bool useHorizontalPredictor; + private readonly bool useHorizontalPredictor; + + /// + /// Sets the deflate compression level. + /// + private readonly DeflateCompressionLevel compressionLevel; /// /// Initializes a new instance of the class. @@ -65,6 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff this.Mode = options.Mode; this.quantizer = options.Quantizer ?? KnownQuantizers.Octree; this.useHorizontalPredictor = options.UseHorizontalPredictor; + this.compressionLevel = options.CompressionLevel; } /// @@ -165,16 +172,16 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff switch (this.Mode) { case TiffEncodingMode.ColorPalette: - imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.useHorizontalPredictor, out colorMap); + imageDataBytes = writer.WritePalettedRgb(image, this.quantizer, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor, out colorMap); break; case TiffEncodingMode.Gray: - imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.useHorizontalPredictor); + imageDataBytes = writer.WriteGray(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; case TiffEncodingMode.BiColor: - imageDataBytes = writer.WriteBiColor(image, this.CompressionType); + imageDataBytes = writer.WriteBiColor(image, this.CompressionType, this.compressionLevel); break; default: - imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.useHorizontalPredictor); + imageDataBytes = writer.WriteRgb(image, this.padding, this.CompressionType, this.compressionLevel, this.useHorizontalPredictor); break; } diff --git a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs index db8bad133..d91de83e6 100644 --- a/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs +++ b/src/ImageSharp/Formats/Tiff/Utils/TiffWriter.cs @@ -138,16 +138,17 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// 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, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) + public int WriteRgb(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 3, padding); Span rowSpan = row.GetSpan(); if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedRgb(image, rowSpan, useHorizontalPredictor); + return this.WriteDeflateCompressedRgb(image, rowSpan, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -179,16 +180,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// 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, bool useHorizontalPredictor) + private int WriteDeflateCompressedRgb(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; using var memoryStream = new MemoryStream(); - - // TODO: move zlib compression from png to a common place? - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); // TODO: move zlib compression from png to a common place? for (int y = 0; y < image.Height; y++) { @@ -286,10 +286,11 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The quantizer to use. /// The padding bytes for each row. /// 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 color map. /// The number of bytes written. - public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor, out IExifValue colorMap) + public int WritePalettedRgb(Image image, IQuantizer quantizer, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor, out IExifValue colorMap) where TPixel : unmanaged, IPixel { int colorsPerChannel = 256; @@ -341,7 +342,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, useHorizontalPredictor); + return this.WriteDeflateCompressedPalettedRgb(image, quantized, padding, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -379,14 +380,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The quantized frame. /// The padding bytes for each row. + /// 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, int padding, bool useHorizontalPredictor) + public int WriteDeflateCompressedPalettedRgb(Image image, IndexedImageFrame quantized, int padding, 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, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); int bytesWritten = 0; for (int y = 0; y < image.Height; y++) @@ -513,9 +515,10 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// The image to write to the stream. /// The padding bytes for each row. /// 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, int padding, TiffEncoderCompression compression, bool useHorizontalPredictor) + public int WriteGray(Image image, int padding, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { using IManagedByteBuffer row = this.AllocateRow(image.Width, 1, padding); @@ -523,7 +526,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteGrayDeflateCompressed(image, rowSpan, useHorizontalPredictor); + return this.WriteGrayDeflateCompressed(image, rowSpan, compressionLevel, useHorizontalPredictor); } if (compression == TiffEncoderCompression.Lzw) @@ -553,16 +556,15 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// /// 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, bool useHorizontalPredictor) + private int WriteGrayDeflateCompressed(Image image, Span rowSpan, DeflateCompressionLevel compressionLevel, bool useHorizontalPredictor) where TPixel : unmanaged, IPixel { int bytesWritten = 0; using var memoryStream = new MemoryStream(); - - // TODO: move zlib compression from png to a common place? - using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); // TODO: move zlib compression from png to a common place? for (int y = 0; y < image.Height; y++) { @@ -656,8 +658,9 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// 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) + public int WriteBiColor(Image image, TiffEncoderCompression compression, DeflateCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { int padding = image.Width % 8 == 0 ? 0 : 1; @@ -674,7 +677,7 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils if (compression == TiffEncoderCompression.Deflate) { - return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow); + return this.WriteBiColorDeflate(imageBlackWhite, pixelRowAsGraySpan, outputRow, compressionLevel); } if (compression == TiffEncoderCompression.PackBits) @@ -734,12 +737,13 @@ namespace SixLabors.ImageSharp.Formats.Experimental.Tiff.Utils /// 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) + 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, PngCompressionLevel.Level6); // TODO: make compression level configurable + using var deflateStream = new ZlibDeflateStream(this.memoryAllocator, memoryStream, compressionLevel); int bytesWritten = 0; for (int y = 0; y < image.Height; y++) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 11bab17fb..5e062a649 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -62,19 +62,19 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png /// /// All types except Palette /// - public static readonly TheoryData CompressionLevels - = new TheoryData + public static readonly TheoryData CompressionLevels + = new TheoryData { - PngCompressionLevel.Level0, - PngCompressionLevel.Level1, - PngCompressionLevel.Level2, - PngCompressionLevel.Level3, - PngCompressionLevel.Level4, - PngCompressionLevel.Level5, - PngCompressionLevel.Level6, - PngCompressionLevel.Level7, - PngCompressionLevel.Level8, - PngCompressionLevel.Level9, + DeflateCompressionLevel.Level0, + DeflateCompressionLevel.Level1, + DeflateCompressionLevel.Level2, + DeflateCompressionLevel.Level3, + DeflateCompressionLevel.Level4, + DeflateCompressionLevel.Level5, + DeflateCompressionLevel.Level6, + DeflateCompressionLevel.Level7, + DeflateCompressionLevel.Level8, + DeflateCompressionLevel.Level9, }; public static readonly TheoryData PaletteSizes = new TheoryData @@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png [Theory] [WithTestPatternImages(nameof(CompressionLevels), 24, 24, PixelTypes.Rgba32)] - public void WorksWithAllCompressionLevels(TestImageProvider provider, PngCompressionLevel compressionLevel) + public void WorksWithAllCompressionLevels(TestImageProvider provider, DeflateCompressionLevel compressionLevel) where TPixel : unmanaged, IPixel { foreach (PngInterlaceMode interlaceMode in InterlaceMode) @@ -573,7 +573,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png PngFilterMethod pngFilterMethod, PngBitDepth bitDepth, PngInterlaceMode interlaceMode, - PngCompressionLevel compressionLevel = PngCompressionLevel.DefaultCompression, + DeflateCompressionLevel compressionLevel = DeflateCompressionLevel.DefaultCompression, int paletteSize = 255, bool appendPngColorType = false, bool appendPngFilterMethod = false, diff --git a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs index 692f92c9f..441328dfe 100644 --- a/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs +++ b/tests/ImageSharp.Tests/Formats/Tiff/Compression/DeflateTiffCompressionTests.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Tiff.Compression Stream compressedStream = new MemoryStream(); using (Stream uncompressedStream = new MemoryStream(data), - deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.PngCompressionLevel.Level6)) + deflateStream = new ZlibDeflateStream(Configuration.Default.MemoryAllocator, compressedStream, ImageSharp.Formats.Png.DeflateCompressionLevel.Level6)) { uncompressedStream.CopyTo(deflateStream); } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs index 4e6b002d0..79bfdc054 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.SaveAsync.cs @@ -138,7 +138,7 @@ namespace SixLabors.ImageSharp.Tests public async Task SaveAsync_WithNonSeekableStream_IsCancellable() { using var image = new Image(4000, 4000); - var encoder = new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression }; + var encoder = new PngEncoder() { CompressionLevel = DeflateCompressionLevel.BestCompression }; using var stream = new MemoryStream(); var asyncStream = new AsyncStreamWrapper(stream, () => false); var cts = new CancellationTokenSource();