Skip to main content

Conversion and clamping

Conversion always keeps the space tag and alpha consistent: after plt_convert(c, 'lab') the value says lab, and plt_convert(..., 'srgb') brings it back. Palette performs the math; this extension only chooses the route.

plt_convert(color, space)​

Signature: plt_convert(color plt_color, space VARCHAR) -> plt_color

Converts to the target space without gamut clamping. Values outside the target's range are returned as they are, which is useful for inspecting intermediate results and for conversions that are supposed to be reversible.

SELECT plt_to_hex(plt_srgb(1, 0.5, 0)) AS srgb,
plt_components(plt_convert(plt_srgb(1, 0.5, 0), 'oklch')) AS as_oklch,
plt_components(plt_convert(plt_srgb(1, 0.5, 0), 'xyz')) AS as_xyz;

Alpha is copied, not converted:

SELECT plt_convert(plt_with_alpha(plt_srgb(1, 0, 0), 0.25), 'lab') AS converted,
plt_alpha(plt_convert(plt_with_alpha(plt_srgb(1, 0, 0), 0.25), 'lab')) AS alpha;

plt_convert_clamped(color, space)​

Signature: plt_convert_clamped(color plt_color, space VARCHAR) -> plt_color

Converts, then clamps the result to the target space's standard range. This is what you want before rendering: a color outside sRGB comes back as the nearest in-gamut sRGB color instead of 1.5 or -0.3 components.

SELECT plt_components(plt_convert(plt_lab(50, 200, 200), 'srgb')) AS unclamped,
plt_components(plt_convert_clamped(plt_lab(50, 200, 200), 'srgb')) AS clamped;

plt_clamp(color)​

Signature: plt_clamp(color plt_color) -> plt_color

Clamps in the color's own space, without converting. Lab's a* and b* stop at ±127, RGB components at 0..1, but a hue is not forced into 0..360 by this function.

SELECT plt_components(plt_lab(50, 200, 200)) AS out_of_range,
plt_components(plt_clamp(plt_lab(50, 200, 200))) AS clamped,
plt_components(plt_srgb(-0.2, 1.4, 0.5)) AS out_of_gamut,
plt_components(plt_clamp(plt_srgb(-0.2, 1.4, 0.5))) AS in_gamut;

plt_is_within_bounds(color)​

Signature: plt_is_within_bounds(color plt_color) -> BOOLEAN

Whether plt_clamp would change anything. Combine it with a WHERE clause to find colors that a renderer would have to clip.

SELECT plt_is_within_bounds(plt_lab(50, 200, 200)) AS lab_ok,
plt_is_within_bounds(plt_clamp(plt_lab(50, 200, 200))) AS clamped_ok,
plt_is_within_bounds(plt_srgb(1, 0, 0)) AS srgb_ok;
SELECT c, plt_is_within_bounds(c) AS in_gamut
FROM (VALUES
(plt_srgb(0.2, 0.4, 0.6)),
(plt_srgb(-0.1, 0.5, 0.5)),
(plt_lch(50, 120, 40))
) t(c);

Which route does a conversion take?​

All conversions go through XYZ (D65), the hub of palette's conversion graph. Encoded RGB spaces are decoded first; D50 spaces (ProPhoto RGB, xyz_d50) are adapted with Bradford; CAM16 uses palette's default viewing conditions. You do not choose the route, but plt_convert and plt_convert_clamped produce the same numbers on it, and one round trip through any invertible space returns the original color to floating-point precision:

SELECT plt_to_hex(plt_convert(plt_convert(plt_srgb(0.2, 0.4, 0.6), 'hsluv'), 'srgb')) AS round_trip;

The one-dimensional luma* spaces are not invertible by design, and srgb_gamma is palette's deprecated gamma 2.2 encoding — use plt_srgb unless you specifically need it.

plt_equals(a, b)​

Signature: plt_equals(a plt_color, b plt_color) -> BOOLEAN

Whether two colors are the same once rendered: both are converted to sRGB, quantized to 8 bits per channel, and alpha is rounded to two decimals. This is tinycolor's equals, and it is usually what you mean when comparing colors from different spaces — plt_srgb(1, 0, 0) and plt_hsl(0, 1, 0.5) are equal. Two colors that a perceptually uniform space might separate by a hair still count as equal here:

SELECT plt_equals(plt_srgb(1, 0, 0), plt_hsl(0, 1, 0.5)) AS across_spaces,
plt_equals(plt_with_alpha(plt_srgb(1, 0, 0), 0.5), plt_with_alpha(plt_srgb(1, 0, 0), 0.6)) AS alpha_differs,
plt_equals(plt_srgb(0.5, 0.5, 0.5), plt_srgb(0.501, 0.5, 0.5)) AS within_a_byte;

Comparing the STRUCTs directly with = does not do this: it also compares the space tag and the full floating-point components, so two srgb values that render identically but differ in the last bit are != there.

Errors​

An unknown space name fails the statement; the message names the function:

SELECT plt_convert(plt_srgb(1, 0, 0), 'nope');
SELECT plt_convert_clamped(plt_srgb(1, 0, 0), 'not-a-space');