diff --git a/src/ImageSharp/Advanced/AotCompilerTools.cs b/src/ImageSharp/Advanced/AotCompilerTools.cs
index 9c285e21de..0cf28c6bb2 100644
--- a/src/ImageSharp/Advanced/AotCompilerTools.cs
+++ b/src/ImageSharp/Advanced/AotCompilerTools.cs
@@ -57,6 +57,9 @@ internal static class AotCompilerTools
/// If you are getting the above error, you need to call this method, which will pre-seed the AoT compiler with the
/// necessary methods to complete the SaveAsGif call. That's it, otherwise you should NEVER need this method!!!
///
+ ///
+ /// This method is used for AOT code generation only. Do not call it at runtime.
+ ///
[Preserve]
private static void SeedPixelFormats()
{
@@ -487,8 +490,10 @@ internal static class AotCompilerTools
private static void AotCompilePixelSamplingStrategys()
where TPixel : unmanaged, IPixel
{
- default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default);
- default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default);
+ default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default(Image));
+ default(DefaultPixelSamplingStrategy).EnumeratePixelRegions(default(ImageFrame));
+ default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default(Image));
+ default(ExtensivePixelSamplingStrategy).EnumeratePixelRegions(default(ImageFrame));
}
///
@@ -513,13 +518,13 @@ internal static class AotCompilerTools
where TPixel : unmanaged, IPixel
where TDither : struct, IDither
{
- var octree = default(OctreeQuantizer);
+ OctreeQuantizer octree = default;
default(TDither).ApplyQuantizationDither, TPixel>(ref octree, default, default, default);
- var palette = default(PaletteQuantizer);
+ PaletteQuantizer palette = default;
default(TDither).ApplyQuantizationDither, TPixel>(ref palette, default, default, default);
- var wu = default(WuQuantizer);
+ WuQuantizer wu = default;
default(TDither).ApplyQuantizationDither, TPixel>(ref wu, default, default, default);
default(TDither).ApplyPaletteDither.DitherProcessor, TPixel>(default, default, default);
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index 98d8b6233f..a4e1f8eef9 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -106,7 +106,7 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals
this.memoryAllocator = memoryAllocator;
this.bitsPerPixel = encoder.BitsPerPixel;
this.quantizer = encoder.Quantizer;
- this.pixelSamplingStrategy = encoder.GlobalPixelSamplingStrategy;
+ this.pixelSamplingStrategy = encoder.PixelSamplingStrategy;
this.infoHeaderType = encoder.SupportTransparency ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion3;
}
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index c0eb160eb3..14d20cf909 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -70,7 +70,7 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
this.skipMetadata = encoder.SkipMetadata;
this.quantizer = encoder.Quantizer;
this.colorTableMode = encoder.ColorTableMode;
- this.pixelSamplingStrategy = encoder.GlobalPixelSamplingStrategy;
+ this.pixelSamplingStrategy = encoder.PixelSamplingStrategy;
}
///
@@ -103,7 +103,8 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
}
else
{
- quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image.Frames.RootFrame, image.Bounds());
+ frameQuantizer.BuildPalette(this.pixelSamplingStrategy, image.Frames.RootFrame);
+ quantized = frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
}
}
diff --git a/src/ImageSharp/Formats/IEncoderOptions.cs b/src/ImageSharp/Formats/IEncoderOptions.cs
index 5e627f02b3..1f8337f0a2 100644
--- a/src/ImageSharp/Formats/IEncoderOptions.cs
+++ b/src/ImageSharp/Formats/IEncoderOptions.cs
@@ -27,7 +27,7 @@ public interface IQuantizingEncoderOptions : IEncoderOptions
IQuantizer Quantizer { get; init; }
///
- /// Gets the used for quantization when building a global color palette.
+ /// Gets the used for quantization when building color palettes.
///
- IPixelSamplingStrategy GlobalPixelSamplingStrategy { get; init; }
+ IPixelSamplingStrategy PixelSamplingStrategy { get; init; }
}
diff --git a/src/ImageSharp/Formats/ImageEncoder.cs b/src/ImageSharp/Formats/ImageEncoder.cs
index ffd87a31bf..074e803958 100644
--- a/src/ImageSharp/Formats/ImageEncoder.cs
+++ b/src/ImageSharp/Formats/ImageEncoder.cs
@@ -33,5 +33,5 @@ public abstract class QuantizingImageEncoder : ImageEncoder, IQuantizingEncoderO
public IQuantizer Quantizer { get; init; } = KnownQuantizers.Octree;
///
- public IPixelSamplingStrategy GlobalPixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
+ public IPixelSamplingStrategy PixelSamplingStrategy { get; init; } = new DefaultPixelSamplingStrategy();
}
diff --git a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
index 8595498ce3..7c7860c58e 100644
--- a/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
+++ b/src/ImageSharp/Formats/Pbm/PbmEncoderCore.cs
@@ -65,7 +65,7 @@ internal sealed class PbmEncoderCore : IImageEncoderInternals
Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream));
- this.DeduceOptions(image);
+ this.SanitizeAndSetEncoderOptions(image);
byte signature = this.DeduceSignature();
this.WriteHeader(stream, signature, image.Size());
@@ -75,7 +75,7 @@ internal sealed class PbmEncoderCore : IImageEncoderInternals
stream.Flush();
}
- private void DeduceOptions(Image image)
+ private void SanitizeAndSetEncoderOptions(Image image)
where TPixel : unmanaged, IPixel
{
this.configuration = image.GetConfiguration();
diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 4267580dec..70dd73b938 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -149,7 +149,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
ImageMetadata metadata = image.Metadata;
PngMetadata pngMetadata = metadata.GetFormatMetadata(PngFormat.Instance);
- this.DeduceOptions(this.encoder, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
+ this.SanitizeAndSetEncoderOptions(this.encoder, pngMetadata, out this.use16Bit, out this.bytesPerPixel);
Image clonedImage = null;
bool clearTransparency = this.encoder.TransparentColorMode == PngTransparentColorMode.Clear;
if (clearTransparency)
@@ -1228,7 +1228,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
/// The PNG metadata.
/// if set to true [use16 bit].
/// The bytes per pixel.
- private void DeduceOptions(
+ private void SanitizeAndSetEncoderOptions(
PngEncoder encoder,
PngMetadata pngMetadata,
out bool use16Bit,
@@ -1298,7 +1298,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable
// Create quantized frame returning the palette and set the bit depth.
using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(image.GetConfiguration());
- frameQuantizer.BuildPalette(encoder.GlobalPixelSamplingStrategy, image);
+ frameQuantizer.BuildPalette(encoder.PixelSamplingStrategy, image);
return frameQuantizer.QuantizeFrame(image.Frames.RootFrame, image.Bounds());
}
diff --git a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs b/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
deleted file mode 100644
index 1e74e630ce..0000000000
--- a/src/ImageSharp/Formats/Tiff/ITiffEncoderOptions.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Six Labors Split License.
-
-using SixLabors.ImageSharp.Compression.Zlib;
-using SixLabors.ImageSharp.Formats.Tiff.Constants;
-using SixLabors.ImageSharp.Processing.Processors.Quantization;
-
-namespace SixLabors.ImageSharp.Formats.Tiff;
-
-///
-/// Encapsulates the options for the .
-///
-internal interface ITiffEncoderOptions
-{
- ///
- /// Gets the number of bits per pixel.
- ///
- TiffBitsPerPixel? BitsPerPixel { get; }
-
- ///
- /// Gets the compression type to use.
- ///
- TiffCompression? Compression { get; }
-
- ///
- /// Gets the compression level 1-9 for the deflate compression mode.
- /// Defaults to .
- ///
- DeflateCompressionLevel? CompressionLevel { get; }
-
- ///
- /// Gets the PhotometricInterpretation to use. Possible options are RGB, RGB with a color palette, gray or BiColor.
- /// If no PhotometricInterpretation is specified or it is unsupported by the encoder, RGB will be used.
- ///
- TiffPhotometricInterpretation? PhotometricInterpretation { get; }
-
- ///
- /// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression.
- ///
- TiffPredictor? HorizontalPredictor { get; }
-
- ///
- /// Gets the quantizer for creating a color palette image.
- ///
- IQuantizer Quantizer { get; }
-}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
index 3b5d347722..e7bb08cdc3 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoder.cs
@@ -4,47 +4,52 @@
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Compression.Zlib;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Tiff;
///
/// Encoder for writing the data image to a stream in TIFF format.
///
-public class TiffEncoder : IImageEncoder, ITiffEncoderOptions
+public class TiffEncoder : QuantizingImageEncoder
{
- ///
- public TiffBitsPerPixel? BitsPerPixel { get; set; }
-
- ///
- public TiffCompression? Compression { get; set; }
-
- ///
- public DeflateCompressionLevel? CompressionLevel { get; set; }
-
- ///
- public TiffPhotometricInterpretation? PhotometricInterpretation { get; set; }
-
- ///
- public TiffPredictor? HorizontalPredictor { get; set; }
-
- ///
- public IQuantizer Quantizer { get; set; }
-
- ///
- public void Encode(Image image, Stream stream)
- where TPixel : unmanaged, IPixel
+ ///
+ /// Gets the number of bits per pixel.
+ ///
+ public TiffBitsPerPixel? BitsPerPixel { get; init; }
+
+ ///
+ /// Gets the compression type to use.
+ ///
+ public TiffCompression? Compression { get; init; }
+
+ ///
+ /// Gets the compression level 1-9 for the deflate compression mode.
+ /// Defaults to .
+ ///
+ public DeflateCompressionLevel? CompressionLevel { get; init; }
+
+ ///
+ /// Gets the PhotometricInterpretation to use. Possible options are RGB, RGB with a color palette, gray or BiColor.
+ /// If no PhotometricInterpretation is specified or it is unsupported by the encoder, RGB will be used.
+ ///
+ public TiffPhotometricInterpretation? PhotometricInterpretation { get; init; }
+
+ ///
+ /// Gets a value indicating which horizontal prediction to use. This can improve the compression ratio with deflate or lzw compression.
+ ///
+ public TiffPredictor? HorizontalPredictor { get; init; }
+
+ ///
+ public override void Encode(Image image, Stream stream)
{
- var encode = new TiffEncoderCore(this, image.GetMemoryAllocator());
+ TiffEncoderCore encode = new(this, image.GetMemoryAllocator());
encode.Encode(image, stream);
}
///
- public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken)
- where TPixel : unmanaged, IPixel
+ public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken)
{
- var encoder = new TiffEncoderCore(this, image.GetMemoryAllocator());
+ TiffEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}
diff --git a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
index 9b50b958c8..849eb69aeb 100644
--- a/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
+++ b/src/ImageSharp/Formats/Tiff/TiffEncoderCore.cs
@@ -10,7 +10,6 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.Metadata;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats.Tiff;
@@ -40,10 +39,15 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
private Configuration configuration;
///
- /// The quantizer for creating color palette image.
+ /// The quantizer for creating color palette images.
///
private readonly IQuantizer quantizer;
+ ///
+ /// The pixel sampling strategy for quantization.
+ ///
+ private readonly IPixelSamplingStrategy pixelSamplingStrategy;
+
///
/// Sets the deflate compression level.
///
@@ -76,11 +80,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
///
/// The options for the encoder.
/// The memory allocator.
- public TiffEncoderCore(ITiffEncoderOptions options, MemoryAllocator memoryAllocator)
+ public TiffEncoderCore(TiffEncoder options, MemoryAllocator memoryAllocator)
{
this.memoryAllocator = memoryAllocator;
this.PhotometricInterpretation = options.PhotometricInterpretation;
- this.quantizer = options.Quantizer ?? KnownQuantizers.Octree;
+ this.quantizer = options.Quantizer;
+ this.pixelSamplingStrategy = options.PixelSamplingStrategy;
this.BitsPerPixel = options.BitsPerPixel;
this.HorizontalPredictor = options.HorizontalPredictor;
this.CompressionType = options.Compression;
@@ -215,6 +220,7 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
this.PhotometricInterpretation,
frame,
this.quantizer,
+ this.pixelSamplingStrategy,
this.memoryAllocator,
this.configuration,
entriesCollector,
@@ -331,7 +337,12 @@ internal sealed class TiffEncoderCore : IImageEncoderInternals
return nextIfdMarker;
}
- private void SanitizeAndSetEncoderOptions(TiffBitsPerPixel? bitsPerPixel, int inputBitsPerPixel, TiffPhotometricInterpretation? photometricInterpretation, TiffCompression compression, TiffPredictor predictor)
+ private void SanitizeAndSetEncoderOptions(
+ TiffBitsPerPixel? bitsPerPixel,
+ int inputBitsPerPixel,
+ TiffPhotometricInterpretation? photometricInterpretation,
+ TiffCompression compression,
+ TiffPredictor predictor)
{
// BitsPerPixel should be the primary source of truth for the encoder options.
if (bitsPerPixel.HasValue)
diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
index bb13137cef..a52d49a353 100644
--- a/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
+++ b/src/ImageSharp/Formats/Tiff/Writers/TiffColorWriterFactory.cs
@@ -14,6 +14,7 @@ internal static class TiffColorWriterFactory
TiffPhotometricInterpretation? photometricInterpretation,
ImageFrame image,
IQuantizer quantizer,
+ IPixelSamplingStrategy pixelSamplingStrategy,
MemoryAllocator memoryAllocator,
Configuration configuration,
TiffEncoderEntriesCollector entriesCollector,
@@ -23,7 +24,7 @@ internal static class TiffColorWriterFactory
switch (photometricInterpretation)
{
case TiffPhotometricInterpretation.PaletteColor:
- return new TiffPaletteWriter(image, quantizer, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
+ return new TiffPaletteWriter(image, quantizer, pixelSamplingStrategy, memoryAllocator, configuration, entriesCollector, bitsPerPixel);
case TiffPhotometricInterpretation.BlackIsZero:
case TiffPhotometricInterpretation.WhiteIsZero:
if (bitsPerPixel == 1)
diff --git a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
index f8810d65ac..87118d6f76 100644
--- a/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
+++ b/src/ImageSharp/Formats/Tiff/Writers/TiffPaletteWriter{TPixel}.cs
@@ -17,19 +17,21 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
private readonly int maxColors;
private readonly int colorPaletteSize;
private readonly int colorPaletteBytes;
- private readonly IndexedImageFrame quantizedImage;
+ private readonly IndexedImageFrame quantizedFrame;
private IMemoryOwner indexedPixelsBuffer;
public TiffPaletteWriter(
- ImageFrame image,
+ ImageFrame frame,
IQuantizer quantizer,
+ IPixelSamplingStrategy pixelSamplingStrategy,
MemoryAllocator memoryAllocator,
Configuration configuration,
TiffEncoderEntriesCollector entriesCollector,
int bitsPerPixel)
- : base(image, memoryAllocator, configuration, entriesCollector)
+ : base(frame, memoryAllocator, configuration, entriesCollector)
{
DebugGuard.NotNull(quantizer, nameof(quantizer));
+ DebugGuard.NotNull(quantizer, nameof(pixelSamplingStrategy));
DebugGuard.NotNull(configuration, nameof(configuration));
DebugGuard.NotNull(entriesCollector, nameof(entriesCollector));
DebugGuard.MustBeBetweenOrEqualTo(bitsPerPixel, 4, 8, nameof(bitsPerPixel));
@@ -38,11 +40,15 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
this.maxColors = this.BitsPerPixel == 4 ? 16 : 256;
this.colorPaletteSize = this.maxColors * 3;
this.colorPaletteBytes = this.colorPaletteSize * 2;
- using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(this.Configuration, new QuantizerOptions()
- {
- MaxColors = this.maxColors
- });
- this.quantizedImage = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds());
+ using IQuantizer frameQuantizer = quantizer.CreatePixelSpecificQuantizer(
+ this.Configuration,
+ new QuantizerOptions()
+ {
+ MaxColors = this.maxColors
+ });
+
+ frameQuantizer.BuildPalette(pixelSamplingStrategy, frame);
+ this.quantizedFrame = frameQuantizer.QuantizeFrame(frame, frame.Bounds());
this.AddColorMapTag();
}
@@ -66,7 +72,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
int lastRow = y + height;
for (int row = y; row < lastRow; row++)
{
- ReadOnlySpan indexedPixelRow = this.quantizedImage.DangerousGetRowSpan(row);
+ ReadOnlySpan indexedPixelRow = this.quantizedFrame.DangerousGetRowSpan(row);
int idxPixels = 0;
for (int x = 0; x < halfWidth; x++)
{
@@ -93,7 +99,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
int indexedPixelsRowIdx = 0;
for (int row = y; row < lastRow; row++)
{
- ReadOnlySpan indexedPixelRow = this.quantizedImage.DangerousGetRowSpan(row);
+ ReadOnlySpan indexedPixelRow = this.quantizedFrame.DangerousGetRowSpan(row);
indexedPixelRow.CopyTo(indexedPixels.Slice(indexedPixelsRowIdx * width, width));
indexedPixelsRowIdx++;
}
@@ -105,7 +111,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
///
protected override void Dispose(bool disposing)
{
- this.quantizedImage?.Dispose();
+ this.quantizedFrame?.Dispose();
this.indexedPixelsBuffer?.Dispose();
}
@@ -114,7 +120,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
using IMemoryOwner colorPaletteBuffer = this.MemoryAllocator.Allocate(this.colorPaletteBytes);
Span colorPalette = colorPaletteBuffer.GetSpan();
- ReadOnlySpan quantizedColors = this.quantizedImage.Palette.Span;
+ ReadOnlySpan quantizedColors = this.quantizedFrame.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.
@@ -126,7 +132,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
// 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[this.colorPaletteSize];
+ ushort[] palette = new ushort[this.colorPaletteSize];
int paletteIdx = 0;
for (int i = 0; i < quantizedColors.Length; i++)
{
@@ -147,7 +153,7 @@ internal sealed class TiffPaletteWriter : TiffBaseColorWriter
palette[paletteIdx++] = quantizedColorRgb48[i].B;
}
- var colorMap = new ExifShortArray(ExifTagValue.ColorMap)
+ ExifShortArray colorMap = new(ExifTagValue.ColorMap)
{
Value = palette
};
diff --git a/src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs b/src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs
index de36d1592a..5c387949e5 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/DefaultPixelSamplingStrategy.cs
@@ -101,4 +101,54 @@ public class DefaultPixelSamplingStrategy : IPixelSamplingStrategy
}
}
}
+
+ ///
+ public IEnumerable> EnumeratePixelRegions(ImageFrame frame)
+ where TPixel : unmanaged, IPixel
+ {
+ long maximumPixels = Math.Min(this.MaximumPixels, (long)frame.Width * frame.Height);
+ long maxNumberOfRows = maximumPixels / frame.Width;
+ long totalNumberOfRows = frame.Height;
+
+ if (totalNumberOfRows <= maxNumberOfRows)
+ {
+ yield return frame.PixelBuffer.GetRegion();
+ }
+ else
+ {
+ double r = maxNumberOfRows / (double)totalNumberOfRows;
+
+ // Use a rough approximation to make sure we don't leave out large contiguous regions:
+ if (maxNumberOfRows > 200)
+ {
+ r = Math.Round(r, 2);
+ }
+ else
+ {
+ r = Math.Round(r, 1);
+ }
+
+ r = Math.Max(this.MinimumScanRatio, r); // always visit the minimum defined portion of the image.
+
+ Rational ratio = new(r);
+
+ int denom = (int)ratio.Denominator;
+ int num = (int)ratio.Numerator;
+
+ for (int pos = 0; pos < totalNumberOfRows; pos++)
+ {
+ int subPos = pos % denom;
+ if (subPos < num)
+ {
+ yield return GetRow(pos);
+ }
+ }
+
+ Buffer2DRegion GetRow(int pos)
+ {
+ int y = pos % frame.Height;
+ return frame.PixelBuffer.GetRegion(0, y, frame.Width, 1);
+ }
+ }
+ }
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/ExtensivePixelSamplingStrategy.cs b/src/ImageSharp/Processing/Processors/Quantization/ExtensivePixelSamplingStrategy.cs
index 580227c2d7..150f785b38 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/ExtensivePixelSamplingStrategy.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/ExtensivePixelSamplingStrategy.cs
@@ -20,4 +20,11 @@ public class ExtensivePixelSamplingStrategy : IPixelSamplingStrategy
yield return frame.PixelBuffer.GetRegion();
}
}
+
+ ///
+ public IEnumerable> EnumeratePixelRegions(ImageFrame frame)
+ where TPixel : unmanaged, IPixel
+ {
+ yield return frame.PixelBuffer.GetRegion();
+ }
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/IPixelSamplingStrategy.cs b/src/ImageSharp/Processing/Processors/Quantization/IPixelSamplingStrategy.cs
index ab118b55d4..55d56679ed 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/IPixelSamplingStrategy.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/IPixelSamplingStrategy.cs
@@ -7,16 +7,25 @@ using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
///
-/// Provides an abstraction to enumerate pixel regions within a multi-framed .
+/// Provides an abstraction to enumerate pixel regions for sampling within .
///
public interface IPixelSamplingStrategy
{
///
- /// Enumerates pixel regions within the image as .
+ /// Enumerates pixel regions for all frames within the image as .
///
/// The image.
/// The pixel type.
/// An enumeration of pixel regions.
IEnumerable> EnumeratePixelRegions(Image image)
where TPixel : unmanaged, IPixel;
+
+ ///
+ /// Enumerates pixel regions within a single image frame as .
+ ///
+ /// The image frame.
+ /// The pixel type.
+ /// An enumeration of pixel regions.
+ IEnumerable> EnumeratePixelRegions(ImageFrame frame)
+ where TPixel : unmanaged, IPixel;
}
diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
index 63094287c7..167cf91282 100644
--- a/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
+++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
@@ -99,13 +99,39 @@ public static class QuantizerUtilities
return destination;
}
- internal static void BuildPalette(
+ ///
+ /// Adds colors to the quantized palette from the given pixel regions.
+ ///
+ /// The pixel format.
+ /// The pixel specific quantizer.
+ /// The pixel sampling strategy.
+ /// The source image to sample from.
+ public static void BuildPalette(
+ this IQuantizer quantizer,
+ IPixelSamplingStrategy pixelSamplingStrategy,
+ Image source)
+ where TPixel : unmanaged, IPixel
+ {
+ foreach (Buffer2DRegion region in pixelSamplingStrategy.EnumeratePixelRegions(source))
+ {
+ quantizer.AddPaletteColors(region);
+ }
+ }
+
+ ///
+ /// Adds colors to the quantized palette from the given pixel regions.
+ ///
+ /// The pixel format.
+ /// The pixel specific quantizer.
+ /// The pixel sampling strategy.
+ /// The source image frame to sample from.
+ public static void BuildPalette(
this IQuantizer quantizer,
IPixelSamplingStrategy pixelSamplingStrategy,
- Image image)
+ ImageFrame source)
where TPixel : unmanaged, IPixel
{
- foreach (Buffer2DRegion region in pixelSamplingStrategy.EnumeratePixelRegions(image))
+ foreach (Buffer2DRegion region in pixelSamplingStrategy.EnumeratePixelRegions(source))
{
quantizer.AddPaletteColors(region);
}
diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
index 83b8a88695..18eb7708ca 100644
--- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs
@@ -144,7 +144,7 @@ public class GifEncoderTests
GifEncoder encoder = new()
{
ColorTableMode = GifColorTableMode.Global,
- GlobalPixelSamplingStrategy = new DefaultPixelSamplingStrategy(maxPixels, scanRatio)
+ PixelSamplingStrategy = new DefaultPixelSamplingStrategy(maxPixels, scanRatio)
};
string testOutputFile = provider.Utility.SaveTestOutputFile(
diff --git a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
index 95ed17f1c7..7907597854 100644
--- a/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Tiff/TiffEncoderHeaderTests.cs
@@ -3,24 +3,21 @@
using SixLabors.ImageSharp.Formats.Tiff;
using SixLabors.ImageSharp.Formats.Tiff.Writers;
-using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Formats.Tiff;
[Trait("Format", "Tiff")]
public class TiffEncoderHeaderTests
{
- private static readonly MemoryAllocator MemoryAllocator = MemoryAllocator.Create();
- private static readonly Configuration Configuration = Configuration.Default;
- private static readonly ITiffEncoderOptions Options = new TiffEncoder();
+ private static readonly TiffEncoder Encoder = new();
[Fact]
public void WriteHeader_WritesValidHeader()
{
- using var stream = new MemoryStream();
- var encoder = new TiffEncoderCore(Options, MemoryAllocator);
+ using MemoryStream stream = new();
+ TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator);
- using (var writer = new TiffStreamWriter(stream))
+ using (TiffStreamWriter writer = new(stream))
{
long firstIfdMarker = TiffEncoderCore.WriteHeader(writer);
}
@@ -31,13 +28,11 @@ public class TiffEncoderHeaderTests
[Fact]
public void WriteHeader_ReturnsFirstIfdMarker()
{
- using var stream = new MemoryStream();
- var encoder = new TiffEncoderCore(Options, MemoryAllocator);
+ using MemoryStream stream = new();
+ TiffEncoderCore encoder = new(Encoder, Configuration.Default.MemoryAllocator);
- using (var writer = new TiffStreamWriter(stream))
- {
- long firstIfdMarker = TiffEncoderCore.WriteHeader(writer);
- Assert.Equal(4, firstIfdMarker);
- }
+ using TiffStreamWriter writer = new(stream);
+ long firstIfdMarker = TiffEncoderCore.WriteHeader(writer);
+ Assert.Equal(4, firstIfdMarker);
}
}