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 : IDisposableRemarks
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
| Name | Description |
|---|---|
| PaletteBuilder(ImageConfiguration?) | Initializes a new instance of the PaletteBuilder class. |
PaletteBuilder(ImageConfiguration?)
PaletteBuilder.csInitializes a new instance of the PaletteBuilder class.
public PaletteBuilder(ImageConfiguration? configuration = null)Parameters
configurationImageConfiguration?- The configuration whose resource limits the histogram is charged to, or
nullfor the default configuration.
Exceptions
ImageResourceLimitException- The histogram exceeds the live-allocation limit.
Methods
| Name | Description |
|---|---|
| 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.csAdds the pixels of every displayed frame of an image and of its poster frame.
public void Add(Image image, CancellationToken cancellationToken = default)Parameters
imageImage- The image.
cancellationTokenCancellationToken- The token checked between rows.
Exceptions
ArgumentNullExceptionimageisnull.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.csAdds the pixels of a frame.
public void Add(ImageFrame frame, CancellationToken cancellationToken = default)Parameters
frameImageFrame- The frame.
cancellationTokenCancellationToken- The token checked between rows.
Exceptions
ArgumentNullExceptionframeisnull.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.
Build(int)
PaletteBuilder.csBuilds the palette of the pixels added so far. The builder can then only be disposed.
public ImagePalette Build(int maxColors = 256)Parameters
maxColorsint- 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
ArgumentOutOfRangeExceptionmaxColorsis not between 2 and 256.InvalidOperationException- No pixel was added, or the palette was already built.
ObjectDisposedException- The builder is disposed.
Dispose()
PaletteBuilder.csReleases the histogram.
public void Dispose()