Packages

img

Create, draw on, filter, resize, decode and encode images, and use the same image value everywhere else in the standard library.

import "std:img"

std:img is OSL's image type. Make one, draw shapes, paths and text on it, filter and transform it, read and write PNG, JPEG, GIF, BMP and TIFF (and decode WebP, AVIF and more), and inspect files without decoding them. It needs no display and no system libraries, so it runs anywhere, in a test or on a server.

An *img.Image is also the common currency of the other graphics packages: qr and svg produce one, window and ui show one, and serve sends one.

osl
import "std:img"

Making an image and reading pixels#

img.new(width, height, options) makes an image that is transparent, or filled with the fill option. A colour is a CSS name ("white"), "#rrggbb", "#rrggbbaa" or {r, g, b, a}. Pixels are counted from 1: 1, 1 is the top-left one.

main.osl
import "std:img"

*img.Image pic = img.new(40, 30, {fill: "white"})
log pic.width() pic.height()
log pic.pixel(1, 1)

pic.setPixel(2, 1, "#2f6feb")
pic.setPixel(3, 1, {r: 255, g: 0, b: 0, a: 128})
log pic.pixel(2, 1)
log pic.pixel(3, 1)
Click the code to edit it

pixel gives back a colour string: #rrggbb when it is opaque and #rrggbbaa when it is not. setPixel replaces a pixel without blending. An image is a class: assigning it shares it, and pic.clone() makes an independent copy. Variables that hold one are written *img.Image.

Drawing shapes#

Drawing methods change the image in place. Each takes a position as {x, y} and an options object. Shapes take fill and stroke colours (and lineWidth); lines take color.

main.osl
import "std:img"

*img.Image pic = img.new(60, 40, {fill: "white"})

pic.rectangle({x: 5, y: 5, width: 20, height: 10}, {fill: "#2f6feb"})
pic.circle({x: 45, y: 20}, 8, {fill: "red", stroke: "black"})
pic.line({x: 1, y: 40}, {x: 60, y: 40}, {color: "green", lineWidth: 2})
pic.polygon([{x: 5, y: 25}, {x: 25, y: 25}, {x: 15, y: 38}], {fill: "orange"})

log pic.pixel(10, 8)
log pic.pixel(45, 20)
log pic.pixel(15, 30)
log pic.pixel(30, 10)
Click the code to edit it

Also available: ellipse, polyline (a line through several points, with lineJoin) and rectangle with radius for rounded corners. Edges are anti-aliased, so a pixel on an edge is a blend. Colours also take a gradient or a pattern:

main.osl
import "std:img"

*img.Image pic = img.new(100, 10)
auto fade = img.linearGradient({x: 1, y: 1}, {x: 100, y: 1}, ["red", "blue"])
pic.rectangle({x: 1, y: 1, width: 100, height: 10}, {fill: fade})

log fade.colorAt(0) fade.colorAt(0.5) fade.colorAt(1)
log pic.pixel(1, 5)
log pic.pixel(100, 5)
Click the code to edit it

img.radialGradient(center, radius, stops) works the same way, and img.pattern(image) tiles an image.

Text#

pic.text(text, {x, y}, options) draws text with its top-left corner at the point. Options include size, color, font, align and maxWidth for wrapping. img.measureText measures text without drawing it, so you can size an image to fit:

main.osl
import "std:img"

auto size = img.measureText("Hello, world", {size: 20})
log size.lines
log size.width > 100 size.height > 20

*img.Image label = img.new(floor(size.width) + 20, floor(size.height) + 10, {fill: "white"})
label.text("Hello, world", {x: 10, y: 5}, {size: 20, color: "black"})
log label.width() label.height()
Click the code to edit it

The default font is "sans". For your own, load one with img.decodeFontFile("fonts/Inter.ttf") and pass the result as font.

Paths#

A path is a list of straight and curved segments you build once and draw many times. Building methods return the path, so they chain. img.parsePath reads the d attribute of an SVG <path>:

main.osl
import "std:img"

auto triangle = img.path().moveTo({x: 5, y: 5}).lineTo({x: 30, y: 5}).lineTo({x: 5, y: 25}).close()
log triangle.bounds()

*img.Image pic = img.new(40, 30, {fill: "white"})
pic.drawPath(triangle, {fill: "navy"})
log pic.pixel(8, 8) pic.pixel(35, 28)

auto heart = img.parsePath("M 10 30 C 10 10 30 10 30 30 Z").unwrap()
log heart.bounds().width

log img.parsePath("M 10 oops").isErr()
Click the code to edit it

Path coordinates are SVG coordinates (the top-left corner is 0, 0), which is one off from pixel coordinates. A path can also be moved with translate, scale and rotate.

Saving, encoding and decoding#

