Why when I paint, and the mouse is moving in the window, the painting is not done at the same time everywhere? (WinAPI) - c++

After asking this question, I changed my code. It works, but when WM_PAINT paints the window and the cursor is moving in it, the painting is not done at the same time everywhere. Here you have a video to see it better. This is my WM_PAINT:
//TV is a struct with the dimensions of the window.
//BitmapBuffer is the bitmap containing the painting.
static int cont;
cont++;
HDC HDc;
PAINTSTRUCT ps;
HDc = BeginPaint(identifier, &ps);
Gdiplus::Graphics lienzo (HDc);
AlphaBlend(HDc, 0, 0, TV.width+4, TV.height+4, buffer, 0, 0, TV.width+4, TV.height+4, CopyInfo);
EndPaint(identifier, &ps);
Since the problem is when moving the mouse, maybe the WM_NCHITTEST message has something to do with it:
case WM_NCHITTEST:
POINTS point1 = MAKEPOINTS(lParam);
POINT point2;
point2.x = point1.x;
point2.y = point1.y;
ScreenToClient(hwnd, &point2);
if (PtInRegion(region, point2.x, point2.y))
{
if (inWindow == false) //inWindow is true when the cursor is in the window
{
inWindow = true;
TrackMouseEvent(&structure);
Repaint(); //this function repaint the buffer and after call invalidrect
}
return HTCLIENT;
}
else
{
if (inWindow == true)
{
inWindow = false;
Repaint();
}
return HTTRANSPARENT;
}
break;
Does anyone have an idea why this happens?

It's hard to see the problem, because your code doesn't define Repaint. However, you should be using InvalidateRect to tell Windows which area is being updated.
THAT SAID...
Windows is hard to program for updating images with out using a double buffering technique. This is when you draw to a memory (bitmap) then bitblt the bitmap to the screen.
You find this answer useful Reduce flicker with GDI+ and C++

Related

GDI in C not drawing successive objects

Attempting to create a basic drawing program using C and GDI in the Windows API. The idea is that the user clicks and drags to create a rectangle or any other shape that is selected. The size is specified by where the user clicks down and releases (just like any drawing program). I'm having an issue where the first shape that the user draws will be painted to the screen, however anything successive is not. I initially was not drawing the shapes in WM_PAINT, but I read on another thread that I need to do that, so I created a struct to store information about objects that have been painted and those are drawn each time the WM_PAINT message is received.
Here's my code for WM_PAINT
case WM_PAINT:
{
for (int i = 0; i < drawingcount; i++)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HPEN hMyPen = CreatePen(mydrawings[i].style, mydrawings[i].thick, RGB(0,255,0));
HPEN hOldPen = SelectObject(hdc, hMyPen);
if (mydrawings[i].shape == RECTANGLE)
{
Rectangle(hdc, mydrawings[i].start_x, mydrawings[i].start_y, mydrawings[i].end_x, mydrawings[i].end_y);
}
else if (mydrawings[i].shape == ELLIPSE)
{
Ellipse(hdc, mydrawings[i].start_x, mydrawings[i].start_y, mydrawings[i].end_x, mydrawings[i].end_y);
}
else if (mydrawings[i].shape == LINE)
{
MoveToEx(hdc, mydrawings[i].start_x, mydrawings[i].start_y, 0);
LineTo(hdc, mydrawings[i].end_x, mydrawings[i].end_y);
}
SelectObject(hdc, hOldPen);
DeleteObject(hMyPen);
//DeleteObject(hOldPen);
EndPaint(hwnd, &ps);
}
}
break;
And here for WM_LBUTTONUP (once user finishes drawing their shape)
case WM_LBUTTONUP:
{
yendclick = GET_Y_LPARAM(lParam);
xendclick = GET_X_LPARAM(lParam);
mydrawings[drawingcount].shape = selected_shape;
mydrawings[drawingcount].start_x = xclick;
mydrawings[drawingcount].start_y = yclick;
mydrawings[drawingcount].end_x = xendclick;
mydrawings[drawingcount].end_y = yendclick;
mydrawings[drawingcount].thick = thickness;
mydrawings[drawingcount].style = style;
drawingcount++;
SendMessage(hwnd, WM_PAINT, wParam, lParam);
}
Struct and enum declarations:
typedef enum myshape
{
RECTANGLE,
ELLIPSE,
LINE,
MAX_SHAPE
} shape;
typedef struct drawing
{
int start_x;
int start_y;
int end_x;
int end_y;
int thick;
int style;
shape shape;
} drawings;
Would very much appreciate any guidance with this!
Don't use PostMessage WM_PAINT, use InvalidateRect and perhaps UpdateWindow instead. See also here: why not to send WM_PAINT manually

Using Coordinate Spaces and Transformations to scroll and scale Enhanced Windows Metafile

INTRODUCTION:
I am building small app that displays .emf (Enhanced Windows Metafile) in a window.
If image can not fit inside window, scroll bars will be used to show the invisible parts.
Since I am dealing with metafiles, I am trying to add zooming as well.
RELEVANT INFORMATION:
I am playing an .emf file (from the disk) in memory device context (created with the CreateCompatibleDC API). Then I use BitBlt API to transfer that image into main window's client area. I am doing all this to avoid flickering.
Reading through the MSDN I found documentation about Using Coordinate Spaces and Transformations and immediately realized its potential for solving my task of scaling / scrolling the metafile.
PROBLEM:
I do not know how to use before mentioned APIs to scale / scroll metafile inside memory device context, so I can BitBlt that image into main window's device context (this is my first time tackling this type of task).
MY EFFORTS TO SOLVE THE PROBLEM:
I have experimented with XFORM matrix to achieve scaling, like this:
case WM_ERASEBKGND: // prevents flickering
return 1L;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
// get main window's client rectangle
RECT rc = { 0 };
GetClientRect(hWnd, &rc);
// fill rectangle with gray brush
// this is necessery because I have bypassed WM_ERASEBKGND,
// see above
FillRect(hdc, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
// OK, here is where I tried to tamper with the APIs
// I mentioned earlier
// my goal would be to scale EMF down by half
int prevGraphicsMode = SetGraphicsMode(hdc, GM_ADVANCED);
XFORM zoomMatrix = { 0 };
zoomMatrix.eDx = 0;
zoomMatrix.eDy = 0;
zoomMatrix.eM11 = 0.5;
zoomMatrix.eM12 = 0;
zoomMatrix.eM21 = 0;
zoomMatrix.eM22 = 0.5;
// apply zooming factor
SetWorldTransform(hdc, &zoomMatrix);
// draw image
HENHMETAFILE hemf = GetEnhMetaFile(L".\\Example.emf");
PlayEnhMetaFile(hdc, hemf, &rc);
DeleteEnhMetaFile(hemf);
// restore original graphics mode
SetGraphicsMode(hdc, prevGraphicsMode);
// all done, end painting
EndPaint(hWnd, &ps);
}
return 0L;
In the above snippet, metafile was scaled properly, and was played from the top left corner of the client area.
I didn't bother with maintaining aspect ratio nor centering the image. My main goal for now was to figure out how to use XFORM matrix to scale metafile.
So far so good, at least so I thought.
I tried doing the same as above for the memory device context, but when BitBliting the image I got horrible pixelation, and BitBlitted image was not properly scaled.
Below is the small snippet that reproduces the above image:
static HDC memDC; // in WndProc
static HBITMAP bmp, bmpOld; // // in WndProc; needed for proper cleanup
case WM_CREATE:
{
HDC hdc = GetDC(hwnd);
// create memory device context
memDC = CreateCompatibleDC(hdc);
// get main window's client rectangle
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
// create bitmap that we will draw on
bmp = CreateCompatibleBitmap(hdc, rc.right - rc.left, rc.bottom - rc.top);
// select bitmap into memory device context
bmpOld = (HBITMAP)SelectObject( memDC, bmp );
// fill rectangle with gray brush
FillRect(memDC, &rc, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
// scale EMF down by half
int prevGraphicsMode = SetGraphicsMode(memDC, GM_ADVANCED);
XFORM zoomMatrix = { 0 };
zoomMatrix.eDx = 0;
zoomMatrix.eDy = 0;
zoomMatrix.eM11 = 0.5;
zoomMatrix.eM12 = 0;
zoomMatrix.eM21 = 0;
zoomMatrix.eM22 = 0.5;
// apply zooming factor
SetWorldTransform(memDC, &zoomMatrix);
// draw image
HENHMETAFILE hemf = GetEnhMetaFile(L".\\Example.emf");
PlayEnhMetaFile(memDC, hemf, &rc);
DeleteEnhMetaFile(hemf);
// restore original graphics mode
SetGraphicsMode(memDC, prevGraphicsMode);
// all done end paint
ReleaseDC(hwnd, hdc);
}
return 0L;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = {0};
GetClientRect(hwnd, &rc);
BitBlt(hdc, 0, 0,
rc.right - rc.left,
rc.bottom - rc.top,
memDC, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
}
return 0L;
case WM_DESTROY:
SelectObject(memDC, bmpOld);
DeleteObject(bmp);
DeleteDC(memDC);
PostQuitMessage(0);
break;
After rereading carefully documentation for the BitBlit I have found the following important segment:
If other transformations exist in the source device context (and a matching transformation is not in effect in the destination device context), the rectangle in the destination device context is stretched, compressed, or rotated, as necessary.
Using ModifyWorldTransform(memDC, NULL, MWT_IDENTITY); as user Jonathan Potter suggested fixed this problem.
Those are my tries as far as scaling is concerned. Then I tried to implement scrolling by experimenting with the SetWindowOrgEx and similar APIs.
I have managed to move the image with few experiments, but I haven't fully grasped why and how that worked.
The reason for that is that I was unable to fully understand terms like window and viewport origins and similar. It is just too abstract for me at the moment. As I write this post, I am rereading again and trying to solve this on my own.
QUESTIONS:
How can I use the APIs (from the link I added above) to scale/scroll/scale and scroll metafile in memory DC, and properly BitBlt it in the main window device context?
REMARKS:
I realize that code example could be large, so I do not ask any. I do not want people to write the code for me, but to help me understand what I must do. I just need to fully grasp the concept of applying the above APIs the way I need. Therefore answers/comments can include instructions and small pseudo code, if found appropriate. I realize that the question might be broad, so I would appreciate if you could help me narrow down my problem with constructive criticism and comments.
Thank you for reading this post, offering help, and your understanding.
Best regards.
According to the SetWorldTransform documentation:
it will not be possible to reset the graphics mode for the device
context to the default GM_COMPATIBLE mode, unless the world
transformation has first been reset to the default identity
transformation
So even though you are attempting to reset the graphics mode, with this call:
SetGraphicsMode(memDC, prevGraphicsMode);
This is actually not working, because you are not first resetting the transformation to the identity transform. Adding the following line before the SetGraphicsMode call resets the transform and returns the DC to normal mapping:
ModifyWorldTransform(memDC, NULL, MWT_IDENTITY);
SetGraphicsMode(memDC, prevGraphicsMode);

flickering when updating ellipse after fillrect in c++ win32

All the action happens in c++ win32 application.
currently used rect fill method (redrawing quite often so it flickers):
RECT r;
PAINTSTRUCT ps;
RECT mRect;
brush_real = CreateSolidBrush(RGB(0, 0, 0));
{
dc = GetDC(GetDlgItem(hwnd, 1));
GetClientRect(GetDlgItem(hwnd, 1), &mRect);
FillRect(dc, &mRect, brush_real);
HBRUSH circleBrush = CreateSolidBrush(RGB(Roriginal, Goriginal, Boriginal)), oldBrush;
oldBrush = (HBRUSH)SelectObject(dc, circleBrush);
Ellipse(dc, mRect.left, mRect.top, mRect.right, mRect.bottom);
SelectObject(dc, oldBrush);
ReleaseDC(GetDlgItem(hwnd, 1), dc);
DeleteObject(oldBrush);
DeleteObject(circleBrush);
}
DeleteObject(brush_real);
this doesn't works fully as intended, because ellipse draw flickers when drawed after fillrect(which fills black background), i didn't yet figured out how to fill background once and for good on object initialization in dialog.
What could you offer ?

WinAPI. How to redraw window which has no background?

Hi I have WNDCLASSEX structure which has this data:
m_wndClass.cbSize = sizeof(WNDCLASSEX);
m_wndClass.style = CS_NOCLOSE;
m_wndClass.lpfnWndProc = WndProc;
m_wndClass.cbClsExtra = 0;
m_wndClass.cbWndExtra = 0;
m_wndClass.hInstance = GetModuleHandle(NULL);
m_wndClass.hIcon = NULL;
m_wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
m_wndClass.hbrBackground = NULL;
m_wndClass.lpszMenuName = NULL;
m_wndClass.lpszClassName = Checkbox::CHECKBOX_CLASS.c_str();
m_wndClass.hIconSm = NULL;
I need to have window without background, because I need to draw text on parent window and text may be any color.
Drawing works fine, code for drawing:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC dc = BeginPaint(window, &ps);
if (!classInfo->m_text.empty())
{
HDC wdc = GetDC(window);
SetBkMode(wdc,TRANSPARENT);
DrawText(wdc, classInfo->m_text.c_str(), -1, &classInfo->m_textRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOCLIP);
ReleaseDC(window, wdc);
}
EndPaint(window, &ps);
break;
}
However I have method to change text of label:
void Checkbox::SetText(const String& text)
{
m_text = text;
SetTextRectSize(); //calculates size of RECT
if (m_window != NULL)
InvalidateRect(m_window, NULL, TRUE);
}
After I create window with label I see label, however if I want to change text on it, it doesn't change (I need to manualy resize window and it changes after that). However I hadn't have this problem at the time when I used to have colored background, for example my window class had this:
m_wndClass.hbrBackground = HBRUSH(COLOR_3DFACE+1);
I want to ask, how to update window which, has no background.
EDIT: I have tried some stuff
FillRect(dc, &rect, (HBRUSH)GetStockObject(NULL_BRUSH));
also tried to change window procedure:
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC) wp;
SetBkMode (hdc, TRANSPARENT);
return (LRESULT)GetStockObject(NULL_BRUSH);
}
And the result is that I draw new text on previous, after setting text into some longer text part of label becomes corupted! see this but after resizing the main window my label becomes readable.
Your code doesn't set the device context's text foreground color for DrawText(), although the default is black. See SetTextColor().
SetBkMode(..., TRANSPARENT) just sets the background color/mode for the DrawText() rect, not the entire window.
You are asking about how to draw the window so it is transparent, but the problem isn't with the drawing at all.
The answer is essentially that everything you have done so far towards making a transparent window is wrong. It looks like the window is transparent, but in fact it is not, as you can see from the behaviour you describe when you move and resize the window. That's the classic symptom.
In other words, you haven't made the window transparent, you have just stopped drawing the background. What you see as the background is just whatever happened to be underneath the window when it was first drawn.
You need make a Layered window. To find out how to make transparent windows, go here:
http://msdn.microsoft.com/en-us/library/ms997507.aspx
Do you want Text/Check/Label be transparent on parent form?
You can Add WS_CLIPSIBLINGS and WS_EX_TRANSPARENT..
Use SetBkMode(hDC, TRANSPARENT) When WM_PAINT message

