PeachImage 0.3.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package PeachImage --version 0.3.1
                    
NuGet\Install-Package PeachImage -Version 0.3.1
                    
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="PeachImage" Version="0.3.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PeachImage" Version="0.3.1" />
                    
Directory.Packages.props
<PackageReference Include="PeachImage" />
                    
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 PeachImage --version 0.3.1
                    
#r "nuget: PeachImage, 0.3.1"
                    
#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 PeachImage@0.3.1
                    
#: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=PeachImage&version=0.3.1
                    
Install as a Cake Addin
#tool nuget:?package=PeachImage&version=0.3.1
                    
Install as a Cake Tool

PeachImage

Pure .NET image format readers and writers for commonly used image formats on the web.

Targets .NET 8.0 and .NET 10.0. No native interop — every codec is managed code, using modern .NET APIs (System.Runtime.Intrinsics, Span<T>/ReadOnlySpan<T>) for performance instead of P/Invoke.

Status

  • JPEG: decode (baseline sequential + progressive, grayscale/YCbCr/RGB/CMYK/YCCK, all standard chroma subsampling, restart markers) and encode (baseline sequential, grayscale/YCbCr) are implemented.
  • BMP: decode (OS/2 1.x/2.x and Windows BITMAPINFOHEADER through BITMAPV5HEADER variants, 1/4/8bpp indexed color, 16/24/32bpp direct color, RLE4/RLE8 compression, arbitrary BI_BITFIELDS/BI_ALPHABITFIELDS masks) and encode (24bpp truecolor, 8bpp indexed grayscale with optional RLE8, 32bpp with an explicit alpha channel via BITMAPV4HEADER + BI_BITFIELDS) are implemented, including explicit alpha-channel support on both sides.
  • PNG: decode and encode for all 5 color types (grayscale, truecolor, palette, grayscale+alpha, truecolor+alpha) at every valid bit depth (1/2/4/8/16 — including via Gray16/Rgb48/Rgba64 pixel formats), Adam7 interlacing, palette + tRNS transparency (both per-entry and single-color-key), optional opt-in gamma correction (PngDecoderOptions.ScreenGamma), and the common ancillary chunks (gAMA/cHRM/sRGB/iCCP/pHYs/tEXt/zTXt/iTXt/tIME/bKGD). Encoding can build an indexed palette automatically (PngEncoderOptions.ColorMode, default Auto): lossless whenever the source has at most MaxColors (default 256) distinct opaque colors and binary alpha, otherwise falling back to grayscale/truecolor(+alpha) unless ColorMode = Indexed forces palette output via median-cut quantization with optional Floyd-Steinberg dithering (Dither) — the same quantizer GIF encoding uses.
  • GIF: decode (GIF87a/GIF89a, interlacing, transparency, multi-frame animation with per-frame disposal methods and the NETSCAPE2.0 loop count via AnimatedImage.Load) and encode (median-cut palette quantization, optional Floyd-Steinberg dithering, animation) are implemented.
  • WebP: decode is implemented for both of WebP's bitstream codecs — VP8 (lossy) and VP8L (lossless) — including alpha (ALPH chunk / VP8L's own alpha) and animation (via AnimatedImage.Load, including the loop count) in the RIFF "simple" and "extended" container formats. Encode supports both bitstreams: lossless (VP8L, the default) with predictor-transform selection, palette/color-indexing detection, subtract-green, and a color cache; and lossy (VP8, opt in via WebpEncoderOptions { Lossless = false }) with quality-driven quantization. Alpha-bearing sources always encode as VP8L regardless of Lossless, since lossy WebP's alpha channel isn't implemented yet. Animated WebP encode is not yet implemented (decode-only for animation).
  • AVIF: decode is implemented for baseline still images — intra-frame AV1, the full in-loop filter chain (deblocking, CDEF, loop restoration), HEIF grid composite images, alpha via the auxiliary-item mechanism, and both 8-bit and 10-bit depth. Animated AVIF, film grain synthesis, gain maps, 12-bit depth, and palette/IntraBC mode remain unimplemented and throw a clear AvifUnsupportedFeatureException rather than a silently wrong result. Encode is implemented for lossy, 8-bit, 4:2:0, opaque still images only (a single av01 item; no HEIF grid/avis animation on the output side even though decode supports reading them, and no partition-tree size search yet — every block is a fixed 8x8 with a real intra-mode decision among DC/vertical/horizontal/smooth/Paeth candidates). Alpha-bearing sources and higher bit depths are rejected with a clear exception rather than silently dropped or downsampled.
  • Other formats are not yet implemented. The public API (Image, AnimatedImage for multi-frame formats like GIF) is designed to support them without breaking changes when they're added. Codec selection is internal — there's no format-specific type or registration step in the public API.

See LIBRARY_COMPARISON.md for performance numbers against SkiaSharp.

Installing PeachImage

Install the PeachImage package from nuget.org

dotnet add package PeachImage

Usage

Single-frame images

The format is auto-detected from the file's contents for every operation below — no setup call needed.

using PeachImage;
using PeachImage.Formats.Jpeg;

// Load, inspect, and convert between formats.
var image = Image.Load("photo.webp");
Console.WriteLine($"{image.Width}x{image.Height} {image.PixelFormat}");

using var output = File.Create("resaved.jpg");
image.Save(output, "jpeg", new JpegEncoderOptions { Quality = 85 });
using PeachImage;

// Read dimensions/format without decoding pixel data.
using var stream = File.OpenRead("photo.avif");
ImageInfo info = Image.Identify(stream);
Console.WriteLine($"{info.Width}x{info.Height} {info.PixelFormat} ({info.FormatName})");
using PeachImage;

// Zero-copy access to the decoded pixel buffer.
var image = Image.Load("photo.png");
Span<byte> pixels = image.GetPixelSpan();
Span<byte> firstRow = image.GetRowSpan(0);

Bytes already in memory (e.g. a buffered upload) load directly — no need to wrap them in a MemoryStream first; a byte[] converts implicitly to ReadOnlySpan<byte>, and decoding reads straight out of that memory with no intermediate copy:

using PeachImage;

byte[] uploadedBytes = await ReadUploadIntoMemoryAsync();
var image = Image.Load(uploadedBytes);

SaveAsync exists for async I/O call paths. Encoding itself is CPU-bound, not I/O-bound, so only the actual stream/file write is awaited — same as LoadAsync otherwise:

using PeachImage;

using var output = File.Create("resaved.jpg");
await image.SaveAsync(output, "jpeg", new JpegEncoderOptions { Quality = 85 });

Animated images

Multi-frame formats (GIF, and WebP for decode — animated WebP encode isn't implemented yet) use AnimatedImage instead, with the same load/save shape:

using PeachImage;
using PeachImage.Formats.Gif;

var animation = AnimatedImage.Load("clip.gif");

foreach (AnimatedImageFrame frame in animation.Frames)
{
    Console.WriteLine($"{frame.Duration.TotalMilliseconds}ms, disposal={frame.Disposal}");
}

using var output = File.Create("resaved.gif");
animation.Save(output, "gif", new GifEncoderOptions { MaxColors = 128, Dither = true });

Resizing

Image.Resize/AnimatedImage.Resize support 15 resampling filters via ResamplingFilter — Bicubic is the default; also available: Box, CatmullRom, Hermite, Lanczos2/Lanczos3/Lanczos5/Lanczos8, MitchellNetravali, NearestNeighbor, Robidoux, RobidouxSharp, Spline, Bilinear, and Welch.

using PeachImage;

var image = Image.Load("photo.jpg");

// Bicubic by default.
var thumbnail = image.Resize(200, 150);

// Or pick a specific filter.
var sharpened = image.Resize(200, 150, new ResizeOptions { Filter = ResamplingFilter.Lanczos3 });

// ResizeMode.Max treats width/height as a bounding box instead of an exact target: scales down to the
// largest size that fits while preserving aspect ratio, and never upscales — if the source already fits,
// the same instance is returned unchanged rather than allocating a needless copy.
var thumbnailWithinBox = image.Resize(200, 200, new ResizeOptions { Mode = ResizeMode.Max });

AnimatedImage.Resize resizes every frame — lazily, as Frames is enumerated — preserving each frame's duration and disposal method; ResizeOptions.Mode works the same way there too:

using PeachImage;

var animation = AnimatedImage.Load("clip.gif");
var resized = animation.Resize(160, 120, new ResizeOptions { Filter = ResamplingFilter.MitchellNetravali });

using var output = File.Create("resized.gif");
resized.Save(output, "gif");

Building & testing

dotnet build PeachImage.slnx
dotnet test PeachImage.slnx

The first dotnet test run automatically fetches JPEG, BMP, and PNG test corpora (the Imazen codec-corpus conformance sets, image-rs/jpeg-decoder's test assets, and — for BMP — the bmp-conformance subset of codec-corpus, itself generated from Jason Summers' bmpsuite; for PNG — the pngsuite subset of codec-corpus, a mirror of Willem van Schaik's classic PngSuite conformance set) into the gitignored tests/corpus/ directory — no separate script needed. Set PEACHIMAGE_SKIP_CORPUS_FETCH=1 to skip network access; corpus-driven tests report as skipped rather than failing.

Benchmarking

dotnet run -c Release --project bench/PeachImage.Benchmarks

Compares PeachImage's decode/encode throughput against SkiaSharp (a dev-only dependency of the benchmark project only — never referenced by the shipped library). See LIBRARY_COMPARISON.md for the latest results.

License

MIT — see LICENSE. One algorithm's numerical structure (the AAN fast DCT/IDCT butterfly wiring) was referenced from libjpeg-turbo during implementation; see THIRD-PARTY-LICENSES.md.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (11)

Showing the top 5 NuGet packages that depend on PeachImage:

Package Downloads
PeachPDF

Renders HTML and CSS to PDF in pure .NET, with no browser or external process: HTML parsing, the CSS cascade, layout, SVG, MathML and the PDF writer all run in-process.

PeachDrawing

A standalone software raster Canvas for PeachDrawing.Core - draw shapes, text and images onto a bitmap and save it in any PeachImage-supported format, with no PDF, HTML or CSS involved.

PeachDrawing.PDF

A PDF Canvas for PeachDrawing.Core: draw shapes, text, images, gradients and effects and write them as PDF, with fonts embedded and subset, PDF/A and PDF/X conformance, encryption and tagged output, in pure .NET.

PeachBrackets

Parses HTML, CSS, SVG and MathML, resolves the cascade and lays the document out into pages, then paints them onto any PeachDrawing canvas: PDF, a bitmap, or your own.

PeachDrawing.Svg

A vector SVG Canvas for PeachDrawing.Core - draw shapes, gradients, clips, text and images and save them as SVG, with paths, curves, gradients and glyph outlines kept as vector content and no PDF, HTML or CSS involved.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.4 0 11/9/2026
0.7.3 1,225 11/3/2026
0.7.2 56 11/3/2026
0.7.1 146 11/2/2026
0.7.0 138 10/31/2026
0.6.2 3,279 10/30/2026
0.6.1 147 10/27/2026
0.6.0 6,063 10/21/2026
0.5.3 166 10/17/2026
0.5.2 5,632 10/14/2026
0.5.1 2,878 10/9/2026
0.5.0 109 10/9/2026
0.4.7 631 10/3/2026
0.4.6 13,804 9/16/2026
0.4.5 236 9/15/2026
0.4.4 158 9/15/2026
0.4.3 145 9/15/2026
0.4.2 4,984 8/26/2026
0.4.1 974 8/23/2026
0.3.1 148 8/21/2026
Loading failed