📄 rgb-ycrcb.txt
字号:
read(device_fd, tmp, Mywidth*Myheight*3);
for(int i = 0; i < 176 * 144; ++i)
{
buffer[4*i] = tmp[3*i];//first bit is blue
buffer[4*i + 1] = tmp[3*i + 1];//second bit is green
buffer[4*i + 2] = tmp[3*i + 2] ;//third bit is red
buffer[4*i + 3] = 130;//forth bit
}
//后面这此是用QT库写的,意思是将buffer的内容转为image再转为pixmap,然后显示出来
QImage img(buffer, Mywidth, Myheight, 32, NULL, 0, QImage::LittleEndian);
QPixmap pic;
pic.convertFromImage(img);
PixmapVideo->setPixmap(pic);
FillEllips 函数填充指定的椭圆。椭圆心为(sx, sy),X 轴半径为 rx,Y 轴半径为 ry。
FillSector 函数填充由圆弧和两条半径形成的扇形。圆心为(x, y),半径为 r,起始弧度为 ang1,终止弧度为 ang2。
FillPolygon 函数填充多边形。pts 表示多边形各个顶点,vertices 表示多边形顶点个数。
FloodFill 从指定点(x, y)开始填注。
需要注意的是,所有填充函数使用当前画刷属性(颜色),并且受当前光栅操作的影响。
下面的例子说明了如何使用 FillCircle 和 FillEllipse 函数填充圆或者椭圆。假定给定了两个点,pts[0] 和 pts[1],其中 pts[0] 是圆心或者椭圆心,而 pts[1] 是圆或者椭圆外切矩形的一个顶点。
int rx = ABS (pts[1].x - pts[0].x);
int ry = ABS (pts[1].y - pts[0].y);
if (rx == ry)
FillCircle (hdc, pts[0].x, pts[0].y, rx);
else
FillEllipse (hdc, pts[0].x, pts[0].y, rx, ry);
5 建立复杂区域
除了利用填充生成器进行填充绘制以外,我们还可以使用填充生成器建立由封闭曲线包围的复杂区域。我们知道,MiniGUI 当中的区域是由互不相交的矩形组成的,并且满足 x-y-banned 的分布规则。利用上述的多边形或者封闭曲线生成器,可以将每条扫描线看成是组成区域的高度为 1 的一个矩形,这样,我们可以利用这些生成器建立复杂区域。MiniGUI 利用现有的封闭曲线生成器,实现了如下的复杂区域生成函数:
BOOL GUIAPI InitCircleRegion (PCLIPRGN dst, int x, int y, int r);
BOOL GUIAPI InitEllipseRegion (PCLIPRGN dst, int x, int y, int rx, int ry);
BOOL GUIAPI InitPolygonRegion (PCLIPRGN dst, const POINT* pts, int vertices);
BOOL GUIAPI InitSectorRegion (PCLIPRGN dst, const POINT* pts, int vertices);
利用这些函数,我们可以将某个区域分别初始化为圆、椭圆、多边形和扇形区域。然后,可以利用这些区域进行点击测试(PtInRegion 和 RectInRegion),或者选择到 DC 当中作为剪切域,从而获得特殊显示效果。
6 直接访问显示缓冲区
在新的 GDI 接口中,我们添加了用来直接访问显示缓冲区的函数,原型如下:
Uint8* GUIAPI LockDC (HDC hdc, const RECT* rw_rc, int* width, int* height, int* pitch);
void GUIAPI UnlockDC (HDC hdc);
LockDC 函数锁定给定 HDC 的指定矩形区域(由矩形 rw_rc指定,设备坐标),然后返回缓冲区头指针。当 width、height、pitch 三个指针不为空时,该函数将返回锁定之后的矩形有效宽度、有效高度和每扫描线所占的字节数。
UnlockDC 函数解开已锁定的 HDC。
锁定一个 HDC 意味着 MiniGUI 进入以互斥方式访问显示缓冲区的状态。如果被锁定的 HDC 是一个屏幕 DC(即非内存 DC),则该函数将在必要时隐藏鼠标光标,并锁定 HDC 对应的全局剪切域。在锁定一个 HDC 之后,程序可通过该函数返回的指针对锁定区域进行访问。需要注意的是,不能长时间锁定一个 HDC,也不应该在锁定一个 HDC 时进行其他额外的系统调用。
假定以锁定矩形左上角为原点建立坐标系,X 轴水平向右,Y 轴垂直向下,则可以通过如下的公式计算该坐标系中(x, y)点对应的缓冲区地址(假定该函数返回的指针值为 frame_buffer):
Uint8* pixel_add = frame_buffer + y * (*pitch) + x * GetGDCapability (hdc, GDCAP_BPP);
根据该 HDC 的颜色深度,就可以对该象素进行读写操作。作为示例,下面的程序段随机填充锁定区域:
int i, width, height, pitch;
RECT rc = {0, 0, 200, 200};
int bpp = GetGDCapability (hdc, GDCAP_BPP);
Uint8* frame_buffer = LockDC (hdc, &rc, &width, &height, &pitch);
Uint8* row = frame_buffer;
for (i = 0; i < *height; i++) {
memset (row, rand ()%0x100, *width * bpp);
row += *pitch;
}
UnlockDC (hdc);
7 YUV 覆盖和 Gamma 校正
为了增强 MiniGUI 对多媒体的支持,我们增加了对 YUV 覆盖(Overlay)和 Gamma 校正的支持。
7.1 YUV 覆盖(Overlay)
多媒体领域中,尤其在涉及到 MPEG 播放时,通常使用 YUV 颜色空间来表示颜色,如果要在屏幕上显示一副 MPEG 解压之后的图片,则需要进行 YUV 颜色空间到 RGB 颜色空间的转换。YUV 覆盖最初来自一些显示芯片的加速功能。这种显示芯片能够在硬件基础上完成 YUV 到 RGB 的转换,免去软件转换带来的性能损失。在这种显示芯片上建立了 YUV 覆盖之后,可以直接将 YUV 信息写入缓冲区,硬件能够自动完成 YUV 到 RGB 的转换,从而在 RGB 显示器上显示出来。在不支持 YUV 覆盖的显示芯片上,MiniGUI 也能够通过软件实现 YUV 覆盖,这时,需要调用 DisplayYUVOverlay 函数将 YUV 信息转换并缩放显示在建立 YUV 覆盖的 DC 设备上。
MiniGUI 提供的 YUV 覆盖操作函数原型如下:
/***************************** YUV overlay support ***************************/
/* 最常见的视频覆盖格式.
*/
#define GAL_YV12_OVERLAY 0x32315659 /* Planar mode: Y + V + U (3 planes) */
#define GAL_IYUV_OVERLAY 0x56555949 /* Planar mode: Y + U + V (3 planes) */
#define GAL_YUY2_OVERLAY 0x32595559 /* Packed mode: Y0+U0+Y1+V0 (1 plane) */
#define GAL_UYVY_OVERLAY 0x59565955 /* Packed mode: U0+Y0+V0+Y1 (1 plane) */
#define GAL_YVYU_OVERLAY 0x55595659 /* Packed mode: Y0+V0+Y1+U0 (1 plane) */
/* 该函数创建一个视频输出覆盖
*/
GAL_Overlay* GUIAPI CreateYUVOverlay (int width, int height,
Uint32 format, HDC hdc);
/* 锁定覆盖进行直接的缓冲区读写,结束后解锁 */
int GAL_LockYUVOverlay (GAL_Overlay *overlay);
void GAL_UnlockYUVOverlay (GAL_Overlay *overlay);
#define LockYUVOverlay GAL_LockYUVOverlay
#define UnlockYUVOverlay GAL_UnlockYUVOverlay
/* 释放视频覆盖 */
void GAL_FreeYUVOverlay (GAL_Overlay *overlay);
#define FreeYUVOverlay GAL_FreeYUVOverlay
/* 将视频覆盖传送到指定 DC 设备上。该函数能够进行 2 维缩放
*/
void GUIAPI DisplayYUVOverlay (GAL_Overlay* overlay, const RECT* dstrect);
有关视频格式的信息,可参见:
http://www.webartz.com/fourcc/indexyuv.htm
有关颜色空间的相互关系的息,可参见:
http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html
7.2 Gamma 校正
Gamma 校正通过为 RGB 颜色空间的每个颜色通道设置 Gamma 因子,来动态调整 RGB 显示器上的实际 RGB 效果。需要注意的是,Gamma 校正需要显示芯片的硬件支持。
应用程序可以通过 SetGamma 函数设置 RGB 三个颜色通道的 Gamma 校正值。该函数原型如下:
int GAL_SetGamma (float red, float green, float blue);
#define SetGamma GAL_SetGamma
线性 Gamma 校正值的范围在 0.1 到 10.0 之间。如果硬件不支持 Gamma 校正,该函数将返回 -1。
应用程序也可以通过 SetGammaRamp 函数设置 RGB 三个颜色通道的非线性 Gamma 校正值。该函数原型如下:
int GAL_SetGammaRamp (Uint16 *red, Uint16 *green, Uint16 *blue);
#define SetGammaRamp GAL_SetGammaRamp
int GAL_GetGammaRamp (Uint16 *red, Uint16 *green, Uint16 *blue);
#define GetGammaRamp GAL_GetGammaRamp
函数 SetGammaRamp 实际设置的是每个颜色通道的 Gamma 转换表,每个表由 256 个值组成,表示设置值和实际值之间的对应关系。当设置屏幕上某个象素的 RGB 分别为 R、G、B 时,实际在显示器上获得的象素 RGB 值分别为:red[R]、green[G]、blue[B]。如果硬件不支持 Gamma 校正,该函数将返回 -1。
函数 GetGammaRamp 获得当前的 Gamma 转换表。
Gamma 校正的最初目的,是为了能够在显示器上精确还原一副图片。Gamma 值在某种程度上表示的是某个颜色通道的对比度变化。但 Gamma 在多媒体和游戏程序中有一些特殊用途――通过 Gamma 校正,可以方便地获得对比度渐进效果。
8 小结
本文描述了自 MiniGUI 1.1.0Pre4 版本发布以来新增的 GDI 接口。这些接口涉及到曲线和填充生成器、复杂曲线的绘制、封闭曲线填充、复杂区域的创建、直接访问 FrameBuffer、YUV 覆盖和 Gamma 校正等等。通过本文的介绍,相信读者能够对 MiniGUI 的新 GDI 接口有一个更加全面的认识。
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -