Framebuffer Colours

Name

Colours -- formats and palette management

Synopsis

#include <cyg/io/framebuf.h>

typedef struct cyg_fb {
    cyg_ucount16    fb_depth;
    cyg_ucount16    fb_format;
    cyg_uint32      fb_flags0;
    …    
} cyg_fb;

extern const cyg_uint8  cyg_fb_palette_ega[16 * 3];
extern const cyg_uint8  cyg_fb_palette_vga[256 * 3];

#define CYG_FB_DEFAULT_PALETTE_BLACK        0x00
#define CYG_FB_DEFAULT_PALETTE_BLUE         0x01
#define CYG_FB_DEFAULT_PALETTE_GREEN        0x02
#define CYG_FB_DEFAULT_PALETTE_CYAN         0x03
#define CYG_FB_DEFAULT_PALETTE_RED          0x04
#define CYG_FB_DEFAULT_PALETTE_MAGENTA      0x05
#define CYG_FB_DEFAULT_PALETTE_BROWN        0x06
#define CYG_FB_DEFAULT_PALETTE_LIGHTGREY    0x07
#define CYG_FB_DEFAULT_PALETTE_LIGHTGRAY    0x07
#define CYG_FB_DEFAULT_PALETTE_DARKGREY     0x08
#define CYG_FB_DEFAULT_PALETTE_DARKGRAY     0x08
#define CYG_FB_DEFAULT_PALETTE_LIGHTBLUE    0x09
#define CYG_FB_DEFAULT_PALETTE_LIGHTGREEN   0x0A
#define CYG_FB_DEFAULT_PALETTE_LIGHTCYAN    0x0B
#define CYG_FB_DEFAULT_PALETTE_LIGHTRED     0x0C
#define CYG_FB_DEFAULT_PALETTE_LIGHTMAGENTA 0x0D
#define CYG_FB_DEFAULT_PALETTE_YELLOW       0x0E
#define CYG_FB_DEFAULT_PALETTE_WHITE        0x0F
      

cyg_ucount16 CYG_FB_FORMAT(framebuf);

void cyg_fb_read_palette(cyg_fb* fb, cyg_ucount32 first, cyg_ucount32 count, void* data);

void cyg_fb_write_palette(cyg_fb* fb, cyg_ucount32 first, cyg_ucount32 count, const void* data, cyg_ucount16 when);

cyg_fb_colour cyg_fb_make_colour(cyg_fb* fb, cyg_ucount8 r, cyg_ucount8 g, cyg_ucount8 b);

void cyg_fb_break_colour(cyg_fb* fb, cyg_fb_colour colour, cyg_ucount8* r, cyg_ucount8* g, cyg_ucount8* b);

void CYG_FB_READ_PALETTE(FRAMEBUF, cyg_ucount32 first, cyg_ucount32 count, void* data);

void CYG_FB_WRITE_PALETTE(FRAMEBUF, cyg_ucount32 first, cyg_ucount32 count, const void* data, cyg_ucount16 when);

cyg_fb_colour CYG_FB_MAKE_COLOUR(FRAMEBUF, cyg_ucount8 r, cyg_ucount8 g, cyg_ucount8 b);

void CYG_FB_BREAK_COLOUR(FRAMEBUF, cyg_fb_colour colour, cyg_ucount8* r, cyg_ucount8* g, cyg_ucount8* b);

Description

Managing colours can be one of the most difficult aspects of writing graphics code, especially if that code is intended to be portable to many different platforms. Displays can vary from 1bpp monochrome, via 2bpp and 4bpp greyscale, through 4bpp and 8bpp paletted, and up to 16bpp and 32bpp true colour - and those are just the more common scenarios. The various drawing primitives like cyg_fb_write_pixel work in terms of cyg_fb_colour values, usually an unsigned integer. Exactly how the hardware interprets a cyg_fb_colour depends on the format.

Colour Formats

