Resize an image in C#
Resize images in C# on any OS with pure .NET code: keep the aspect ratio, fit or crop to a box, make thumbnails, set JPEG quality and resize ASP.NET Core uploads.
To resize an image in C#, load it, call Resize with the width you want and save it. With OneImage that’s one line, Image.Load("photo.jpg").Resize(width: 800).Save("photo-800.jpg"), and the height follows the aspect ratio. It is pure C#, so the same code runs on Windows, Linux, macOS and in containers, unlike System.Drawing, which is Windows-only on current .NET. Below: fitting and cropping to a box, thumbnails, not enlarging small images, quality settings, several sizes at once and ASP.NET Core uploads. Every sample was run against OneImage 1.6.0 on .NET 10.
dotnet add package RecursiveAI.OneImage
OneImage targets .NET 10. The examples use a 3000×3002 photograph (photo.jpg) and a phone-style photo stored as 1200×800 with an EXIF orientation of 6, meaning “rotate 90° to display”, and a GPS position (portrait.jpg).
Resize to a width and keep the aspect ratio
using OneImage; // 800 px wide; the height follows the aspect ratio. Image.Load("photo.jpg").Resize(width: 800).Save("photo-800.jpg");
The 3000×3002 photo becomes 800×801. Give only height: to fix the height instead. The output format comes from the extension, so .png, .webp, .gif, .tif, .bmp or .ico work the same way. Nothing is decoded until Save, and for JPEG input the decoder reads the image at 1/2, 1/4 or 1/8 scale when that is enough for the target size, so a large photo isn’t held in memory at full resolution.
Resize to a fixed size: fit, crop or pad
Give both a width and a height and choose how the image fills that box with fit:
using OneImage; Image photo = Image.Load("photo.jpg"); // Fit inside 800x600 (Fit.Contain is the default): 600x600 for a square photo. photo.Resize(width: 800, height: 600).Save("contain.jpg"); // Fill 800x600 exactly and crop what overflows, keeping the center. photo.Resize(width: 800, height: 600, fit: Fit.Cover).Save("cover.jpg"); // Fit inside 800x600 and pad the rest with white. photo.Resize(width: 800, height: 600, fit: Fit.Pad, background: new Rgba32(255, 255, 255, 255)) .Save("pad.jpg");
These wrote 600×600, 800×600 and 800×600 files. Image is immutable, so one loaded photo can feed all three.
Fit | Result |
|---|---|
Contain (default) | Fits inside the box; aspect ratio kept; one side may be smaller. |
Cover | Fills the box; aspect ratio kept; the overflow is cropped. Choose the kept part with anchor: (nine positions, or Anchor.Smart, which picks the busiest area). |
Pad | Fits inside the box, then pads to the exact size with background:. |
BoxPad | Like Pad, but never enlarges. |
Min | Covers the box without cropping; never enlarges. |
Stretch | Exactly the box; aspect ratio not kept. |
Create a thumbnail in C#
For images that go on the web, use Image.Thumbnail. It is Resize with the defaults a published copy needs:
using OneImage; // Upright, at most 400 px wide, never enlarged, sRGB, EXIF and GPS removed. Image.Thumbnail("portrait.jpg", 400).Save("portrait-400.jpg"); // A 200x200 square that fills the box, for avatars and grids. Image.Thumbnail("portrait.jpg", 200, 200, fit: Fit.Cover).Save("portrait-square.jpg");
In order, it applies the EXIF orientation, resizes (never larger than the original unless you pass upscale: true), converts to sRGB with a compact embedded profile and removes EXIF, XMP and IPTC. The portrait stored sideways at 1200×800 came out upright at 400×600, and the square at 200×200, both with no EXIF block, so no GPS position. Phone photos are the usual source of “my resized image is rotated”: the pixels are stored sideways and the orientation tag says how to show them (see EXIF orientation in C#). OneImage applies that tag when it loads, in Load as well as in Thumbnail.
Don’t enlarge small images
Resize gives you the size you asked for, even if that means enlarging. Pass upscale: false when the target is a maximum:
using OneImage; // Resize enlarges when asked to; upscale: false stops at the original size. // portrait.jpg is 800x1200 once upright, so this writes 800x1200, not 2000x3000. Image.Load("portrait.jpg").Resize(width: 2000, upscale: false).Save("not-enlarged.jpg");
A plain Load and Save keeps the metadata, so this file still carries the GPS block. That’s right for archiving. For anything public, use Thumbnail or call StripMetadata() before saving.
Resize without losing quality
Two settings decide how a resized image looks: the resampling filter and the encoder quality.
using OneImage; var jpeg = new SaveOptions { EncoderOptions = new JpegEncoderOptions { Quality = 85 } }; Image.Load("photo.jpg") .Resize(width: 1200, filter: ResamplingFilter.Lanczos3) .Save("photo-1200.jpg", jpeg);
- Filter. The default,
Auto, already uses Lanczos3 when shrinking and Catmull-Rom when enlarging, so you rarely need to set one.Mitchellis softer, andNearestkeeps hard pixel edges for pixel art and icons. - Linear light. Resampling runs in linear light on premultiplied alpha, so transparent edges don’t pick up color from fully transparent pixels. Pass
linear: falseto filter the stored values instead. - JPEG quality. The default is 75 on the libjpeg scale. Here 85 gave a 1200×1201 file of 298 KB. For smaller files at the same look, write WebP instead; see our guide to converting images to WebP in C#.
Make several sizes from one image
Responsive images need the same picture at several widths. Load once and derive each size:
using OneImage; Image photo = Image.Load("photo.jpg"); // nothing is decoded yet foreach (int width in new[] { 320, 640, 1280 }) { photo.Resize(width: width, upscale: false).Save($"photo-{width}.webp"); }
This wrote 320×320, 640×640 and 1280×1281 WebP files of 34, 72 and 166 KB, ready for a srcset.
Resize uploaded images in ASP.NET Core
The same calls take a Stream. This minimal API endpoint stores every upload as an upright JPEG at most 1600 px wide, with private metadata removed:
using OneImage; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); string folder = Path.Combine(app.Environment.ContentRootPath, "uploads"); Directory.CreateDirectory(folder); app.MapPost("/photos", async (IFormFile file) => { try { await using Stream upload = file.OpenReadStream(); Image photo = Image.Thumbnail(upload, 1600); // upright, at most 1600 px wide, no EXIF/GPS string name = $"{Guid.NewGuid():N}.jpg"; await photo.SaveAsync(Path.Combine(folder, name)); return Results.Ok(new { name, photo.Width, photo.Height }); } catch (ImageException ex) // not an image, damaged, or over the size limits { return Results.BadRequest(ex.Message); } }).DisableAntiforgery(); app.Run();
Posting the two test photos returned {"width":1600,"height":1601} and {"width":800,"height":1200}. The portrait was turned upright and not enlarged, and neither stored file had an EXIF block. A text file got a 400 with “The data is not in a recognized image format.” Uploads are untrusted, so OneImage’s size limits are on by default and are checked from the file headers before large buffers are allocated (limits).
If you’d rather resize on request than on upload, with URLs like /photos/a.jpg?width=400, the OneImage.Web middleware does that with a disk cache.
What about System.Drawing, SkiaSharp and ImageSharp?
- System.Drawing (
Bitmap,Graphics.DrawImage) is the answer in most older posts. Since .NET 6 it is supported only on Windows, and on Linux or macOS it throws “System.Drawing.Common is not supported on this platform” or a similar error. Our page on fixing that error explains why and maps the common calls. - SkiaSharp and ImageSharp both resize well. SkiaSharp wraps native Skia binaries per platform (on Linux you add them yourself; see SkiaSharp on Linux and Docker), and recent ImageSharp releases need a Six Labors license key to build. Our ImageSharp alternatives comparison covers both, with sources.
OneImage’s published benchmarks include JPEG and WebP thumbnail tasks against ImageSharp, SkiaSharp, NetVips and Magick.NET, with memory use, the losses and the method. For everything else it does, see the overview.