pic.save(path) writes the file and picks the format from the extension. img.decodeFile(path) reads one back. Both return a result, because files go missing and data can be corrupt:

main.osl
import "std:img"
import "std:fs"

fs.remove("shape.png", {force: true}).unwrap()

*img.Image pic = img.new(40, 30, {fill: "white"})
pic.rectangle({x: 5, y: 5, width: 20, height: 10}, {fill: "#2f6feb"})
pic.save("shape.png").unwrap()

auto info = img.infoFile("shape.png").unwrap()
log info.format info.width info.height info.hasAlpha

*img.Image back = img.decodeFile("shape.png").unwrap()
log back.pixel(10, 10)

log img.decodeFile("missing.png").unwrapErr().kind
Click the code to edit it

img.infoFile reads only the header, which is how to check an upload's size before you spend memory on decoding it. For data you hold in memory, pic.encode("png") returns bytes, img.decode(bytes) and img.info(bytes) read them, and pic.toDataURL() makes a data: URL for HTML:

main.osl
import "std:img"

*img.Image pic = img.new(8, 8, {fill: "red"})
byte[] png = pic.encode("png").unwrap()
byte[] jpeg = pic.encode("jpeg", {quality: 80}).unwrap()
log img.info(png).unwrap().format img.info(jpeg).unwrap().format
log pic.toDataURL().unwrap().startsWith("data:image/png;base64,")

log img.decode([1, 2, 3, 4]).unwrapErr().kind
Click the code to edit it

Encoding supports PNG, JPEG, GIF, BMP and TIFF. Decoding also reads WebP, AVIF, HEIC, JPEG XL, ICO and QOI. For an animation, img.frames(bytes) iterates over every frame, with frame.delay() in milliseconds.

Resizing, cropping and turning#

These return a copy and leave the original alone. Pass 0 for one side of resize to keep the aspect ratio. fit is "fill", "contain" or "cover", and filter is "lanczos" (the default), "bicubic", "bilinear" or "nearest" for pixel art:

main.osl
import "std:img"

*img.Image pic = img.new(40, 30, {fill: "white"})
pic.rectangle({x: 1, y: 1, width: 20, height: 30}, {fill: "red"})

auto half = pic.resize(20, 0)
log half.width() half.height()

auto thumb = pic.resize(16, 16, {fit: "cover"})
log thumb.width() thumb.height()

auto part = pic.crop({x: 1, y: 1, width: 10, height: 10})
log part.width() part.pixel(1, 1)

auto turned = pic.rotate(90)
log turned.width() turned.height()
log pic.flipX().pixel(40, 5)
Click the code to edit it

crop takes a rectangle {x, y, width, height} and throws a ValueError if it reaches outside the image.

Filters#

Filters return a copy too. They follow CSS: grayscale, invert, brightness(amount), contrast(amount), saturate(amount), hueRotate(degrees), blur(radius), sharpen(amount), threshold(level), opacity(amount), pixelate(size) and tint(color, amount):

main.osl
import "std:img"

*img.Image gray = img.new(2, 2, {fill: "#808080"})
log gray.brightness(2).pixel(1, 1)
log gray.invert().pixel(1, 1)
log gray.threshold(0.4).pixel(1, 1)
log gray.tint("red", 0.5).pixel(1, 1)
Click the code to edit it

For an effect the filters don't cover, mapPixels calls a function with each pixel as {r, g, b, a} and its position. Return a colour, or null to keep the pixel. It costs about a second per megapixel, so it suits small images:

main.osl
import "std:img"

*img.Image pic = img.new(3, 1)
pic.setPixel(1, 1, "#ff0000")
pic.setPixel(2, 1, "#00ff00")
pic.setPixel(3, 1, "#0000ff")

auto swapped = pic.mapPixels(def(c, x, y) -> {r: c.b, g: c.g, b: c.r, a: c.a})
log swapped.pixel(1, 1) swapped.pixel(2, 1) swapped.pixel(3, 1)
Click the code to edit it

Combining images#

target.draw(source, x, y) paints one image onto another with the source's top-left pixel at x, y. Options are blend, opacity, width and height. mask hides an image wherever another is dark (with channel: "luminance") or transparent:

main.osl
import "std:img"

*img.Image base = img.new(4, 4, {fill: "red"})
*img.Image patch = img.new(2, 2, {fill: "blue"})

base.draw(patch, 2, 2)
log base.pixel(2, 2) base.pixel(1, 1)

base.draw(patch, 1, 1, {opacity: 0.5})
log base.pixel(1, 1)

