Linux framebuffer像素位域通用实现

5
我正在编写一个小型库,用于与Linux的帧缓冲抽象交互。所有我的图形卡使用相同的像素格式(每个通道一个八位字节,四个通道,BGRA排序),因此到目前为止,该库仅假定这种格式。但是,如果想要该库在任何Linux帧缓冲上运行,就必须使用帧缓冲API提供的像素格式数据。你不需要知道帧缓冲是如何工作的来回答这个问题(希望如此),只需要掌握一些我不太擅长的位操作。以下是我的头文件中提供的像素格式信息:
/* 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 */
};

我需要将包含R、G、B和A四个字符的像素按照上述信息格式写入到char数组中。我曾尝试过几种错误的方法,如下所示:

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 将坐标转换为索引。fb 是指向帧缓存(内存映射)的指针。

有人可以指点我如何将这些像素进行转换和赋值吗?

1个回答

4

亲爱的寻找答案的可能不存在的人:这是我现有的看起来能工作的hackish代码:

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

缺少处理字节序、msb_right或任何优化。


当长度为8时,它可以工作。当长度>8时,您正在向右移动负位数,这是UB或实现定义,我不记得了。我猜你想做的是 (r >> (vinfo.red.length - 8))。我也会避免使用 long,而是使用 intint32_t,最好使用 unsigned - user3528438
你的 fb[xy2off(x, y)] = 的方式并不是很高效。一个好的方法是 *(uint32_t*)(fb+xy2off(x, y)) =pxl;,这样你可以通过一条指令移动4个值,前提是内存正确对齐。fbchar *,所以你没有违反严格别名规则。 - user3528438
谢谢你的建议!如果你把它作为一个答案发布,我会接受你改进后的答案。 - nebuch
我们需要将其应用于帧缓冲区中的每个像素吗?难道没有一次性完成所有操作的方法吗? - étale-cohomology
1
如果C允许您在运行时构建函数,那将是解决此问题的一种优雅方案。我的解决方案是为常见配置制作几个函数,并在没有匹配项时回退到此函数。 - nebuch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接