實作遠端螢幕截圖的思路很簡單,就是直接擷取裝置的顯存資料,由PC再現畫面。由于我們已經實作了Custom信道,是以我們在原有程式基礎上,增添一個Custom_Command_Screenshots指令,就可以完成資料的擷取。但是比較麻煩的是,對不同的LCD裝置,同樣顯示畫面,顯存資料有可能不同,對嵌入式裝置,常見的LCD顯示是16位色(也有1位或8位色的,但比較少見),簡便起見,我們僅考慮16色顯示畫面的截圖。
16位色圖根據RGB的分量數值,一般有如下幾種模式:1555,565,555,第一種最高位含有透明度,我們把它和555歸為一類,第二種和第三種比較常見。
對565和555來說,中間一定是G(綠色),其分量值或5位或6位,沒有什麼分歧。麻煩的是,低5位(或高5位)有可能是紅色,也可能是藍色,如果混淆,則顯示的畫面會出現偏色,是以必須要準确擷取該配置資訊。
擷取資料和配置後,我們完全可以把擷取的資料一個點一個點地畫出來,但是這樣做,不僅導緻畫面顯示慢,還會使我們失去一次深入探究C#位圖呈現技術的機會。
查相關資料,我們發現在BITMAPINFOHEADER(位圖資訊頭)中有一項biCompression,其含義如下:
BI_RGB:沒有壓縮
BI_RLE8:每個象素8比特的RLE壓縮編碼,壓縮格式由2位元組組成(重複象素計數和顔色索引);
BI_RLE4:每個象素4比特的RLE壓縮編碼,壓縮格式由2位元組組成
BI_BITFIELDS:每個象素的比特由指定的掩碼決定。
如果我們位圖的參數設定為PixelFormat.Format16bppRgb555模式,則biCompression的值為BI_RGB,則我們将無法去設定RGB/BGR了,因為該結構體之後,就是位圖資料了。
如果我們設定的參數為PixelFormat.Format16bppRgb565,則biCompression值為BITFIELDS,BITMAPINFOHEADER結構體之後,會增加12個位元組的資料,分别為4位元組的R值掩碼、4位元組的G值掩碼和4位元組的B值掩碼,預設資料是0xF800、0x07E0、0x001F。
好了,我們的問題解決了,通過配置以上資料,便可解決我們的問題。
此外,該配置參數從何而來?裝置的開發者一定比上層軟體開發者更清楚,是以這個參數配置,就由我們裝置上的代碼提供。
程式實作後,工作畫面如下:
一、NativeCode代碼
如下代碼均在Customprocess.cpp檔案中添加。
i、新增指令
#define Custom_Command_Screenshots 0x02
ii、傳回配置參數和顯存資料
case Custom_Command_Screenshots:
UINT32 ScreenDataOffset = inData[4]<<24 | inData[3] <<16 | inData[2]<<8 | inData[1];
UINT16 ScreenDataSize = inData[6]<<8 | inData[5];
if(ScreenDataOffset == 0xFFFFFFFF)
{
*outLength=6;
//555 BGR
UINT16 R_Mask = 0x001F;
UINT16 G_Mask = 0x03E0;
UINT16 B_Mask = 0x7C00;
outData[0] = (UINT8)(R_Mask & 0xFF);
outData[1] = (UINT8)((R_Mask >> 8) & 0xFF);
outData[2] = (UINT8)(G_Mask & 0xFF);
outData[3] = (UINT8)((G_Mask >> 8) & 0xFF);
outData[4] = (UINT8)(B_Mask & 0xFF);
outData[5] = (UINT8)((B_Mask >> 8) & 0xFF);
}
else
{
UINT8 *pScreen= (UINT8 *)LCD_GetFrameBuffer();
*outLength = ScreenDataSize;
memcpy(outData,(UINT8 *)(pScreen+ScreenDataOffset), ScreenDataSize);
break;
裝置上的代碼編寫完畢,是不是很簡單?!
二、Screenshots插件開發
private void palScreen_Paint(object sender, PaintEventArgs e)
{
if (LCD_BitsPerPixel == 16)
{
LCD_bmp = new Bitmap(LCD_Width, LCD_Heigth, System.Drawing.Imaging.PixelFormat.Format16bppRgb565);
MemoryStream ms = new MemoryStream();
LCD_bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
ms.Flush();
Byte[] bytMask = new byte[4];
ms.Position = 54;
ms.Write(BitConverter.GetBytes(R_Mask), 0, 4);
ms.Write(BitConverter.GetBytes(G_Mask), 0, 4);
ms.Write(BitConverter.GetBytes(B_Mask), 0, 4);
ms.Write(LCD_Buffer, 0, LCD_Buffer.Length);
LCD_bmp = new Bitmap(ms);
//顯示的畫面是倒的,是以要翻轉一下
LCD_bmp.RotateFlip(RotateFlipType.Rotate180FlipX);
e.Graphics.DrawImage(LCD_bmp, 0, 0);
}
else //顯示紅X,表示不支援
e.Graphics.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, LCD_Width, LCD_Heigth));
e.Graphics.DrawRectangle(new Pen(Color.Red, 2), new Rectangle(0, 0, LCD_Width, LCD_Heigth));
e.Graphics.DrawLine(new Pen(Color.Red, 2), 0, 0, LCD_Width, LCD_Heigth);
e.Graphics.DrawLine(new Pen(Color.Red, 2), 0, LCD_Width, 0, LCD_Heigth);
}
本系列的文章,我已經草拟了若幹後續文章的題目,諸如《加載檔案系統中的Pe檔案》、《Pe檔案探析》、《動态加載Native Code(dll)》和《實作函數回調》等等,但是最近對uc/os-ii比較感興趣,準備把.Net MF移植到該系統上去,以期改善.Net MF的實時性能,是以這期間,更多的可能是寫些關于uc/os-ii的文章。