[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.
This commit is contained in:
Katayama Hirofumi MZ
2026-06-17 23:39:56 +09:00
committed by GitHub
parent b7932431e7
commit ee60345493
7 changed files with 68 additions and 20 deletions

View File

@@ -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();

View File

@@ -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<CCanvasWindow>

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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()

View File

@@ -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)