Skip to content

Resize, crop, rotate, and flip

Copy page

Geometry work starts by defining the output canvas: its dimensions, whether cropping is allowed, how out-of-bounds areas are filled, and whether EXIF Orientation has already been normalized. The sections below split each geometry operation into its own output and code block.

Every variant uses the same source photograph:

Gate of the Orient night photograph used for geometry transforms
Input: JPEG1938 × 1292, 610,191 bytes

The reference output was generated with govips v2.19.0 / libvips 8.17.3.

Shrink the input to fit within 400 × 400, never enlarging it. The code below computes the scale factor dynamically and clamps the smaller edge to 1.0 to avoid upscaling.

The same night scene resized to fit within 400 pixels
resize-fit: WebPfit inside 400 × 400, no enlargement
func scale(image *vips.ImageRef) float64 {
s := 400.0 / float64(image.Width())
if hs := 400.0 / float64(image.Height()); hs < s {
s = hs
}
if s > 1.0 {
s = 1.0
}
return s
}
终端窗口
# inside run(): vips.Startup(nil) and defer vips.Shutdown();
image, err := vips.NewImageFromFile("input.jpg")
if err != nil { return err }
defer image.Close()
s := scale(image)
if err := image.Resize(s, vips.KernelLanczos3); err != nil { return err }
data, _, err := image.ExportWebp(vips.NewWebpExportParams())
if err != nil { return err }
return os.WriteFile("resize-fit.webp", data, 0o644)

Take a 500 × 500 square starting at top-left (520, 250). Validate that the rectangle stays within bounds before cropping.

A 500-pixel square crop focusing on the illuminated building
Coordinate crop: WebPleft 520, top 250, 500 × 500
image, err := vips.NewImageFromFile("input.jpg")
if err != nil { return err }
defer image.Close()
if err := image.ExtractArea(520, 250, 500, 500); err != nil { return err }
data, _, err := image.ExportWebp(vips.NewWebpExportParams())
if err != nil { return err }
return os.WriteFile("crop.webp", data, 0o644)

Rotate the image clockwise by 30 degrees. Arbitrary-angle rotation needs interpolation, may enlarge the canvas, and introduces a background region — here filled with dark #1d2d35.

Night photograph rotated clockwise by 30 degrees on a dark expanded canvas
Rotation: WebP30°, dark background #1d2d35
image, err := vips.NewImageFromFile("input.jpg")
if err != nil { return err }
defer image.Close()
if err := image.Similarity(1, 30, &vips.ColorRGBA{R: 29, G: 45, B: 53, A: 255}, 0, 0, 0, 0); err != nil { return err }
data, _, err := image.ExportWebp(vips.NewWebpExportParams())
if err != nil { return err }
return os.WriteFile("rotate-30.webp", data, 0o644)

Flip the image along the horizontal axis; the output canvas dimensions stay the same.

Horizontally flipped Gate of the Orient night photograph
Horizontal flip: WebPoutput canvas 1938 × 1292
image, err := vips.NewImageFromFile("input.jpg")
if err != nil { return err }
defer image.Close()
if err := image.Flip(vips.DirectionHorizontal); err != nil { return err }
data, _, err := image.ExportWebp(vips.NewWebpExportParams())
if err != nil { return err }
return os.WriteFile("flip-horizontal.webp", data, 0o644)

The variants differ in canvas size and background: fit keeps the proportional downsample, crop takes a fixed region, and rotation may enlarge the canvas and add a fill colour. In production, decide the output canvas, background strategy, and area of interest explicitly rather than relying on a binding default.

  • Unchecked crop bounds: an out-of-range extract_area returns an error.
  • Relying on default scaling: passing only two numbers without a fit/cover/inside policy gives different defaults across bindings.
  • Ignoring the background colour: transparent outputs and formats without alpha need different rotation backgrounds.