GDI基本概念及思想
编写代码的一般步骤:先用CreateDC创建(或GetDC获取)device content(DC),然后用GetObject获取(或使用创建object的函数创建)需要的object,并用SelectObject将获取的object选入device content(DC),再使用object进行相应的画图操作,最后用SelectObject将原来的object重新选入DC,并delete或release删除或释放前面创建或获取的DC。
1. 关于device context (DC)设备内容:用来显示位图的地方.
四种设备内容:显示器设备内容、打印机设备内容、内存设备内容、Information设备内容,常用的有显示器DC和内存DC.
Device Context Types: There are four types of DCs: display, printer, memory (or compatible), and information.
Device context | Description |
Display | Supports drawing operations on a video display. |
Printer | Supports drawing operations on a printer or plotter. |
Memory | Supports drawing operations on a bitmap. |
Information | Supports the retrieval of device data. |
其中需要注意的一种类型是: Memory Device Contexts,将在bitmap处理中详细介绍。
创建和获取DC的相关函数:
CreateDC,CreateCompatibleDC,GetDC,GetCurrentDC.
其中CreateCompatibleDC用于创建内存设备内容.
刷新、释放和删除DC的相关函数:
ResetDC,ReleaseDC,DeleteDC.前者当DC有变动时用来重置DC,后者用来释放使用万完的DC.
DeleteDC与CreateDC对应使用
ReleaseDC与GetDC对应使用
2.关于graphical object(GDI objects):
包括:pen,brush,bitmap,palette,region,path.
获取、选择和删除object的相关函数:
GetObject,GetCurrentObject,SelectObject,DeleteObject。
关于SelectObject需要注意的地方:在使用完object后需要将原来默认的object重新选入DC替换掉新的object,bitmap只可以选入"内存DC",一个bitmap不能同时选入多个DC.
This function returns the previously selected object of the specified type. An application should always replace a new object with the original, default object after it has finished drawing with the new object. An application cannot select a bitmap into more than one DC at a time.
如果object是创建的,则使用完后必须删除创建的object,创建object的一些函数如下:
Graphic object | Function |
Bitmap | CreateBitmap, CreateBitmapIndirect,CreateCompatibleBitmap, CreateDiscardableBitmap,CreateDIBitmap |
Brush | CreateBrushIndirect, CreateDIBPatternBrush,CreateDIBPatternBrushPt, CreateHatchBrush,CreatePatternBrush, CreateSolidBrush |
Color Palette | CreatePalette |
Font | CreateFont, CreateFontIndirect |
Pen | CreatePen, CreatePenIndirect, ExtCreatePen |
Region | CreateEllipticRgn, CreateEllipticRgnIndirect,CreatePolygonRgn, CreatePolyPolygonRgn, CreateRectRgn,CreateRectRgnIndirect, CreateRoundRectRgn |
下面重点介绍GDI中关于bitmap的处理:
关于Bitmap和Device Context:
1.关于bitmap的一些基本概念:
DIB:设备无关位图。
DDB:设备相关位图。
扫描顺序scan order:bottom-up DIB和top-down DIB。
DDB总是top-down的.
2. 关于drawing surface:
可以理解为绘制图象的区域,程序调用CreateDC或GetDC后,系统在返回DC句柄(handle)前先将一个drawing surface(绘制区域)选入DC,再返回DC句柄,后面进行的绘制操作将限制在这个drawing surface(绘制区域)中。例如:如果使用CreateDC创建一个关于显示器的DC(device content),那么绘制区域为显示器的整个屏幕,而如果使用GetDC获取一个对话框窗口或客户区的DC,那么绘制区域则为对话框窗口或客户区。
To creates a DC, call the CreateDC function; to retrieve a DC, call the GetDCfunction. Before returning a handle that identifies that DC, the system selects a drawing surface into the DC. If the application called the CreateDC function to create a device context for a VGA display, the dimensions of this drawing surface are 640-by-480 pixels. If the application called the GetDC function, the dimensions reflect the size of the client area.
3.关于内存设备内容。
因为与某个具体的device content相关,所以又叫作compatible device context.可以象操作其它device content一样操作它,效果相同,唯一的区别是它不会显示出来,必须用BitBlt之类的函数将它的内容COPY(复制)到它相关的具体device content中才能显示出来。它的主要目的是用来暂存图象数据。
It is an array of bits in memory that an application can use temporarily to store the color data for bitmaps created on a normal drawing surface. Because the bitmap is compatible with the device, a memory DC is also sometimes referred to as a compatibledevice context.
使用CreateCompatibleDC创建Memory Device Contexts。
4.关于bitmap的创建。
一些关于bitmap创建的函数:
CreateBitmap, CreateBitmapIndirect两个基本相同,都创建DDB(device-
dependent bitmap),只能使用selectobject将创建的bitmap选入相关的(具有相同格式)device。
CreateCompatibleBitmap和CreateDIBitmap也都创建DDB,但前者指定了一个相关的device,而后者是从一个DIB创建DDB.
CreateDIBSection才真正创建DIB.
BitBlt | Performs a bit-block transfer. |
CreateBitmap | Creates a bitmap. |
CreateBitmapIndirect | Creates a bitmap. |
CreateCompatibleBitmap | Creates a bitmap compatible with a device. |
CreateDIBitmap | Creates a device-dependent bitmap (DDB) from a DIB. |
CreateDIBSection | Creates a DIB that applications can write to directly. |
LoadBitmap | Loads a bitmap from a module's executable file. |
StretchBlt | Copies a bitmap and stretches or compresses it. |
StretchDIBits | Copies the color data in a DIB. |
其中BitBlt和StretchBlt和StretchDIBvits用来真正显示bitmap,而
LoadBitmap则用来加载资源文件中的bitmap。
5.关于bitmap的存储。
The established bitmap file format consists of a BITMAPFILEHEADER structure followed by a BITMAPINFOHEADER, BITMAPV4HEADER, or BITMAPV5HEADER structure. An array ofRGBQUAD structures (also called a color table) follows the bitmap information header structure. The color table is followed by a second array of indexes into the color table (the actual bitmap data).
The bitmap file format is shown in the following illustration.
图一
可以看出bitmap由BITMAPFILEHEADER+BITMAPINFOHEADER(或其它bitmap header结构)+颜色表+指向颜色表的实际图象数据组成。
下面主要来看一看bitmap的四个组成部分:
一、关于BITMAPFILEHEADER结构:
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;
Members
bfType
指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。
Specifies the file type, must be BM.
bfSize
指定文件大小,包括这14个字节。单位:bytes。
Specifies the size, in bytes, of the bitmap file.
bfReserved1
Reserved; must be zero.
bfReserved2
Reserved; must be zero.
bfOffBits
为从文件头到实际的位图数据的偏移字节数,即图一中前三个部分的长度之和。
Specifies the offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits.
二、关于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;
Members
biSize
此结构(BITMAPINFOHEADER)的大小,单位:byte。
Specifies the number of bytes required by the structure.
biWidth
bitmap的宽,单位:pixel。
Specifies the width of the bitmap, in pixels.
biHeight
bitmap的高,单位:pixel。
Specifies the height of the bitmap, in pixels.
biPlanes
目标设备(即bitmap最终绘制的设备)平面的数目。一般为1,例如显示器和打印机都只有一个显示平面,对话框窗口和客户区也只有一个面,因此biPlanes的值必须设为1.
Specifies the number of planes for the target device. This value must be set to 1.
biBitCount
表示一个像素的颜色需要用到的位数,取决于bitmap的颜色数目(biBitCount 和bitmap颜色数目的关系:2^ biBitCount等于bitmap的颜色数目,很简单,因为二进制1 bit(位)只能表示2^1即两个值,2位可以表示2^2个值……)。例如,如果是黑白二色图,则表示一个像素的颜色只需要1位,即biBitCount为1,如果bitmap是16色的话,则每一个像素需要4位才能表示其颜色。
常用的值为1(黑白二色图), 4(16色图), 8(256色), 24(真彩色图)
单位:bits。
biCompression
指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS(都是一些Windows定义好的常量)。要说明的是,Windows位图可以采用RLE4和RLE8的压缩格式,但用的不多。一般bitmap为第一种不压缩的情况,即biCompression为BI_RGB的情况。
biSizeImage
指定实际的位图数据占用的字节数,如果biCompression为BI_RGB,则该项可能为零。因为BI_RGB为不压缩,所以biSizeImage其实也可以从以下的公式中计算出来:
biSizeImage=biWidth’ × biHeight
要注意的是:上述公式中的biWidth’必须是4的整倍数(所以不是biWidth,而是biWidth’,表示大于或等于biWidth的,最接近4的整倍数。举个例子,如果biWidth=240,则biWidth’=240;如果biWidth=241,biWidth’=244)。如果biCompression为压缩值,则上面的等式不成立。
单位:bytes。
biXPelsPerMeter
指定目标设备的水平分辨率,单位是每米的象素个数。
biYPelsPerMeter
指定目标设备的垂直分辨率,单位同上。
biClrUsed
指定本图象实际用到的颜色数,即颜色表(或调色板)中颜色的数目。如果该值为零,则用到的颜色数为2biBitCount。
biClrImportant
指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。
三、关于颜色表(即调色板palette)
The RGBQUAD structure describes a color consisting of relative intensities of red, green, and blue.
RGBQUAD结构用来表示颜色,bitmap结构的第三部分是一个RGBQUAD数组,数组中元素的数目就是bitmap的颜色数,也就是说数组中每一个元素(每一个RGBQUAD)代表一种颜色。RGBQUAD结构的大小为4字节,所以bitmap第三部分调色板的大小等于bitmap的颜色数目乘以4.举个例子:如果是黑白二色图,则
Bitmap的调色板大小为8字节;如果是16色图,则bitmap的调色板大小为64字节。
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
Members
rgbBlue
此颜色的蓝色分量,取值范围0~255,刚好可以用一个BYTE表示,因此类型为BYTE.
Specifies the intensity of blue in the color.
rgbGreen
此颜色的绿色分量,同上。
Specifies the intensity of green in the color.
rgbRed
此颜色的红色分量,同上。
Specifies the intensity of red in the color.
rgbReserved
Reserved; must be zero.
注意:真彩色图(24位色图)没有第三部分,也就是说没有调色板。
四、关于实际位图数据(color-index array)部分:
The color-index array associates a color, in the form of an index to an RGBQUADstructure, with each pixel in a bitmap. Thus, the number of bits in the color-index array equals the number of pixels times the number of bits needed to index theRGBQUAD structures. For example, an 8x8 black-and-white bitmap has a color-index array of 8 * 8 * 1 = 64 bits, because one bit is needed to index two colors. The Redbrick.bmp, mentioned in About Bitmaps, is a 32x32 bitmap with 16 colors; its color-index array is 32 * 32 * 4 = 4096 bits because four bits index 16 colors.
位图中的每一个像素都是一个指向颜色表某一项的索引值,即实际的位图数据。颜色表即RGBQUAD数组中不同的项(或元素)代表不同的颜色,因此不同颜色的像素指向颜色表中不同的项,相同颜色的像素指向指向的项是相同的。
这一部分(即实际位图数据)的大小等于像素的数目乘以表示bitmap所有颜色(即颜色表中颜色的数目)所需要的位数(bits)。例如:一个32×32的bitmap,如果调色板中的颜色数为16,即16色图,因为表示16色需要4bits,那么该位图的实际数据大小=32×32×4 = 4096 bits = 512 bytes。
思考:
位图结构为什么要定义成这四个部分?这样有什么好处?BITMAPFILEHEADER和
BITMAPINFOHEADER各定义了bitmap的哪些方面?为什么要引入调色板?
下面的内容摘录自《数字图象处理编程入门》
我们知道,普通的显示器屏幕是由许许多多点构成的,我们称之为象素。显示时采用扫描的方法:电子枪每次从左到右扫描一行,为每个象素着色,然后从上到下这样扫描若干行,就扫过了一屏。为了防止闪烁,每秒要重复上述过程几十次。例如我们常说的屏幕分辨率为640×480,刷新频率为70Hz,意思是说每行要扫描640个象素,一共有480行,每秒重复扫描屏幕70次。
我们称这种显示器为位映象设备。所谓位映象,就是指一个二维的象素矩阵,而位图就是采用位映象方法显示和存储的图象。举个例子,图1.1是一幅普通的黑白位图,图1.2是被放大后的图,图中每个方格代表了一个象素。我们可以看到:整个骷髅就是由这样一些黑点和白点组成的。
那么,彩色图是怎么回事呢?
我们先来说说三元色RGB概念。
我们知道,自然界中的所有颜色都可以由红、绿、蓝(R,G,B)组合而成。有的颜色含有红色成分多一些,如深红;有的含有红色成分少一些,如浅红。针对含有红色成分的多少,可以分成0到255共256个等级,0级表示不含红色成分;255级表示含有100%的红色成分。同样,绿色和蓝色也被分成256级。这种分级概念称为量化。
这样,根据红、绿、蓝各种不同的组合我们就能表示出256×256×256,约1600万种颜色。这么多颜色对于我们人眼来说已经足够丰富了。
表1.1 常见颜色的RGB组合值
颜色 | R | G | B |
红 | 255 | 0 | 0 |
蓝 | 0 | 255 | 0 |
绿 | 0 | 0 | 255 |
黄 | 255 | 255 | 0 |
紫 | 255 | 0 | 255 |
青 | 0 | 255 | 255 |
白 | 255 | 255 | 255 |
黑 | 0 | 0 | 0 |
灰 | 128 | 128 | 128 |
你大概已经明白了,当一幅图中每个象素赋予不同的RGB值时,能呈现出五彩缤纷的颜色了,这样就形成了彩色图。的确是这样的,但实际上的做法还有些差别。
让我们来看看下面的例子。
有一个长宽各为200个象素,颜色数为16色的彩色图,每一个象素都用R、G、B三个分量表示。因为每个分量有256个级别,要用8位(bit),即一个字节(byte)来表示,所以每个象素需要用3个字节。整个图象要用200×200×3,约120k字节,可不是一个小数目呀!如果我们用下面的方法,就能省的多。
因为是一个16色图,也就是说这幅图中最多只有16种颜色,我们可以用一个表:表中的每一行记录一种颜色的R、G、B值。这样当我们表示一个象素的颜色时,只需要指出该颜色是在第几行,即该颜色在表中的索引值。举个例子,如果表的第0行为255,0,0(红色),那么当某个象素为红色时,只需要标明0即可。
让我们再来计算一下:16种状态可以用4位(bit)表示,所以一个象素要用半个字节。整个图象要用200×200×0.5,约20k字节,再加上表占用的字节为3×16=48字节.整个占用的字节数约为前面的1/6,省很多吧?
这张R、G、B的表,就是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(Look Up Table),似乎更确切一些。Windows位图中便用到了调色板技术。其实不光是Windows位图,许多图象文件格式如pcx、tif、gif等都用到了。所以很好地掌握调色板的概念是十分有用的。
有一种图,它的颜色数高达256×256×256种,也就是说包含我们上述提到的R、G、B颜色表示方法中所有的颜色,这种图叫做真彩色图(true color)。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用R、G、B三个分量字节表示,而不采用调色板技术。原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24位(因为总共有224种颜色,即调色板有224行),和直接用R,G,B三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个256×256×256×3个字节的大调色板。所以真彩色图直接用R、G、B三个分量表示,它又叫做24位色图。