WINCE實現直接寫屏(一)
來源:互聯網
上載者:User
//========================================================================
//TITLE:
// WINCE實現直接寫屏
//AUTHOR:
// norains
//DATE:
// Friday 28-April-2006
//========================================================================
DOS下的直接寫屏估計很多人都知道;如果真的不知道,在網上一搜,恩...有不少現成的代碼.^_^今天要解決的是,在WINCE下實現直接寫屏.
其實說穿了不值錢,原理極其簡單,只要往顯示緩衝地址直接寫值就OK.
首先說一下基本的配置.我們採用的是sharp LH7A404H的開發版,其視頻匯流排是16bit;換而言之,就是一個象素用兩個位元組表示.最為重要的是,其硬體電路可以直接輸出565格式16bit的位元影像.關於位元影像,根據色深域(biBitCount)取值可以分為六種,分別是1,4,8,16,24,32;而其中16bit的位元影像根據紅,綠,藍的掩碼不同分為兩種格式:555(R:0x7C00,G:0x03E0,B:001F),565(R:0xF800,G:0x07E0,B:0x001F).更詳細的解說,可以參考別的資料;若要理解此篇而言,此知識點足矣.^_^
好了,我們就開始旅程吧.以下的工作,要實現這麼一個過程:當接收到一個訊號後,在螢幕中間繪製一副32*32的位元影像;停頓1秒之後,位元影像消失,回複原來螢幕本來面目.
我們直接在顯示驅動中添加代碼,對於LH7A404而言,是soclcd.cpp檔案.
恩,最初,我們需要再初始化中建立一個線程:
if (CreateThread(0, 0, DisplayInfoThread, (void *)m_ulFrmBufVirt, 0, NULL) == NULL)
{
RETAILMSG(1,(TEXT("norains Failed/n/r")));
return ;
}
DisplayInfoThread是我們線程處理函數,m_ulFrmBufVirt是傳給函數的型參,儲存的是顯示緩衝的地址.
接下來便是重中之重,編寫我們的處理函數了.
函數原型如此:static DWORD WINAPI DisplayInfoThread(LPVOID pData)
首先,我們先要建立一個訊號量,只有接收到訊號量的時候才開始寫屏.建立訊號量是一件很簡單的事情:
HANDLE hdEvent = CreateEvent(NULL, FALSE, FALSE, L"MyLockInfo");
...
while(true)
{
WaitForSingleObject(hdEvent, INFINITE); //如果沒有接收到訊號量,則一直停在此處
...
}
在進入迴圈體之前,我們還有兩件事情要做:一是分配一段記憶體用以保留原來螢幕的資料;二是將視頻指標指向螢幕中間,恩,因為我們要在中間顯示位元影像.
pSaveBuf = (PUCHAR) AllocPhysMem(TCHLK_SAVEBUFF_SIZE,PAGE_READWRITE|PAGE_NOCACHE,0,0,&ulOffset);
...
pVideoBase += ((m_iScreenHeight-TCHLK_LOG_HEIGHT)>>1)*m_iScreenWidth*2 + ((m_iScreenWidth-TCHLK_LOG_WIDTH)>>1)*2;
...
TCHLK_LOG_HEIGHT是我們自己定義的宏變數,代表的是位元影像的高度.為了大家看得明白,把其它宏定義也列出來吧:
#define TCHLK_LOG_WIDTH 32 //寬度
#define TCHLK_LOG_HEIGHT 32 //高度
#define TCHLK_SAVEBUFF_SIZE 0x2000 //緩衝要佔據的空間大小
好了,接下來我們要做的就是把螢幕的原來象素點儲存下來:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)(pSaveBuf+ulOffset), (PUCHAR)pVideoBuf, TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
儲存完畢,接下來不用說,肯定就是寫屏了:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pBmpInfo+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
恩,這裡pBmpInfo是一個數組,儲存的是以十六進位表示的BMP位元影像資訊.雖然文中用到的是32*32的位元影像,但資料量也有8K!恩,如果在文章中間插入這個位元影像資料,影響美觀是肯定的,所以我們還是把這數組定義放在文章最後吧.如果你迫不及待想看這數組定義,恩,就請往最後翻頁吧.
當然咯,我們需要控制位元影像顯示的時間.一個小小的語句段就可以解決問題:
iCount= 100; //about 1s
while (iCount -- > 0) HalStallExecution(10*10000);
最後嘛,時間到,不用說,就是恢複原來螢幕象素.和繪製位元影像一樣,也是往緩衝拷貝資料:
ulOffset = 0;
pVideoBuf = pVideoBase;
while(ulOffset<TCHLK_LOG_HEIGHT*TCHLK_LOG_WIDTH*2)
{
memcpy((PUCHAR)pVideoBuf, (PUCHAR)(pSaveBuf+ulOffset), TCHLK_LOG_WIDTH*2);
pVideoBuf += m_iScreenWidth<<1;
ulOffset += TCHLK_LOG_WIDTH*2;
}
不過,總有事情要出乎我們預料的.有時候,螢幕上的圖片一閃而過,特別是播放電影的時候.這個問題嘛,是因為別的程式同時往緩衝寫資料.解決這個問題也很簡單,不過就顯得比較霸道.我們自己可以編寫兩個函數,一個用來關閉中斷,另一個開中斷.關閉中斷的時候,誰都不能往螢幕寫資料.只是這兩個函數因不同硬體有不同實現,就此略過.我們在此使用的是LH7A404預先寫好的兩個函數:
BOOL fWasEnabled = InterruptsDisable();
...
InterruptsRestore(fWasEnabled);
好了,至此,一切大功告成!不過,可能有些執著的偏執狂對於之前分配的記憶體沒有釋放一直耿耿於懷,好吧,那我們就在迴圈體外對記憶體進行釋放吧:
FreePhysMem((void *)pSaveBuf);
不過,估計這語句是永遠也執行不到了.^_^