ParallelPixels 6.0.0
dotnet add package ParallelPixels --version 6.0.0
NuGet\Install-Package ParallelPixels -Version 6.0.0
<PackageReference Include="ParallelPixels" Version="6.0.0" />
<PackageVersion Include="ParallelPixels" Version="6.0.0" />
<PackageReference Include="ParallelPixels" />
paket add ParallelPixels --version 6.0.0
#r "nuget: ParallelPixels, 6.0.0"
#:package ParallelPixels@6.0.0
#addin nuget:?package=ParallelPixels&version=6.0.0
#tool nuget:?package=ParallelPixels&version=6.0.0
ParallelPixels
ParallelPixels is a fully managed, cross-platform .NET library for decoding, encoding, transforming, and analyzing raster images. It is developed and maintained by Qius Solutions.
The design centers on a strongly typed Image<TPixel> model, pluggable codecs, a fluent processing pipeline (Mutate / Clone), rich metadata profiles, and a configurable memory allocator—suited to server, desktop, cloud, and embedded workloads.
| Target framework | .NET 10 (net10.0) |
| NuGet package | ParallelPixels |
| Root namespace | ParallelPixels |
| License | MIT |
Table of contents
- Why ParallelPixels
- Installation
- Quick start
- Core concepts
- Loading, identifying, and detecting formats
- Saving and encoding
- Pixel formats
- Supported image formats
- Processing pipeline
- Transforms
- Color filters
- Convolution
- Effects
- Quantization and dithering
- Binarization
- Drawing and overlays
- Normalization
- Graphics options and blending
- Metadata
- Color and color spaces
- Configuration and memory
- Advanced pixel access
- Advanced reference
- Common use cases
- Building from source
- Publishing to NuGet
- Repository layout
- Community and maintainers
Why ParallelPixels
- Fully managed — no native dependencies; runs anywhere .NET 10 runs.
- Typed pixels — work in
Rgba32,Rgb24,L8,Rgba64, vectors, and many packed formats without opaque “bitmap” blobs. - Codec breadth — JPEG, PNG/APNG, GIF, BMP, TIFF, TGA, WebP, PBM/PGM/PPM, and QOI out of the box.
- Processing as composition — chain resize, filters, convolution, dithering, drawing, and custom row processors.
- Production-minded memory — pooled allocators with configurable limits; optional contiguous buffers; decode-time resize to reduce peak RAM.
- Metadata first-class — EXIF, IPTC, ICC, XMP, CICP, plus format-specific metadata bags.
Installation
dotnet add package ParallelPixels
Primary namespaces:
using ParallelPixels;
using ParallelPixels.PixelFormats;
using ParallelPixels.Processing;
using ParallelPixels.Formats.Jpeg; // or Png, Gif, Webp, …
using ParallelPixels.Metadata;
Optional MSBuild global usings: set UseParallelPixels to true or enable and import the package’s ParallelPixels.props (ships under the package build/ folder). That adds:
ParallelPixelsParallelPixels.PixelFormatsParallelPixels.Processing
Quick start
using ParallelPixels;
using ParallelPixels.Formats.Jpeg;
using ParallelPixels.PixelFormats;
using ParallelPixels.Processing;
// Load → transform → save
using Image<Rgba32> image = Image.Load<Rgba32>("photo.jpg");
image.Mutate(ctx => ctx
.Resize(new ResizeOptions
{
Size = new Size(800, 0), // width 800, height preserves aspect
Mode = ResizeMode.Max,
Sampler = KnownResamplers.Lanczos3
})
.GaussianSharpen(1.2f)
.Brightness(1.05f));
image.Save("out.jpg", new JpegEncoder { Quality = 85 });
// Async decode/encode
await using FileStream input = File.OpenRead("photo.png");
using Image img = await Image.LoadAsync(input);
await img.SaveAsPngAsync("copy.png");
Thumbnail while decoding (less memory):
using Image image = Image.Load(new DecoderOptions
{
TargetSize = new Size(256, 256),
Sampler = KnownResamplers.Box
}, "huge.tiff");
Core concepts
Image and Image<TPixel>
| Type | Role |
|---|---|
Image |
Non-generic base: dimensions, metadata, frames, format-agnostic save helpers. |
Image<TPixel> |
Strongly typed pixel buffer where TPixel : unmanaged, IPixel<TPixel>. |
ImageFrame / ImageFrame<TPixel> |
Single frame (animated GIF/WebP/APNG, multipage TIFF). |
ImageFrameCollection / ImageFrameCollection<TPixel> |
Frame list on an image. |
IndexedImageFrame<TPixel> |
Palettized frame used during quantization. |
ImageInfo |
Lightweight identify result: size, pixel type info, metadata, per-frame metadata—without decoding full pixels. |
PixelAccessor<TPixel> |
Scoped row access helper for advanced scenarios. |
Images implement IDisposable. Prefer using so pooled memory returns to the allocator.
Frames and animation
Animated and multipage formats expose multiple frames via image.Frames. Frame and root metadata carry delay, disposal, loop counts, and format-specific fields (GifDisposalMethod, WebP blend/disposal, PNG/APNG blend methods, etc.).
Control how many frames are decoded with DecoderOptions.MaxFrames (default: effectively unlimited; minimum clamped to 1).
Configuration
Configuration controls codecs, parallelism, IO buffering, and memory:
| Property | Default | Purpose |
|---|---|---|
MaxDegreeOfParallelism |
Environment.ProcessorCount |
Parallelism for processors (-1 = unlimited via ParallelOptions semantics; must not be 0 or < -1). |
StreamProcessingBufferSize |
8096 |
Copy buffer size for stream IO. |
PreferContiguousImageBuffers |
false |
Prefer single contiguous pixel buffers when size allows. |
ReadOrigin |
ReadOrigin.Current |
Stream read position strategy for seekable sources. |
MemoryAllocator |
MemoryAllocator.Default |
Process-wide pool-backed allocator (customize carefully—prefer one busy instance). |
ImageFormatsManager |
preconfigured | Registry of detectors, decoders, encoders. |
Properties |
empty concurrent dictionary | Extensibility bag for processors/modules. |
Configure(IImageFormatConfigurationModule) |
— | Register additional format modules. |
Default formats registered by Configuration.Default:
PNG, JPEG, GIF, BMP, PBM, TGA, TIFF, WebP, QOI.
Clone configuration with configuration.Clone() when you need a shallow copy sharing the same format manager/allocator.
Loading, identifying, and detecting formats
All load paths accept optional DecoderOptions. Overloads exist for file path, stream, and ReadOnlySpan<byte>, sync and async where IO applies.
DecoderOptions (general)
| Property | Type | Default | Meaning |
|---|---|---|---|
Configuration |
Configuration |
Configuration.Default |
Codecs, allocator, parallelism. |
TargetSize |
Size? |
null |
Decode into a size using behavior equivalent to ResizeMode.Max. |
Sampler |
IResampler |
KnownResamplers.Box |
Resampler used when TargetSize scales. |
SkipMetadata |
bool |
false |
Skip reading encoded metadata. |
MaxFrames |
uint |
int.MaxValue (clamped ≥ 1) |
Max frames to decode. |
Specialized decoder options
Some formats expose ISpecializedDecoderOptions with GeneralOptions plus format-specific knobs:
| Type | Extra properties |
|---|---|
JpegDecoderOptions |
ResizeMode → JpegDecoderResizeMode: Combined, IdctOnly, ScaleOnly |
PngDecoderOptions |
PngCrcChunkHandling (default IgnoreNonCritical); MaxUncompressedAncillaryChunkSizeBytes (default 8 MB) |
WebpDecoderOptions |
BackgroundColorHandling: Standard / Ignore (animation canvas background) |
BmpDecoderOptions |
RleSkippedPixelHandling (default Black): Black, Transparent, FirstColorOfPalette |
Use specialized decoders via Image.Load overloads that take the specialized decoder instance, or register/custom decode pipelines as needed.
Identify vs load vs detect
IImageFormat format = Image.DetectFormat("file.bin");
ImageInfo info = Image.Identify("file.bin"); // headers + metadata, no full raster
using Image image = Image.Load("file.bin"); // boxed Image
using Image<Rgba32> typed = Image.Load<Rgba32>("file.bin");
Async variants: DetectFormatAsync, IdentifyAsync, LoadAsync / LoadAsync<TPixel>.
Raw pixel construction
Image<Rgba32> fromPixels = Image.LoadPixelData<Rgba32>(pixelSpan, width, height);
Image<Rgba32> wrapped = Image.WrapMemory<Rgba32>(memoryOwner, width, height);
// Also: Memory<T>, Span-backed, unmanaged pointers — see Image.WrapMemory overloads.
WrapMemory does not copy; the image views external memory. Lifetime of the owner must outlive (or be transferred carefully with) the Image.
Saving and encoding
Generic save
image.Save("out.png"); // format inferred from extension via ImageFormatsManager
image.Save(stream, PngFormat.Instance);
image.Save(stream, new PngEncoder { CompressionLevel = PngCompressionLevel.BestCompression });
await image.SaveAsync("out.webp", new WebpEncoder { Quality = 80 });
string b64 = image.ToBase64String(PngFormat.Instance);
Typed SaveAs* helpers
For each built-in format the library ships sync/async overloads for path and stream, with and without an explicit encoder instance:
| Method family | Encoder type |
|---|---|
SaveAsJpeg / SaveAsJpegAsync |
JpegEncoder |
SaveAsPng / SaveAsPngAsync |
PngEncoder |
SaveAsGif / SaveAsGifAsync |
GifEncoder |
SaveAsBmp / SaveAsBmpAsync |
BmpEncoder |
SaveAsTiff / SaveAsTiffAsync |
TiffEncoder |
SaveAsTga / SaveAsTgaAsync |
TgaEncoder |
SaveAsWebp / SaveAsWebpAsync |
WebpEncoder |
SaveAsPbm / SaveAsPbmAsync |
PbmEncoder |
SaveAsQoi / SaveAsQoiAsync |
QoiEncoder |
await image.SaveAsPngAsync("copy.png");
await image.SaveAsJpegAsync(stream, new JpegEncoder { Quality = 85 }, cancellationToken);
Encoder base options
All encoders:
| Property | Meaning |
|---|---|
SkipMetadata |
Do not write metadata profiles into the bitstream. |
Quantizing encoders (QuantizingImageEncoder — PNG palette modes, GIF, BMP indexed, TIFF palette):
| Property | Default | Meaning |
|---|---|---|
Quantizer |
format-dependent / null |
IQuantizer (see KnownQuantizers) |
PixelSamplingStrategy |
DefaultPixelSamplingStrategy |
How pixels are sampled when building palettes |
Encode is cancellable via EncodeAsync(..., CancellationToken). Non-seekable streams are buffered through a chunked memory stream using the image’s allocator.
Pixel formats
Pixels live under ParallelPixels.PixelFormats. Naming orders components least → most significant left to right (e.g. in Rgba32, R is the low byte, A the high byte).
Common “working” formats
| Type | Layout | Typical use |
|---|---|---|
Rgba32 |
8-bit RGBA | Default general-purpose |
Rgb24 |
8-bit RGB | Opaque photos |
Bgra32 / Argb32 / Abgr32 |
8-bit alternate orders | Interop with Windows/GDI-style buffers |
Bgr24 |
8-bit BGR | Interop |
Rgba64 / Rgb48 |
16-bit/channel | High bit-depth pipelines |
L8 / L16 |
Luminance | Grayscale |
La16 / La32 |
Luminance + alpha | |
A8 |
Alpha only | Masks |
RgbaVector |
Vector4 float |
HDR / filter math intermediates |
Packed / specialty formats
Bgr565, Bgra5551, Bgra4444, Rgba1010102, Rg32, Byte4, Short2, Short4, NormalizedByte2/4, NormalizedShort2/4, HalfSingle, HalfVector2, HalfVector4, and related packed vectors for GPU/interop scenarios.
All implement IPixel<TSelf>; packed types may also implement IPackedVector<TPacked>.
Blending modes (pixel compositing)
Used by drawing/overlays via GraphicsOptions:
Color (PixelColorBlendingMode): Normal, Multiply, Add, Subtract, Screen, Darken, Lighten, Overlay, HardLight
Alpha (PixelAlphaCompositionMode): SrcOver, Src, SrcAtop, SrcIn, SrcOut, Dest, DestAtop, DestOver, DestIn, DestOut, Clear, Xor
Supported image formats
Registered by default on Configuration.Default:
| Format | Extensions | MIME | Encoder | Decoder notes |
|---|---|---|---|---|
| JPEG | jpg, jpeg, jfif |
image/jpeg, image/pjpeg |
JpegEncoder |
Baseline/progressive family; specialized resize modes |
| PNG / APNG | png, apng |
image/png, image/apng |
PngEncoder |
CRC policy + ancillary chunk size limits |
| GIF | gif |
image/gif |
GifEncoder |
Animation, global/local palettes |
| BMP | bm, bmp, dip |
image/bmp, image/x-windows-bmp |
BmpEncoder |
RLE skip handling on decode |
| TIFF | tiff, tif |
image/tiff, image/tiff-fx |
TiffEncoder |
Broad compression/photometric coverage (see below) |
| TGA | tga, vda, icb, vst |
image/x-tga, image/x-targa |
TgaEncoder |
Optional RLE |
| WebP | webp |
image/webp |
WebpEncoder |
Lossy VP8, lossless VP8L, animation options |
| PBM family | pbm, pgm, ppm |
portable anymap MIME types | PbmEncoder |
Plain or binary PNM |
| QOI | qoi |
image/qoi, image/x-qoi, image/vnd.qoi |
QoiEncoder |
“Quite OK Image” |
JPEG — JpegEncoder / JpegDecoderOptions
| Encoder property | Type | Notes |
|---|---|---|
Quality |
int? |
1–100; when unset → EXIF/JPEG metadata quality if present, else 75 (Quantization.DefaultQualityFactor) |
Interleaved |
bool? |
When unset → metadata, else true (all components in one scan) |
ColorType |
JpegEncodingColor? |
When unset → metadata, else YCbCrRatio420 |
SkipMetadata |
bool |
Inherited |
JpegEncodingColor: YCbCrRatio420, YCbCrRatio444, YCbCrRatio422, YCbCrRatio411, YCbCrRatio410, Luminance, Rgb, Cmyk, Ycck
Decoder resize strategy (JpegDecoderResizeMode): Combined, IdctOnly, ScaleOnly — choose how IDCT/scale cooperate with DecoderOptions.TargetSize.
image.Save("out.jpg", new JpegEncoder
{
Quality = 90,
ColorType = JpegEncodingColor.YCbCrRatio420,
Interleaved = true
});
PNG — PngEncoder / PngDecoderOptions
| Encoder property | Type | Default / notes |
|---|---|---|
BitDepth |
PngBitDepth? |
Bit1, Bit2, Bit4, Bit8, Bit16 |
ColorType |
PngColorType? |
Grayscale, Rgb, Palette, GrayscaleWithAlpha, RgbWithAlpha |
FilterMethod |
PngFilterMethod? |
None, Sub, Up, Average, Paeth, Adaptive |
CompressionLevel |
PngCompressionLevel |
DefaultCompression (= Level6); Level0…Level9 / BestSpeed / BestCompression |
TextCompressionThreshold |
int |
1024 — compress text metadata above this length |
Gamma |
float? |
Optional gAMA |
Threshold |
byte |
Transparency threshold (byte.MaxValue) |
InterlaceMethod |
PngInterlaceMode? |
None, Adam7 |
ChunkFilter |
PngChunkFilter? |
Flags: exclude physical/gamma/exif/text/all |
TransparentColorMode |
PngTransparentColorMode |
Preserve / Clear (force transparent black for better compression) |
Quantizer / PixelSamplingStrategy |
from base | Used for palette encoding |
Decoder: PngCrcChunkHandling — IgnoreNone, IgnoreNonCritical (default), IgnoreData, IgnoreAll; MaxUncompressedAncillaryChunkSizeBytes default 8 MB.
GIF — GifEncoder
| Property | Notes |
|---|---|
ColorTableMode |
GifColorTableMode?: Global or Local |
Quantizer |
Palette generation for truecolor sources |
Animation timing/disposal live in frame/root metadata (GifDisposalMethod: Unspecified, NotDispose, RestoreToBackground, RestoreToPrevious).
BMP — BmpEncoder / BmpDecoderOptions
| Encoder property | Notes |
|---|---|
BitsPerPixel |
BmpBitsPerPixel?: Pixel1, Pixel2, Pixel4, Pixel8, Pixel16, Pixel24, Pixel32 |
SupportTransparency |
With 32 bpp, writes BITFIELDS V4 header instead of classic V3 |
Quantizer |
Default KnownQuantizers.Octree for indexed depths |
Decoder RLE skipped pixels: Black, Transparent, FirstColorOfPalette.
TIFF — TiffEncoder
| Property | Notes |
|---|---|
BitsPerPixel |
Bit1…Bit64 subset enum (Bit1, Bit4, Bit6, Bit8, … Bit64) |
Compression |
See compression table |
CompressionLevel |
Deflate levels via DeflateCompressionLevel |
PhotometricInterpretation |
RGB / palette / gray / bi-level preferred for encode |
HorizontalPredictor |
None, Horizontal, FloatingPoint — helps LZW/Deflate |
Quantizer |
Default KnownQuantizers.Octree |
Compression support (library notes):
| Compression | Encode | Decode |
|---|---|---|
| None | ✓ | ✓ |
| PackBits | ✓ | ✓ |
| LZW | ✓ | ✓ |
| Deflate | ✓ | ✓ |
| Old Deflate | ✓ | |
| Ccitt1D / Group3 / Group4 | ✓ | ✓ |
| JPEG (TechNote 2) | ✓ | ✓ |
| Old JPEG | ✓ (chunky) | |
| WebP-in-TIFF | ✓ |
Photometric (high level): encode focuses on WhiteIsZero, BlackIsZero, RGB (chunky), PaletteColor. Decode additionally covers planar RGB, Separated, YCbCr, CieLab, CMYK, tiled images, etc. Multipage decode currently expects frames of equal dimensions.
TGA — TgaEncoder
| Property | Default | Notes |
|---|---|---|
BitsPerPixel |
inferred | Pixel8, Pixel16, Pixel24, Pixel32 |
Compression |
RunLength |
or None |
WebP — WebpEncoder / WebpDecoderOptions
| Property | Default | Notes |
|---|---|---|
FileFormat |
lossy if unset | WebpFileFormatType.Lossy / Lossless |
Quality |
75 |
Lossy: size↔quality; Lossless: effort 0–100 |
Method |
Default (= Level4) |
Level0/Fastest … Level6/BestQuality |
UseAlphaCompression |
true |
Lossless alpha plane |
EntropyPasses |
1 |
Range 1–10 |
SpatialNoiseShaping |
50 |
0–100 |
FilterStrength |
60 |
Deblocking strength 0–100 |
TransparentColorMode |
Clear |
or Preserve |
NearLossless |
false |
Near-lossless preprocessing |
NearLosslessQuality |
100 |
0–100 |
Animation background: BackgroundColorHandling.Standard or Ignore.
PBM / PGM / PPM — PbmEncoder
| Property | Values |
|---|---|
Encoding |
Plain, Binary |
ColorType |
BlackAndWhite, Grayscale, Rgb |
ComponentType |
Bit, Byte, Short |
QOI — QoiEncoder
| Property | Notes |
|---|---|
Channels |
Rgb (3) or Rgba (4) — informative header field |
ColorSpace |
SrgbWithLinearAlpha or AllChannelsLinear — informative |
QoiDecoder is internal; consumers still decode via Image.Load / LoadAsync (the format module registers the decoder). There is no public Decode API on Image—use Load / Identify / DetectFormat.
Processing pipeline
All geometric and pixel processors hang off IImageProcessingContext.
// In-place
image.Mutate(ctx => ctx.Resize(800, 600).Grayscale());
// Non-destructive
using Image<Rgba32> copy = image.Clone(ctx => ctx.DetectEdges());
// Explicit configuration
image.Mutate(customConfig, ctx => ctx.GaussianBlur(3));
// Explicit processor instances
image.Mutate(new ResizeProcessor(new ResizeOptions { Size = new Size(100, 100) }, image.Size));
Most operations also accept an optional Rectangle to limit the source region. Filters that change dimensions (resize, crop, pad, rotate orthogonal, etc.) update the working image size as they run in the chain.
Transforms
Resize
ctx.Resize(800, 600);
ctx.Resize(800, 600, KnownResamplers.Lanczos3);
ctx.Resize(new ResizeOptions { /* … */ });
ResizeOptions
| Property | Default | Meaning |
|---|---|---|
Mode |
Crop |
See modes below |
Position |
Center |
Anchor when cropping/padding |
CenterCoordinates |
null |
Optional focal point |
Size |
— | Target size (0 on one axis preserves aspect in several overloads) |
Sampler |
Bicubic |
IResampler |
Compand |
false |
Gamma compress/expand during resize |
TargetRectangle |
null |
Destination rect inside working buffer |
PremultiplyAlpha |
true |
Premultiply during resampling |
PadColor |
transparent/default | Background when padding |
ResizeMode
| Mode | Behavior |
|---|---|
Crop |
Scale to cover target, crop overflow |
Pad |
Scale to fit, pad remainder |
BoxPad |
Pad without upscaling source (downscale acts like Pad) |
Max |
Fit inside target, keep aspect |
Min |
Grow until shortest side matches; no upscaling |
Stretch |
Ignore aspect |
Manual |
Exact target rect supplied |
AnchorPositionMode: Center, Top, Bottom, Left, Right, TopLeft, TopRight, BottomRight, BottomLeft
KnownResamplers: Bicubic, Box, CatmullRom, Hermite, Lanczos2/3/5/8, MitchellNetravali, NearestNeighbor, Robidoux, RobidouxSharp, Spline, Triangle (bilinear), Welch
Crop, pad, entropy crop
Crop(Rectangle)/Crop(width, height)— exact regionEntropyCrop()/EntropyCrop(float threshold)— trim low-entropy borders (default threshold 0.5)Pad(int width, int height)/ withColor— expand canvas (ResizeMode.BoxPad)
Rotate / flip / auto-orient
Rotate(degrees)— arbitrary angle (resampling)Rotate(RotateMode)—None,Rotate90,Rotate180,Rotate270(orthogonal, no resample)Flip(FlipMode)—None,Horizontal,VerticalRotateFlip(...)— combinedAutoOrient()— apply EXIF orientation then clear the tag
Skew, affine, projective, swizzle
Skew(degreesX, degreesY)(+ rectangle overloads)Transform(AffineTransformBuilder)— compose scale, rotate, skew, translate, matricesTransform(ProjectiveTransformBuilder)— adds taper (TaperSide/TaperCorner) and projective matricesSwizzle(ISwizzler)— pixel permutation patterns
AffineTransformBuilder / ProjectiveTransformBuilder support Prepend* and Append* for rotation (degrees/radians), scale, skew, translation, and raw matrices; BuildMatrix(Size|Rectangle) and GetTransformedSize(...). Optional TransformSpace selects coordinate interpretation.
Color filters
Applied as color-matrix / channel operations on IImageProcessingContext (most have rectangle overloads):
| Method | Parameters / notes |
|---|---|
Brightness(float) |
Multiplier |
Contrast(float) |
Multiplier |
Saturate(float) |
Multiplier |
Hue(float degrees) |
Rotation in hue |
Lightness(float) |
|
Opacity(float) |
|
Invert() |
|
Grayscale() / Grayscale(GrayscaleMode) |
Bt709, Bt601 |
BlackWhite() |
|
Sepia() |
|
Kodachrome(), Lomograph(), Polaroid() |
Photographic matrices |
ColorBlindness(ColorBlindnessMode) |
See below |
Filter(ColorMatrix) |
Custom matrix via FilterExtensions |
ColorBlindnessMode: Achromatomaly, Achromatopsia, Deuteranomaly, Deuteranopia, Protanomaly, Protanopia, Tritanomaly, Tritanopia
Reusable matrices also appear on KnownFilterMatrices.
Convolution
| Method | Key parameters |
|---|---|
GaussianBlur |
sigma; optional Rectangle; optional BorderWrappingMode for X/Y |
GaussianSharpen |
same shape as GaussianBlur |
BoxBlur |
radius; optional rect + BorderWrappingMode X/Y |
MedianBlur |
radius, preserveAlpha |
BokehBlur |
radius, components, gamma |
DetectEdges |
kernel + optional grayscale / rect |
BorderWrappingMode (edge sampling for separable blurs): Repeat, Wrap, Mirror, Bounce
KnownEdgeDetectorKernels: Kayyali, Kirsch, Laplacian3x3, Laplacian5x5, LaplacianOfGaussian, Prewitt, RobertsCross, Robinson, Scharr, Sobel
Kernels split into 2D, compass, and isotropic families—overloads accept the matching kernel type.
Effects
| Method | Notes |
|---|---|
Pixelate() / Pixelate(int size) |
Default size 4; optional rectangle |
OilPaint() / OilPaint(int levels, int brushSize) |
Defaults levels=10, brushSize=15 |
ProcessPixelRowsAsVector4(PixelRowOperation) |
Custom per-row Span<Vector4> processing; modifiers for premultiply/compand/scale; position-aware PixelRowOperation<Point> overloads |
Quantization and dithering
Quantize
ctx.Quantize(); // KnownQuantizers.Octree
ctx.Quantize(KnownQuantizers.Wu);
ctx.Quantize(quantizer, rectangle);
KnownQuantizers
| Name | Role |
|---|---|
Octree |
Fast adaptive octree |
Wu |
Xiaolin Wu — high quality |
WebSafe |
CSS Color Module Level 4 web-safe palette |
Werner |
Werner’s Nomenclature palette |
Encoders for GIF/BMP/TIFF palette modes consume the same IQuantizer infrastructure.
Dither
ctx.Dither(); // KnownDitherings.Bayer8x8
ctx.Dither(KnownDitherings.FloydSteinberg);
ctx.Dither(quantizer, dither);
KnownDitherings
Ordered: Bayer2x2, Ordered3x3, Bayer4x4, Bayer8x8, Bayer16x16
Error diffusion: Atkinson, Burks, FloydSteinberg, JarvisJudiceNinke, Sierra2, Sierra3, SierraLite, StevensonArce, Stucki
Binarization
| Method | Notes |
|---|---|
BinaryThreshold(float threshold) |
Plus mode, colors, rectangle overloads |
BinaryDither(IDither) |
Threshold + dither |
AdaptiveThreshold(...) |
Integral-image adaptive; optional upper/lower colors, thresholdLimit, rectangle |
BinaryThresholdMode: Luminance (BT.709), Saturation, MaxChroma (YCbCr)
Drawing and overlays
Draw image
ctx.DrawImage(otherImage, new Point(10, 20), 0.5f);
ctx.DrawImage(otherImage, rectangle, graphicsOptions);
Opacity, source/destination rectangles, and full GraphicsOptions overloads are available.
Overlays
| Method | Notes |
|---|---|
BackgroundColor(Color) |
Fill background (rect optional) |
Glow(Color) / radius / GraphicsOptions |
Soft glow |
Vignette(Color) / radii / options |
Vignette burn |
Normalization
Histogram equalization and leveling:
ctx.HistogramEqualization();
ctx.HistogramEqualization(new HistogramEqualizationOptions
{
Method = HistogramEqualizationMethod.AdaptiveTileInterpolation,
LuminanceLevels = 256,
ClipHistogram = true,
ClipLimit = 350,
NumberOfTiles = 8,
SyncChannels = true
});
HistogramEqualizationMethod: Global, AdaptiveTileInterpolation, AdaptiveSlidingWindow, AutoLevel
| Option | Default | Meaning |
|---|---|---|
Method |
Global |
Algorithm family |
LuminanceLevels |
256 |
Histogram bins |
ClipHistogram |
false |
CLAHE-style clipping |
ClipLimit |
350 |
Clip threshold when enabled |
NumberOfTiles |
8 |
Tiles for adaptive modes |
SyncChannels |
true |
Keep RGB channels synchronized |
Graphics options and blending
GraphicsOptions influences drawing and overlays:
| Property | Default | Notes |
|---|---|---|
Antialias |
true |
|
AntialiasSubpixelDepth |
16 |
≥ 0 |
BlendPercentage |
1 |
0…1 opacity of the operation |
ColorBlendingMode |
Normal |
See pixel blending modes |
AlphaCompositionMode |
SrcOver |
Porter–Duff style |
Defaults can be adjusted via GraphicOptionsDefaultsExtensions:
// Per processing context
ctx.SetGraphicsOptions(o => { o.BlendPercentage = 0.5f; o.Antialias = false; });
GraphicsOptions current = ctx.GetGraphicsOptions();
// Process-wide defaults on Configuration
config.SetGraphicsOptions(new GraphicsOptions { ColorBlendingMode = PixelColorBlendingMode.Multiply });
Metadata
Attached to Image.Metadata (ImageMetadata) and per-frame ImageFrameMetadata.
Resolution
| Member | Default |
|---|---|
HorizontalResolution / VerticalResolution |
96 |
ResolutionUnits |
PixelResolutionUnit.PixelsPerInch |
Profiles
| Property | Profile |
|---|---|
ExifProfile |
EXIF tags (orientation, camera, GPS, …) |
IptcProfile |
IPTC-IIM editorial fields |
IccProfile |
ICC color management |
XmpProfile |
XMP packet bytes/XML |
CicpProfile |
CICP coding-independent code points (HDR/video color signaling) |
DecodedImageFormat |
Format detected at decode (informational) |
Format-specific bags: GetFormatMetadata / TryGetFormatMetadata keyed by IImageFormat<TFormatMetadata> (PNG textual chunks, GIF animation root data, WebP animation, TIFF IFD-derived data, etc.).
AutoOrient() consumes EXIF orientation. Encoders honor SkipMetadata to strip profiles on write.
Color and color spaces
ParallelPixels.Color
Color is a lossless working color (internally expanded) with:
- Factories:
FromRgba,FromRgb,FromPixel<TPixel>,Parse/ParseHex,TryParse* WithAlpha,ToHex,ToPixel<TPixel>, bulkToPixelspans- Named colors and palettes (
Color.NamedColors, web-safe, Werner)
ParallelPixels.ColorSpaces
Structural color spaces for conversion pipelines, including:
Rgb, LinearRgb, Cmyk, YCbCr, Hsl, Hsv, CieXyz, CieLab, CieLch, CieLchuv, CieLuv, CieXyy, HunterLab, Lms, plus illuminants, working spaces, and companding helpers.
Use ColorSpaceConverter (with ColorSpaceConverterOptions for white points, target RGB working space, and chromatic adaptation) for explicit ToXxx / span Convert / Adapt pipelines—beyond simple pixel casts. Illuminants include A–E, D50–D75, F2/F7/F11; working spaces include sRGB, Rec.709, Rec.2020, Adobe RGB, ProPhoto, and others.
Configuration and memory
Custom configuration example
Configuration config = Configuration.Default.Clone();
config.MaxDegreeOfParallelism = 4;
config.PreferContiguousImageBuffers = true;
config.MemoryAllocator = MemoryAllocator.Create(new MemoryAllocatorOptions
{
MaximumPoolSizeMegabytes = 128,
AllocationLimitMegabytes = 1024
});
using Image<Rgba32> image = Image.Load<Rgba32>(new DecoderOptions { Configuration = config }, path);
MemoryAllocatorOptions
| Property | Meaning |
|---|---|
MaximumPoolSizeMegabytes |
Cap on retained pool size (null = platform default) |
AllocationLimitMegabytes |
Max discontiguous allocation (null → ~1 GB on 32-bit, ~4 GB on 64-bit) |
MemoryAllocator.Create() builds the default pooled unmanaged allocator. Call ReleaseRetainedResources() only after disposing associated images when tearing down a custom allocator.
Guidance: allocators are expensive—prefer one process-wide instance (Configuration.Default.MemoryAllocator = …) rather than per-request pools.
Advanced pixel access
image.ProcessPixelRows(accessor =>
{
for (int y = 0; y < accessor.Height; y++)
{
Span<Rgba32> row = accessor.GetRowSpan(y);
// mutate row
}
});
image.Mutate(ctx => ctx.ProcessPixelRowsAsVector4(span =>
{
for (int i = 0; i < span.Length; i++)
span[i] *= 0.9f;
}));
Lower-level parallelism utilities live under ParallelPixels.Advanced (ParallelRowIterator) for custom processors. Prefer public Mutate extensions unless you are authoring an IImageProcessor.
Advanced reference
This section covers remaining public APIs that operators often need once past the happy path.
Constructing images and converting pixel types
using Image<Rgba32> blank = new(800, 600);
using Image<Rgba32> filled = new(800, 600, Color.White.ToPixel<Rgba32>());
using Image<Rgba32> configured = new(config, 800, 600, background);
// Deep clone same pixel type
using Image<Rgba32> copy = image.Clone();
// Convert all frames to another pixel format (new image)
using Image<Bgra32> bgra = image.CloneAs<Bgra32>();
using Image<L8> gray = image.CloneAs<L8>(customConfig);
CloneAs<TPixel2> allocates a new typed image and converts every frame. Use it for interop (e.g. Bgra32 for Windows buffers) or bit-depth changes (Rgba64 ↔ Rgba32).
Creating images from memory
Besides LoadPixelData / WrapMemory (see Loading):
| API | Copies? | Ownership |
|---|---|---|
LoadPixelData<TPixel>(span, w, h) |
Yes | Image owns new buffer |
WrapMemory<TPixel>(Memory<T> / IMemoryOwner<T> / byte memory / void*) |
No | Caller must keep buffer alive; owners can transfer lifetime carefully |
Stream read origin
Configuration.ReadOrigin:
| Value | Behavior |
|---|---|
Current (default) |
Read from the stream’s current position |
Begin |
Seek to the start before reading (seekable streams) |
Integral images
Used by adaptive thresholding and custom algorithms:
using Buffer2D<ulong> integral = image.CalculateIntegralImage();
using Buffer2D<ulong> region = image.CalculateIntegralImage(new Rectangle(10, 10, 100, 100));
// Also: image.Frames.RootFrame.CalculateIntegralImage(...)
Returns a Buffer2D<ulong> (dispose / return via normal buffer lifetime rules tied to the allocator).
Pixel row conversion modifiers
ProcessPixelRowsAsVector4 accepts PixelConversionModifiers (flags):
| Flag | Meaning |
|---|---|
None |
Raw scaled vectors as stored |
Scale |
Scale to/from 0–1 working range as applicable |
Premultiply |
Premultiply / unpremultiply alpha around the operation |
SRgbCompand |
Compact/expand sRGB gamma around the operation |
Combine with |. Position-aware overload: PixelRowOperation<Point> receives the row origin.
Quantizer options and custom quantizers
KnownQuantizers.* are convenience instances. For control, construct quantizers with QuantizerOptions:
var options = new QuantizerOptions
{
MaxColors = 64, // clamped 1..256 (default 256)
Dither = KnownDitherings.FloydSteinberg, // default; set null to disable
DitherScale = 0.75f, // 0..1 (default 1)
ColorMatchingMode = ColorMatchingMode.Exact // Coarse (default) | Hybrid | Exact
};
IQuantizer wu = new WuQuantizer(options);
IQuantizer octree = new OctreeQuantizer(options);
IQuantizer customPalette = new PaletteQuantizer(paletteColors, options);
// Also: WebSafePaletteQuantizer(options), WernerPaletteQuantizer(options)
image.Mutate(ctx => ctx.Quantize(wu));
// Encoders that quantize:
new GifEncoder
{
Quantizer = wu,
PixelSamplingStrategy = new ExtensivePixelSamplingStrategy()
};
IPixelSamplingStrategy
| Type | Behavior |
|---|---|
DefaultPixelSamplingStrategy |
Caps work (~4096² pixels default) with a minimum scan ratio (default 0.1); configurable via ctor (maximumPixels, minimumScanRatio) |
ExtensivePixelSamplingStrategy |
Enumerates full frame regions — more accurate palettes, more CPU |
KnownFilterMatrices
Static photographic / accessibility matrices and factories (usable with ctx.Filter(matrix)):
- Filters:
AchromatomalyFilter,AchromatopsiaFilter,DeuteranomalyFilter,DeuteranopiaFilter,ProtanomalyFilter,ProtanopiaFilter,TritanomalyFilter,TritanopiaFilter,BlackWhiteFilter,KodachromeFilter,LomographFilter,PolaroidFilter - Factories:
CreateBrightnessFilter,CreateContrastFilter,CreateGrayscaleBt601Filter,CreateGrayscaleBt709Filter,CreateHueFilter,CreateInvertFilter,CreateOpacityFilter,CreateSaturateFilter,CreateLightnessFilter,CreateSepiaFilter
Animation metadata (GIF / APNG / WebP)
Root and per-frame bags are retrieved via format helpers:
GifMetadata gif = image.Metadata.GetGifMetadata();
GifFrameMetadata frame = image.Frames[0].Metadata.GetGifMetadata();
PngMetadata png = image.Metadata.GetPngMetadata(); // RepeatCount, AnimateRootFrame, …
WebpMetadata webp = image.Metadata.GetWebpMetadata();
Also: TryGetGifMetadata / TryGetPngMetadata / TryGetWebpMetadata (+ frame variants).
| Format | Root fields (high level) | Frame fields (high level) |
|---|---|---|
| GIF | RepeatCount, ColorTableMode, GlobalColorTable, BackgroundColorIndex, Comments |
FrameDelay, DisposalMethod, local table, transparency index |
| PNG/APNG | RepeatCount, AnimateRootFrame, bit depth/color type, gamma, text, color table |
FrameDelay (Rational), DisposalMethod, BlendMethod |
| WebP | FileFormat, RepeatCount, BackgroundColor |
FrameDelay, BlendMethod, DisposalMethod |
Disposal / blend enums:
- GIF
GifDisposalMethod:Unspecified,NotDispose,RestoreToBackground,RestoreToPrevious - PNG
PngDisposalMethod:DoNotDispose,RestoreToBackground,RestoreToPrevious;PngBlendMethod:Source,Over - WebP
WebpDisposalMethod:DoNotDispose,RestoreToBackground;WebpBlendMethod:Over,Source
Manage frames with image.Frames: AddFrame, InsertFrame, RemoveFrame, MoveFrame, CloneFrame, ExportFrame, CreateFrame, indexer, RootFrame, Count.
Memory allocator variants
| API | Visibility | Role |
|---|---|---|
MemoryAllocator.Default / Create(options) |
public | Process-wide pooled unmanaged allocator (implementation internal) |
SimpleGcMemoryAllocator |
public | Allocates fresh managed arrays; no pool — useful for diagnostics or tiny tools |
| Pool implementation types | internal | Not part of the public contract |
config.MemoryAllocator = new SimpleGcMemoryAllocator();
// or
config.MemoryAllocator = MemoryAllocator.Create(new MemoryAllocatorOptions
{
MaximumPoolSizeMegabytes = 64,
AllocationLimitMegabytes = 512
});
Geometry helpers
GeometryUtilities.DegreeToRadian(float) / RadianToDegree(float) — small helpers used when composing transforms manually.
Image format manager (custom codecs)
config.ImageFormatsManager.SetEncoder(PngFormat.Instance, new PngEncoder { CompressionLevel = PngCompressionLevel.BestCompression });
config.ImageFormatsManager.SetDecoder(JpegFormat.Instance, JpegDecoder.Instance);
config.ImageFormatsManager.TryFindFormatByFileExtension("webp", out IImageFormat? fmt);
config.Configure(new MyCustomFormatModule()); // IImageFormatConfigurationModule
Projective / affine transform spaces
TransformSpace on builders: Pixel (default) or Coordinate — controls how matrix math interprets the source rectangle when building. Projective builders add PrependTaper / AppendTaper(TaperSide, TaperCorner, fraction) for perspective-like tapers (TaperSide: Left/Top/Right/Bottom; TaperCorner: LeftOrTop/RightOrBottom/Both).
Platform note (iOS / AOT)
Generic-heavy paths (notably GIF quantization/encoding) can stress ahead-of-time compilers. The library contains internal AOT seeding tooling under ParallelPixels.Advanced for Xamarin/.NET iOS scenarios. Typical desktop/server consumers never need it; if you hit AOT JIT errors around palette construction, consult the library’s advanced/AOT guidance in source comments.
Full public pixel struct list
A8, Abgr32, Argb32, Bgr24, Bgr565, Bgra32, Bgra4444, Bgra5551, Byte4, HalfSingle, HalfVector2, HalfVector4, L8, L16, La16, La32, NormalizedByte2, NormalizedByte4, NormalizedShort2, NormalizedShort4, Rg32, Rgb24, Rgb48, Rgba1010102, Rgba32, Rgba64, RgbaVector, Short2, Short4.
Built-in formats use unassociated (straight) alpha or no alpha—not premultiplied storage in the pixel type itself (premultiply is applied transiently during some processors when requested).
Common use cases
1. Image CDN / thumbnail service
Decode with TargetSize, use ResizeMode.Max or Crop with Lanczos, encode JPEG/WebP with explicit quality. Cap MaxDegreeOfParallelism per host CPU. Use a single customized MemoryAllocator for the process.
2. User avatar pipeline
Load → AutoOrient → Resize square with ResizeMode.Crop + AnchorPositionMode.Center → optional GaussianSharpen → PNG or WebP.
3. Document / FAX TIFF
Decode TIFF (Group4/G3 supported) → BinaryThreshold or keep 1-bit → encode TIFF with CcittGroup4Fax or PNG for web preview.
4. Transparent product shots
Prefer Rgba32 → resize with PremultiplyAlpha = true → WebP lossless or PNG with TransparentColorMode = Clear for compression.
5. Palette GIF animation
Compose frames on Image<Rgba32>, set frame metadata delays/disposal, encode with GifEncoder + KnownQuantizers.Wu and optional FloydSteinberg.
6. Scientific / high bit depth
Work in Rgba64 or L16, use Compand = true on resize when exchanging with gamma-encoded 8-bit outputs, export TIFF 16-bit or PNG Bit16.
7. Content moderation / edge features
Mutate(x => x.Grayscale().DetectEdges(KnownEdgeDetectorKernels.Sobel)) as a cheap feature map; or ProcessPixelRowsAsVector4 for custom scores.
8. Interop with existing buffers
Image.WrapMemory<Bgra32>(owner, width, height) over Windows-style buffers; encode without an extra copy when layouts match.
Building from source
Prerequisites
- .NET 10 SDK
- Git LFS (test assets)
git clone https://github.com/qius-solutions/parallel-pixels.git
cd parallel-pixels
git lfs pull
dotnet build ParallelPixels.sln -c Debug -p:RunAnalyzers=false
Notes:
- Release builds may treat StyleCop/analyzers as errors; use
-p:RunAnalyzers=falsewhile iterating. - Optional blame ignore:
git config blame.ignoreRevsFile .git-blame-ignore-revs - On Windows, enable long paths:
git config --system core.longpaths true
Guard / ThrowHelper unit tests:
dotnet test tests/SharedInfrastructure.Tests/SharedInfrastructure.Tests.csproj -c Debug
Publishing to NuGet
Pack (from the repository root):
dotnet pack src/ParallelPixels/ParallelPixels.csproj -c Release \
-p:PackageVersion=<VERSION> \
-p:PackageOutputPath=./artifacts \
-p:TreatWarningsAsErrors=false
Push:
dotnet nuget push ./src/ParallelPixels/artifacts/ParallelPixels.<VERSION>.nupkg \
--api-key <YOUR_TOKEN> \
--source https://api.nuget.org/v3/index.json
Replace <VERSION> (e.g. 1.0.0) and <YOUR_TOKEN> with your NuGet.org API key. The pack command writes the .nupkg under the project’s artifacts/ output path used above.
Repository layout
| Path | Purpose |
|---|---|
ParallelPixels.sln, Directory.Build.* |
Solution + shared MSBuild |
msbuild/ |
QiusSolutions.* props/targets, rulesets, StyleCop, signing key |
src/ParallelPixels/ |
Library: Formats/, PixelFormats/, Processing/, Metadata/, Memory/, Color*, compression, Common/Helpers (Guard, ThrowHelper) |
tests/ParallelPixels.Tests/ |
Unit tests + fixtures |
tests/SharedInfrastructure.Tests/ |
Guard / DebugGuard tests |
tests/ParallelPixels.Benchmarks/ |
BenchmarkDotNet |
tests/ParallelPixels.Tests.ProfilingSandbox/ |
Manual profiling host |
tests/Images/ |
Reference images (Git LFS) |
ParallelPixels.props |
Optional consumer global usings |
Codec-specific engineering notes live beside sources, e.g. src/ParallelPixels/Formats/Tiff/README.md, Formats/Jpeg/README.md, PixelFormats/README.md.
Community and maintainers
- Organization: Qius Solutions
- Repository: qius-solutions/parallel-pixels
- Discussions: design questions and ideas
- Issues: reproducible bugs and tracked work
Maintainer: Qius Solutions — contributions welcome under the MIT license.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.