HPPH 2.0.0

dotnet add package HPPH --version 2.0.0
                    
NuGet\Install-Package HPPH -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HPPH" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HPPH" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="HPPH" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HPPH --version 2.0.0
                    
#r "nuget: HPPH, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HPPH@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HPPH&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=HPPH&version=2.0.0
                    
Install as a Cake Tool

HPPH

C# High Performance Pixel Handling

Summary

Helpers to work with 24 and 32 bit color data, optimized for performance and written fully in C#.
Most operations are allocation free and use SIMD (up to AVX-512) where available.

Package Description
HPPH Core package, contains everything described below
HPPH.SkiaSharp Loading of images and conversion from and to SkiaSharp images and bitmaps
HPPH.System.Drawing Loading of images and conversion from and to System.Drawing bitmaps (Windows only)
HPPH.WPF Loading of images and conversion from and to WPF bitmaps (Windows only)

Supports .NET 10 and .NET 11.

Colors

Type Size
ColorRGB, ColorBGR 24 bit
ColorARGB, ColorABGR, ColorRGBA, ColorBGRA 32 bit

All colors implement IColor<T> and expose R, G, B and A. 24 bit colors always return 255 for A.
The format of a color type is available through T.ColorFormat or the instances on IColorFormat.

ColorRGBA color = new(255, 128, 0, 255);
IColorFormat format = ColorRGBA.ColorFormat; // same as IColorFormat.RGBA

Images

Image<T> is a class holding its pixels in a byte array.
RefImage<T> is a ref struct that works on top of any span (arrays, stackalloc, native memory).
Both can be a view on a region of a bigger buffer, so Stride (bytes per row) is not always Width * BytesPerPixel.

IImage is used where the color format is only known at runtime. Image<T> is the only implementation.

Creating

// copies the data
Image<ColorRGB> image = Image<ColorRGB>.Create(colors, width, height);
Image<ColorBGRA> image2 = Image<ColorBGRA>.Create(bytes, width, height, stride);

// uses the buffer without copying, changes to it show up in the image
Image<ColorBGRA> wrapped = Image<ColorBGRA>.Wrap(bytes, width, height, stride);
RefImage<ColorBGRA> refImage = RefImage<ColorBGRA>.Wrap(span, width, height, stride);

// color format only known at runtime
IImage image3 = format.CreateImage(bytes, width, height, stride);
IImage wrapped2 = format.WrapImage(bytes, width, height, stride);

Accessing pixels

ColorRGB pixel = image[x, y];

foreach (ColorRGB color in image) { }                       // row by row
foreach (ImageRow<ColorRGB> row in image.Rows) { }          // row.AsSpan() for a ReadOnlySpan<ColorRGB>
foreach (ImageColumn<ColorRGB> column in image.Columns) { }

ColorRGB[] firstRow = image.Rows[0].ToArray();

// the memory of the image, for example to pass it on with its stride
ReadOnlySpan<byte> bytes = image.GetBytes(); // from the first pixel to the end of the last row, includes padding

Regions

Regions share the data of the image, nothing is copied.
A slice keeps the whole buffer of its image alive. Use ToImage to get an independent copy, for example before storing a small region of a big image.

RefImage<ColorRGB> region = image[10, 10, 100, 50];   // allocation free
Image<ColorRGB> slice = image.Slice(10, 10, 100, 50); // can be stored in a field
Image<ColorRGB> copy = region.ToImage();              // independent copy
Image<ColorRGB> copy2 = slice.ToImage();              // same for slices

Copying and converting

ColorRGB[] pixels = image.ToArray();
byte[] raw = image.ToRawArray();      // rows without padding
image.CopyTo(buffer);
image.CopyTo(buffer, stride);         // rows are written stride bytes apart

Image<ColorBGRA> bgra = image.ConvertTo<ColorBGRA>();
image.ConvertTo<ColorBGRA>(buffer, stride);

Comparing

Equals checks for reference equality. Use ContentEquals to compare format, size and pixels. Row padding is ignored.

bool equal = image.ContentEquals(other);

Custom operations

To work with an IImage without knowing its color format, implement IImageOperation<TResult> and pass it to Execute.

readonly struct MaxRed : IImageOperation<byte>
{
    public byte Execute<T>(RefImage<T> image) where T : unmanaged, IColor<T>
    {
        byte max = 0;
        foreach (T color in image)
            max = Math.Max(max, color.R);

        return max;
    }
}

byte maxRed = image.Execute<MaxRed, byte>(default); // no allocations
byte maxRed2 = image.Execute(new MaxRed());         // shorter, but boxes struct operations

Image<T> and RefImage<T> offer the same Execute methods.

Operations

All operations are extension methods in PixelHelper.
Sum, average, min-max and palettes work on Image<T>, RefImage<T>, IImage and ReadOnlySpan<T>. Sorting and conversion work on spans.
Flip, rotate and downscale work on Image<T>, RefImage<T> and IImage. They return a new image or write into a buffer you provide.

