[MSPAINT] Improve behaviour on memory shortage (#7780)

Improve UX on memory shortage.
JIRA issue: CORE-20020
- Clear history on memory shortage.
- Improve CCanvasWindow::OnPaint on memory
  shortage.
- Add null check in CCanvasWindow::DoDraw.
- Retire using of cache in CCanvasWindow.
This commit is contained in:
Katayama Hirofumi MZ 2025-03-25 10:45:23 +09:00 committed by GitHub
parent c930c8c464
commit 9563c07146
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 47 additions and 18 deletions

View file

@ -16,16 +16,11 @@ CCanvasWindow::CCanvasWindow()
, m_hitCanvasSizeBox(HIT_NONE)
, m_ptOrig { -1, -1 }
{
m_ahbmCached[0] = m_ahbmCached[1] = NULL;
m_rcResizing.SetRectEmpty();
}
CCanvasWindow::~CCanvasWindow()
{
if (m_ahbmCached[0])
::DeleteObject(m_ahbmCached[0]);
if (m_ahbmCached[1])
::DeleteObject(m_ahbmCached[1]);
}
RECT CCanvasWindow::GetBaseRect()
@ -119,16 +114,30 @@ VOID CCanvasWindow::zoomTo(INT newZoom, LONG left, LONG top)
Invalidate(TRUE);
}
VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
BOOL CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
{
// This is the target area we have to draw on
CRect rcCanvasDraw;
rcCanvasDraw.IntersectRect(&rcClient, &rcPaint);
// Calculate image size
CRect rcImage;
GetImageRect(rcImage);
SIZE sizeImage = { imageModel.GetWidth(), imageModel.GetHeight() };
// We use a memory bitmap to reduce flickering
HBITMAP hbmCache1 = CreateDIBWithProperties(rcClient.right, rcClient.bottom);
if (!hbmCache1)
return FALSE; // Out of memory
HBITMAP hbmCache2 = CreateDIBWithProperties(sizeImage.cx, sizeImage.cy);
if (!hbmCache2)
{
::DeleteObject(hbmCache1);
return FALSE; // Out of memory
}
HDC hdcMem0 = ::CreateCompatibleDC(hDC);
m_ahbmCached[0] = CachedBufferDIB(m_ahbmCached[0], rcClient.right, rcClient.bottom);
HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, m_ahbmCached[0]);
HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, hbmCache1);
// Fill the background on hdcMem0
::FillRect(hdcMem0, &rcCanvasDraw, (HBRUSH)(COLOR_APPWORKSPACE + 1));
@ -138,11 +147,6 @@ VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
if (!selectionModel.m_bShow && !::IsWindowVisible(textEditWindow))
drawSizeBoxes(hdcMem0, &rcBase, FALSE, &rcCanvasDraw);
// Calculate image size
CRect rcImage;
GetImageRect(rcImage);
SIZE sizeImage = { imageModel.GetWidth(), imageModel.GetHeight() };
// Calculate the target area on the image
CRect rcImageDraw = rcCanvasDraw;
CanvasToImage(rcImageDraw);
@ -154,8 +158,7 @@ VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
// hdcMem1 <-- imageModel
HDC hdcMem1 = ::CreateCompatibleDC(hDC);
m_ahbmCached[1] = CachedBufferDIB(m_ahbmCached[1], sizeImage.cx, sizeImage.cy);
HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, m_ahbmCached[1]);
HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, hbmCache2);
::BitBlt(hdcMem1, rcImageDraw.left, rcImageDraw.top, rcImageDraw.Width(), rcImageDraw.Height(),
imageModel.GetDC(), rcImageDraw.left, rcImageDraw.top, SRCCOPY);
@ -208,6 +211,10 @@ VOID CCanvasWindow::DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint)
// Clean up hdcMem0
::SelectObject(hdcMem0, hbm0Old);
::DeleteDC(hdcMem0);
::DeleteObject(hbmCache2);
::DeleteObject(hbmCache1);
return TRUE;
}
VOID CCanvasWindow::updateScrollRange()
@ -691,9 +698,22 @@ LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
RECT rcClient;
GetClientRect(&rcClient);
static BOOL s_bShowedOutOfMemory = FALSE; // Don't show "Out Of Memory" message multiple time
PAINTSTRUCT ps;
HDC hDC = BeginPaint(&ps);
DoDraw(hDC, rcClient, ps.rcPaint);
if (DoDraw(hDC, rcClient, ps.rcPaint))
{
s_bShowedOutOfMemory = FALSE;
}
else if (!s_bShowedOutOfMemory)
{
ShowOutOfMemory();
s_bShowedOutOfMemory = TRUE;
imageModel.ClearHistory(); // Reduce memory usage
}
EndPaint(&ps);
return 0;
}

View file

@ -57,12 +57,11 @@ public:
protected:
HITTEST m_hitCanvasSizeBox;
POINT m_ptOrig; // The origin of drag start
HBITMAP m_ahbmCached[2]; // The cached buffer bitmaps
CRect m_rcResizing; // Resizing rectagle
HITTEST CanvasHitTest(POINT pt);
RECT GetBaseRect();
VOID DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint);
BOOL DoDraw(HDC hDC, RECT& rcClient, RECT& rcPaint);
VOID OnHVScroll(WPARAM wParam, INT fnBar);
LRESULT OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

View file

@ -230,6 +230,7 @@ HBITMAP InitializeImage(LPCWSTR name, LPWIN32_FIND_DATAW pFound, BOOL isFile)
if (hBitmap == NULL)
{
ShowOutOfMemory();
imageModel.ClearHistory();
return NULL;
}

View file

@ -130,6 +130,7 @@ void ImageModel::PushImageForUndo()
if (hbm == NULL)
{
ShowOutOfMemory();
ClearHistory();
return;
}
@ -143,6 +144,7 @@ void ImageModel::PushImageForUndo(HBITMAP hbm)
if (hbm == NULL)
{
ShowOutOfMemory();
ClearHistory();
return;
}
@ -201,6 +203,7 @@ void ImageModel::Crop(int nWidth, int nHeight, int nOffsetX, int nOffsetY)
if (!hbmNew)
{
ShowOutOfMemory();
ClearHistory();
return;
}

View file

@ -957,9 +957,14 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
HBITMAP hbmCopy = selectionModel.GetSelectionContents();
HGLOBAL hGlobal = BitmapToClipboardDIB(hbmCopy);
if (hGlobal)
{
::SetClipboardData(CF_DIB, hGlobal);
}
else
{
ShowOutOfMemory();
imageModel.ClearHistory();
}
::DeleteObject(hbmCopy);
}
@ -1093,6 +1098,7 @@ LRESULT CMainWindow::OnCommand(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bH
if (!hbmSelection)
{
ShowOutOfMemory();
imageModel.ClearHistory();
break;
}
SaveDIBToFile(hbmSelection, szFileName, FALSE);