GDI C++ (1)

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

CreateBitmapCreateBitmapIndirect,CreateCompatibleBitmapCreateDiscardableBitmap,CreateDIBitmap

Brush

CreateBrushIndirectCreateDIBPatternBrush,CreateDIBPatternBrushPtCreateHatchBrush,CreatePatternBrushCreateSolidBrush

Color Palette

CreatePalette

Font

CreateFontCreateFontIndirect

Pen

CreatePenCreatePenIndirectExtCreatePen

Region

CreateEllipticRgnCreateEllipticRgnIndirect,CreatePolygonRgnCreatePolyPolygonRgnCreateRectRgn,CreateRectRgnIndirectCreateRoundRectRgn

 

 

下面重点介绍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创建的函数:

CreateBitmapCreateBitmapIndirect两个基本相同,都创建DDB(device-

dependent bitmap),只能使用selectobject将创建的bitmap选入相关的(具有相同格式)device。

CreateCompatibleBitmapCreateDIBitmap也都创建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 BITMAPINFOHEADERBITMAPV4HEADER, 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概念。

我们知道,自然界中的所有颜色都可以由红、绿、蓝(RGB)组合而成。有的颜色含有红色成分多一些,如深红;有的含有红色成分少一些,如浅红。针对含有红色成分的多少,可以分成0255256个等级,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色的彩色图,每一个象素都用RGB三个分量表示。因为每个分量有256个级别,要用8(bit),即一个字节(byte)来表示,所以每个象素需要用3个字节。整个图象要用200×200×3,约120k字节,可不是一个小数目呀!如果我们用下面的方法,就能省的多。

因为是一个16色图,也就是说这幅图中最多只有16种颜色,我们可以用一个表:表中的每一行记录一种颜色的RGB值。这样当我们表示一个象素的颜色时,只需要指出该颜色是在第几行,即该颜色在表中的索引值。举个例子,如果表的第0行为25500(红色),那么当某个象素为红色时,只需要标明0即可。

让我们再来计算一下:16种状态可以用4(bit)表示,所以一个象素要用半个字节。整个图象要用200×200×0.5,约20k字节,再加上表占用的字节为3×16=48字节.整个占用的字节数约为前面的1/6,省很多吧?

这张RGB的表,就是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(Look Up Table),似乎更确切一些。Windows位图中便用到了调色板技术。其实不光是Windows位图,许多图象文件格式如pcxtifgif等都用到了。所以很好地掌握调色板的概念是十分有用的。

有一种图,它的颜色数高达256×256×256种,也就是说包含我们上述提到的RGB颜色表示方法中所有的颜色,这种图叫做真彩色图(true color)。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用RGB三个分量字节表示,而不采用调色板技术。原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24(因为总共有224种颜色,即调色板有224),和直接用RGB三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个256×256×256×3个字节的大调色板。所以真彩色图直接用RGB三个分量表示,它又叫做24位色图。

 

posted @ 2012-02-09 08:57  bigbigtree  阅读(1264)  评论(0编辑  收藏  举报