[MSPAINT] Introduce partial image history, Part 2 (#6005)

Follow-up to #5994. Reduce the lag and the
cost of drawing on large image.
- Introduce partial image history on
  SmoothDrawTool and ShapeTool.
- We can draw with pen smoothly even
  when the image is huge (10000x10000).
CORE-19237
This commit is contained in:
Katayama Hirofumi MZ 2023-11-23 07:14:57 +09:00 committed by GitHub
parent 5b5aaf6687
commit e579220098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 163 additions and 153 deletions

View file

@ -40,18 +40,14 @@ RECT CCanvasWindow::GetBaseRect()
VOID CCanvasWindow::ImageToCanvas(POINT& pt) VOID CCanvasWindow::ImageToCanvas(POINT& pt)
{ {
pt.x = Zoomed(pt.x); Zoomed(pt);
pt.y = Zoomed(pt.y);
pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ); pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ);
pt.y += GRIP_SIZE - GetScrollPos(SB_VERT); pt.y += GRIP_SIZE - GetScrollPos(SB_VERT);
} }
VOID CCanvasWindow::ImageToCanvas(RECT& rc) VOID CCanvasWindow::ImageToCanvas(RECT& rc)
{ {
rc.left = Zoomed(rc.left); Zoomed(rc);
rc.top = Zoomed(rc.top);
rc.right = Zoomed(rc.right);
rc.bottom = Zoomed(rc.bottom);
::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT)); ::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));
} }
@ -61,8 +57,7 @@ VOID CCanvasWindow::CanvasToImage(POINT& pt, BOOL bZoomed)
pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT); pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT);
if (bZoomed) if (bZoomed)
return; return;
pt.x = UnZoomed(pt.x); UnZoomed(pt);
pt.y = UnZoomed(pt.y);
} }
VOID CCanvasWindow::CanvasToImage(RECT& rc, BOOL bZoomed) VOID CCanvasWindow::CanvasToImage(RECT& rc, BOOL bZoomed)
@ -70,15 +65,12 @@ VOID CCanvasWindow::CanvasToImage(RECT& rc, BOOL bZoomed)
::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE); ::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE);
if (bZoomed) if (bZoomed)
return; return;
rc.left = UnZoomed(rc.left); UnZoomed(rc);
rc.top = UnZoomed(rc.top);
rc.right = UnZoomed(rc.right);
rc.bottom = UnZoomed(rc.bottom);
} }
VOID CCanvasWindow::GetImageRect(RECT& rc) VOID CCanvasWindow::GetImageRect(RECT& rc)
{ {
::SetRect(&rc, 0, 0, imageModel.GetWidth(), imageModel.GetHeight()); rc = { 0, 0, imageModel.GetWidth(), imageModel.GetHeight() };
} }
HITTEST CCanvasWindow::CanvasHitTest(POINT pt) HITTEST CCanvasWindow::CanvasHitTest(POINT pt)
@ -689,7 +681,7 @@ LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL&
{ {
if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd) if (wParam == VK_ESCAPE && ::GetCapture() == m_hWnd)
{ {
// Cancel dragging cancelDrawing();
::ReleaseCapture(); ::ReleaseCapture();
m_nMouseDownMsg = 0; m_nMouseDownMsg = 0;
m_hitCanvasSizeBox = HIT_NONE; m_hitCanvasSizeBox = HIT_NONE;

View file

@ -46,6 +46,7 @@ enum HITTEST // hit
void ShowOutOfMemory(void); void ShowOutOfMemory(void);
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);
void getBoundaryOfPtStack(RECT& rcBoundary, INT cPoints, const POINT *pPoints);
#define DEG2RAD(degree) (((degree) * M_PI) / 180) #define DEG2RAD(degree) (((degree) * M_PI) / 180)
#define RAD2DEG(radian) ((LONG)(((radian) * 180) / M_PI)) #define RAD2DEG(radian) ((LONG)(((radian) * 180) / M_PI))

View file

@ -122,7 +122,6 @@ LRESULT CMiniatureWindow::OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lPara
{ {
// Avoid too small // Avoid too small
LPMINMAXINFO pInfo = (LPMINMAXINFO)lParam; LPMINMAXINFO pInfo = (LPMINMAXINFO)lParam;
pInfo->ptMinTrackSize.x = 100; pInfo->ptMinTrackSize = { 100, 75 };
pInfo->ptMinTrackSize.y = 75;
return 0; return 0;
} }

View file

@ -9,9 +9,11 @@
/* INCLUDES *********************************************************/ /* INCLUDES *********************************************************/
#include "precomp.h" #include "precomp.h"
#include <atlalloc.h>
INT ToolBase::s_pointSP = 0; SIZE_T ToolBase::s_pointSP = 0;
POINT ToolBase::s_pointStack[256] = { { 0 } }; static SIZE_T s_maxPointSP = 0;
static CHeapPtr<POINT, CLocalAllocator> s_pointStack;
static POINT g_ptStart, g_ptEnd; static POINT g_ptStart, g_ptEnd;
/* FUNCTIONS ********************************************************/ /* FUNCTIONS ********************************************************/
@ -51,11 +53,31 @@ BOOL nearlyEqualPoints(INT x0, INT y0, INT x1, INT y1)
return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold); return (abs(x1 - x0) <= cxThreshold) && (abs(y1 - y0) <= cyThreshold);
} }
void getBoundaryOfPtStack(RECT& rcBoundary, INT cPoints, const POINT *pPoints)
{
POINT ptMin = { MAXLONG, MAXLONG }, ptMax = { (LONG)MINLONG, (LONG)MINLONG };
while (cPoints-- > 0)
{
LONG x = pPoints->x, y = pPoints->y;
ptMin = { min(x, ptMin.x), min(y, ptMin.y) };
ptMax = { max(x, ptMax.x), max(y, ptMax.y) };
++pPoints;
}
ptMax.x += 1;
ptMax.y += 1;
CRect rc(ptMin, ptMax);
rcBoundary = rc;
}
void ToolBase::reset() void ToolBase::reset()
{ {
s_pointSP = 0; s_pointSP = 0;
g_ptStart.x = g_ptStart.y = g_ptEnd.x = g_ptEnd.y = -1; g_ptEnd = g_ptStart = { -1, -1 };
selectionModel.ResetPtStack(); selectionModel.ResetPtStack();
if (selectionModel.m_bShow) if (selectionModel.m_bShow)
{ {
selectionModel.Landing(); selectionModel.Landing();
@ -93,6 +115,24 @@ void ToolBase::OnDrawSelectionOnCanvas(HDC hdc)
drawSizeBoxes(hdc, &rcSelection, TRUE); drawSizeBoxes(hdc, &rcSelection, TRUE);
} }
void ToolBase::pushToPtStack(LONG x, LONG y)
{
if (s_pointSP >= s_maxPointSP)
{
SIZE_T newMax = s_maxPointSP + 512;
SIZE_T cbNew = newMax * sizeof(POINT);
if (!s_pointStack.ReallocateBytes(cbNew))
{
ATLTRACE("%d, %d, %d\n", (INT)s_pointSP, (INT)s_maxPointSP, (INT)cbNew);
return;
}
s_maxPointSP = newMax;
}
s_pointStack[s_pointSP++] = { x, y };
}
/* TOOLS ********************************************************/ /* TOOLS ********************************************************/
// TOOL_FREESEL // TOOL_FREESEL
@ -410,43 +450,43 @@ struct SmoothDrawTool : ToolBase
{ {
DIRECTION m_direction = NO_DIRECTION; DIRECTION m_direction = NO_DIRECTION;
BOOL m_bShiftDown = FALSE; BOOL m_bShiftDown = FALSE;
BOOL m_bLeftButton = FALSE;
SmoothDrawTool(TOOLTYPE type) : ToolBase(type) SmoothDrawTool(TOOLTYPE type) : ToolBase(type)
{ {
} }
virtual void draw(BOOL bLeftButton, LONG x, LONG y) = 0; virtual void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) = 0;
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
{ {
m_direction = NO_DIRECTION; m_direction = NO_DIRECTION;
imageModel.PushImageForUndo();
imageModel.NotifyImageChanged();
m_bShiftDown = (::GetKeyState(VK_SHIFT) & 0x8000); // Is Shift key pressed? m_bShiftDown = (::GetKeyState(VK_SHIFT) & 0x8000); // Is Shift key pressed?
m_bLeftButton = bLeftButton;
s_pointSP = 0;
pushToPtStack(x, y);
pushToPtStack(x, y); // We have to draw the first point
imageModel.NotifyImageChanged();
} }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
if (m_bShiftDown) if (!m_bShiftDown)
{ {
if (m_direction == NO_DIRECTION) pushToPtStack(x, y);
{ imageModel.NotifyImageChanged();
m_direction = GetDirection(g_ptStart.x, g_ptStart.y, x, y);
if (m_direction == NO_DIRECTION)
return FALSE;
}
RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
}
else
{
draw(bLeftButton, x, y);
g_ptStart.x = g_ptEnd.x = x;
g_ptStart.y = g_ptEnd.y = y;
return TRUE; return TRUE;
} }
draw(bLeftButton, x, y); if (m_direction == NO_DIRECTION)
{
m_direction = GetDirection(g_ptStart.x, g_ptStart.y, x, y);
if (m_direction == NO_DIRECTION)
return FALSE;
}
RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
pushToPtStack(x, y);
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
return TRUE; return TRUE;
} }
@ -454,24 +494,30 @@ struct SmoothDrawTool : ToolBase
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
if (m_bShiftDown && m_direction != NO_DIRECTION) if (m_bShiftDown && m_direction != NO_DIRECTION)
{
RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y); RestrictDrawDirection(m_direction, g_ptStart.x, g_ptStart.y, x, y);
}
draw(bLeftButton, x, y); pushToPtStack(x, y);
CRect rcPartial;
getBoundaryOfPtStack(rcPartial, s_pointSP, s_pointStack);
SIZE size = toolsModel.GetToolSize();
rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
imageModel.PushImageForUndo(rcPartial);
OnDrawOverlayOnImage(m_hdc);
imageModel.NotifyImageChanged();
OnEndDraw(FALSE); OnEndDraw(FALSE);
return TRUE; return TRUE;
} }
void OnEndDraw(BOOL bCancel) override void OnDrawOverlayOnImage(HDC hdc) override
{ {
if (bCancel) for (SIZE_T i = 1; i < s_pointSP; ++i)
{ {
LONG x = 0, y = 0; OnDraw(hdc, m_bLeftButton, s_pointStack[i - 1], s_pointStack[i]);
OnButtonUp(FALSE, x, y);
imageModel.Undo(TRUE);
} }
ToolBase::OnEndDraw(bCancel);
} }
}; };
@ -482,12 +528,12 @@ struct RubberTool : SmoothDrawTool
{ {
} }
void draw(BOOL bLeftButton, LONG x, LONG y) override void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
{ {
if (bLeftButton) if (bLeftButton)
Erase(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_bg, toolsModel.GetRubberRadius()); Erase(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_bg, toolsModel.GetRubberRadius());
else else
Replace(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, m_fg, m_bg, toolsModel.GetRubberRadius()); Replace(hdc, pt0.x, pt0.y, pt1.x, pt1.y, m_fg, m_bg, toolsModel.GetRubberRadius());
} }
void OnSpecialTweak(BOOL bMinus) override void OnSpecialTweak(BOOL bMinus) override
@ -617,10 +663,10 @@ struct PenTool : SmoothDrawTool
{ {
} }
void draw(BOOL bLeftButton, LONG x, LONG y) override void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
{ {
COLORREF rgb = bLeftButton ? m_fg : m_bg; COLORREF rgb = bLeftButton ? m_fg : m_bg;
Line(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, toolsModel.GetPenWidth()); Line(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetPenWidth());
} }
void OnSpecialTweak(BOOL bMinus) override void OnSpecialTweak(BOOL bMinus) override
@ -636,10 +682,10 @@ struct BrushTool : SmoothDrawTool
{ {
} }
void draw(BOOL bLeftButton, LONG x, LONG y) override void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
{ {
COLORREF rgb = bLeftButton ? m_fg : m_bg; COLORREF rgb = bLeftButton ? m_fg : m_bg;
Brush(m_hdc, g_ptEnd.x, g_ptEnd.y, x, y, rgb, toolsModel.GetBrushStyle(), Brush(hdc, pt0.x, pt0.y, pt1.x, pt1.y, rgb, toolsModel.GetBrushStyle(),
toolsModel.GetBrushWidth()); toolsModel.GetBrushWidth());
} }
@ -652,14 +698,28 @@ struct BrushTool : SmoothDrawTool
// TOOL_AIRBRUSH // TOOL_AIRBRUSH
struct AirBrushTool : SmoothDrawTool struct AirBrushTool : SmoothDrawTool
{ {
DWORD m_dwTick = 0;
AirBrushTool() : SmoothDrawTool(TOOL_AIRBRUSH) AirBrushTool() : SmoothDrawTool(TOOL_AIRBRUSH)
{ {
} }
void draw(BOOL bLeftButton, LONG x, LONG y) override void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
{
m_dwTick = GetTickCount();
SmoothDrawTool::OnButtonDown(bLeftButton, x, y, bDoubleClick);
}
void OnDrawOverlayOnImage(HDC hdc) override
{
srand(m_dwTick);
SmoothDrawTool::OnDrawOverlayOnImage(hdc);
}
void OnDraw(HDC hdc, BOOL bLeftButton, POINT pt0, POINT pt1) override
{ {
COLORREF rgb = bLeftButton ? m_fg : m_bg; COLORREF rgb = bLeftButton ? m_fg : m_bg;
Airbrush(m_hdc, x, y, rgb, toolsModel.GetAirBrushRadius()); Airbrush(hdc, pt1.x, pt1.y, rgb, toolsModel.GetAirBrushRadius());
} }
void OnSpecialTweak(BOOL bMinus) override void OnSpecialTweak(BOOL bMinus) override
@ -823,7 +883,6 @@ struct LineTool : TwoPointDrawTool
struct BezierTool : ToolBase struct BezierTool : ToolBase
{ {
BOOL m_bLeftButton = FALSE; BOOL m_bLeftButton = FALSE;
BOOL m_bDrawing = FALSE;
BezierTool() : ToolBase(TOOL_BEZIER) BezierTool() : ToolBase(TOOL_BEZIER)
{ {
@ -831,20 +890,17 @@ struct BezierTool : ToolBase
void OnDrawOverlayOnImage(HDC hdc) void OnDrawOverlayOnImage(HDC hdc)
{ {
if (!m_bDrawing)
return;
COLORREF rgb = (m_bLeftButton ? m_fg : m_bg); COLORREF rgb = (m_bLeftButton ? m_fg : m_bg);
switch (s_pointSP) switch (s_pointSP)
{ {
case 1: case 2:
Line(hdc, s_pointStack[0].x, s_pointStack[0].y, s_pointStack[1].x, s_pointStack[1].y, rgb, Line(hdc, s_pointStack[0].x, s_pointStack[0].y, s_pointStack[1].x, s_pointStack[1].y, rgb,
toolsModel.GetLineWidth()); toolsModel.GetLineWidth());
break; break;
case 2: case 3:
Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[2], s_pointStack[1], rgb, toolsModel.GetLineWidth()); Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[2], s_pointStack[1], rgb, toolsModel.GetLineWidth());
break; break;
case 3: case 4:
Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[3], s_pointStack[1], rgb, toolsModel.GetLineWidth()); Bezier(hdc, s_pointStack[0], s_pointStack[2], s_pointStack[3], s_pointStack[1], rgb, toolsModel.GetLineWidth());
break; break;
} }
@ -854,18 +910,14 @@ struct BezierTool : ToolBase
{ {
m_bLeftButton = bLeftButton; m_bLeftButton = bLeftButton;
if (!m_bDrawing) if (s_pointSP == 0)
{ {
m_bDrawing = TRUE; pushToPtStack(x, y);
s_pointStack[s_pointSP].x = s_pointStack[s_pointSP + 1].x = x; pushToPtStack(x, y);
s_pointStack[s_pointSP].y = s_pointStack[s_pointSP + 1].y = y;
++s_pointSP;
} }
else else
{ {
++s_pointSP; s_pointStack[s_pointSP - 1] = { x, y };
s_pointStack[s_pointSP].x = x;
s_pointStack[s_pointSP].y = y;
} }
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
@ -873,33 +925,32 @@ struct BezierTool : ToolBase
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
s_pointStack[s_pointSP].x = x; if (s_pointSP > 0)
s_pointStack[s_pointSP].y = y; s_pointStack[s_pointSP - 1] = { x, y };
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
return TRUE; return TRUE;
} }
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
s_pointStack[s_pointSP].x = x; if (s_pointSP >= 4)
s_pointStack[s_pointSP].y = y;
if (s_pointSP >= 3)
{ {
OnEndDraw(FALSE); OnEndDraw(FALSE);
return TRUE; return TRUE;
} }
pushToPtStack(x, y);
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
return TRUE; return TRUE;
} }
void OnEndDraw(BOOL bCancel) override void OnEndDraw(BOOL bCancel) override
{ {
if (!bCancel) if (!bCancel && s_pointSP > 1)
{ {
// FIXME: I couldn't calculate boundary rectangle from Bezier curve
imageModel.PushImageForUndo(); imageModel.PushImageForUndo();
OnDrawOverlayOnImage(m_hdc); OnDrawOverlayOnImage(m_hdc);
} }
m_bDrawing = FALSE;
ToolBase::OnEndDraw(bCancel); ToolBase::OnEndDraw(bCancel);
} }
@ -945,9 +996,9 @@ struct ShapeTool : ToolBase
return; return;
if (m_bLeftButton) if (m_bLeftButton)
Poly(hdc, s_pointStack, s_pointSP + 1, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); Poly(hdc, s_pointStack, s_pointSP, m_fg, m_bg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
else else
Poly(hdc, s_pointStack, s_pointSP + 1, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE); Poly(hdc, s_pointStack, s_pointSP, m_bg, m_fg, toolsModel.GetLineWidth(), toolsModel.GetShapeStyle(), m_bClosed, FALSE);
} }
void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override void OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) override
@ -958,32 +1009,29 @@ struct ShapeTool : ToolBase
if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0))
roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y);
s_pointStack[s_pointSP].x = x; pushToPtStack(x, y);
s_pointStack[s_pointSP].y = y;
if (s_pointSP && bDoubleClick) if (s_pointSP > 1 && bDoubleClick)
{ {
OnEndDraw(FALSE); OnEndDraw(FALSE);
return; return;
} }
if (s_pointSP == 0) if (s_pointSP == 1)
{ pushToPtStack(x, y); // We have to draw the first point
s_pointSP++;
s_pointStack[s_pointSP].x = x;
s_pointStack[s_pointSP].y = y;
}
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
} }
BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnMouseMove(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) if (s_pointSP > 1)
roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); {
if (GetAsyncKeyState(VK_SHIFT) < 0)
roundTo8Directions(s_pointStack[s_pointSP - 2].x, s_pointStack[s_pointSP - 2].y, x, y);
s_pointStack[s_pointSP].x = x; s_pointStack[s_pointSP - 1] = { x, y };
s_pointStack[s_pointSP].y = y; }
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
return TRUE; return TRUE;
@ -991,8 +1039,8 @@ struct ShapeTool : ToolBase
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
{ {
if ((s_pointSP > 0) && (GetAsyncKeyState(VK_SHIFT) < 0)) if ((s_pointSP > 1) && (GetAsyncKeyState(VK_SHIFT) < 0))
roundTo8Directions(s_pointStack[s_pointSP - 1].x, s_pointStack[s_pointSP - 1].y, x, y); roundTo8Directions(s_pointStack[s_pointSP - 2].x, s_pointStack[s_pointSP - 2].y, x, y);
m_bClosed = FALSE; m_bClosed = FALSE;
if (nearlyEqualPoints(x, y, s_pointStack[0].x, s_pointStack[0].y)) if (nearlyEqualPoints(x, y, s_pointStack[0].x, s_pointStack[0].y))
@ -1000,35 +1048,28 @@ struct ShapeTool : ToolBase
OnEndDraw(FALSE); OnEndDraw(FALSE);
return TRUE; return TRUE;
} }
else
{
s_pointSP++;
s_pointStack[s_pointSP].x = x;
s_pointStack[s_pointSP].y = y;
}
if (s_pointSP == _countof(s_pointStack))
s_pointSP--;
pushToPtStack(x, y);
imageModel.NotifyImageChanged(); imageModel.NotifyImageChanged();
return TRUE; return TRUE;
} }
void OnEndDraw(BOOL bCancel) override void OnEndDraw(BOOL bCancel) override
{ {
if (!bCancel) if (!bCancel && s_pointSP > 1)
{ {
if (s_pointSP) CRect rcPartial;
{ getBoundaryOfPtStack(rcPartial, s_pointSP, s_pointStack);
--s_pointSP;
m_bClosed = TRUE;
imageModel.PushImageForUndo(); SIZE size = toolsModel.GetToolSize();
OnDrawOverlayOnImage(m_hdc); rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
}
m_bClosed = FALSE; imageModel.PushImageForUndo(rcPartial);
s_pointSP = 0;
m_bClosed = TRUE;
OnDrawOverlayOnImage(m_hdc);
} }
m_bClosed = FALSE;
ToolBase::OnEndDraw(bCancel); ToolBase::OnEndDraw(bCancel);
} }
@ -1107,8 +1148,7 @@ ToolBase::createToolObject(TOOLTYPE type)
void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick) void ToolsModel::OnButtonDown(BOOL bLeftButton, LONG x, LONG y, BOOL bDoubleClick)
{ {
m_pToolObject->beginEvent(); m_pToolObject->beginEvent();
g_ptStart.x = g_ptEnd.x = x; g_ptEnd = g_ptStart = { x, y };
g_ptStart.y = g_ptEnd.y = y;
m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick); m_pToolObject->OnButtonDown(bLeftButton, x, y, bDoubleClick);
m_pToolObject->endEvent(); m_pToolObject->endEvent();
} }
@ -1117,10 +1157,8 @@ void ToolsModel::OnMouseMove(BOOL bLeftButton, LONG x, LONG y)
{ {
m_pToolObject->beginEvent(); m_pToolObject->beginEvent();
if (m_pToolObject->OnMouseMove(bLeftButton, x, y)) if (m_pToolObject->OnMouseMove(bLeftButton, x, y))
{ g_ptEnd = { x, y };
g_ptEnd.x = x;
g_ptEnd.y = y;
}
m_pToolObject->endEvent(); m_pToolObject->endEvent();
} }
@ -1128,10 +1166,8 @@ void ToolsModel::OnButtonUp(BOOL bLeftButton, LONG x, LONG y)
{ {
m_pToolObject->beginEvent(); m_pToolObject->beginEvent();
if (m_pToolObject->OnButtonUp(bLeftButton, x, y)) if (m_pToolObject->OnButtonUp(bLeftButton, x, y))
{ g_ptEnd = { x, y };
g_ptEnd.x = x;
g_ptEnd.y = y;
}
m_pToolObject->endEvent(); m_pToolObject->endEvent();
} }