*img.Image mask = img.new(4, 4, {fill: "black"})
mask.rectangle({x: 1, y: 1, width: 2, height: 4}, {fill: "white"})
*img.Image green = img.new(4, 4, {fill: "green"})
green.mask(mask, {channel: "luminance"})
log green.pixel(1, 1) green.pixel(4, 1)
Click the code to edit it

Raw pixels#

pic.pixels() returns every pixel as bytes (red, green, blue, alpha, row by row), and img.fromPixels(width, height, bytes) builds an image back from them. It is the way to hand an image to code that wants raw data:

main.osl
import "std:img"

*img.Image pic = img.new(2, 1)
pic.setPixel(1, 1, "#ff8000")
pic.setPixel(2, 1, "#0000ff")

byte[] raw = pic.pixels()
log raw.len
log raw[1] raw[2] raw[3] raw[4]

*img.Image copy = img.fromPixels(2, 1, raw)
log copy.pixel(1, 1) copy.pixel(2, 1)
Click the code to edit it

Closing#

Images keep their pixels until the garbage collector takes them. For a large image you are done with, pic.close() frees them at once. Using a closed image throws a StateError.

Your turn

Checkerboard

Make a 4 by 4 image, black and white like a checkerboard with a white pixel at 1, 1, then print the colours of the pixels at 1, 1, 2, 1 and 1, 2. Use setPixel and a nested loop.

main.osl
Ctrl Enter runs
Expected output
#ffffff #000000 #000000
Quick check

pic.resize(100, 100) is called on a 400 by 400 image. What is pic?

Your turn

A thumbnail

Finish thumbnail so it returns a copy of the image that is 50 pixels wide, with the height following the aspect ratio. The original must stay as it is.

main.osl
Ctrl Enter runs
Expected output
50 25
200 100

API reference#

107 members, generated from the std:img source of OSL 9.3.0.

Functions

#img.clone(*img.Image i) *img.Image

Deprecated. Deprecated: use pic.clone() instead.

clone copies an image, or returns null for a closed image.

#img.decode(byte[] data, object? options) result<*img.Image, error>

decode reads an image from encoded bytes. An animation gives its first frame and an ICO its largest icon. Option: autoOrient (default false). Fails with ParseError for corrupt data and UnsupportedError for unknown formats or images over 100 megapixels.

#img.decodeBytes(byte[] data) *img.Image

Deprecated. Deprecated: use img.decode(data) instead.

decodeBytes decodes image bytes, or returns null when it can't.

#img.decodeFile(string path, object? options) result<*img.Image, error>

decodeFile reads and decodes the image file at path, as img.decode does with the same options. Fails with NotFoundError, PermissionError or IOError when the file can't be read, and otherwise as img.decode does.

#img.decodeFont(byte[] data, object? options) result<*img.Font, error>

decodeFont reads a TrueType or OpenType font (TTF or OTF, or one font of a TTC or OTC collection) from bytes, for the font option of pic.text and img.measureText. Options: index, which font of a collection to use, counting from 1 (default 1).

Fails with ParseError when data isn't a font, UnsupportedError for WOFF and WOFF2 web fonts, and ValueError when index is past the last font.

#img.decodeFontFile(string path, object? options) result<*img.Font, error>

decodeFontFile reads the font file at path, as img.decodeFont does with the same options. Fails with NotFoundError, PermissionError or IOError when the file can't be read, and otherwise as img.decodeFont does.

#img.encodeJPEGBytes(*img.Image i, int q) byte[]

Deprecated. Deprecated: use pic.encode("jpeg", {quality: q}) instead.

encodeJPEGBytes encodes an image as JPEG with quality clamped to 1-100, or returns null for a closed image.

#img.encodePNGBytes(*img.Image i) byte[]

Deprecated. Deprecated: use pic.encode("png") instead.

encodePNGBytes encodes an image as PNG, or returns null for a closed image.

#img.fill(*img.Image i, int r, int g, int b, int a) boolean

Deprecated. Deprecated: use pic.fill(color) instead.

fill sets every pixel of an image to the channels r, g, b, a (0-255), and returns false for a closed image. A channel outside 0-255 throws ValueError.

#img.frames(byte[] data) result<iter<*img.Image>, error>

frames iterates over an animation's frames, a TIFF's pages or an ICO's icons; a still image yields itself once. Fails like img.decode, and a corrupt frame throws ParseError from the loop.

#img.framesFile(string path) result<iter<*img.Image>, error>

framesFile iterates over every image in the file at path, as img.frames does. Fails with NotFoundError, PermissionError or IOError when the file can't be read, and otherwise as img.frames does.

#img.fromPixels(int width, int height, byte[] data) *img.Image

fromPixels creates a width x height image from data laid out as pic.pixels() returns it: four bytes per pixel (red, green, blue, alpha, not premultiplied), row by row. Throws ValueError unless data is exactly width * height * 4 bytes and the size is valid for img.new.

