Extensions to System.Drawing


Extension methods for System.Drawing types (bitmaps, images, colors, graphics).
Extension Methods
Bitmap Extensions (Bitmap)
Bitmap Locking
Lock(...) - Lock bitmap for direct pixel access (8 overloads for different combinations)
Lock() - Lock entire bitmap with ReadWrite mode
Lock(Rectangle) - Lock region
Lock(ImageLockMode) - Lock with specific mode
Lock(PixelFormat) - Lock with specific format
Lock(Rectangle, ImageLockMode)
Lock(Rectangle, PixelFormat)
Lock(ImageLockMode, PixelFormat)
Lock(Rectangle, ImageLockMode, PixelFormat) - Full control
- Returns format-specific
IBitmapLocker implementation
ConvertPixelFormat(PixelFormat) - Convert bitmap to different pixel format
Crop(Rectangle, PixelFormat = DontCare) - Extract region from bitmap
Resize(int width, int height, InterpolationMode = Bicubic) - Resize bitmap
Rotated(float angle, Point? center = null) - Create rotated copy of bitmap
RotateInplace(float angle, Point? center = null) - Rotate bitmap in-place
RotateTo(Bitmap target, float angle, Point? center = null) - Rotate to target bitmap
Color Quantization & Dithering
ReduceColors<TQuantizer, TDitherer>(quantizer, ditherer, colorCount, isHighQuality) - Reduce image to indexed palette
- Returns indexed bitmap (1bpp for 2 colors, 4bpp for ≤16, 8bpp for ≤256)
isHighQuality: true uses OkLab perceptual color space (slower, better gradients)
isHighQuality: false uses linear RGB with Euclidean distance (faster)
using var original = new Bitmap("photo.png");
// High quality: OkLab color space, Floyd-Steinberg dithering
using var indexed = original.ReduceColors(
new WuQuantizer(),
ErrorDiffusion.FloydSteinberg,
16,
isHighQuality: true);
// Fast: Linear RGB, serpentine dithering
using var fast = original.ReduceColors(
new OctreeQuantizer(),
ErrorDiffusion.Atkinson.Serpentine,
256,
isHighQuality: false);
indexed.Save("indexed.gif");
Image Extensions (Image)
Multi-Page Support
GetPageAt(int page) - Get specific page from multi-page image (e.g., TIFF)
GetPageCount() - Get total number of pages in image
Save Operations
SaveToPng(FileInfo/string) - Save image as PNG
SaveToTiff(string) - Save image as TIFF
SaveToJpeg(string/Stream, double quality = 1) - Save as JPEG with quality (0.0-1.0)
Conversions
ToIcon(int targetRes = 0) - Convert image to Icon
ToBase64DataUri() - Convert to Base64 data URI string
FromBase64DataUri(string) (static-like extension on string) - Create image from Base64 data URI
Image Processing
MakeGrayscale() - Convert image to grayscale
Threshold(byte threshold = 127) - Apply threshold filter (black/white)
ApplyPixelProcessor(Func<Color, Color> processor) - Apply custom pixel transformation function
MirrorAlongX() - Mirror horizontally
MirrorAlongY() - Mirror vertically
Resize(int longSide) - Resize to fit within square, keep aspect
Resize(int longSide, Color fillColor) - Resize with fill color
Resize(int width, int height, bool keepAspect = true, Color? fillColor = null) - Resize with options
Resize(int width = -1, int height = -1, InterpolationMode = Default) - Resize with interpolation
Rotate(float angle) - Rotate image by angle
GetRectangle(Rectangle) - Extract rectangular region
ReplaceColorWithTransparency(Color) - Replace specific color with transparency
Color Extensions (Color)
Color Space Conversion
Rgb / RgbNormalized - Convert to RGB color space
Hsl / HslNormalized - Convert to HSL (Hue, Saturation, Lightness)
Hsv / HsvNormalized - Convert to HSV (Hue, Saturation, Value)
Hwb / HwbNormalized - Convert to HWB (Hue, Whiteness, Blackness)
Cmyk / CmykNormalized - Convert to CMYK (Cyan, Magenta, Yellow, Key)
Xyz / XyzNormalized - Convert to CIE XYZ tristimulus values
Lab / LabNormalized - Convert to CIE Lab* perceptual color space
Yuv / YuvNormalized - Convert to YUV (Luma + chrominance)
YCbCr / YCbCrNormalized - Convert to YCbCr digital video encoding
Din99 / Din99Normalized - Convert to DIN99 (DIN 6176) perceptual space
Color Comparison
IsLike(Color other, byte luminanceDelta = 24, byte chromaUDelta = 7, byte chromaVDelta = 6) - Compare colors in YUV space with tolerance
IsLikeNaive(Color other, int tolerance = 2) - Simple RGB comparison with tolerance
Color Blending
BlendWith(Color other, float current, float max) - Interpolate between two colors
Graphics Extensions (Graphics)
DrawString(float x, float y, string text, Font font, Brush brush, ContentAlignment anchor) - Draw text with anchor positioning
DrawCross(float/int x, float/int y, float/int size, Pen pen) - Draw cross marker (4 overloads including Point/PointF)
DrawCircle(Pen pen, float centerX, float centerY, float radius) - Draw circle outline
FillCircle(Brush brush, float centerX, float centerY, float radius) - Draw filled circle
Rectangle Extensions (Rectangle)
MultiplyBy(int factor) - Scale rectangle uniformly
MultiplyBy(int xfactor, int yfactor) - Scale rectangle with different X/Y factors
CollidesWith(Rectangle/RectangleF) - Check rectangle collision
CollidesWith(Point/PointF) - Check if point is inside
CollidesWith(int x, int y) / CollidesWith(float x, float y) - Check if coordinates are inside
Center() - Get center point
SetLeft/SetRight/SetTop/SetBottom(int) - Create new rectangle with modified edge
RectangleF Extensions (RectangleF)
(Similar methods to Rectangle, for floating-point rectangles)
Size Extensions (Size)
Center() - Get center point
Point Extensions (Point)
(Generated from T4 template - numeric operations on points)
FileInfo Extensions (FileInfo)
GetIcon(bool smallIcon = false, bool linkOverlay = false) - Get Windows shell icon for file
- Uses native SHGetFileInfo API
Custom Types
IBitmapLocker Interface
Format-specific bitmap pixel access implementations providing optimized read/write access to bitmap data. The correct locker is automatically selected based on the bitmap's pixel format when calling Lock().
| Locker |
Pixel Format |
Bits |
Description |
| Argb16161616BitmapLocker |
Format64bppArgb |
64 |
High dynamic range ARGB (16 bits/channel) |
| Argb1555BitmapLocker |
Format16bppArgb1555 |
16 |
1-bit alpha + 5 bits RGB |
| Argb8888BitmapLocker |
Format32bppArgb |
32 |
Standard 32-bit ARGB (8 bits/channel) |
| Gray16BitmapLocker |
Format16bppGrayScale |
16 |
16-bit grayscale |
| Indexed1BitmapLocker |
Format1bppIndexed |
1 |
Monochrome indexed (2 colors) |
| Indexed4BitmapLocker |
Format4bppIndexed |
4 |
16-color indexed |
| Indexed8BitmapLocker |
Format8bppIndexed |
8 |
256-color indexed |
| PArgb16161616BitmapLocker |
Format64bppPArgb |
64 |
Premultiplied alpha 64-bit |
| PArgb8888BitmapLocker |
Format32bppPArgb |
32 |
Premultiplied alpha 32-bit |
| Rgb161616BitmapLocker |
Format48bppRgb |
48 |
High dynamic range RGB (16 bits/channel) |
| Rgb555BitmapLocker |
Format16bppRgb555 |
16 |
5 bits per RGB channel |
| Rgb565BitmapLocker |
Format16bppRgb565 |
16 |
5-6-5 bits RGB |
| Rgb888BitmapLocker |
Format24bppRgb |
24 |
Standard 24-bit RGB |
| Rgb888XBitmapLocker |
Format32bppRgb |
32 |
24-bit RGB with padding byte |
| SubRegionBitmapLocker |
Any |
- |
Wraps another locker for sub-region access |
| UnsupportedDrawingBitmapLocker |
Other |
- |
Fallback using GetPixel/SetPixel |
Each locker provides:
- Direct pointer access to pixel data via
BitmapData
- Indexed pixel access via
this[x, y]
- Fast
GetPixelBgra8888() and SetPixelBgra8888() methods
- Automatic disposal via
IDisposable
Color Spaces (System.Drawing.ColorSpaces)
A comprehensive color space conversion and comparison library with zero-cost generic abstractions.
Color Space Types
| Type |
Components |
Description |
Cmyk / CmykNormalized |
C, M, Y, K |
Subtractive printing model |
Din99 / Din99Normalized |
L, a, b |
DIN 6176 perceptual space |
Hsl / HslNormalized |
H, S, L |
Hue, Saturation, Lightness |
Hsv / HsvNormalized |
H, S, V |
Hue, Saturation, Value |
Hwb / HwbNormalized |
H, W, B |
Hue, Whiteness, Blackness |
Lab / LabNormalized |
L, a, b |
CIE Lab* perceptual color space |
Rgb / RgbNormalized |
R, G, B |
Standard RGB color model |
Xyz / XyzNormalized |
X, Y, Z |
CIE XYZ tristimulus values |
YCbCr / YCbCrNormalized |
Y, Cb, Cr |
Digital video color encoding |
Yuv / YuvNormalized |
Y, U, V |
Luma + chrominance (PAL/NTSC) |
Distance Calculators (Hawkynt.ColorProcessing.Metrics)
Color metrics implementing IColorMetric<T> for palette lookups, quantization, and color comparison.
Generic Metrics
| Calculator |
Color Space |
Squared Variant |
Description |
Euclidean3F<TKey> |
Any 3-component float |
EuclideanSquared3F<TKey> |
$\sqrt{\Delta c_1^2 + \Delta c_2^2 + \Delta c_3^2}$ |
Euclidean4F<TKey> |
Any 4-component float |
EuclideanSquared4F<TKey> |
$\sqrt{\Delta c_1^2 + \Delta c_2^2 + \Delta c_3^2 + \Delta c_4^2}$ |
Euclidean3B<TKey> |
Any 3-component byte |
EuclideanSquared3B<TKey> |
Byte version (0-255 per channel) |
Euclidean4B<TKey> |
Any 4-component byte |
EuclideanSquared4B<TKey> |
Byte version with alpha |
Chebyshev3F<TKey> |
Any 3-component float |
- |
$\max(\lvert\Delta c_1\rvert, \lvert\Delta c_2\rvert, \lvert\Delta c_3\rvert)$ |
Chebyshev4F<TKey> |
Any 4-component float |
- |
$\max(\lvert\Delta c_1\rvert, \lvert\Delta c_2\rvert, \lvert\Delta c_3\rvert, \lvert\Delta c_4\rvert)$ |
Chebyshev3B<TKey> |
Any 3-component byte |
- |
Byte version |
Chebyshev4B<TKey> |
Any 4-component byte |
- |
Byte version with alpha |
Manhattan3F<TKey> |
Any 3-component float |
- |
$\lvert\Delta c_1\rvert + \lvert\Delta c_2\rvert + \lvert\Delta c_3\rvert$ |
Manhattan4F<TKey> |
Any 4-component float |
- |
$\lvert\Delta c_1\rvert + \lvert\Delta c_2\rvert + \lvert\Delta c_3\rvert + \lvert\Delta c_4\rvert$ |
Weighted Metrics
| Calculator |
Squared Variant |
Description |
WeightedEuclidean3F<TKey> |
WeightedEuclideanSquared3F<TKey> |
$\sqrt{w_1 \Delta c_1^2 + w_2 \Delta c_2^2 + w_3 \Delta c_3^2}$ |
WeightedEuclidean4F<TKey> |
WeightedEuclideanSquared4F<TKey> |
$\sqrt{w_1 \Delta c_1^2 + w_2 \Delta c_2^2 + w_3 \Delta c_3^2 + w_4 \Delta c_4^2}$ |
WeightedChebyshev3F<TKey> |
- |
$\max(w_1\lvert\Delta c_1\rvert, w_2\lvert\Delta c_2\rvert, w_3\lvert\Delta c_3\rvert)$ |
WeightedManhattan3F<TKey> |
- |
$w_1\lvert\Delta c_1\rvert + w_2\lvert\Delta c_2\rvert + w_3\lvert\Delta c_3\rvert$ |
Perceptual Lab Metrics (Hawkynt.ColorProcessing.Metrics.Lab)
RGB-Specific Metrics (Hawkynt.ColorProcessing.Metrics.Rgb)
| Calculator |
Squared Variant |
Reference |
Description |
CompuPhase |
CompuPhaseSquared |
Redmean |
Weighted RGB approximation using mean red |
PngQuant |
PngQuantSquared |
pngquant |
Considers blending on black/white backgrounds |
Equality Comparators
| Comparator |
Description |
ExactEquality<TKey> |
Exact bit-level match only |
ThresholdEquality<TKey> |
Match within configurable tolerance |
Note: Squared variants are faster (no sqrt) when only relative comparison is needed.
Palette Lookup
The PaletteLookup<TWork, TMetric> struct provides efficient nearest-neighbor color matching with automatic caching.
Members
| Member |
Type |
Description |
Count |
int |
Number of colors in the palette |
this[int index] |
TWork |
Get palette color at index |
FindNearest(in TWork color) |
int |
Find index of nearest palette color |
FindNearestColor(in TWork color) |
TWork |
Find nearest palette color directly |
FindNearest(in TWork color, out TWork nearest) |
int |
Get both index and nearest color |
Usage
using Hawkynt.ColorProcessing;
using Hawkynt.ColorProcessing.Metrics;
using Hawkynt.ColorProcessing.Working;
// Create palette lookup with a metric
var lookup = new PaletteLookup<LinearRgbaF, EuclideanSquared4F<LinearRgbaF>>(
workPalette,
default);
// Get palette size
int paletteSize = lookup.Count;
// Access palette colors directly
LinearRgbaF firstColor = lookup[0];
// Find nearest by index
int index = lookup.FindNearest(targetColor);
// Find nearest color directly
LinearRgbaF nearest = lookup.FindNearestColor(targetColor);
// Get both index and color in one call
int idx = lookup.FindNearest(targetColor, out LinearRgbaF nearestColor);
// Efficient batch processing - results are cached automatically
foreach (var pixel in imagePixels) {
var paletteIndex = lookup.FindNearest(pixel); // O(1) for repeated colors
}
// Using different metrics
var labLookup = new PaletteLookup<LabF, CIEDE2000>(labPalette, default);
var rgbLookup = new PaletteLookup<LinearRgbF, CompuPhaseSquared>(rgbPalette, default);
Color Interpolation (System.Drawing.ColorSpaces.Interpolation)
| Type |
Description |
CircularHueLerp<T> |
Hue-aware interpolation for HSL/HSV/HWB |
ColorGradient<T> |
Multi-stop gradient with configurable interpolation |
ColorLerp<T> |
Linear interpolation in any 3-component color space |
ColorLerp4<T> |
Linear interpolation in 4-component spaces (CMYK) |
using System.Drawing.ColorSpaces.Interpolation;
// Interpolate in perceptual Lab space
var lerp = new ColorLerp<Lab>();
var midpoint = lerp.Lerp(color1, color2, 0.5f);
// Create smooth gradients
var gradient = new ColorGradient<Hsl>(Color.Red, Color.Blue);
var colors = gradient.GetColors(10); // 10 evenly-spaced colors
Color Quantization
Quantizers reduce the number of colors in an image to generate optimized palettes. They analyze color distribution and select representative colors that best preserve visual quality.
Adaptive Quantizers
Adaptive quantizers analyze the image to generate an optimal palette for each specific image.
Quantizer Parameters
| Quantizer |
Parameter |
Type |
Default |
Range |
Description |
| PngQuantQuantizer |
MedianCutIterations |
int |
3 |
1-10 |
Median Cut passes with weight adjustment |
|
KMeansIterations |
int |
10 |
1-100 |
K-means/Voronoi refinement iterations |
|
ConvergenceThreshold |
float |
0.0001f |
0.00001-0.01 |
K-means convergence threshold |
|
ErrorBoostFactor |
float |
2.0f |
1-5 |
Weight boost for underrepresented colors |
| KMeansQuantizer |
MaxIterations |
int |
100 |
10-1000 |
Maximum clustering iterations |
|
ConvergenceThreshold |
float |
0.001f |
0.0001-0.1 |
Stop when centroids move less than this |
| BisectingKMeansQuantizer |
MaxIterationsPerSplit |
int |
10 |
1-50 |
K-means iterations per bisection |
|
BisectionTrials |
int |
3 |
1-10 |
Trials per split (keeps best result) |
|
ConvergenceThreshold |
float |
0.001f |
0.0001-0.1 |
Stop when centroids move less than this |
| IncrementalKMeansQuantizer |
RefinementPasses |
int |
3 |
0-10 |
Additional refinement passes after initial |
| GaussianMixtureQuantizer |
MaxIterations |
int |
50 |
10-500 |
Maximum EM iterations |
|
ConvergenceThreshold |
float |
0.0001f |
0.00001-0.01 |
Log-likelihood convergence threshold |
|
MinVariance |
float |
0.0001f |
0.00001-0.01 |
Minimum variance (prevents singular matrices) |
|
MaxSampleSize |
int |
10000 |
1000-100000 |
Maximum histogram entries to process |
| ColorQuantizationNetworkQuantizer |
MaxEpochs |
int |
100 |
10-500 |
Training epochs |
|
InitialLearningRate |
float |
0.3f |
0.01-1.0 |
Initial learning rate |
|
ConscienceFactor |
float |
0.1f |
0-1 |
Balanced neuron usage factor |
|
UseFrequencySensitive |
bool |
true |
- |
Enable frequency-sensitive learning |
|
MaxSampleSize |
int |
10000 |
1000-100000 |
Maximum histogram entries to process |
| AduQuantizer |
IterationCount |
int |
10 |
1-100 |
Competitive learning iterations |
| NeuquantQuantizer |
MaxIterations |
int |
100 |
1-1000 |
Network training iterations |
|
InitialAlpha |
float |
0.1f |
0.01-1.0 |
Initial learning rate |
| SpatialColorQuantizer |
MaxIterations |
int |
100 |
10-500 |
Annealing iterations |
|
SpatialWeight |
float |
0.5f |
0-1 |
Weight for spatial coherence |
|
InitialTemperature |
float |
1.0f |
0.1-10 |
Starting annealing temperature |
| FuzzyCMeansQuantizer |
MaxIterations |
int |
100 |
10-500 |
Clustering iterations |
|
Fuzziness |
float |
2.0f |
1.1-5 |
Cluster overlap (higher = softer) |
|
MaxSampleSize |
int |
10000 |
1000-100000 |
Maximum histogram entries to process |
| HierarchicalCompetitiveLearningQuantizer |
EpochsPerSplit |
int |
5 |
1-20 |
CL training epochs after each cluster split |
|
InitialLearningRate |
float |
0.1f |
0.01-0.5 |
Initial learning rate for CL |
|
MaxSampleSize |
int |
8192 |
1000-100000 |
Maximum histogram entries to process |
| GeneticCMeansQuantizer |
PopulationSize |
int |
20 |
10-100 |
Number of candidate palettes |
|
Generations |
int |
50 |
10-500 |
Number of generations to evolve |
|
TournamentSize |
int |
3 |
2-10 |
Tournament selection size |
|
MutationSigma |
float |
10.0f |
1-50 |
Gaussian mutation standard deviation |
|
EliteCount |
int |
2 |
0-10 |
Elite individuals preserved each generation |
|
CMeansIterationsPerOffspring |
int |
3 |
1-10 |
C-Means iterations per offspring |
|
MaxSampleSize |
int |
10000 |
1000-100000 |
Maximum histogram entries to process |
| EnhancedOctreeQuantizer |
MaxLevel |
int |
5 |
3-7 |
Tree depth (2^level max leaves) |
|
ReservedLevel2Colors |
int |
64 |
0-128 |
Colors reserved at level 2 for coverage |
| OctreeSomQuantizer |
MaxEpochs |
int |
50 |
10-200 |
SOM training epochs |
|
WinnerLearningRate |
float |
0.1f |
0.01-0.5 |
Learning rate for BMU |
|
NeighborLearningRate |
float |
0.001f |
0.0001-0.01 |
Learning rate for neighbors (1% of winner) |
|
MaxSampleSize |
int |
8192 |
1000-100000 |
Maximum histogram entries to process |
| HuffmanQuantizer |
CandidatesToExamine |
int |
20 |
5-100 |
Top candidates for merge selection |
|
SimilarityWeight |
float |
10000.0f |
100-100000 |
Balance frequency vs. similarity in merge |
Quantizer Wrappers
Wrappers that enhance other quantizers by applying additional preprocessing or post-processing. Wrappers can be chained for combined effects.
Preprocessing Wrappers
| Wrapper |
Description |
PcaQuantizerWrapper |
Transforms colors to PCA-aligned space before quantization. Ref |
BitReductionWrapper |
Reduces color precision by masking off LSBs, creating posterized/retro effects and faster quantization |
Postprocessing Wrappers
Wrapper Parameters
| Wrapper |
Parameter |
Type |
Default |
Range |
Description |
| BitReductionWrapper |
bitsToRemove |
int |
1 |
1-7 |
LSBs to mask off per component (1=128 levels) |
| KMeansRefinementWrapper |
iterations |
int |
10 |
1-100 |
K-means refinement iterations |
| AcoRefinementWrapper |
antCount |
int |
20 |
1-100 |
Number of ants exploring solutions |
|
iterations |
int |
50 |
1-500 |
ACO iterations |
|
evaporationRate |
double |
0.1 |
0.0-1.0 |
Pheromone evaporation rate |
|
seed |
int? |
null |
- |
Random seed for reproducibility |
// Wrap a quantizer with PCA preprocessing
var pcaEnhanced = new PcaQuantizerWrapper<WuQuantizer>(new WuQuantizer());
// Add K-means refinement to any quantizer
var refined = new KMeansRefinementWrapper<MedianCutQuantizer>(new MedianCutQuantizer(), iterations: 10);
// Use ACO for complex color distributions (slower but may escape local optima)
var acoRefined = new AcoRefinementWrapper<OctreeQuantizer>(
new OctreeQuantizer(),
antCount: 20,
iterations: 50,
seed: 42 // for reproducible results
);
// Reduce color precision for retro/posterized effects (4 bits removed = 16 levels per channel)
var posterized = new BitReductionWrapper<MedianCutQuantizer>(new MedianCutQuantizer(), bitsToRemove: 4);
// Chain wrappers for combined effects: BitReduction → KMeans → PCA → Octree
var chained = new BitReductionWrapper<KMeansRefinementWrapper<PcaQuantizerWrapper<OctreeQuantizer>>>(
new KMeansRefinementWrapper<PcaQuantizerWrapper<OctreeQuantizer>>(
new PcaQuantizerWrapper<OctreeQuantizer>(new OctreeQuantizer()),
iterations: 5),
bitsToRemove: 2
);
Fixed Palette Quantizers
Fixed palette quantizers use predefined color palettes for specific platforms or standards.
Quantizer Usage
using Hawkynt.ColorProcessing.Quantization;
// Generate palette from colors
var quantizer = new WuQuantizer();
Bgra8888[] palette = quantizer.GeneratePalette(colors, 16);
// Generate from histogram (weighted by frequency)
var histogram = new List<(Bgra8888 color, uint count)> { ... };
Bgra8888[] palette = quantizer.GeneratePalette(histogram, 256);
// Fixed palette
var egaPalette = new Ega16Quantizer().GetPalette();
// Reduce colors with quantization
using var reduced = bitmap.ReduceColors(new WuQuantizer(), ErrorDiffusion.FloydSteinberg, 16);
// High-quality PngQuant-style quantization (variance median cut + K-means refinement)
var pngQuant = new PngQuantQuantizer {
MedianCutIterations = 3, // Iterations with weight adjustment for underrepresented colors
KMeansIterations = 10, // Voronoi/K-means refinement passes
ErrorBoostFactor = 2.0f // Boost for poorly quantized colors
};
using var pngResult = bitmap.ReduceColors(pngQuant, ErrorDiffusion.FloydSteinberg, 256);
K-Means Color Metrics
The KMeansQuantizer supports any IColorMetric<Bgra8888> for clustering. See Color Metrics for all available metrics.
// Default K-Means (squared Euclidean - fastest)
var kmeans = new KMeansQuantizer();
// With perceptual Lab-based distance
var perceptual = new KMeansQuantizer<CIEDE2000>();
// With weighted RGB perception
var weighted = new KMeansQuantizer<PngQuantDistance>(default);
// With custom iterations
var custom = new KMeansQuantizer { MaxIterations = 200, ConvergenceThreshold = 0.0001f };
Dithering
Error diffusion dithering distributes quantization error to neighboring pixels for smoother gradients.
Error-Diffusion Ditherers
| Ditherer |
Author |
Year |
Neighbors |
Reference |
Atkinson |
Bill Atkinson |
1984 |
6 |
Apple MacPaint, 75% error diffusion. Ref |
JarvisJudiceNinke |
J.F. Jarvis, C.N. Judice, W.H. Ninke |
1976 |
12 |
CGIP vol. 5. Ref |
Pigeon |
Steven Pigeon |
2013 |
7 |
Blog post with analysis. Ref |
ShiauFan |
J.N. Shiau, Z. Fan |
1993 |
4 |
US Patent 5,353,127. Ref |
ShiauFan2 |
J.N. Shiau, Z. Fan |
1993 |
5 |
US Patent 5,353,127. Ref |
StevensonArce |
R.L. Stevenson, G.R. Arce |
1985 |
12 |
JOSA A vol. 2. Ref |
Stucki |
P. Stucki |
1981 |
12 |
IBM Research RZ1060. Ref |
Burkes |
D. Burkes |
1988 |
7 |
CIS Graphics Support Forum, LIB 15. Ref |
Diagonal |
- |
- |
1 |
Single diagonal neighbor |
Diamond |
- |
- |
8 |
Symmetric diamond pattern |
DoubleDown |
- |
- |
3 |
Two rows down |
Down |
- |
- |
1 |
Single pixel below |
EqualFloydSteinberg |
- |
- |
4 |
Equal weight distribution variant |
FalseFloydSteinberg |
- |
- |
3 |
Simplified 3-neighbor variant |
Fan93 |
Z. Fan |
1992 |
4 |
SPIE'92, "A Simple Modification of Error Diffusion Weights" |
FloydSteinberg |
R.W. Floyd, L. Steinberg |
1976 |
4 |
Proc. SID vol. 17, "An Adaptive Algorithm for Spatial Greyscale" |
HorizontalDiamond |
- |
- |
6 |
Diamond with horizontal bias |
Sierra |
Frankie Sierra |
1989 |
10 |
Three-line filter, King's Quest era |
SierraLite |
Frankie Sierra |
1990 |
3 |
Filter Lite - minimal variant |
Simple |
- |
- |
1 |
Single neighbor diffusion |
TwoD |
- |
- |
2 |
Simple 2-neighbor |
TwoRowSierra |
Frankie Sierra |
1990 |
7 |
Two-row variant |
VerticalDiamond |
- |
- |
8 |
Diamond with vertical bias |
Ordered Dithering
Ordered dithering uses threshold matrices to determine pixel output. Unlike error diffusion, pixels can be processed independently (parallelizable).
// Ordered dithering with Bayer matrix
var ditherer = OrderedDitherer.Bayer8x8;
// Adjust strength (0.0 - 1.0)
var reduced = OrderedDitherer.Bayer4x4.WithStrength(0.5f);
Noise Dithering
Noise dithering adds random or pseudo-random thresholds before quantization. Can be processed in parallel.
| Ditherer |
Description |
WhiteNoise |
Uniform random threshold with equal energy at all frequencies |
BlueNoise |
Spatially-filtered noise with reduced low-frequency content |
PinkNoise |
1/f noise, equal energy per octave, more natural-looking |
BrownNoise |
1/f² noise (Brownian motion), strong low-frequency, smooth organic |
VioletNoise |
f noise, high-frequency emphasis, sharp textured appearance |
GreyNoise |
Perceptually uniform noise adjusted for human vision response |
InterleavedGradientNoise |
Deterministic pseudo-random noise for temporal AA. Ref |
// Noise dithering
var ditherer = NoiseDitherer.BlueNoise;
// Other noise types
var pink = NoiseDitherer.PinkNoise; // More natural than white
var brown = NoiseDitherer.BrownNoise; // Smooth, organic appearance
var violet = NoiseDitherer.VioletNoise; // Sharp, textured
var grey = NoiseDitherer.GreyNoise; // Perceptually uniform
// Adjust strength and seed
var custom = NoiseDitherer.WhiteNoise.WithStrength(0.8f).WithSeed(12345);
Advanced Ditherers
High-quality ditherers for specialized applications with superior visual quality.
Adaptive Ditherers
Ditherers that analyze local image content to adjust their behavior.
| Ditherer |
Description |
SmartDitherer |
Automatically selects best ditherer based on local content |
AdaptiveDitherer |
Adjusts error diffusion strength based on local contrast |
AdaptiveMatrixDitherer |
Uses content-aware threshold matrices |
GradientAwareDitherer |
Preserves gradients while dithering flat areas |
StructureAwareDitherer |
Maintains structural features |
DebandingDitherer |
Specifically designed to reduce banding artifacts |
Ditherer Configuration
Error diffusion ditherers support two scan modes via separate zero-cost types:
ErrorDiffusion - Linear left-to-right scanning
ErrorDiffusionSerpentine - Alternating direction per row (reduces directional artifacts)
// Basic usage (linear scan)
var ditherer = ErrorDiffusion.FloydSteinberg;
// Serpentine scanning (returns ErrorDiffusionSerpentine type - zero-cost abstraction)
var serpentine = ErrorDiffusion.FloydSteinberg.Serpentine;
// Switch back to linear if needed
var linear = serpentine.Linear;
// Adjust strength (0.0 - 1.0)
var reduced = ErrorDiffusion.Atkinson.WithStrength(0.75f);
// Combine options (serpentine with reduced strength)
var custom = ErrorDiffusion.JarvisJudiceNinke.Serpentine.WithStrength(0.9f);
Riemersma Ditherer (Space-Filling Curves)
The Riemersma ditherer uses space-filling curves to traverse the image, maintaining spatial locality for better error diffusion. Interactive visualization
| Curve Type |
Subdivision |
Order Range |
Coverage per Order |
| Hilbert |
2×2 |
1-7 |
2ⁿ × 2ⁿ pixels |
| Peano |
3×3 |
1-5 |
3ⁿ × 3ⁿ pixels |
| Linear |
- |
- |
Serpentine scan |
// Pre-configured instances
var hilbert = RiemersmaDitherer.Default; // Hilbert curve, history size 16
var peano = RiemersmaDitherer.Peano; // Peano curve, history size 16
var linear = RiemersmaDitherer.LinearScan; // Simple serpentine
// Different history sizes (affects error decay)
var small = RiemersmaDitherer.Small; // History size 8 (faster)
var large = RiemersmaDitherer.Large; // History size 32 (higher quality)
// Custom configuration with explicit curve type
var customHilbert = new RiemersmaDitherer(16, SpaceFillingCurve.Hilbert);
var customPeano = new RiemersmaDitherer(16, SpaceFillingCurve.Peano);
// Specify exact curve order (auto-calculated if omitted)
var hilbertOrder4 = new RiemersmaDitherer(16, SpaceFillingCurve.Hilbert, 4); // 16×16 coverage
var peanoOrder3 = new RiemersmaDitherer(16, SpaceFillingCurve.Peano, 3); // 27×27 coverage
Image Scaling / Pixel Art Rescaling
The library provides a comprehensive collection of image scaling algorithms, from simple interpolation methods to sophisticated pixel art scalers and retro gaming effects.
Upscaling Methods
using var source = new Bitmap("sprite.png");
// Pixel art scalers
using var scaled2x = source.Upscale(Eagle.X2);
using var scaled3x = source.Upscale(SuperEagle.X2);
using var hq4x = source.Upscale(Hqnx.X4);
// Anti-aliased scaling
using var smaa = source.Upscale(Smaa.X2);
// Edge-preserving smoothing
using var bilateral = source.Upscale(Bilateral.X2);
Available Scalers
Anti-Aliasing
| Scaler |
Author |
Year |
Scales |
Description |
Smaa |
Jorge Jimenez et al. |
2012 |
2x, 3x, 4x |
Subpixel Morphological Anti-Aliasing - detects edges using luma gradients and applies weighted pixel blending. Presets: X2, X3, X4 with .Low, .Medium, .High, .Ultra quality variants. |
Fxaa |
Timothy Lottes/NVIDIA |
2009 |
2x, 3x, 4x |
Fast Approximate Anti-Aliasing using luma-based edge detection. Ref |
Mlaa |
Alexander Reshetov |
2009 |
2x, 3x, 4x |
Morphological Anti-Aliasing using pattern detection (L, Z, U shapes). Ref |
ReverseAa |
Christoph Feck / Hyllian |
2011 |
2x |
Reverse anti-aliasing using gradient-based tilt computation for smooth edges |
Edge-Preserving Filters
| Scaler |
Author |
Year |
Scales |
Description |
Bilateral |
Tomasi & Manduchi |
1998 |
2x, 3x, 4x |
Edge-preserving smoothing filter combining spatial and range weighting. Presets: X2, X3, X4 with .Soft (σs=2.0, σr=0.3) and .Sharp (σs=1.0, σr=0.1) variants. |
Edge-Directed Interpolation
| Scaler |
Author |
Year |
Scales |
Description |
Nedi |
Xin Li & M.T. Orchard |
2001 |
2x, 3x, 4x |
New Edge-Directed Interpolation using local autocorrelation for adaptive edge-aware upscaling. Ref |
Nnedi3 |
tritical |
2010 |
2x, 3x, 4x |
Neural Network Edge Directed Interpolation using trained weights for high-quality edge-directed scaling |
SuperXbr |
Hyllian |
2015 |
2x |
Super-Scale2x Refinement with 2-pass edge-directed scaling and anti-ringing |
Pixel Art Scalers
| Scaler |
Author |
Year |
Scales |
Description |
Eagle |
- |
1990s |
2x, 3x |
Classic pixel doubling with corner detection |
SuperEagle |
Kreed |
2001 |
2x |
Enhanced Eagle with better diagonal handling |
Super2xSaI |
Kreed |
1999 |
2x |
2x Scale and Interpolation engine |
Epx / Scale2x |
Andrea Mazzoleni |
2001 |
2x, 3x |
Edge-preserving pixel expansion |
Hqnx |
Maxim Stepin |
2003 |
2x, 3x, 4x |
High-quality magnification using YUV comparisons |
Lqnx |
- |
- |
2x, 3x, 4x |
Low-quality simplified variant of HQnx. Ref |
Xbr |
Hyllian |
2011 |
2x, 3x, 4x |
xBR (scale By Rules) edge-detection scaler |
Xbrz |
Zenju |
2012 |
2x-6x |
Enhanced xBR with improved edge handling |
Sal |
Kreed |
2001 |
2x |
Simple Assembly Language scaler with anti-aliasing |
Mmpx |
Morgan McGuire |
2021 |
2x |
Modern AI-inspired pixel art scaling |
Omniscale |
libretro |
2015 |
2x-6x |
Multi-method hybrid scaler |
RotSprite |
Xenowhirl |
2007 |
2x-4x |
Rotation-aware pixel art scaling |
Clean |
- |
- |
2x, 3x |
Clean edge pixel art scaler |
TriplePoint |
Hawkynt |
2011 |
2x, 3x |
3x scaler using diagonal color analysis |
ScaleNxSfx |
Sp00kyFox |
2013 |
2x, 3x |
ScaleNx with effects (corner blending) |
ScaleNxPlus |
Hawkynt |
2011 |
2x, 3x |
Enhanced ScaleNx with better diagonals |
ScaleHq |
Hawkynt |
2011 |
2x, 3x |
HQ-style pixel art scaler |
EpxB |
SNES9x Team |
2003 |
2x |
Enhanced EPX with complex edge detection |
EpxC |
- |
- |
2x |
EPX variant with additional edge cases. Ref |
Des |
FNES Team |
2000 |
1x |
Diagonal Edge Scaling filter (pre-processing) |
Des2 |
FNES Team |
2000 |
2x |
DES with 2x scaling using edge-directed scaling |
ScaleFx |
Sp00kyFox |
2014 |
3x |
Scale3x with enhanced edge detection |
Simple & Utility Scalers
| Scaler |
Author |
Scales |
Description |
NearestNeighbor |
- |
Nx |
Simple pixel duplication |
Bilinear |
- |
Nx |
Linear interpolation |
Bicubic |
- |
Nx |
Cubic spline interpolation |
Lanczos |
- |
Nx |
Sinc-windowed interpolation |
Normal |
- |
2x, 3x, 4x |
Fixed-factor pixel duplication |
Pixellate |
- |
Nx |
Pixelation/mosaic effect |
BilinearPlus |
VBA Team |
2x |
VBA weighted bilinear interpolation (5:2:1 weighting) |
SharpBilinear |
LibRetro |
2x, 3x, 4x |
Integer prescaling + bilinear for crisp pixels |
Quilez |
Inigo Quilez |
2x, 3x, 4x |
Quintic smoothstep interpolation for smooth gradients. Ref |
NearestNeighborPlus |
- |
2x, 3x, 4x |
Enhanced nearest neighbor with edge detection |
Soft |
- |
2x, 3x, 4x |
Soft interpolation with gentle blending |
SoftSmart |
- |
2x, 3x, 4x |
Smart soft interpolation with adaptive blending |
Cut |
- |
2x, 3x, 4x |
Cut-based scaling utility |
Ddt |
Sp00kyFox |
2x |
Diagonal De-interpolation Technique |
CatmullRom |
- |
2x, 3x, 4x |
Catmull-Rom spline interpolation (pixel-art variant) |
Retro Display Effects
| Scaler |
Author |
Scales |
Description |
DotMatrix |
ScummVM |
2x, 3x, 4x |
Dot-matrix display simulation with brightness falloff |
LcdGrid |
- |
2x, 3x, 4x |
LCD subpixel grid simulation |
ScanlineHorizontal |
Hawkynt |
2x1 |
CRT vertical scanline effect |
ScanlineVertical |
Hawkynt |
1x2 |
CRT horizontal scanline effect |
Tv2x / Tv3x / Tv4x |
- |
2x, 3x, 4x |
TV scanline simulation |
MameRgb |
MAME Team |
2x, 3x |
LCD RGB subpixel channel filter simulation |
MameAdvInterp |
MAME Team |
2x, 3x |
MAME advanced interpolation with scanline effect |
HawkyntTv |
Hawkynt |
2x, 3x, 4x |
TV effect with configurable scanline and phosphor patterns |
Scaler Configuration
Many scalers support configuration options:
// SMAA quality levels
using var low = source.Upscale(Smaa.X2.Low);
using var ultra = source.Upscale(Smaa.X2.Ultra);
// Bilateral filter parameters
using var soft = source.Upscale(Bilateral.X2.Soft); // σs=2.0, σr=0.3
using var sharp = source.Upscale(Bilateral.X2.Sharp); // σs=1.0, σr=0.1
using var custom = source.Upscale(new Bilateral(3, 1.5f, 0.2f));
// Scanline brightness
using var scanlines = source.Upscale(new ScanlineHorizontal(brightness: 0.3f));
Image Resamplers
High-quality interpolation filters for arbitrary scaling to any resolution. Unlike pixel art scalers that work at fixed integer factors, resamplers use mathematical kernels to compute pixel values at any scale.
Resampling Methods
using Hawkynt.ColorProcessing.Resizing.Resamplers;
// Generic type syntax (parameterless)
using var result = bitmap.Resample<Lanczos3>(newWidth, newHeight);
using var result = bitmap.Resample<MitchellNetravali>(newWidth, newHeight);
// Instance syntax (parameterized)
using var result = bitmap.Resample(new Bicubic(-0.75f), newWidth, newHeight);
using var result = bitmap.Resample(new Gaussian(sigma: 1.0f), newWidth, newHeight);
Basic Filters
Cubic Family
| Resampler |
Author |
Year |
Radius |
Parameters |
Reference |
Bicubic |
Robert Keys |
1981 |
2 |
a (-0.5f) |
Keys coefficient. Ref |
MitchellNetravali |
Mitchell, Netravali |
1988 |
2 |
b (1/3), c (1/3) |
Balanced cubic. Ref |
CatmullRom |
Catmull, Rom |
1974 |
2 |
None |
Sharp spline (B=0, C=0.5). Ref |
Robidoux |
Nicolas Robidoux |
2011 |
2 |
None |
Optimized for photos. Ref |
RobidouxSharp |
Nicolas Robidoux |
2011 |
2 |
None |
Sharper variant. Ref |
RobidouxSoft |
Nicolas Robidoux |
2011 |
2 |
None |
Smoother variant. Ref |
Lanczos Family
| Resampler |
Radius |
Sharpness |
Ringing |
Best For |
Lanczos2 |
2 |
Good |
Low |
General use. Ref |
Lanczos3 |
3 |
Very good |
Moderate |
Photos (recommended). Ref |
Lanczos4 |
4 |
Excellent |
Higher |
Maximum sharpness. Ref |
Lanczos5 |
5 |
Maximum |
Significant |
Special cases. Ref |
Jinc |
3+ |
Excellent |
Moderate |
2D (uses Bessel J₁). Ref |
B-Spline & O-MOMS
Spline & Window Filters
Lagrange Polynomial Interpolation
Note: Lagrange interpolation passes exactly through all sample points, making it suitable for applications
requiring exact value preservation. No prefiltering is required.
Edge-Directed & Modern Upscalers
| Resampler |
Author |
Year |
Radius |
Parameters |
Description |
Dcci |
Li, Orchard |
2001 |
2 |
cubicA, coherenceThreshold |
Edge-directed interpolation. Ref |
Fsr |
AMD |
2021 |
2 |
sharpness (0.5f) |
FidelityFX Super Resolution. Ref |
Ravu |
- |
2017 |
2 |
sharpness, antiRinging |
Robust Adaptive Video Upscaling. Ref |
Eedi2 |
tritical |
2005 |
2 |
- |
Enhanced Edge-Directed Interp. Ref |
KrigBilateral |
- |
- |
3 |
sigma, radius |
Kriging-based bilateral upscaling. Ref |
Specialized Filters
| Resampler |
Author |
Radius |
Parameters |
Description |
Gaussian |
Gauss |
2+ |
sigma (0.5f), radius |
Gaussian blur/interpolation. Ref |
NoHalo |
Nicolas Robidoux |
3 |
None |
Minimizes halo artifacts. Ref |
LoHalo |
Nicolas Robidoux |
3 |
None |
Low-halo variant. Ref |
MagicKernelSharp |
John Googol |
2 |
sharpness |
4-tap max sharpness kernel. Ref |
Resampler Parameters
| Resampler |
Parameter |
Type |
Default |
Range |
Description |
| Bicubic |
a |
float |
-0.5f |
-1 to 0 |
Keys coefficient (-0.5=standard, -0.75=sharper) |
| MitchellNetravali |
b |
float |
0.333f |
0-1 |
Blur parameter |
|
c |
float |
0.333f |
0-1 |
Ringing parameter |
| Kaiser |
radius |
int |
3 |
1-10 |
Filter radius |
|
beta |
float |
8.6f |
0-20 |
Shape parameter |
| Gaussian |
sigma |
float |
0.5f |
0.1-5 |
Standard deviation |
| Fsr |
sharpness |
float |
0.5f |
0-1 |
Sharpness level |
| Ravu |
sharpness |
float |
0.5f |
0-1 |
Sharpness level |
|
antiRinging |
float |
0.5f |
0-1 |
Anti-ringing strength |
| Dcci |
cubicA |
float |
-0.5f |
-1 to 0 |
Cubic coefficient |
|
coherenceThreshold |
float |
0.3f |
0-1 |
Edge detection threshold |
Resampler Usage Examples
// High-quality photo scaling
using var photo = bitmap.Resample<Lanczos3>(newWidth, newHeight);
// Balanced quality (recommended for most uses)
using var balanced = bitmap.Resample<MitchellNetravali>(newWidth, newHeight);
// Sharp results with custom bicubic
using var sharp = bitmap.Resample(new Bicubic(-0.75f), newWidth, newHeight);
// Edge-preserving upscaling
using var edges = bitmap.Resample(Dcci.Sharp, newWidth, newHeight);
// Modern AI-like upscaling
using var fsr = bitmap.Resample(Fsr.Sharp, newWidth, newHeight);
// Halo-free scaling
using var nohalo = bitmap.Resample<NoHalo>(newWidth, newHeight);
// Smooth Gaussian
using var smooth = bitmap.Resample(new Gaussian(1.0f), newWidth, newHeight);
Algorithm Coverage
This library provides comprehensive coverage of resampling algorithms from major image processing libraries:
| Source Library |
Algorithms Covered |
Notes |
| ImageMagick |
Point, Box, Triangle, Hermite, Cubic, Catrom, Mitchell, Lanczos, Spline, Gaussian, Kaiser, Blackman, Hann, Hamming, Jinc, Robidoux variants, Parzen (BSpline4), Lagrange, and all window functions |
Full coverage |
| FFmpeg libswscale |
Point, Bilinear, Bicubic, Lanczos, Spline16, Spline36, Gaussian, Sinc, Area |
Full coverage |
| GEGL |
NoHalo, LoHalo |
Full coverage |
| bisqwit.iki.fi |
Yliluoma 1-4, Knoll pattern dithering |
Full coverage |
Installation
dotnet add package FrameworkExtensions.System.Drawing
License
LGPL 3.0 or later - See LICENSE for details