Browse Source

Simplify API and clone options.

pull/2894/head
James Jackson-South 11 months ago
parent
commit
16b4eb6c81
  1. 4
      src/ImageSharp/Formats/AlphaAwareImageEncoder.cs
  2. 39
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 13
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  4. 2
      src/ImageSharp/Formats/TransparentColorMode.cs
  5. 29
      src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs
  6. 6
      src/ImageSharp/Processing/Processors/Quantization/IQuantizingPixelRowDelegate{TPixel}.cs
  7. 20
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs
  8. 14
      src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs
  9. 3
      src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs
  10. 90
      src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs
  11. 20
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

4
src/ImageSharp/Formats/AlphaAwareImageEncoder.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Processing.Processors.Quantization;
namespace SixLabors.ImageSharp.Formats;
/// <summary>
@ -10,6 +12,8 @@ public abstract class AlphaAwareImageEncoder : ImageEncoder
{
/// <summary>
/// Gets or initializes the mode that determines how transparent pixels are handled during encoding.
/// This overrides any other settings that may affect the encoding of transparent pixels
/// including those passed via <see cref="QuantizerOptions"/>.
/// </summary>
public TransparentColorMode TransparentColorMode { get; init; }
}

39
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -150,10 +150,7 @@ internal sealed class GifEncoderCore
TransparentColorMode mode = this.transparentColorMode;
QuantizerOptions options = this.quantizer.Options.DeepClone(o => o.TransparentColorMode = mode);
// Quantize the first frame. Checking to see whether we can clear the transparent pixels
// to allow for a smaller color palette and encoded result.
// TODO: What should we use as the background color here?
Color background = Color.Transparent;
// Quantize the first frame.
using (IQuantizer<TPixel> frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, options))
{
IPixelSamplingStrategy strategy = this.pixelSamplingStrategy;
@ -163,12 +160,12 @@ internal sealed class GifEncoderCore
{
if (useGlobalTable)
{
frameQuantizer.BuildPalette(mode, strategy, image);
frameQuantizer.BuildPalette(strategy, image);
quantized = frameQuantizer.QuantizeFrame(encodingFrame, image.Bounds);
}
else
{
frameQuantizer.BuildPalette(mode, strategy, encodingFrame);
frameQuantizer.BuildPalette(strategy, encodingFrame);
quantized = frameQuantizer.QuantizeFrame(encodingFrame, encodingFrame.Bounds);
}
}
@ -176,13 +173,14 @@ internal sealed class GifEncoderCore
{
quantized = this.QuantizeAdditionalFrameAndUpdateMetadata(
encodingFrame,
options,
encodingFrame.Bounds,
frameMetadata,
true,
default,
false,
frameMetadata.HasTransparency ? frameMetadata.TransparencyIndex : -1,
background);
Color.Transparent);
}
}
@ -197,6 +195,7 @@ internal sealed class GifEncoderCore
frameMetadata.TransparencyIndex = ClampIndex(derivedTransparencyIndex);
}
// TODO: We should be checking the metadata here also I think?
if (!TryGetBackgroundIndex(quantized, this.backgroundColor, out byte backgroundIndex))
{
backgroundIndex = derivedTransparencyIndex >= 0
@ -235,6 +234,7 @@ internal sealed class GifEncoderCore
this.EncodeAdditionalFrames(
stream,
image,
options,
globalPalette,
derivedTransparencyIndex,
frameMetadata.DisposalMode,
@ -264,6 +264,7 @@ internal sealed class GifEncoderCore
private void EncodeAdditionalFrames<TPixel>(
Stream stream,
Image<TPixel> image,
QuantizerOptions options,
ReadOnlyMemory<TPixel> globalPalette,
int globalTransparencyIndex,
FrameDisposalMode previousDisposalMode,
@ -301,7 +302,7 @@ internal sealed class GifEncoderCore
// The palette quantizer can reuse the same global pixel map across multiple frames since the palette is unchanging.
// This allows a reduction of memory usage across multi-frame gifs using a global palette
// and also allows use to reuse the cache from previous runs.
globalPaletteQuantizer = new(this.configuration, this.quantizer!.Options, globalPalette);
globalPaletteQuantizer = new(this.configuration, options, globalPalette);
hasGlobalPaletteQuantizer = true;
}
@ -311,6 +312,7 @@ internal sealed class GifEncoderCore
currentFrame,
nextFrame,
encodingFrame,
options,
useLocal,
gifMetadata,
globalPaletteQuantizer,
@ -361,6 +363,7 @@ internal sealed class GifEncoderCore
ImageFrame<TPixel> currentFrame,
ImageFrame<TPixel>? nextFrame,
ImageFrame<TPixel> encodingFrame,
QuantizerOptions options,
bool useLocal,
GifFrameMetadata metadata,
PaletteQuantizer<TPixel> globalPaletteQuantizer,
@ -392,6 +395,7 @@ internal sealed class GifEncoderCore
using IndexedImageFrame<TPixel> quantized = this.QuantizeAdditionalFrameAndUpdateMetadata(
encodingFrame,
options,
bounds,
metadata,
useLocal,
@ -416,6 +420,7 @@ internal sealed class GifEncoderCore
private IndexedImageFrame<TPixel> QuantizeAdditionalFrameAndUpdateMetadata<TPixel>(
ImageFrame<TPixel> encodingFrame,
QuantizerOptions options,
Rectangle bounds,
GifFrameMetadata metadata,
bool useLocal,
@ -446,7 +451,12 @@ internal sealed class GifEncoderCore
transparencyIndex = palette.Length;
metadata.TransparencyIndex = ClampIndex(transparencyIndex);
PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex, transparentColor);
QuantizerOptions paletteOptions = options.DeepClone(o =>
{
o.MaxColors = palette.Length;
o.Dither = null;
});
PaletteQuantizer quantizer = new(palette, paletteOptions, transparencyIndex, transparentColor);
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
}
@ -454,7 +464,7 @@ internal sealed class GifEncoderCore
{
// We must quantize the frame to generate a local color table.
IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : FallbackQuantizer;
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, options);
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
// The transparency index derived by the quantizer will differ from the index
@ -466,7 +476,12 @@ internal sealed class GifEncoderCore
else
{
// Just use the local palette.
PaletteQuantizer quantizer = new(palette, new() { Dither = null }, transparencyIndex, transparentColor);
QuantizerOptions paletteOptions = options.DeepClone(o =>
{
o.MaxColors = palette.Length;
o.Dither = null;
});
PaletteQuantizer quantizer = new(palette, paletteOptions, transparencyIndex, transparentColor);
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
}
@ -475,7 +490,7 @@ internal sealed class GifEncoderCore
{
// We must quantize the frame to generate a local color table.
IQuantizer quantizer = this.hasQuantizer ? this.quantizer! : FallbackQuantizer;
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, quantizer.Options);
using IQuantizer<TPixel> frameQuantizer = quantizer.CreatePixelSpecificQuantizer<TPixel>(this.configuration, options);
quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(encodingFrame, bounds);
// The transparency index derived by the quantizer might differ from the index

13
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -1574,13 +1574,21 @@ internal sealed class PngEncoderCore : IDisposable
{
// We can use the color data from the decoded metadata here.
// We avoid dithering by default to preserve the original colors.
this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value, new() { Dither = null });
QuantizerOptions options = new() { Dither = null, TransparentColorMode = encoder.TransparentColorMode };
this.quantizer = new PaletteQuantizer(metadata.ColorTable.Value, options);
}
else
{
// Don't use the default transparency threshold for quantization as PNG can handle multiple transparent colors.
// We choose a value that is close to zero so that edge cases causes by lower bit depths for the alpha channel are handled correctly.
this.quantizer = new WuQuantizer(new QuantizerOptions { TransparencyThreshold = 0, MaxColors = ColorNumerics.GetColorCountForBitDepth(bitDepth) });
QuantizerOptions options = new()
{
TransparencyThreshold = 0,
MaxColors = ColorNumerics.GetColorCountForBitDepth(bitDepth),
TransparentColorMode = encoder.TransparentColorMode
};
this.quantizer = new WuQuantizer(options);
}
}
@ -1604,7 +1612,6 @@ internal sealed class PngEncoderCore : IDisposable
}
frameQuantizer.BuildPalette(
encoder.TransparentColorMode,
encoder.PixelSamplingStrategy,
image);

