C/C++ GDI绘制位图(可替代图片框组件) 以及 图片失真 解决方法

位图图像(Bitmap),亦称为点阵图像或绘制图像,是由称作像素(图片元素)的单个

点组成的。BMP文件的图像深度(用多少个二进制位来表示每个点的颜色)可选1bit、4bit、8bit及24bit,

BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。

Windows下绘制位图使用双缓冲技术,直接将位图选到内存兼容DC中,然后一次显示到窗口DC上。

位图绘制步骤

第一步:

使用CreateCompatibleDC创建一个内存兼容DC,但这个DC尺寸很小,通常是1*1像素大小,不能直接绘制。

第二步:

使用LoadlmageLoadBitmap加载现有位图,使用SelectObject将位图选入到兼容DC,这时位图尺寸大小就相当于了内存设备环境尺寸大小。

第三步:

使用BitBltStretchBlt将兼容DC一次性显示到窗口DC上。

步骤详细说明

绘制位图跟双缓冲绘制技术原理差不多,

所以,第一步,和第三步的函数介绍请参考:http://www.qingzhouquanzi.com/292.html

只不过 那个是创建位图,而这个是加载现有的位图,可以先看 那一篇再看这个 更容易理解

第二步加载位图函数说明:

HANDLE WINAPl LoadImage (

HINSTANCE    hinst,//模块句柄,如果要加载独立资源,如c: \ my.bmp,那么此参数设为NULL

LPCTSTR    lpszName,//图片路径或MAKEINTRESOURCE(iDB_XXX)宏产生的字符串

UINT    uType,//IMAGE_BITMAP位图、IMAGE_CURSOR光标、IMAGE_ICON图标

int    cxDesired,//图标或光标的宽度、如果此参数为零并且未使用LR_DEFAULTSIZE,

                                则该函数将使用实际的资源高度。

int    cyDesired,    //图标或光标的高度

UINT    fuLoad    //标识

) ;

fuLoad 可以是以下值 

LR_CREATEDIBSECTION    

当uType参数指定IMAGE_BITMAP时,会导致函数返回DIB段位图而不是兼容的位图。该标志对于加载位图而不将其映射到显示设备的颜色很有用。

LR_DEFAULTCOLOR    

默认标志;它什么也没做。它的意思是“不是LR_MONOCHROME”。

LR_DEFAULTSIZE    

如果cxDesired或cyDesired值设置为零,则使用游标或图标的系统度量值指定的宽度或高度。

如果未指定此标志且cxDesired和cyDesired设置为零,则该函数将使用实际的资源大小。如果资源包含多个图像,则该函数使用第一个图像的大小。

LR_LOADFROMFILE    (一般这个用的比较多)

从由lpszName(图标,光标或位图文件)指定的文件加载独立映像。

返回值:

如果函数成功,则返回值是新加载的图像的句柄。

如果函数失败,返回值为NULL。要获得扩展的错误信息,请调用GetLastError。

备注:

使用完毕后调用DeleteObject释放位图,DestroyCursor释放光标,Destroylcon释放图标

代码例子

case WM_PAINT:
{
	PAINTSTRUCT ps;//绘图结构体
	HDC hdc=BeginPaint(hwnd, &ps);
	

	//第一步:=创建一个内存兼容DC,此时它的大小是1*1,很小,不能绘制
	HDC hMenDC = CreateCompatibleDC(hdc);


	//第二步:加载现有位图
	HBITMAP hMenBmp = (HBITMAP)LoadImage(NULL,
		TEXT("D:/c++练习/精通Windows API高级编程_练习/58GDI绘图 加载图片 显示到窗口上(可对图片进行绘制)/1.bmp"),
		IMAGE_BITMAP,
		0, 0, LR_LOADFROMFILE);
	//并且把兼容位图选到兼容内存DC中,此时DC的大小与位图一致,可以绘制
	SelectObject(hMenDC, hMenBmp); 


	//第三步:在内存兼容DC上绘制各种图形
	//SetTextColor(hMenDC,RGB(255, 0, 0));
	//TextOut(hMenDC, 0, 0, TEXT("这是一个简单的绘图例子"), lstrlen(TEXT("这是一个简单的绘图例子")));
	//Rectangle(hMenDC, 30, 30, 80, 80);

	//第四步 把内存中的图像一次性画到窗口DC上

	//不拉伸
	BitBlt(hdc, 0, 0, 100, 100, hMenDC, 0, 0,SRCCOPY);
	//拉伸
	StretchBlt(hdc,0, 100, 200, 200, hMenDC, 0, 0, 800, 800, SRCCOPY);
	//缩放
	StretchBlt(hdc, 300, 0, 50, 50, hMenDC, 0, 0, 800, 800, SRCCOPY);


	//最后不要忘记释放资源
	DeleteDC(hMenDC);
	DeleteObject(hMenBmp);


	EndPaint(hwnd,&ps);
	break;
}

运行结果(此时图片失真!):

图片失真解决方法:

StretchBlt会碰到一些与点阵图放大缩小缩放相关的一些问题:

操作会造成产生的图像有些失真。

1、在扩展一个点阵图时,StretchBlt必须复制图素行或列。如果放大倍数不是原图的整数倍,那么此

2、当目标矩形比源矩形小的时候,必须要将一些点去掉,默认做法是将要去掉的点与旁边的点做AND运算,所以导致像素错误,引起失真。

那么 可以使用 SetStretchBltMode函数可以设置指定设备环境中的位图拉伸模式解决失真问题。

int SetStretchBltMode(

HDC    hdc,//设备上下文句柄

int    iStretchMode //位图伸展模式

);

iStretchMode  可选值:

BLACKONWHITE: 使用消除和现在的像素颜色值进行逻辑AND(与)操作运算。如果该位图是单色位图,那么该模式以牺牲白色像素为代价,保留黑色像素点。

COLORONCOLOR:删除像素。该模式删除所有消除的像素行,不保留其信息。

HALFTONE:将源矩形区中的像素映射到目标矩形区的像素块中,覆盖目标像素块的一般颜色与源像素的颜

色接近。在设置完HALFTONE拉伸模之后,应用程序必须调用SetBrushOrgEx函数来设置刷子的起始点。如果没有成功,那么会出现刷子没对准的情况。

STRETCH_ANDSCANS: 与BLACKONWHITE一样。

STRETCH_DELETESCANS: 与COLORONCOLOR一样。

STRETCH_HALFTONE:与HALFTONE相同。

STRETCH_ORSCANS: 与WHITEONBLACK相同。

更多模式请参考msdn

返回值:

如果函数成功,返回值为非零。

如果函数失败,返回值为零,调用GetLastError获得扩展的错误信息.

代码实例,在拉伸之前设置拉伸模式

//解决拉伸失真问题
SetStretchBltMode(hdc, COLORONCOLOR);

//不拉伸
BitBlt(hdc, 0, 0, 100, 100, hMenDC, 0, 0,SRCCOPY);
//拉伸
StretchBlt(hdc,0, 100, 200, 200, hMenDC, 0, 0, 800, 800, SRCCOPY);
//缩放
StretchBlt(hdc, 300, 0, 50, 50, hMenDC, 0, 0, 800, 800, SRCCOPY);

运行结果: