Resizing

edit

Resize resizes every frame of an image and its poster frame, in one operation: an animation keeps its frames, their durations and its loop count.

Fit inside a box

By default, Resize fits the image inside the size of its ResizeOptions and keeps the aspect ratio:

public static void ResizeAnimation(string inputPath, string outputPath)
{
    using var image = Image.Load(inputPath);
    image.Resize(new ResizeOptions(320, 240) { Mode = ResizeMode.Contain, Filter = ResamplingFilter.Lanczos3 });

    // Same format as the extension; APNG/GIF timing and play count come from the decoded image
    image.Save(outputPath);
}

A 400x200 image resized into 320x240 becomes 320x160. The image is enlarged when it is smaller than the box: set AllowUpscaling = false to keep it at its size instead.

Fill a box exactly

ResizeMode.Cover scales the image to cover the whole box and cuts what overflows. Anchor says which part is kept:

public static void ResizeToFill(string inputPath, string outputPath)
{
    using var image = Image.Load(inputPath);

    // Exactly 200x200: the image is scaled to cover the box and the overflow is cut. The top anchor keeps the top of
    // a portrait, and cuts its bottom
    image.Resize(new ResizeOptions(200, 200) { Mode = ResizeMode.Cover, Anchor = ResizeAnchor.Top });
    image.Save(outputPath);
}

Letterbox

ResizeMode.Pad fits the image inside the box, then pads it to exactly the size of the box with BackgroundColor, the image placed at Anchor:

public static void Letterbox(string inputPath, string outputPath)
{
    using var image = Image.Load(inputPath);

    // Fits inside 320x240, then pads to exactly 320x240 with black bars, the image centered (ResizeOptions.Anchor).
    // The padding is transparent by default, which a pixel format without alpha (an RGB JPEG) cannot store
    image.Resize(new ResizeOptions(320, 240) { Mode = ResizeMode.Pad, BackgroundColor = new Rgba64(0, 0, 0) });

    // A 4-pixel white frame on every side: 328x248
    image.Pad(4, 4, 4, 4, new Rgba64(65535, 65535, 65535));
    image.Save(outputPath);
}

The padding is transparent black by default. A pixel format without alpha (Rgb24, Gray8, Gray16, the formats of most JPEG images) cannot store it: set an opaque BackgroundColor, or Resize throws an UnsupportedImageFeatureException. A centered image gets the extra pixel of an odd padding on its right or bottom side. The padding never blends into the edges of the image.

Modes

ModeResultSize of the result
Contain (default)Fits inside the box, keeps the aspect ratioAt most the box; one side matches it
CoverCovers the box, keeps the aspect ratio, cuts the overflow around AnchorExactly the box
StretchFills the box, changes the aspect ratioExactly the box
PadFits inside the box like Contain, then pads it at AnchorExactly the box
BoxPadLike Pad, but an image that already fits is only placed on the canvas, never enlargedExactly the box

Anchor is one of the nine ResizeAnchor positions (Center by default, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight).

AllowUpscaling = false keeps Contain, Pad and BoxPad within the original size (a "max" resize), and makes Cover and Stretch throw an ArgumentException instead of enlarging. AllowDownscaling = false keeps Contain at or above the original size (a "min" resize), and makes the other modes throw an ArgumentException instead of reducing.

The target size is computed with exact arithmetic, rounded to the nearest pixel, and is at least 1 pixel. A resize to the current size leaves the image unchanged.

Resampling filters

Filter chooses how the new pixels are computed from the old ones:

FilterKernelRadiusInterpolating
NearestNeighborThe pixel that contains the center: no new color is created-yes
BoxThe average of the pixels in the footprint of the output pixel (exact for integer reductions)1/2yes
BilinearTriangle1yes
HermiteCubic, B = 0, C = 01yes
Bicubic (default)Catmull-Rom, cubic B = 0, C = 1/22yes
MitchellNetravaliCubic, B = C = 1/32no
Robidoux, RobidouxSharpKeys cubics2no
SplineCubic B-spline, B = 1, C = 02no
Lanczos2, Lanczos3, Lanczos5, Lanczos8Sinc windowed by sinc, with 2, 3, 5 or 8 lobes2, 3, 5, 8yes

The filters are sampled at the pixel centers, with the edges of the image repeated. When an image is reduced, the radius of the filter widens with the reduction. A non-interpolating filter also smooths an axis that keeps its size, for example the width of an image whose height only changes.

Transparency and color space

Colors are filtered premultiplied by alpha: the hidden color of a transparent pixel never bleeds into its neighbors, and a pixel whose alpha becomes zero is transparent black. A frame whose pixels are all opaque stays opaque. 16-bit images are filtered with 16-bit precision, and the colors of floating-point samples are not clamped.

WorkingSpace chooses the values that are averaged:

  • ResizeWorkingSpace.Encoded (default) filters the stored sample values, like most image tools.
  • ResizeWorkingSpace.LinearSrgb converts the samples to linear light first and back afterward, which gives more physically correct averages, for example for thin bright lines. An untagged image and an ICC profile recognized as sRGB or sGray use the sRGB transfer function; any other ICC profile is rejected rather than misinterpreted: convert such an image first with ConvertColorProfile (see Color management).

Cursors and metadata

A cursor hotspot moves to floor(x * newWidth / width) horizontally, and likewise vertically, as Windows scales a cursor. A Cover resize that cuts the pixel of the hotspot throws an UnsupportedImageFeatureException and leaves the image unchanged. The EXIF dimensions are updated and the EXIF thumbnail is removed.

For thumbnails of many images and for large images, see also Create thumbnails and Performance.