【玩转.Net MF – 04】远程屏幕截图

简介: 实现远程屏幕截图的思路很简单,就是直接获取设备的显存数据,由PC再现画面。由于我们已经实现了Custom信道,所以我们在原有程序基础上,增添一个Custom_Command_Screenshots命令,就可以完成数据的获取。

前篇文章,我们实现了远程文件查看器,现在我们趁热打铁为.Net MF实现远程屏幕截图程序(类似VS2008远程工具中的远程放大程序)。

实现远程屏幕截图的思路很简单,就是直接获取设备的显存数据,由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的文章。

相关文章
|
安全
.Net MF V4.0开源前的代码整理
已经有好长一段时间没有更新博客了,一是去美国总部和台湾出差用了不少时间,二是做.Net MF代码整理又花了近一个月的时间。不过令人欣慰的是,目前.Net MF V4.0的相关代码整理已经告一段落,就等着下一步的开源了
628 0
|
内存技术
【玩转.Net MF – 01】Flash远程读写
目前在PC远程访问设备Flash,也就是部署TinyCLR和下载应用程序
537 0
|
内存技术
【玩转.Net MF – 02】让PC成为MF的鼠标键盘
通过扩展我以前为.Net MF开发的WinForm库(参见我以前的文章《开源System.Windows.Forms库,让.Net Micro Framework界面开发和上位机一样简单》),增加一个输入代理层,就可以实现虚拟鼠标和键盘输入。
564 0
|
网络协议
【玩转.Net MF – 03】远程文件查看器
做过WinCE或Windows Mobile开发的人都知道,VS2008开发工具提供了些远程工具,诸如远程文件查看器、远程注册表编辑器、远程堆查看器和远程放大等等。受此启发,所以才有了MF的远程文件查看器。
605 0
|
芯片 物联网 内存技术
WG7310(WLAN+Bluetooth+FM)芯片在.Net MF中的应用
WG7310芯片是Ti推出的一款芯片,集成了WLAN、Bluetooth、FM等功能(最近又推出了四合一的芯片,把GPS功能也集成了进去),由于以前在.Net MF上的一些工作是基于Ti DM335开发板上的,所以开发.Net MF系统的WiFi功能就选用了WG7310芯片。
700 0
|
芯片
免费发放firmwave,打造史上最低价.Net MF开发板
很久以前就曾多方位思考限制.Net Micro Framework发展的原因是什么?在物联网和Cortex-M3大行其道的今天,应该有更大的发展空间才对,为什么现在还是关注者甚少?我想主要原因有三,一、源码代码是否开源;二、是否有低价开发板;三、TinyCLR是否够小。
738 0
【STM32 .Net MF开发板学习-03】TinyGUI绘图示例
.Net Micro Framework官方图形库是WPF,由于目前ST Cortex-M3开发板RAM太小,最大才512K(常见是128K或256k),并且Cortex-M3的CPU主频也不太高,运行WPF图形框架显得过于重了,所以我这边推出了轻量级图形库TinyGUI
575 0
|
内存技术
【STM32 .Net MF开发板学习-04】TinyGUI位图显示
由于Cortex-M3开发板的RAM比较小,比如EM-STM3210E仅128K,所以显示位图是个比较棘手的事,如320*240 16位的位图大小就为150K,由于官方的WPF以一个BMP位图为本底进行绘图,所以RAM内存需求至少大于150K。
606 0
【STM32 .Net MF开发板学习-06】蜂鸣器和LED数码管显示
无论是蜂鸣器还是LED数码管显示,其实这二者对代码编写来说没有太大区别,都是GPIO的一个典型应用。红牛开发板有一个蜂鸣器,而EM-STM3210E有一个四位LED数码管,代码都相对简单,不值的为二者单独写一篇博文,所以二者合一以一篇文章来说明,不过两个示例代码是独立的。
630 0
|
内存技术
【STM32 .Net MF开发板学习-07】全屏位图无闪烁显示
16位320*240的位图大小为150K字节,而对于EM-STM3210E开发板来说,RAM仅有128K,远不够显示一幅完整位图,红牛的开发板即使有256K的RAM,但是刨去堆、栈及TinyCLR本身所用,剩下的也不多了,所以要显示全屏位图,必须分块显示。
511 0