#img.info(byte[] data) result<{animated: boolean, format: string, frames: int?, hasAlpha: boolean?, height: int, orientation: int, width: int}, error>

info reads an image's format, size and frame count from its header, without decoding pixels. The record has format, width, height, frames, animated, hasAlpha and orientation. Fails with ParseError for corrupt data and UnsupportedError for an unknown format.

#img.infoFile(string path) result<{animated: boolean, format: string, frames: int?, hasAlpha: boolean?, height: int, orientation: int, width: int}, error>

infoFile reads the image header of the file at path, as img.info does, reading only the parts of the file it needs. Fails with NotFoundError, PermissionError or IOError when the file can't be read, and otherwise as img.info does.

#img.isAnimatedBytes(byte[] data) boolean

Deprecated. Deprecated: use img.info(data).animated instead.

isAnimatedBytes reports whether image bytes hold an animation with more than one frame.

#img.linearGradient(object from, object to, array stops, object? options) *img.Gradient

linearGradient returns a gradient along the line from one point {x, y} to another, in image pixels, to use as a fill, stroke or color.

stops is an array of colors spread evenly along the line, or of {offset, color} objects with offsets from 0 (at from) to 1 (at to). Colors blend premultiplied, so fading to "transparent" keeps the color. Options: spread, what the gradient does past its ends: "pad" (the default; the end colors continue), "repeat" or "reflect". Throws ValueError for no stops, an invalid color or offset, or a malformed point.

#img.measureText(string text, object? options) {ascent: number, descent: number, height: number, lineHeight: number, lines: string[], width: number}

measureText lays text out as pic.text would and measures it, without drawing. Options: font, size, maxWidth and lineHeight, as for pic.text.

The record's keys, all in pixels: width, of the widest line; height, from the top of the first line to the bottom of the last; ascent and descent, how far the font reaches above and below a baseline; lineHeight, the distance between baselines; and lines, the text of each line after wrapping. Throws ValueError for an invalid option.

#img.new(int width, int height, object? options) *img.Image

new creates a width x height image, transparent unless the fill option gives a color ("#rrggbb", "#rrggbbaa", a CSS name such as "white", or {r, g, b, a}), an *img.Gradient or an *img.Pattern to start it with. Throws ValueError unless both sides are 1 to 100000 pixels and the image is at most 100 megapixels, or for an invalid fill.

#img.normalizeOrientationBytes(*img.Image i, byte[] data) *img.Image

Deprecated. Deprecated: use img.decode(data, {autoOrient: true}) or pic.orient(img.info(data).orientation) instead.

normalizeOrientationBytes turns an image upright using the EXIF orientation in data, the bytes it was decoded from. It returns the image itself when no change is needed and null for a closed image.

#img.open(string path) *img.Image

Deprecated. Deprecated: use img.decodeFile(path) instead.

open decodes the image file at path, or returns null when it can't.

#img.openSize(string path) int[]

Deprecated. Deprecated: use img.infoFile(path) instead.

openSize returns the [width, height] of the image file at path, or [0, 0] when it can't be read.

#img.orient(*img.Image i, int orientation) *img.Image

Deprecated. Deprecated: use pic.orient(orientation) instead.

orient applies an EXIF orientation, or returns null for one outside 1-8 or a closed image.

#img.parsePath(string text) result<*img.Path, error>

parsePath reads SVG path data, the d attribute of an SVG <path>, such as "M 10 10 L 90 10 Q 50 60 10 10 Z": the M, L, H, V, C, S, Q, T, A and Z commands, in absolute (upper case) and relative (lower case) forms. The numbers are SVG coordinates, which put the image's top-left corner at 0, 0, so the data draws where an SVG renderer would draw it: SVG's 0 to 1 is pixel 1. Fails with ParseError, naming the character where the data goes wrong.

#img.path() *img.Path

path returns a new empty path to build with moveTo, lineTo, quadTo, cubicTo, arc, arcTo, rect, circle, ellipse and close, and to draw with pic.drawPath. Building methods change the path and return it, so calls chain: img.path().moveTo(a).lineTo(b).close().

#img.pattern(*img.Image image) *img.Pattern

pattern returns image repeated in tiles across the whole plane, one tile's top-left pixel at pixel 1, 1, to use as a fill, stroke or color. The pattern copies the image, so later changes to it don't show. Throws StateError when the image is closed.

#img.radialGradient(object center, number radius, array stops, object? options) *img.Gradient

radialGradient returns a gradient in circles around center, {x, y}, reaching the last stop at radius pixels, to use as a fill, stroke or color. stops and the spread option are as for img.linearGradient, with offset 0 at the center and 1 at radius. Throws ValueError for a negative radius, no stops or an invalid stop.

