在做嵌入式应用程序开发时,有的场景下因为要追求图像显示效率,需要直接访问Frame Buffer,比如更流畅的视频显示。基于minigui框架的应用程序该如何访问Frame Buffer呢?
最近就在为这个事儿头疼, 之前在设计时,视频输出是将一帧图像解码为BITMAP后作为窗口的背景画到屏幕上,这在PC模拟器上跑没啥问题,等到直接上开发板跑的时候,问题就来了----太慢。毕竟通过minigui这个框架要把一个BITMAP刷到屏幕上要经过好多个环节。所以肯定不如直接写Frame Buffer来得快呀。
于是就在想如何在MiniGUI的框架下直接读写Frame Buffer呢,翻遍了minigui公开的接口函数,没有提供这种直接读写Frame Buffer的方法。
不死心,在minigui源码中从BitBlt
函数的实现代码开始一层层往下查。又倒过来从fbcon图形引擎的实现代码向上查。最终找到了GAL_Surface
这个结构,这是NEWGAL的一个数据结构,定义在libminigui-3.2.0/src/include/newgal.h
,如下(请关注本文作者添加了中文注释的字段)
/* This structure should be treated as read-only, except for 'pixels', which, if not NULL, contains the raw pixel data for the surface. */
typedef struct GAL_Surface {
Uint32 flags; /* Read-only */
GAL_PixelFormat *format; /* 描述FrameBuffer的像素格式(RGB24,RGB565)的数据结构 */
void *video; /* Read-only */
int w, h; /* FrameBuffer宽高(像素) */
/* VW[2018-01-18]: For 64b, use signed int instead of Uint32 for pitch. */
int pitch; /* 每行像素的长度(字节) */
void *pixels; /* FrameBuffer的起始地址 */
int offset; /* Private */
/* Hardware-specific surface info */
struct private_hwdata *hwdata;
/* clipping information */
GAL_Rect clip_rect; /* Read-only */
/* info for fast blit mapping to other surfaces */
struct GAL_BlitMap *map; /* Private */
/* format version, bumped at every change to invalidate blit maps */
unsigned int format_version; /* Private */
/* Reference count -- used when freeing surface */
int refcount; /* Read-mostly */
} GAL_Surface;
不管结构中的其他字段,有了上面这个对象所提供的Frame Buffer的宽高(w,h
),起始地址(pixels
),行步长(pitch
)信息,像素格式(GAL_PixelFormat
结构中的BitsPerPixel
,BytesPerPixel
字段),已经可以直接操作Frame Buffer了.
那么如何获取当前图形引擎的GAL_Surface
对象呢?还是要看libminigui-3.2.0/src/include/newgal.h
这个头文件,如下:
/* * This function returns a pointer to the current display surface. * If GAL is doing format conversion on the display surface, this * function returns the publicly visible surface, not the real video * surface. */
extern GAL_Surface * GAL_GetVideoSurface (void);
好了,有了GAL_GetVideoSurface
函数可以得到GAL_Surface
, 但你会发现 libminigui-3.2.0/src/include/newgal.h
这个头文件并没有出现在MiniGUI的release清单中,是未公开的。所以要把这个头文件复制到自己的项目中才能引用其中的函数。(记得要把newgal.h
中的#include "gdi.h"
改为#include
,否则编译通不过)
下面是关于从GAL_Surface
直接访问Frame Buffer的简单示例
#include
#include "newgal.h"
/** * 图像顺时针旋转90度并缩放到目标BITMAP的尺寸 * @param src 原图像 * @param [out] dst 缩放旋转后的图像 */
static void scaledBitmap_Rotate90 (const PBITMAP src,PBITMAP dst)
{
uint8_t* dst_line= dst->bmBits;
uint32_t sx,sy,st;
uint8_t* spixel;
// x,y缩放比例
float ratex = (float)src->bmHeight/dst->bmWidth,ratey = (float)src->bmWidth/dst->bmHeight;
for(int dy=0; dy < dst->bmHeight; ++dy)
{
for(int dx=0; dx < dst->bmWidth; ++dx)
{
// 坐标缩放
sx = (uint32_t)(int)(dx * ratex),sy = (uint32_t)(int)(dy * ratey);
// 坐标旋转
st = sx,sx = sy,sy= src->bmHeight - st;
// 目标像素的原始图像中的位置
spixel = src->bmBits + sy*src->bmPitch + sx*src->bmBytesPerPixel;
switch(dst->bmBytesPerPixel)
{
case 1:
*(dst_line +dx) = *spixel;
break;
case 2:
*((uint16_t*)(dst_line + dx*dst->bmBytesPerPixel)) = *((uint16_t*)spixel);
break;
case 4:
*((uint32_t*)(dst_line + dx*dst->bmBytesPerPixel)) = *((uint32_t*)spixel);
break;
case 3:
default:
memcpy(dst_line+dx*dst->bmBytesPerPixel,spixel,dst->bmBytesPerPixel);
break;
}
}
dst_line += dst->bmPitch;
}
}
void test()
{
// 指向 frame buffer的 BITMAP
BITMAP fb_bmp;
// 获取当前图像引擎的GAL_Surface对象
GAL_Surface* sf = GAL_GetVideoSurface();
printf("GAL_Surface:%d x %d,BitsPerPixel %d,pitch %d",
sf->w,sf->h,sf->format->BitsPerPixel,sf->pitch);
// 根据GAL_Surface的参数将fb_bmp初始化为一个映射到FrameBuffer的BITMAP.
if(!InitBitmap(HDC_SCREEN,sf->w,sf->h,sf->pitch,(BYTE*)sf->pixels,&fb_bmp))
{
printf("InitBitmap fail :%dx%d",sf->w,sf->h);
return ;
}
// 将要显示的图像pbmp直接输出到frame buffer
// 这个只是示例,你可以向代表frame buffer的fb_bmp写入任何数据,都会直接在屏幕显示
scaledBitmap_Rotate90 (pbmp,&fb_bmp);
}