Something went wrong. Try again.
Reactos
Something went wrong. Try again.
21 kB · 739 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740/* * PROJECT: PAINT for ReactOS * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Providing the canvas window class * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> */
#include "precomp.h"
CCanvasWindow canvasWindow;
/* FUNCTIONS ********************************************************/
CCanvasWindow::CCanvasWindow() : m_drawing(FALSE) , m_hitCanvasSizeBox(HIT_NONE) , m_ptOrig { -1, -1 }{ m_rcResizing.SetRectEmpty();}
CCanvasWindow::~CCanvasWindow(){}
RECT CCanvasWindow::GetBaseRect(){ CRect rcBase; GetImageRect(rcBase); ImageToCanvas(rcBase); rcBase.InflateRect(GRIP_SIZE, GRIP_SIZE); return rcBase;}
VOID CCanvasWindow::ImageToCanvas(POINT& pt){ Zoomed(pt); pt.x += GRIP_SIZE - GetScrollPos(SB_HORZ); pt.y += GRIP_SIZE - GetScrollPos(SB_VERT);}
VOID CCanvasWindow::ImageToCanvas(RECT& rc){ Zoomed(rc); ::OffsetRect(&rc, GRIP_SIZE - GetScrollPos(SB_HORZ), GRIP_SIZE - GetScrollPos(SB_VERT));}
VOID CCanvasWindow::CanvasToImage(POINT& pt){ pt.x -= GRIP_SIZE - GetScrollPos(SB_HORZ); pt.y -= GRIP_SIZE - GetScrollPos(SB_VERT); UnZoomed(pt);}
VOID CCanvasWindow::CanvasToImage(RECT& rc){ ::OffsetRect(&rc, GetScrollPos(SB_HORZ) - GRIP_SIZE, GetScrollPos(SB_VERT) - GRIP_SIZE); UnZoomed(rc);}
VOID CCanvasWindow::GetImageRect(RECT& rc){ rc = { 0, 0, imageModel.GetWidth(), imageModel.GetHeight() };}
HITTEST CCanvasWindow::CanvasHitTest(POINT pt){ if (selectionModel.m_bShow || ::IsWindowVisible(textEditWindow)) return HIT_INNER; RECT rcBase = GetBaseRect(); return getSizeBoxHitTest(pt, &rcBase);}
VOID CCanvasWindow::getNewZoomRect(CRect& rcView, INT newZoom, CPoint ptTarget){ CRect rcImage; GetImageRect(rcImage); ImageToCanvas(rcImage);
// Calculate the zoom rectangle INT oldZoom = toolsModel.GetZoom(); GetClientRect(rcView); LONG cxView = rcView.right * oldZoom / newZoom, cyView = rcView.bottom * oldZoom / newZoom; rcView.SetRect(ptTarget.x - cxView / 2, ptTarget.y - cyView / 2, ptTarget.x + cxView / 2, ptTarget.y + cyView / 2);
// Shift the rectangle if necessary INT dx = 0, dy = 0; if (rcView.left < rcImage.left) dx = rcImage.left - rcView.left; else if (rcImage.right < rcView.right) dx = rcImage.right - rcView.right; if (rcView.top < rcImage.top) dy = rcImage.top - rcView.top; else if (rcImage.bottom < rcView.bottom) dy = rcImage.bottom - rcView.bottom; rcView.OffsetRect(dx, dy);
rcView.IntersectRect(&rcView, &rcImage);}
VOID CCanvasWindow::zoomTo(INT newZoom, LONG left, LONG top){ POINT pt = { left, top }; CanvasToImage(pt);
toolsModel.SetZoom(newZoom); ImageToCanvas(pt); pt.x += GetScrollPos(SB_HORZ); pt.y += GetScrollPos(SB_VERT);
updateScrollRange(); updateScrollPos(pt.x, pt.y); Invalidate(TRUE);}
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); HGDIOBJ hbm0Old = ::SelectObject(hdcMem0, hbmCache1);
// Fill the background on hdcMem0 ::FillRect(hdcMem0, &rcCanvasDraw, (HBRUSH)(COLOR_APPWORKSPACE + 1));
// Draw the sizeboxes if necessary RECT rcBase = GetBaseRect(); if (!selectionModel.m_bShow && !::IsWindowVisible(textEditWindow)) drawSizeBoxes(hdcMem0, &rcBase, FALSE, &rcCanvasDraw);
// Calculate the target area on the image CRect rcImageDraw = rcCanvasDraw; CanvasToImage(rcImageDraw); rcImageDraw.IntersectRect(&rcImageDraw, &rcImage);
// Consider rounding down by zooming rcImageDraw.right += 1; rcImageDraw.bottom += 1;
// hdcMem1 <-- imageModel HDC hdcMem1 = ::CreateCompatibleDC(hDC); HGDIOBJ hbm1Old = ::SelectObject(hdcMem1, hbmCache2); ::BitBlt(hdcMem1, rcImageDraw.left, rcImageDraw.top, rcImageDraw.Width(), rcImageDraw.Height(), imageModel.GetDC(), rcImageDraw.left, rcImageDraw.top, SRCCOPY);
// Draw overlay #1 on hdcMem1 toolsModel.OnDrawOverlayOnImage(hdcMem1);
// Transfer the bits with stretch (hdcMem0 <-- hdcMem1) ImageToCanvas(rcImage); ::StretchBlt(hdcMem0, rcImage.left, rcImage.top, rcImage.Width(), rcImage.Height(), hdcMem1, 0, 0, sizeImage.cx, sizeImage.cy, SRCCOPY);
// Clean up hdcMem1 ::SelectObject(hdcMem1, hbm1Old); ::DeleteDC(hdcMem1);
// Draw the grid on hdcMem0 if (g_showGrid && toolsModel.GetZoom() >= 4000) { HPEN oldPen = (HPEN) ::SelectObject(hdcMem0, ::CreatePen(PS_SOLID, 1, RGB(160, 160, 160))); for (INT counter = 0; counter < sizeImage.cy; counter++) { POINT pt0 = { 0, counter }, pt1 = { sizeImage.cx, counter }; ImageToCanvas(pt0); ImageToCanvas(pt1); ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL); ::LineTo(hdcMem0, pt1.x, pt1.y); } for (INT counter = 0; counter < sizeImage.cx; counter++) { POINT pt0 = { counter, 0 }, pt1 = { counter, sizeImage.cy }; ImageToCanvas(pt0); ImageToCanvas(pt1); ::MoveToEx(hdcMem0, pt0.x, pt0.y, NULL); ::LineTo(hdcMem0, pt1.x, pt1.y); } ::DeleteObject(::SelectObject(hdcMem0, oldPen)); }
// Draw overlay #2 on hdcMem0 toolsModel.OnDrawOverlayOnCanvas(hdcMem0);
// Draw new frame on hdcMem0 if any if (m_hitCanvasSizeBox != HIT_NONE && !m_rcResizing.IsRectEmpty()) DrawXorRect(hdcMem0, &m_rcResizing);
// Transfer the bits (hDC <-- hdcMem0) ::BitBlt(hDC, rcCanvasDraw.left, rcCanvasDraw.top, rcCanvasDraw.Width(), rcCanvasDraw.Height(), hdcMem0, rcCanvasDraw.left, rcCanvasDraw.top, SRCCOPY);
// Clean up hdcMem0 ::SelectObject(hdcMem0, hbm0Old); ::DeleteDC(hdcMem0); ::DeleteObject(hbmCache2); ::DeleteObject(hbmCache1);
return TRUE;}
VOID CCanvasWindow::updateScrollRange(){ CRect rcClient; GetClientRect(&rcClient);
CSize sizePage(rcClient.right, rcClient.bottom); CSize sizeZoomed = { Zoomed(imageModel.GetWidth()), Zoomed(imageModel.GetHeight()) }; CSize sizeWhole = { sizeZoomed.cx + (GRIP_SIZE * 2), sizeZoomed.cy + (GRIP_SIZE * 2) };
// show/hide the scrollbars ShowScrollBar(SB_HORZ, sizePage.cx < sizeWhole.cx); ShowScrollBar(SB_VERT, sizePage.cy < sizeWhole.cy);
if (sizePage.cx < sizeWhole.cx || sizePage.cy < sizeWhole.cy) { GetClientRect(&rcClient); // Scrollbars might change, get client rectangle again sizePage = CSize(rcClient.right, rcClient.bottom); }
SCROLLINFO si = { sizeof(si), SIF_PAGE | SIF_RANGE }; si.nMin = 0;
si.nMax = sizeWhole.cx; si.nPage = sizePage.cx; SetScrollInfo(SB_HORZ, &si);
si.nMax = sizeWhole.cy; si.nPage = sizePage.cy; SetScrollInfo(SB_VERT, &si);}
VOID CCanvasWindow::updateScrollPos(INT x, INT y){ SetScrollPos(SB_HORZ, x); SetScrollPos(SB_VERT, y);}
LRESULT CCanvasWindow::OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (m_hWnd) updateScrollRange();
return 0;}
VOID CCanvasWindow::OnHVScroll(WPARAM wParam, INT fnBar){ SCROLLINFO si; si.cbSize = sizeof(SCROLLINFO); si.fMask = SIF_ALL; GetScrollInfo(fnBar, &si); switch (LOWORD(wParam)) { case SB_THUMBTRACK: case SB_THUMBPOSITION: si.nPos = (SHORT)HIWORD(wParam); break; case SB_LINELEFT: si.nPos -= 5; break; case SB_LINERIGHT: si.nPos += 5; break; case SB_PAGELEFT: si.nPos -= si.nPage; break; case SB_PAGERIGHT: si.nPos += si.nPage; break; } si.nPos = max(min(si.nPos, si.nMax), si.nMin); SetScrollInfo(fnBar, &si); Invalidate();}
LRESULT CCanvasWindow::OnHScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ OnHVScroll(wParam, SB_HORZ); return 0;}
LRESULT CCanvasWindow::OnVScroll(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ OnHVScroll(wParam, SB_VERT); return 0;}
LRESULT CCanvasWindow::OnButtonDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
m_nMouseDownMsg = nMsg; BOOL bLeftButton = (nMsg == WM_LBUTTONDOWN);
if (nMsg == WM_MBUTTONDOWN) { m_ptOrig = pt; SetCapture(); ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_HANDDRAG))); return 0; }
HITTEST hitSelection = selectionModel.hitTest(pt); if (hitSelection != HIT_NONE) { m_drawing = TRUE; CanvasToImage(pt); SetCapture(); toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE); Invalidate(); return 0; }
HITTEST hit = CanvasHitTest(pt); if (hit == HIT_NONE || hit == HIT_BORDER) { switch (toolsModel.GetActiveTool()) { case TOOL_BEZIER: case TOOL_SHAPE: toolsModel.OnEndDraw(TRUE); Invalidate(); break;
case TOOL_FREESEL: case TOOL_RECTSEL: toolsModel.OnEndDraw(FALSE); Invalidate(); break;
default: break; }
toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions return 0; }
CanvasToImage(pt);
if (hit == HIT_INNER) { m_drawing = TRUE; SetCapture(); toolsModel.OnButtonDown(bLeftButton, pt.x, pt.y, FALSE); Invalidate(); return 0; }
if (bLeftButton) { m_hitCanvasSizeBox = hit; m_ptOrig = pt; SetCapture(); }
return 0;}
LRESULT CCanvasWindow::OnButtonDblClk(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; CanvasToImage(pt);
m_drawing = FALSE; ::ReleaseCapture(); m_nMouseDownMsg = 0;
toolsModel.OnButtonDown(nMsg == WM_LBUTTONDBLCLK, pt.x, pt.y, TRUE); toolsModel.resetTool(); Invalidate(); return 0;}
LRESULT CCanvasWindow::OnMouseMove(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
if (m_nMouseDownMsg == WM_MBUTTONDOWN) { INT x = GetScrollPos(SB_HORZ) - (pt.x - m_ptOrig.x); INT y = GetScrollPos(SB_VERT) - (pt.y - m_ptOrig.y); SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, x), 0); SendMessage(WM_VSCROLL, MAKEWPARAM(SB_THUMBPOSITION, y), 0); m_ptOrig = pt; return 0; }
CanvasToImage(pt);
if (toolsModel.GetActiveTool() == TOOL_ZOOM) Invalidate();
if (!m_drawing || toolsModel.GetActiveTool() <= TOOL_AIRBRUSH) { TRACKMOUSEEVENT tme = { sizeof(tme) }; tme.dwFlags = TME_LEAVE; tme.hwndTrack = m_hWnd; tme.dwHoverTime = 0; ::TrackMouseEvent(&tme);
if (!m_drawing) { CRect rcImage; GetImageRect(rcImage);
CStringW strCoord; if (rcImage.PtInRect(pt)) strCoord.Format(L"%ld, %ld", pt.x, pt.y); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 1, (LPARAM)(LPCWSTR)strCoord); } }
if (m_drawing || toolsModel.IsSelection()) { toolsModel.DrawWithMouseTool(pt, wParam); return 0; }
if (m_hitCanvasSizeBox == HIT_NONE || ::GetCapture() != m_hWnd) return 0;
// Dragging now... Calculate the new size INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); INT cxDelta = pt.x - m_ptOrig.x; INT cyDelta = pt.y - m_ptOrig.y; switch (m_hitCanvasSizeBox) { case HIT_UPPER_LEFT: cxImage -= cxDelta; cyImage -= cyDelta; break; case HIT_UPPER_CENTER: cyImage -= cyDelta; break; case HIT_UPPER_RIGHT: cxImage += cxDelta; cyImage -= cyDelta; break; case HIT_MIDDLE_LEFT: cxImage -= cxDelta; break; case HIT_MIDDLE_RIGHT: cxImage += cxDelta; break; case HIT_LOWER_LEFT: cxImage -= cxDelta; cyImage += cyDelta; break; case HIT_LOWER_CENTER: cyImage += cyDelta; break; case HIT_LOWER_RIGHT: cxImage += cxDelta; cyImage += cyDelta; break; default: return 0; }
// Limit bitmap size cxImage = max(1, cxImage); cyImage = max(1, cyImage); cxImage = min(MAXWORD, cxImage); cyImage = min(MAXWORD, cyImage);
// Display new size CStringW strSize; strSize.Format(L"%d x %d", cxImage, cyImage); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)(LPCWSTR)strSize);
// Dragging now... Fix the position... CRect rcResizing = { 0, 0, cxImage, cyImage }; switch (m_hitCanvasSizeBox) { case HIT_UPPER_LEFT: rcResizing.OffsetRect(cxDelta, cyDelta); break; case HIT_UPPER_CENTER: rcResizing.OffsetRect(0, cyDelta); break; case HIT_UPPER_RIGHT: rcResizing.OffsetRect(0, cyDelta); break; case HIT_MIDDLE_LEFT: rcResizing.OffsetRect(cxDelta, 0); break; case HIT_LOWER_LEFT: rcResizing.OffsetRect(cxDelta, 0); break; default: break; } ImageToCanvas(rcResizing); m_rcResizing = rcResizing; Invalidate(TRUE);
return 0;}
LRESULT CCanvasWindow::OnButtonUp(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; CanvasToImage(pt);
::ReleaseCapture();
BOOL bLeftButton = (m_nMouseDownMsg == WM_LBUTTONDOWN); m_nMouseDownMsg = 0;
if (m_drawing) { m_drawing = FALSE; toolsModel.OnButtonUp(bLeftButton, pt.x, pt.y); Invalidate(FALSE); ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)L""); return 0; }
if (m_hitCanvasSizeBox == HIT_NONE || !bLeftButton) return 0;
// Resize the image INT cxImage = imageModel.GetWidth(), cyImage = imageModel.GetHeight(); INT cxDelta = pt.x - m_ptOrig.x; INT cyDelta = pt.y - m_ptOrig.y; switch (m_hitCanvasSizeBox) { case HIT_UPPER_LEFT: imageModel.Crop(cxImage - cxDelta, cyImage - cyDelta, cxDelta, cyDelta); break; case HIT_UPPER_CENTER: imageModel.Crop(cxImage, cyImage - cyDelta, 0, cyDelta); break; case HIT_UPPER_RIGHT: imageModel.Crop(cxImage + cxDelta, cyImage - cyDelta, 0, cyDelta); break; case HIT_MIDDLE_LEFT: imageModel.Crop(cxImage - cxDelta, cyImage, cxDelta, 0); break; case HIT_MIDDLE_RIGHT: imageModel.Crop(cxImage + cxDelta, cyImage, 0, 0); break; case HIT_LOWER_LEFT: imageModel.Crop(cxImage - cxDelta, cyImage + cyDelta, cxDelta, 0); break; case HIT_LOWER_CENTER: imageModel.Crop(cxImage, cyImage + cyDelta, 0, 0); break; case HIT_LOWER_RIGHT: imageModel.Crop(cxImage + cxDelta, cyImage + cyDelta, 0, 0); break; default: break; } m_rcResizing.SetRectEmpty();
g_imageSaved = FALSE;
m_hitCanvasSizeBox = HIT_NONE; toolsModel.resetTool(); // resets the point-buffer of the polygon and bezier functions updateScrollRange(); Invalidate(TRUE); return 0;}
LRESULT CCanvasWindow::OnSetCursor(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (CWaitCursor::IsWaiting()) { bHandled = FALSE; return 0; }
if (m_nMouseDownMsg == WM_MBUTTONDOWN) { ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_HANDDRAG))); return 0; }
POINT pt; ::GetCursorPos(&pt); ScreenToClient(&pt);
CRect rcClient; GetClientRect(&rcClient);
if (!rcClient.PtInRect(pt)) { bHandled = FALSE; return 0; }
HITTEST hitSelection = selectionModel.hitTest(pt); if (hitSelection != HIT_NONE) { if (!setCursorOnSizeBox(hitSelection)) ::SetCursor(::LoadCursorW(NULL, (LPCWSTR)IDC_SIZEALL)); return 0; }
CRect rcImage; GetImageRect(rcImage); ImageToCanvas(rcImage);
if (rcImage.PtInRect(pt)) { switch (toolsModel.GetActiveTool()) { case TOOL_FILL: ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_FILL))); break; case TOOL_COLOR: ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_COLOR))); break; case TOOL_ZOOM: ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_ZOOM))); break; case TOOL_PEN: ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_PEN))); break; case TOOL_AIRBRUSH: ::SetCursor(::LoadCursorW(g_hinstExe, MAKEINTRESOURCEW(IDC_AIRBRUSH))); break; default: ::SetCursor(::LoadCursorW(NULL, (LPCWSTR)IDC_CROSS)); } return 0; }
if (selectionModel.m_bShow || !setCursorOnSizeBox(CanvasHitTest(pt))) bHandled = FALSE;
return 0;}
LRESULT CCanvasWindow::OnKeyDown(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ if (wParam == VK_ESCAPE) { OnEndDraw(TRUE); ::ReleaseCapture(); m_nMouseDownMsg = 0; m_hitCanvasSizeBox = HIT_NONE; m_rcResizing.SetRectEmpty(); Invalidate(TRUE); }
return 0;}
LRESULT CCanvasWindow::OnCancelMode(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ // Cancel dragging m_hitCanvasSizeBox = HIT_NONE; m_rcResizing.SetRectEmpty(); Invalidate(TRUE); return 0;}
LRESULT CCanvasWindow::OnMouseWheel(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ return ::SendMessageW(GetParent(), nMsg, wParam, lParam);}
LRESULT CCanvasWindow::OnCaptureChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ ::SendMessageW(g_hStatusBar, SB_SETTEXT, 2, (LPARAM)L""); return 0;}
LRESULT CCanvasWindow::OnEraseBkgnd(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ return TRUE; // do nothing => transparent background}
LRESULT CCanvasWindow::OnPaint(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ RECT rcClient; GetClientRect(&rcClient);
static BOOL s_bShowedOutOfMemory = FALSE; // Don't show "Out Of Memory" message multiple time
PAINTSTRUCT ps; HDC hDC = BeginPaint(&ps);
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;}
VOID CCanvasWindow::OnEndDraw(BOOL bCancel){ m_drawing = FALSE; toolsModel.OnEndDraw(bCancel); Invalidate(FALSE);}
LRESULT CCanvasWindow::OnCtlColorEdit(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ SetTextColor((HDC)wParam, paletteModel.GetFgColor()); SetBkMode((HDC)wParam, TRANSPARENT); return (LRESULT)GetStockObject(NULL_BRUSH);}
LRESULT CCanvasWindow::OnPaletteModelColorChanged(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled){ imageModel.NotifyImageChanged(); return 0;}