ImageSharp.Drawing 3.1.0 is out. You can download it today.
V3 introduced the canvas model, retained scenes, and optional WebGPU rendering. 3.1 builds on that foundation with a declarative effect system for layers, backdrop filters in the CSS mold, and exact aliased rasterization. It also adds premultiplied GPU rendering and a faster text pipeline built on Fonts 3.1.
What's new since 3.0.0 #
The main changes since 3.0.0 are:
- Layer effects: blur, drop shadow, glow, inner shadow, and color matrix treatments applied through
SaveLayer(...). - Backdrop effects: blur, acrylic, drop shadow, and a family of color filters applied to the pixels behind a region.
- WebGPU shader implementations for the built-in effects, so GPU-backed canvases apply them without a CPU round trip.
- Exact center-sampled aliased rasterization on both the CPU and WebGPU backends.
- Premultiplied rendering, alpha-aware readback, and a surface alpha mode for compositing over native content.
- Public drawing backend APIs and a staged WebGPU support probe that names each failing capability.
- Text rendering improvements: positioned-glyph drawing APIs, glyph batching, culling, decoration fixes, and shared glyph caches.
- Font palette selection and correct COLR v1 composite group rendering, on both the CPU and WebGPU backends.
- Performance work and fixes across clipping,
DrawImage, gradients, strokes, and the WebGPU runtime.
Layer effects #
SaveLayer(...) already gave the canvas isolated compositing groups. 3.1 adds LayerEffect, a declarative treatment that transforms the layer content when the layer is restored.
The built-in content effects are BlurLayerEffect, DropShadowLayerEffect, GlowLayerEffect, InnerShadowLayerEffect, and ColorMatrixLayerEffect. The layer isolates the content, so the effect operates on exactly what was drawn into it, against transparency. Layer bounds expand automatically by the effect's reach, so blurred or offset output is never cut off.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Image<Rgba32> image = new(360, 220, Color.White.ToPixel<Rgba32>());
image.Mutate(ctx => ctx.Paint(canvas =>
{
_ = canvas.SaveLayer(
new GraphicsOptions(),
new Rectangle(60, 40, 240, 140),
new DropShadowLayerEffect(new Point(6, 6), 4F, Color.Black.WithAlpha(0.5F)));
canvas.FillEllipse(Brushes.Solid(Color.OrangeRed), new(180, 110), new(90, 50));
canvas.Restore();
}));
An overload accepts an IPath region instead of a rectangle. The effect then processes only pixels covered by the path, and its output lands on the path translated by the effect's offset.
Backdrop filters #
BackdropLayerEffect brings the CSS backdrop-filter model to the canvas. The filter runs against the pixels already beneath the layer's region when the layer opens. The result is clipped to the region, and the layer's own content renders above it, staying sharp.
The built-in backdrop effects are BackdropBlurLayerEffect, BackdropAcrylicLayerEffect, BackdropDropShadowLayerEffect, and BackdropColorMatrixLayerEffect, with prebuilt variants for brightness, contrast, grayscale, hue rotation, inversion, opacity, sepia, and saturation.
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using Image<Rgba32> image = Image.Load<Rgba32>("photo.jpg");
image.Mutate(ctx => ctx.Paint(canvas =>
{
Rectangle panel = new(70, 46, 220, 128);
_ = canvas.SaveLayer(
new GraphicsOptions(),
panel,
new BackdropAcrylicLayerEffect(10F, Color.White.WithAlpha(0.3F)));
// The backdrop inside the panel is blurred and tinted; content here stays sharp.
canvas.Draw(Pens.Solid(Color.White, 2), panel);
canvas.Restore();
}));
The WebGPU package ships shader-backed versions of the whole set, such as WebGPUGaussianBlurLayerEffect and WebGPUBackdropAcrylicLayerEffect. These execute as WGSL shader passes, so a SaveLayer(...) effect stays on the GPU. An equivalent Apply(...) processor round-trips through CPU memory instead. Each carries its CPU equivalent as a built-in fallback, so one instance renders identically on both backends.
Both families are also open for extension, on both backends. On the CPU side, derive from LayerEffect or BackdropLayerEffect and pass the base constructor any ImageSharp processing operation. On the GPU side, derive from WebGPUShaderLayerEffect or WebGPUBackdropShaderLayerEffect with your own WGSL source, declared uniforms, and a CPU fallback. The shader contract is one entry function, with layer_load, layer_load_unassociated, and layer_sample provided for reading the input, plus multi-pass support.
Exact aliased rasterization #
Aliased rendering was rebuilt. When antialiasing is off, coverage is now decided by sampling the geometry at each pixel center, on both the CPU and WebGPU backends. Thin fill features that fall between pixel centers are recovered, so hairline geometry does not disappear.
This replaces the old threshold model, and GraphicsOptions.AntialiasThreshold is gone with it. Binary-coverage output for masks, hit-test surfaces, and pixel-art-style rendering now matches classic center-sampled rasterizers, with nothing to tune.
Premultiplied rendering and GPU interop #
WebGPU render targets can now store associated (premultiplied) alpha, the working representation of GPU pipelines. Construct a WebGPURenderTarget with PixelAlphaRepresentation.Associated, and readback returns the matching premultiplied pixel type from ImageSharp 4.1: Rgba32P, Bgra32P, NormalizedByte4P, or RgbaHalfP.
External surfaces gain an AlphaMode option. Premultiplied lets a WebGPU surface blend over other native content, Opaque presents every pixel as solid, and Auto lets WebGPU choose.
The drawing backend types behind the WebGPU package are now public. Applications can share device contexts, manage surfaces, and integrate the renderer with their own native plumbing. Before any target is constructed, the staged support probe reports exactly which capability is missing on machines that cannot render.
Text rendering #
3.1 runs on Fonts 3.1 and picks up its improvements directly: skip-ink decorations, hinting modes, weight synthesis, and faster, corpus-verified multilingual shaping.
The drawing side changed too:
- Text entry points on
DrawingCanvasandTextBuildernow accept positioned glyph spans (glyphIdspluspoints), so shaped output from the new FontsTextShaperAPI can be drawn directly. - Glyphs batch by subpixel position, and text outside the visible bounds is culled.
- Decorations on path text merge into continuous bands instead of per-glyph segments, and skip-ink carving matches the rendered geometry.
- Glyph outlines now cache per canvas and persist across
DrawTextcalls, a community contribution. A new publicDrawingTextCachecan also be shared across canvases. - Color text follows the Fonts 3.1 palette selection through
RichTextOptions, and COLR v1 composite groups render with their declared blend modes. The WebGPU blend shaders implement the same composite set, so emoji and color glyphs match across backends.
Performance #
The 3.0 announcement measured FillParis, a Paris street map with 50K fill paths rendered at 1096×1060 with per-path transforms and opacity. Re-measured on this release against SkiaSharp as the baseline (AMD Ryzen AI Max+ 395 with Radeon 8060S, Windows 11, .NET 10, BenchmarkDotNet, Server GC):
| Method | Mean | Ratio |
|---|---|---|
| SkiaSharp | 103.4 ms | 1.00 |
| System.Drawing | 147.3 ms | 1.42 |
| ImageSharp.Drawing CPU | 43.0 ms | 0.42 |
| ImageSharp.Drawing CPU, retained scene | 11.9 ms | 0.11 |
| ImageSharp.Drawing WebGPU | 20.0 ms | 0.19 |
| ImageSharp.Drawing WebGPU, retained scene | 5.6 ms | 0.05 |
The CPU row reports the median: one benchmark launch of three degraded, and the ratio is computed from the median.
The same scene also moved against the 3.0 release. The CPU backend improved from 48.5 ms to 43.0 ms, and the retained CPU path improved from 17.9 ms to 11.9 ms, 1.5x faster. The immediate WebGPU path improved from 24.8 ms to 20.0 ms, 1.24x faster. The retained WebGPU path held at roughly 5.5 ms, already bound by the GPU. Both runs used the same machine, with both native baselines steady across the two runs.
Repeated text benefits from three 3.1 changes at once: the per-canvas glyph cache, subpixel glyph batching, and the zero-allocation Fonts 3.1 shaper. We measured 3.0.0 against 3.1.0 with one harness on the same machine as the table above. The harness draws the same phrase repeatedly on one canvas, best of ten repetitions per row, on .NET 10:
DrawText runs |
3.0.0 | 3.1.0 | Speedup | 3.0.0 allocations | 3.1.0 allocations |
|---|---|---|---|---|---|
| 50 | 69.8 ms | 27.6 ms | 2.5x | 53.3 MB | 6.8 MB |
| 250 | 107.1 ms | 49.7 ms | 2.2x | 266.3 MB | 31.4 MB |
| 1,000 | 456.6 ms | 105.3 ms | 4.3x | 1,075.3 MB | 125.7 MB |
At 1,000 runs, 3.1 renders the workload 4.3x faster and allocates 8.6x less. The glyph cache itself started as a community contribution.
DrawImage also gains a measured community improvement. It now converts only the required source region. Drawing a one-eighth region of a 22.9-megapixel source dropped from 103 MB and 24.4 ms to 24 MB and 14.4 ms.
Fixes #
Other changes worth knowing about:
- The clipping API was refactored, with difference clips supported and rectangular clips optimized.
- Elliptic gradient brushes and clip layers were fixed.
- Stroke cap and join arcs derive their sweep from relative angles, fixing round caps that vanished on the WebGPU backend.
- Huge path coordinates no longer overflow the stack during scanline conversion.
- The WebGPU runtime resolves native assets across deployment layouts, shares one instance across surfaces, warms pipelines ahead of first use, and scopes device polling to submission indexes for reliable readback.
Closing #
3.1 adds effects to the canvas model: grouped treatments, backdrop filters, exact binary coverage, and premultiplied GPU output. The API stays the same on the CPU and GPU backends.
Please read the release notes for detailed information.
Thank you to everyone who reported issues and contributed code to this release.
- Previous: Announcing ImageSharp 4.1.0