I'm writing a tiny library to interface with Linux's framebuffer abstraction. All of my graphics cards use the same pixel format (one octet per channel, four channels, BGRA ordering), so thus far the library just assumes this format. However, the framebuffer API provides pixel format data that I must use if I want the library to work on any Linux framebuffer. You don't need to know how the framebuffer works to answer this one (I hope), just some bit-fiddling I'm not proficient with. Here's the pixel format info provided in my headers:
/* Interpretation of offset for color fields: All offsets are from the right,
* inside a "pixel" value, which is exactly 'bits_per_pixel' wide (means: you
* can use the offset as right argument to <<). A pixel afterwards is a bit
* stream and is written to video memory as that unmodified.
*
* For pseudocolor: offset and length should be the same for all color
* components. Offset specifies the position of the least significant bit
* of the pallette index in a pixel value. Length indicates the number
* of available palette entries (i.e. # of entries = 1 << length).
*/
struct fb_bitfield {
__u32 offset; /* beginning of bitfield */
__u32 length; /* length of bitfield */
__u32 msb_right; /* != 0 : Most significant bit is */
/* right */
};
/* snip */
struct fb_var_screeninfo {
/* snip */
__u32 bits_per_pixel; /* guess what */
__u32 grayscale; /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
/* snip */
};
I need to write a pixel formatted with the above info to a char array from four chars (R, G, B and A). The misguided attempts I've made to do this look like this:
long pxl = 0;
/* downsample each channel to the length (assuming its less than 8 bits) */
/* with a right-shift, then left shift it over into place */
/* haven't done anything with fb_bitfield.msb_right */
pxl |= (r >> (8 - vinfo.red.length)) << vinfo.red.offset;
pxl |= (g >> (8 - vinfo.green.length)) << vinfo.green.offset;
pxl |= (b >> (8 - vinfo.blue.length)) << vinfo.blue.offset;
pxl |= (a >> (8 - vinfo.transp.length)) << vinfo.transp.offset;
fb[xy2off(x, y)] = /* umm... */;
/* little endian, big endian? Can I just assign here? */
xy2off
converts coordinates to an index. fb
is a pointer to the framebuffer (memory mapped).
Can anyone point me in the right direction for converting and assigning these pixels?
Dear likely nonexistent people looking for an answer to this: here's the hackish code I've got that seems to work now:
void pixel(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { int i; long pxl = 0; pxl |= (r >> (8 - vinfo.red.length)) << vinfo.red.offset; pxl |= (g >> (8 - vinfo.green.length)) << vinfo.green.offset; pxl |= (b >> (8 - vinfo.blue.length)) << vinfo.blue.offset; pxl |= (a >> (8 - vinfo.transp.length)) << vinfo.transp.offset; for (i = 0; i < sizeof(long); i++) { fb[xy2off(x, y) + i] = ((char *)(&pxl))[i]; } }
Missing endianness handling, dealing with msb_right, or any sort of optimization.