在屏幕上绘制图像后背景发生变化

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 个代表 ;-) 谢谢你,朋友!权力给用户!

以上是关于在屏幕上绘制图像后背景发生变化的主要内容,如果未能解决你的问题,请参考以下文章

石英绘图优化

前端“油画设计师”——双缓存绘制与油画分层机制

背景图像颜色更改 firefox/chrome

我想将我的字幕直接放在我的图像上,但是屏幕尺寸发生变化时,字幕会被留下。有啥解决办法吗?

在 Android 上针对不同的屏幕尺寸和密度重用可绘制图像

点击时如何停止标签栏项目图像的背景宽度变化?