Image Manipulation
Image manipulation operations transform the geometry and structure of images, including resizing, cropping, layering, and drawing.
Resize
Scale an image to new dimensions with interpolation methods to maintain quality.
Signature
resize(options: ResizeOptions): this
interface ResizeOptions {
width: number;
height: number;
method?: "bilinear" | "nearest" | "bicubic";
fit?: "stretch" | "fit" | "fill" | "cover" | "contain";
}
Parameters
width- Target width in pixelsheight- Target height in pixelsmethod- Interpolation method (default: "bilinear")"bilinear"- Smooth, high-quality scaling (default)"nearest"- Fast, pixelated scaling (faster)"bicubic"- Highest quality cubic interpolation (slowest)
fit- Fitting mode (default: "stretch")"stretch"- Stretch image to fill dimensions (may distort)"fit"/"contain"- Fit within dimensions maintaining aspect ratio (letterbox)"fill"/"cover"- Fill dimensions maintaining aspect ratio (crop)
Example
import { Image } from "jsr:@cross/image";
const data = await Deno.readFile("photo.jpg");
const image = await Image.decode(data);
// High-quality resize (default)
image.resize({ width: 800, height: 600 });
// Best quality cubic interpolation
image.resize({ width: 800, height: 600, method: "bicubic" });
// Fast pixelated resize
image.resize({ width: 400, height: 300, method: "nearest" });
// Fit within dimensions (maintain aspect ratio with letterboxing)
image.resize({ width: 800, height: 600, fit: "fit" });
// Fill dimensions (maintain aspect ratio, crop if needed)
image.resize({ width: 800, height: 600, fit: "cover" });
// Create thumbnail
image.resize({ width: 150, height: 150 });
const output = await image.encode("png");
await Deno.writeFile("resized.png", output);
Use Cases
- Creating thumbnails
- Responsive image generation
- Retina display optimization
- Pixel art scaling
Aspect Ratio
With the fit parameter, you can easily maintain aspect ratio:
const image = await Image.decode(data);
// Fit within 800x600 maintaining aspect ratio (letterbox if needed)
image.resize({ width: 800, height: 600, fit: "fit" });
// Fill 800x600 maintaining aspect ratio (crop if needed)
image.resize({ width: 800, height: 600, fit: "cover" });
Or calculate dimensions manually for exact control:
const image = await Image.decode(data);
const aspectRatio = image.width / image.height;
// Resize to width, maintain aspect ratio
const targetWidth = 800;
const targetHeight = Math.round(targetWidth / aspectRatio);
image.resize({ width: targetWidth, height: targetHeight });
Performance
- Bilinear interpolation: Good quality, moderate speed (default)
- Bicubic interpolation: Best quality, slowest
- Nearest neighbor: Lower quality, very fast
- Scaling down is faster than scaling up
Crop
Extract a rectangular region from an image.
Signature
crop(x: number, y: number, width: number, height: number): this
Parameters
x- Starting X coordinate (left edge)y- Starting Y coordinate (top edge)width- Width of crop regionheight- Height of crop region
Example
import { Image } from "jsr:@cross/image";
const data = await Deno.readFile("photo.jpg");
const image = await Image.decode(data);
// Crop to center square
const size = Math.min(image.width, image.height);
const x = (image.width - size) / 2;
const y = (image.height - size) / 2;
image.crop(x, y, size, size);
// Extract top-left corner
image.crop(0, 0, 200, 200);
// Extract bottom-right
image.crop(image.width - 300, image.height - 300, 300, 300);
const output = await image.encode("png");
await Deno.writeFile("cropped.png", output);
Use Cases
- Creating square thumbnails
- Removing borders
- Extracting regions of interest
- Preparing for fixed-size layouts
Tips
- Crop coordinates are in pixels from top-left (0,0)
- For best quality, crop before resizing in your operation chain
- Out of bounds coordinates are clamped automatically
Composite
Layer one image on top of another with alpha blending and positioning.
Signature
composite(overlay: Image, x: number, y: number, opacity?: number): this
Parameters
overlay- Image to composite on topx- X position for overlay (can be negative)y- Y position for overlay (can be negative)opacity- Overlay opacity from 0 to 1 (default: 1)0.0- Fully transparent (invisible)0.5- Half transparent1.0- Fully opaque
Example
import { Image } from "jsr:@cross/image";
// Load base image
const baseData = await Deno.readFile("background.jpg");
const base = await Image.decode(baseData);
// Load overlay
const logoData = await Deno.readFile("logo.png");
const logo = await Image.decode(logoData);
// Composite at top-left corner
base.composite(logo, 10, 10);
// Composite with 50% opacity
base.composite(logo, 100, 100, 0.5);
// Composite centered
const x = (base.width - logo.width) / 2;
const y = (base.height - logo.height) / 2;
base.composite(logo, x, y, 0.8);
const output = await base.encode("png");
await Deno.writeFile("composited.png", output);
Use Cases
- Adding watermarks
- Creating thumbnails with borders
- Layering multiple images
- Building image compositions
- Picture-in-picture effects
Alpha Blending
Composite uses proper "over" alpha compositing:
// Semi-transparent overlay on semi-transparent base
const canvas = Image.create(400, 300, 255, 255, 255, 200);
const overlay = Image.create(200, 150, 255, 0, 0, 150);
canvas.composite(overlay, 50, 50, 0.7);
Tips
- Negative positions partially clip the overlay
- Overlay alpha is multiplied by opacity parameter
- Use PNG format for overlay images with transparency
Fill Rectangle
Draw solid or semi-transparent rectangles on the image.
Signature
fillRect(
x: number,
y: number,
width: number,
height: number,
r: number,
g: number,
b: number,
a?: number
): this
Parameters
x- Starting X coordinatey- Starting Y coordinatewidth- Rectangle widthheight- Rectangle heightr- Red component (0-255)g- Green component (0-255)b- Blue component (0-255)a- Alpha component (0-255, default: 255)
Example
import { Image } from "jsr:@cross/image";
// Create white canvas
const canvas = Image.create(800, 600, 255, 255, 255);
// Draw solid red rectangle
canvas.fillRect(100, 100, 200, 150, 255, 0, 0);
// Draw semi-transparent blue rectangle
canvas.fillRect(250, 200, 300, 200, 0, 0, 255, 128);
// Draw black border (4 rectangles)
const borderWidth = 10;
canvas.fillRect(0, 0, canvas.width, borderWidth, 0, 0, 0); // Top
canvas.fillRect(
0,
canvas.height - borderWidth,
canvas.width,
borderWidth,
0,
0,
0,
); // Bottom
canvas.fillRect(0, 0, borderWidth, canvas.height, 0, 0, 0); // Left
canvas.fillRect(
canvas.width - borderWidth,
0,
borderWidth,
canvas.height,
0,
0,
0,
); // Right
const output = await canvas.encode("png");
await Deno.writeFile("rectangles.png", output);
Use Cases
- Drawing borders
- Creating color blocks
- Adding overlays
- Redacting content
- Creating patterns
Alpha Compositing
Rectangles use proper alpha blending with existing pixels:
// Layer semi-transparent rectangles
canvas.fillRect(100, 100, 200, 200, 255, 0, 0, 100); // Red
canvas.fillRect(150, 150, 200, 200, 0, 0, 255, 100); // Blue
// Overlapping area will be blended
Complex Manipulations
Combine operations for advanced effects:
Creating Thumbnails with Borders
const image = await Image.decode(data);
// Resize to thumbnail
image.resize({ width: 200, height: 200 });
// Add white border
const border = 5;
const bordered = Image.create(
image.width + border * 2,
image.height + border * 2,
255,
255,
255,
);
bordered.composite(image, border, border);
await Deno.writeFile("thumb.png", await bordered.encode("png"));
Picture-in-Picture
const main = await Image.decode(await Deno.readFile("main.jpg"));
const pip = await Image.decode(await Deno.readFile("pip.jpg"));
// Resize pip to 25% of main
pip.resize({
width: Math.round(main.width * 0.25),
height: Math.round(main.height * 0.25),
});
// Position in bottom-right with padding
const padding = 20;
main.composite(
pip,
main.width - pip.width - padding,
main.height - pip.height - padding,
0.9,
);
await Deno.writeFile("pip-result.jpg", await main.encode("jpeg"));
Cropping and Resizing Workflow
const image = await Image.decode(data);
// Center crop to square
const size = Math.min(image.width, image.height);
image.crop(
(image.width - size) / 2,
(image.height - size) / 2,
size,
size,
);
// Resize to final dimensions
image.resize({ width: 512, height: 512 });
await Deno.writeFile("square.png", await image.encode("png"));
Rotation and Flipping
Rotate and flip images for orientation correction and creative effects.
Rotate by Degrees
Rotate an image by a specified angle in degrees (rounded to nearest 90°).
Signature
rotate(degrees: number): this
Parameters
degrees- Rotation angle in degrees (positive = clockwise, negative = counter-clockwise)
Example
import { Image } from "jsr:@cross/image";
const image = await Image.decode(await Deno.readFile("photo.jpg"));
// Rotate 90° clockwise
image.rotate(90);
// Rotate 90° counter-clockwise
image.rotate(-90);
// Rotate 180°
image.rotate(180);
// Rotations are rounded to nearest 90°
image.rotate(45); // Rounds to 0° (nearest)
image.rotate(135); // Rounds to 180°
await Deno.writeFile("rotated.jpg", await image.encode("jpeg"));
Rotate 90° Increments
For precise control, use specific rotation methods.
Signature
rotate90(): this // 90° clockwise
rotate180(): this // 180°
rotate270(): this // 270° clockwise (90° counter-clockwise)
Example
const image = await Image.decode(await Deno.readFile("photo.jpg"));
// Precise rotation
image.rotate90(); // 90° clockwise
// image.rotate180(); // 180°
// image.rotate270(); // 270° clockwise
await Deno.writeFile("rotated.jpg", await image.encode("jpeg"));
Flip Horizontal/Vertical
Mirror or flip images.
Signature
flipHorizontal(): this // Mirror left-right
flipVertical(): this // Flip top-bottom
Example
const image = await Image.decode(await Deno.readFile("photo.jpg"));
// Create mirror effect
image.flipHorizontal();
// Or flip upside down
// image.flipVertical();
await Deno.writeFile("flipped.jpg", await image.encode("jpeg"));
Use Cases
- Orientation correction - Fix images from cameras with incorrect orientation
- EXIF orientation - Apply orientation metadata to pixel data
- Creative effects - Kaleidoscope patterns, reflections
- Mirroring - Create symmetrical designs
- Upside-down text - Artistic effects
Performance
- All rotation and flip operations are optimized for speed
- Dimensions swap for 90° and 270° rotations
- Dimensions remain the same for 180° rotations and flips
- No quality loss (pixel-perfect transformations)
Combining Operations
const image = await Image.decode(await Deno.readFile("photo.jpg"));
// Rotate and flip for complex transformations
image.rotate90().flipHorizontal();
// EXIF orientation correction example
// Note: Always check metadata exists before accessing orientation
const orientation = image.metadata?.orientation;
if (orientation === 3) {
image.rotate180();
} else if (orientation === 6) {
image.rotate90();
} else if (orientation === 8) {
image.rotate270();
}
await Deno.writeFile("corrected.jpg", await image.encode("jpeg"));