Pyromaniac

Pyromaniac PRM: ColourMap

Pyromaniac PRM: ColourMap

RISC OS PyromaniacColourMap

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:

  1. Call SWI ColourMap_Start, passing R1 = 0, to read the initial size of the workspace required.
  2. Allocate a workspace block of at least the size returned.
  3. Call SWI ColourMap_Start again, passing the workspace pointer and its size in R1 and R2, to initialise the workspace.
  4. Call SWI ColourMap_ApplyMapping with the desired mapping type and parameters.
  5. 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.
  6. Repeat calling SWI ColourMap_ApplyMapping to add further mappings, as needed.
  7. Call SWI ColourMap_End to obtain a colour mapping descriptor suitable for use with DrawFile, SpriteExtend, or other clients.
  8. 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.

OffsetContents
0Magic word (&9072340E), used to validate the workspace
4Size of workspace currently used, in bytes
8Colour mapping descriptor: workspace pointer (or 0 if not yet valid)
12Colour mapping descriptor: function pointer (or 0 if not yet valid)
16First mapping entry (see below); further entries follow contiguously

Each mapping entry within the workspace has the following layout:

OffsetContents
0Mapping type (a Mapping types value), or -1 to mark the end of the list
4Size of the mapping's workspace data, in bytes
8Function pointer for this mapping (filled in by SWI ColourMap_End)
12Mapping-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.

OffsetContents
0Pointer to the workspace for this mapping
4Pointer 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.

TypeDescription
0Blend the colour towards a target colour
1Apply a user-supplied colour mapping descriptor
2Apply no transformation (identity mapping)
3Invert each colour component
4Convert colour to greyscale
5Convert colour to greyscale and scale to a colour range
6Apply gamma correction to each colour component
7Increase or decrease the contrast of the image
8Add a fixed offset to each colour component

Colour format

Colours supplied to ColourMap SWIs and returned by them use the RISC OS 32-bit colour word format:

OffsetContents
0Blue component (bits 24-31 of word, i.e. bits 7-0 at byte offset 3)
1Green component
2Red component
3Always 0 (unused)

In hexadecimal notation this is written as &BBGGRR00.

SWI calls

ColourMap_StartSWI &567C0
Initialise a ColourMap workspace block, or read the minimum workspace size
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)
R0preserved
R1preserved
R2=If called with R1 = 0: minimum workspace size required (positive). If called with a workspace pointer: size of workspace used (positive).
Interrupts are undefined
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant

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.

ColourMap_ApplyMappingSWI &567C1
Add a colour mapping operation to the workspace
R0=

Flags and mapping type:

Bit(s)NameMeaning
0-7Type Mapping type:
TypeMapping
0Blend the colour towards a target colour
1Apply a user-supplied colour mapping descriptor
2Apply no transformation (identity mapping)
3Invert each colour component
4Convert colour to greyscale
5Convert colour to greyscale and scale to a colour range
6Apply gamma correction to each colour component
7Increase or decrease the contrast of the image
8Add a fixed offset to each colour component
8-31Reserved, must be zero
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)
R0preserved
R1preserved
R2=If workspace was too small: negative value giving the required workspace size. Otherwise preserved.
Interrupts are undefined
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant

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.

ColourMap_ApplyMapping 0BlendSWI &567C1
Blend the colour towards a target colour
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
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_ApplyMapping 1UserSWI &567C1
Apply a user-supplied colour mapping descriptor
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
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_ApplyMapping 2IdentitySWI &567C1
Apply no transformation (identity mapping)
R0=2 (mapping type)
R1=Pointer to the workspace block
R2=Size of the workspace block, in bytes
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

The identity mapping passes colours through unchanged. This is rarely useful in practice but is provided for completeness.

ColourMap_ApplyMapping 3InvertSWI &567C1
Invert each colour component
R0=3 (mapping type)
R1=Pointer to the workspace block
R2=Size of the workspace block, in bytes
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

Each colour component is replaced by its bitwise complement, giving a photographic negative effect. A component value of 0 becomes &FF, and vice versa.

ColourMap_ApplyMapping 4MonochromeSWI &567C1
Convert colour to greyscale
R0=4 (mapping type)
R1=Pointer to the workspace block
R2=Size of the workspace block, in bytes
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_ApplyMapping 5MonoScaleSWI &567C1
Convert colour to greyscale and scale to a colour range
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)
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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).

ColourMap_ApplyMapping 6GammaSWI &567C1
Apply gamma correction to each colour component
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)
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_ApplyMapping 7ContrastSWI &567C1
Increase or decrease the contrast of the image
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)
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_ApplyMapping 8BrightnessSWI &567C1
Add a fixed offset to each colour component
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)
R0preserved
R1preserved
R2=Negative required workspace size on out-of-memory error; otherwise preserved
Interrupts are undefined
Fast interrupts are undefined
Processor is in undefined mode
Not defined

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.

ColourMap_EndSWI &567C2
Obtain the colour mapping descriptor from the workspace
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
R1preserved
R2preserved
Interrupts are undefined
Fast interrupts are enabled
Processor is in SVC mode
SWI is not re-entrant

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.

Error_ColourMap_NotEnoughWorkspaceError &81AC00
Not enough memory in the ColourMap workspace

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.

Error_ColourMap_WorkspaceCorruptError &81AC01
ColourMap workspace corrupt

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.

Error_ColourMap_BadFlagsError &81AC02
Reserved bits set in flags to ColourMap module

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.

Error_ColourMap_BadMappingTypeError &81AC03
Mapping type not recognised by the ColourMap module

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;
}