#img.resize(*img.Image i, int w, int h) *img.Image

Deprecated. Deprecated: use pic.resize(w, h) instead.

resize scales an image with Lanczos filtering, where 0 for one side keeps the aspect ratio, or returns null for invalid sizes or a closed image.

#img.resizeFast(*img.Image i, int w, int h) *img.Image

Deprecated. Deprecated: use pic.resize(w, h, {filter: "bilinear"}) instead.

resizeFast scales an image with bilinear filtering, or returns null for invalid sizes or a closed image.

#img.resizeFit(*img.Image i, int maxW, int maxH) *img.Image

Deprecated. Deprecated: use pic.resize(maxW, maxH, {fit: "contain"}) instead.

resizeFit scales an image to fit inside maxW x maxH, keeping the aspect ratio, or returns null for invalid sizes or a closed image.

#img.resizeHeight(*img.Image i, int h) *img.Image

Deprecated. Deprecated: use pic.resize(0, h) instead.

resizeHeight scales an image to height h, keeping the aspect ratio.

#img.resizeWidth(*img.Image i, int w) *img.Image

Deprecated. Deprecated: use pic.resize(w, 0) instead.

resizeWidth scales an image to width w, keeping the aspect ratio.

#img.rgb(int r, int g, int b) img.Color

rgb returns an opaque img.Color from red, green and blue channels, each 0 to 255. Throws ValueError for a channel outside that range.

#img.rgba(int r, int g, int b, int a) img.Color

rgba returns an img.Color from red, green, blue and alpha channels, each 0 to 255; alpha 0 is fully transparent. Throws ValueError for a channel outside that range.

#img.rotate(*img.Image i, number angle) *img.Image

Deprecated. Deprecated: use pic.rotate(degrees) instead.

rotate turns an image clockwise by angle degrees, or returns null for a non-finite angle or a closed image.

#img.saveJPEG(*img.Image i, string path, int quality) boolean

Deprecated. Deprecated: use pic.save(path, {quality: q}) instead.

saveJPEG writes an image to path as JPEG with quality clamped to 1-100, returning false on failure.

#img.savePNG(*img.Image i, string path) boolean

Deprecated. Deprecated: use pic.save(path) instead.

savePNG writes an image to path as PNG, returning false on failure.

#img.setColorProfile(*img.Image i, any value) boolean

Deprecated. Deprecated: use pic.setColorProfile(profile) instead.

setColorProfile sets an image's ICC profile from bytes, a string or an array of byte numbers, without checking it; empty input clears it. Returns false for a closed image or invalid input.

#img.sizeBytes(byte[] data) object

Deprecated. Deprecated: use img.info(data) instead.

sizeBytes returns {w, h} for image bytes, or {} when they can't be read.

*img.Image

#image.blur(number radius) *img.Image

blur returns a copy blurred like a Gaussian blur with a standard deviation of radius pixels, as CSS blur() is; 0 leaves it as it is. Pixels past the edges repeat the edge, so edges don't fade, and transparency blurs without dark fringes. It takes the same time for any radius. Throws ValueError for a radius outside 0-1000.

#image.bounds() {height: int, width: int, x: int, y: int}

bounds returns the region {x, y, width, height} this frame of an animation changed, with x, y its top-left pixel (1, 1 is the image's top-left corner). For any other image it is the whole image, {x: 1, y: 1, width, height}, so pic.crop(pic.bounds()) is always valid.

#image.brightness(number amount) *img.Image

brightness returns a copy with every color multiplied by amount: 1 leaves it as it is, 0 is black and 2 twice as bright (clipped at white). Throws ValueError for an amount outside 0-1000.

#image.circle(object center, number radius, object? options)

circle draws a circle around center, {x, y}, with radius in pixels, in place. It takes rectangle's options except radius. Throws ValueError for a malformed center, a negative radius or an invalid option.

#image.clone() *img.Image

clone returns an independent copy, keeping the color profile and frame details.

#image.close()

close releases the image's pixels. Closing twice does nothing; any other method of a closed image throws StateError.

#image.colorProfile() byte[]

colorProfile returns the embedded ICC color profile, or null when the image has none.

#image.contrast(number amount) *img.Image

contrast returns a copy with every color moved away from middle gray by amount: 1 leaves it as it is, 0 is solid gray and 2 doubles the contrast. Throws ValueError for an amount outside 0-1000.

#image.crop(object rect) *img.Image

crop returns a copy of the rectangle {x, y, width, height} of this image, in pixels with x, y its top-left pixel (1, 1 is the image's top-left corner); the image itself is unchanged. pic.crop(pic.bounds()) is the whole image, or for an animation frame the part that frame changed. Throws ValueError when a key is missing or not a whole number, or the rectangle is empty or reaches outside the image.

#image.delay() number

delay returns how long this frame of an animation is shown, in milliseconds. It is 0 for a still image and for a frame whose file sets no delay.

#image.draw(*img.Image source, int x, int y, object? options)

draw paints source onto this image with source's top-left pixel at pixel x, y, in place: at 1, 1 their corners line up, and 0 or less puts part of source off the left or top edge. Options: blend (default "over"), opacity, width, height and filter. Throws ValueError for an invalid option, TypeError for a null source and StateError when either image is closed.

#image.drawPath(*img.Path path, object? options)

drawPath draws path, from img.path or img.parsePath, in place. Each subpath is filled as if closed, by the nonzero rule; the outline follows the path as drawn, closing only subpaths that path.close() closed. It takes rectangle's options except radius. Throws ValueError for an invalid option and TypeError for a null path.

#image.ellipse(object center, object radii, object? options)

ellipse draws an ellipse around center, {x, y}, with radii {x, y} in pixels, in place. It takes rectangle's options except radius. Throws ValueError for a malformed center or radii, or an invalid option.

#image.encode(string format, object? options) result<byte[], error>

encode returns the image encoded as format: "png", "jpeg" (or "jpg"), "gif", "bmp" or "tiff" (or "tif"). PNG and JPEG embed the color profile. GIF keeps the exact colors of images with up to 256 of them, and otherwise dithers to a fixed palette; pixels under 50% opacity become transparent. Options: quality (JPEG only, 1-100, default 90).

Fails with UnsupportedError for WebP, AVIF, HEIC, JPEG XL, ICO and QOI, which can't be encoded. Throws ValueError for an unknown format or a quality outside 1-100.

#image.fill(any color)

fill sets every pixel to color, in place, replacing what was there (transparency included) rather than blending. color is "#rrggbb", "#rrggbbaa", a CSS name such as "white", {r, g, b, a}, an *img.Gradient or an *img.Pattern. Throws ValueError for an invalid color.

#image.flipX() *img.Image

flipX returns a copy mirrored left to right.

#image.flipY() *img.Image

flipY returns a copy mirrored top to bottom.

#image.grayscale() *img.Image

grayscale returns a gray copy, each pixel's luminance as CSS grayscale(1) computes it; alpha is kept.

#image.height() int

height returns the height in pixels.

#image.hueRotate(number degrees) *img.Image

hueRotate returns a copy with every color's hue turned by degrees around the color wheel, as CSS hue-rotate() does; 0 and 360 leave it as it is. Throws ValueError for a non-finite angle.

#image.index() int

index returns this frame's position in its file, counting from 1. It is 1 for an image that isn't one of several.

#image.invert() *img.Image

invert returns a copy with every color inverted (255 - channel); alpha is kept.

#image.line(object from, object to, object? options)

line draws a straight line from {x, y} to {x, y}, in place. Options: color, lineWidth, lineCap, dash, dashOffset, opacity, blend and antialias. Throws ValueError for a malformed point or option and StateError when the image is closed.

#image.mapPixels(any fn) *img.Image

mapPixels returns a copy where each pixel is fn(color, x, y): color is the pixel as {r, g, b, a} with 0-255 channels and x, y its position, 1, 1 for the top-left pixel as in pixel and setPixel. fn returns the new color, in any form a color can take, or null to keep the pixel. It calls fn once per pixel, so it suits small images and per-pixel effects the filters don't cover; it takes about a second per megapixel. Throws ValueError when fn returns an invalid color and TypeError when fn is null.

#image.mask(*img.Image mask, object? options)

mask multiplies this image's alpha by mask's, in place, so the image shows only where mask is opaque. Options: channel is "alpha" (the default) or "luminance", which uses mask's brightness times its alpha, so white shows and black hides. Throws ValueError unless mask is the same size as this image, and StateError when either is closed.

#image.opacity(number amount) *img.Image

opacity returns a copy with every pixel's alpha multiplied by amount: 1 leaves it as it is and 0 makes it transparent. Throws ValueError for an amount outside 0-1.

#image.orient(int orientation) *img.Image

orient returns a copy turned and flipped so that an image stored with EXIF orientation 1-8 displays upright; the image itself is unchanged. Use it with img.info(data).orientation, or decode with {autoOrient: true}. Throws ValueError for an orientation outside 1-8.

#image.pixel(int x, int y) string

pixel returns the color at pixel x, y (1, 1 is the top-left one) as "#rrggbb" when it is opaque and "#rrggbbaa" otherwise, the form colors.stringify uses; every fully transparent pixel is "#00000000". Throws ValueError when the point is outside the image.

#image.pixelate(int size) *img.Image

pixelate returns a copy made of size x size pixel blocks, each the average color of the pixels under it, counting from the top-left corner; 1 leaves the image as it is. Throws ValueError for a size below 1.

#image.pixels() byte[]

pixels returns a copy of every pixel as bytes, four per pixel (red, green, blue, alpha, each 0-255, not premultiplied), row by row from the top-left corner, so pixel x, y starts at byte ((y - 1) * width + x - 1) * 4 + 1 of the 1-based array.

#image.polygon(object[] points, object? options)

polygon draws the closed shape through points (an array of {x, y}), in place; it may cross itself, and where it overlaps itself is inside (the nonzero rule). It takes rectangle's options except radius. Throws ValueError for a malformed point or option.

#image.polyline(object[] points, object? options)

polyline draws connected straight lines through points (an array of {x, y}), in place, with lineJoin ("miter", the default, "round" or "bevel") and miterLimit (default 10) shaping the corners; it takes line's other options. Throws ValueError for a malformed point or option.

#image.rectangle(object rect, object? options)

rectangle draws {x, y, width, height} in place. Options: fill, stroke, radius and the outline options line uses. Throws ValueError for a malformed rectangle, a negative size or an invalid option.

#image.resize(int width, int height, object? options) *img.Image

resize returns a copy scaled to width x height pixels; the image itself is unchanged. Pass 0 for one side to keep the aspect ratio. Options: filter is "lanczos" (the default, sharpest), "bicubic", "mitchell", "bilinear" (faster) or "nearest" (blocky, for pixel art); fit is "fill" (the default: exactly width x height), "contain" (as large as fits inside width x height, keeping the aspect ratio) or "cover" (fills width x height exactly, keeping the aspect ratio and cropping the overflow evenly). Throws ValueError for negative sizes, both sides 0, a result over 100 megapixels, or an unknown option value.

#image.rotate(number degrees) *img.Image

rotate returns a copy turned clockwise by degrees; the image itself is unchanged. Multiples of 90 are exact; other angles enlarge the copy to fit the turned image and leave its corners transparent. Throws ValueError for a non-finite angle.

#image.saturate(number amount) *img.Image

saturate returns a copy with its colors' saturation scaled by amount, as CSS saturate() does: 1 leaves it as it is, 0 is gray and 2 is twice as vivid. Throws ValueError for an amount outside 0-1000.

#image.save(string path, object? options) result<unit, error>

save encodes the image and writes it to path, choosing the format from the file extension (.png, .jpg, .jpeg, .gif, .bmp, .tif or .tiff) unless the format option names one. Options: format, and quality (JPEG only, 1-100, default 90).

Fails with NotFoundError, PermissionError or IOError when the file can't be written, and UnsupportedError as encode does. Throws ValueError when the format can't be told from path or is unknown.

#image.setColorProfile(byte[] profile)

setColorProfile replaces the ICC color profile that encode and save embed, in place; empty bytes remove it. Throws ValueError when profile isn't an ICC profile or is over 8 MB.

#image.setPixel(int x, int y, any color)

setPixel sets pixel x, y (1, 1 is the top-left one) to color ("#rrggbb", "#rrggbbaa", a CSS name such as "red", or {r, g, b, a}), in place, replacing it rather than blending. Throws ValueError for a point outside the image or an invalid color.

#image.setPixels(byte[] data)

setPixels replaces every pixel, in place, with data laid out as pixels() returns it. Throws ValueError unless data is exactly width * height * 4 bytes.

#image.sharpen(number amount, object? options) *img.Image

sharpen returns a copy sharpened with an unsharp mask: each color moves away from its blurred neighbourhood by amount, so 0 leaves the image as it is and 1 is a strong sharpen. Options: radius, the blur's standard deviation in pixels (default 1); larger radii sharpen coarser detail. Alpha is kept. Throws ValueError for an amount outside 0-100 or a radius outside 0-1000.

#image.size() {h: int (deprecated), height: int, w: int (deprecated), width: int}

size returns the image's {width, height} in pixels. The record also carries the deprecated keys w and h, the same values under the names img.Image.size used before.

#image.text(string text, object point, object? options)

text draws text with its top-left corner at {x, y}, in place. "\n" starts a new line. Options: font (default "sans"), size (default 16), color, align, baseline, maxWidth, lineHeight, opacity, blend and antialias. Throws ValueError for a malformed point or option.

#image.threshold(number level) *img.Image

threshold returns a black and white copy: white where a pixel's luminance is at least level (0-1), black elsewhere. Alpha is kept. Throws ValueError for a level outside 0-1.

#image.tint(any color, number amount) *img.Image

tint returns a copy with every color mixed toward color by amount: 0 leaves it as it is and 1 gives every pixel color's red, green and blue (scaled by color's own alpha). Alpha is kept. Throws ValueError for an invalid color or an amount outside 0-1.

#image.toDataURL(object? options) result<string, error>

toDataURL returns the image encoded as a data URL such as "data:image/png;base64,...", for HTML, CSS or an <img> tag. Options: format ("png", the default, "jpeg", "gif", "bmp" or "tiff") and quality (JPEG only, 1-100, default 90). Fails and throws as encode does.

#image.width() int

width returns the width in pixels.

*img.Path

#path.arc(object center, number radius, object? options) *img.Path

arc adds an arc of the circle around center, {x, y}, with radius in pixels, joined to the current point by a straight line, as a canvas arc() does. Angles are degrees, with 0 pointing right and 90 pointing down (clockwise on screen). Options: start (default 0), end (default 360) and counterclockwise (default false, sweeping clockwise from start to end). A sweep of 360 degrees or more draws the whole circle. Throws ValueError for a negative radius or a non-finite angle. Returns the path.

#path.arcTo(object control, object point, number radius) *img.Path

arcTo adds an arc with radius (pixels) that rounds the corner at control: it is tangent to the line from the current point to control and to the line from control to point (both {x, y}), joined to the current point by a straight line, as a canvas arcTo() does. The path then continues from where the arc ends, not from point. Throws ValueError for a negative radius. Returns the path.

#path.bounds() {height: number, width: number, x: number, y: number}?

bounds returns the smallest rectangle {x, y, width, height} holding the path's lines and curves, or null when the path draws nothing.

#path.circle(object center, number radius) *img.Path

circle adds a circle around center, {x, y}, with radius in pixels, as a closed subpath. Throws ValueError for a negative radius. Returns the path.

#path.clone() *img.Path

clone returns an independent copy of the path.

#path.close() *img.Path

close joins the current point back to the start of the subpath with a straight line, so an outline turns the corner there instead of ending in caps. Returns the path.

#path.cubicTo(object control1, object control2, object point) *img.Path

cubicTo adds a cubic Bézier curve from the current point, pulled toward control1 and then control2, to point (all {x, y}). Returns the path.

#path.ellipse(object center, object radii) *img.Path

ellipse adds an ellipse around center, {x, y}, with radii {x, y} in pixels, as a closed subpath. Throws ValueError for negative radii. Returns the path.

#path.lineTo(object point) *img.Path

lineTo adds a straight line from the current point to point, {x, y} (starting the subpath there when the path is empty). Returns the path.

#path.moveTo(object point) *img.Path

moveTo starts a new subpath at point, {x, y}. Returns the path.

#path.quadTo(object control, object point) *img.Path

quadTo adds a quadratic Bézier curve from the current point, pulled toward control, to point (both {x, y}). Returns the path.

#path.rect(object rect, object? options) *img.Path

rect adds the rectangle rect, {x, y, width, height}, as a closed subpath. Options: radius rounds the corners (pixels, default 0). Throws ValueError for a malformed rectangle or a negative size. Returns the path.

#path.rotate(number degrees, object? options) *img.Path

rotate turns everything already in the path clockwise by degrees around 1, 1 (the image's top-left corner), or around the center option, {x, y}. Throws ValueError for a non-finite angle. Returns the path.

#path.scale(number x, number y, object? options) *img.Path

scale stretches everything already in the path by x horizontally and y vertically, away from 1, 1 (the image's top-left corner) or from the center option, {x, y}; negative factors mirror it. Throws ValueError for a non-finite factor. Returns the path.

#path.translate(number x, number y) *img.Path

translate moves everything already in the path by x, y pixels. Throws ValueError for a non-finite distance. Returns the path.

*img.Gradient

#gradient.colorAt(number offset) string

colorAt returns the gradient's color at offset (0-1 between its first and last stop, outside that range as spread says) as "#rrggbb", or "#rrggbbaa" when it isn't opaque, for picking colors from a scale. Throws ValueError for a non-finite offset.

*img.Pattern

A handle with no methods of its own: pass it to the package's functions.

*img.Font

#font.name() string

name returns the font's full name from its name table, such as "Go Regular" or "DejaVu Sans Bold".

*img.Color

#color.a() int

a returns the color's alpha channel, 0 (transparent) to 255 (opaque).

#color.b() int

b returns the color's blue channel, 0 to 255.

#color.g() int

g returns the color's green channel, 0 to 255.

#color.r() int

r returns the color's red channel, 0 to 255.

#color.withAlpha(int a) img.Color

withAlpha returns a copy of the color with alpha a, from 0 (fully transparent) to 255 (opaque): img.rgb(255, 0, 0).withAlpha(128) is a half-transparent red. Throws ValueError for an alpha outside that range.