There are a number of ways of finding out how these values will be interpreted by the hardware:

  1. The CYG_FB_FLAGS0_TRUE_COLOUR flag is set for all true colour displays. The format parameter can be examined for more details but this is not usually necessary. Instead code can use cyg_fb_make_colour or CYG_FB_MAKE_COLOUR to construct a cyg_fb_colour value from red, green and blue components.

  2. If the CYG_FB_FLAGS0_WRITEABLE_PALETTE flag is set then a cyg_fb_colour value is an index into a lookup table known as the palette, and this table contains red, green and blue components. The size of the palette is determined by the display depth, so 16 entries for a 4bpp display and 256 entries for an 8bpp display. Application code or a graphics library can install its own palette so can control exactly what colour each cyg_fb_colour value corresponds to. Alternatively there is support for installing a default palette.

  3. If CYG_FB_FLAGS0_PALETTE is set but CYG_FB_FLAGS0_WRITEABLE_PALETTE is clear then the hardware uses a fixed palette. There is no easy way for portable software to handle this case. The palette can be read at run-time, allowing the application's desired colours to be mapped to whichever palette entry provides the best match. However normally it will be necessary to write code specifically for the fixed palette.

  4. Otherwise the display is monochrome or greyscale, depending on the depth. There are still variations, for example on a monochrome display colour 0 can be either white or black.

As an alternative or to provide additional information, the exact colour format is provided by the fb_format field of the cyg_fb structure or by the CYG_FB_FORMAT macro. It can be one of the following (more entries may be added in future):

CYG_FB_FORMAT_1BPP_MONO_0_BLACK

simple 1bpp monochrome display, with 0 as black or the darker of the two colours, and 1 as white or the ligher colour.

CYG_FB_FORMAT_1BPP_MONO_0_WHITE

simple 1bpp monochrome display, with 0 as white or the lighter of the two colours, and 1 as black or the darker colour.

CYG_FB_FORMAT_1BPP_PAL888

a 1bpp display which cannot easily be described as monochrome. This is unusual and not readily supported by portable code. It can happen if the framebuffer normally runs at a higher depth, for example 4bpp or 8bpp paletted, but is run at only 1bpp to save memory. Hence only two of the palette entries are used, but can be set to arbitrary colours. The palette may be read-only or read-write.

CYG_FB_FORMAT_2BPP_GREYSCALE_0_BLACK

a 2bpp display offering four shades of grey, with 0 as black or the darkest of the four shades, and 3 as white or the lightest.

CYG_FB_FORMAT_2BPP_GREYSCALE_0_WHITE

a 2bpp display offering four shades of grey, with 0 as white or the lightest of the four shades, and 3 as black or the darkest.

CYG_FB_FORMAT_2BPP_PAL888

a 2bpp display which cannot easily be described as greyscale, for example providing black, red, blue and white as the four colours. This is unusual and not readily supported by portable code. It can happen if the framebuffer normally runs at a higher depth, for example 4bpp or 8bpp paletted, but is run at only 2bpp to save memory. Hence only four of the palette entries are used, but can be set to arbitrary colours. The palette may be read-only or read-write.

CYG_FB_FORMAT_4BPP_GREYSCALE_0_BLACK

a 4bpp display offering sixteen shades of grey, with 0 as black or the darkest of the 16 shades, and 15 as white or the lighest.

CYG_FB_FORMAT_4BPP_GREYSCALE_0_WHITE

a 4bpp display offering sixteen shades of grey, with 0 as white or the lightest of the 16 shades, and 15 as black or the darkest.

CYG_FB_FORMAT_4BPP_PAL888

a 4bpp paletted display, allowing for 16 different colours on screen at the same time. The palette may be read-only or read-write.

CYG_FB_FORMAT_8BPP_PAL888

an 8bpp paletted display, allowing for 256 different colours on screen at the same time. The palette may be read-only or read-write.

CYG_FB_FORMAT_8BPP_TRUE_332

an 8bpp true colour display, with three bits (eight levels) of red and green intensity and two bits (four levels) of blue intensity.

CYG_FB_FORMAT_16BPP_TRUE_565

a 16bpp true colour display with 5 bits each for red and blue and 6 bits for green.

CYG_FB_FORMAT_16BPP_TRUE_555

a 16bpp true colour display with five bits each for red, green and blue, and one unused bit.

CYG_FB_FORMAT_32BPP_TRUE_0888

a 32bpp true colour display with eight bits each for red, green and blue and eight bits unused.

For the true colour formats the format does not define exactly which bits in the pixel are used for which colour. Instead the cyg_fb_make_colour and cyg_fb_break_colour functions or the equivalent macros should be used to construct or decompose pixel values.

Paletted Displays

Palettes are the common way of implementing low-end colour displays. There are two variants. A read-only palette provides a fixed set of colours and it is up to application code to use these colours appropriately. A read-write palette allows the application to select its own set of colours. Displays providing a read-write palette will have the CYG_FB_FLAGS0_WRITEABLE_PALETTE flag set in addition to CYG_FB_FLAGS0_PALETTE.

Even if application code can install its own palette, many applications do not exploit this functionality and instead stick with a default. There are two standard palettes: the 16-entry PC EGA for 4bpp displays; and the 256-entry PC VGA, a superset of the EGA one, for 8bpp displays. This package provides the data for both, in the form of arrays cyg_fb_palette_ega and cyg_fb_palette_vga, and 16 #define's such as CYG_FB_DEFAULT_PALETTE_BLACK for the EGA colours and the first 16 VGA colours. By default device drivers for read-write paletted displays will install the appropriate default palette, but this can be suppressed using configuration option CYGFUN_IO_FRAMEBUF_INSTALL_DEFAULT_PALETTE. If a custom palette will be used then installing the default palette involves wasting 48 or 768 bytes of memory.

It should be emphasized that displays vary widely. A colour such as CYG_FB_DEFAULT_PALETTE_YELLOW may appear rather differently on two different displays, although it should always be recognizable as yellow. Developers may wish to fine-tune the palette for specific hardware.

The current palette can be retrieved using cyg_fb_read_palette or CYG_FB_READ_PALETTE. The first and count arguments control which palette entries should be retrieved. For example, to retrieve just palette entry 12 first should be set to 12 and count should be set to 1. To retrieve all 256 entries for an 8bpp display, first should be set to 0 and count should be set to 256. The data argument should point at an array of bytes, allowing three bytes for every entry. Byte 0 will contain the red intensity for the first entry, byte 1 green and byte 2 blue.

For read-write palettes the palette can be updated using cyg_fb_write_palette or CYG_FB_WRITE_PALETTE. The first and count arguments are the same as for cyg_fb_read_palette, and the data argument should point at a suitable byte array packed in the same way. The when argument should be one of CYG_FB_UPDATE_NOW or CYG_FB_UPDATE_VERTICAL_RETRACE. With some displays updating the palette in the middle of an update may result in visual noise, so synchronizing to the vertical retrace avoids this. However not all device drivers will support this.

There is an assumption that palette entries use 8 bits for each of the red, green and blue colour intensities. This is not always the case, but the device drivers will perform appropriate adjustments. Some hardware may use only 6 bits per colour, and the device driver will ignore the bottom two bits of the supplied intensity values. Occasionally hardware may use more than 8 bits, in which case the supplied 8 bits are shifted left appropriately and zero-padded. Device drivers for such hardware may also provide device-specific routines to manipulate the palette in a non-portable fashion.

True Colour displays

True colour displays are often easier to manage than paletted displays. However this comes at the cost of extra memory. A 16bpp true colour display requires twice as much memory as an 8bpp paletted display, yet can offer only 32 or 64 levels of intensity for each colour as opposed to the 256 levels provided by a palette. It also requires twice as much video memory bandwidth to send all the pixel data to the display for every refresh, which may impact the performance of the rest of the system. A 32bpp true colour display offers the same colour intensities but requires four times the memory and four times the bandwidth.

Exactly how the colour bits are organized in a cyg_fb_colour pixel value is not defined by the colour format. Instead code should use the cyg_fb_make_colour or CYG_FB_MAKE_COLOUR primitives. These take 8-bit intensity levels for red, green and blue, and return the corresponding cyg_fb_colour. When using the macro interface the arithmetic happens at compile-time, for example:

#define BLACK        CYG_FB_MAKE_COLOUR(FRAMEBUF,   0,   0,   0)
#define WHITE        CYG_FB_MAKE_COLOUR(FRAMEBUF, 255, 255, 255)
#define RED          CYG_FB_MAKE_COLOUR(FRAMEBUF, 255,   0,   0)
#define GREEN        CYG_FB_MAKE_COLOUR(FRAMEBUF,   0, 255,   0)
#define BLUE         CYG_FB_MAKE_COLOUR(FRAMEBUF,   0,   0, 255)
#define YELLOW       CYG_FB_MAKE_COLOUR(FRAMEBUF, 255, 255,  80)
    

Displays vary widely so the numbers may need to be adjusted to give the exact desired colours.

For symmetry there are also cyg_fb_break_colour and CYG_FB_BREAK_COLOUR primitives. These take a cyg_fb_colour value and decompose it into its red, green and blue components.