Bitmap disappearing when program is minimized

I'm a student for c++ game-programming. We have to program a textadventure on console for windows.
We get points for the implementation of bitmaps.
I have the following code to Display the bitmap. It works well but when i minimize my window, my bitmap disappears.
Where's the problem?
bool DisplayBitmap(char *szBitmap, int PosX = -1, int PosY = -1, bool AutoZoom = false)
{
HWND hWnd = FindWindow("ConsoleWindowClass",NULL); // Konsolen-Fenster identifizieren
if (!hWnd) return false;
HDC hDC = GetDC(hWnd); // Device Context des Fensters ermitteln
if (!hDC) return false;
HDC hBitmapDC = CreateCompatibleDC(hDC); // Device Context für Bitmap bereitstellen
if (!hBitmapDC) return false;
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,szBitmap, // Bild aus Datei laden
IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
if (!hBitmap) return false;
BITMAP bmp;
GetObject(hBitmap,sizeof(bmp),&bmp); // Zeiger auf Bitmap-Objekt anfordern
RECT Client;
GetClientRect(hWnd, &Client); // Fenstergröße bestimmen
if (PosX == -1) PosX = ((Client.right-Client.left)-bmp.bmWidth) / 2;
if (PosY == -1) PosY = 250-bmp.bmHeight; // ggfs. automatisch zentrieren
if (PosY < 0) PosY = 0; // und Zoomfaktor berechnen
double ZoomFactor = (Client.right-Client.left-2*PosX) / double(bmp.bmWidth);
SelectObject(hBitmapDC,hBitmap); // Bitmap kopieren (evtl. mit Zoom)
if (!AutoZoom) BitBlt(hDC,PosX,PosY,bmp.bmWidth,bmp.bmHeight,hBitmapDC,0,0,SRCCOPY);
else StretchBlt(hDC,PosX,PosY,int(bmp.bmWidth*ZoomFactor),int(bmp.bmHeight*ZoomFactor),hBitmapDC,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
DeleteObject(hBitmap); // Objekte und Device Contexts wieder freigeben
ReleaseDC(hWnd,hBitmapDC);
ReleaseDC(hWnd,hDC);
return true;
}
The bitmap disappears because the console window is repainted when restored and the repainting procedure knows nothing about your bitmap.
Windows are repainted quite often: every time your window is brought up or resized or restored from iconic state the window is repainted (well, may be not every time, because the system can make some caching... but still). When the window should be repainted, Windows sends WM_PAINT message to the window. Tha application handles this message and redraws the window contents in the window DC. This is how GUI-applications work. But if you use console, you can not handle window messages -- the system does it for you, and you can not change the repainting procedure.
If you want your bitmap to be always there, you should create your own window and process the WM_PAINT message (or, you can use some predefined control intended for displaying bitmaps). To do this you must create a GUI-application. You can not do this with console application.
The code you provided is just a hack and it is not supporsed to work as you expect. When the code is executed the bitmap is drawn. But once the part of the window is hidden beyond another window the image will be lost and nobody will restore it.
By the way, if your professor gave you this example, probably he will be satisfied by the result it produces. But never do like this in real life :)