mirror of
https://github.com/reactos/reactos.git
synced 2025-06-05 09:20:30 +00:00
[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:
parent
5b5aaf6687
commit
e579220098
7 changed files with 163 additions and 153 deletions
|
@ -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;
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue