imagine
import imagine
Reading, writing, drawing and transforming raster images.
imagine covers the whole path an image takes through a program:
decoding it from a file or an upload, resizing and cropping it,
adjusting its colour, drawing shapes and text onto it, compositing
several together, and writing the result back out in whatever format
suits.
import imagine { Image, Font, LANCZOS }
Image.open('photo.jpg')
.thumbnail(600, 600, LANCZOS)
.sharpen(0.6)
.save('thumb.webp', { quality: 82 })
The shape of the API
Almost everything is a method on [[imagine.Image]], and almost everything returns an image, so operations chain. There is one rule worth learning up front:
- Operations that change the image’s size return a new image.
resize(),crop(),rotate(),flip()andpad()leave the original alone. - Everything else changes the image in place. Drawing, filters and compositing modify the image you called them on and hand it back for chaining.
Call clone() when you want to keep an image before filtering it.
Colours
Anywhere a colour is expected you can write a [[imagine.Color]], a
hexadecimal string, a CSS colour name, a packed 0xRRGGBBAA number, or
a list of channels. These are all the same red:
image.fill(Color(255, 0, 0))
image.fill('#ff0000')
image.fill('red')
image.fill(0xFF0000FF)
image.fill([255, 0, 0])
Alpha runs 0 (transparent) to 255 (opaque), as in CSS and PNG. The colour space conversions come from [[colors]], so hue is in degrees and saturation and lightness are in percentage points.
Pixels
An image is 8-bit RGBA with straight (not premultiplied) alpha, laid out
row by row with no padding, so pixel (x, y) begins at byte (y * width + x) * 4.
[[imagine.Canvas.pixels]] hands back that buffer live, which is the
fastest way to write an operation this module does not provide:
var buffer = image.pixels()
var total = buffer.length()
iter var at = 0; at < total; at += 4 {
buffer[at] = 255 - buffer[at]
}
Formats
Read: PNG, JPEG, GIF, BMP, TIFF, TGA, WebP, QOI, ICO, PNM, WBMP. Write: all of those plus AVIF.
AVIF is write-only, and TGA carries no signature of its own so it can only be identified by its file extension or by naming the format outright. [[imagine.capabilities]] reports what this build can actually do, so a program can check rather than guess.
Animated GIF and animated WebP are read through [[imagine.Animation]], and animated GIF can be written.
Drawing
Every filled shape becomes a polygon and goes through one anti-aliased scanline rasterizer, and every outline becomes the polygon around its stroke and goes through the same one. Shapes this module does not name can be drawn by handing [[imagine.Canvas.fill_polygon]] the points.
import imagine { Image, Color }
Image(300, 200, 'white')
.fill_rounded_rect(20, 20, 260, 160, 16, '#4f46e5')
.circle(150, 100, 50, 'white', { thickness: 4 })
.save('card.png')
Text
Text needs a font, loaded from a file with [[imagine.Font.load]] or
found on the machine with [[imagine.Font.system]]. Glyphs are placed by
advance width with kerning; complex scripts that need shaping are not
supported, and the Font documentation says exactly what that rules
out.
Memory
Four bytes per pixel, so a 6000x4000 photograph occupies 96 MB however small its file was. For images whose size you do not control, [[imagine.probe]] reads the dimensions out of the header without decoding anything.
var header = imagine.probe(upload)
if header == nil or header.width * header.height > 40000000 {
raise Exception('image is missing or too large')
}
The imagine API
Every public name in imagine, wherever it is declared. Each links to
the page that documents it.
| Name | Kind | Summary |
|---|---|---|
imagine.ALIGN_CENTER | constant | Lines of a multi-line string are centred against each other. |
imagine.ALIGN_LEFT | constant | Lines of a multi-line string start at the same left edge. |
imagine.ALIGN_RIGHT | constant | Lines of a multi-line string end at the same right edge. |
imagine.AVIF | constant | AVIF. |
imagine.Animation | class | A sequence of images with a delay between each, read from or written to an animated format. |
imagine.BICUBIC | constant | Bicubic (Catmull-Rom). |
imagine.BILINEAR | constant | Bilinear. |
imagine.BLACK | constant | Opaque black. |
imagine.BLEND_ADD | constant | Adds the channels, clamped at white. |
imagine.BLEND_COLOR_BURN | constant | Darkens the backdrop toward the source. |
imagine.BLEND_COLOR_DODGE | constant | Brightens the backdrop toward the source. |
imagine.BLEND_DARKEN | constant | Keeps whichever channel is darker. |
imagine.BLEND_DIFFERENCE | constant | The absolute difference between the two colours. |
imagine.BLEND_EXCLUSION | constant | Like difference, but lower contrast in the midtones. |
imagine.BLEND_HARD_LIGHT | constant | Overlay with the roles of source and backdrop swapped. |
imagine.BLEND_LIGHTEN | constant | Keeps whichever channel is lighter. |
imagine.BLEND_MULTIPLY | constant | Multiplies the two colours. |
imagine.BLEND_NORMAL | constant | Ordinary alpha compositing: the source is painted over the destination according to its alpha. |
imagine.BLEND_OVERLAY | constant | Multiply where the backdrop is dark, screen where it is light. |
imagine.BLEND_SCREEN | constant | The inverse of multiply. |
imagine.BLEND_SOFT_LIGHT | constant | A gentler hard light, as if the source were a diffuse spotlight. |
imagine.BLEND_SUBTRACT | constant | Subtracts the source from the backdrop, clamped at black. |
imagine.BLUE | constant | Opaque pure blue. |
imagine.BMP | constant | BMP. |
imagine.BOTTOM | constant | |
imagine.BOTTOM_LEFT | constant | |
imagine.BOTTOM_RIGHT | constant | |
imagine.BoundsError | class | Raised when a rectangle, crop or resize falls outside the image, or when a dimension is zero or negative. |
imagine.CAP_BUTT | constant | The stroke stops dead at its endpoint. |
imagine.CAP_ROUND | constant | The stroke ends in a half-disc, so it reaches half its width past the endpoint. |
imagine.CAP_SQUARE | constant | The stroke ends in a square, so it reaches half its width past the endpoint, the same distance a round cap… |
imagine.CENTER | constant | |
imagine.CYAN | constant | Opaque cyan. |
imagine.Canvas | class | A mutable RGBA pixel surface and everything that draws onto one. |
imagine.Color | class | An 8-bit RGBA colour. |
imagine.DecodeError | class | Raised when image data cannot be read: the bytes are not an image at all, the format is one this build cannot… |
imagine.EDGE_CLAMP | constant | Pixels off the edge take the value of the nearest edge pixel. |
imagine.EDGE_TRANSPARENT | constant | Pixels off the edge are treated as transparent black. |
imagine.EDGE_WRAP | constant | Pixels off the edge wrap to the opposite side. |
imagine.EncodeError | class | Raised when an image cannot be written in the requested format, either because this build has no encoder for… |
imagine.FLIP_BOTH | constant | Mirror both ways at once, which is the same as rotating 180 degrees. |
imagine.FLIP_HORIZONTAL | constant | Mirror left to right. |
imagine.FLIP_VERTICAL | constant | Mirror top to bottom. |
imagine.Font | class | A typeface at a particular size, ready to draw with. |
imagine.FontError | class | Raised when a font cannot be parsed, cannot be found on the system, or does not carry the horizontal metrics… |
imagine.FormatError | class | Raised when a format name is not one imagine knows, or when a file extension cannot be mapped to a format. |
imagine.GAUSSIAN | constant | Gaussian. |
imagine.GIF | constant | GIF. |
imagine.GRAY | constant | Opaque mid grey. |
imagine.GREEN | constant | Opaque pure green. |
imagine.ICO | constant | Windows ICO. |
imagine.Image | class | A raster image: a rectangle of 8-bit RGBA pixels, everything that draws onto one, and everything that… |
imagine.ImageError | class | Base class for every error the imagine module raises. |
imagine.JPEG | constant | JPEG. |
imagine.LANCZOS | constant | Lanczos with a 3-lobe window. |
imagine.LEFT | constant | |
imagine.MAGENTA | constant | Opaque magenta. |
imagine.NEAREST | constant | Nearest-neighbour. |
imagine.PNG | constant | PNG. |
imagine.PNM | constant | Netpbm (PBM, PGM, PPM). |
imagine.QOI | constant | QOI, the Quite OK Image format. |
imagine.RED | constant | Opaque pure red. |
imagine.RIGHT | constant | |
imagine.StrokeFont | class | The built-in font: a sans-serif drawn from centre-line strokes rather than loaded from a file. |
imagine.TGA | constant | Truevision TGA. |
imagine.TIFF | constant | TIFF. |
imagine.TOP | constant | |
imagine.TOP_LEFT | constant | Where a smaller image or a piece of text sits inside a larger box, used by Image.cover(), Image.contain()… |
imagine.TOP_RIGHT | constant | |
imagine.TRANSPARENT | constant | |
imagine.WBMP | constant | Wireless Bitmap. |
imagine.WEBP | constant | WebP. |
imagine.WHITE | constant | Opaque white. |
imagine.YELLOW | constant | Opaque yellow. |
imagine.capabilities | function | What this build can do, as a dictionary holding decode, encode and animated, each a list of format… |
imagine.create | function | Creates a new image. |
imagine.decode | function | Decodes image data held in memory. |
imagine.detect | function | Identifies image data by its contents, returning a format name or nil. |
imagine.filters.LUMA | constant | Rec. |
imagine.filters.MATRIX_LUMA | constant | The luma weights the colour-matrix filters use, from the SVG and CSS filter specifications. |
imagine.filters.box_blur_kernel | function | A 3x3 box blur kernel; every neighbour counts equally. |
imagine.filters.brightness_lut | function | A lookup table that adds a fixed amount to every value. |
imagine.filters.build_lut | function | Builds a 256-entry lookup table from a function. |
imagine.filters.combine_matrices | function | Multiplies two colour matrices, giving one matrix with the effect of applying first and then second. |
imagine.filters.contrast_lut | function | A lookup table that pushes values away from or toward mid-grey. |
imagine.filters.duotone_matrix | function | A colour matrix that replaces every pixel’s colour with a blend between two colours chosen by its brightness,… |
imagine.filters.edge_kernel | function | A 3x3 Laplacian kernel that leaves only the edges. |
imagine.filters.emboss_kernel | function | A 3x3 kernel that lifts edges into a grey relief. |
imagine.filters.gamma_lut | function | A lookup table applying a gamma curve. |
imagine.filters.gaussian_kernel | function | A square Gaussian kernel with the given standard deviation. |
imagine.filters.grayscale_matrix | function | A colour matrix that collapses every colour to its grey of equal perceived brightness. |
imagine.filters.hue_rotate_matrix | function | A colour matrix that rotates every hue around the colour wheel by an angle in degrees, leaving brightness and… |
imagine.filters.identity_lut | function | A lookup table that leaves every value alone. |
imagine.filters.identity_matrix | function | The identity colour matrix: applying it changes nothing. |
imagine.filters.invert_lut | function | A lookup table that inverts every value. |
imagine.filters.levels_lut | function | A lookup table implementing a levels adjustment. |
imagine.filters.mean_removal_kernel | function | A 3x3 kernel that removes local mean, exaggerating detail. |
imagine.filters.posterize_lut | function | A lookup table that reduces each channel to a fixed number of evenly spaced steps. |
imagine.filters.saturation_matrix | function | A colour matrix that scales saturation. |
imagine.filters.scale_lut | function | A lookup table that scales every value by a factor. |
imagine.filters.sepia_matrix | function | A colour matrix approximating the warm brown cast of a sepia photograph. |
imagine.filters.sharpen_kernel | function | A 3x3 sharpening kernel. |
imagine.filters.smooth_kernel | function | A 3x3 smoothing kernel weighted toward the centre pixel. |
imagine.filters.threshold_lut | function | A lookup table that forces every value to black or white. |
imagine.formats.animatable | function | Every format this build can read as an animation. |
imagine.formats.decode_wbmp | function | Decodes a WBMP into straight RGBA pixels. |
imagine.formats.detect | function | Identifies image data by its content rather than its name, or returns nil when the bytes are not a… |
imagine.formats.encode_wbmp | function | Encodes straight RGBA pixels as a WBMP. |
imagine.formats.extension_for | function | The file extension a format is normally written with, without a leading dot. |
imagine.formats.from_extension | function | The format name a file extension implies, or nil when the extension is not one this module knows. |
imagine.formats.mime_for | function | The IANA media type for a format, suitable for a Content-Type header. |
imagine.formats.normalize | function | Normalises a format name, accepting the common aliases. |
imagine.formats.probe | function | Reads an image’s format and dimensions without decoding its pixels. |
imagine.formats.readable | function | Every format this build can read. |
imagine.formats.writable | function | Every format this build can write. |
imagine.open | function | Opens an image file. |
imagine.probe | function | Reads an image’s format and dimensions from its header, without decoding any pixels. |
imagine.strokefont.ASCENDER | constant | How far the tallest glyphs rise above the baseline. |
imagine.strokefont.CAP_HEIGHT | constant | The height of a capital letter. |
imagine.strokefont.DESCENDER | constant | How far descenders fall below the baseline, as a negative number. |
imagine.strokefont.EM | constant | The em square’s height in design units. |
imagine.strokefont.GLYPHS | constant | Every glyph, keyed by character. |
imagine.strokefont.NOTDEF | constant | What an unmapped character draws: an empty box, the same convention a font uses for a glyph it does not have. |
imagine.strokefont.WEIGHT | constant | The default stroke width in design units, a little under a tenth of the em, which is the usual weight for a… |
imagine.strokefont.X_HEIGHT | constant | The height of a lowercase letter with no ascender. |
Submodules
| Module | Reached as | Summary |
|---|---|---|
imagine.animation | imagine.animation.* | Animation: an ordered set of frames with per-frame delays, which is what an animated GIF or WebP decodes to… |
imagine.canvas | imagine.canvas.* | Canvas: the drawing surface. |
imagine.color | imagine.* | Color: one RGBA colour, and the conversions between the ways of naming one. |
imagine.constants | imagine.* | Every named constant the module takes as an argument: resampling filters, blend modes, line caps, edge… |
imagine.errors | imagine.* | Every error the module raises, under ImageError as their root. |
imagine.filters | imagine.filters.* | The maths behind the filters: colour matrices, lookup tables and convolution kernels. |
imagine.font | imagine.font.* | Font: a loaded TrueType or OpenType face, ready to measure and draw text with. |
imagine.formats | imagine.formats.* | What the build can actually read and write, and how to tell one format from another. |
imagine.image | imagine.image.* | Image: a raster image as an RGBA pixel buffer, and everything that transforms one. |
imagine.strokefont | imagine.strokefont.* | StrokeFont: the font built into the module, defined as stroke geometry rather than glyph outlines. |
Functions
open()
imagine.open(source, options) -> Image
Opens an image file.
A shorthand for [[imagine.Image.open]].
Parameters
source(string|file)options(?dict)
Returns Image
decode()
imagine.decode(data, options) -> Image
Decodes image data held in memory.
A shorthand for [[imagine.Image.decode]].
Parameters
data(bytes)options(?dict)
Returns Image
create()
imagine.create(width, height, fill) -> Image
Creates a new image.
A shorthand for the [[imagine.Image]] constructor.
Parameters
width(number)height(number)fill(?Color|string|number)
Returns Image
probe()
imagine.probe(data) -> ?dict
Reads an image’s format and dimensions from its header, without decoding any pixels.
Returns {format, width, height}, or nil when the data is not a
recognisable image. Reading a large JPEG’s header costs microseconds
where decoding it costs tens of milliseconds, which makes this the right
way to size up an upload before committing to it.
Parameters
data(bytes)
Returns ?dict
detect()
imagine.detect(data) -> ?string
Identifies image data by its contents, returning a format name or nil.
Parameters
data(bytes)
Returns ?string
capabilities()
imagine.capabilities() -> dict
What this build can do, as a dictionary holding decode, encode and
animated, each a list of format names.
Worth checking rather than assuming: AVIF appears under encode but not
decode, and which formats are compiled in can differ between builds.
Returns dict
2021, Richard Ore and Zuri contributors