Something went wrong. Try again.
Reactos
Something went wrong. Try again.
8.4 kB · 361 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362/* * PROJECT: PAINT for ReactOS * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Undo and redo functionality * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> * Copyright 2023 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"
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()) { canvasWindow.updateScrollRange(); canvasWindow.Invalidate(); }
if (miniature.IsWindow()) miniature.Invalidate();}
ImageModel::ImageModel() : m_hDrawingDC(::CreateCompatibleDC(NULL)) , m_currInd(0) , m_undoSteps(0) , m_redoSteps(0){ ZeroMemory(m_historyItems, sizeof(m_historyItems));
m_hbmMaster = CreateColorDIB(1, 1, RGB(255, 255, 255)); m_hbmOld = ::SelectObject(m_hDrawingDC, m_hbmMaster);
g_imageSaved = TRUE;}
ImageModel::~ImageModel(){ ::SelectObject(m_hDrawingDC, m_hbmOld); // De-select ::DeleteDC(m_hDrawingDC); ::DeleteObject(m_hbmMaster); 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); if (!CanUndo()) return;
selectionModel.HideSelection();
m_currInd = (m_currInd + HISTORYSIZE - 1) % HISTORYSIZE; // Go previous ATLASSERT(m_hbmMaster != NULL); SwapPart(); ::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select
m_undoSteps--; if (bClearRedo) m_redoSteps = 0; else if (m_redoSteps < HISTORYSIZE - 1) m_redoSteps++;
NotifyImageChanged();}
void ImageModel::Redo(){ ATLTRACE("%s: %d\n", __FUNCTION__, m_redoSteps); if (!CanRedo()) return;
selectionModel.HideSelection();
ATLASSERT(m_hbmMaster != NULL); SwapPart(); m_currInd = (m_currInd + 1) % HISTORYSIZE; // Go next ::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select
m_redoSteps--; if (m_undoSteps < HISTORYSIZE - 1) m_undoSteps++;
NotifyImageChanged();}
void ImageModel::ClearHistory(){ for (int i = 0; i < HISTORYSIZE; ++i) { m_historyItems[i].clear(); }
m_undoSteps = 0; m_redoSteps = 0;}
void ImageModel::PushImageForUndo(){ HBITMAP hbm = CopyBitmap(); if (hbm == NULL) { ShowOutOfMemory(); ClearHistory(); return; }
PushImageForUndo(hbm);}
void ImageModel::PushImageForUndo(HBITMAP hbm){ ATLTRACE("%s: %d\n", __FUNCTION__, m_currInd);
if (hbm == NULL) { ShowOutOfMemory(); ClearHistory(); return; }
IMAGE_PART& part = m_historyItems[m_currInd]; part.clear(); part.m_hbmImage = m_hbmMaster; m_hbmMaster = hbm; ::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;
CRect rcImage = { 0, 0, GetWidth(), GetHeight() }; CRect& rc = part.m_rcPart; if (!rc.IntersectRect(rc, rcImage)) rc.SetRect(-1, -1, 0, 0);
HBITMAP hbmMaster = LockBitmap(); part.m_hbmImage = getSubImage(hbmMaster, rc); UnlockBitmap(hbmMaster);
PushDone();}
void ImageModel::PushDone(){ m_currInd = (m_currInd + 1) % HISTORYSIZE; // Go next
if (m_undoSteps < HISTORYSIZE - 1) m_undoSteps++; m_redoSteps = 0;
g_imageSaved = FALSE; NotifyImageChanged();}
void ImageModel::Crop(int nWidth, int nHeight, int nOffsetX, int nOffsetY){ // We cannot create bitmaps of size zero if (nWidth <= 0) nWidth = 1; if (nHeight <= 0) nHeight = 1;
// Create a white HBITMAP HBITMAP hbmNew = CreateColorDIB(nWidth, nHeight, RGB(255, 255, 255)); if (!hbmNew) { ShowOutOfMemory(); ClearHistory(); return; }
// Put the master image as a sub-image RECT rcPart = { -nOffsetX, -nOffsetY, GetWidth() - nOffsetX, GetHeight() - nOffsetY }; HBITMAP hbmOld = imageModel.LockBitmap(); putSubImage(hbmNew, rcPart, hbmOld); imageModel.UnlockBitmap(hbmOld);
// Push it PushImageForUndo(hbmNew);
NotifyImageChanged();}
void ImageModel::SaveImage(LPCWSTR lpFileName){ SaveDIBToFile(m_hbmMaster, lpFileName, TRUE);}
BOOL ImageModel::IsImageSaved() const{ return g_imageSaved;}
void ImageModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY){ int oldWidth = GetWidth(); int oldHeight = GetHeight(); INT newWidth = oldWidth * nStretchPercentX / 100; INT newHeight = oldHeight * nStretchPercentY / 100; if (oldWidth != newWidth || oldHeight != newHeight) { HBITMAP hbm0 = CopyDIBImage(m_hbmMaster, newWidth, newHeight); PushImageForUndo(hbm0); } if (nSkewDegX) { HBITMAP hbm1 = SkewDIB(m_hDrawingDC, m_hbmMaster, nSkewDegX, FALSE); PushImageForUndo(hbm1); } if (nSkewDegY) { HBITMAP hbm2 = SkewDIB(m_hDrawingDC, m_hbmMaster, nSkewDegY, TRUE); PushImageForUndo(hbm2); } NotifyImageChanged();}
int ImageModel::GetWidth() const{ return GetDIBWidth(m_hbmMaster);}
int ImageModel::GetHeight() const{ return GetDIBHeight(m_hbmMaster);}
void ImageModel::InvertColors(){ RECT rect = {0, 0, GetWidth(), GetHeight()}; PushImageForUndo(); InvertRect(m_hDrawingDC, &rect); NotifyImageChanged();}
HDC ImageModel::GetDC(){ return m_hDrawingDC;}
void ImageModel::FlipHorizontally(){ PushImageForUndo(); StretchBlt(m_hDrawingDC, GetWidth() - 1, 0, -GetWidth(), GetHeight(), GetDC(), 0, 0, GetWidth(), GetHeight(), SRCCOPY); NotifyImageChanged();}
void ImageModel::FlipVertically(){ PushImageForUndo(); StretchBlt(m_hDrawingDC, 0, GetHeight() - 1, GetWidth(), -GetHeight(), GetDC(), 0, 0, GetWidth(), GetHeight(), SRCCOPY); NotifyImageChanged();}
void ImageModel::RotateNTimes90Degrees(int iN){ switch (iN) { case 1: case 3: { HBITMAP hbm = Rotate90DegreeBlt(m_hDrawingDC, GetWidth(), GetHeight(), iN == 1, FALSE); PushImageForUndo(hbm); break; } case 2: { PushImageForUndo(); ::StretchBlt(m_hDrawingDC, GetWidth() - 1, GetHeight() - 1, -GetWidth(), -GetHeight(), m_hDrawingDC, 0, 0, GetWidth(), GetHeight(), SRCCOPY); break; } } NotifyImageChanged();}
void ImageModel::Clamp(POINT& pt) const{ pt.x = max(0, min(pt.x, GetWidth())); pt.y = max(0, min(pt.y, GetHeight()));}
HBITMAP ImageModel::CopyBitmap(){ HBITMAP hBitmap = LockBitmap(); HBITMAP ret = CopyDIBImage(hBitmap); UnlockBitmap(hBitmap); return ret;}
BOOL ImageModel::IsBlackAndWhite(){ HBITMAP hBitmap = LockBitmap(); BOOL bBlackAndWhite = IsBitmapBlackAndWhite(hBitmap); UnlockBitmap(hBitmap); return bBlackAndWhite;}
void ImageModel::PushBlackAndWhite(){ HBITMAP hBitmap = LockBitmap(); HBITMAP hNewBitmap = ConvertToBlackAndWhite(hBitmap); UnlockBitmap(hBitmap);
PushImageForUndo(hNewBitmap);}
HBITMAP ImageModel::LockBitmap(){ // NOTE: An app cannot select a bitmap into more than one device context at a time. ::SelectObject(m_hDrawingDC, m_hbmOld); // De-select HBITMAP hbmLocked = m_hbmMaster; m_hbmMaster = NULL; return hbmLocked;}
void ImageModel::UnlockBitmap(HBITMAP hbmLocked){ m_hbmMaster = hbmLocked; m_hbmOld = ::SelectObject(m_hDrawingDC, m_hbmMaster); // Re-select}