图形设备

更新时间:
2024-05-13
下载文档

图形设备

图形设备,也称作帧缓冲(FrameBuffer)设备,通过该设备可以直接操作显存本身。SylixOS 中图形设备名称为/dev/fb0,如果硬件支持多个图层,相应的会有 /dev/fb1、/dev/fb2 等设备存在。在使用图形设备之前,需要首先获取其与显示模式相关的信息,比如分辨率,每个像素占用的字节大小及其 RGB 的编码结构、显存的大小等信息,这样我们才能将需要显示的图像数据正确地写入显存。在 SylixOS 中,用以描述图形设备信息的结构定义位于 SylixOS/system/device/graph/gmemDev.h 如下程序所示。

typedef struct {
    ULONG             GMVI_ulXRes;              /*  可视区域                        */
    ULONG             GMVI_ulYRes;

    ULONG             GMVI_ulXResVirtual;       /*  虚拟区域                        */
    ULONG             GMVI_ulYResVirtual;

    ULONG             GMVI_ulXOffset;           /*  显示区域偏移                    */
    ULONG             GMVI_ulYOffset;
    ULONG             GMVI_ulBitsPerPixel;      /*  每个像素的数据位数               */
    ULONG             GMVI_ulBytesPerPixel;     /*  每个像素的存储字节数             */
                                                /*  有些图形处理器 DMA 为了对齐      */
                                                /*  使用了填补无效字节               */
    ULONG             GMVI_ulGrayscale;         /*  灰度等级                        */

    ULONG             GMVI_ulRedMask;           /*  红色掩码                        */
    ULONG             GMVI_ulGreenMask;         /*  绿色掩码                        */
    ULONG             GMVI_ulBlueMask;          /*  蓝色掩码                        */
    ULONG             GMVI_ulTransMask;         /*  透明度掩码                      */

    LW_GM_BITFIELD    GMVI_gmbfRed;             /* true color bitfield             */
    LW_GM_BITFIELD    GMVI_gmbfGreen;
    LW_GM_BITFIELD    GMVI_gmbfBlue;
    LW_GM_BITFIELD    GMVI_gmbfTrans;

    BOOL              GMVI_bHardwareAccelerate; /*  是否使用硬件加速                */
    ULONG             GMVI_ulMode;              /*  显示模式                       */
    ULONG             GMVI_ulStatus;            /*  显示器状态                     */
} LW_GM_VARINFO;
typedef LW_GM_VARINFO  *PLW_GM_VARINFO;
typedef struct {
    PCHAR             GMSI_pcName;              /*  显示器名称                      */
    ULONG             GMSI_ulId;                /*  ID                             */
    size_t            GMSI_stMemSize;           /*  framebuffer内存大小             */
    size_t            GMSI_stMemSizePerLine;    /*  每一行的内存大小                */
    caddr_t           GMSI_pcMem;               /*  显示内存 (需要驱动程序映射)      */
} LW_GM_SCRINFO;
typedef LW_GM_SCRINFO  *PLW_GM_SCRINFO;

LW_GM_VARINFO 包含了与显示数据密切相关的信息。其中最重要的是像素的 RGB 掩码及其占用的数据位数。数据位数有以下几种:

  • 8 位:最多能显示 256 种颜色。如果硬件仅支持黑白显示,则一个像素可支持 256 阶灰度值。如果硬件支持彩色显示,通常情况下这 256 个编码值对应 256 种生活中最常用的颜色,这便是调色板模式,即用有限的颜色来近似表达实际的显示需求。
  • 16 位:最大能显示 65536 种颜色,也称作伪真彩色,支持 16 位色彩的硬件能够显示生活中绝大多数的颜色。16 位数据显示下,有 RGB555 和 RGB565 两种编码方式,这可以通过上面的 GMVI_ulRedMask、GMVI_ulGreenMask、GMVI_ulBlueMask 得到。
  • 24 位:能够显示多达 1600 万种颜色,用肉眼几乎无法分辨出与实际颜色的差异,因此也叫做真彩色。在 24 位显示下,一个像素的红、绿、蓝三种颜色分别使用 8 位表示。
  • 32 位:相对于 24 位,它多出的 8 位用来表示像素的 256 阶透明度(0 表示不透明,255 表示完全透明,此时该像素不被显示)。使用 GMVI_ulTransMask 可以知道像素的透明度值的位置。

在大多数嵌入式系统中,显示控制器通常支持 8 位或 16 位数据显示,某些高端的处理器才支持 24 位或 32 位真彩显示。通常情况下,8 位数据显示时一个像素所占用的内存为 1 个字节,相应地 16 位占用 2 个字节内存,24 位占用 3 个字节内存,32 位占用 4 个字节内存。但是,可能某些硬件对 DMA 内存的对齐限制,在各个像素所占用内存之间需要填充字节以满足对齐需要。因此,实际使用中,我们应该使用 GMVI_ulBitsPerPixel 并配合 RGB 掩码值设置像素内容,使用 GMVI_ulBytesPerPixel 处理像素之间的内存偏移。

LW_GM_SCRINFO 包含了显存的一些必要信息,GMSI_stMemSize 表示显存总的字节大小,GMSI_stMemSizePerLine 表示每一行所占用的字节大小,根据这些信息我们可以知道如何处理行与行之间的内存偏移,同时也可以知道总的列数。

通常情况下,我们根据以上所讲的信息就能正确地完成图像的显示,此外还有其他一些信息,如可视区域、虚拟区域、显示区域偏移、硬件加速等。下面的程序简单地展示图形设备的使用方法。

#include <SylixOS.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
static VOID  __drawPixel (VOID                *pvFrameBuffer,
                          LW_GM_SCRINFO       *pscrinfo,
                          LW_GM_VARINFO       *pvarinfo,
                          INT                  iX,
                          INT                  iY,
                          UINT32               uiColor)
{
    VOID    *pvPixelAddr;
    if ((iX < 0) || (iX >= pvarinfo->GMVI_ulXResVirtual) ||
         (iY < 0) || (iY >= pvarinfo->GMVI_ulYResVirtual)) {
        return;
    }
pvPixelAddr = (UINT8 *)pvFrameBuffer + iY * 
pscrinfo->GMSI_stMemSizePerLine;
    switch (pvarinfo->GMVI_ulBitsPerPixel) {
    case 16:
        if (pvarinfo->GMVI_ulGreenMask == (0x3f << 5)) { /*  RGB565模式         */
            uiColor = ((uiColor & 0xff0000) >> 16 >> 3 << 11) |
                        ((uiColor & 0x00ff00) >>  8 >> 2 <<  5)  |
                        ((uiColor & 0x0000ff) >>  0 >> 3 <<  0);
        } else {                                             /*  RGB555模式             */
            uiColor = ((uiColor & 0xff0000) >> 16 >> 3 << 10) |
                        ((uiColor & 0x00ff00) >>  8 >> 3 <<  5)  |
                        ((uiColor & 0x0000ff) >>  0 >> 3 <<  0);
        }
        *((UINT16 *)pvPixelAddr + iX) = (UINT16)uiColor;
        break;
    case 24:
    case 32:
        *((UINT32 *)pvPixelAddr + iX) = uiColor;
        break;
    }
}
static VOID __drawHorizLine (VOID              *pvFrameBuffer,
                             LW_GM_SCRINFO     *pscrinfo,
                             LW_GM_VARINFO     *pvarinfo,
                             INT                iXstart,
                             INT                iYstart,
                             INT                iXend,
                             UINT32             uiColor)
{
    for (; iXstart <= iXend; iXstart++) {
        __drawPixel(pvFrameBuffer, pscrinfo, pvarinfo, 
iXstart, iYstart, uiColor);
    }
}
int  main (int argc, char *argv[])
{
    INT                   iFbFd;
    LW_GM_SCRINFO       scrInfo;
    LW_GM_VARINFO       varInfo;
    INT                   iError;
    VOID                 *pvFrameBuffer;
    iFbFd = open("/dev/fb0", O_RDWR);
    if (iFbFd < 0) {
        fprintf(stderr, "open /dev/fb0 failed.\n");
        return  (-1);
    }
    iError = ioctl(iFbFd, LW_GM_GET_SCRINFO, &scrInfo);
    if (iError < 0) {
        fprintf(stderr, "get /dev/fb0 screen info failed.\n");
        goto __error;
    }
    iError = ioctl(iFbFd, LW_GM_GET_VARINFO, &varInfo);
    if (iError < 0) {
        fprintf(stderr, "get /dev/fb0 var info failed.\n");
        goto __error;
    }
    pvFrameBuffer = mmap(LW_NULL, scrInfo.GMSI_stMemSize,
                             PROT_READ | PROT_WRITE, MAP_SHARED, iFbFd, 0);
    if (pvFrameBuffer == MAP_FAILED) {
        fprintf(stderr, "mmap /dev/fb0 failed.\n");
        goto __error;
    }
    memset(pvFrameBuffer, 0xff, scrInfo.GMSI_stMemSize); /* 清屏                 */

    __drawHorizLine(pvFrameBuffer, &scrInfo, &varInfo, 0, 10,
                       varInfo.GMVI_ulXResVirtual - 1, 0xff0000);
    __drawHorizLine(pvFrameBuffer, &scrInfo, &varInfo, 0, 20,
                       varInfo.GMVI_ulXResVirtual - 1, 0x00ff00);
    __drawHorizLine(pvFrameBuffer, &scrInfo, &varInfo, 0, 30,
                       varInfo.GMVI_ulXResVirtual - 1, 0x0000ff);
    munmap(pvFrameBuffer, scrInfo.GMSI_stMemSize);
    close(iFbFd);    
    return  (0);
__error:
    close(iFbFd);
    return  (-1);
}

在 main 函数中,我们首先获取与显示相关的信息,随后使用 mmap 将显存映射到用户虚拟空间,这样我们操作该虚拟空间相当于直接操作显存,关于 mmap 的详细介绍见 12.4 虚拟内存管理部分。

在 __drawPixel 函数中,参数 uiColor 定义为一个 32 位的数据类型,从低地址到高地址开始,字节 0 表示蓝色,字节 1 表示绿色,字节 2 表示红色,这和真彩色(24 位或 32 位)的颜色格式一样。如果是 16 位数据模式,我们仅通过绿色掩码值,即可知道当前的 RGB 模式,并根据相应的模式进行对应的转换。注意,由于 16 位模式下不能完全表示所有的真彩色,因此作了相应的线性转换处理,比如 0~255,如果是 5 位数据,则对应 0~31,如果是 6 位数据,则对应 0~63。由于本例的重点是展示图形设备的使用方法,因此仅仅简单地画出三条水平直线,读者可通过其他方式了解画任意直线、圆形或者椭圆形的相关算法。

限于篇幅,同时为了使程序简单直观,很多地方没有作详细的处理,例如并没有考虑像素实际所占用的字节大小,而是假设它刚好与其数据位数一致;也没有考虑真实显示区域与虚拟显示区域之间的偏移,而是假设它们的偏移为 0;程序中也没有处理 8 位数据显示的情况;也忽略了 32 位模式下像素的透明度信息。读者须明白,这些信息在实际应用中都必须进行适当处理。

通常情况下,应用程序通过 GUI 组件间接操作显存设备,GUI 本身会处理上面所讲的所有信息。只有在某些特殊场合(比如需要更高显示效率,或者一些简单的图形化应用),才需要直接操作显存本身。

文档内容是否对您有所帮助?
有帮助
没帮助