[MSPAINT] Improve Zoom tool (#5798)

- Delete global zoomTo function.
- Add CCanvasWindow::zoomTo and
  CCanvasWindow::getNewZoomRect functions.
- Rename CCanvasWindow::updateScrollInfo as
  CCanvasWindow::updateScrollRange.
- Rename CCanvasWindow::resetScrollPos as
  CCanvasWindow::updateScrollPos.
- Draw the proper zoom rectangle on mouse move.
- Revert the active tool on click when the tool
  was Zoom.
CORE-19094
This commit is contained in:
Katayama Hirofumi MZ 2023-10-17 07:25:50 +09:00 committed by GitHub
parent e2d3aa7f4a
commit 8f1eb03ad2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 113 additions and 73 deletions

View file

@ -89,6 +89,49 @@ HITTEST CCanvasWindow::CanvasHitTest(POINT pt)
return getSizeBoxHitTest(pt, &rcBase); return getSizeBoxHitTest(pt, &rcBase);
} }
VOID CCanvasWindow::getNewZoomRect(CRect& rcView, INT newZoom, CPoint ptTarget)
{
CRect rcImage;
GetImageRect(rcImage);
ImageToCanvas(rcImage);
// Calculate the zoom rectangle
INT oldZoom = toolsModel.GetZoom();
GetClientRect(rcView);
LONG cxView = rcView.right * oldZoom / newZoom, cyView = rcView.bottom * oldZoom / newZoom;
::SetRect(&rcView, ptTarget.x - cxView / 2, ptTarget.y - cyView / 2,
ptTarget.x + cxView / 2, ptTarget.y + cyView / 2);
// Shift the rectangle if necessary
INT dx = 0, dy = 0;
if (rcView.left < rcImage.left)
dx = rcImage.left - rcView.left;
else if (rcImage.right < rcView.right)
dx = rcImage.right - rcView.right;
if (rcView.top < rcImage.top)
dy = rcImage.top - rcView.top;
else if (rcImage.bottom < rcView.bottom)
dy = rcImage.bottom - rcView.bottom;
rcView.OffsetRect(dx, dy);
rcView.IntersectRect(&rcView, &rcImage);
}
VOID CCanvasWindow::zoomTo(INT newZoom, LONG left, LONG top)
{
POINT pt = { left, top };
CanvasToImage(pt);
toolsModel.SetZoom(newZoom);
ImageToCanvas(pt);
pt.x += GetScrollPos(SB_HORZ);
pt.y += GetScrollPos(SB_VERT);
updateScrollRange();
updateScrollPos(pt.x, pt.y);
Invalidate(TRUE);
}
VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint) VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
{ {
// This is the target area we have to draw on // This is the target area we have to draw on
@ -180,7 +223,7 @@ VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
::DeleteDC(hdcMem0); ::DeleteDC(hdcMem0);
} }
VOID CCanvasWindow::updateScrollInfo() VOID CCanvasWindow::updateScrollRange()
{ {
CRect rcClient; CRect rcClient;
GetClientRect(&rcClient); GetClientRect(&rcClient);
@ -211,16 +254,16 @@ VOID CCanvasWindow::updateScrollInfo()
SetScrollInfo(SB_VERT, &si); SetScrollInfo(SB_VERT, &si);
} }
VOID CCanvasWindow::resetScrollPos() VOID CCanvasWindow::updateScrollPos(INT x, INT y)
{ {
SetScrollPos(SB_HORZ, 0); SetScrollPos(SB_HORZ, x);
SetScrollPos(SB_VERT, 0); SetScrollPos(SB_VERT, y);
} }
LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ {
if (m_hWnd) if (m_hWnd)
updateScrollInfo(); updateScrollRange();
return 0; return 0;
} }
@ -251,7 +294,7 @@ VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar)
break; break;
} }
SetScrollInfo(fnBar, &si); SetScrollInfo(fnBar, &si);
updateScrollInfo(); updateScrollRange();
Invalidate(FALSE); // FIXME: Flicker Invalidate(FALSE); // FIXME: Flicker
} }
@ -632,7 +675,7 @@ LRESULT CCanvasWindow::OnLRButtonUp(BOOL bLeftButton, UINT nMsg, WPARAM wParam,
m_hitCanvasSizeBox = HIT_NONE; m_hitCanvasSizeBox = HIT_NONE;
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions
updateScrollInfo(); updateScrollRange();
Invalidate(TRUE); Invalidate(TRUE);
return 0; return 0;
} }

View file

@ -42,8 +42,8 @@ public:
VOID cancelDrawing(); VOID cancelDrawing();
VOID finishDrawing(); VOID finishDrawing();
VOID updateScrollInfo(); VOID updateScrollRange();
VOID resetScrollPos(); VOID updateScrollPos(INT x = 0, INT y = 0);
VOID ImageToCanvas(POINT& pt); VOID ImageToCanvas(POINT& pt);
VOID ImageToCanvas(RECT& rc); VOID ImageToCanvas(RECT& rc);
@ -51,6 +51,8 @@ public:
VOID CanvasToImage(RECT& rc, BOOL bZoomed = FALSE); VOID CanvasToImage(RECT& rc, BOOL bZoomed = FALSE);
VOID GetImageRect(RECT& rc); VOID GetImageRect(RECT& rc);
VOID MoveSelection(INT xDelta, INT yDelta); VOID MoveSelection(INT xDelta, INT yDelta);
VOID getNewZoomRect(CRect& rcView, INT newZoom, CPoint ptTarget);
VOID zoomTo(INT newZoom, LONG left = 0, LONG top = 0);
protected: protected:
HITTEST m_hitSelection; HITTEST m_hitSelection;

