minigui 3.2.0:直接訪問framebuffer的方法及示例
在做嵌入式應用程式開發時,有的場景下因為要追求影象顯示效率,需要直接訪問Frame Buffer,比如更流暢的視訊顯示。基於minigui框架的應用程式該如何訪問Frame Buffer呢?
最近就在為這個事兒頭疼, 之前在設計時,視訊輸出是將一幀影象解碼為BITMAP後作為視窗的背景畫到螢幕上,這在PC模擬器上跑沒啥問題,等到直接上開發板跑的時候,問題就來了----太慢。畢竟通過minigui這個框架要把一個BITMAP刷到螢幕上要經過好多個環節。所以肯定不如直接寫Frame Buffer來得快呀。
於是就在想如何在MiniGUI的框架下直接讀寫Frame Buffer呢,翻遍了minigui公開的介面函式,沒有提供這種直接讀寫Frame Buffer的方法。
不死心,在minigui原始碼中從BitBlt
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 <minigui/gdi.h>
,否則編譯通不過)
下面是關於從GAL_Surface
直接訪問Frame Buffer的簡單示例
#include <string.h>
#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);
}