5

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?

pnuts
  • 58,317
  • 11
  • 87
  • 139
nebuch
  • 6,475
  • 4
  • 20
  • 39

1 Answers1

4

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.

nebuch
  • 6,475
  • 4
  • 20
  • 39
  • It'll work when length=8. When length>8 then you are right shifting a negative number of bits, which is either UB or implementation defined, I don't remember. I guess you want to do `(r >> ( vinfo.red.length -8))`. I would also avoid `long`, and use `int` or `int32_t` instead, `unsigned ` would be better. – user3528438 Aug 24 '15 at 12:31
  • And your way of doing `fb[xy2off(x, y)] =` isn't very efficient. A good way to do it is `*(uint32_t*)(fb+xy2off(x, y)) =pxl;`, then you can move 4 values with one instruction, assuming memory is correctly aligned. `fb` is `char *` so you are not violating strict aliasing rule. – user3528438 Aug 24 '15 at 12:35
  • Thanks for the advice! I will accept your improved answer if you post it as one – nebuch Aug 24 '15 at 19:33
  • Do we need to apply this to *every* pixel in the framebuffer?? Is there no way to do it all at once? – étale-cohomology Jul 26 '16 at 11:56
  • 1
    @Diego Alonso Cortez If C allowed you to build functions at runtime, that'd be an elegant solution to this problem. My solution was to make several functions for common configurations and fall back on this one if none matched. – nebuch Aug 09 '16 at 20:39