View file

@ -23,7 +23,7 @@ SelectionModel::SelectionModel()
{ {
::SetRectEmpty(&m_rc); ::SetRectEmpty(&m_rc);
::SetRectEmpty(&m_rcOld); ::SetRectEmpty(&m_rcOld);
m_ptHit.x = m_ptHit.y = -1; m_ptHit = { -1, -1 };
} }
SelectionModel::~SelectionModel() SelectionModel::~SelectionModel()
@ -71,17 +71,8 @@ void SelectionModel::ShiftPtStack(INT dx, INT dy)
void SelectionModel::BuildMaskFromPtStack() void SelectionModel::BuildMaskFromPtStack()
{ {
CRect rc = { MAXLONG, MAXLONG, 0, 0 }; CRect rc;
for (INT i = 0; i < m_iPtSP; ++i) getBoundaryOfPtStack(rc, m_iPtSP, m_ptStack);
{
POINT& pt = m_ptStack[i];
rc.left = min(pt.x, rc.left);
rc.top = min(pt.y, rc.top);
rc.right = max(pt.x, rc.right);
rc.bottom = max(pt.y, rc.bottom);
}
rc.right += 1;
rc.bottom += 1;
m_rc = m_rcOld = rc; m_rc = m_rcOld = rc;

View file

@ -43,8 +43,7 @@ struct ToolBase
TOOLTYPE m_tool; TOOLTYPE m_tool;
HDC m_hdc; HDC m_hdc;
COLORREF m_fg, m_bg; COLORREF m_fg, m_bg;
static INT s_pointSP; static SIZE_T s_pointSP;
static POINT s_pointStack[256];
ToolBase(TOOLTYPE tool) : m_tool(tool), m_hdc(NULL) { } ToolBase(TOOLTYPE tool) : m_tool(tool), m_hdc(NULL) { }
virtual ~ToolBase() { } virtual ~ToolBase() { }
@ -63,6 +62,7 @@ struct ToolBase
void beginEvent(); void beginEvent();
void endEvent(); void endEvent();
void reset(); void reset();
void pushToPtStack(LONG x, LONG y);
static ToolBase* createToolObject(TOOLTYPE type); static ToolBase* createToolObject(TOOLTYPE type);
@ -165,28 +165,20 @@ static inline int UnZoomed(int xy)
static inline void Zoomed(POINT& pt) static inline void Zoomed(POINT& pt)
{ {
pt.x = Zoomed(pt.x); pt = { Zoomed(pt.x), Zoomed(pt.y) };
pt.y = Zoomed(pt.y);
} }
static inline void Zoomed(RECT& rc) static inline void Zoomed(RECT& rc)
{ {
rc.left = Zoomed(rc.left); rc = { Zoomed(rc.left), Zoomed(rc.top), Zoomed(rc.right), Zoomed(rc.bottom) };
rc.top = Zoomed(rc.top);
rc.right = Zoomed(rc.right);
rc.bottom = Zoomed(rc.bottom);
} }
static inline void UnZoomed(POINT& pt) static inline void UnZoomed(POINT& pt)
{ {
pt.x = UnZoomed(pt.x); pt = { UnZoomed(pt.x), UnZoomed(pt.y) };
pt.y = UnZoomed(pt.y);
} }
static inline void UnZoomed(RECT& rc) static inline void UnZoomed(RECT& rc)
{ {
rc.left = UnZoomed(rc.left); rc = { UnZoomed(rc.left), UnZoomed(rc.top), UnZoomed(rc.right), UnZoomed(rc.bottom) };
rc.top = UnZoomed(rc.top);
rc.right = UnZoomed(rc.right);
rc.bottom = UnZoomed(rc.bottom);
} }

View file

@ -498,9 +498,8 @@ LRESULT CMainWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHand
LRESULT CMainWindow::OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT CMainWindow::OnGetMinMaxInfo(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ {
MINMAXINFO *mm = (LPMINMAXINFO) lParam; MINMAXINFO *mm = (MINMAXINFO*)lParam;
mm->ptMinTrackSize.x = 330; mm->ptMinTrackSize = { 330, 360 };
mm->ptMinTrackSize.y = 360;
return 0; return 0;
} }