WINVNC源码分析——图像

Posted 松狮MVP

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WINVNC源码分析——图像相关的知识,希望对你有一定的参考价值。

WINVNC是用bitmap格式保存桌面图像的,所以先补充下相关知识。

 

图像“像素”(Pixel):把影像放大数倍,会发现这些连续色调其实是由许多色彩相近的小方点所组成,这些小方点就是构成影像的最小单位“像素”(Pixel)。

图像分辨率:表示每一个方向上的像素数量,比如640X480,表示由640X480个像素组成。

DPI(Dot Per Inch):表示每英寸显示的像素数。图像显示清晰效果就看这个拉,一英寸等于25.4mm。

位/像素 (bpp:bits per pixel):既每个像素数据需要占用的bit数目,用来保存颜色。比如8位可得到256种颜色,16位可得到65,536种颜色,而24位可得到16,777,216种颜色。

 

view plain
  1. //WINDOWS API:
  2. int     nFullWidth=GetSystemMetrics(SM_CXSCREEN); //获取x方向分辨率     
  3. int     nFullHeight=GetSystemMetrics(SM_CYSCREEN); //获取y方向分辨率
  4. BOOL EnumDisplaySettingsEx( //获取屏幕分辨率和bpp 
  5. LPCTSTR lpszDeviceName,  // display device 
  6. DWORD iModeNum,          // graphics mode 
  7. LPDEVMODE lpDevMode,      // graphics mode settings 
  8. DWORD dwFlags            // options);
  9. LONG ChangeDisplaySettingsEx( //改变屏幕分辨率和颜色质量 
  10. LPCTSTR lpszDeviceName,  // name of display device
  11. LPDEVMODE lpDevMode,     // graphics mode 
  12. HWND hwnd,               // not used; must be NULL 
  13. DWORD dwflags,            // graphics mode options 
  14. LPVOID lParam            // video parameters (or NULL)
  15. );
  16. int GetDeviceCaps(  //获取想要的设备信息,包括dpi等 
  17. HDC hdc,     // handle to DC 
  18. int nIndex   // index of capability
  19. );  
 

 

图像格式一般分为两种:映射图像和向量图像,bitmap就是映射图像。metafile是向量图像。

位映射图像用离散的像素来处理输出设备;而向量图像用笛卡尔座标系统来处理输出设备,其线条和填充对象能被个别拖移。现在大多数的图像输出设备是位映射设备,这包括视讯显示、点阵打印机、激光打印机和喷墨打印机。而笔式绘图机则是向量输出设备。

编辑bitmap大小一般就是复制或删除像素的某些行和列的像素。

 

view plain
  1. //WINDOWS API:
  2. BitBlt (hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, ySrc, dwROP) ;//从称为「来源」的设备上下文中将一个矩形区的像素传输到称为「目的(destination)」的另一个设备上下文中相同大小的矩形区。来源和目的设备上下文可以相同而且bpp必须相同。、
  3. StretchBlt ( hdcDst, xDst, yDst, cxDst, cyDst,hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP) ;//在复制时拉伸或者压缩图像尺寸
  4. int GetDIBits(  HDC hdc,           // handle to DC 
  5. HBITMAP hbmp,      // handle to bitmap 
  6. UINT uStartScan,   // first scan line to set 
  7. UINT cScanLines,   // number of scan lines to copy 
  8. LPVOID lpvBits,    // array for bitmap bits 
  9. LPBITMAPINFO lpbi, // bitmap data buffer 
  10. UINT uUsage        // RGB or palette index
  11. );
  12. //读取bitmap数据到缓冲区lpvBits。既按扫描顺序一个一个的像素数据。
  13. HDC CreateCompatibleDC(HDC hdc);//创建一个内存
  14. DCHBITMAP CreateCompatibleBitmap( 
  15. HDC hdc,        // handle to DC 
  16. int nWidth,     // width of bitmap,
  17. in pixels 
  18. int nHeight     // height of bitmap,
  19. in pixels
  20. );
  21. //创建设备环境兼容的位图。注意如果bitmap要做不同设备copy,hdc尽量不要用内存DC的。因为由CreateCompatibleBitmap函数创建的位图的颜色格式与由参数hdc标识的设备的颜色格式匹配。该位图可以选入任意一个与原设备兼容的内存设备环境中。由于内存设备环境允许彩色和单色两种位图。因此当指定的设备环境是内存设备环境时,由CreateCompatibleBitmap函数返回的位图格式不一定相同。然而为非内存设备环境创建的兼容位图通常拥有相同的颜色格式,并且使用与指定的设备环境一样的色彩调色板。
  22. HBITMAP CreateDIBSection(  HDC hdc,                 // handle to DC 
  23. CONST BITMAPINFO *pbmi,  // bitmap data 
  24. UINT iUsage,             // data type indicator 
  25. VOID **ppvBits,          // bit values 
  26. HANDLE hSection,         // handle to file mapping object 
  27. DWORD dwOffset           // offset to bitmap bit values
  28. );
  29. //可以得到创建的图像在内存中的首地址,可以直接访问图像内存地址了,这样就不要用GetDIBits来获取数据了GdiFlush();
  30. //让应用程序进入等待状态,直到所有待决的绘图操作完成为止。阻塞等屏幕绘画完。通过成批合并绘图操作命令,win32图形子系统(GDI)可改善绘图的性能。如调用一系列绘图命令,他们都返回布尔值(TRUE表示成功,零表示失败),就可将他们置于一个内部GDI队列里。此时,函数可以立即返回。随后,GDI子系统会执行这些待决的绘图命令。可考虑一种最常见的情况。在这种情况下,系统安装了一块显示卡。卡上自带图形处理器或加速器。画图的时候,GDI只需将图形命令简单的发送给显示卡,另其完成实际的操作。如果必须等待每个绘图命令都完成并返回,系统和应用程序的性能就会受到显示卡绘图速度的极大限制。所以在这个时候,GDI将绘图命令置于一个名为“批”(Batch)的队列里。这样一来,系统和应用程序就能继续运行,同时仍然让显示卡进行绘图操作。  
 

 

 

//下面是几个很重要的数据结构

BITMAPFILEHEADER

    BITMAPFILEHEADER结构包含关于类型,大小,布局设备无关的位图信息。

    typedef struct tagBITMAPFILEHEADER  

      WORD    bfType; 

      DWORD   bfSize; 

      WORD    bfReserved1; 

      WORD    bfReserved2; 

      DWORD   bfOffBits; 

     BITMAPFILEHEADER, *PBITMAPFILEHEADER; 

    /*成员

        bfType

            声明文件类型,必须是BM。

        bfSize

            声明位图文件的大小,以字节为单位。

        bfReserved1

            保留,必须为0。

        bfReserved2

            保留,必须为0。

        bfOffBits

            声明偏移量,从BITMAPFILEHEADER结构开始到位图数据,以字节为单位。

    备注

        在设备无关文件中,BITMAPINFOHEADER结构紧随其后。

*/

 

//BITMAPINFOHEADER

    //BITMAPINFOHEADER结构包含设备无关位图的大小以及色彩格式的信息。

    typedef struct tagBITMAPINFOHEADER

      DWORD  biSize; 

      LONG   biWidth; 

      LONG   biHeight; 

      WORD   biPlanes; 

      WORD   biBitCount; 

      DWORD  biCompression; 

      DWORD  biSizeImage; 

      LONG   biXPelsPerMeter; 

      LONG   biYPelsPerMeter; 

      DWORD  biClrUsed; 

      DWORD  biClrImportant; 

     BITMAPINFOHEADER, *PBITMAPINFOHEADER; 

    /*成员

        biSize

            声明这个结构体所需要的字节数。

        biWidth

            声明位图的宽,以像素为单位。

        biHeight 

            声明位图的高,以像素为单位。如果biHeight是正数,位图是一个自下而上的设备无关位图,起点在左下角。如果biHeight是负数,位图是一个自上而下的设备无关位图,起点在左上角。

            如果biHeight是负数,指示一个自上而下的设备无关位图,biCompression就必须为BI_RGB或者BI_BITFIELDS。自上而下的设备无关位图不能被压缩。

        biPlanes

            为目标装置声明平面数,这个值必须是1。

        biBitCount

            声明每像素位数。BITMAPINFOHEADER的成员biBitCount确定了定义每一个像素需要的位数,以及位图所需颜色的最大值。这个成员必须是以下的值:

 

  Value   Meaning

    0        JPEG或者PNG格式声明每像素位数

    1        位图是单色的,包含两个颜色信息,位图数组中的每一位代表一个像素。如果这一位是0,那么这个点的颜色就是调色板中第一个RGBQUAD结构所定义的颜色。如果这一位是1,那么这个点的颜色就是调色板中第二个GBQUAD结构所定义的颜色。

    4        位图最多有16种颜色。调色板数组中有16个元素。位图中的每一个像素由一个4位的索引指向调色板。举个例子,位图的第一个字节是0x1F,这个字节代表两个像素,第一个像素颜色是调色板中的第二个RGBQUAD结构所定义的颜色,第二个像素颜色是调色板中的第十六个RGBQUAD结构所定义的颜色。

    8        位图最多有256种颜色。调色板中有256个元素。每一个字节代表一个颜色。

    16       位图有2^16种颜色。如果biCompression为BI_RGB,则没有调色板。RGB565使用16位表示一个像素,这16位中的5位用于R,6位用于G,5位用于B。 RGB555是另一种16位的RGB格式,RGB分量都用5位表示(剩下的1位不用)。比如从高位到地位:X R R R R G G G G G B B B B B (X表示不用,可以忽略)。

  可以组合使用屏蔽字和移位操作来得到RGB各分量的值:

  #define RGB555_MASK_RED 0x7C00

  #define RGB555_MASK_GREEN 0x03E0

  #define RGB555_MASK_BLUE 0x001F

  R = (wPixel & RGB555_MASK_RED) >> 10; // 取值范围0-31

  G = (wPixel & RGB555_MASK_GREEN) >> 5; // 取值范围0-31

  B = wPixel & RGB555_MASK_BLUE; // 取值范围0-31

    24       位图最多有2^24种颜色。没有调色板。位图数组中每三个字节代表一个像素的红绿蓝相对强度。

        biCompression

            BI_RGB:对一种颜色进行编码的方法统称为“颜色空间”或“色域”。用最简单的话说,世界上任何一种颜色的“颜色空间”都可定义成一个固定的数字或变量。RGB(红、绿、蓝)只是众多颜色空间的一种。采用这种编码方法,每种颜色都可用三个变量来表示-红色绿色以及蓝色的强度。RGB1、RGB4、RGB8都是调色板类型的RGB格式,在描述这些媒体类型的格式细节时,通常会在BITMAPINFOHEADER数据结构后面跟着一个调色板(定义一系列颜色)。它们的图像数据并不是真正的颜色值,而是当前像素颜色值在调色板中的索引。以RGB1(2色位图)为例,比如它的调色板中定义的两种颜色值依次为0x000000(黑色)和0xFFFFFF(白色),那么图像数据001101010111…(每个像素用1位表示)表示对应各像素的颜色为:黑黑白白黑白黑白黑白白白…。

        biSizeImage

            声明图像文件的大小,以字节为单位。如果是BI_RGB位图,可能是0。

        biXPelsPerMeter 

            声明横向点距,以像素每米为单位。

        biYPelsPerMeter

            声明纵向点距,以像素每米为单位。

        biClrUsed

            声明实际使用到的颜色个数,如果是0,则使用所有颜色。

        biClrImportant

            声明显示这个位图必须的颜色索引个数,如果是0,则需要所有颜色。

*/

 

//RGBQUAD

    //RGBQUAD结构描述红绿蓝相对亮度

    typedef struct tagRGBQUAD

      BYTE    rgbBlue; 

      BYTE    rgbGreen; 

      BYTE    rgbRed; 

      BYTE    rgbReserved; 

     RGBQUAD;   

    /*成员 

        rgbBlue

            声明蓝色强度

        rgbGreen

            声明绿色强度

        rgbRed

            声明红色强度

        rgbReserved

            保留,必须是0

*/

 

 

//我们结合源码看下函数BOOL vncDesktop::SetPixShifts()就能更好的理解它们

view plain
  1. struct _BMInfo BOOL            truecolour;BITMAPINFO       bmi;// Colormap info - comes straight after BITMAPINFO - **HACK**RGBQUAD            cmap[256]; m_bminfo;  
 

 

view plain
  1. BOOLvncDesktop::SetPixShifts()
  2. // Sort out the colour shifts, etc.
  3. DWORD redMask=0, blueMask=0, greenMask = 0;
  4. switch (m_bminfo.bmi.bmiHeader.biBitCount)
  5. case 16:
  6. // Standard 16-bit display
  7. if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
  8. //需要自己来指定三色各占哪几位,把该色的所占位置1其它位置0,保存该值(暂时叫色标吧)。
  9. //biBitCount位16,对于blue颜色来说0-4这5个位置1,得到blue色标blueMask = 0x001f,当然这里作了强制转换,因为blue为无符号32位
  10. //如果一个无符号16位的像素数据uspixel,先要强转为无符号32位的uipixel,当然不转也无所谓的,因为对应的色标也必定从无符号16位强转获取的。
  11. //我们要获得blue的索引值只要uipixel&blueMask就OK拉
  12. //当然red的索引值还是需要移位下(uipixel&redMask)>>10
  13. // each word single pixel 5-5-5
  14. redMask = 0x7c00;
  15. greenMask = 0x03e0;
  16. blueMask = 0x001f;
  17. else
  18. if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)//通过读取RGBQUAD来获取色标
  19. redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
  20. greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
  21. blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
  22. break;
  23. case 32:// Standard 24/32 bit displays
  24. if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)//同上,区别是每个颜色占了8位拉 each word single pixel 8-8-8
  25. redMask = 0xff0000;
  26. greenMask = 0xff00;
  27. blueMask = 0x00ff;
  28. else
  29. if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
  30. //同上
  31. redMask =   *(DWORD *) &m_bminfo.bmi.bmiColors[0];
  32. greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
  33. blueMask =  *(DWORD *) &m_bminfo.bmi.bmiColors[2];
  34. break;
  35. default:// Other pixel formats are only valid if they're palette-based
  36. if (m_bminfo.truecolour)
  37. vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshifts/n");
  38. return FALSE;
  39. return TRUE;
  40. // Convert the data we just retrieved
  41. MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
  42. MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
  43. MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);
  44. return TRUE;
  45.   
 

 

看看函数最后调用的inline void vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)函数:

view plain
  1. inline voidvncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
  2. for (shift = 0; (mask & 1) == 0; shift++)
  3. mask >>= 1;//无符号32位的mask右移操作,直到碰到位的值位1则退出。那么shift保留了mask从低位到高位数值连续为0的个数。
  4. //把右移后的值牵制转换成无符号16位,赋值给max。
  5. max = (CARD16) mask;
  6. //结合调用MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);来看
  7. //那么m_scrinfo.format.redMax保存的就是red的最大值,m_scrinfo.format.redShift是色标到最大值的位移值
  8. //假设一个无符号N(16或者32)位的像素数据upixel,
  9. //那么red的值就是( uspixel  & (m_scrinfo.format.redMax<<m_scrinfo.format.redShift) )>>shift
  10.   
 

 

下篇再看看桌面包装类vncDesktop的其它函数。

以上是关于WINVNC源码分析——图像的主要内容,如果未能解决你的问题,请参考以下文章

WINVNC分析——源码执行流程

WINVNC分析——源码执行流程

WINVNC源码分析——vnchooks

WINVNC源码分析——vnchooks

WINVNC源码分析——IO之rdr库

WINVNC源码分析——IO之rdr库