https://git.reactos.org/?p=reactos.git;a=commitdiff;h=72081168fb0f8e279ba24eaab601fee4525a71d7

commit 72081168fb0f8e279ba24eaab601fee4525a71d7
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Sun Nov 19 12:59:39 2023 +0900
Commit:     GitHub <[email protected]>
CommitDate: Sun Nov 19 12:59:39 2023 +0900

    [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
---
 base/applications/mspaint/history.cpp    | 71 +++++++++++++++++++++++++-------
 base/applications/mspaint/history.h      | 15 ++++++-
 base/applications/mspaint/mouse.cpp      |  7 +++-
 base/applications/mspaint/toolsmodel.cpp | 41 ++++++++++++++++++
 base/applications/mspaint/toolsmodel.h   |  2 +
 5 files changed, 118 insertions(+), 18 deletions(-)

diff --git a/base/applications/mspaint/history.cpp 
b/base/applications/mspaint/history.cpp
index c0f347d0aa9..2f7480b5f45 100644
--- a/base/applications/mspaint/history.cpp
+++ b/base/applications/mspaint/history.cpp
@@ -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;
diff --git a/base/applications/mspaint/history.h 
b/base/applications/mspaint/history.h
index e827a430b35..87c98515136 100644
--- a/base/applications/mspaint/history.h
+++ b/base/applications/mspaint/history.h
@@ -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();
 };
diff --git a/base/applications/mspaint/mouse.cpp 
b/base/applications/mspaint/mouse.cpp
index d650259f6cb..2aa17872c81 100644
--- a/base/applications/mspaint/mouse.cpp
+++ b/base/applications/mspaint/mouse.cpp
@@ -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();
diff --git a/base/applications/mspaint/toolsmodel.cpp 
b/base/applications/mspaint/toolsmodel.cpp
index 42ee980c2b3..c19a1152c24 100644
--- a/base/applications/mspaint/toolsmodel.cpp
+++ b/base/applications/mspaint/toolsmodel.cpp
@@ -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;
diff --git a/base/applications/mspaint/toolsmodel.h 
b/base/applications/mspaint/toolsmodel.h
index a5737010d9b..1bc5ee256c0 100644
--- a/base/applications/mspaint/toolsmodel.h
+++ b/base/applications/mspaint/toolsmodel.h
@@ -124,6 +124,8 @@ public:
     void SetRubberRadius(int nRubberRadius);
     void MakeRubberThickerOrThinner(BOOL bThinner);
 
+    SIZE GetToolSize() const;
+
     BOOL IsBackgroundTransparent() const;
     void SetBackgroundTransparent(BOOL bTransparent);
 

Reply via email to