loading

Formats

PNG, JPEG, BMP and GIF are decoded by C# that runs anywhere; WebP and AVIF come from the browser's own codecs. Support varies by browser, version and platform, so Bmage probes instead of assuming - and refuses to pass off a substitute as the format you asked for.

Encode one image every way

The same 1200 × 800 scene, encoded at quality 0.82 in each format. A format this browser cannot write is attempted anyway, to show what the exporter does about it.

What decodes and encodes where

FormatManaged decodeManaged encodeBrowser decodeBrowser encode
PNGyesyesyesyes
JPEGyesyesyesyes
BMPyesyesyesvia managed
GIFfirst framenofirst frameno
WebPnonoyesusually
AVIFnonousuallyrarely
SVGrefusednorefusedno

"Managed" means pure C#, working anywhere including a server. "Browser" means the platform's own codecs, the only way to reach WebP and AVIF in a browser. When both are available the browser codec wins, because it is hardware accelerated and covers more formats; the managed codecs remain the fallback, which is what makes prerendering and server-side use work. The Capabilities page shows the probe's answer for this browser.

Never assume, always probe

Asked to encode a format they cannot write, browsers silently produce a PNG instead. Bmage probes by actually encoding a pixel and checking what came back, and detects the substitution at export time too.

C#
var capabilities = await capabilityProvider.GetAsync();​if (capabilities.CanEncode(ImageFormat.Avif)) { /* ... */ }if (capabilities.SupportsWebP) { /* ... */ }​try{    var avif = await exporter.EncodeAsync(image, ImageExportOptions.Avif);}catch (ImageCapabilityException ex){    // "This browser cannot encode Avif; it produced image/png instead. Check    //  ImageCapabilities.CanEncode before exporting and fall back to a supported format."}

If you would rather degrade than handle an exception, ask for a preference order:

C#
var encoded = await exporter.EncodeWithFallbackAsync(    image,    [ImageFormat.Avif, ImageFormat.WebP, ImageFormat.Jpeg],    new ImageExportOptions { Quality = 0.82 });​Console.WriteLine(encoded.Format);   // whichever one this browser can actually write

Choosing a format

SituationChoose
Photographs, broad compatibilityJPEG at 0.8 to 0.85
Photographs, modern browsersWebP at 0.8, roughly 25 to 35% smaller than JPEG at similar quality
Anything needing transparencyWebP, or PNG for lossless
Screenshots, diagrams, flat colourPNG
Smallest possible, where supportedAVIF, but always with a fallback
ClipboardPNG; most browsers accept nothing else

Circular avatars need transparency, so prefer WebP or PNG there - a lossy opaque format shows the corners. The Avatar page switches its fallback for exactly that reason.

Quality and size budgets

C#
var encoded = await exporter.EncodeAsync(image, new ImageExportOptions{    Format      = ImageFormat.WebP,    Quality     = 0.9,    MaxFileSize = 100 * 1024,    MinQuality  = 0.5,    MaxFileSizeStrategy = MaxFileSizeStrategy.QualityThenDimensions,});​Console.WriteLine($"{encoded.Length} bytes at quality {encoded.Quality}");

MaxFileSize bisects on quality first, down to MinQuality, then shrinks the dimensions if the strategy allows it. The quality actually used is reported on the result, so you can show it. When the target is impossible you get an ImageEncodeException explaining what the smallest attempt produced, rather than a silently oversized file.

StrategyBehaviour
QualityThenDimensionsLower quality first, then shrink. The default.
QualityOnlyNever change dimensions; throw if the budget cannot be met.
DimensionsOnlyKeep quality fixed and shrink.

Transparency without an alpha channel

JPEG has no alpha channel. Exporting a transparent image as JPEG composites it over ImageExportOptions.Background, which defaults to white - without it, browsers composite over black, which is rarely what anyone wants. Set it explicitly when white is wrong for your page.

C#
new ImageExportOptions { Format = ImageFormat.Jpeg, Background = new Rgba32(18, 18, 20) }

Format detection

Content is trusted over file names and content types, because both can be wrong or hostile. ImageProcessor.Identify reads dimensions from the header without decoding any pixels, which is how an oversized upload is rejected before anything is allocated. DisplaySize accounts for EXIF orientation, so a portrait photo from a phone reports portrait dimensions even though its stored pixels are landscape.

C#
var format = ImageFormats.Detect(bytes);              // magic bytesvar byName = ImageFormats.FromFileName("photo.JPG");  // convenience onlyvar byMime = ImageFormats.FromMimeType("image/webp"); // convenience only​var info = processor.Identify(bytes);if (info is null) return Error("Not a supported image.");if ((long)info.Width * info.Height > 40_000_000) return Error("Too large.");Console.WriteLine($"{info.Width}×{info.Height} {info.Format}, displays as {info.DisplaySize}");

GIF and SVG

GIF decodes its first frame, with palettes, interlacing and transparency; animation is out of scope, and GIF encoding is supported by no browser canvas and is not implemented - use WebP or PNG.

SVG is refused with an explanatory error rather than rasterised. Rendering untrusted SVG can execute embedded scripts and fetch remote resources, and a library cannot make that safe on the caller's behalf. If you control the SVG, rasterise it yourself and pass the pixels.

An unhandled error has occurred. Reload 🗙