Introduction and Overview
The ColourMap module provides an interface for constructing combinations of colour mapping functions, as used by SpriteExtend, DrawFile and others. These functions allow colours within images to be manipulated to affect their rendering.
Typical uses for colour mapping include highlighting or fading graphical elements, as used within the Window Manager. The colour mapping is performed through a colour mapping descriptor -- a function pointer and workspace pair which are used to translate colours. The ColourMap module provides descriptors for commonly used colour operations, allowing clients to perform mappings without needing to provide their own implementations.
Multiple colour mapping operations may be chained together: each operation in the chain is applied in turn to produce the final result.
Technical Details
Sequence of operations
The normal sequence of operations to create and use a colour mapping descriptor is:
- Call SWI ColourMap_Start, passing R1 = 0, to read the initial size of the workspace required.
- Allocate a workspace block of at least the size returned.
- Call SWI ColourMap_Start again, passing the workspace pointer and its size in R1 and R2, to initialise the workspace.
- Call SWI ColourMap_ApplyMapping with the desired mapping type and parameters.
- If an error occurs and R2 is returned as a negative value, the workspace was too small. Reallocate the workspace to the size indicated (negated), reinitialise it with SWI ColourMap_Start, and re-apply all mappings. If R2 is positive on error, the error relates to the mapping itself.
- Repeat calling SWI ColourMap_ApplyMapping to add further mappings, as needed.
- Call SWI ColourMap_End to obtain a colour mapping descriptor suitable for use with DrawFile, SpriteExtend, or other clients.
- Release the workspace once colour mapping is no longer required.
Workspace properties
The workspace block is provided by the client. Its required size can be determined by calling SWI ColourMap_Start with R1 = 0. Providing a larger workspace than the minimum will reduce the likelihood of reallocation requests being returned from subsequent SWI ColourMap_ApplyMapping calls.
During the construction phase (between SWI ColourMap_Start and SWI ColourMap_End), the workspace may be freely relocated in memory by the client, as the ColourMap module holds no direct references to it during this phase. However, once SWI ColourMap_End has been called, the workspace must remain at a fixed address. The descriptor returned contains a direct pointer into the workspace; relocating the workspace after this point will invalidate the descriptor.
If the workspace must be moved after SWI ColourMap_End has been called, call SWI ColourMap_End again at the new location to obtain a fresh, valid descriptor.
The client may release the workspace memory once the colour mappings it provides are no longer required. The ColourMap module holds no references to the workspace after use. Multiple independent clients may use the ColourMap module simultaneously.
If the ColourMap module is killed or reinitialised, any descriptors previously returned by SWI ColourMap_End may become invalid.
The workspace requirement for any given set of mappings remains constant regardless of the parameters supplied to those mappings. This allows clients to reuse workspace blocks for similar mappings without relocation.
The workspace format may change between versions of the module. Workspace blocks must not be stored in persistent storage, such as data files or embedded within applications.
No executable code is placed within the workspace, so it may be located outside the 26-bit executable address range.
Workspace format
The internal layout of the workspace is shown below. Clients should treat the workspace as opaque and must not modify it directly.
| Offset | Contents |
|---|---|
| 0 | Magic word (&9072340E), used to validate the workspace |
| 4 | Size of workspace currently used, in bytes |
| 8 | Colour mapping descriptor: workspace pointer (or 0 if not yet valid) |
| 12 | Colour mapping descriptor: function pointer (or 0 if not yet valid) |
| 16 | First mapping entry (see below); further entries follow contiguously |
Each mapping entry within the workspace has the following layout:
| Offset | Contents |
|---|---|
| 0 | Mapping type (a Mapping types value), or -1 to mark the end of the list |
| 4 | Size of the mapping's workspace data, in bytes |
| 8 | Function pointer for this mapping (filled in by SWI ColourMap_End) |
| 12 | Mapping-specific workspace data |
Colour mapping descriptor
The colour mapping descriptor is a two-word block returned by SWI ColourMap_End. It may be passed directly to interfaces such as SpriteExtend and DrawFile which accept a colour mapping descriptor pointer.
| Offset | Contents |
|---|---|
| 0 | Pointer to the workspace for this mapping |
| 4 | Pointer to the function that applies this mapping |
The descriptor is valid only while the workspace remains at its current address and the ColourMap module remains loaded.
Mapping types
The following mapping types are recognised by ColourMap_ApplyMapping. The type value is supplied in bits 0-7 of R0.
Colour format
Colours supplied to ColourMap SWIs and returned by them use the RISC OS 32-bit colour word format:
| Offset | Contents |
|---|---|
| 0 | Blue component (bits 24-31 of word, i.e. bits 7-0 at byte offset 3) |
| 1 | Green component |
| 2 | Red component |
| 3 | Always 0 (unused) |
In hexadecimal notation this is written as &BBGGRR00.
SWI calls
| R0 | = | Flags (reserved, must be 0) |
| R1 | = | Pointer to the workspace block, or 0 to read the minimum required size |
| R2 | = | Size of the workspace block, in bytes (ignored if R1 = 0) |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | If called with R1 = 0: minimum workspace size required (positive). If called with a workspace pointer: size of workspace used (positive). |
This SWI initialises a block of memory for use as a ColourMap workspace, or returns the minimum size required for such a block.
When called with R1 = 0, the minimum workspace size is returned in R2. This is the size needed to hold the standard mapping header and at least one instance of the most common mapping (blend to colour). Allocating a larger workspace will reduce the chance of reallocation being requested by subsequent SWI ColourMap_ApplyMapping calls.
When called with R1 pointing to an allocated block, the workspace is initialised ready for mappings to be added. If the workspace is too small, an Error_ColourMap_NotEnoughWorkspace error is returned and R2 is set to the negative of the size required.
| R0 | = |
Flags and mapping type:
| ||||||||||||
| R1 | = | Pointer to the workspace block | ||||||||||||
| R2 | = | Size of the workspace block, in bytes | ||||||||||||
| R3 | = | Dependent on mapping type (see below) | ||||||||||||
| R4 | = | Dependent on mapping type (see below) | ||||||||||||
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | If workspace was too small: negative value giving the required workspace size. Otherwise preserved. |
This SWI appends a colour mapping operation to those already held in the workspace. Mappings are applied in the order they were added when SWI ColourMap_End constructs the final descriptor.
If the workspace is too small to accommodate the new mapping, a Error_ColourMap_NotEnoughWorkspace error is returned and R2 is set to the negative of the total workspace size required. The client should reallocate the workspace to that size, reinitialise it with SWI ColourMap_Start, and re-apply all mappings.
See the per-reason entries below for the specific register usage and behaviour of each mapping type.
| R0 | = | 0 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | Target colour (&BBGGRR00) |
| R4 | = | Blend factor: 0 for fully source colour, &FF for fully target colour |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Each colour component (red, green and blue) is blended linearly between the source pixel colour and the target colour according to the blend factor. A factor of 0 leaves the source colour unchanged; a factor of &FF replaces the source colour entirely with the target colour.
| R0 | = | 1 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | Pointer to a colour mapping descriptor; a copy is taken |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Chains a standard colour mapping descriptor into the mapping sequence. A copy of the descriptor is taken at the time of the call, so the original descriptor need not remain valid after the call returns. This allows multiple mapping functions to be composed together.
| R0 | = | 2 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
The identity mapping passes colours through unchanged. This is rarely useful in practice but is provided for completeness.
| R0 | = | 3 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Each colour component is replaced by its bitwise complement, giving a photographic negative effect. A component value of 0 becomes &FF, and vice versa.
| R0 | = | 4 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
The colour is converted to a luminance-weighted greyscale using the standard coefficients for red, green and blue. The result is a neutral grey with equal red, green and blue components.
| R0 | = | 5 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | 'Black' colour: the output colour corresponding to a luminance of 0 (&BBGGRR00) |
| R4 | = | 'White' colour: the output colour corresponding to a luminance of &FF (&BBGGRR00) |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
The colour is first converted to a greyscale luminance value (0-255) using the same method as SWI ColourMap_ApplyMapping 4. Each component of the output colour is then linearly interpolated between the corresponding component of the 'black' colour and the 'white' colour according to the luminance. This allows, for example, converting an image to a blue-scale (setting the 'black' colour to dark blue and the 'white' colour to light blue).
| R0 | = | 6 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | Gamma level × 256 (valid range 0-1024, representing 0.0-4.0) |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Applies gamma correction to each colour component. The correction maps the linear component values through a logarithmic curve whose shape is determined by the gamma level. The correction is stored as a 256-entry lookup table in the workspace.
A gamma level of 256 (1.0) produces no change. Values below 256 darken the image; values above 256 lighten it.
| R0 | = | 7 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | Contrast factor × 256 (valid range 0-1024, representing 0.0-4.0) |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Adjusts the contrast of each colour component using a lookup table. A factor of 256 (1.0) leaves the image unchanged. Factors above 256 expand the range of component values, increasing contrast; factors below 256 compress the range, decreasing contrast.
| R0 | = | 8 (mapping type) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R3 | = | Value to add to each component (-256 to +256) |
| R0 | preserved | |
| R1 | preserved | |
| R2 | = | Negative required workspace size on out-of-memory error; otherwise preserved |
Adds the given value to each of the red, green and blue components independently, brightening or darkening the image uniformly. Component values are clamped to the range 0-255.
| R0 | = | Flags (reserved, must be 0) |
| R1 | = | Pointer to the workspace block |
| R2 | = | Size of the workspace block, in bytes |
| R0 | = | Pointer to the colour mapping descriptor, or 0 if no mappings are held in the workspace |
| R1 | preserved | |
| R2 | preserved | |
This SWI constructs and returns a colour mapping descriptor representing all the mappings currently held in the workspace. The descriptor may be passed directly to interfaces such as SpriteExtend or DrawFile that accept colour mapping descriptors.
If no mappings have been added to the workspace, 0 is returned in R0 to indicate that no colour mapping is needed.
Once this call has been made, the workspace must not be relocated or modified. If the workspace is moved or further mappings are applied, this SWI must be called again to obtain a new valid descriptor.
The returned descriptor points into the workspace. The descriptor becomes invalid if the workspace is freed or the ColourMap module is killed.
Error messages
The ColourMap module uses error base &81AC00.
Returned by SWI ColourMap_Start or SWI ColourMap_ApplyMapping when the workspace provided is too small. R2 is set to the negative of the workspace size required, allowing the client to reallocate the workspace and retry.
Returned when the workspace block supplied to a SWI does not contain a valid magic number, or when the reported workspace size is inconsistent with the block. This typically indicates that the wrong pointer or size was supplied, or that the workspace was overwritten by the client.
Returned when any reserved flag bits (bits 8-31 of R0 for SWI ColourMap_ApplyMapping, or any bits in R0 for SWI ColourMap_Start) are set to non-zero values.
Returned by SWI ColourMap_ApplyMapping when the mapping type supplied in bits 0-7 of R0 is not one of the types supported by the module.
Examples
The following example in C illustrates the typical usage sequence to create a descriptor that blends towards a red colour at 50% and then desaturates the result to greyscale.
#include "modhead.h" /* For SWI definitions */
_kernel_oserror *apply_blend_and_mono(void **desc_out)
{
_kernel_swi_regs r;
_kernel_oserror *err;
char *workspace = NULL;
int wssize;
/* Step 1: query the minimum workspace size */
r.r[0] = 0;
r.r[1] = 0;
r.r[2] = 0;
err = _kernel_swi(ColourMap_Start, &r, &r);
if (err) return err;
wssize = -r.r[2]; /* returned as negative on size query */
/* Step 2: allocate the workspace */
workspace = malloc(wssize);
if (!workspace) return /* out of memory error */;
retry:
/* Step 3: initialise the workspace */
r.r[0] = 0;
r.r[1] = (int)workspace;
r.r[2] = wssize;
err = _kernel_swi(ColourMap_Start, &r, &r);
if (err) goto fail;
/* Step 4: apply a 50% blend towards red (&000000FF = red in BBGGRR00) */
r.r[0] = 0; /* type = Blend */
r.r[1] = (int)workspace;
r.r[2] = wssize;
r.r[3] = 0x000000FF; /* red */
r.r[4] = 0x80; /* 50% blend */
err = _kernel_swi(ColourMap_ApplyMapping, &r, &r);
if (err)
{
if (r.r[2] < 0)
{
/* workspace too small: grow and retry */
wssize = -r.r[2];
free(workspace);
workspace = malloc(wssize);
if (!workspace) return /* out of memory error */;
goto retry;
}
goto fail;
}
/* Step 5: apply a monochrome conversion */
r.r[0] = 4; /* type = Monochrome */
r.r[1] = (int)workspace;
r.r[2] = wssize;
err = _kernel_swi(ColourMap_ApplyMapping, &r, &r);
if (err) goto fail;
/* Step 6: obtain the colour mapping descriptor */
r.r[0] = 0;
r.r[1] = (int)workspace;
r.r[2] = wssize;
err = _kernel_swi(ColourMap_End, &r, &r);
if (err) goto fail;
*desc_out = (void *)r.r[0];
return NULL;
fail:
free(workspace);
return err;
}