2
src/ImageSharp/Formats/TransparentColorMode.cs

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats;
/// <summary>
/// Specifies how transparent pixels should be handled during encoding and quantization.
/// Specifies how pixels with transparent alpha components should be handled during encoding and quantization.
/// </summary>
public enum TransparentColorMode
{

29
src/ImageSharp/Processing/Processors/Quantization/IQuantizer{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -36,37 +35,13 @@ public interface IQuantizer<TPixel> : IDisposable
/// Adds colors to the quantized palette from the given pixel source.
/// </summary>
/// <param name="pixelRegion">The <see cref="Buffer2DRegion{T}"/> of source pixels to register.</param>
public void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion)
=> this.AddPaletteColors(pixelRegion, TransparentColorMode.Preserve);
/// <summary>
/// Adds colors to the quantized palette from the given pixel source.
/// </summary>
/// <param name="pixelRegion">The <see cref="Buffer2DRegion{T}"/> of source pixels to register.</param>
/// <param name="mode">The <see cref="TransparentColorMode"/> to use when adding colors to the palette.</param>
public void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion, TransparentColorMode mode);
/// <summary>
/// Quantizes an image frame and return the resulting output pixels.
/// </summary>
/// <param name="source">The source image frame to quantize.</param>
/// <param name="bounds">The bounds within the frame to quantize.</param>
/// <returns>
/// A <see cref="IndexedImageFrame{TPixel}"/> representing a quantized version of the source frame pixels.
/// </returns>
/// <remarks>
/// Only executes the second (quantization) step. The palette has to be built by calling <see cref="AddPaletteColors(in Buffer2DRegion{TPixel})"/>.
/// To run both steps, use <see cref="QuantizerUtilities.BuildPaletteAndQuantizeFrame{TPixel}(IQuantizer{TPixel}, ImageFrame{TPixel}, Rectangle)"/>.
/// </remarks>
public IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> this.QuantizeFrame(source, bounds, TransparentColorMode.Preserve);
public void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion);
/// <summary>
/// Quantizes an image frame and return the resulting output pixels.
/// </summary>
/// <param name="source">The source image frame to quantize.</param>
/// <param name="bounds">The bounds within the frame to quantize.</param>
/// <param name="mode">The <see cref="TransparentColorMode"/> to use when quantizing the frame.</param>
/// <returns>
/// A <see cref="IndexedImageFrame{TPixel}"/> representing a quantized version of the source frame pixels.
/// </returns>
@ -74,7 +49,7 @@ public interface IQuantizer<TPixel> : IDisposable
/// Only executes the second (quantization) step. The palette has to be built by calling <see cref="AddPaletteColors(in Buffer2DRegion{TPixel})"/>.
/// To run both steps, use <see cref="QuantizerUtilities.BuildPaletteAndQuantizeFrame{TPixel}(IQuantizer{TPixel}, ImageFrame{TPixel}, Rectangle)"/>.
/// </remarks>
public IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds, TransparentColorMode mode);
public IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds);
/// <summary>
/// Returns the index and color from the quantized palette corresponding to the given color.

6
src/ImageSharp/Processing/Processors/Quantization/IQuantizingPixelRowDelegate{TPixel}.cs

@ -1,7 +1,6 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
@ -13,11 +12,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
internal interface IQuantizingPixelRowDelegate<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
/// <summary>
/// Gets the transparent color mode to use when adding colors to the palette.
/// </summary>
public TransparentColorMode TransparentColorMode { get; }
/// <summary>
/// Processes a row of pixels for quantization.
/// </summary>

20
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -75,9 +74,9 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
}
/// <inheritdoc/>
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion, TransparentColorMode mode)
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion)
{
PixelRowDelegate pixelRowDelegate = new(this.octree, mode);
PixelRowDelegate pixelRowDelegate = new(this.octree);
QuantizerUtilities.AddPaletteColors<OctreeQuantizer<TPixel>, TPixel, Rgba32, PixelRowDelegate>(
ref Unsafe.AsRef(in this),
in pixelRegion,
@ -103,12 +102,7 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> this.QuantizeFrame(source, bounds, TransparentColorMode.Preserve);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds, TransparentColorMode mode)
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds, mode);
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
@ -146,13 +140,7 @@ public struct OctreeQuantizer<TPixel> : IQuantizer<TPixel>
{
private readonly Octree octree;
public PixelRowDelegate(Octree octree, TransparentColorMode mode)
{
this.octree = octree;
this.TransparentColorMode = mode;
}
public TransparentColorMode TransparentColorMode { get; }
public PixelRowDelegate(Octree octree) => this.octree = octree;
public void Invoke(ReadOnlySpan<Rgba32> row, int rowIndex) => this.octree.AddColors(row);
}

14
src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs

@ -3,7 +3,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -76,29 +75,18 @@ internal struct PaletteQuantizer<TPixel> : IQuantizer<TPixel>
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion)
=> this.AddPaletteColors(in pixelRegion, TransparentColorMode.Preserve);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion, TransparentColorMode mode)
{
}
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> this.QuantizeFrame(source, bounds, TransparentColorMode.Preserve);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds, TransparentColorMode mode)
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds, mode);
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly byte GetQuantizedColor(TPixel color, out TPixel match)
{
// TODO: We need to use thesholding here.
if (this.transparencyIndex >= 0 && color.Equals(this.transparentColor))
{
match = this.transparentColor;

3
src/ImageSharp/Processing/Processors/Quantization/QuantizerOptions.cs

@ -81,7 +81,8 @@ public class QuantizerOptions : IDeepCloneable<QuantizerOptions>
}
/// <summary>
/// Gets or sets the transparent color mode used for handling transparent colors.
/// Gets or sets the transparent color mode used for handling transparent colors
/// when not using thresholding.
/// Defaults to <see cref="TransparentColorMode.Preserve"/>.
/// </summary>
public TransparentColorMode TransparentColorMode { get; set; } = TransparentColorMode.Preserve;

90
src/ImageSharp/Processing/Processors/Quantization/QuantizerUtilities.cs

@ -18,7 +18,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization;
/// </summary>
public static class QuantizerUtilities
{
internal static QuantizerOptions DeepClone(this QuantizerOptions options, Action<QuantizerOptions>? mutate)
/// <summary>
/// Performs a deep clone the <see cref="QuantizerOptions"/> instance and optionally mutates the clone.
/// </summary>
/// <param name="options">The <see cref="QuantizerOptions"/> instance to clone.</param>
/// <param name="mutate">An optional delegate to mutate the cloned instance.</param>
/// <returns>The cloned <see cref="QuantizerOptions"/> instance.</returns>
public static QuantizerOptions DeepClone(this QuantizerOptions options, Action<QuantizerOptions>? mutate)
{
QuantizerOptions clone = options.DeepClone();
mutate?.Invoke(clone);
@ -170,29 +176,6 @@ public static class QuantizerUtilities
ImageFrame<TPixel> source,
Rectangle bounds)
where TPixel : unmanaged, IPixel<TPixel>
=> BuildPaletteAndQuantizeFrame(
quantizer,
source,
bounds,
TransparentColorMode.Preserve);
/// <summary>
/// Execute both steps of the quantization.
/// </summary>
/// <param name="quantizer">The pixel specific quantizer.</param>
/// <param name="source">The source image frame to quantize.</param>
/// <param name="bounds">The bounds within the frame to quantize.</param>
/// <param name="mode">The transparent color mode.</param>
/// <typeparam name="TPixel">The pixel type.</typeparam>
/// <returns>
/// A <see cref="IndexedImageFrame{TPixel}"/> representing a quantized version of the source frame pixels.
/// </returns>
public static IndexedImageFrame<TPixel> BuildPaletteAndQuantizeFrame<TPixel>(
this IQuantizer<TPixel> quantizer,
ImageFrame<TPixel> source,
Rectangle bounds,
TransparentColorMode mode)
where TPixel : unmanaged, IPixel<TPixel>
{
Guard.NotNull(quantizer, nameof(quantizer));
Guard.NotNull(source, nameof(source));
@ -200,7 +183,7 @@ public static class QuantizerUtilities
Rectangle interest = Rectangle.Intersect(source.Bounds, bounds);
Buffer2DRegion<TPixel> region = source.PixelBuffer.GetRegion(interest);
quantizer.AddPaletteColors(in region, mode);
quantizer.AddPaletteColors(in region);
return quantizer.QuantizeFrame(source, bounds);
}
@ -212,15 +195,13 @@ public static class QuantizerUtilities
/// <param name="quantizer">The pixel specific quantizer.</param>
/// <param name="source">The source image frame to quantize.</param>
/// <param name="bounds">The bounds within the frame to quantize.</param>
/// <param name="mode">The transparent color mode.</param>
/// <returns>
/// A <see cref="IndexedImageFrame{TPixel}"/> representing a quantized version of the source frame pixels.
/// </returns>
public static IndexedImageFrame<TPixel> QuantizeFrame<TFrameQuantizer, TPixel>(
ref TFrameQuantizer quantizer,
ImageFrame<TPixel> source,
Rectangle bounds,
TransparentColorMode mode)
Rectangle bounds)
where TFrameQuantizer : struct, IQuantizer<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
@ -235,13 +216,13 @@ public static class QuantizerUtilities
if (quantizer.Options.Dither is null)
{
SecondPass(ref quantizer, source, destination, interest, mode);
SecondPass(ref quantizer, source, destination, interest);
}
else
{
// We clone the image as we don't want to alter the original via error diffusion based dithering.
using ImageFrame<TPixel> clone = source.Clone();
SecondPass(ref quantizer, clone, destination, interest, mode);
SecondPass(ref quantizer, clone, destination, interest);
}
return destination;
@ -259,29 +240,10 @@ public static class QuantizerUtilities
IPixelSamplingStrategy pixelSamplingStrategy,
Image<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> quantizer.BuildPalette(
TransparentColorMode.Preserve,
pixelSamplingStrategy,
source);
/// <summary>
/// Adds colors to the quantized palette from the given pixel regions.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="quantizer">The pixel specific quantizer.</param>
/// <param name="mode">The transparent color mode.</param>
/// <param name="pixelSamplingStrategy">The pixel sampling strategy.</param>
/// <param name="source">The source image to sample from.</param>
public static void BuildPalette<TPixel>(
this IQuantizer<TPixel> quantizer,
TransparentColorMode mode,
IPixelSamplingStrategy pixelSamplingStrategy,
Image<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
{
foreach (Buffer2DRegion<TPixel> region in pixelSamplingStrategy.EnumeratePixelRegions(source))
{
quantizer.AddPaletteColors(in region, mode);
quantizer.AddPaletteColors(in region);
}
}
@ -297,29 +259,10 @@ public static class QuantizerUtilities
IPixelSamplingStrategy pixelSamplingStrategy,
ImageFrame<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
=> quantizer.BuildPalette(
TransparentColorMode.Preserve,
pixelSamplingStrategy,
source);
/// <summary>
/// Adds colors to the quantized palette from the given pixel regions.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="quantizer">The pixel specific quantizer.</param>
/// <param name="mode">The transparent color mode.</param>
/// <param name="pixelSamplingStrategy">The pixel sampling strategy.</param>
/// <param name="source">The source image frame to sample from.</param>
public static void BuildPalette<TPixel>(
this IQuantizer<TPixel> quantizer,
TransparentColorMode mode,
IPixelSamplingStrategy pixelSamplingStrategy,
ImageFrame<TPixel> source)
where TPixel : unmanaged, IPixel<TPixel>
{
foreach (Buffer2DRegion<TPixel> region in pixelSamplingStrategy.EnumeratePixelRegions(source))
{
quantizer.AddPaletteColors(in region, mode);
quantizer.AddPaletteColors(in region);
}
}
@ -340,7 +283,7 @@ public static class QuantizerUtilities
Span<TPixel2> delegateRow = delegateRowOwner.Memory.Span;
bool replaceByThreshold = ShouldReplacePixelsByAlphaThreshold<TPixel>(threshold);
bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(rowDelegate.TransparentColorMode);
bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(mode);
if (replaceByThreshold || replaceTransparent)
{
@ -389,13 +332,14 @@ public static class QuantizerUtilities
ref TFrameQuantizer quantizer,
ImageFrame<TPixel> source,
IndexedImageFrame<TPixel> destination,
Rectangle bounds,
TransparentColorMode mode)
Rectangle bounds)
where TFrameQuantizer : struct, IQuantizer<TPixel>
where TPixel : unmanaged, IPixel<TPixel>
{
float threshold = quantizer.Options.TransparencyThreshold;
bool replaceByThreshold = ShouldReplacePixelsByAlphaThreshold<TPixel>(threshold);
TransparentColorMode mode = quantizer.Options.TransparentColorMode;
bool replaceTransparent = EncodingUtilities.ShouldReplaceTransparentPixels<TPixel>(mode);
IDither? dither = quantizer.Options.Dither;

20
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

@ -6,7 +6,6 @@ using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@ -107,9 +106,9 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
}
/// <inheritdoc/>
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion, TransparentColorMode mode)
public readonly void AddPaletteColors(in Buffer2DRegion<TPixel> pixelRegion)
{
PixelRowDelegate pixelRowDelegate = new(ref Unsafe.AsRef(in this), mode);
PixelRowDelegate pixelRowDelegate = new(ref Unsafe.AsRef(in this));
QuantizerUtilities.AddPaletteColors<WuQuantizer<TPixel>, TPixel, Rgba32, PixelRowDelegate>(
ref Unsafe.AsRef(in this),
in pixelRegion,
@ -162,12 +161,7 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds)
=> this.QuantizeFrame(source, bounds, TransparentColorMode.Preserve);
/// <inheritdoc/>
[MethodImpl(InliningOptions.ShortMethod)]
public readonly IndexedImageFrame<TPixel> QuantizeFrame(ImageFrame<TPixel> source, Rectangle bounds, TransparentColorMode mode)
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds, mode);
=> QuantizerUtilities.QuantizeFrame(ref Unsafe.AsRef(in this), source, bounds);
/// <inheritdoc/>
public readonly byte GetQuantizedColor(TPixel color, out TPixel match)
@ -891,13 +885,7 @@ internal struct WuQuantizer<TPixel> : IQuantizer<TPixel>
{
private readonly WuQuantizer<TPixel> quantizer;
public PixelRowDelegate(ref WuQuantizer<TPixel> quantizer, TransparentColorMode mode)
{
this.quantizer = quantizer;
this.TransparentColorMode = mode;
}
public TransparentColorMode TransparentColorMode { get; }
public PixelRowDelegate(ref WuQuantizer<TPixel> quantizer) => this.quantizer = quantizer;
public void Invoke(ReadOnlySpan<Rgba32> row, int rowIndex) => this.quantizer.Build3DHistogram(row);
}

Loading…
Cancel
Save