1. 程式人生 > >minigui 3.2.0:直接訪問framebuffer的方法及示例

minigui 3.2.0:直接訪問framebuffer的方法及示例

在做嵌入式應用程式開發時,有的場景下因為要追求影象顯示效率,需要直接訪問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 <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);
}