diff --git a/src/ImageSharp/Color/Color.WebSafePalette.cs b/src/ImageSharp/Color/Color.WebSafePalette.cs index 506432ac2d..8e5fb2a55b 100644 --- a/src/ImageSharp/Color/Color.WebSafePalette.cs +++ b/src/ImageSharp/Color/Color.WebSafePalette.cs @@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp /// /// Gets a collection of named, web safe colors as defined in the CSS Color Module Level 4. /// - public static ReadOnlySpan WebSafePalette => WebSafePaletteLazy.Value; + public static ReadOnlyMemory WebSafePalette => WebSafePaletteLazy.Value; private static Color[] CreateWebSafePalette() => new[] { diff --git a/src/ImageSharp/Color/Color.WernerPalette.cs b/src/ImageSharp/Color/Color.WernerPalette.cs index 37980b15fd..768fe065cd 100644 --- a/src/ImageSharp/Color/Color.WernerPalette.cs +++ b/src/ImageSharp/Color/Color.WernerPalette.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp /// Gets a collection of colors as defined in the original second edition of Werner’s Nomenclature of Colours 1821. /// The hex codes were collected and defined by Nicholas Rougeux . /// - public static ReadOnlySpan WernerPalette => WernerPaletteLazy.Value; + public static ReadOnlyMemory WernerPalette => WernerPaletteLazy.Value; private static Color[] CreateWernerPalette() => new[] { diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 12a515cca7..2770790a28 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -144,8 +144,6 @@ namespace SixLabors.ImageSharp.Formats.Gif private void EncodeGlobal(Image image, QuantizedFrame quantized, int transparencyIndex, Stream stream) where TPixel : struct, IPixel { - var palleteQuantizer = new PaletteQuantizer(quantized.Palette, this.quantizer.Diffuser); - for (int i = 0; i < image.Frames.Count; i++) { ImageFrame frame = image.Frames[i]; @@ -160,10 +158,13 @@ namespace SixLabors.ImageSharp.Formats.Gif } else { - using (IFrameQuantizer palleteFrameQuantizer = palleteQuantizer.CreateFrameQuantizer(image.GetConfiguration())) - using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) + using (IFrameQuantizer palleteFrameQuantizer = + new PaletteFrameQuantizer(this.quantizer.Diffuser, quantized.Palette)) { - this.WriteImageData(paletteQuantized, stream); + using (QuantizedFrame paletteQuantized = palleteFrameQuantizer.QuantizeFrame(frame)) + { + this.WriteImageData(paletteQuantized, stream); + } } } } diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/DiffuseExtensions.cs index cd714c3da3..f9a1bdde0e 100644 --- a/src/ImageSharp/Processing/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/DiffuseExtensions.cs @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, - ReadOnlySpan palette) => + ReadOnlyMemory palette) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); /// @@ -91,7 +91,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, - ReadOnlySpan palette, + ReadOnlyMemory palette, Rectangle rectangle) => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); } diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/DitherExtensions.cs index 55794aec20..f83a9e9e81 100644 --- a/src/ImageSharp/Processing/DitherExtensions.cs +++ b/src/ImageSharp/Processing/DitherExtensions.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Dither( this IImageProcessingContext source, IOrderedDither dither, - ReadOnlySpan palette) => + ReadOnlyMemory palette) => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); /// @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Dither( this IImageProcessingContext source, IOrderedDither dither, - ReadOnlySpan palette, + ReadOnlyMemory palette, Rectangle rectangle) => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); } diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs index 5436a7733f..e0b79c2b20 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffusionPaletteProcessor.cs @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// The error diffuser /// The threshold to split the image. Must be between 0 and 1. /// The palette to select substitute colors from. - public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, ReadOnlySpan palette) + public ErrorDiffusionPaletteProcessor(IErrorDiffuser diffuser, float threshold, ReadOnlyMemory palette) : base(palette) { Guard.NotNull(diffuser, nameof(diffuser)); diff --git a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs index ba7c1e9980..ac6554d4c1 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/OrderedDitherPaletteProcessor.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// /// The ordered ditherer. /// The palette to select substitute colors from. - public OrderedDitherPaletteProcessor(IOrderedDither dither, ReadOnlySpan palette) + public OrderedDitherPaletteProcessor(IOrderedDither dither, ReadOnlyMemory palette) : base(palette) => this.Dither = dither ?? throw new ArgumentNullException(nameof(dither)); /// diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs index 904d026345..de798b64bc 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor.cs @@ -12,23 +12,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering /// public abstract class PaletteDitherProcessor : IImageProcessor { - private readonly Color[] palette; - /// /// Initializes a new instance of the class. /// /// The palette to select substitute colors from. - protected PaletteDitherProcessor(ReadOnlySpan palette) + protected PaletteDitherProcessor(ReadOnlyMemory palette) { - // This shouldn't be a perf issue: - // these arrays are small, and created with low frequency. - this.palette = palette.ToArray(); + this.Palette = palette; } /// /// Gets the palette to select substitute colors from. /// - public ReadOnlySpan Palette => this.palette; + public ReadOnlyMemory Palette { get; } /// public abstract IImageProcessor CreatePixelSpecificProcessor() diff --git a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs index 334eab833c..205b589b1e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/PaletteDitherProcessor{TPixel}.cs @@ -20,13 +20,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering { private readonly Dictionary> cache = new Dictionary>(); + private TPixel[] palette; + /// /// The vector representation of the image palette. /// private Vector4[] paletteVector; - private TPixel[] palette; - /// /// Initializes a new instance of the class. /// @@ -37,6 +37,33 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering protected PaletteDitherProcessor Definition { get; } + protected override void BeforeFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + base.BeforeFrameApply(source, sourceRectangle, configuration); + + // Lazy init palette: + if (this.palette is null) + { + ReadOnlySpan sourcePalette = this.Definition.Palette.Span; + this.palette = new TPixel[sourcePalette.Length]; + Color.ToPixel(configuration, sourcePalette, this.palette); + } + + // Lazy init paletteVector: + if (this.paletteVector is null) + { + this.paletteVector = new Vector4[this.palette.Length]; + PixelOperations.Instance.ToVector4( + configuration, + (ReadOnlySpan)this.palette, + (Span)this.paletteVector, + PixelConversionModifiers.Scale); + } + } + /// /// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space. /// @@ -88,25 +115,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering return pair; } - - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - base.BeforeFrameApply(source, sourceRectangle, configuration); - - // Lazy init palette: - if (this.palette is null) - { - ReadOnlySpan sourcePalette = this.Definition.Palette; - this.palette = new TPixel[sourcePalette.Length]; - Color.ToPixel(configuration, sourcePalette, this.palette); - } - - // Lazy init paletteVector: - if (this.paletteVector is null) - { - this.paletteVector = new Vector4[this.palette.Length]; - PixelOperations.Instance.ToVector4(configuration, (ReadOnlySpan)this.palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); - } - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs index dcdddb217c..255be5ed59 100644 --- a/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Overlays/GlowProcessor.cs @@ -54,7 +54,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Overlays } /// - /// Gets the options effecting blending and composition + /// Gets the options effecting blending and composition. /// public GraphicsOptions GraphicsOptions { get; } diff --git a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs similarity index 81% rename from src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs index f23343f6d7..6d8136513f 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizerBase{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/FrameQuantizer{TPixel}.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// The base class for all implementations /// /// The pixel format. - public abstract class FrameQuantizerBase : IFrameQuantizer + public abstract class FrameQuantizer : IFrameQuantizer where TPixel : struct, IPixel { /// @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization private Vector4[] paletteVector; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The quantizer /// @@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// only call the method. /// If two passes are required, the code will also call . /// - protected FrameQuantizerBase(IQuantizer quantizer, bool singlePass) + protected FrameQuantizer(IQuantizer quantizer, bool singlePass) { Guard.NotNull(quantizer, nameof(quantizer)); @@ -52,6 +52,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.Dither = this.Diffuser != null; this.singlePass = singlePass; } + + /// + /// Initializes a new instance of the class. + /// + /// The diffuser + /// + /// If true, the quantization process only needs to loop through the source pixels once + /// + /// + /// If you construct this class with a true for , then the code will + /// only call the method. + /// If two passes are required, the code will also call . + /// + protected FrameQuantizer(IErrorDiffuser diffuser, bool singlePass) + { + this.Diffuser = diffuser; + this.Dither = this.Diffuser != null; + this.singlePass = singlePass; + } /// public bool Dither { get; } @@ -77,22 +96,28 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } // Collect the palette. Required before the second pass runs. - TPixel[] palette = this.GetPalette(); + ReadOnlyMemory palette = this.GetPalette(); this.paletteVector = new Vector4[palette.Length]; - PixelOperations.Instance.ToVector4(image.Configuration, (ReadOnlySpan)palette, (Span)this.paletteVector, PixelConversionModifiers.Scale); - var quantizedFrame = new QuantizedFrame(image.MemoryAllocator, width, height, palette); + PixelOperations.Instance.ToVector4(image.Configuration, palette.Span, (Span)this.paletteVector, PixelConversionModifiers.Scale); + + // TODO: Pass ReadOnlyMemory instead of array! + var quantizedFrame = new QuantizedFrame( + image.MemoryAllocator, + width, + height, + palette.Span.ToArray()); if (this.Dither) { // We clone the image as we don't want to alter the original via dithering. using (ImageFrame clone = image.Clone()) { - this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette, width, height); + this.SecondPass(clone, quantizedFrame.GetPixelSpan(), palette.Span, width, height); } } else { - this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette, width, height); + this.SecondPass(image, quantizedFrame.GetPixelSpan(), palette.Span, width, height); } return quantizedFrame; @@ -134,7 +159,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// - protected abstract TPixel[] GetPalette(); + protected abstract ReadOnlyMemory GetPalette(); /// /// Returns the index of the first instance of the transparent color in the palette. diff --git a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs index dd56375f63..85a4d20295 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/OctreeFrameQuantizer{TPixel}.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class OctreeFrameQuantizer : FrameQuantizerBase + internal sealed class OctreeFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { /// @@ -136,10 +136,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization } } - internal TPixel[] AotGetPalette() => this.GetPalette(); + internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); /// - protected override TPixel[] GetPalette() => this.octree.Palletize(this.colors); + protected override ReadOnlyMemory GetPalette() => this.octree.Palletize(this.colors); /// /// Process the pixel in the second pass of the algorithm. diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs index f8a19f8c40..265c343e68 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteFrameQuantizer{TPixel}.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Dithering; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { @@ -14,21 +15,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class PaletteFrameQuantizer : FrameQuantizerBase + internal sealed class PaletteFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { /// /// The reduced image palette. /// - private readonly TPixel[] palette; + private readonly ReadOnlyMemory palette; /// /// Initializes a new instance of the class. /// - /// The palette quantizer. + /// The palette quantizer. /// An array of all colors in the palette. - public PaletteFrameQuantizer(IQuantizer quantizer, TPixel[] colors) - : base(quantizer, true) => this.palette = colors; + public PaletteFrameQuantizer(IErrorDiffuser diffuser, ReadOnlyMemory colors) + : base(diffuser, true) => this.palette = colors; /// protected override void SecondPass( @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected override TPixel[] GetPalette() => this.palette; + protected override ReadOnlyMemory GetPalette() => this.palette; /// /// Process the pixel in the second pass of the algorithm diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs index 6b2be3d038..ba434a4b21 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; + using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Dithering; @@ -14,61 +15,65 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// By default the quantizer uses dithering. /// /// - public abstract class PaletteQuantizer : IQuantizer + public class PaletteQuantizer : IQuantizer { /// /// Initializes a new instance of the class. /// - protected PaletteQuantizer() - : this(true) + /// The palette. + public PaletteQuantizer(ReadOnlyMemory palette) + : this(palette, true) { } /// /// Initializes a new instance of the class. /// + /// The palette. /// Whether to apply dithering to the output image - protected PaletteQuantizer(bool dither) - : this(GetDiffuser(dither)) + public PaletteQuantizer(ReadOnlyMemory palette, bool dither) + : this(palette, GetDiffuser(dither)) { } /// /// Initializes a new instance of the class. /// + /// The palette. /// The error diffusion algorithm, if any, to apply to the output image - protected PaletteQuantizer(IErrorDiffuser diffuser) => this.Diffuser = diffuser; + public PaletteQuantizer(ReadOnlyMemory palette, IErrorDiffuser diffuser) + { + this.Palette = palette; + this.Diffuser = diffuser; + } /// public IErrorDiffuser Diffuser { get; } - /// - public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - where TPixel : struct, IPixel; - - /// - public abstract IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - where TPixel : struct, IPixel; - /// - /// Creates the generic frame quantizer. + /// Gets the palette. /// - /// The pixel format. - /// The to configure internal operations. - /// The color palette. - /// The maximum number of colors to hold in the color palette. - /// The - protected IFrameQuantizer CreateFrameQuantizer(Configuration configuration, TPixel[] palette, int maxColors) + public ReadOnlyMemory Palette { get; } + + /// + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) where TPixel : struct, IPixel { - int max = Math.Min(QuantizerConstants.MaxColors, Math.Min(maxColors, palette.Length)); + TPixel[] palette = new TPixel[this.Palette.Length]; + Color.ToPixel(configuration, this.Palette.Span, palette.AsSpan()); + return new PaletteFrameQuantizer(this.Diffuser, palette); + } - if (max != palette.Length) - { - return new PaletteFrameQuantizer(this, palette.AsSpan(0, max).ToArray()); - } + /// + public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) + where TPixel : struct, IPixel + { + maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); + int max = Math.Min(maxColors, this.Palette.Length); - return new PaletteFrameQuantizer(this, palette); + TPixel[] palette = new TPixel[max]; + Color.ToPixel(configuration, this.Palette.Span.Slice(0, max), palette.AsSpan()); + return new PaletteFrameQuantizer(this.Diffuser, palette); } private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; diff --git a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs deleted file mode 100644 index a350adfc0c..0000000000 --- a/src/ImageSharp/Processing/Processors/Quantization/PaletteQuantizer{TPixel}.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.Runtime.CompilerServices; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Dithering; - -namespace SixLabors.ImageSharp.Processing.Processors.Quantization -{ - /// - /// A generic palette quantizer. - /// - /// The pixel format. - public class PaletteQuantizer : IQuantizer - where TPixel : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - public PaletteQuantizer(TPixel[] palette) - : this(palette, true) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - /// Whether to apply dithering to the output image - public PaletteQuantizer(TPixel[] palette, bool dither) - : this(palette, GetDiffuser(dither)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The color palette to use. - /// The error diffusion algorithm, if any, to apply to the output image - public PaletteQuantizer(TPixel[] palette, IErrorDiffuser diffuser) - { - Guard.MustBeBetweenOrEqualTo(palette.Length, QuantizerConstants.MinColors, QuantizerConstants.MaxColors, nameof(palette)); - this.Palette = palette; - this.Diffuser = diffuser; - } - - /// - public IErrorDiffuser Diffuser { get; } - - /// - /// Gets the palette. - /// - public TPixel[] Palette { get; } - - /// - /// Creates the generic frame quantizer. - /// - /// The to configure internal operations. - /// The . - public IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => ((IQuantizer)this).CreateFrameQuantizer(configuration); - - /// - /// Creates the generic frame quantizer. - /// - /// The to configure internal operations. - /// The maximum number of colors to hold in the color palette. - /// The . - public IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => ((IQuantizer)this).CreateFrameQuantizer(configuration, maxColors); - - /// - IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration) - { - if (!typeof(TPixel).Equals(typeof(TPixel1))) - { - throw new InvalidOperationException("Generic method type must be the same as class type."); - } - - TPixel[] paletteRef = this.Palette; - return new PaletteFrameQuantizer(this, Unsafe.As(ref paletteRef)); - } - - /// - IFrameQuantizer IQuantizer.CreateFrameQuantizer(Configuration configuration, int maxColors) - { - if (!typeof(TPixel).Equals(typeof(TPixel1))) - { - throw new InvalidOperationException("Generic method type must be the same as class type."); - } - - TPixel[] paletteRef = this.Palette; - TPixel1[] castPalette = Unsafe.As(ref paletteRef); - - maxColors = maxColors.Clamp(QuantizerConstants.MinColors, QuantizerConstants.MaxColors); - int max = Math.Min(maxColors, castPalette.Length); - - if (max != castPalette.Length) - { - return new PaletteFrameQuantizer(this, castPalette.AsSpan(0, max).ToArray()); - } - - return new PaletteFrameQuantizer(this, castPalette); - } - - private static IErrorDiffuser GetDiffuser(bool dither) => dither ? KnownDiffusers.FloydSteinberg : null; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs index 93630a9166..3f6dfde281 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WebSafePaletteQuantizer.cs @@ -15,6 +15,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// public WebSafePaletteQuantizer() + : this(true) { } @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public WebSafePaletteQuantizer(bool dither) - : base(dither) + : base(Color.WebSafePalette, dither) { } @@ -32,16 +33,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public WebSafePaletteQuantizer(IErrorDiffuser diffuser) - : base(diffuser) + : base(Color.WebSafePalette, diffuser) { } - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette.Length); - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => this.CreateFrameQuantizer(configuration, NamedColors.WebSafePalette, maxColors); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs index 2ff9f5090c..d659ecabf7 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WernerPaletteQuantizer.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// Initializes a new instance of the class. /// public WernerPaletteQuantizer() + : this(true) { } @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// Whether to apply dithering to the output image public WernerPaletteQuantizer(bool dither) - : base(dither) + : base(Color.WernerPalette, dither) { } @@ -33,16 +34,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// The error diffusion algorithm, if any, to apply to the output image public WernerPaletteQuantizer(IErrorDiffuser diffuser) - : base(diffuser) + : base(Color.WernerPalette, diffuser) { } - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration) - => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette.Length); - - /// - public override IFrameQuantizer CreateFrameQuantizer(Configuration configuration, int maxColors) - => this.CreateFrameQuantizer(configuration, NamedColors.WernerPalette, maxColors); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs index 1f1513adf1..95918f564a 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/WuFrameQuantizer{TPixel}.cs @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization /// /// /// The pixel format. - internal sealed class WuFrameQuantizer : FrameQuantizerBase + internal sealed class WuFrameQuantizer : FrameQuantizer where TPixel : struct, IPixel { // The following two variables determine the amount of bits to preserve when calculating the histogram. @@ -171,10 +171,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization this.tag?.Dispose(); } - internal TPixel[] AotGetPalette() => this.GetPalette(); + internal ReadOnlyMemory AotGetPalette() => this.GetPalette(); /// - protected override TPixel[] GetPalette() + protected override ReadOnlyMemory GetPalette() { if (this.palette is null) { diff --git a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs index 59dcde2a63..d91688cd2c 100644 --- a/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs +++ b/tests/ImageSharp.Tests/Processing/Dithering/DitherTest.cs @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Binarization this.operations.Dither(this.orderedDither); OrderedDitherPaletteProcessor p = this.Verify(); Assert.Equal(this.orderedDither, p.Dither); - Assert.Equal(Color.WebSafePalette.ToArray(), p.Palette); + Assert.Equal(Color.WebSafePalette, p.Palette); } [Fact] diff --git a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs index a4e6edd53e..05ce700476 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Quantization/PaletteQuantizerTests.cs @@ -11,21 +11,21 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization { public class PaletteQuantizerTests { - private static readonly Rgba32[] Rgb = new Rgba32[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue }; + private static readonly Color[] Rgb = new Color[] { Rgba32.Red, Rgba32.Green, Rgba32.Blue }; [Fact] public void PaletteQuantizerConstructor() { - var quantizer = new PaletteQuantizer(Rgb); + var quantizer = new PaletteQuantizer(Rgb); Assert.Equal(Rgb, quantizer.Palette); Assert.Equal(KnownDiffusers.FloydSteinberg, quantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, false); + quantizer = new PaletteQuantizer(Rgb, false); Assert.Equal(Rgb, quantizer.Palette); Assert.Null(quantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); Assert.Equal(Rgb, quantizer.Palette); Assert.Equal(KnownDiffusers.Atkinson, quantizer.Diffuser); } @@ -33,35 +33,27 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Quantization [Fact] public void PaletteQuantizerCanCreateFrameQuantizer() { - var quantizer = new PaletteQuantizer(Rgb); - IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + var quantizer = new PaletteQuantizer(Rgb); + IFrameQuantizer frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.True(frameQuantizer.Dither); Assert.Equal(KnownDiffusers.FloydSteinberg, frameQuantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, false); - frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + quantizer = new PaletteQuantizer(Rgb, false); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.False(frameQuantizer.Dither); Assert.Null(frameQuantizer.Diffuser); - quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); - frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); + quantizer = new PaletteQuantizer(Rgb, KnownDiffusers.Atkinson); + frameQuantizer = quantizer.CreateFrameQuantizer(Configuration.Default); Assert.NotNull(frameQuantizer); Assert.True(frameQuantizer.Dither); Assert.Equal(KnownDiffusers.Atkinson, frameQuantizer.Diffuser); } - [Fact] - public void PaletteQuantizerThrowsOnInvalidGenericMethodCall() - { - var quantizer = new PaletteQuantizer(Rgb); - - Assert.Throws(() => ((IQuantizer)quantizer).CreateFrameQuantizer(Configuration.Default)); - } - [Fact] public void KnownQuantizersWebSafeTests() {