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
- //WINDOWS API:
- int nFullWidth=GetSystemMetrics(SM_CXSCREEN); //获取x方向分辨率
- int nFullHeight=GetSystemMetrics(SM_CYSCREEN); //获取y方向分辨率
- BOOL EnumDisplaySettingsEx( //获取屏幕分辨率和bpp
- LPCTSTR lpszDeviceName, // display device
- DWORD iModeNum, // graphics mode
- LPDEVMODE lpDevMode, // graphics mode settings
- DWORD dwFlags // options);
- LONG ChangeDisplaySettingsEx( //改变屏幕分辨率和颜色质量
- LPCTSTR lpszDeviceName, // name of display device
- LPDEVMODE lpDevMode, // graphics mode
- HWND hwnd, // not used; must be NULL
- DWORD dwflags, // graphics mode options
- LPVOID lParam // video parameters (or NULL)
- );
- int GetDeviceCaps( //获取想要的设备信息,包括dpi等
- HDC hdc, // handle to DC
- int nIndex // index of capability
- );
图像格式一般分为两种:映射图像和向量图像,bitmap就是映射图像。metafile是向量图像。
位映射图像用离散的像素来处理输出设备;而向量图像用笛卡尔座标系统来处理输出设备,其线条和填充对象能被个别拖移。现在大多数的图像输出设备是位映射设备,这包括视讯显示、点阵打印机、激光打印机和喷墨打印机。而笔式绘图机则是向量输出设备。
编辑bitmap大小一般就是复制或删除像素的某些行和列的像素。
view plain
- //WINDOWS API:
- BitBlt (hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, ySrc, dwROP) ;//从称为「来源」的设备上下文中将一个矩形区的像素传输到称为「目的(destination)」的另一个设备上下文中相同大小的矩形区。来源和目的设备上下文可以相同而且bpp必须相同。、
- StretchBlt ( hdcDst, xDst, yDst, cxDst, cyDst,hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP) ;//在复制时拉伸或者压缩图像尺寸
- int GetDIBits( HDC hdc, // handle to DC
- HBITMAP hbmp, // handle to bitmap
- UINT uStartScan, // first scan line to set
- UINT cScanLines, // number of scan lines to copy
- LPVOID lpvBits, // array for bitmap bits
- LPBITMAPINFO lpbi, // bitmap data buffer
- UINT uUsage // RGB or palette index
- );
- //读取bitmap数据到缓冲区lpvBits。既按扫描顺序一个一个的像素数据。
- HDC CreateCompatibleDC(HDC hdc);//创建一个内存
- DCHBITMAP CreateCompatibleBitmap(
- HDC hdc, // handle to DC
- int nWidth, // width of bitmap,
- in pixels
- int nHeight // height of bitmap,
- in pixels
- );
- //创建设备环境兼容的位图。注意如果bitmap要做不同设备copy,hdc尽量不要用内存DC的。因为由CreateCompatibleBitmap函数创建的位图的颜色格式与由参数hdc标识的设备的颜色格式匹配。该位图可以选入任意一个与原设备兼容的内存设备环境中。由于内存设备环境允许彩色和单色两种位图。因此当指定的设备环境是内存设备环境时,由CreateCompatibleBitmap函数返回的位图格式不一定相同。然而为非内存设备环境创建的兼容位图通常拥有相同的颜色格式,并且使用与指定的设备环境一样的色彩调色板。
- HBITMAP CreateDIBSection( HDC hdc, // handle to DC
- CONST BITMAPINFO *pbmi, // bitmap data
- UINT iUsage, // data type indicator
- VOID **ppvBits, // bit values
- HANDLE hSection, // handle to file mapping object
- DWORD dwOffset // offset to bitmap bit values
- );
- //可以得到创建的图像在内存中的首地址,可以直接访问图像内存地址了,这样就不要用GetDIBits来获取数据了GdiFlush();
- //让应用程序进入等待状态,直到所有待决的绘图操作完成为止。阻塞等屏幕绘画完。通过成批合并绘图操作命令,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- struct _BMInfo BOOL truecolour;BITMAPINFO bmi;// Colormap info - comes straight after BITMAPINFO - **HACK**RGBQUAD cmap[256]; m_bminfo;
view plain
- BOOLvncDesktop::SetPixShifts()
- // Sort out the colour shifts, etc.
- DWORD redMask=0, blueMask=0, greenMask = 0;
- switch (m_bminfo.bmi.bmiHeader.biBitCount)
- case 16:
- // Standard 16-bit display
- if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)
- //需要自己来指定三色各占哪几位,把该色的所占位置1其它位置0,保存该值(暂时叫色标吧)。
- //biBitCount位16,对于blue颜色来说0-4这5个位置1,得到blue色标blueMask = 0x001f,当然这里作了强制转换,因为blue为无符号32位
- //如果一个无符号16位的像素数据uspixel,先要强转为无符号32位的uipixel,当然不转也无所谓的,因为对应的色标也必定从无符号16位强转获取的。
- //我们要获得blue的索引值只要uipixel&blueMask就OK拉
- //当然red的索引值还是需要移位下(uipixel&redMask)>>10
- // each word single pixel 5-5-5
- redMask = 0x7c00;
- greenMask = 0x03e0;
- blueMask = 0x001f;
- else
- if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)//通过读取RGBQUAD来获取色标
- redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
- greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
- blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
- break;
- case 32:// Standard 24/32 bit displays
- if (m_bminfo.bmi.bmiHeader.biCompression == BI_RGB)//同上,区别是每个颜色占了8位拉 each word single pixel 8-8-8
- redMask = 0xff0000;
- greenMask = 0xff00;
- blueMask = 0x00ff;
- else
- if (m_bminfo.bmi.bmiHeader.biCompression == BI_BITFIELDS)
- //同上
- redMask = *(DWORD *) &m_bminfo.bmi.bmiColors[0];
- greenMask = *(DWORD *) &m_bminfo.bmi.bmiColors[1];
- blueMask = *(DWORD *) &m_bminfo.bmi.bmiColors[2];
- break;
- default:// Other pixel formats are only valid if they're palette-based
- if (m_bminfo.truecolour)
- vnclog.Print(LL_INTERR, "unsupported truecolour pixel format for setpixshifts/n");
- return FALSE;
- return TRUE;
- // Convert the data we just retrieved
- MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);
- MaskToMaxAndShift(greenMask, m_scrinfo.format.greenMax, m_scrinfo.format.greenShift);
- MaskToMaxAndShift(blueMask, m_scrinfo.format.blueMax, m_scrinfo.format.blueShift);
- return TRUE;
看看函数最后调用的inline void vncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)函数:
view plain- inline voidvncDesktop::MaskToMaxAndShift(DWORD mask, CARD16 &max, CARD8 &shift)
- for (shift = 0; (mask & 1) == 0; shift++)
- mask >>= 1;//无符号32位的mask右移操作,直到碰到位的值位1则退出。那么shift保留了mask从低位到高位数值连续为0的个数。
- //把右移后的值牵制转换成无符号16位,赋值给max。
- max = (CARD16) mask;
- //结合调用MaskToMaxAndShift(redMask, m_scrinfo.format.redMax, m_scrinfo.format.redShift);来看
- //那么m_scrinfo.format.redMax保存的就是red的最大值,m_scrinfo.format.redShift是色标到最大值的位移值
- //假设一个无符号N(16或者32)位的像素数据upixel,
- //那么red的值就是( uspixel & (m_scrinfo.format.redMax<<m_scrinfo.format.redShift) )>>shift
下篇再看看桌面包装类vncDesktop的其它函数。
以上是关于WINVNC源码分析——图像的主要内容,如果未能解决你的问题,请参考以下文章