---
title: "Getting started"
url: https://recursiveai.org/oneimage/docs/getting-started/
description: "Install OneImage, make a first thumbnail, convert formats, load, edit and save, and serve resized images from ASP.NET Core."
version: "1.10.1"
---

# Getting started

OneImage is an image library for .NET written in pure C#, with no dependency beyond .NET itself. This page goes from install to a first thumbnail, format conversion, editing, and serving resized images from ASP.NET Core. Every sample on it was compiled and run against the 1.10.1 packages from NuGet.

## Install

OneImage targets .NET 10 (`net10.0`).

```sh
dotnet add package RecursiveAI.OneImage
```

The package is `RecursiveAI.OneImage` and the namespace is `OneImage`. The package has no dependencies: no native binaries, no other NuGet packages. NativeAOT and trimming are supported.

## Your first thumbnail

```csharp
using OneImage;

// 400 px wide, upright, in sRGB, EXIF and GPS removed, written as WebP.
Image.Thumbnail("in.jpg", 400).Save("thumb.webp");
```

`Image.Thumbnail` does what a web-ready copy needs, in this order: it applies the EXIF orientation, resizes (a JPEG is decoded at 1/2, 1/4 or 1/8 scale when that is enough), converts to sRGB and strips EXIF, XMP and IPTC. It never makes an image larger than the original unless you pass `upscale: true`. It also takes a `Stream`, a `ReadOnlyMemory<byte>` or an existing `Image`.

## Convert format and quality

```csharp
// The output format follows the file extension.
Image.Load("in.jpg").Save("out.png");

// Quality and other codec settings go in SaveOptions.
Image.Load("in.png").Save("out.jpg",
    new SaveOptions { EncoderOptions = new JpegEncoderOptions { Quality = 85 } });
Image.Load("in.png").Save("out.webp",
    new SaveOptions { EncoderOptions = new WebPEncoderOptions { Quality = 80 } });

// A stream has no extension, so name the format.
using var stream = File.Create("out-stream.png");
Image.Load("in.jpg").Save(stream, PngFormat.Instance);
```

JPEG and WebP default to quality 75. Every format's options (PNG compression level, WebP lossless, GIF palette size, TIFF compression) are in the [guide](https://recursiveai.org/oneimage/docs/guide/#saving). The core package reads and writes PNG, JPEG, WebP, GIF, TIFF, BMP and ICO, and detects the format from the content, not the extension.

## Load, edit, save

```csharp
Image photo = Image.Load("in.jpg");     // reads the headers only
Console.WriteLine($"{photo.Width} x {photo.Height}");

// Fill a 200 x 200 box, cropping around the busiest part.
photo.Resize(width: 200, height: 200, fit: Fit.Cover, anchor: Anchor.Smart)
     .Save("square.jpg");

// Each call returns a new Image; photo itself never changes.
photo.Crop(0, 0, 1200, 800)
     .Rotate(90)
     .Flip(FlipAxis.Horizontal)
     .Save("edited.png");
```

