Skip to content

Instantly share code, notes, and snippets.

@antonfirsov
Last active February 19, 2020 00:58
Show Gist options
  • Save antonfirsov/cbad26bffc39fdd9803b7978a2e87abb to your computer and use it in GitHub Desktop.
Save antonfirsov/cbad26bffc39fdd9803b7978a2e87abb to your computer and use it in GitHub Desktop.
Dithering and Quantization API proposal
public interface IFrameQuantizer<TPixel>
{
QuantizerOptions Options { get; }
// Make FirstPass explicit on the interface.
// For single-step PaletteQuantizer this is NOP
void BuildPalette(ImageFrame<TPixel> image, Rectangle bounds);
ReadOnlyMemory<TPixel> Palette { get; }
// Note that we are always passing {palette: this.Palette.Span}
// Because of performance reasons
byte GetQuantizedColor(TPixel color, ReadOnlySpan<TPixel> palette, out TPixel match);
// FUTURE bulk variant
void GetQuantizedColors(
ReadOnlySpan<TPixel> colors,
ReadOnlySpan<TPixel> palette,
Span<byte> indices, S
pan<TPixel> matches);
}
// No need for a FrameQuantizer<TPixel> base class, since the shared logic is now moved
// to the QuantizerExtensions utility class.
// The quantizer should be a struct
public struct WuFrameQuantizer<TPixel> : IFrameQuantizer<TPixel>
{
// ...
}
public interface IDither
{
// Passing TQuantizer as a generic value-type argument enables devirtualization
void ApplyQuantizer<TPixel, TFrameQuantizer>(
TFrameQuantizer q,
ImageFrame<TPixel> frame,
Rectangle bounds,
Memory<byte> output,
ReadOnlyMemory<TPixel> palette)
where TFrameQuantizer : struct, IFrameQuantizer<TPixel>;
// To be used with PaletteDither processor.
// ??? how should TPaletteDitherThingie look like ???
void ApplyPaletteDitherProcessor<TPixel, TPaletteDitherThingie>(
ImageFrame<TPixel> frame,
TPaletteDitherThingie paletteDither);
}
public static class QuantizerExtensions
{
public static QuantizeFrame<TFrameQuantizer, TPixel>(
this TFrameQuantizer q,
ImageFrame<TPixel> image,
Rectangle bounds)
where TFrameQuantizer : IFrameQuantizer<TPixel>
{
// contains current QuantizeFrame logic, invoking SecondPass
}
private static void SecondPass<TFrameQuantizer, TPixel>(
TFrameQuantizer q,
ImageFrame<TPixel> frame,
Rectangle bounds,
Memory<byte> output,
ReadOnlyMemory<TPixel> palette)
where TFrameQuantizer : IFrameuantizer<TPixel>
{
q.Options.Dither.ApplyQuantizer(q, frame, bounds, output, palette);
}
}
internal class ErrorDiffusion : IDither
{
public void ApplyQuantizer<TPixel, TQuantizer>(
TFrameQuantizer q,
ImageFrame<TPixel> frame,
Rectangle bounds,
Memory<byte> output,
ReadOnlyMemory<TPixel> palette)
where TQuantizer : struct, IFrameQuantizer<TPixel>
{
// contains the current logic from FrameQuantizer L164-L180
// + ErrorDither.Dither
// Calls to q.GetQuantizedColor(...) are expected to be devirtualized & inlined
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment