#include <cyg/io/framebuf.h> typedef struct cyg_fb { cyg_ucount16 fb_depth; cyg_ucount16 fb_format; cyg_ucount16 fb_width; cyg_ucount16 fb_height; #ifdef CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT cyg_ucount16 fb_viewport_width; cyg_ucount16 fb_viewport_height; #endif void* fb_base; cyg_ucount16 fb_stride; cyg_uint32 fb_flags0; … } cyg_fb; |
cyg_fb* CYG_FB_STRUCT
(FRAMEBUF);
cyg_ucount16 CYG_FB_DEPTH
(FRAMEBUF);
cyg_ucount16 CYG_FB_FORMAT
(FRAMEBUF);
cyg_ucount16 CYG_FB_WIDTH
(FRAMEBUF);
cyg_ucount16 CYG_FB_HEIGHT
(FRAMEBUF);
cyg_ucount16 CYG_FB_VIEWPORT_WIDTH
(FRAMEBUF);
cyg_ucount16 CYG_FB_VIEWPORT_HEIGHT
(FRAMEBUF);
void* CYG_FB_BASE
(FRAMEBUF);
cyg_ucount16 CYG_FB_STRIDE
(FRAMEBUF);
cyg_uint32 CYG_FB_FLAGS0
(FRAMEBUF);
When developing an application for a specific platform the various framebuffer parameters such as width and height are known, and the code can be written accordingly. However when writing code that should work on many platforms with different framebuffer devices, for example a graphics library, the code must be able to get these parameters and adapt.
Code using the function API can extract the parameters from the
cyg_fb
structures at run-time. The macro API
provides dedicated macros for each parameter. These do not follow the
usual eCos convention where the result is provided via an extra
argument. Instead the result is returned as normal, and is guaranteed
to be a compile-time constant. This allows code like the following:
#if CYG_FB_DEPTH(FRAMEBUF) < 8 … #else … #endif |
or alternatively:
if (CYG_FB_DEPTH(FRAMEBUF) < 8) { … } else { … } |
or:
switch (CYG_FB_DEPTH(FRAMEBUF)) { case 1 : … break; case 2 : … break; case 4 : … break; case 8 : … break; case 16 : … break; case 32 : … break; } |
In terms of the code actually generated by the compiler these approaches have much the same effect. The macros expand to a compile-time constant so unnecessary code can be easily eliminated.
The available parameters are as follows:
The number of bits per pixel or bpp. The common depths are 1, 2, 4, 8, 16 and 32.
How the pixel values are mapped on to visible colours, for example true colour or paletted or greyscale.
The number of framebuffer pixels horizontally and vertically.
With some devices the framebuffer height and/or width are greater than
what the display can actually show. The display is said to offer a
viewport into the larger framebuffer. The number of visible pixels is
determined from the viewport width and height. The position of the
viewport is controlled via an ioctl
.
Within a cyg_fb
structure these fields are
only present if
CYGHWR_IO_FRAMEBUF_FUNCTIONALITY_VIEWPORT
is defined, to avoid wasting data space on fields that are unnecessary
for the current platform. For the macro API the viewport macros should only be used
if CYG_FB_FLAGS0_VIEWPORT is set for the framebuffer:
#if (CYG_FB_FLAGS0(FRAMEBUF) & CYG_FB_FLAGS0_VIEWPORT) … #endif |
For linear framebuffers these parameters provide the information needed to access framebuffer memory. The stride is in bytes.
This gives further information about the hardware capabilities. Some of this overlaps with other parameters, especially when it comes to colour, because it is often easier to test for a single flag than for a range of colour modes. The current flags are:
Framebuffer memory is organized in a conventional fashion and can be accessed directly by higher-level code using the base and stride parameters.
This flag is only relevant for 1bpp, 2bpp and 4bpp devices and controls how the pixels are organized within each byte. If the flag is set then the layout is little-endian: for a 1bpp device pixel (0,0) occupies bit 0 of the first byte of framebuffer memory. The more common layout is big-endian where pixel (0,0) occupies bit 7 of the first byte.
The framebuffer uses a true colour format where the value of each pixel directly encodes the red, green and blue intensities. This is common for 16bpp and 32bpp devices, and is occasionally used for 8bpp devices.
The framebuffer uses a palette. A pixel value does not directly encode the colours, but instead acts as an index into a separate table of colour values. That table may be read-only or read-write. Paletted displays are common for 8bpp and some 4bpp displays.
The palette is read-write.
Palette updates can be synchronized to a vertical blank, in other
words a brief time period when the display is not being updated,
by using CYG_FB_UPDATE_VERTICAL_RETRACE as the last
argument to cyg_fb_write_palette
or
CYG_FB_WRITE_PALETTE
. With some hardware updating
the palette in the middle of a screen update may result in visual noise.
The framebuffer contains more pixels than can be shown on the display.
Instead the display provides a viewport into the framebuffer. An
ioctl
can be used to move the viewport.
The display does not show the current contents of the framebuffer, so the results of drawing into the framebuffer are not immediately visible. Instead higher-level code needs to perform an explicit synch operation to update the display.
The hardware supports two or more pages, each of width*height pixels,
only one of which is visible on the display. This allows higher-level
code to update one page without disturbing what is currently visible.
An ioctl
is used to switch the visible page.
The display can be blanked without affecting the framebuffer contents or settings.
There is a backlight which can be switched on or off. Some hardware provides finer-grained control over the backlight intensity.
Often it is desirable to perform some initialization such as clearing
the screen or setting the palette before the display is switched on, to avoid visual
noise. However not all hardware allows this. If this flag is set then
it is possible to access framebuffer memory and the palette before the
cyg_fb_on
or CYG_FB_ON
operation. It may also be possible to perform some other operations
such as activating the backlight, but that is implementation-defined.
To allow for future expansion there are also flags1, flags2, and
flags3 fields. These may get used for encoding additional
ioctl
functionality, support for hardware
acceleration, and similar features.
There are drawing primitives for writing and reading individual pixels. However these involve a certain amount of arithmetic each time to get from a position to an address within the frame buffer, plus function call overhead if the function API is used, and this will slow down graphics operations.
When the framebuffer device is known at compile-time and the macro API is used then there are additional macros specifically for iterating over parts of the frame buffer. These should prove very efficient for many graphics operations. However if the device is selected at run-time then the macros are not appropriate and code may want to manipulate framebuffer memory directly. This is possible if two conditions are satisfied:
The CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER flag must be set. Otherwise framebuffer memory is either not directly accessible or has a non-linear layout.
The CYG_FB_FLAGS0_DOUBLE_BUFFER flag must be clear. An efficient double buffer synch operation requires knowing what part of the framebuffer have been updated, and the various drawing primitives will keep track of this. If higher-level code then starts manipulating the framebuffer directly the synch operation may perform only a partial update.
The base, stride, depth, width and height parameters, plus the CYG_FB_FLAGS0_LE flag for 1bpp, 2bpp and 4bpp devices, provide all the information needed to access framebuffer memory. A linear framebuffer has pixel (0,0) at the base address. Incrementing y means adding stride bytes to the pointer.
The base and stride parameters may be set even if CYG_FB_FLAGS0_LINEAR_FRAMEBUFFER is clear. This can be useful if for example the display is rotated in software from landscape to portrait mode. However the meaning of these parameters for non-linear framebuffers is implementation-defined.