Getting Started
Five concrete scenarios, each ~3 minutes.
Install
Section titled “Install”dotnet add package Pragmatic.ImagingThe NuGet includes the native binary for win-x64. Linux and macOS need a locally-built binary today — see native-deployment.md.
Scenario 1 — resize and convert
Section titled “Scenario 1 — resize and convert”Load a JPEG, thumbnail it, write WebP.
using Pragmatic.Imaging;
using var pipe = ImagePipeline.Load(File.ReadAllBytes("photo.jpg"));
pipe.Thumbnail(maxWidth: 400, maxHeight: 400);using var output = File.Create("photo-thumb.webp");pipe.EncodeTo(output, ImageFormat.WebP);Thumbnail preserves aspect ratio and fits the longest side within the box. For an exact fixed size, use Resize(width, height).
Scenario 2 — apply filters
Section titled “Scenario 2 — apply filters”Grayscale + sharpen + tonal tweaks.
using var pipe = ImagePipeline.Load(bytes);
pipe.Grayscale() .Sharpen(sigma: 1.5f, threshold: 2) .Brightness(10) // -255..255 .Contrast(15f); // additive; 0 = identity
using var output = File.Create("scan.png");pipe.EncodeTo(output, ImageFormat.Png);See operations.md for every filter parameter.
Scenario 3 — inspect an upload before decoding
Section titled “Scenario 3 — inspect an upload before decoding”Validate dimensions and format cheaply (header only) before running anything expensive, then hand the same limits to Load.
using Pragmatic.Imaging;
var info = ImageInfo.FromStream(Request.Body, maxBytes: 65_536);
if (info.Format is not (ImageFormat.Jpeg or ImageFormat.Png or ImageFormat.Webp)) return BadRequest("Unsupported format");if (info.Width > 8000 || info.Height > 8000) return BadRequest("Image too large");
Request.Body.Position = 0; // ImageInfo only read the header
var options = new ImagingOptions{ MaxWidth = 8000, MaxHeight = 8000, MaxMegapixels = 50, AllowedFormats = [ImageFormat.Jpeg, ImageFormat.Png, ImageFormat.Webp],};
using var pipe = ImagePipeline.Load(Request.Body, options);// ... proceed with processingImagePipeline.Load re-checks the same ImagingOptions from the header before decoding, so even if you skip the manual pre-check the limits still hold. AllowedFormats is a fail-closed allow-list; a rejected upload throws ImagingException { Reason = FormatNotAllowed }.
Scenario 4 — batch thumbnailing
Section titled “Scenario 4 — batch thumbnailing”ImageBatch is static and works on byte[] inputs with bounded concurrency.
using Pragmatic.Imaging;
var files = Directory.GetFiles("photos", "*.jpg");var images = await Task.WhenAll(files.Select(f => File.ReadAllBytesAsync(f)));
byte[][] thumbs = await ImageBatch.ThumbnailsAsync( images, maxWidth: 256, maxHeight: 256, format: ImageFormat.WebP, quality: 75, maxConcurrency: 4); // 0 → Environment.ProcessorCount / 2
for (var i = 0; i < files.Length; i++) await File.WriteAllBytesAsync(Path.ChangeExtension(files[i], ".thumb.webp"), thumbs[i]);maxConcurrency caps how many pipelines are alive at once — 4–8 is a safe start on a typical server. For a custom per-item operation, use ImageBatch.ProcessAsync(images, data => …).
Scenario 5 — generate a QR code
Section titled “Scenario 5 — generate a QR code”No pipeline needed — QR generation is a direct static call.
using var output = File.Create("qr.png");QrCode.GeneratePng( text: "https://pragmaticdesign.net", output: output, moduleSize: 10, // pixels per module (1–1000) margin: 2); // quiet zone in modules (≤100)Output is always PNG, black on white. Thread-safe, no options object.
Beyond the five scenarios
Section titled “Beyond the five scenarios”One-liners
Section titled “One-liners”When you don’t need a pipeline, ImageConverter wraps the common cases:
byte[] thumb = await ImageConverter.ThumbnailAsync(bytes, 400, 400, ImageFormat.Jpeg, quality: 90);byte[] webp = await ImageConverter.ConvertAsync(bytes, ImageFormat.WebP);byte[] clean = ImageConverter.StripExif(bytes); // drop metadata via re-encodeStreaming to an HTTP response
Section titled “Streaming to an HTTP response”[HttpGet("/thumbnail")]public async Task ThumbnailAsync(CancellationToken ct){ var bytes = await GetImageAsync(ct); using var pipe = ImagePipeline.Load(bytes, ImagingOptions.Strict); pipe.Thumbnail(400, 400); Response.ContentType = "image/webp"; await pipe.EncodeToStreamAsync(Response.Body, ImageFormat.WebP, ct: ct);}Always pass ImagingOptions (start from ImagingOptions.Strict) for untrusted input — the defaults are conservative but not tailored to your workload.
Runnable samples
Section titled “Runnable samples”Pragmatic.Imaging.Samples— QR / image info / resize / format / filters / crop / rotate / flip / chained transforms / batch
- Operations reference — full method catalogue with parameters and semantics
- Native deployment — platforms, AOT, Docker notes
- Concepts — architecture, thread safety, error model