---
title: "Migrate from ImageSharp to OneImage"
url: https://recursiveai.org/oneimage/articles/migrate-from-imagesharp/
description: "Port ImageSharp code to OneImage step by step: Mutate, ResizeOptions and ResizeMode, encoders, metadata, DrawImage, ProcessPixelRows, errors and ImageSharp.Web."
date: 2026-10-23
author: OneImage team
---

# Migrate from ImageSharp to OneImage

Port ImageSharp code to OneImage step by step: Mutate, ResizeOptions and ResizeMode, encoders, metadata, DrawImage, ProcessPixelRows, errors and ImageSharp.Web.

October 23, 2026 · OneImage team

Moving from ImageSharp to OneImage is mostly mechanical. `image.Mutate(x => x.Resize(...))` becomes a chain of calls on an immutable `Image`, encoders become `SaveOptions`, and `Image<Rgba32>` becomes plain `Image`. This page puts the common ImageSharp calls next to their OneImage equivalents and then lists the behavior changes to check after porting. The ImageSharp code follows Six Labors’ [documentation](https://docs.sixlabors.com/articles/imagesharp/index.html) and was compiled against ImageSharp 3.1.12 and 4.1.3. The OneImage code was run against OneImage 1.6.0 on .NET 10, on the same inputs.

Teams usually migrate because of the license. ImageSharp 3.x and later use the Six Labors Split License, and 3.2.0 and 4.x check for a license key when you build in Release. Our [ImageSharp license guide](https://recursiveai.org/oneimage/articles/imagesharp-license/) goes through who pays. Security fixes are the other reason. ImageSharp 3.1.12, the last 3.x release that builds without a key, falls within the affected range (2.0.0 through 4.1.1) of [GHSA-j3p4-wp97-rph4](https://github.com/advisories/GHSA-j3p4-wp97-rph4), rated high and published October 7, 2026. GitHub lists 4.1.2 as the first patched version.

## Swap the package

*Terminal · NuGet*

```
dotnet remove package SixLabors.ImageSharp
dotnet add package RecursiveAI.OneImage
```

OneImage targets .NET 10. Replace `using SixLabors.ImageSharp;` and its sub-namespaces (`.Processing`, `.Formats.Jpeg`, `.PixelFormats` and so on) with a single `using OneImage;`. It is licensed MIT OR Apache-2.0, with no key and no native binaries.

## What changes in the programming model

| ImageSharp | OneImage |
|---|---|
| `image.Mutate(x => ...)` changes the image in place | Each operation returns a new immutable `Image`; chain them |
| `image.Clone(x => ...)` | Not needed: keep the original and derive new images from it |
| `Image<Rgba32>`, `Image.Load<Rgba32>(...)` | `Image` and `Image.Load(...)`; pixel types appear only when you read or write pixels |
| `using Image image = ...` | `Image` has no `Dispose`; only the mutable `Canvas` does |
| Operations run when called | Operations are recorded and computed tile by tile at `Save` |
| `AutoOrient()` when you call it | EXIF orientation applied at `Load` |

## Load, resize and save

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
 
using Image image = Image.Load("photo.jpg");
image.Mutate(x => x.Resize(800, 0));   // 0 keeps the aspect ratio
image.Save("photo-800.jpg");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
Image.Load("photo.jpg").Resize(width: 800).Save("photo-800.jpg");
```

Both wrote an 800×801 JPEG from our 3000×3002 photo. Leave out `height:` (rather than passing 0) to keep the aspect ratio. The output format follows the file extension in both libraries.

## ResizeOptions and ResizeMode

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
 
using Image image = Image.Load("photo.jpg");
image.Mutate(x => x.Resize(new ResizeOptions
{
    Size = new Size(800, 600),
    Mode = ResizeMode.Max
}));
image.Save("photo-max.jpg");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
// ResizeMode.Max is Fit.Contain, the default. upscale: false stops at the original size.
Image.Load("photo.jpg")
     .Resize(width: 800, height: 600, fit: Fit.Contain, upscale: false)
     .Save("photo-max.jpg");
```

Both wrote 600×600, fitted inside 800×600. `ResizeOptions` becomes named arguments on `Resize`: `Mode` is `fit:`, `Position` is `anchor:`, `PadColor` is `background:` and `Sampler` is `filter:`. The modes map like this, per OneImage’s [migration reference](https://recursiveai.org/oneimage/docs/migrating/#thumbnail-that-fills-a-box):

| `ResizeMode` | OneImage |
|---|---|
| `Max` | `Fit.Contain`, the default (also spelled `Fit.Max`) |
| `Crop` | `Fit.Cover` (also spelled `Fit.Crop`) |
| `Pad` | `Fit.Pad` with `background:` |
| `BoxPad` | `Fit.BoxPad` |
| `Min` | `Fit.Min` |
| `Stretch` | `Fit.Stretch` |
| `Manual` with `TargetRectangle` | `ResizeInto(canvasWidth, canvasHeight, rect)` |

Two details catch people out:

- **The default mode differs.** ImageSharp’s [resize documentation](https://docs.sixlabors.com/articles/imagesharp/resize.html) notes that `ResizeOptions.Mode` defaults to `Crop`. OneImage’s `Resize` defaults to `Fit.Contain`. If your code set a `Size` without a `Mode`, pass `fit: Fit.Cover`.
- **To prevent upscaling, pass `upscale: false`.** It stops any fit mode at the original size. `Image.Thumbnail` never enlarges unless you pass `upscale: true`.

Resampling filters keep their names: `KnownResamplers.Lanczos3` becomes `ResamplingFilter.Lanczos3`, and the same goes for `CatmullRom`, `Spline` and the others. ImageSharp’s documented default is `Bicubic`. OneImage’s default, `Auto`, uses Lanczos3 when shrinking and Catmull-Rom when enlarging. More resize options are in our guide to [resizing images in C#](https://recursiveai.org/oneimage/articles/resize-image-csharp/).

## Crop, rotate, flip and AutoOrient

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
 
using Image image = Image.Load("portrait.jpg");
image.Mutate(x => x
    .AutoOrient()
    .Crop(new Rectangle(100, 80, 600, 600))
    .Rotate(90)
    .Flip(FlipMode.Horizontal));
image.Save("edited.png");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
// No AutoOrient(): Load has already applied the EXIF orientation.
Image.Load("portrait.jpg")
     .Crop(100, 80, 600, 600)
     .Rotate(90)
     .Flip(FlipAxis.Horizontal)
     .Save("edited.png");
```

Both produced a 600×600 crop of our phone-style test photo, matching each other to within one level per channel on average. `Rectangle` arguments become four integers (or an `ImageRect`), and `FlipMode` becomes `FlipAxis`. Delete `AutoOrient()` calls. If one stays in a pipeline that has already been oriented at load, it does nothing, but code that rotated by the EXIF tag by hand will turn the picture twice. See [EXIF orientation in C#](https://recursiveai.org/oneimage/articles/exif-orientation-csharp/) and [cropping images in C#](https://recursiveai.org/oneimage/articles/crop-image-csharp/).

## Encoders and quality

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Webp;
 
using Image image = Image.Load("photo.jpg");
image.Save("photo-q85.jpg", new JpegEncoder { Quality = 85 });
 
using var webp = new MemoryStream();
image.Save(webp, new WebpEncoder { Quality = 80 });
Console.WriteLine($"{webp.Length} bytes of WebP");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
Image image = Image.Load("photo.jpg");
image.Save("photo-q85.jpg", new SaveOptions { EncoderOptions = new JpegEncoderOptions { Quality = 85 } });
 
using var webp = new MemoryStream();
image.Save(webp, WebPFormat.Instance, new SaveOptions { EncoderOptions = new WebPEncoderOptions { Quality = 80 } });
Console.WriteLine($"{webp.Length} bytes of WebP");
```

Encoder objects become option objects inside `SaveOptions`: `JpegEncoder` becomes `JpegEncoderOptions`, `WebpEncoder` becomes `WebPEncoderOptions` (lossless is `Lossless = true`), and `PngEncoder` becomes `PngEncoderOptions`. When saving to a stream, name the format with `JpegFormat.Instance`, `PngFormat.Instance`, `WebPFormat.Instance` and so on. Quality scales are not interchangeable between encoders, so check output sizes and looks on your own images. Our [WebP guide](https://recursiveai.org/oneimage/articles/convert-image-to-webp-csharp/) covers the WebP settings.

## Identify

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
 
ImageInfo info = Image.Identify("photo.jpg");
Console.WriteLine($"{info.Width}x{info.Height}");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
ImageInfo info = Image.Identify("photo.jpg");
Console.WriteLine($"{info.Width}x{info.Height}");
```

The same call with the same name, and both print `3000x3002`. OneImage’s `Identify` reports the size as stored, before orientation, and its `Frames` list gives the frame count.

## Strip metadata

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
 
using Image image = Image.Load("portrait.jpg");
image.Metadata.ExifProfile = null;
image.Metadata.IptcProfile = null;
image.Metadata.XmpProfile = null;
image.Save("portrait-stripped.jpg");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
// Removes EXIF, XMP, IPTC and the ICC profile; StripMetadata(keepIcc: true) keeps the profile.
Image.Load("portrait.jpg").StripMetadata().Save("portrait-stripped.jpg");
```

Both outputs had no EXIF block. The OneImage file was 800×1200 and upright. The ImageSharp one was 1200×800, the stored layout, because this code never calls `AutoOrient()` before the tag is removed. To read EXIF values, OneImage has `Metadata.ReadExif()`, which returns typed orientation, camera, capture time and GPS fields.

## DrawImage becomes Composite

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
 
using Image image = Image.Load("photo.jpg");
using Image logo = Image.Load("logo.png");
image.Mutate(x => x.DrawImage(logo, new Point(20, 20), 0.8f));
image.Save("watermarked.jpg");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
Image.Load("photo.jpg")
     .Composite(Image.Load("logo.png"), x: 20, y: 20, opacity: 0.8f)
     .Save("watermarked.jpg");
```

`DrawImage(foreground, point, opacity)` maps to `Composite(overlay, x:, y:, opacity:)`. ImageSharp’s `PixelColorBlendingMode` and `PixelAlphaCompositionMode` map to the `blend:` and `alpha:` parameters, with the same member names.

## ProcessPixelRows becomes Canvas.Lock

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
 
using Image<Rgba32> image = Image.Load<Rgba32>("logo.png");
 
image.ProcessPixelRows(accessor =>
{
    for (int y = 0; y < accessor.Height; y++)
    {
        Span<Rgba32> row = accessor.GetRowSpan(y);
 
        for (int x = 0; x < row.Length; x++)
        {
            ref Rgba32 pixel = ref row[x];
            if (pixel.A == 0)
            {
                pixel = new Rgba32(0, 0, 0, 0);
            }
        }
    }
});
 
image.Save("logo-clean.png");
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
using Canvas canvas = Image.Load("logo.png").ToCanvas();
 
// T must match the canvas format: this PNG decodes to Rgba8, so T is Rgba32.
using (PixelLock<Rgba32> pixels = canvas.Lock<Rgba32>(new ImageRect(0, 0, canvas.Width, canvas.Height), LockAccess.ReadWrite))
{
    for (int y = 0; y < pixels.Height; y++)
    {
        Span<Rgba32> row = pixels.GetRow(y);
 
        for (int x = 0; x < row.Length; x++)
        {
            if (row[x].A == 0)
            {
                row[x] = new Rgba32(0, 0, 0, 0);
            }
        }
    }
}
 
canvas.Snapshot().Save("logo-clean.png");
```

The two outputs were pixel-for-pixel identical. `ProcessPixelRows` and `GetRowSpan` become a `Canvas`, OneImage’s mutable, tiled image, with a `PixelLock` whose `GetRow` returns a `Span`. The pixel type must match the canvas format: JPEGs decode to `Rgb24`, and PNGs with alpha to `Rgba32`. For read-only work, `image.ToArray<Rgba32>()` or `CopyPixelsTo` converts any format.

## Exceptions

*ImageSharp · 3.1 / 4.x*

```
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
 
try
{
    using Image image = Image.Load("upload.bin");
    image.Mutate(x => x.Resize(800, 0));
    image.Save("upload.jpg");
}
catch (UnknownImageFormatException)
{
    Console.WriteLine("Not an image.");
}
catch (InvalidImageContentException)
{
    Console.WriteLine("Damaged image.");
}
```

*OneImage · 1.6.0 · .NET 10*

```
using OneImage;
 
try
{
    // Load reads the headers; damaged pixel data can surface at Save, so keep both inside the try.
    Image.Load("upload.bin").Resize(width: 800).Save("upload.jpg");
}
catch (ImageFormatException)
{
    Console.WriteLine("Not an image, or a damaged one.");
}
catch (ImageLimitException)
{
    Console.WriteLine("Over the size limits.");
}
```

Given a text file, the first printed “Not an image.” and the second “Not an image, or a damaged one.” ImageSharp’s [troubleshooting page](https://docs.sixlabors.com/articles/imagesharp/troubleshooting.html) describes `UnknownImageFormatException` (no registered format matched) and `InvalidImageContentException` (the format was recognized but the data is invalid). OneImage reports both as `ImageFormatException`. Its size limits are on by default and checked from the headers, and going over one throws `ImageLimitException`. All of them derive from `ImageException`. Because pixels are decoded lazily, damaged pixel data can surface at `Save`, so keep the save inside the `try`, or load with `new LoadOptions { Validate = true }` to fail at `Load`.

## ImageSharp.Web becomes OneImage.Web

For URL-driven resizing in ASP.NET Core, replace `SixLabors.ImageSharp.Web` with `RecursiveAI.OneImage.Web`. Its [compatibility notes](https://recursiveai.org/oneimage/docs/web/#imagesharpweb-compatibility) say ImageSharp.Web 3.2.0’s query spellings (`rmode`, `ranchor`, `rsampler`, `bgcolor`, `quality` and others) are accepted, so existing URLs keep working:

*Program.cs · OneImage.Web 1.6.0 · ASP.NET Core*

```
using OneImage.Web;
 
var builder = WebApplication.CreateBuilder(args);
 
// Was: builder.Services.AddImageSharp();
builder.Services.AddOneImage(options => options.UseImageSharpDefaults());
 
var app = builder.Build();
 
// Was: app.UseImageSharp();
app.UseOneImage();      // before UseStaticFiles, as with ImageSharp.Web
app.UseStaticFiles();
app.Run();
```

The `AddImageSharp()` and `UseImageSharp()` lines are from ImageSharp.Web’s [getting-started page](https://docs.sixlabors.com/articles/imagesharp.web/gettingstarted.html). `UseImageSharpDefaults()` keeps ImageSharp.Web’s behavior of cropping and enlarging when a width and height come without a mode. With it, `?width=400&height=300` returned 400×300 from our square photo, `?width=400&height=300&rmode=max` returned 300×300, and `?width=300&format=webp&quality=80` returned a WebP. Signed URLs need new tokens, because HMAC tokens aren’t interchangeable. Custom `IImageWebProcessor` classes move into the `OnBeforeSaveAsync` hook.

## Behavior to check after porting

1. **Orientation.** Photos come out upright without `AutoOrient()`. Remove hand-written EXIF rotation.
2. **Resize output.** Resampling runs in linear light on premultiplied alpha, so gradients and edges can differ slightly from ImageSharp’s. Compare a few outputs, and pass `linear: false` to filter the stored values instead.
3. **Error timing.** Wrap `Save` as well as `Load`, or load with `Validate = true`.
4. **Limits.** Very large or suspicious files throw `ImageLimitException`. Raise the limits for trusted data that needs it.
5. **File lifetime.** `Image.Load(path)` rereads the file when pixels are needed, so keep it unchanged until you have saved. Load from a `Stream` or `byte[]` to detach from the file.
6. **Formats.** OneImage reads and writes PNG, JPEG, WebP, GIF, TIFF, BMP and ICO, with add-on packages for SVG, PDF and Photoshop files. Check your inputs against that list.

The project’s full [migration reference](https://recursiveai.org/oneimage/docs/migrating/) covers more of the API side by side, including filters, color adjustments, histograms and perspective transforms, and ImageSharp.Drawing, Fonts and ImageSharp.Web. To compare libraries before you decide, see [ImageSharp alternatives](https://recursiveai.org/oneimage/articles/imagesharp-alternatives/). Moving from SkiaSharp instead? See [migrating from SkiaSharp](https://recursiveai.org/oneimage/articles/migrate-from-skiasharp/).
