windows图形编程 学习杂谈 之 高效率窗口背景
来源:互联网 发布:南昌seo外包 编辑:程序博客网 时间:2024/05/16 23:42
刚开始学习windows下的图形编程,只会用API创建窗口和最简单的消息函数。
总想给窗口画个背景图片,那么就开始吧。编程只看不动手是不会提高的。
开始从网上找资料,主要看的是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;
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;
- };
- windows图形编程 学习杂谈 之 高效率窗口背景
- windows图形编程 学习杂谈 之 高效率窗口背景
- 《Windows核心编程》---图形用户界面之窗口
- 图形用户界面之窗口事件编程
- Windows网络编程杂谈
- Windows网络编程杂谈
- windows 编程杂谈 (小结)
- Windows图形编程(版本3):创建一个全屏窗口
- Windows编程里,怎样设置窗口背景透明
- 驱动编程学习杂谈
- Qt二维图形编程之窗口-视口概念理解
- Windows编程学习笔记 lesson2 窗口创建
- Windows编程学习笔记 lesson3 窗口创建
- Directx3D9学习之二:Windows编程之最简单窗口程序
- 【Python学习】之 简单图形编程
- Windows编程之窗口和MFC
- Windows编程之SDK窗口程序浅析
- Windows编程之MFC窗口程序浅析
- Node.js学习(14)----EJS模板引擎
- SOCKET端口复用
- ION-中传
- windows上svn图标不显示
- delphi 中文urlencode的简单办法
- windows图形编程 学习杂谈 之 高效率窗口背景
- 个人网站开发 (序)
- MySQL服务器的启动和停止
- 删除表中的重复记录
- 【希尔排序】
- 百钱百鸡--char3
- 如何去写 Android init.rc
- ios手势识别(双击、捏、旋转、拖动、划动、长按, 上下左右滑动)
- Linux shell脚本全面学习