在屏幕上绘制图像后背景发生变化
Posted
技术标签:
【中文标题】在屏幕上绘制图像后背景发生变化【英文标题】:Background changes after images are drawn on the screen 【发布时间】:2013-11-19 13:53:22 【问题描述】:我们正在使用 C++ 中的 Visual Studios 2012 在课堂上学习 Windows 原始触摸事件。我让我的演示工作,它完全按照它的预期工作,即在您的指尖下方绘制圆圈以检测是否已引发触摸事件。但是经过一段时间后,如果您仍然在画圈的情况下按下屏幕,屏幕会变成蓝色!圆圈仍然显示,但它们也是蓝色的,带有黑色轮廓,我仍然可以移动它们。我给教授看了,他似乎不明白这就是我来这里的原因。谁能看看我的代码,让我知道它的原因是什么?
// GT_HelloWorldWin32.cpp
// compile with: /D_UNICODE /DUNICODE /DWIN32 /D_WINDOWS /c
#ifndef WINVER // Specifies that the minimum required platform is Windows 7.
#define WINVER 0x0601 // Change this to the appropriate value to target other versions of Windows.
#endif
#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows 7.
#define _WIN32_WINNT 0x0601 // Change this to the appropriate value to target other versions of Windows.
#endif
#include <windows.h> // for windows touch
#include <windowsx.h> // included for point conversion
#include <stdlib.h>
#include <string.h>
#include <tchar.h>
#include "wtypes.h"
#include <iostream>
using namespace std;
// The main window class name.
TCHAR szWindowClass[] = _T("win32app");
// The string that appears in the application's title bar.
TCHAR szTitle[] = _T("Hello World!");
//Instancing the handler
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Maximum ammount of touches allowed
#define MAXPOINTS 10
// You will use this array to track touch points
int points[MAXPOINTS][2];
// You will use this array to switch the color / track ids
int idLookup[MAXPOINTS];
// You can make the touch points larger
// by changing this radius value
static int radius = 30;
// There should be at least as many colors
// as there can be touch points so that you
// can have different colors for each point
COLORREF colors[] = RGB(153,255,51),
RGB(153,0,0),
RGB(0,153,0),
RGB(255,255,0),
RGB(255,51,204),
RGB(0,0,0),
RGB(0,153,0),
RGB(153, 255, 255),
RGB(153,153,255),
RGB(0,51,153)
;
int wmId, wmEvent, i, x, y, index;
UINT cInputs;
PTOUCHINPUT pInputs;
POINT ptInput;
// This function is used to return an index given an ID
int GetContactIndex(int dwID)
for (int i=0; i < MAXPOINTS; i++)
if (idLookup[i] == -1)
idLookup[i] = dwID;
return i;
else
if (idLookup[i] == dwID)
return i;
// Out of contacts
return -1;
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
WNDCLASSEX wcex;
int width = 0, height = 0; // Screen resolution
GetScreenResolution(width, height);
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
hInst = hInstance; // Store instance handle in our global variable
// The parameters to CreateWindow explained:
// szWindowClass: the name of the application
// szTitle: the text that appears in the title bar
// WS_OVERLAPPEDWINDOW: the type of window to create
// CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
// 500, 100: initial size (width, length)
// NULL: the parent of this window
// NULL: this application does not have a menu bar
// hInstance: the first parameter from WinMain
// NULL: not used in this application
HWND hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
800, 600,
NULL,
NULL,
hInstance,
NULL
);
if (!hWnd)
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("Win32 Guided Tour"),
NULL);
return 1;
// register the window for touch instead of gestures
RegisterTouchWindow(hWnd, 0);
// the following code initializes the points
for (int i=0; i< MAXPOINTS; i++)
points[i][0] = -1;
points[i][1] = -1;
idLookup[i] = -1;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
TranslateMessage(&msg);
DispatchMessage(&msg);
return (int) msg.wParam;
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes touch messages for the main window.
//
// WM_TOUCH - handles WM_TOUCH messages in the application
// WM_DESTROY - post a quit message and return
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
// For double buffering
static HDC memDC = 0;
static HBITMAP hMemBmp = 0;
HBITMAP hOldBmp = 0;
//For drawing / fills
PAINTSTRUCT ps;
HDC hdc;
switch (message)
case WM_TOUCH:
//LOWORD(wParam) = number of touch points in this message
//HIWORD(wParam) = reserved for future use
//lParam = handle for use with GetTouchInputInfo
cInputs = LOWORD(wParam);
pInputs = new TOUCHINPUT[ cInputs ];
if(pInputs)
if( GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT)) )
for (int i=0; i < static_cast<INT>(cInputs); i++)
TOUCHINPUT ti = pInputs[i];
index = GetContactIndex(ti.dwID);
if(ti.dwID != 0 && index < MAXPOINTS )
//get screen corrdinates of touch
ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
//get coordinates relative to the top left of the application window
ScreenToClient(hWnd, &ptInput);
if(ti.dwFlags & TOUCHEVENTF_UP)
points[index][0] = -1;
points[index][1] = -1;
else
points[index][0] = ptInput.x;
points[index][1] = ptInput.y;
CloseTouchInputHandle((HTOUCHINPUT)lParam);
delete [] pInputs;
InvalidateRect(hWnd, NULL, FALSE);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
RECT client;
GetClientRect(hWnd, &client);
//START DOUBLE BUFFERING
if (!memDC)
memDC = CreateCompatibleDC(hdc);
hMemBmp = CreateCompatibleBitmap(hdc, client.right, client.bottom);
hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);
FillRect(memDC, &client, CreateSolidBrush(RGB(255,255,255)));
//Draw Touched Points
for (i=0; i < MAXPOINTS; i++)
SelectObject( memDC, CreateSolidBrush(colors[i]));
x = points[i][0];
y = points[i][1];
if (x >0 && y>0)
Ellipse(memDC, x - radius, y - radius, x + radius, y + radius);
BitBlt(hdc, 0,0, client.right, client.bottom, memDC, 0,0, SRCCOPY);
EndPaint(hWnd, &ps);
ReleaseDC(hWnd, hdc);
//DeleteObject(hMemBmp);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
return 0;
【问题讨论】:
我注意到一件事 - 你不应该在EndPaint
之后调用 ReleaseDC(hWnd, hdc)
,你应该在某个地方调用 DeleteDC(memDC)
。
那么,只需像 EndPaint 然后 ReleaseDC(...) 那样重新排列顺序?
不,你根本不应该打电话给ReleaseDC
!这是EndPaint
将为您做的事情之一。
好吧,我刚刚评论了。但我的背景仍然变蓝。
【参考方案1】:
我现在看到了问题。您有 GDI 资源泄漏。
在两个地方你调用CreateSolidBrush
,但你永远不会删除你创建的画笔。你在这里做
FillRect(memDC, &client, CreateSolidBrush(RGB(255,255,255)));
这里
SelectObject( memDC, CreateSolidBrush(colors[i]));
确实,您应该将每个CreateSolidBrush
函数的结果分配给HBRUSH
,然后在完成后调用DeleteObject
。
您还需要释放您已注释掉的位图:DeleteObject(hMemBmp)
,并删除对 ReleaseDC
的调用,正如我在 cmets 中所说的那样。
通常,您应该仔细跟踪您创建的所有 GDI 对象,并确保在完成后删除它们。
【讨论】:
你太棒了,先生!有效。哇,我看到你有超过 7k 的代表,我希望有一天我能做到。非常感谢! @user3009036 不客气,那个 7K 代表只花了我 9 个月的时间,所以如果您继续在这里提出更多问题/回答,您也可以这样做!我赞成你的问题,再给你 5 个代表 ;-) 谢谢你,朋友!权力给用户!以上是关于在屏幕上绘制图像后背景发生变化的主要内容,如果未能解决你的问题,请参考以下文章
我想将我的字幕直接放在我的图像上,但是屏幕尺寸发生变化时,字幕会被留下。有啥解决办法吗?