mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
[MSPAINT] Introduce partial image history (#5994)
- Add IMAGE_PART structure and use as history items. - Overload ImageModel::PushImageForUndo(const RECT& rcPartial). - Add ToolsModel::GetToolSize. - Implement partial image history on TwoPointDrawTool. CORE-19094
This commit is contained in:
parent
416d63026e
commit
72081168fb
5 changed files with 118 additions and 18 deletions
|
@ -12,6 +12,14 @@ ImageModel imageModel;
|
|||
|
||||
/* FUNCTIONS ********************************************************/
|
||||
|
||||
void IMAGE_PART::clear()
|
||||
{
|
||||
::DeleteObject(m_hbmImage);
|
||||
m_hbmImage = NULL;
|
||||
m_rcPart.SetRectEmpty();
|
||||
m_bPartial = FALSE;
|
||||
}
|
||||
|
||||
void ImageModel::NotifyImageChanged()
|
||||
{
|
||||
if (canvasWindow.IsWindow())
|
||||
|
@ -30,7 +38,7 @@ ImageModel::ImageModel()
|
|||
, m_undoSteps(0)
|
||||
, m_redoSteps(0)
|
||||
{
|
||||
ZeroMemory(m_hBms, sizeof(m_hBms));
|
||||
ZeroMemory(m_historyItems, sizeof(m_historyItems));
|
||||
|
||||
m_hbmMaster = CreateColorDIB(1, 1, RGB(255, 255, 255));
|
||||
m_hbmOld = ::SelectObject(m_hDrawingDC, m_hbmMaster);
|
||||
|
@ -46,6 +54,23 @@ ImageModel::~ImageModel()
|
|||
ClearHistory();
|
||||
}
|
||||
|
||||
void ImageModel::SwapPart()
|
||||
{
|
||||
IMAGE_PART& part = m_historyItems[m_currInd];
|
||||
if (!part.m_bPartial)
|
||||
{
|
||||
Swap(m_hbmMaster, part.m_hbmImage);
|
||||
return;
|
||||
}
|
||||
|
||||
HBITMAP hbmMaster = LockBitmap();
|
||||
HBITMAP hbmPart = getSubImage(hbmMaster, part.m_rcPart);
|
||||
putSubImage(hbmMaster, part.m_rcPart, part.m_hbmImage);
|
||||
::DeleteObject(part.m_hbmImage);
|
||||
part.m_hbmImage = hbmPart;
|
||||
UnlockBitmap(hbmMaster);
|
||||
}
|
||||
|
||||
void ImageModel::Undo(BOOL bClearRedo)
|
||||
{
|
||||
ATLTRACE("%s: %d\n", __FUNCTION__, m_undoSteps);
|
||||
|
@ -55,11 +80,8 @@ void ImageModel::Undo(BOOL bClearRedo)
|
|||
selectionModel.HideSelection();
|
||||
|
||||
m_currInd = (m_currInd + HISTORYSIZE - 1) % HISTORYSIZE; // Go previous
|
||||
|
||||
ATLASSERT(m_hbmMaster != NULL);
|
||||
ATLASSERT(m_hBms[m_currInd] != NULL);
|
||||
|
||||
Swap(m_hbmMaster, m_hBms[m_currInd]);
|
||||
SwapPart();
|
||||
::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select
|
||||
|
||||
m_undoSteps--;
|
||||
|
@ -80,9 +102,7 @@ void ImageModel::Redo()
|
|||
selectionModel.HideSelection();
|
||||
|
||||
ATLASSERT(m_hbmMaster != NULL);
|
||||
ATLASSERT(m_hBms[m_currInd] != NULL);
|
||||
|
||||
Swap(m_hbmMaster, m_hBms[m_currInd]);
|
||||
SwapPart();
|
||||
m_currInd = (m_currInd + 1) % HISTORYSIZE; // Go next
|
||||
::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select
|
||||
|
||||
|
@ -97,11 +117,7 @@ void ImageModel::ClearHistory()
|
|||
{
|
||||
for (int i = 0; i < HISTORYSIZE; ++i)
|
||||
{
|
||||
if (m_hBms[i])
|
||||
{
|
||||
::DeleteObject(m_hBms[i]);
|
||||
m_hBms[i] = NULL;
|
||||
}
|
||||
m_historyItems[i].clear();
|
||||
}
|
||||
|
||||
m_undoSteps = 0;
|
||||
|
@ -130,12 +146,35 @@ void ImageModel::PushImageForUndo(HBITMAP hbm)
|
|||
return;
|
||||
}
|
||||
|
||||
::DeleteObject(m_hBms[m_currInd]);
|
||||
m_hBms[m_currInd] = m_hbmMaster;
|
||||
IMAGE_PART& part = m_historyItems[m_currInd];
|
||||
part.clear();
|
||||
part.m_hbmImage = m_hbmMaster;
|
||||
m_hbmMaster = hbm;
|
||||
m_currInd = (m_currInd + 1) % HISTORYSIZE; // Go next
|
||||
::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select
|
||||
|
||||
PushDone();
|
||||
}
|
||||
|
||||
void ImageModel::PushImageForUndo(const RECT& rcPartial)
|
||||
{
|
||||
ATLTRACE("%s: %d\n", __FUNCTION__, m_currInd);
|
||||
|
||||
IMAGE_PART& part = m_historyItems[m_currInd];
|
||||
part.clear();
|
||||
part.m_bPartial = TRUE;
|
||||
part.m_rcPart = rcPartial;
|
||||
|
||||
HBITMAP hbmMaster = LockBitmap();
|
||||
part.m_hbmImage = getSubImage(hbmMaster, rcPartial);
|
||||
UnlockBitmap(hbmMaster);
|
||||
|
||||
PushDone();
|
||||
}
|
||||
|
||||
void ImageModel::PushDone()
|
||||
{
|
||||
m_currInd = (m_currInd + 1) % HISTORYSIZE; // Go next
|
||||
|
||||
if (m_undoSteps < HISTORYSIZE - 1)
|
||||
m_undoSteps++;
|
||||
m_redoSteps = 0;
|
||||
|
|
|
@ -10,6 +10,15 @@
|
|||
/* HISTORYSIZE = number of possible undo-steps + 1 */
|
||||
#define HISTORYSIZE 11
|
||||
|
||||
struct IMAGE_PART
|
||||
{
|
||||
CRect m_rcPart;
|
||||
HBITMAP m_hbmImage;
|
||||
BOOL m_bPartial;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
class ImageModel
|
||||
{
|
||||
public:
|
||||
|
@ -21,6 +30,7 @@ public:
|
|||
BOOL CanRedo() const { return m_redoSteps > 0; }
|
||||
void PushImageForUndo();
|
||||
void PushImageForUndo(HBITMAP hbm);
|
||||
void PushImageForUndo(const RECT& rcPartial);
|
||||
void Undo(BOOL bClearRedo = FALSE);
|
||||
void Redo(void);
|
||||
void ClearHistory(void);
|
||||
|
@ -49,6 +59,9 @@ protected:
|
|||
int m_currInd; // The current index in m_hBms
|
||||
int m_undoSteps; // The undo-able count
|
||||
int m_redoSteps; // The redo-able count
|
||||
HBITMAP m_hBms[HISTORYSIZE]; // A rotation buffer of HBITMAPs
|
||||
IMAGE_PART m_historyItems[HISTORYSIZE]; // A ring buffer of IMAGE_PARTs
|
||||
HGDIOBJ m_hbmOld;
|
||||
|
||||
void SwapPart();
|
||||
void PushDone();
|
||||
};
|
||||
|
|
|
@ -291,7 +291,12 @@ struct TwoPointDrawTool : ToolBase
|
|||
|
||||
BOOL OnButtonUp(BOOL bLeftButton, LONG& x, LONG& y) override
|
||||
{
|
||||
imageModel.PushImageForUndo();
|
||||
CRect rcPartial(g_ptStart, g_ptEnd);
|
||||
rcPartial.NormalizeRect();
|
||||
SIZE size = toolsModel.GetToolSize();
|
||||
rcPartial.InflateRect((size.cx + 1) / 2, (size.cy + 1) / 2);
|
||||
imageModel.PushImageForUndo(rcPartial);
|
||||
|
||||
OnDrawOverlayOnImage(m_hdc);
|
||||
m_bDrawing = FALSE;
|
||||
imageModel.NotifyImageChanged();
|
||||
|
|
|
@ -202,6 +202,47 @@ void ToolsModel::SetRubberRadius(int nRubberRadius)
|
|||
NotifyToolSettingsChanged();
|
||||
}
|
||||
|
||||
SIZE ToolsModel::GetToolSize() const
|
||||
{
|
||||
SIZE size;
|
||||
switch (m_activeTool)
|
||||
{
|
||||
case TOOL_FREESEL:
|
||||
case TOOL_RECTSEL:
|
||||
case TOOL_COLOR:
|
||||
case TOOL_ZOOM:
|
||||
case TOOL_TEXT:
|
||||
case TOOL_FILL:
|
||||
size.cx = size.cy = 1;
|
||||
break;
|
||||
case TOOL_LINE:
|
||||
case TOOL_BEZIER:
|
||||
case TOOL_RECT:
|
||||
case TOOL_RRECT:
|
||||
case TOOL_SHAPE:
|
||||
case TOOL_ELLIPSE:
|
||||
size.cx = size.cy = GetLineWidth();
|
||||
break;
|
||||
case TOOL_AIRBRUSH:
|
||||
size.cx = size.cy = GetAirBrushRadius() * 2;
|
||||
break;
|
||||
case TOOL_RUBBER:
|
||||
size.cx = size.cy = GetRubberRadius() * 2;
|
||||
break;
|
||||
case TOOL_BRUSH:
|
||||
size.cx = size.cy = GetBrushWidth();
|
||||
break;
|
||||
case TOOL_PEN:
|
||||
size.cx = size.cy = GetPenWidth();
|
||||
break;
|
||||
}
|
||||
if (size.cx < 1)
|
||||
size.cx = 1;
|
||||
if (size.cy < 1)
|
||||
size.cy = 1;
|
||||
return size;
|
||||
}
|
||||
|
||||
BOOL ToolsModel::IsBackgroundTransparent() const
|
||||
{
|
||||
return m_transpBg;
|
||||
|
|
|
@ -124,6 +124,8 @@ public:
|
|||
void SetRubberRadius(int nRubberRadius);
|
||||
void MakeRubberThickerOrThinner(BOOL bThinner);
|
||||
|
||||
SIZE GetToolSize() const;
|
||||
|
||||
BOOL IsBackgroundTransparent() const;
|
||||
void SetBackgroundTransparent(BOOL bTransparent);
|
||||
|
||||
|
|
Loading…
Reference in a new issue