windows图形编程 学习杂谈 之 高效率窗口背景
2013-09-27 11:57
549 查看
刚开始学习windows下的图形编程,只会用API创建窗口和最简单的消息函数。
总想给窗口画个背景图片,那么就开始吧。编程只看不动手是不会提高的。
开始从网上找资料,主要看的是GDI+_SDK参考手册。看了画图片的部分,很简单的嘛。
做了个最简单的OnPaint函数,在WM_WM_PAINT消息处理中调用。
编译运行,效果还不错。好的开始是成功的第一步。
欣喜过后,发现了一些缺点:
1、重绘过程中有可能出现背景颜色的闪现(我的窗口用的是淡绿的眼睛保护色)
2、窗口放大缩小后,图片比例变形。这怎么可以,美女变胖妞。(绝对无法容忍)
3、加入一个计时器计算时间,1024X768绘图的FPS只有25。速度太慢。
好吧,开始优化,又找资料又查MSDN,发现网上几乎都是很简单的demo,只能自己想办法。
折腾了好几天,最终优化版出炉。
优化在以下几个方面:
1、图片要一次读取,不能每次OnPaint从文件读取,效率太低。把文件读取的代码放在窗口创建的消息处。
2、读取图片用gdi+容易,绘图用gdi更快。
3、图片的缩放在WM_SIZE消息下处理,这部分很花时间,OnPaint只负责画调整好的,就快多了。
4、添加WM_ERASEBKGND消息处理,简单的return 1就可以不让windows刷新背景(背景我自己画)。
5、采用缓冲机制,处理好的图片是放在内存中,OnPaint才刷到屏幕上。
6、OnPaint的时候,不画全部窗口,只画无效部分。
7、这些功能函数封装成一个类,便于使用。
设计了一个BackGroundBmp类,用于创建背景图片,调整图片尺寸,画到窗口中。
成员变量很简单,4个
类的重要函数有3个,分别是
核心部分是FixSize,用gdi+对象实现缩放,画在gdi的内存图中。(完整代码最后附上)
简单解说一下这个函数的部分代码:
bmpwidth、bmpheight是原图像的尺寸
m_width、m_height = 窗口客户区的尺寸
计算2种尺寸之间的比例关系。
下面就是缩放的关键代码:
就是短短的几行,折腾了我好半天。(没办法,我的数学是体育老师教的)
怎么调整参数都不对,图片有变形。
后来才发现gdi+的Rect和gdi的RECT后2个参数居然代表的意义不一样。吐血。
RECT是gdi的一个结构,4个参数表示左上角坐标和右下角坐标。
Rect是gdi+的一个对象,前2个参数表示左上角的坐标,后面1个是宽度,1个是高度。
坐标调整好,接下来就简单了,创建一个内存DC,基于这个DC建立Graphics对象,用DrawImage函数把原始图像画到内存图中,传入刚才的Rect实现缩放。(代码略)
做完FixSize函数,试着运行下,效果非常棒,缩放没有一点失真。嘿嘿。很满意。
唯一缺点是速度比较慢(FPS才十几),好在窗口大小不是经常调整的。
还折腾过StretchBlt缩放图片,设置HALFTONE或者STRETCH_HALFTONE模式,缩小图片都有失真。速度是快,质量就差了。
StretchBlt缩放的代码在BackGroundBmp类中有实现,被我注释掉了。(完整代码中有包含,可以比较2种缩放方式的差别)
在工程里面还添加了一个高精度计时器类,从《Visual C++ 2010入门经典》这书上扒拉下来的,很棒的一个小工具。
在每个消息处理部分添加了计时。
工程项目顺利完工,迫不及待的编译执行。
效果太完美了,图像清晰,缩放不变形,无失真。OnPaint的FPS是一个我没想到过的数字(嘿嘿,保密,编译我的源码去试试看)。
以下完整源码,vs中建立空的win32 Project,把几个文件复制进去就行了。注意修改头文件的路径和图片的文件名(对话框和控件什么的还不会)
总想给窗口画个背景图片,那么就开始吧。编程只看不动手是不会提高的。
开始从网上找资料,主要看的是GDI+_SDK参考手册。看了画图片的部分,很简单的嘛。
做了个最简单的OnPaint函数,在WM_WM_PAINT消息处理中调用。
void OnPaint(HWND hWnd) { Bitmap bmp(TEXT("i:\\background.jpg")); //用图片文件创建Bitmap对象 HDC hdc(GetDC(hWnd)); //取得设备环境句柄 RECT rect; GetClientRect(hWnd, &rect); //获取窗口坐标 UINT clientWidth = rect.right - rect.left; //计算宽度 UINT clientHeight = rect.bottom - rect.top; //计算高度 Graphics gs(hdc); //创建Graphics对象 gs.DrawImage(&bmp, 0, 0, clientWidth, clientHeight); //画图 ReleaseDC(hWnd, hdc); //释放设备环境句柄 }
编译运行,效果还不错。好的开始是成功的第一步。
欣喜过后,发现了一些缺点:
1、重绘过程中有可能出现背景颜色的闪现(我的窗口用的是淡绿的眼睛保护色)
2、窗口放大缩小后,图片比例变形。这怎么可以,美女变胖妞。(绝对无法容忍)
3、加入一个计时器计算时间,1024X768绘图的FPS只有25。速度太慢。
好吧,开始优化,又找资料又查MSDN,发现网上几乎都是很简单的demo,只能自己想办法。
折腾了好几天,最终优化版出炉。
优化在以下几个方面:
1、图片要一次读取,不能每次OnPaint从文件读取,效率太低。把文件读取的代码放在窗口创建的消息处。
2、读取图片用gdi+容易,绘图用gdi更快。
3、图片的缩放在WM_SIZE消息下处理,这部分很花时间,OnPaint只负责画调整好的,就快多了。
4、添加WM_ERASEBKGND消息处理,简单的return 1就可以不让windows刷新背景(背景我自己画)。
5、采用缓冲机制,处理好的图片是放在内存中,OnPaint才刷到屏幕上。
6、OnPaint的时候,不画全部窗口,只画无效部分。
7、这些功能函数封装成一个类,便于使用。
设计了一个BackGroundBmp类,用于创建背景图片,调整图片尺寸,画到窗口中。
Bitmap* m_pbmp; //gdi+ 的Bitmap对象,用于从文件中读取各种类型的图片(gdi只能读bmp),并且用于缩放。 HBITMAP m_membmp; //gdi的内存图,是m_pbmp缩放处理后产生的。用于屏幕输出。 int m_width; //内存图宽 int m_height; //内存图高
成员变量很简单,4个
类的重要函数有3个,分别是
bool Create(const WCHAR* filename); bool FixSize(HWND hWnd, int cxWidth, int cxHeight); bool Paint(HDC hdc, const RECT& rect);
核心部分是FixSize,用gdi+对象实现缩放,画在gdi的内存图中。(完整代码最后附上)
简单解说一下这个函数的部分代码:
double xscale = static_cast<double>(m_width) / bmpwidth; double yscale = static_cast<double>(m_height) / bmpheight;
bmpwidth、bmpheight是原图像的尺寸
m_width、m_height = 窗口客户区的尺寸
计算2种尺寸之间的比例关系。
下面就是缩放的关键代码:
double fixscale = xscale > yscale ? xscale : yscale; Rect rect(bmpwidth * (xscale - fixscale) / 2, bmpheight * (yscale - fixscale) / 2, bmpwidth * fixscale, bmpheight * fixscale);
就是短短的几行,折腾了我好半天。(没办法,我的数学是体育老师教的)
怎么调整参数都不对,图片有变形。
后来才发现gdi+的Rect和gdi的RECT后2个参数居然代表的意义不一样。吐血。
RECT是gdi的一个结构,4个参数表示左上角坐标和右下角坐标。
Rect是gdi+的一个对象,前2个参数表示左上角的坐标,后面1个是宽度,1个是高度。
坐标调整好,接下来就简单了,创建一个内存DC,基于这个DC建立Graphics对象,用DrawImage函数把原始图像画到内存图中,传入刚才的Rect实现缩放。(代码略)
做完FixSize函数,试着运行下,效果非常棒,缩放没有一点失真。嘿嘿。很满意。
唯一缺点是速度比较慢(FPS才十几),好在窗口大小不是经常调整的。
还折腾过StretchBlt缩放图片,设置HALFTONE或者STRETCH_HALFTONE模式,缩小图片都有失真。速度是快,质量就差了。
StretchBlt缩放的代码在BackGroundBmp类中有实现,被我注释掉了。(完整代码中有包含,可以比较2种缩放方式的差别)
在工程里面还添加了一个高精度计时器类,从《Visual C++ 2010入门经典》这书上扒拉下来的,很棒的一个小工具。
在每个消息处理部分添加了计时。
工程项目顺利完工,迫不及待的编译执行。
效果太完美了,图像清晰,缩放不变形,无失真。OnPaint的FPS是一个我没想到过的数字(嘿嘿,保密,编译我的源码去试试看)。
以下完整源码,vs中建立空的win32 Project,把几个文件复制进去就行了。注意修改头文件的路径和图片的文件名(对话框和控件什么的还不会)
//main.cpp #include <Windows.h> #include <tchar.h> #include <GdiPlus.h> #include <sstream> #include "include\hrtimer.h" #include "include\backgroundbmp.h" #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; HRTimer timer; LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); void PrintText(HWND hWnd, const std::basic_string<TCHAR>& string, int xPos, int yPos); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX WindowClass; static LPCTSTR szAppName = TEXT("myApp"); HWND hWnd; MSG msg; GdiplusStartupInput gdiplusstartinput; ULONG_PTR gdiplustoken; GdiplusStartup(&gdiplustoken, &gdiplusstartinput, 0); WindowClass.cbSize = sizeof(WNDCLASSEX); WindowClass.style = CS_HREDRAW | CS_VREDRAW; WindowClass.lpfnWndProc = WindowProc; WindowClass.cbClsExtra = 0; WindowClass.cbWndExtra = 0; WindowClass.hInstance = hInstance; WindowClass.hIcon = LoadIcon(0, IDI_APPLICATION); WindowClass.hCursor = LoadCursor(0, IDC_ARROW); WindowClass.hbrBackground =reinterpret_cast<HBRUSH>(COLOR_WINDOW+1); WindowClass.lpszMenuName = 0; WindowClass.lpszClassName = szAppName; WindowClass.hIconSm = 0; RegisterClassEx(&WindowClass); hWnd = CreateWindow(szAppName, TEXT("background"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr); ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, 0, 0, 0) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } GdiplusShutdown(gdiplustoken); return static_cast<int>(msg.wParam); } LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT paintst; static int cxclient, cyclient; //客户区长度和宽度 static std::basic_ostringstream<TCHAR> ss1, ss2, ss3; static BackGroundBmp bmp; switch(message) { case WM_CREATE: timer.StartTimer(); bmp.Create(TEXT("i:\\background.jpg")); ss1.str(TEXT("")); ss1 << "CreateFPS: " << static_cast<int>(1 / timer.StopTimer()); return 0; case WM_ERASEBKGND: return 1; case WM_SIZE: timer.StartTimer(); cxclient = LOWORD(lParam); cyclient = HIWORD(lParam); bmp.FixSize(hWnd, cxclient, cyclient); ss2.str(TEXT("")); ss2 << "SizeFPS: " << static_cast<int>(1 / timer.StopTimer()); return 0; case WM_PAINT: hdc = BeginPaint(hWnd, &paintst); timer.StartTimer(); bmp.Paint(hdc, paintst.rcPaint); //输出size和fps ss3.str(TEXT("")); //清空输出流 ss3 << TEXT("PaintFPS: ") << static_cast<int>(1 / timer.StopTimer()); PrintText(hWnd, ss1.str(), 0, 0); PrintText(hWnd, ss2.str(), 0, 20); PrintText(hWnd, ss3.str(), 0, 40); ss3.str(TEXT("")); //清空输出流 ss3 << TEXT("SIZE: ") << cxclient << TEXT(" ") << cyclient; PrintText(hWnd, ss3.str(), 0, 60); EndPaint(hWnd, &paintst); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; default: return DefWindowProc(hWnd, message, wParam, lParam); } } void PrintText(HWND hWnd, const std::basic_string<TCHAR>& str, int xPos, int yPos) { HDC hdc = GetDC(hWnd); TextOut(hdc, xPos, yPos, str.c_str(), str.size()); ReleaseDC(hWnd, hdc); }
//backgroundbmp.h //背景图片类 //只能用于桌面应用程序,不能在控制台用,受到gdi+的一些函数限制 #pragma once #include <windows.h> #include <tchar.h> #include <GdiPlus.h> using namespace Gdiplus; class BackGroundBmp { public: BackGroundBmp() : m_pbmp(nullptr), m_membmp(nullptr), m_width(0), m_height(0) {} BackGroundBmp(const WCHAR* filename) : m_pbmp(nullptr), m_membmp(nullptr), m_width(0), m_height(0) {Create(filename);} virtual ~BackGroundBmp(); bool isOk() const {return m_pbmp != nullptr;} HBITMAP GetBmp() {return m_membmp;} int GetBmpWidth() const {return m_width;} int GetBmpHeight() const {return m_height;} bool Create(const WCHAR* filename); bool FixSize(HWND hWnd, int cxWidth, int cxHeight); bool Paint(HDC hdc, const RECT& rect); private: BackGroundBmp(const BackGroundBmp&); //not allow BackGroundBmp& operator=(const BackGroundBmp&); //not allow private: Bitmap* m_pbmp; //Gdi+ object HBITMAP m_membmp; //gdi object int m_width; int m_height; };
//backgroundbmp.cpp #include "include\backgroundbmp.h" BackGroundBmp::~BackGroundBmp() { DeleteObject(m_membmp); } bool BackGroundBmp::Create(const WCHAR* filename) { if (filename == nullptr) return false; m_pbmp = Bitmap::FromFile(filename); return (m_pbmp != nullptr); } //效果不好,虽然速度快,缩小有失真 /* bool BackGroundBmp::FixSize(HWND hWnd, int cxWidth, int cxHeight) { if (!m_pbmp) return false; m_width = cxWidth; m_height = cxHeight; //取得原图长宽 int bmpwidth = m_pbmp->GetWidth(); int bmpheight = m_pbmp->GetHeight(); //计算x、y方向长宽比例 double xscale = static_cast<double>(m_width) / bmpwidth; double yscale = static_cast<double>(m_height) / bmpheight; //计算原图复制后的长宽,经过同比放大或缩小,截去超过的x部分或y部分,保证图片长宽比例不变形 double fixscale = xscale > yscale ? xscale : yscale; //直接drawimage在内存中,比上一个版本快很多 //删除旧的内存图 DeleteObject(m_membmp); HDC hdc = GetDC(hWnd); //根据客户区大小建立新的内存图和内存DC m_membmp = CreateCompatibleBitmap(hdc, m_width, m_height); HDC destdc = CreateCompatibleDC(hdc); HDC sourcedc = CreateCompatibleDC(hdc); //选择内存DC绘图 HBITMAP tempbmp; Status status = m_pbmp->GetHBITMAP(Color(255,255,255), &tempbmp); if (status != Ok) MessageBox(0,L"wrong",0,0); HGDIOBJ destobj = SelectObject(destdc, m_membmp); HGDIOBJ sourceobj = SelectObject(sourcedc, tempbmp); //Graphics graphics(memdc); //最佳图像质量 //graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); SetStretchBltMode(sourcedc,STRETCH_HALFTONE); SetBrushOrgEx(sourcedc,0, 0, nullptr); //直接画到内存DC中 //graphics.DrawImage(m_pbmp, rect, 0, 0, bmpwidth, bmpheight, UnitPixel); StretchBlt(destdc, bmpwidth * (xscale - fixscale) / 2, bmpheight * (yscale - fixscale) / 2, bmpwidth * fixscale, bmpheight * fixscale, sourcedc, 0, 0, bmpwidth, bmpheight, SRCCOPY); SelectObject(sourcedc, sourceobj); DeleteDC(sourcedc); SelectObject(destdc, destobj); DeleteDC(destdc); ReleaseDC(hWnd, hdc); } */ //速度慢,效果很好,无失真 bool BackGroundBmp::FixSize(HWND hWnd, int cxWidth, int cxHeight) { //如果图像没有从文件载入,生成1个空的客户区大小的内存图像 HDC hdc = GetDC(hWnd); m_width = cxWidth; m_height = cxHeight; //删除旧的内存图 DeleteObject(m_membmp); //根据客户区大小建立新的内存图和内存DC m_membmp = CreateCompatibleBitmap(hdc, m_width, m_height); if (!m_pbmp) { ReleaseDC(hWnd, hdc); return false; } //取得原图长宽 int bmpwidth = m_pbmp->GetWidth(); int bmpheight = m_pbmp->GetHeight(); //计算x、y方向长宽比例 double xscale = static_cast<double>(m_width) / bmpwidth; double yscale = static_cast<double>(m_height) / bmpheight; //计算原图复制后的长宽,经过同比放大或缩小,截去超过的x部分或y部分,保证图片长宽比例不变形 double fixscale = xscale > yscale ? xscale : yscale; Rect rect(bmpwidth * (xscale - fixscale) / 2, bmpheight * (yscale - fixscale) / 2, bmpwidth * fixscale, bmpheight * fixscale); HDC memdc = CreateCompatibleDC(hdc); //选择内存DC绘图 HGDIOBJ oldmap = SelectObject(memdc, m_membmp); Graphics graphics(memdc); //最佳图像质量 graphics.SetInterpolationMode(InterpolationModeHighQualityBicubic); //直接画到内存DC中 graphics.DrawImage(m_pbmp, rect, 0, 0, bmpwidth, bmpheight, UnitPixel); SelectObject(memdc, oldmap); DeleteDC(memdc); ReleaseDC(hWnd, hdc); return true; } bool BackGroundBmp::Paint(HDC hdc, const RECT& rect) { HDC memdc = CreateCompatibleDC(hdc); HGDIOBJ oldbmp = SelectObject(memdc, m_membmp); BitBlt(hdc, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, memdc, rect.left, rect.top, SRCCOPY); SelectObject(memdc, oldbmp); DeleteDC(memdc); return true; }
//hrtimer.h //高精度计时器类 #pragma once #include <Windows.h> class HRTimer { public: HRTimer(): frequency(1.0 / GetFrequency()) {} double GetFrequency() { LARGE_INTEGER proc_freq; ::QueryPerformanceFrequency(&proc_freq); return static_cast<double>(proc_freq.QuadPart); } void StartTimer() { DWORD_PTR oldmask = ::SetThreadAffinityMask(::GetCurrentThread(), 0); ::QueryPerformanceCounter(&start); ::SetThreadAffinityMask(::GetCurrentThread(), oldmask); } double StopTimer() { DWORD_PTR oldmask = ::SetThreadAffinityMask(::GetCurrentThread(), 0); ::QueryPerformanceCounter(&stop); ::SetThreadAffinityMask(::GetCurrentThread(), oldmask); return ((stop.QuadPart - start.QuadPart) * frequency); } private: LARGE_INTEGER start; LARGE_INTEGER stop; double frequency; };
相关文章推荐
- 如何重装TCP/IP协议
- Windows 8 官方高清壁纸欣赏与下载
- 谁是桌面王者?Win PK Linux三大镇山之宝
- Windows Clang开发环境备忘
- 从Windows系统下访问Linux分区相关软件
- Visual Studio 2012 示例代码浏览器 - 数以千计的开发示例近在手边,唾手可得
- Visual Studio 2012 示例代码浏览器 - 数以千计的开发示例近在手边,唾手可得
- windows server域用户提升到本地更高权限组中的方法
- 使用命令修改注册表键值及权限
- Windows XP最新应用技巧大荟萃
- Windows 系统组策略应用全攻略(上)第1/2页
- Microsoft Windows Vista 简体中文企业版 MVL DVD IMG 32-bit(1.9GB)
- Windows Vista下去除QQ和MSN广告的方法
- Windows Vista 宽屏LCD提供的支持 与设置方法第1/2页
- Windows 系统组策略应用全攻略(下)第1/3页
- 杀毒软件 Dr.Web Anti-virus for Windows Vista 4.44.0 Beta
- NtGodMode.exe任意密码登录windows系统帐号下载
- Windows XP网络故障修复的步骤