天天看點

【玩轉.Net MF – 04】遠端螢幕截圖

實作遠端螢幕截圖的思路很簡單,就是直接擷取裝置的顯存資料,由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的文章。

繼續閱讀