`Resize` takes a `Fit` (`Contain`, `Cover`, `Stretch`, `Pad`, `BoxPad`, `Min`) and an `Anchor`; leave out `width` or `height` to keep the aspect ratio. Crops, rotations, padding, color adjustments, blurs and filters, compositing and pixel access through a `Canvas` are in the [guide](https://recursiveai.org/oneimage/docs/guide/#operations).

## The lazy model

An `Image` is a description of work, not a decoded bitmap: `Load` reads only the headers, each operation returns a new immutable `Image`, and pixels are computed tile by tile when you `Save`. So a file whose header is fine but whose data is damaged loads without error and throws at `Save`: put the `Save` inside your `try`, or load with `Validate = true`.

```csharp
try
{
    Image.Load("upload.jpg").Resize(width: 800).Save("upload.webp");
}
catch (ImageException ex)    // damaged data, a size limit, an unsupported operation
{
    Console.WriteLine(ex.Message);
}

// To fail at Load instead, decode once up front.
Image upload = Image.Load("upload.jpg", new LoadOptions { Validate = true });
```

All of OneImage's own exceptions derive from `ImageException`. Because an image loaded from a path reopens the file when its pixels are needed, keep the file in place until you are done, or load from a `Stream` or `byte[]` instead ([sources and lifetime](https://recursiveai.org/oneimage/docs/guide/#sources-and-lifetime)).

## Serve images from ASP.NET Core

`RecursiveAI.OneImage.Web` resizes, crops and converts images on request, driven by the query string, and caches the results on disk. It is a separate package, so the core package stays free of ASP.NET Core.

```sh
dotnet add package RecursiveAI.OneImage.Web
```

```csharp
using OneImage.Web;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOneImage();

var app = builder.Build();
app.UseOneImage();      // before UseStaticFiles
app.UseStaticFiles();
app.Run();
```

Every image under `wwwroot` can now be asked for in another size or format:

```text
/photos/cat.jpg?width=400                    400 px wide, same format, aspect ratio kept
/photos/cat.jpg?w=300&h=300&fit=cover        300 x 300, cropped around the center
/photos/cat.jpg?width=800&format=webp&q=80   WebP at quality 80
```

A request without commands falls through to the static file middleware untouched, and an invalid value such as `width=abc` is a 400, never silently clamped. Requests for the same result are processed once and then served from the cache, with `ETag`, `Last-Modified` and 304 responses.

On a public site, sign your URLs or allow only presets, because every distinct valid set of commands creates a cache entry. With a key set, a request without a valid `hmac` gets a 403. Here the key is at least 32 random bytes, base64, from configuration (for example the environment variable `Images__Key`):

```csharp
using OneImage.Web;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOneImage(o =>
{
    o.MaxWidth = 2000;
    o.Presets["thumb"] = "width=200&height=200&fit=cover&format=webp";
    o.HmacSecretKey = Convert.FromBase64String(builder.Configuration["Images:Key"]!);
})
.ConfigureCache(c => c.CacheRootPath = "/var/cache/images");

var app = builder.Build();
app.UseOneImage();
app.UseStaticFiles();

var signer = app.Services.GetRequiredService<ImageUrlSigner>();
string src = signer.Sign("/photos/cat.jpg?preset=thumb");   // adds &hmac=...
app.MapGet("/", () => Results.Text($"<img src=\"{src}\">", "text/html"));
app.Run();
```

The defaults assume a public, unauthenticated endpoint: output is at most 4096 px a side and 16,777,216 pixels, sources at most 64 MiB, queries at most 1024 characters, at most one image per processor (at least two) is processed at a time, EXIF, XMP and IPTC are removed, and the cache folder sits outside `wwwroot`. The ImageSharp.Web query spellings (`rmode`, `ranchor`, `rsampler`, `bgcolor` and the rest) are accepted too. Commands, providers, hooks and the cache layout are in [OneImage.Web](https://recursiveai.org/oneimage/docs/web/).

## Add-on packages

Drawing (paths, fills, strokes, brushes, clipping, boolean operations) and layered documents are in the core package. The rest comes as separate packages, so you only ship what you use:

| Package | What it does | Install |
|---|---|---|
| [OneImage.Web](https://recursiveai.org/oneimage/docs/web/) | ASP.NET Core middleware: URL-driven resizing, cropping and conversion, a disk cache, signed URLs and request limits. | `dotnet add package RecursiveAI.OneImage.Web` |
| [OneImage.Text](https://recursiveai.org/oneimage/docs/text/) | Fonts and text: OpenType shaping, right-to-left and complex scripts, line breaking, rich text and color glyphs. Depends on RecursiveAI.OneFont. | `dotnet add package RecursiveAI.OneImage.Text` |
| [OneImage.Svg](https://recursiveai.org/oneimage/docs/svg/) | SVG and SVGZ as an image format, drawn at the size you ask for. Depends on OneImage.Text. | `dotnet add package RecursiveAI.OneImage.Svg` |
| [OneImage.Psd](https://recursiveai.org/oneimage/docs/psd/) | PSD and PSB: the merged image as an image format, and the layers as a document you can edit and write back. | `dotnet add package RecursiveAI.OneImage.Psd` |
| [OneImage.Pdf](https://recursiveai.org/oneimage/docs/pdf/) | PDF pages as an image format, each page a frame, drawn at the size you ask for. Depends on RecursiveAI.OnePdf. | `dotnet add package RecursiveAI.OneImage.Pdf` |

SVG, PSD and PDF are not in the default configuration. Register the ones you use, then load them like any other file:

```csharp
using OneImage;
using OneImage.Pdf;
using OneImage.Psd;
using OneImage.Svg;

var config = new ImageConfiguration()
    .Add(SvgFormat.Instance)
    .Add(PsdFormat.Instance)
    .Add(new PdfFormat(new PdfRenderOptions { Dpi = 150 }));
var load = new LoadOptions { Configuration = config };

// SVG is drawn at the size you ask for, not scaled up.
Image.Load("logo.svg", load).Resize(width: 400).Save("logo.png");

// PSD loads as its merged image.
Image.Load("poster.psd", load).Resize(width: 800).Save("poster.webp");

// Each PDF page is a frame.
Image.Load("report.pdf", load).Frames[0].ToImage().Save("page1.png");
```

Text is drawn through the same vector drawing as shapes:

```csharp
using OneImage;
using OneImage.Drawing;
using OneImage.Text;
using Path = OneImage.Drawing.Path;

var fonts = new FontCollection();
Font font = fonts.Add("DejaVuSans.ttf").CreateFont(48);

Image.Load("in.jpg")
    .Fill(new Rgba32(255, 255, 255, 180), Path.RoundedRectangle(40, 40, 520, 120, 16))
    .DrawText("Hello from OneImage", new TextOptions(font) { Origin = new(70, 115) },
              Brush.Solid(new Rgba32(20, 20, 20, 255)))
    .Save("captioned.jpg");
```

## Where next

- [Guide](https://recursiveai.org/oneimage/docs/guide/): the lazy model, errors, operations, `Canvas`, metadata, color, animation, limits, threading, AOT and trimming.
- [OneImage.Web](https://recursiveai.org/oneimage/docs/web/): every command, the security defaults, signed URLs, caching and providers.
- [Drawing](https://recursiveai.org/oneimage/docs/drawing/), [Text](https://recursiveai.org/oneimage/docs/text/) and [Layers](https://recursiveai.org/oneimage/docs/layers/).
- [Migrating from ImageSharp and System.Drawing](https://recursiveai.org/oneimage/docs/migrating/), with code side by side, and [migrating from SkiaSharp](https://recursiveai.org/oneimage/articles/migrate-from-skiasharp/).
- [Comparison](https://recursiveai.org/oneimage/docs/comparison/) with ImageSharp, SkiaSharp, Magick.NET and NetVips, and the [benchmarks](https://recursiveai.org/oneimage/docs/performance/).
- [All docs](https://recursiveai.org/oneimage/docs/), or all of them as one Markdown file for an LLM: [llms-full.txt](https://recursiveai.org/oneimage/llms-full.txt).

Guides: [compress images](https://recursiveai.org/oneimage/articles/compress-image-csharp/), [grayscale](https://recursiveai.org/oneimage/articles/convert-image-to-grayscale-csharp/), [watermarks](https://recursiveai.org/oneimage/articles/add-watermark-to-image-csharp/), [animated GIFs](https://recursiveai.org/oneimage/articles/resize-animated-gif-csharp/), [multipage TIFF](https://recursiveai.org/oneimage/articles/split-multipage-tiff-csharp/), [PDF to TIFF](https://recursiveai.org/oneimage/articles/pdf-to-tiff-csharp/), [PSD to PNG](https://recursiveai.org/oneimage/articles/psd-to-png-csharp/) and [resizing on the fly in ASP.NET Core](https://recursiveai.org/oneimage/articles/resize-images-on-the-fly-aspnet-core/).