Sum

Sums up each channel into a long.

ColorSum sum = image.Sum();
long red = sum.R;

Average

ColorRGB average = image.Average();
IColor average2 = someIImage.Average();

Min-Max

Gets the minimum and maximum of each channel.

ColorMinMax minMax = image.MinMax();
byte redRange = minMax.RedRange; // RedMax - RedMin

Sort

Sorts in place by a single channel. The sort is stable.

Span<ColorRGB> colors = image.ToArray();
colors.SortByRed();
colors.SortByGreen();
colors.SortByBlue();
colors.SortByAlpha();

Color palettes

Reduces the colors to a palette of the given size.

ColorRGB[] palette = image.CreateColorPalette(16);

// pixelCounts[i] is the number of pixels represented by palette[i]
Span<long> pixelCounts = stackalloc long[16];
ColorRGB[] palette2 = image.CreateColorPalette(16, pixelCounts);

CreateColorPalette uses Wu's algorithm. It's higher quality and in most cases faster than median cut.
To pick one explicitly use CreateWuColorPalette or CreateMedianCutColorPalette (palette size has to be a power of 2).

  • Splits are only done on red, green and blue, alpha is averaged.
  • Palettes are ordered by the number of pixels each color represents (most common first).
  • Palettes never contain duplicates. If there aren't enough distinct colors, the palette is smaller than requested.

Conversion

All formats can be converted into each other.

Span<ColorRGB> rgb = image.ToArray();

ColorBGRA[] bgra = rgb.Convert<ColorRGB, ColorBGRA>();
rgb.Convert(bgraBuffer);

// only for formats of the same size
Span<ColorBGR> bgr = rgb.ConvertInPlace<ColorRGB, ColorBGR>();

Conversion is mostly limited by memory bandwidth. Writing to freshly allocated memory often costs more than the conversion itself, so reuse buffers where possible.

Flip

Mirrors an image. Horizontal swaps left and right, Vertical swaps top and bottom.

Image<ColorRGB> mirrored = image.Flip(ImageFlip.Horizontal);
Image<ColorRGB> upsideDown = image.Flip(ImageFlip.Horizontal | ImageFlip.Vertical);

image.Flip(ImageFlip.Vertical, buffer);         // Span<ColorRGB>, rows without padding
image.Flip(ImageFlip.Vertical, bytes, stride);  // rows are written stride bytes apart

The destination can't overlap the memory of the image.

Rotate

Rotates an image clockwise by 90, 180 or 270 degrees. For 90 and 270 degrees width and height are swapped.

Image<ColorRGB> rotated = image.Rotate(ImageRotation.Rotate90); // image.Height x image.Width

image.Rotate(ImageRotation.Rotate270, buffer);                  // Span<ColorRGB>, rows without padding
image.Rotate(ImageRotation.Rotate270, bytes, image.Height * 3); // the stride is for the rotated rows

The destination can't overlap the memory of the image.

Downscale

Shrinks an image by an integer factor: each pixel of the result is the average of a block of factor x factor pixels, rounded like Average.
Columns and rows at the edges that don't fill a whole block are left out. The result is at least 1 x 1 pixel.

Image<ColorBGRA> half = image.Downscale(2);           // image.Width / 2 x image.Height / 2
Image<ColorBGRA> wide = image.Downscale(4, 2);        // factors for x and y
ColorBGRA average = image.Downscale(int.MaxValue)[0, 0]; // same as image.Average()

(int width, int height) = PixelHelper.GetDownscaledSize(image.Width, image.Height, 2);
image.Downscale(2, bytes, width * 4);

The destination can't overlap the memory of the image.

SkiaSharp, System.Drawing and WPF

Loading files and converting from and to their image types:

Image<ColorBGRA> image = ImageHelper.LoadImage("image.png");
using SKImage skImage = image.ToSKImage();

See HPPH.SkiaSharp, HPPH.System.Drawing and HPPH.WPF for details.

Product 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.  net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net11.0

    • No dependencies.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on HPPH:

Package Downloads
RGB.NET.Presets

Presets-Presets of RGB.NET

ScreenCapture.NET

Core functionality for Screen-Capturing

StableDiffusion.NET

Stable-Diffusion for .NET based on stable-diffusion.cpp

HPPH.SkiaSharp

SkiaSharp extensions for HPPH

HPPH.System.Drawing

System.Drawing extensions for HPPH

GitHub repositories (3)

Showing the top 3 popular GitHub repositories that depend on HPPH:

Repository Stars
DarthAffe/RGB.NET
The one-stop SDK for RGB-peripherals
DarthAffe/StableDiffusion.NET
C# Wrapper for StableDiffusion.cpp
DarthAffe/ScreenCapture.NET
Fast and easy to use screen-capturing
Version Downloads Last Updated
2.0.0 0 11/14/2026
1.0.0 24,974 7/21/2024