imagine.color
import imagine
Everything here is re-exported by
imagine, soimport imagineis enough and the names are called asimagine.*. Importingimagine.coloron its own works too and reaches the same definitions.
Color: one RGBA colour, and the conversions between the ways of naming
one.
It reads and writes hex, RGB, HSL and HSV, blends against another colour, and reports its own luminance. The named constants are the handful worth not spelling out every time.
Constants
TRANSPARENT
imagine.TRANSPARENT
BLACK
imagine.BLACK: Color
Opaque black.
WHITE
imagine.WHITE: Color
Opaque white.
GRAY
imagine.GRAY: Color
Opaque mid grey.
RED
imagine.RED: Color
Opaque pure red.
GREEN
imagine.GREEN: Color
Opaque pure green.
This is the additive primary, not CSS’s green, which is half as
bright. Color.named('green') gives that one.
BLUE
imagine.BLUE: Color
Opaque pure blue.
YELLOW
imagine.YELLOW: Color
Opaque yellow.
CYAN
imagine.CYAN: Color
Opaque cyan.
MAGENTA
imagine.MAGENTA: Color
Opaque magenta.
Classes
Color
class imagine.Color
An 8-bit RGBA colour.
Alpha runs 0 (fully transparent) to 255 (fully opaque), the same
convention as CSS, PNG and every modern image format. Colours are
immutable: every method that would change one returns a new Color, so
a colour held in a variable is safe to pass around and reuse.
import imagine { Color }
var red = Color(255, 0, 0)
var glass = red.with_alpha(128)
var brand = Color.hex('#4f46e5')
Relationship to the colors module
The colour space conversions here are the standard library’s, from
[[colors]]; Color is the value type that carries a result around and
puts it into a pixel buffer. Anything colors can do to a hexadecimal
string can be done to a Color by way of to_hex() and Color.hex().
Three conventions follow from that and are worth knowing:
-
Hue is in degrees and saturation, lightness and value are in percentage points (0-100), exactly as in
colorsand in CSS. They are not 0-1 ratios. -
to_hex()returns a leading#, because that is the form you paste into a stylesheet.colorsreturns the bare form. Both are accepted everywhere either is. -
A malformed hexadecimal colour or an unknown colour name raises
ValueError, which is whatcolorsreports for them, rather than this module’s ownImageError. An argument of the wrong type raisesTypeErrorfrom the declaration it failed. -
printable — has a
@to_string(), soechoandprint()show something useful -
serializable — has a
@to_json(), so it can be handed straight tojson.encode()
Fields
| Field | Type | Description |
|---|---|---|
r | number | The red channel, 0-255. |
g | number | The green channel, 0-255. |
b | number | The blue channel, 0-255. |
a | number | The alpha channel, 0 (transparent) to 255 (opaque). |
Constructor
imagine.Color(r, g, b, a)
Creates a colour from its channels.
Values outside 0-255 are clamped rather than rejected, and fractional values are rounded.
Parameters
r(number) — The red channel.g(number) — The green channel.b(number) — The blue channel.a(?number) — The alpha channel - Default: 255 (opaque).
Color.rgb()
imagine.Color.rgb(r, g, b) -> Color
Creates an opaque colour from red, green and blue channels.
The same as calling the constructor with three arguments; it exists so a
call site that also uses Color.hsl() or Color.lab() can name the
space it is working in.
Parameters
r(number)g(number)b(number)
Returns Color
Color.rgba()
imagine.Color.rgba(r, g, b, a) -> Color
Creates a colour from red, green, blue and alpha channels.
Parameters
r(number)g(number)b(number)a(number)
Returns Color
Color.gray()
imagine.Color.gray(value, a) -> Color
Creates a shade of grey.
Parameters
value(number) — The brightness, 0 (black) to 255 (white).a(?number) — The alpha channel - Default: 255.
Returns Color
Color.packed()
imagine.Color.packed(value: number) -> Color
Creates a colour from a packed 0xRRGGBBAA integer.
Alpha is in the least significant byte, which is the layout
to_packed() produces and the one this module passes around internally.
%> Color.packed(0xFF0000FF).to_hex()
'#ff0000'
Parameters
value(number)
Returns Color
Color.hex()
imagine.Color.hex(spec) -> Color
Creates a colour from a CSS-style hexadecimal string.
All four CSS lengths are accepted, with or without the leading #, in
either case: #rgb, #rgba, #rrggbb and #rrggbbaa. The short forms
double each digit, so #f0a is #ff00aa.
Parameters
spec(string)
Returns Color
Raises ValueError When spec is not a hexadecimal colour.
Color.named()
imagine.Color.named(name) -> Color
Looks up one of the CSS named colours.
Matching ignores case, spaces, hyphens and underscores, so 'Dark Sea Green'
and 'darkseagreen' are the same colour. The full CSS Color Level 4
list is available, including both spellings of the greys and
transparent.
Parameters
name(string)
Returns Color
Raises ValueError When name is not a CSS colour name.
Color.hsl()
imagine.Color.hsl(h, s, l, a) -> Color
Creates a colour from hue, saturation and lightness.
Parameters
h(number) — The hue in degrees. Wraps, so 400 is the same as 40.s(number) — The saturation in percentage points, 0-100.l(number) — The lightness in percentage points, 0 (black) to 100 (white).a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.hsv()
imagine.Color.hsv(h, s, v, a) -> Color
Creates a colour from hue, saturation and value (also called HSB).
Parameters
h(number) — The hue in degrees.s(number) — The saturation in percentage points, 0-100.v(number) — The value in percentage points, 0-100.a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.hwb()
imagine.Color.hwb(h, w, b, a) -> Color
Creates a colour from hue, whiteness and blackness.
Parameters
h(number) — The hue in degrees.w(number) — The whiteness in percentage points, 0-100.b(number) — The blackness in percentage points, 0-100.a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.cmyk()
imagine.Color.cmyk(c, m, y, k, a) -> Color
Creates a colour from cyan, magenta, yellow and key components.
This is the naive conversion, not a colour-managed one: it takes no account of an output profile, so it is right for generating colours and wrong for predicting what a printing press will do.
Parameters
c(number) — 0-100.m(number) — 0-100.y(number) — 0-100.k(number) — 0-100.a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.xyz()
imagine.Color.xyz(x, y, z, a) -> Color
Creates a colour from CIE XYZ tristimulus values, against the D65 white point.
Parameters
x(number)y(number)z(number)a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.lab()
imagine.Color.lab(l, a_star, b_star, a) -> Color
Creates a colour from CIE Lab*, against the D65 white point.
Lab is perceptually uniform, which makes it the right space for interpolating between two colours when the intermediate steps need to look evenly spaced.
Parameters
l(number) — Lightness, 0-100.a_star(number) — The green-red axis.b_star(number) — The blue-yellow axis.a(?number) — The alpha channel, 0-255 - Default: 255.
Returns Color
Color.parse()
imagine.Color.parse(value) -> Color
Turns whatever a caller passed into a Color.
Every drawing method calls this on its colour argument, so a colour can be written the shortest way that reads clearly at the call site. Accepted forms:
- a
Color, returned unchanged - a hexadecimal string,
'#4f46e5'or'4f46e5' - a CSS colour name,
'rebeccapurple' - a packed
0xRRGGBBAAnumber - a list of 3 or 4 channels,
[255, 0, 0]
Parameters
value(Color|string|number|list)
Returns Color
Raises ImageError When value is not a colour in any of those
forms.
Color.to_packed()
imagine.Color.to_packed() -> number
Returns the colour as a packed 0xRRGGBBAA integer.
Returns number
Color.to_hex()
imagine.Color.to_hex(always_alpha) -> string
Returns the colour as a hexadecimal string with a leading #.
The alpha digits are included only when the colour is not fully opaque,
unless always_alpha asks for them, so the common case reads as the
familiar six digits.
%> Color(255, 0, 0).to_hex()
'#ff0000'
%> Color(255, 0, 0, 128).to_hex()
'#ff000080'
Parameters
always_alpha(?bool) — Always emit the alpha digits - Default: false.
Returns string
Color.to_hsl()
imagine.Color.to_hsl() -> dict
Returns the colour in HSL, as a dictionary holding h (degrees), s
and l (percentage points) and a (0-255).
Returns dict
Color.to_hsv()
imagine.Color.to_hsv() -> dict
Returns the colour in HSV, as a dictionary holding h (degrees), s
and v (percentage points) and a (0-255).
Returns dict
Color.to_hwb()
imagine.Color.to_hwb() -> dict
Returns the colour in HWB, as a dictionary holding h (degrees), w
and b (percentage points) and a (0-255).
Returns dict
Color.to_cmyk()
imagine.Color.to_cmyk() -> dict
Returns the colour in CMYK, as a dictionary holding c, m, y and
k (percentage points) and a (0-255).
Returns dict
Color.to_xyz()
imagine.Color.to_xyz() -> dict
Returns the colour in CIE XYZ, as a dictionary holding x, y, z and
a (0-255).
Returns dict
Color.to_lab()
imagine.Color.to_lab() -> dict
Returns the colour in CIE Lab*, as a dictionary holding l, a_star,
b_star and a (0-255).
The axes are named a_star and b_star rather than a and b so
neither collides with the alpha channel.
Returns dict
Color.with_alpha()
imagine.Color.with_alpha(a) -> Color
Returns a copy of the colour with a different alpha channel.
Parameters
a(number) — The new alpha, 0-255.
Returns Color
Color.fade()
imagine.Color.fade(factor) -> Color
Returns a copy of the colour with its alpha scaled by a factor.
A factor of 0.5 halves whatever opacity the colour already had, rather than setting it to half opaque, so fading an already translucent colour behaves the way stacking two layers would.
Parameters
factor(number) — 0 (transparent) to 1 (unchanged).
Returns Color
Color.lighten()
imagine.Color.lighten(amount) -> Color
Returns a lighter version of the colour, keeping its hue.
Parameters
amount(number) — Percentage points of HSL lightness to add, 0-100.
Returns Color
Color.darken()
imagine.Color.darken(amount) -> Color
Returns a darker version of the colour, keeping its hue.
Parameters
amount(number) — Percentage points of HSL lightness to remove, 0-100.
Returns Color
Color.saturate()
imagine.Color.saturate(amount) -> Color
Returns a more saturated version of the colour.
Parameters
amount(number) — Percentage points of HSL saturation to add, 0-100.
Returns Color
Color.desaturate()
imagine.Color.desaturate(amount) -> Color
Returns a less saturated version of the colour.
An amount of 100 removes all colour, which is not the same as
to_grayscale(): this keeps HSL lightness, that one weights the
channels for perceived brightness.
Parameters
amount(number) — Percentage points of HSL saturation to remove, 0-100.
Returns Color
Color.rotate_hue()
imagine.Color.rotate_hue(degrees) -> Color
Returns the colour rotated around the hue wheel.
Parameters
degrees(number) — How far to rotate. Negative rotates backwards.
Returns Color
Color.mix()
imagine.Color.mix(other, t) -> Color
Returns a linear blend of this colour and another.
All four channels are interpolated, alpha included, so mixing a transparent colour in also makes the result more transparent.
Parameters
other(Color|string|number|list) — The colour to blend toward.t(number) — 0 returns this colour, 1 returns other.
Returns Color
Color.over()
imagine.Color.over(backdrop) -> Color
Composites this colour over an opaque backdrop and returns the flattened result.
This is what happens when a translucent colour is drawn onto a solid background, and it is how a colour has to be resolved before writing it to a format with no alpha channel, such as JPEG.
Parameters
backdrop(Color|string|number|list)
Returns Color
Color.luminance()
imagine.Color.luminance() -> number
Returns the relative luminance of the colour, 0 (black) to 1 (white), as defined by WCAG 2.
The alpha channel is ignored: luminance is a property of the colour
itself, and a translucent colour’s apparent brightness depends on
whatever is behind it. Composite with over() first if that matters.
Returns number
Color.contrast_ratio()
imagine.Color.contrast_ratio(other) -> number
Returns the WCAG 2 contrast ratio between this colour and another, from 1 (identical) to 21 (black against white).
Text is generally held to need a ratio of at least 4.5 against its background, or 3 at large sizes.
Parameters
other(Color|string|number|list)
Returns number
Color.is_dark()
imagine.Color.is_dark() -> bool
Returns true when the colour is dark enough that light text reads
better on it than dark text.
The threshold is the luminance at which contrast against white overtakes contrast against black.
Returns bool
Color.is_light()
imagine.Color.is_light() -> bool
The opposite of is_dark().
Returns bool
Color.best_contrast()
imagine.Color.best_contrast(light, dark) -> Color
Returns whichever of two colours reads better on top of this one.
Parameters
light(?Color|string) — The colour to use on a dark background - Default: white.dark(?Color|string) — The colour to use on a light background - Default: black.
Returns Color
Color.to_grayscale()
imagine.Color.to_grayscale() -> Color
Returns the grey with the same perceived brightness as this colour, keeping its alpha.
This uses the Rec. 601 luma weights, which is what image editors mean by
“greyscale” and what Image.grayscale() applies. It is deliberately not
[[colors.grayscale]], which sets HSL saturation to zero and so keeps a
bright yellow as bright as a dark blue.
Returns Color
Color.invert()
imagine.Color.invert() -> Color
Returns the colour with its red, green and blue channels inverted.
Alpha is left alone, since inverting it would turn a visible colour invisible rather than changing how it looks.
Returns Color
Color.equals()
imagine.Color.equals(other) -> bool
Returns true when both colours have identical channels.
Parameters
other(any)
Returns bool
Color.to_list()
imagine.Color.to_list() -> list
Returns the colour as a list of its four channels.
Returns list
Color.to_dict()
imagine.Color.to_dict() -> dict
Returns the colour as a dictionary holding r, g, b and a.
Returns dict
Color.to_string()
imagine.Color.to_string()
2026, Richard Ore and Zuri contributors