From ee60345493d4e1f07e5fe8088d4aedb11a8bb1dd Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Wed, 17 Jun 2026 23:39:56 +0900 Subject: [PATCH] [MSPAINT] Zooming and cursor shape (#9125) #8974 implemented custom mouse cursor shapes. However, the custom cursor shape didn't consider zooming. This PR applies zooming to the custom cursor shapes. JIRA issue: CORE-20520 - Enhance CStyledCursor for zooming. - Enhance CopyMonoImage and CopyDIBImage for stretch mode. - Add DEFAULT_ZOOM constant (1000). - Send WM_SETCURSOR message on ToolsModel::SetZoom. - Improve accuracy of Zoomed and UnZoomed functions by using MulDiv function. --- base/applications/mspaint/canvas.cpp | 36 +++++++++++++++++++----- base/applications/mspaint/canvas.h | 5 ++-- base/applications/mspaint/dib.cpp | 31 ++++++++++++++++++-- base/applications/mspaint/dib.h | 8 ++---- base/applications/mspaint/precomp.h | 1 + base/applications/mspaint/toolsmodel.cpp | 3 +- base/applications/mspaint/toolsmodel.h | 4 +-- 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/base/applications/mspaint/canvas.cpp b/base/applications/mspaint/canvas.cpp index 31c3e83aaa1..64535a86408 100644 --- a/base/applications/mspaint/canvas.cpp +++ b/base/applications/mspaint/canvas.cpp @@ -13,10 +13,10 @@ CCanvasWindow canvasWindow; /* FUNCTIONS ********************************************************/ HCURSOR -CStyledCursor::CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber) +CStyledCursor::CreateStyledCursor(BrushStyle style, INT zoom, INT radius, COLORREF color, BOOL is_rubber) { const INT diameter = 2 * radius; - if (diameter <= 2) + if (diameter * zoom / DEFAULT_ZOOM <= 2) { HCURSOR hCursor = ::LoadCursor(NULL, IDC_CROSS); return hCursor ? CopyCursor(hCursor) : NULL; @@ -24,7 +24,7 @@ CStyledCursor::CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, const INT crosshair1 = 6, crosshair2 = crosshair1 - 2; const INT width = diameter + 2 * crosshair1, height = diameter + 2 * crosshair1; - const DWORD hotX = width / 2, hotY = height / 2; + DWORD hotX = width / 2, hotY = height / 2; HDC hdcScreen = ::GetDC(NULL); if (!hdcScreen) @@ -116,6 +116,20 @@ CStyledCursor::CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, ::ReleaseDC(NULL, hdcScreen); ::DeleteDC(hdcMem); + if (zoom != DEFAULT_ZOOM) + { + INT newWidth = width * zoom / DEFAULT_ZOOM; + INT newHeight = height * zoom / DEFAULT_ZOOM; + HBITMAP hbmMaskNew = CopyMonoImage(hbmMask, newWidth, newHeight, STRETCH_DELETESCANS); + HBITMAP hbmColorNew = CopyDIBImage(hbmColor, newWidth, newHeight, STRETCH_DELETESCANS); + ::DeleteObject(hbmMask); + ::DeleteObject(hbmColor); + hbmMask = hbmMaskNew; + hbmColor = hbmColorNew; + hotX = width * zoom / (2 * DEFAULT_ZOOM); + hotY = height * zoom / (2 * DEFAULT_ZOOM); + } + ICONINFO ii = { FALSE, hotX, hotY, hbmMask, hbmColor }; HCURSOR hCursor = (HCURSOR)::CreateIconIndirect(&ii); @@ -125,9 +139,13 @@ CStyledCursor::CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, return hCursor; } -void CStyledCursor::SetStyle(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber) +void CStyledCursor::SetStyle(BrushStyle style, INT zoom, INT radius, COLORREF color, BOOL is_rubber) { - if (m_hCursor && m_style == style && m_radius == radius && m_color == color && + if (m_hCursor && + m_style == style && + m_zoom == zoom && + m_radius == radius && + m_color == color && m_is_rubber == is_rubber) { return; @@ -136,8 +154,9 @@ void CStyledCursor::SetStyle(BrushStyle style, INT radius, COLORREF color, BOOL if (m_hCursor) DestroyCursor(m_hCursor); - m_hCursor = CreateStyledCursor(style, radius, color, is_rubber); + m_hCursor = CreateStyledCursor(style, zoom, radius, color, is_rubber); m_style = style; + m_zoom = zoom; m_radius = radius; m_color = color; m_is_rubber = is_rubber; @@ -775,7 +794,9 @@ LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL break; case TOOL_RUBBER: { - m_hRubberCursor.SetStyle(BrushStyleSquare, toolsModel.GetRubberRadius(), + m_hRubberCursor.SetStyle(BrushStyleSquare, + toolsModel.GetZoom(), + toolsModel.GetRubberRadius(), paletteModel.GetBgColor(), TRUE); m_hRubberCursor.SetCursor(); break; @@ -783,6 +804,7 @@ LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL case TOOL_BRUSH: { m_hBrushCursor.SetStyle(toolsModel.GetBrushStyle(), + toolsModel.GetZoom(), toolsModel.GetBrushWidth() / 2, paletteModel.GetFgColor(), FALSE); m_hBrushCursor.SetCursor(); diff --git a/base/applications/mspaint/canvas.h b/base/applications/mspaint/canvas.h index fc0e1117cff..17982805b04 100644 --- a/base/applications/mspaint/canvas.h +++ b/base/applications/mspaint/canvas.h @@ -19,7 +19,7 @@ public: ::DestroyCursor(m_hCursor); } - void SetStyle(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber); + void SetStyle(BrushStyle style, INT zoom, INT radius, COLORREF color, BOOL is_rubber); void SetCursor() { @@ -32,11 +32,12 @@ public: protected: HCURSOR m_hCursor = NULL; BrushStyle m_style; + INT m_zoom = -1; INT m_radius = -1; COLORREF m_color = CLR_INVALID; BOOL m_is_rubber = FALSE; - static HCURSOR CreateStyledCursor(BrushStyle style, INT radius, COLORREF color, BOOL is_rubber); + static HCURSOR CreateStyledCursor(BrushStyle style, INT zoom, INT radius, COLORREF color, BOOL is_rubber); }; class CCanvasWindow : public CWindowImpl diff --git a/base/applications/mspaint/dib.cpp b/base/applications/mspaint/dib.cpp index 93670e0e34c..9d4d1638cec 100644 --- a/base/applications/mspaint/dib.cpp +++ b/base/applications/mspaint/dib.cpp @@ -84,7 +84,7 @@ CreateColorDIB(int width, int height, COLORREF rgb) return ret; } -HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy) +HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy, INT stretchMode) { BITMAP bm; if (!::GetObjectW(hbm, sizeof(bm), &bm)) @@ -96,14 +96,41 @@ HBITMAP CopyMonoImage(HBITMAP hbm, INT cx, INT cy) cy = bm.bmHeight; } + HDC hdc1 = ::CreateCompatibleDC(NULL); + HDC hdc2 = ::CreateCompatibleDC(NULL); HBITMAP hbmNew = ::CreateBitmap(cx, cy, 1, 1, NULL); - if (!hbmNew) + HGDIOBJ hbm1Old = ::SelectObject(hdc1, hbm); + HGDIOBJ hbm2Old = ::SelectObject(hdc2, hbmNew); + ::SetStretchBltMode(hdc2, stretchMode); + ::StretchBlt(hdc2, 0, 0, cx, cy, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); + ::SelectObject(hdc1, hbm1Old); + ::SelectObject(hdc2, hbm2Old); + ::DeleteDC(hdc1); + ::DeleteDC(hdc2); + return hbmNew; +} + +HBITMAP CopyDIBImage(HBITMAP hbm, INT cx, INT cy, INT stretchMode) +{ + if (stretchMode == STRETCH_HALFTONE) + return (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, cx, cy, LR_CREATEDIBSECTION); + + BITMAP bm; + if (!::GetObjectW(hbm, sizeof(bm), &bm)) return NULL; + if (cx == 0 || cy == 0) + { + cx = bm.bmWidth; + cy = bm.bmHeight; + } + HDC hdc1 = ::CreateCompatibleDC(NULL); HDC hdc2 = ::CreateCompatibleDC(NULL); + HBITMAP hbmNew = CreateDIBWithProperties(cx, cy); HGDIOBJ hbm1Old = ::SelectObject(hdc1, hbm); HGDIOBJ hbm2Old = ::SelectObject(hdc2, hbmNew); + ::SetStretchBltMode(hdc2, stretchMode); ::StretchBlt(hdc2, 0, 0, cx, cy, hdc1, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); ::SelectObject(hdc1, hbm1Old); ::SelectObject(hdc2, hbm2Old); diff --git a/base/applications/mspaint/dib.h b/base/applications/mspaint/dib.h index 9d5ab00c47c..82c02bb5870 100644 --- a/base/applications/mspaint/dib.h +++ b/base/applications/mspaint/dib.h @@ -14,12 +14,8 @@ HBITMAP CreateColorDIB(int width, int height, COLORREF rgb); HBITMAP CachedBufferDIB(HBITMAP hbm, int minimalWidth, int minimalHeight); HBITMAP ConvertToBlackAndWhite(HBITMAP hbm); -HBITMAP CopyMonoImage(HBITMAP hbm, INT cx = 0, INT cy = 0); - -static inline HBITMAP CopyDIBImage(HBITMAP hbm, INT cx = 0, INT cy = 0) -{ - return (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, cx, cy, LR_COPYRETURNORG | LR_CREATEDIBSECTION); -} +HBITMAP CopyMonoImage(HBITMAP hbm, INT cx = 0, INT cy = 0, INT stretchMode = STRETCH_HALFTONE); +HBITMAP CopyDIBImage(HBITMAP hbm, INT cx = 0, INT cy = 0, INT stretchMode = STRETCH_HALFTONE); int GetDIBWidth(HBITMAP hbm); int GetDIBHeight(HBITMAP hbm); diff --git a/base/applications/mspaint/precomp.h b/base/applications/mspaint/precomp.h index f5efb4da77b..6475b1fee71 100644 --- a/base/applications/mspaint/precomp.h +++ b/base/applications/mspaint/precomp.h @@ -43,6 +43,7 @@ #define GRIP_SIZE 3 #define MIN_ZOOM 125 #define MAX_ZOOM 8000 +#define DEFAULT_ZOOM 1000 #define MAX_LONG_PATH 512 diff --git a/base/applications/mspaint/toolsmodel.cpp b/base/applications/mspaint/toolsmodel.cpp index 1fa514ad66b..93fa233c685 100644 --- a/base/applications/mspaint/toolsmodel.cpp +++ b/base/applications/mspaint/toolsmodel.cpp @@ -21,7 +21,7 @@ ToolsModel::ToolsModel() m_airBrushRadius = 5; m_rubberRadius = 4; m_transpBg = FALSE; - m_zoom = 1000; + m_zoom = DEFAULT_ZOOM; m_pToolObject = GetOrCreateTool(m_activeTool); } @@ -276,6 +276,7 @@ void ToolsModel::SetZoom(int nZoom) { m_zoom = nZoom; NotifyZoomChanged(); + SendSetCursor(); } void ToolsModel::NotifyToolChanged() diff --git a/base/applications/mspaint/toolsmodel.h b/base/applications/mspaint/toolsmodel.h index d3a0181bd1d..2d4813866c6 100644 --- a/base/applications/mspaint/toolsmodel.h +++ b/base/applications/mspaint/toolsmodel.h @@ -150,12 +150,12 @@ extern ToolsModel toolsModel; static inline int Zoomed(int xy) { - return xy * toolsModel.GetZoom() / 1000; + return MulDiv(xy, toolsModel.GetZoom(), DEFAULT_ZOOM); } static inline int UnZoomed(int xy) { - return xy * 1000 / toolsModel.GetZoom(); + return MulDiv(xy, DEFAULT_ZOOM, toolsModel.GetZoom()); } static inline void Zoomed(POINT& pt)