View file

@ -43,7 +43,6 @@ enum HITTEST // hit
/* FUNCTIONS ********************************************************/ /* FUNCTIONS ********************************************************/
BOOL zoomTo(int newZoom, int mouseX, int mouseY);
BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1); BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1);
BOOL OpenMailer(HWND hWnd, LPCWSTR pszPathName); BOOL OpenMailer(HWND hWnd, LPCWSTR pszPathName);

View file

@ -241,7 +241,7 @@ HBITMAP InitializeImage(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile)
HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile) HBITMAP SetBitmapAndInfo(HBITMAP hBitmap, LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile)
{ {
// update image // update image
canvasWindow.resetScrollPos(); canvasWindow.updateScrollPos();
imageModel.PushImageForUndo(hBitmap); imageModel.PushImageForUndo(hBitmap);
imageModel.ClearHistory(); imageModel.ClearHistory();

View file

@ -16,7 +16,7 @@ void ImageModel::NotifyImageChanged()
{ {
if (canvasWindow.IsWindow()) if (canvasWindow.IsWindow())
{ {
canvasWindow.updateScrollInfo(); canvasWindow.updateScrollRange();
canvasWindow.Invalidate(); canvasWindow.Invalidate();
} }

View file

@ -563,40 +563,67 @@ struct ColorTool : ToolBase
// TOOL_ZOOM // TOOL_ZOOM
struct ZoomTool : ToolBase struct ZoomTool : ToolBase
{ {
BOOL m_bZoomed = FALSE;
ZoomTool() : ToolBase(TOOL_ZOOM) ZoomTool() : ToolBase(TOOL_ZOOM)
{ {
} }
BOOL getNewZoomRect(CRect& rcView, INT newZoom);
void OnDrawOverlayOnCanvas(HDC hdc) override void OnDrawOverlayOnCanvas(HDC hdc) override
{ {
CRect rc; CRect rcView;
canvasWindow.GetImageRect(rc); INT oldZoom = toolsModel.GetZoom();
canvasWindow.ImageToCanvas(rc); if (oldZoom < MAX_ZOOM && getNewZoomRect(rcView, oldZoom * 2))
DrawXorRect(hdc, &rcView);
POINT pt;
::GetCursorPos(&pt);
::ScreenToClient(canvasWindow, &pt);
// FIXME: Draw the border of the area that is to be zoomed in
if (rc.PtInRect(pt))
DrawXorRect(hdc, &rc);
} }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
{ {
INT newZoom, oldZoom = toolsModel.GetZoom();
if (bLeftButton) if (bLeftButton)
{ newZoom = (oldZoom < MAX_ZOOM) ? (oldZoom * 2) : MIN_ZOOM;
if (toolsModel.GetZoom() < MAX_ZOOM)
zoomTo(toolsModel.GetZoom() * 2, x, y);
}
else else
newZoom = (oldZoom > MIN_ZOOM) ? (oldZoom / 2) : MAX_ZOOM;
m_bZoomed = FALSE;
if (oldZoom != newZoom)
{ {
if (toolsModel.GetZoom() > MIN_ZOOM) CRect rcView;
zoomTo(toolsModel.GetZoom() / 2, x, y); if (getNewZoomRect(rcView, newZoom))
{
canvasWindow.zoomTo(newZoom, rcView.left, rcView.top);
m_bZoomed = TRUE;
}
} }
} }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
{
if (m_bZoomed)
toolsModel.SetActiveTool(toolsModel.GetOldActiveTool());
return TRUE;
}
}; };
BOOL ZoomTool::getNewZoomRect(CRect& rcView, INT newZoom)
{
CPoint pt;
::GetCursorPos(&pt);
canvasWindow.ScreenToClient(&pt);
canvasWindow.getNewZoomRect(rcView, newZoom, pt);
CRect rc;
canvasWindow.GetImageRect(rc);
canvasWindow.ImageToCanvas(rc);
return rc.PtInRect(pt);
}
// TOOL_PEN // TOOL_PEN
struct PenTool : SmoothDrawTool struct PenTool : SmoothDrawTool
{ {

View file

@ -309,7 +309,7 @@ LRESULT CToolSettingsWindow::OnDestroy(UINT nMsg, WPARAM wParam, LPARAM lParam,
LRESULT CToolSettingsWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT CToolSettingsWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ {
INT trackPos = MAX_ZOOM_TRACK - (INT)trackbarZoom.SendMessage(TBM_GETPOS, 0, 0); INT trackPos = MAX_ZOOM_TRACK - (INT)trackbarZoom.SendMessage(TBM_GETPOS, 0, 0);
zoomTo(MIN_ZOOM << trackPos, 0, 0); canvasWindow.zoomTo(MIN_ZOOM << trackPos);
INT zoomRate = toolsModel.GetZoom(); INT zoomRate = toolsModel.GetZoom();

View file

@ -41,37 +41,6 @@ static HWND DoHtmlHelpW(HWND hwndCaller, LPCWSTR pszFile, UINT uCommand, DWORD_P
return s_pHtmlHelpW(hwndCaller, pszFile, uCommand, dwData); return s_pHtmlHelpW(hwndCaller, pszFile, uCommand, dwData);
} }
BOOL
zoomTo(int newZoom, int mouseX, int mouseY)
{
int x, y, w, h;
RECT clientRectScrollbox;
canvasWindow.GetClientRect(&clientRectScrollbox);
RECT clientRectImageArea;
::SetRect(&clientRectImageArea, 0, 0, imageModel.GetWidth(), imageModel.GetHeight());
Zoomed(clientRectImageArea);
w = clientRectImageArea.right * newZoom / toolsModel.GetZoom();
h = clientRectImageArea.bottom * newZoom / toolsModel.GetZoom();
if (!w || !h)
{
return FALSE;
}
w = clientRectImageArea.right * clientRectScrollbox.right / w;
h = clientRectImageArea.bottom * clientRectScrollbox.bottom / h;
x = max(0, min(clientRectImageArea.right - w, mouseX - w / 2)) * newZoom / toolsModel.GetZoom();
y = max(0, min(clientRectImageArea.bottom - h, mouseY - h / 2)) * newZoom / toolsModel.GetZoom();
toolsModel.SetZoom(newZoom);
canvasWindow.Invalidate(TRUE);
canvasWindow.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0);
canvasWindow.SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, y), 0);
return TRUE;
}
void CMainWindow::alignChildrenToMainWindow() void CMainWindow::alignChildrenToMainWindow()
{ {
RECT clientRect, rc; RECT clientRect, rc;
@ -216,20 +185,20 @@ LRESULT CMainWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
{ {
INT zDelta = (SHORT)HIWORD(wParam); INT zDelta = (SHORT)HIWORD(wParam);
if (::GetAsyncKeyState(VK_CONTROL) < 0) if (::GetKeyState(VK_CONTROL) < 0) // Ctrl+Wheel
{ {
if (zDelta < 0) if (zDelta < 0)
{ {
if (toolsModel.GetZoom() > MIN_ZOOM) if (toolsModel.GetZoom() > MIN_ZOOM)
zoomTo(toolsModel.GetZoom() / 2, 0, 0); canvasWindow.zoomTo(toolsModel.GetZoom() / 2);
} }
else if (zDelta > 0) else if (zDelta > 0)
{ {
if (toolsModel.GetZoom() < MAX_ZOOM) if (toolsModel.GetZoom() < MAX_ZOOM)
zoomTo(toolsModel.GetZoom() * 2, 0, 0); canvasWindow.zoomTo(toolsModel.GetZoom() * 2);
} }
} }
else else // Wheel only
{ {
UINT nCount = 3; UINT nCount = 3;
if (::GetAsyncKeyState(VK_SHIFT) < 0) if (::GetAsyncKeyState(VK_SHIFT) < 0)
@ -921,7 +890,7 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
case IDM_IMAGEROTATEMIRROR: case IDM_IMAGEROTATEMIRROR:
{ {
CWaitCursor waitCursor; CWaitCursor waitCursor;
canvasWindow.resetScrollPos(); canvasWindow.updateScrollPos();
switch (mirrorRotateDialog.DoModal(mainWindow.m_hWnd)) switch (mirrorRotateDialog.DoModal(mainWindow.m_hWnd))
{ {
case 1: /* flip horizontally */ case 1: /* flip horizontally */
@ -1054,25 +1023,25 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
break; break;
case IDM_VIEWZOOM125: case IDM_VIEWZOOM125:
zoomTo(125, 0, 0); canvasWindow.zoomTo(125);
break; break;
case IDM_VIEWZOOM25: case IDM_VIEWZOOM25:
zoomTo(250, 0, 0); canvasWindow.zoomTo(250);
break; break;
case IDM_VIEWZOOM50: case IDM_VIEWZOOM50:
zoomTo(500, 0, 0); canvasWindow.zoomTo(500);
break; break;
case IDM_VIEWZOOM100: case IDM_VIEWZOOM100:
zoomTo(1000, 0, 0); canvasWindow.zoomTo(1000);
break; break;
case IDM_VIEWZOOM200: case IDM_VIEWZOOM200:
zoomTo(2000, 0, 0); canvasWindow.zoomTo(2000);
break; break;
case IDM_VIEWZOOM400: case IDM_VIEWZOOM400:
zoomTo(4000, 0, 0); canvasWindow.zoomTo(4000);
break; break;
case IDM_VIEWZOOM800: case IDM_VIEWZOOM800:
zoomTo(8000, 0, 0); canvasWindow.zoomTo(8000);
break; break;
case IDM_VIEWFULLSCREEN: case IDM_VIEWFULLSCREEN: