程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 【玩轉.Net MF – 04】遠程屏幕截圖

【玩轉.Net MF – 04】遠程屏幕截圖

編輯:關於.NET

實現遠程屏幕截圖的思路很簡單,就是直接獲取設備的顯存數據,由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#位圖呈現技術的 機會。

下面我們將深入研究.Net Framework的位圖顯示技術,首先聲明一個和設備顯 示尺寸一樣大小的位圖(new Bitmap)。幸運的是,我們發現可以設置 PixelFormat.Format16bppRgb565和PixelFormat.Format16bppRgb555參數。但究 竟是RGB還是BGR模式卻無法設置,如果不管這個參數,我們最終的截圖,你會發 現和顯示設備上的畫面偏色(很感謝顯示設備的默認16色模式和windows不同,否 則這個問題也許被掩蓋了)。解決方案有兩種,一是修改設備的顯示驅動(我在 開發Cortex-M3的開發板顯示驅動時,發現其顯示模式RGB/BGR是可以配置的), 二是由上位機解決這個問題。為了使程序的通用性更強,我選擇了後者。

查相關資料,我們發現在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);
              ms.Flush();
              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的文 章。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved