PaletteBuilder Class

Computes one palette from the pixels of one or more frames, for example a palette shared by every frame of an animation (QuantizeOptions.Palette).

public sealed class PaletteBuilder : IDisposable

Remarks

The pixels are read as 8-bit RGBA, with the conversion of Image.CloneAs (a floating-point sample outside [0, 1] is rejected). Every pixel counts once, whatever its frame. PaletteBuilder.Build returns the exact colors when at most the requested number are used, and otherwise a deterministic variance-based median cut: the colors are counted on a histogram of fixed size (5 bits per channel for the opaque colors, 4 bits per channel and 3 bits of alpha for the translucent ones), the box with the largest weighted squared error is split along its axis of largest variance at the point that maximizes the between-part variance, and each palette color is the weighted mean of its box; k-means then refines the palette (each color moves to its nearest entry and each entry to the weighted mean of its colors, at most eight times, on exact integer sums). Opaque and translucent colors are never in the same box or cluster (unless a single color is requested), and translucent colors are compared premultiplied by their alpha. Fully transparent pixels give the palette one transparent entry, which counts toward the number of colors.

The result is the same for the same pixels on every platform, whatever the order in which the frames are added. The histogram (about 2.6 MiB) is charged to the resource limits of the configuration given at creation. A builder supports one operation at a time and builds one palette.

Example

[Fact]
public void PaletteBuilder_SharedPalette()
{
    // An animation of two frames with different colors
    using var animation = new Image<Rgba32>(32, 32);
    animation.AppendFrame();
    for (var i = 0; i < animation.Frames.Count; i++)
    {
        var shift = i * 100;
        animation.Frames[i].ProcessPixelRows(pixels =>
        {
            for (var y = 0; y < pixels.Height; y++)
            {
                var row = pixels.GetRowSpan(y);
                for (var x = 0; x < row.Length; x++)
                {
                    row[x] = new Rgba32((byte)(x * 8), (byte)((y * 4) + shift), (byte)shift);
                }
            }
        });
    }

    // One palette for every frame, then every frame maps to it
    ImagePalette palette;
    using (var builder = new PaletteBuilder())
    {
        builder.Add(animation);
        palette = builder.Build(maxColors: 64);
    }

    animation.Quantize(new QuantizeOptions { Palette = palette, Dithering = DitheringMode.Sierra });

    foreach (var frame in animation.Frames)
    {
        var colors = new HashSet<Rgba32>();
        frame.ProcessPixelRows(pixels =>
        {
            for (var y = 0; y < pixels.Height; y++)
            {
                colors.UnionWith(pixels.GetRowSpan(y).ToArray());
            }
        });

        Assert.True(colors.IsSubsetOf(palette.Colors.ToArray()));
    }
}
Implements

Constructors

NameDescription
PaletteBuilder(ImageConfiguration?)Initializes a new instance of the PaletteBuilder class.

PaletteBuilder(ImageConfiguration?)

PaletteBuilder.cs

Initializes a new instance of the PaletteBuilder class.

public PaletteBuilder(ImageConfiguration? configuration = null)

Parameters

configuration ImageConfiguration?
The configuration whose resource limits the histogram is charged to, or null for the default configuration.

Exceptions

ImageResourceLimitException
The histogram exceeds the live-allocation limit.

Methods

NameDescription
Add(Image, CancellationToken)Adds the pixels of every displayed frame of an image and of its poster frame.
Add(ImageFrame, CancellationToken)Adds the pixels of a frame.
Build(int)Builds the palette of the pixels added so far. The builder can then only be disposed.
Dispose()Releases the histogram.

Add overloads

Add(Image, CancellationToken)

PaletteBuilder.cs

Adds the pixels of every displayed frame of an image and of its poster frame.

public void Add(Image image, CancellationToken cancellationToken = default)
Parameters
image Image
The image.
cancellationToken CancellationToken
The token checked between rows.
Exceptions
ArgumentNullException
image is null.
UnsupportedImageFeatureException
A floating-point sample is outside [0, 1] or is not a number.
InvalidOperationException
The palette was already built, the builder would count more than 2^31 - 1 pixels, or a pixel lease is active.
ObjectDisposedException
The builder or the image is disposed.

Add(ImageFrame, CancellationToken)

PaletteBuilder.cs

Adds the pixels of a frame.

public void Add(ImageFrame frame, CancellationToken cancellationToken = default)
Parameters
frame ImageFrame
The frame.
cancellationToken CancellationToken
The token checked between rows.
Exceptions
ArgumentNullException
frame is null.
UnsupportedImageFeatureException
A floating-point sample is outside [0, 1] or is not a number.
InvalidOperationException
The palette was already built, the builder would count more than 2^31 - 1 pixels, or a pixel lease is active.
ObjectDisposedException
The builder is disposed, or the frame is no longer attached to a live image.

Builds the palette of the pixels added so far. The builder can then only be disposed.

public ImagePalette Build(int maxColors = 256)

Parameters

maxColors int
The largest number of colors, including the transparent entry. Defaults to 256.

Returns

ImagePalette The palette: opaque colors first, then translucent colors, then the transparent entry.

Exceptions

ArgumentOutOfRangeException
maxColors is not between 2 and 256.
InvalidOperationException
No pixel was added, or the palette was already built.
ObjectDisposedException
The builder is disposed.

Releases the histogram.

public void Dispose()