第9章 GDI编程2—文字、位图与图标
与DOS等传统字符界面用点阵模板显示文字不同,GUI中的文字一般是利用轮廓字体中的控制点数据经计算后再绘制出来的。Windows中的文字,采用的是基于二次贝塞尔曲线的TrueType轮廓字体(字体文件为*.TTF)。MFC中的字体类为CFont,字体对象选入DC 后,再用于TextOut等函数的文字绘制输出。
位图是指由像素阵列组成的数字化点阵图像,是GUI的基本元素。位图可分成与设备相关和无关的两类,分别用于图像的显示和存储,对应的MFC类分别为CBitmap和CImage。
图标是一类大小固定的方型小位图,用于代表应用程序或文档子窗口。为了满足不同显示设备和不同使用目的的需要,在一个程序的图标资源中,一般包含多个不同大小和位数的图像。MFC为应用程序提供了默认的图标,可用资源编辑器对其进行修改。
9.1 输出文本
一般使用CDC类的TextOut等函数,采用DC中的当前字体和文本颜色等来输出文本。还可以用SetTextAlign函数来设置文本的对齐方式,用SetTextColor和SetBkColor函数来分别设置文本的前景和背景颜色。
9.1.1 文本输出函数
在CDC类中,封装了多种文本输出函数,它们都是使用当前的字体、文本颜色和背景颜色,在屏幕或打印机上输出文本(绘制文字)。
常用的文本输出函数有基本的TextOut、矩形区域的DrawText和可裁减和调字间距的扩展的ExtTextOut。
1.TextOut
TextOut的函数原型为:
virtual BOOL TextOut( int x, int y, LPCTSTR lpszString, int nCount );
BOOL TextOut( int x, int y, const CString& str );
其中,x与y为显示串的左上角坐标,lpszString或str为要显示的文本串,nCount为串lpszString
中的字符数。例如:
pDC->TextOut(10, 30, L"Test text");
2.DrawText
DrawText的函数原型为:
virtual int DrawText( LPCTSTR lpszString, int nCount, LPRECT lpRect, UINT nFormat );
int DrawText( const CString& str, LPRECT lpRect, UINT nFormat );
其中,lpszString或str为要显示的文本串,nCount为串lpszString中的字符数,lpRect为指定的显示区域,nFormat为显示格式,可取表9-1中值的位或“|”组合。
表9-1 文本显示格式nFormat的常用取值
符号常量含义
DT_BOTTOM 文本底对齐,必需与DT_SINGLELINE.同时使用
DT_CALCRECT 计算并调整矩形的宽与高
DT_CENTER 水平居中显示文本
DT_EXPANDTABS 扩展Tab字符(默认为8个字符宽)
DT_LEFT 居左显示文本
DT_NOCLIP 不剪裁(可提高显示速度)
DT_NOPREFIX 终止对表示加下划线的前缀字符&的处理
DT_RIGHT 居右显示文本
DT_SINGLELINE 单行显示文本,忽略回车和换行符
DT_TABSTOP 设置Tab的字符位数,该数放在nFormat的高位字节中
DT_TOP 文本顶对齐,必需与DT_SINGLELINE.同时使用
DT_VCENTER 垂直居中显示文本
DT_WORDBREAK 文本行超过矩形区域的宽时换行
3.ExtTextOut
ExtTextOut的函数原型为:
virtual BOOL ExtTextOut( int x, int y, UINT nOptions, LPCRECT lpRect,
LPCTSTR lpszString, UINT nCount, LPINT lpDxWidths );
BOOL ExtTextOut( int x, int y, UINT nOptions, LPCRECT lpRect,
const CString& str, LPINT lpDxWidths );
其中,nOptions可为ETO_CLIPPED(裁剪)/ ETO_OPAQUE(不透明),lpRect为裁剪矩形(可为NULL),lpDxWidths指定字符的间距(为NULL时使用默认间距)。
9.1.2 设置文本对齐
可用CDC类的SetTextAlign函数来设置TextOut和ExtTextOut输出文本时的对齐方式:
UINT SetTextAlign( UINT nFlags );
其中,nFlags可为表9-2中值的位或“|”组合,默认的文本对齐方式为“左顶”即“TA_LEFT | TA_TOP”。
例如(输出见图9-1):
RECT rect;
GetClientRect(&rect);
int w = rect.right, h = rect.bottom;
pDC->TextOut(10, 10, L"这是左上角"); // 默认为“TA_LEFT | TA_TOP”
pDC->SetTextAlign(TA_CENTER);
pDC->TextOut(w / 2, 10, L"这是顶部中央");
pDC->SetTextAlign(TA_RIGHT);
pDC->TextOut(w - 10, 10, L"这是右上角");
pDC->SetTextAlign(TA_BOTTOM);
pDC->TextOut(10, h - 10, L"这是左下角");
pDC->SetTextAlign(TA_BOTTOM | TA_RIGHT);
pDC->TextOut(w - 10, h - 10, L"这是右下角");
图9-1 文本对齐例的输出
9.1.3 设置文本颜色
在8.3.5中已经讲过,可使用CDC类的成员函数SetTextColor和SetBkColor来分别设置输出文本的前景色和背景色(默认的文本前景色为黑色、背景色为空):
virtual COLORREF SetTextColor( COLORREF crColor );
virtual COLORREF SetBkColor( COLORREF crColor );
例如:
pDC->SetTextColor( RGB(255, 0, 0) );
pDC->SetBkColor( RGB(0, 0, 128) );
9.2 字体
为了能使用不同的字体和大小来输出文本串,必须使用作为GDI对象的CFont类。Windows和常用的字处理软件(如Word)和绘图软件(如CorelDraw)等应用软件会提供多种与设备无关的字体,主要是TrueType类型的轮廓字体。
9.2.1 CFont类
CFont类是CGDIObject的派生类:CObject→CGDIObject→CFont。只有一个默认构造函数CFont( ); 必须用字体创建成员函数CreateFont[Indirect]或CreatePointFont [Indirect]来进行初始化。
1.字体创建函数
CFont类的字体创建成员函数的原型有:
BOOL CreateFont( int nHeight, int nWidth, int nEscapement, int nOrientation, int nWeight, BYTE bItalic, BYTE bUnderline, BYTE cStrikeOut, BYTE
nCharSet, BYTE nOutPrecision, BYTE nClipPrecision, BYTE nQuality,
BYTE nPitchAndFamily, LPCTSTR lpszFaceName );
BOOL CreateFontIndirect(const LOGFONT* lpLogFont );
BOOL CreatePointFont( int nPointSize, LPCTSTR lpszFaceName, CDC* pDC = NULL );
BOOL CreatePointFontIndirect( const LOGFONT* lpLogFont, CDC* pDC = NULL );
注意:CFont类的这些逻辑字体创建函数,并不是从无到有创建一个全新的GDI字体,而只是从Windows的物理字体库中选择一个与所设置参数最匹配的字体。
在这些字体创建函数中,CreateFont的参数有很多,与下面要讨论的逻辑字体结构LOGFONT中字段相对应。而CreatePointFont函数则提供了创建字体的一种简单方法,一般只需提供字体的大小和名称两个参数:
●nPointSize为字体大小,以0.1点(像素/墨点/磅数)为单位。汉字的字号与nPointSize
值及磅数的对应关系见表9-3和图9-2,图书和文献的正文一般为5号宋体字。另
外,若pDC非空,则系统会将设备单位点自动转换为pDC中的映射模式所指定的
逻辑单位。
●lpszFaceName为字体名称字符串的指针,常用的中文字体名有:宋体、仿宋
_GB2312、楷体_GB2312、黑体、隶书、幼圆等,常用的英文字体名有:Times New
Roman、Arial Narrow和Courier New等,参见图9-3。
图9-2 汉字的字号与磅数
图9-3 常用的中英文字体
例如:
CFont font; // 定义字体对象
font.CreatePointFont(105, L"宋体"); // 创建5号宋体字下面是输出如图9-2的不同大小字符串的OnDraw函数(可以创建一个采用传统界面的单文档MFC应用程序项目Font):
void CFontView::OnDraw(CDC* pDC)
{
CFont font, *pOldFont; // 定义字体对象和指针
int iSize, y = 0; // 定义字体大小变量和垂直坐标变量
// 定义对应于汉字字号的nPointSize值数组
int fs[16] = {420, 360, 260, 240, 220, 180, 160, 150, 140, 120, 105, 90, 75, 65, 55, 50};
wchar_t buf[40]; // 定义字符串缓冲区
// 定义字号与字体名称的字符串对象
CString sFontNum, sFontName = L"宋体";
for (int i = 0; i < 16; i++) { // 字号循环
iSize = fs[i]; // 获取当前字号的nPointSize值
font.CreatePointFont(iSize, sFontName); // 创建字体对象
pOldFont = pDC->SelectObject(&font); // 选入字体对象
sFontNum = GetFontNumber(iSize); // 获取当前字号
swprintf_s(buf, 40, L"这是%s%s字(%g磅)", sFontName,
sFontNum, iSize / 10.0); // 生成输出的字符串
pDC->TextOut(0, y, buf); // 输出字符串
pDC->SelectObject(pOldFont); // 选出字体
font.DeleteObject(); // 删除字体对象
y += fs[i] / 6; // 更新y值
}
}
其中用到了将字体大小iSize转换为中文字号串sFontNum的自定义函数GetFontNumber:CString CFontView::GetFontNumber(int size) {
switch(size) {
case 420: return L"初号";
case 360: return L"小初";
case 260: return L"一号";
case 240: return L"小一";
case 220: return L"二号";
case 180: return L"小二";
case 160: return L"三号";
case 150: return L"小三";
case 140: return L"四号";
case 120: return L"小四";
case 105: return L"五号";
case 90: return L"小五";
case 75: return L"六号";
case 65: return L"小六";
case 55: return L"七号";
case 50: return L"八号";
}
return L"未知";
}
还需要在视图类的头文件中,添加此函数的原型:
CString GetFontNumber(int size);
下面的代码段的输出对应于图9-3:
CFont font, *pOldFont;
CString fontNames[] = {L"宋体", L"仿宋_GB2312", L"楷体_GB2312",
L"黑体", L"隶书", L"幼圆", L"Times New Roman",
L"Arial Narrow", L"Courier New"};
int size = 180, x = 0, y = 0;
for (int i = 0; i < 9; i++) {
font.CreatePointFont(size, fontNames[i]);
pOldFont = pDC->SelectObject(&font);
pDC->TextOut(x, y, fontNames[i]);
pDC->SelectObject(pOldFont);
font.DeleteObject();
x += 200;
if ( (i + 1) % 3 == 0) { x = 0; y += size / 6;} }
2.逻辑字体结构
字体间接创建函数CreateFontIndirect和CreatePointFontIndirect中所使用的逻辑字体结构LOGFONT的定义为(wingdi.h):
typedef struct tagLOGFONT { // lf
LONG lfHeight; // 字体的逻辑高度
LONG lfWidth; // 字体平均宽度的逻辑值
LONG lfEscapement; // 字符串底线与水平线的夹角(单位为0.1度)
LONG lfOrientation; // 每个字符的底线与水平线的夹角(单位为0.1度)
LONG lfWeight; // 字体的粗细(磅数= 每1000点中所含的像素/墨点数),
BYTE lfItalic; // TRUE则字体为斜体字
BYTE lfUnderline; // TRUE则字体带下划线
BYTE lfStrikeOut; // TRUE则字体带删除线
BYTE lfCharSet; // 字符集
BYTE lfOutPrecision; // 输出字体的精度
BYTE lfClipPrecision; //输出时字体被裁剪的精度
BYTE lfQuality; // 输出质量
BYTE lfPitchAndFamily; // 字符间距与字体家族
WCHAR lfFaceName[LF_FACESIZE]; // 字体名称
} LOGFONTW;
typedef LOGFONTW LOGFONT;
下面是该结构中的各个参数的含义及取值:
●lfHeight
?>0时,高度被转化为设备单位,大小相对于字体的网格高度。
?=0时,使用合理的默认高度。
?<0时,高度被转化为设备单位,大小相对于字体的字符高度。
例如,对MM_TEXT映射模式,若字体的点尺寸为nPointSize,则可取
lfHeight = -MulDiv(nPointSize, pDC->GetDeviceCaps(LOGPIXELSY), 720);
其中,MulDiv将前两个32位整数相乘得一个64位整数,然后再除以第3个32位整数,而函数GetDeviceCaps可用来获得各种显示设备的信息,LOGPIXELSY指垂直方向上
每逻辑英寸所含的像素数。
●lfWidth字体平均宽度的逻辑值,为0时,会自动选择一个与高度最匹配的值。
●lfWeight的单位为千分比,取值范围为0(全白)~1000(全黑),0为默认粗细、400
为正常、700为粗体,可取表9-4中列出的符号常量。
●lfCharSet可取预定义的字符集符号常量,常用的有:
ANSI_CHARSET // = 0
CHINESEBIG5_CHARSET // = 136
DEFAULT_CHARSET // = 1
GB2312_CHARSET // = 134
CHINESE_GB2312 // = 134?
OEM_CHARSET // = 255
SYMBOL_CHARSET // = 2
●lfOutPrecision决定输出的字体与所设定的各种参数的匹配方式,可取下列符号常量值:
OUT_DEFAULT_PRECIS // = 0 默认精度
OUT_DEVICE_PRECIS // = 5 选择同名字体中的设备字体匹配
OUT_OUTLINE_PRECIS // = 8 选择轮廓字体匹配
OUT_RASTER_PRECIS // = 6 选择同名字体中的光栅字体匹配
OUT_STRING_PRECIS // = 1只在获取时返回:光栅字体
OUT_STROKE_PRECIS // = 3 Win95:选矢量字体;Win95/NT:只在获取时返
回:TrueType、轮廓或矢量字体
OUT_TT_ONL Y_PRECIS // = 7 只选择TrueTyope字体匹配,若没有则采用默认
OUT_TT_PRECIS // = 4 选择同名字体中的TrueTyope字体匹配
●lfClipPrecision决定如何裁剪超出剪裁区的字体,可取下列符号常量值:
CLIP_DEFAULT_PRECIS // = 0默认精度
CLIP_STROKE_PRECIS // = 2只在获取时返回:光栅、矢量或TrueType字体●lfQuality可取下列符号常量值:
CLIP_DEFAULT_PRECIS // = 0 默认质量
DRAFT_QUALITY // = 1草稿质量
PROOF_QUALITY // = 2 校样质量
●lfPitchAndFamily中的字符间距(pitch)占字节的低2位,可取下列符号常量值:
DEFAULT_PITCH // =0 默认间距
FIXED_PITCH // = 1 固定间距
V ARIABLE_PITCH // = 2 可变间距
字体家族(family) 占字节的4~7位,可取下列符号常量值:
FF_DECORATIVE // = 5<<4 装饰性新奇字体,如Old English
FF_DONTCARE // = 0<<4 不关心或不知道
FF_MODERN // = 3<<4等宽字体,有或无衬线,如Pica、Elite和CourierNew?
FF_ROMAN // = 1<<4 罗马字体,变宽、有衬线,如MS? Serif
FF_SCRIPT // = 4<<4 手迹字体,看上去向是手写体,如Script和Cursive
FF_SWISS // = 2<<4 瑞士字体,变宽、无衬线,如MS? Sans Serif 可以用位或“|”将间距常量与家族常量组合使用。
默认的系统字体的逻辑字体结构的数据成员取值为:
lfHeight 16
lfWidth 7
lfEscapement 0
lfOrientation 0
lfWeight 700 = FW_BOLD // 粗字体
lfItalic 0 = FALSE
lfUnderline 0 = FALSE
lfStrikeOut 0 = FALSE
lfCharSet 134 = CHINESE_GB2312
lfOutPrecision 1 = OUT_STRING_PRECIS // 光栅字体
lfClipPrecision 2 = CLIP_STROKE_PRECIS //光栅、矢量或TrueType字体
lfQuality 2 = PROOF_QUALITY // 校样质量
lfPitchAndFamily 0x22 = V ARIABLE_PITCH | FF_SWISS // 可变间距、变宽/无衬线字体lfFaceName "System" // 一般中文对应于“宋体”、西文对应于“Times New Roman”
3.获取逻辑字体结构
可先用CDC的成员函数GetCurrentFont得到当前DC中的字体对象,然后再用CFont
的成员函数GetLogFont来获取字体对象的逻辑字体结构:
CFont* GetCurrentFont( ) const;
int GetLogFont( LOGFONT * pLogFont );
例如:
CFont *pFont = pDC->GetCurrentFont( );
LOGFONT logFont;
pFont->GetLogFont(&logFont );
也可以先创建一个字体对象,再获取对应的逻辑字体结构。例如:
CFont font;
font.CreatePointFont(120, L"黑体"); // 创建小四号黑体字
LOGFONT logFont;
font.GetLogFont(&logFont);
9.2.2 字体公用对话框
为了方便用户选择各种字体参数,可使用如图9-4所示的字体公用对话框。
图9-4 字体公用对话框
可用该对话框获取字体的名称、大小、字型、效果、字符集等创建字体对象所需的参数值,还能获取设置字体前景色所需的颜色值,但是却不能得到输出字体的角度值。
使用字体公用对话框需要用到CFontDialog类。CFontDialog类的构造函数为CFontDialog( LPLOGFONT lplfInitial = NULL, DWORD dwFlags = CF_EFFECTS | CF_SCREENFONTS, CDC* pdcPrinter = NULL, CWnd* pParentWnd = NULL ); 其中,lplfInitial为上面所讲的逻辑字体结构的指针,dwFlags为对话框的可选参数,CF_EFFECTS表示对话框中有删除线和下划线复选框与选择颜色的下拉式组合框,CF_SCREENFONTS则使得对话框中只列出系统支持的显示字体。
如果设置了CF_EFFECTS标志,还可以用字体公用对话框的公共数据成员m_cf结构的rgbColors域,来初始化(和获取)字体的颜色(不过只能为16种系统颜色之一,否则会显示为黑色)。例如:
fontDlg.m_cf.rgbColors = m_textColor;
CFontDialog类的常用成员函数有:
virtual int DoModal( ); // 显示对话框,返回IDOK或IDCANCEL
void GetCurrentFont( LPLOGFONT lplf ); // 返回当前逻辑字体结构到lplf
CString GetFaceName( ) const; // 返回字体名称串
CString GetStyleName( ) const; // 返回字体风格名称串
int GetSize( ) const; // 返回所选择的字体大小,以0.1点为单位
int GetWeight( ) const; // 返回字体的磅数
COLORREF GetColor( ) const; // 返回所选择的字体颜色
BOOL IsStrikeOut( ) const; // 若选中了删除线,则返回TRUE
BOOL IsUnderline( ) const; // 若选中了下划线,则返回TRUE
BOOL IsBold( ) const; // 若选中了粗体或粗斜体,则返回TRUE
BOOL IsItalic( ) const; // 若选中了斜体或粗斜体,则返回TRUE
9.2.3 字体的选择与使用
若只是按名称和大小来创建字体,可直接使用CFont类的成员函数CreatePointFont。若还要设置字体粗细、下划线、斜体和方向等,则需要自己编写一个函数SetMyFont来使用字
体创建函数CreateFont,很多参数都可以用默认值,例如:
在视图类的头文件中定义若干类变量:
CFont m_font;
LOGFONT m_logFont;
wchar_t m_fontName[LF_FACESIZE];
int m_iFontSize;
COLORREF m_textColor;
BOOL m_bItalic, m_bBold, m_bUnderline, m_bStrikeOut;
... ...
注意,m_fontName不能被定义成空指针(LPCTSTR m_fontName;),并直接使用它。
在构造函数中初始化:
CFontView::CFontView() {
... ...
wcscpy_s(m_fontName, LF_FACESIZE, L"宋体");
m_iFontSize = 120;
m_font.CreatePointFont(m_iFontSize, m_fontName);
m_font.GetLogFont(&m_logFont);
m_textColor = RGB(0, 0, 0);
m_bItalic = false;
m_bBold = false;
m_bUnderline = false;
m_bStrikeOut = false;
... ...
}
下面是自定义函数SetMyFont的原型:
BOOL SetMyFont(CDC *pDC, LPCTSTR faceName, int size, COLORREF col = RGB(0, 0, 0), int angle = 0,
BOOL italic = FALSE, BOOL bold = FALSE,
BOOL underline = FALSE, BOOL strikeOut = FALSE);
下面是SetMyFont函数的函数体:
BOOL CFontView::SetMyFont(CDC *pDC, LPCTSTR name, int size,
COLORREF col, int angle, BOOL italic, BOOL bold,
BOOL underline, BOOL strikeOut) {
int height = -MulDiv(size, pDC->GetDeviceCaps(LOGPIXELSY), 720);
CFont font; // m_font.DeleteObject(); 并用m_font代替font:
BOOL bOk = font.CreateFont(height, 0, angle, angle,
bold?700:400, italic, underline, strikeOut,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_PITCH | FF_DONTCARE, name);
if (bOk) {
font.GetLogFont(&m_logFont);
pDC->SelectObject(font);
pDC->SetTextColor(col);
}
return bOk;
}
可以在自己定义的菜单项ID_GET_FONT的事件处理函数OnGetFont中打开字体公用
对话框,获得字体参数:
void CFontView::OnGetFont() {
CFontDialog fontDlg(&m_logFont);
fontDlg.m_cf.rgbColors = m_textColor;
if (fontDlg.DoModal() == IDOK) {
wcscpy_s(m_fontName, LF_FACESIZE, fontDlg.GetFaceName());
m_iFontSize = fontDlg.GetSize();
m_textColor = fontDlg.GetColor();
m_bItalic = fontDlg.IsItalic();
m_bBold = fontDlg.IsBold();
m_bStrikeOut = fontDlg.IsStrikeOut();
m_bUnderline = fontDlg.IsUnderline();
}
RedrawWindow(); // 重绘窗口(请求调用OnDraw)
}
注意,不能让m_fontName = fontDlg.GetFaceName();,因为m_fontName 为宽字符数组,而GetFaceName函数返回的是CString对象。
在Ondraw函数中设置字体并输出测试字符串:
void CFontView::OnDraw(CDC* pDC)
{
CFontDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc) return;
// TODO: 在此处为本机数据添加绘制代码
SetMyFont(pDC, m_fontName, m_iFontSize, m_textColor, 0, m_bItalic, m_bBold, m_bUnderline, m_bStrikeOut);
pDC->TextOut(20, 20, L"测试字符串");
}
图9-5为创建并使用字体的一般过程框图(粗体为核心步骤)。
图9-5 创建和使用字体的步骤
其中,粗体为核心步骤、点划线框部分对应于OnGetFont函数、虚线框部分对应于SetMyFont函数下面是一个利用SetMyFont函数来创建字体,并输出旋转文本串的例子。其中,OnTextRotation为菜单项ID_TEXT_ROTA TION的事件处理函数,angle为字符底线与水平线夹角的度数(注意,字体方向参数的单位为0.1度)。
void CFontView::OnTextRotation() {
CDC *pDC = GetDC();
RECT rect;
GetClientRect(&rect);
CString sFontNum;
wchar_t buf[100];
pDC->FillSolidRect(&rect, RGB(255, 255, 255));
for (int angle = 0; angle <= 360; angle += 10) {
SetMyFont(pDC, m_fontName, m_iFontSize, m_textColor, angle * 10, m_bItalic, m_bBold, m_bUnderline,
m_bStrikeOut);
sFontNum = GetFontNumber(m_iFontSize);
swprintf_s(buf,100, L"字体:名称=%s,字号=%s,大小=%g磅",
m_fontName, sFontNum, m_iFontSize / 10.0);
pDC->TextOut(rect.right / 2, rect.bottom / 2, buf);
}
ReleaseDC(pDC);
}
输出结果如图9-6所示。
9.3 位图
位图(bitmap)就是点阵图或光栅
图,由像素阵列组成。鼠标的光标、工
具条上的按钮、菜单项左边的图形标
识、代表程序的图标、窗口标题条上的图9-6 旋转文本输出
最小化、最大化和关闭图案等都是典型的位图。位图可分成设备相关位图DDB和设备无关
位图DIB,分别用于图像的显示和存储,对应的MFC类分别为CBitmap和CImage,可利
用这两个类各自的Attach函数进行相互转换。
9.3.1 颜色深度
位图的每个像素用若干二进制位表示,二进制位的个数称为位图的颜色深度。常见的颜
色深度及所能表示的颜色数目见表9-5。
表9-5 颜色(位)数与颜色位图类型
颜色位数颜色数颜色/位图类型
1 21 = 2
2 22 = 4
索引色/伪彩图
4 24 = 16
8 28 = 256 索引色/伪彩图或直接色/灰度图
15 215 = 32, 768
直接色/真彩图(高彩图)
16 216 = 65, 536
24 224 = 16, 777, 216 直接色/真彩图(全彩图)
低于或等于8位(256色)的位图,都为索引色,即像素的值不是颜色的基色强度,而
只是调色板中的颜色表项的索引值(序号)。而高于或等于15位(32, 768色)的位图,都
为直接色,即像素的值是颜色的各基色强度,不需要调色板。
9.3.2 DDB与DIB
设备相关位图DDB依赖于硬件的调色板,设备无关位图DIB自己带有颜色信息,它们分别用于图像的显示和存储。
1.DDB
DDB(Device Dependent Bitmap,设备相关位图)依赖于硬件的调色板,又叫GDI位图。
有保存DDB位图参数的结构类型BITMAP:
typedef struct tagBITMAP { // bm
LONG bmType; // 类型,必须=0
LONG bmWidth; // 宽度,像素个数,必须>0
LONG bmHeight; // 高度,像素个数,必须>0
LONG bmWidthBytes; // 每条扫描线(即每行像素)的字节数
// 必须能被2整除(即每行字对齐,不足的位补0)WORD bmPlanes; // 位面数= 颜色深度
WORD bmBitsPixel; // 颜色深度
LPVOID bmBits; // 指向存放位图数据的字符(字节)数组的指针} BITMAP;
2.DIB
DIB(Device Independent Bitmap,设备无关位图)带有自己的颜色信息,通常存储在BMP文件中,直接由Win32支持。DIB所包含的图像信息主要有:颜色格式、分辨率、调色板和压缩标志。有若干对应的结构来描述这些信息,如BITMAPFILEHEADER、BITMAPINFO、BITMAPINFOHEADER。
3.DDB与DIB
DDB与DIB是相关的,它们往往是同一幅图片的两种状态:DDB——显示(显存)、DIB——存储(文件)。
在MFC中DDB的功能被封装在CBitmap类中,而DIB的功能则被封装在CImage类中,可通过各自的Attach函数在它们二者之间进行相互转换。
9.3.3 CBitmap类
MFC将GDI中的DDB位图封装进CBitmap类,在该类中提供了对位图的操作。与CPen、CBrush、CFont等GDI对象一样,CBitmap也是CGdiObject的派生类,参见图8-6。
1.成员函数
表9-6 CBitmap类的成员函数
成员函数说明
构造函数CBitmap 构造CBitmap对象
初始化LoadBitmap 从资源装载位图
LoadOEMBitmap 装载预定义位图
LoadMappedBitmap 从资源装载位图,并将颜色映射到当前系统颜色CreateBitmap 创建具有指定高宽和图案的DDB内存位图CreateBitmapIndirect 由BITMAP结构创建位图CreateCompatibleBitmap 创建与指定DC兼容的位图CreateDiscardableBitmap 创建与指定DC兼容的可废弃位图
属性
GetBitmap 获取位图的BITMAP结构
operator HBITMAP 将CBitmap对象自动转换成HBITMAP指针的操作符
操作FromHandle 由位图指针HBITMAP构造CBitmap对象的静态函数SetBitmapBits 将指定的缓冲区的位图数据复制到CBitmap中GetBitmapBits 将CBitmap中的位图数据复制到指定的缓冲区SetBitmapDimension 设置位图的高和宽(以0.1毫米为单位)GetBitmapDimension 获取位图的高和宽(以0.1毫米为单位)
其中,默认构造函数CBitmap( );只是简单地创建一个空的CBitmap对象,该对象必须用位
图装载或创建函数来初始化。其他常用的成员函数的原型有:
BOOL LoadBitmap( UINT nIDResource );
BOOL CreateBitmap( int nWidth, int nHeight, UINT nPlanes, UINT nBitcount,
const void* lpBits );
BOOL CreateCompatibleBitmap( CDC* pDC, int nWidth, int nHeight );
int GetBitmap( BITMAP* pBitMap );
operator HBITMAP( ) const;
static CBitmap* PASCAL FromHandle( HBITMAP hBitmap );
2.使用DDB位图
使用DDB的主要步骤包括用LoadBitmap函数装入位图资源及用BitBlt函数显示图片,DDB 还可以用于创建图案刷和读显位图文件。
1)装载位图资源——可以用CBitmap类的成员函数LoadBitmap来装载位图资源: