https://git.reactos.org/?p=reactos.git;a=commitdiff;h=44b2a46d032fd50255f55…
commit 44b2a46d032fd50255f55895dc9f7c389778e212
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Wed Mar 15 07:29:25 2023 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Wed Mar 15 07:29:25 2023 +0900
[MSPAINT] Avoid flickering when resizing (#5144)
- Add CPaletteWindow::OnEraseBkgnd to avoid flickering.
- Add getColorBoxRect and drawColorBox helper functions to draw a color box.
- Add CPaletteWindow::DoHitTest helper function to do a hit test.
- Improve CPaletteWindow::OnPaint by using a memory bitmap.
- Improve readability of CMainWindow::alignChildrenToMainWindow.
CORE-18867
---
base/applications/mspaint/palette.cpp | 175 +++++++++++++++++++++++-----------
base/applications/mspaint/palette.h | 9 ++
base/applications/mspaint/resource.h | 1 +
base/applications/mspaint/toolbox.cpp | 20 ++--
base/applications/mspaint/toolbox.h | 6 ++
base/applications/mspaint/winproc.cpp | 55 ++++++-----
6 files changed, 173 insertions(+), 93 deletions(-)
diff --git a/base/applications/mspaint/palette.cpp
b/base/applications/mspaint/palette.cpp
index 24fe9c96bf5..7795b97c28e 100644
--- a/base/applications/mspaint/palette.cpp
+++ b/base/applications/mspaint/palette.cpp
@@ -4,95 +4,162 @@
* FILE: base/applications/mspaint/palette.cpp
* PURPOSE: Window procedure of the palette window
* PROGRAMMERS: Benedikt Freisen
+ * Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
*/
-/* INCLUDES *********************************************************/
-
#include "precomp.h"
+/* The private metrics */
+#define CXY_SELECTEDBOX 15 /* width / height of a selected color box */
+#define X_MARGIN 4 /* horizontal margin */
+#define Y_MARGIN ((rcClient.bottom / 2) - CXY_COLORBOX) /* center position minus one
color box */
+#define X_COLORBOX_OFFSET (X_MARGIN + CXY_BIGBOX + X_MARGIN)
+#define COLOR_COUNT 28
+#define HALF_COLOR_COUNT (COLOR_COUNT / 2)
+
/* FUNCTIONS ********************************************************/
+static VOID drawColorBox(HDC hDC, LPCRECT prc, COLORREF rgbColor, UINT nBorder)
+{
+ RECT rc = *prc;
+ ::FillRect(hDC, &rc, (HBRUSH)(COLOR_3DFACE + 1));
+ ::DrawEdge(hDC, &rc, nBorder, BF_RECT | BF_ADJUST);
+
+ HBRUSH hbr = ::CreateSolidBrush(rgbColor);
+ ::FillRect(hDC, &rc, hbr);
+ ::DeleteObject(hbr);
+}
+
+static VOID getColorBoxRect(LPRECT prc, const RECT& rcClient, INT iColor)
+{
+ INT dx = (iColor % HALF_COLOR_COUNT) * CXY_COLORBOX; /* delta x */
+ INT dy = (iColor / HALF_COLOR_COUNT) * CXY_COLORBOX; /* delta y */
+ prc->left = X_COLORBOX_OFFSET + dx;
+ prc->right = prc->left + CXY_COLORBOX;
+ prc->top = Y_MARGIN + dy;
+ prc->bottom = prc->top + CXY_COLORBOX;
+}
+
+INT CPaletteWindow::DoHitTest(INT xPos, INT yPos) const
+{
+ RECT rcClient;
+ GetClientRect(&rcClient);
+
+ /* delta x and y */
+ INT dx = (xPos - X_COLORBOX_OFFSET), dy = (yPos - Y_MARGIN);
+
+ /* horizontal and vertical indexes */
+ INT ix = (dx / CXY_COLORBOX), iy = (dy / CXY_COLORBOX);
+
+ /* Is it inside of a color box? */
+ if (0 <= ix && ix < HALF_COLOR_COUNT && 0 <= iy &&
iy < 2)
+ return ix + (iy * HALF_COLOR_COUNT); /* return the color index */
+
+ return -1; /* Not found */
+}
+
+LRESULT CPaletteWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
+{
+ return TRUE; /* Avoid flickering */
+}
+
LRESULT CPaletteWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
{
- RECT rc = { 0, 0, 31, 32 };
- HPEN oldPen;
- HBRUSH oldBrush;
- int i, a, b;
+ RECT rc, rcClient;
+ GetClientRect(&rcClient);
PAINTSTRUCT ps;
HDC hDC = BeginPaint(&ps);
- for(b = 2; b < 30; b++)
- for(a = 2; a < 29; a++)
- if ((a + b) % 2 == 1)
- SetPixel(hDC, a, b, GetSysColor(COLOR_BTNHILIGHT));
-
- DrawEdge(hDC, &rc, EDGE_RAISED, BF_TOPLEFT);
- DrawEdge(hDC, &rc, BDR_SUNKENOUTER, BF_TOPLEFT | BF_BOTTOMRIGHT);
- SetRect(&rc, 11, 12, 26, 27);
- DrawEdge(hDC, &rc, BDR_RAISEDINNER, BF_RECT | BF_MIDDLE);
- oldPen = (HPEN) SelectObject(hDC, CreatePen(PS_NULL, 0, 0));
- oldBrush = (HBRUSH) SelectObject(hDC, CreateSolidBrush(paletteModel.GetBgColor()));
- Rectangle(hDC, rc.left, rc.top + 2, rc.right - 1, rc.bottom - 1);
- DeleteObject(SelectObject(hDC, oldBrush));
- SetRect(&rc, 4, 5, 19, 20);
- DrawEdge(hDC, &rc, BDR_RAISEDINNER, BF_RECT | BF_MIDDLE);
- oldBrush = (HBRUSH) SelectObject(hDC, CreateSolidBrush(paletteModel.GetFgColor()));
- Rectangle(hDC, rc.left + 2, rc.top + 2, rc.right - 1, rc.bottom - 1);
- DeleteObject(SelectObject(hDC, oldBrush));
- DeleteObject(SelectObject(hDC, oldPen));
-
- for(i = 0; i < 28; i++)
+ /* To avoid flickering, we use a memory bitmap.
+ The left and top values are zeros in client rectangle */
+ HDC hMemDC = ::CreateCompatibleDC(hDC);
+ HBITMAP hbm = ::CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom);
+ HGDIOBJ hbmOld = ::SelectObject(hMemDC, hbm);
+
+ /* Fill the background (since WM_ERASEBKGND handling is disabled) */
+ ::FillRect(hMemDC, &rcClient, (HBRUSH)(COLOR_3DFACE + 1));
+
+ /* Draw the big box that contains the black box and the white box */
+ ::SetRect(&rc, X_MARGIN, Y_MARGIN, X_MARGIN + CXY_BIGBOX, Y_MARGIN +
CXY_BIGBOX);
+ ::DrawEdge(hMemDC, &rc, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ COLORREF rgbLight = ::GetSysColor(COLOR_3DHIGHLIGHT);
+ for (INT y = rc.top; y < rc.bottom; ++y)
+ {
+ BOOL bLight = (y & 1);
+ for (INT x = rc.left; x < rc.right; ++x)
+ {
+ if (bLight)
+ ::SetPixelV(hMemDC, x, y, rgbLight);
+ bLight = !bLight;
+ }
+ }
+
+ /* Draw the white box in the big box, at 5/8 position */
+ rc.left = X_MARGIN + (CXY_BIGBOX * 5 / 8) - (CXY_SELECTEDBOX / 2);
+ rc.top = Y_MARGIN + (CXY_BIGBOX * 5 / 8) - (CXY_SELECTEDBOX / 2);
+ rc.right = rc.left + CXY_SELECTEDBOX;
+ rc.bottom = rc.top + CXY_SELECTEDBOX;
+ drawColorBox(hMemDC, &rc, paletteModel.GetBgColor(), BDR_RAISEDINNER);
+
+ /* Draw the black box (overlapping the white box), at 3/8 position */
+ rc.left = X_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
+ rc.top = Y_MARGIN + (CXY_BIGBOX * 3 / 8) - (CXY_SELECTEDBOX / 2);
+ rc.right = rc.left + CXY_SELECTEDBOX;
+ rc.bottom = rc.top + CXY_SELECTEDBOX;
+ drawColorBox(hMemDC, &rc, paletteModel.GetFgColor(), BDR_RAISEDINNER);
+
+ /* Draw the normal color boxes */
+ for (INT i = 0; i < COLOR_COUNT; i++)
{
- SetRect(&rc, 31 + (i % 14) * 16,
- 0 + (i / 14) * 16, 16 + 31 + (i % 14) * 16, 16 + 0 + (i / 14) * 16);
- DrawEdge(hDC, &rc, EDGE_RAISED, BF_TOPLEFT);
- DrawEdge(hDC, &rc, BDR_SUNKENOUTER, BF_RECT);
- oldPen = (HPEN) SelectObject(hDC, CreatePen(PS_NULL, 0, 0));
- oldBrush = (HBRUSH) SelectObject(hDC,
CreateSolidBrush(paletteModel.GetColor(i)));
- Rectangle(hDC, rc.left + 2, rc.top + 2, rc.right - 1, rc.bottom - 1);
- DeleteObject(SelectObject(hDC, oldBrush));
- DeleteObject(SelectObject(hDC, oldPen));
+ getColorBoxRect(&rc, rcClient, i);
+ drawColorBox(hMemDC, &rc, paletteModel.GetColor(i), BDR_SUNKENOUTER);
}
+
+ /* Transfer bits (hDC <-- hMemDC) */
+ ::BitBlt(hDC, 0, 0, rcClient.right, rcClient.bottom, hMemDC, 0, 0, SRCCOPY);
+
+ ::SelectObject(hMemDC, hbmOld);
+ ::DeleteDC(hMemDC);
EndPaint(&ps);
return 0;
}
LRESULT CPaletteWindow::OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
{
- if (GET_X_LPARAM(lParam) >= 31)
- paletteModel.SetFgColor(paletteModel.GetColor((GET_X_LPARAM(lParam) - 31) / 16 +
(GET_Y_LPARAM(lParam) / 16) * 14));
+ INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ if (iColor != -1)
+ paletteModel.SetFgColor(paletteModel.GetColor(iColor));
return 0;
}
LRESULT CPaletteWindow::OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
{
- if (GET_X_LPARAM(lParam) >= 31)
- paletteModel.SetBgColor(paletteModel.GetColor((GET_X_LPARAM(lParam) - 31) / 16 +
(GET_Y_LPARAM(lParam) / 16) * 14));
+ INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ if (iColor != -1)
+ paletteModel.SetBgColor(paletteModel.GetColor(iColor));
return 0;
}
LRESULT CPaletteWindow::OnLButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
- if (GET_X_LPARAM(lParam) >= 31)
- if (ChooseColor(&choosecolor))
- {
- paletteModel.SetColor((GET_X_LPARAM(lParam) - 31) / 16 +
(GET_Y_LPARAM(lParam) / 16) * 14,
- choosecolor.rgbResult);
- paletteModel.SetFgColor(choosecolor.rgbResult);
- }
+ INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ if (iColor != -1 && ChooseColor(&choosecolor))
+ {
+ paletteModel.SetColor(iColor, choosecolor.rgbResult);
+ paletteModel.SetFgColor(choosecolor.rgbResult);
+ }
return 0;
}
LRESULT CPaletteWindow::OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled)
{
- if (GET_X_LPARAM(lParam) >= 31)
- if (ChooseColor(&choosecolor))
- {
- paletteModel.SetColor((GET_X_LPARAM(lParam) - 31) / 16 +
(GET_Y_LPARAM(lParam) / 16) * 14,
- choosecolor.rgbResult);
- paletteModel.SetBgColor(choosecolor.rgbResult);
- }
+ INT iColor = DoHitTest(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+ if (iColor != -1 && ChooseColor(&choosecolor))
+ {
+ paletteModel.SetColor(iColor, choosecolor.rgbResult);
+ paletteModel.SetBgColor(choosecolor.rgbResult);
+ }
return 0;
}
diff --git a/base/applications/mspaint/palette.h b/base/applications/mspaint/palette.h
index ca84fc5e9d3..13a7b1663be 100644
--- a/base/applications/mspaint/palette.h
+++ b/base/applications/mspaint/palette.h
@@ -8,12 +8,17 @@
#pragma once
+#define CXY_COLORBOX 16 /* width / height of a normal color box */
+#define CXY_BIGBOX (CXY_COLORBOX * 2) /* width / height of the big box */
+#define CY_PALETTE (8 + CXY_BIGBOX + 8)
+
class CPaletteWindow : public CWindowImpl<CPaletteWindow>
{
public:
DECLARE_WND_CLASS_EX(_T("Palette"), CS_DBLCLKS, COLOR_BTNFACE)
BEGIN_MSG_MAP(CPaletteWindow)
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
MESSAGE_HANDLER(WM_RBUTTONDOWN, OnRButtonDown)
@@ -23,6 +28,7 @@ public:
MESSAGE_HANDLER(WM_PALETTEMODELPALETTECHANGED, OnPaletteModelPaletteChanged)
END_MSG_MAP()
+ LRESULT OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnLButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
LRESULT OnRButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
@@ -30,4 +36,7 @@ public:
LRESULT OnRButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled);
LRESULT OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled);
LRESULT OnPaletteModelPaletteChanged(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandled);
+
+protected:
+ INT DoHitTest(INT xPos, INT yPos) const;
};
diff --git a/base/applications/mspaint/resource.h b/base/applications/mspaint/resource.h
index 3e563416309..fed82f9e1f0 100644
--- a/base/applications/mspaint/resource.h
+++ b/base/applications/mspaint/resource.h
@@ -119,6 +119,7 @@
#define ID_SHAPE 613
#define ID_ELLIPSE 614
#define ID_RRECT 615
+#define NUM_TOOLS (ID_RRECT - ID_FREESEL + 1)
#define ID_ACCELERATORS 800
diff --git a/base/applications/mspaint/toolbox.cpp
b/base/applications/mspaint/toolbox.cpp
index d11917b5cac..575ad62692c 100644
--- a/base/applications/mspaint/toolbox.cpp
+++ b/base/applications/mspaint/toolbox.cpp
@@ -18,15 +18,13 @@ LRESULT CToolBox::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandl
HIMAGELIST hImageList;
HBITMAP tempBm;
int i;
- TCHAR tooltips[16][30];
-
- /*
- * FIXME: Unintentionally there is a line above the tool bar (hidden by y-offset).
- * To prevent cropping of the buttons height has been increased from 200 to 205
- */
- RECT toolbarPos = {1, -2, 1 + 50, -2 + 205};
- toolbar.Create(TOOLBARCLASSNAME, m_hWnd, toolbarPos, NULL,
- WS_CHILD | WS_VISIBLE | CCS_NOPARENTALIGN | CCS_VERT | CCS_NORESIZE |
TBSTYLE_TOOLTIPS);
+ TCHAR tooltips[NUM_TOOLS][30];
+
+ /* NOTE: The horizontal line above the toolbar is hidden by CCS_NODIVIDER style. */
+ RECT toolbarPos = {0, 0, CX_TOOLBAR, CY_TOOLBAR};
+ DWORD style = WS_CHILD | WS_VISIBLE | CCS_NOPARENTALIGN | CCS_VERT | CCS_NORESIZE |
+ TBSTYLE_TOOLTIPS | TBSTYLE_FLAT | CCS_NODIVIDER;
+ toolbar.Create(TOOLBARCLASSNAME, m_hWnd, toolbarPos, NULL, style);
hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 16, 0);
toolbar.SendMessage(TB_SETIMAGELIST, 0, (LPARAM) hImageList);
tempBm = (HBITMAP) LoadImage(hProgInstance, MAKEINTRESOURCE(IDB_TOOLBARICONS),
IMAGE_BITMAP, 256, 16, 0);
@@ -34,7 +32,7 @@ LRESULT CToolBox::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandl
DeleteObject(tempBm);
toolbar.SendMessage(TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
- for(i = 0; i < 16; i++)
+ for (i = 0; i < NUM_TOOLS; i++)
{
TBBUTTON tbbutton;
int wrapnow = 0;
@@ -54,7 +52,7 @@ LRESULT CToolBox::OnCreate(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHandl
toolbar.SendMessage(TB_CHECKBUTTON, ID_PEN, MAKELPARAM(TRUE, 0));
toolbar.SendMessage(TB_SETMAXTEXTROWS, 0, 0);
- toolbar.SendMessage(TB_SETBUTTONSIZE, 0, MAKELPARAM(25, 25));
+ toolbar.SendMessage(TB_SETBUTTONSIZE, 0, MAKELPARAM(CXY_TB_BUTTON, CXY_TB_BUTTON));
return 0;
}
diff --git a/base/applications/mspaint/toolbox.h b/base/applications/mspaint/toolbox.h
index f7cdfc91288..fc02299565b 100644
--- a/base/applications/mspaint/toolbox.h
+++ b/base/applications/mspaint/toolbox.h
@@ -9,6 +9,12 @@
#pragma once
+#define TOOLBAR_ROWS 8
+#define TOOLBAR_COLUMNS 2
+#define CXY_TB_BUTTON 25
+#define CX_TOOLBAR ((TOOLBAR_COLUMNS * CXY_TB_BUTTON) + 2)
+#define CY_TOOLBAR ((TOOLBAR_ROWS * CXY_TB_BUTTON) + 2)
+
class CToolBox : public CWindowImpl<CToolBox>
{
public:
diff --git a/base/applications/mspaint/winproc.cpp
b/base/applications/mspaint/winproc.cpp
index 3a064799c40..366e711ed06 100644
--- a/base/applications/mspaint/winproc.cpp
+++ b/base/applications/mspaint/winproc.cpp
@@ -47,45 +47,45 @@ zoomTo(int newZoom, int mouseX, int mouseY)
void CMainWindow::alignChildrenToMainWindow()
{
- int x, y, w, h;
- RECT clientRect;
+ RECT clientRect, rc;
GetClientRect(&clientRect);
+ RECT rcSpace = clientRect;
- if (::IsWindowVisible(toolBoxContainer))
+ if (::IsWindowVisible(hStatusBar))
{
- x = 56;
- w = clientRect.right - 56;
+ ::GetWindowRect(hStatusBar, &rc);
+ rcSpace.bottom -= rc.bottom - rc.top;
}
- else
+
+ HDWP hDWP = ::BeginDeferWindowPos(3);
+
+ if (::IsWindowVisible(toolBoxContainer))
{
- x = 0;
- w = clientRect.right;
+ hDWP = ::DeferWindowPos(hDWP, toolBoxContainer, NULL,
+ rcSpace.left, rcSpace.top,
+ CX_TOOLBAR, rcSpace.bottom - rcSpace.top,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
+ rcSpace.left += CX_TOOLBAR;
}
+
if (::IsWindowVisible(paletteWindow))
{
- y = 49;
- h = clientRect.bottom - 49;
- }
- else
- {
- y = 3;
- h = clientRect.bottom - 3;
+ hDWP = ::DeferWindowPos(hDWP, paletteWindow, NULL,
+ rcSpace.left, rcSpace.top,
+ rcSpace.right - rcSpace.left, CY_PALETTE,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
+ rcSpace.top += CY_PALETTE;
}
- INT statusBarHeight = 0;
- if (::IsWindowVisible(hStatusBar))
+ if (scrollboxWindow.IsWindow())
{
- RECT Rect;
- INT borders[3];
- ::SendMessage(hStatusBar, SB_GETRECT, 0, (LPARAM)&Rect);
- ::SendMessage(hStatusBar, SB_GETBORDERS, 0, (LPARAM)&borders);
- statusBarHeight = Rect.bottom - Rect.top + borders[1];
+ hDWP = ::DeferWindowPos(hDWP, scrollboxWindow, NULL,
+ rcSpace.left, rcSpace.top,
+ rcSpace.right - rcSpace.left, rcSpace.bottom -
rcSpace.top,
+ SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION);
}
- if (scrollboxWindow.IsWindow())
- scrollboxWindow.MoveWindow(x, y, w, h - statusBarHeight, TRUE);
- if (paletteWindow.IsWindow())
- paletteWindow.MoveWindow(x, 9, 255, 32, TRUE);
+ ::EndDeferWindowPos(hDWP);
}
void CMainWindow::saveImage(BOOL overwrite)
@@ -369,11 +369,10 @@ LRESULT CMainWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam,
BOOL& bHand
int test[] = { LOWORD(lParam) - 260, LOWORD(lParam) - 140, LOWORD(lParam) - 20 };
if (::IsWindow(hStatusBar))
{
- ::SendMessage(hStatusBar, WM_SIZE, wParam, lParam);
+ ::SendMessage(hStatusBar, WM_SIZE, 0, 0);
::SendMessage(hStatusBar, SB_SETPARTS, 3, (LPARAM)&test);
}
alignChildrenToMainWindow();
- Invalidate(TRUE);
return 0;
}