2015-05-08 16:02:36 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: PAINT for ReactOS
|
|
|
|
* LICENSE: LGPL
|
2015-09-09 13:13:35 +00:00
|
|
|
* FILE: base/applications/mspaint/history.cpp
|
2015-05-08 16:02:36 +00:00
|
|
|
* PURPOSE: Undo and redo functionality
|
|
|
|
* PROGRAMMERS: Benedikt Freisen
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* INCLUDES *********************************************************/
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
|
|
/* FUNCTIONS ********************************************************/
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::NotifyDimensionsChanged()
|
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
if (imageArea.IsWindow())
|
|
|
|
imageArea.SendMessage(WM_IMAGEMODELDIMENSIONSCHANGED);
|
2015-07-07 11:56:37 +00:00
|
|
|
}
|
2015-05-08 16:02:36 +00:00
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::NotifyImageChanged()
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
if (imageArea.IsWindow())
|
|
|
|
imageArea.SendMessage(WM_IMAGEMODELIMAGECHANGED);
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
|
2015-07-23 07:48:32 +00:00
|
|
|
ImageModel::ImageModel()
|
|
|
|
{
|
|
|
|
currInd = 0;
|
|
|
|
undoSteps = 0;
|
|
|
|
redoSteps = 0;
|
|
|
|
imageSaved = TRUE;
|
2015-07-23 13:11:54 +00:00
|
|
|
|
2015-08-16 19:52:37 +00:00
|
|
|
// prepare a minimal usable bitmap
|
|
|
|
int imgXRes = 1;
|
|
|
|
int imgYRes = 1;
|
2015-07-23 13:11:54 +00:00
|
|
|
|
|
|
|
hDrawingDC = CreateCompatibleDC(NULL);
|
|
|
|
SelectObject(hDrawingDC, CreatePen(PS_SOLID, 0, paletteModel.GetFgColor()));
|
|
|
|
SelectObject(hDrawingDC, CreateSolidBrush(paletteModel.GetBgColor()));
|
|
|
|
|
|
|
|
hBms[0] = CreateDIBWithProperties(imgXRes, imgYRes);
|
|
|
|
SelectObject(hDrawingDC, hBms[0]);
|
|
|
|
Rectangle(hDrawingDC, 0 - 1, 0 - 1, imgXRes + 1, imgYRes + 1);
|
2015-07-23 07:48:32 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::CopyPrevious()
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
ATLTRACE("%s: %d\n", __FUNCTION__, currInd);
|
2015-05-08 16:02:36 +00:00
|
|
|
DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
|
2022-01-09 23:50:37 +00:00
|
|
|
hBms[(currInd + 1) % HISTORYSIZE] = CopyDIBImage(hBms[currInd]);
|
2015-05-08 16:02:36 +00:00
|
|
|
currInd = (currInd + 1) % HISTORYSIZE;
|
|
|
|
if (undoSteps < HISTORYSIZE - 1)
|
|
|
|
undoSteps++;
|
|
|
|
redoSteps = 0;
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
imageSaved = FALSE;
|
|
|
|
}
|
|
|
|
|
2022-01-30 03:05:23 +00:00
|
|
|
void ImageModel::Undo(BOOL bClearRedo)
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
ATLTRACE("%s: %d\n", __FUNCTION__, undoSteps);
|
2015-05-08 16:02:36 +00:00
|
|
|
if (undoSteps > 0)
|
|
|
|
{
|
2015-07-07 11:56:37 +00:00
|
|
|
int oldWidth = GetWidth();
|
|
|
|
int oldHeight = GetHeight();
|
2015-07-07 10:42:49 +00:00
|
|
|
selectionWindow.ShowWindow(SW_HIDE);
|
2015-05-08 16:02:36 +00:00
|
|
|
currInd = (currInd + HISTORYSIZE - 1) % HISTORYSIZE;
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
undoSteps--;
|
2022-01-30 03:05:23 +00:00
|
|
|
if (bClearRedo)
|
|
|
|
redoSteps = 0;
|
|
|
|
else if (redoSteps < HISTORYSIZE - 1)
|
2015-05-08 16:02:36 +00:00
|
|
|
redoSteps++;
|
2015-07-07 11:56:37 +00:00
|
|
|
if (GetWidth() != oldWidth || GetHeight() != oldHeight)
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
NotifyImageChanged();
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::Redo()
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
ATLTRACE("%s: %d\n", __FUNCTION__, redoSteps);
|
2015-05-08 16:02:36 +00:00
|
|
|
if (redoSteps > 0)
|
|
|
|
{
|
2015-07-07 11:56:37 +00:00
|
|
|
int oldWidth = GetWidth();
|
|
|
|
int oldHeight = GetHeight();
|
2015-07-07 10:42:49 +00:00
|
|
|
selectionWindow.ShowWindow(SW_HIDE);
|
2015-05-08 16:02:36 +00:00
|
|
|
currInd = (currInd + 1) % HISTORYSIZE;
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
redoSteps--;
|
|
|
|
if (undoSteps < HISTORYSIZE - 1)
|
|
|
|
undoSteps++;
|
2015-07-07 11:56:37 +00:00
|
|
|
if (GetWidth() != oldWidth || GetHeight() != oldHeight)
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
NotifyImageChanged();
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::ResetToPrevious()
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2022-01-22 15:59:12 +00:00
|
|
|
ATLTRACE("%s: %d\n", __FUNCTION__, currInd);
|
2015-05-08 16:02:36 +00:00
|
|
|
DeleteObject(hBms[currInd]);
|
2022-01-09 23:50:37 +00:00
|
|
|
hBms[currInd] = CopyDIBImage(hBms[(currInd + HISTORYSIZE - 1) % HISTORYSIZE]);
|
2015-05-08 16:02:36 +00:00
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
2015-07-07 11:56:37 +00:00
|
|
|
NotifyImageChanged();
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::ClearHistory()
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
|
|
|
undoSteps = 0;
|
|
|
|
redoSteps = 0;
|
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::Insert(HBITMAP hbm)
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
2015-07-07 11:56:37 +00:00
|
|
|
int oldWidth = GetWidth();
|
|
|
|
int oldHeight = GetHeight();
|
2015-05-08 16:02:36 +00:00
|
|
|
DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
|
|
|
|
hBms[(currInd + 1) % HISTORYSIZE] = hbm;
|
|
|
|
currInd = (currInd + 1) % HISTORYSIZE;
|
|
|
|
if (undoSteps < HISTORYSIZE - 1)
|
|
|
|
undoSteps++;
|
|
|
|
redoSteps = 0;
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
2015-07-07 11:56:37 +00:00
|
|
|
if (GetWidth() != oldWidth || GetHeight() != oldHeight)
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
NotifyImageChanged();
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
void ImageModel::Crop(int nWidth, int nHeight, int nOffsetX, int nOffsetY)
|
2015-05-08 16:02:36 +00:00
|
|
|
{
|
|
|
|
HDC hdc;
|
|
|
|
HPEN oldPen;
|
|
|
|
HBRUSH oldBrush;
|
2015-07-07 11:56:37 +00:00
|
|
|
int oldWidth = GetWidth();
|
|
|
|
int oldHeight = GetHeight();
|
2015-05-08 16:02:36 +00:00
|
|
|
|
2017-06-07 10:00:04 +00:00
|
|
|
if (nWidth <= 0)
|
|
|
|
nWidth = 1;
|
|
|
|
if (nHeight <= 0)
|
|
|
|
nHeight = 1;
|
|
|
|
|
2015-05-08 16:02:36 +00:00
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
|
2015-07-07 11:56:37 +00:00
|
|
|
hBms[(currInd + 1) % HISTORYSIZE] = CreateDIBWithProperties(nWidth, nHeight);
|
2015-05-08 16:02:36 +00:00
|
|
|
currInd = (currInd + 1) % HISTORYSIZE;
|
|
|
|
if (undoSteps < HISTORYSIZE - 1)
|
|
|
|
undoSteps++;
|
|
|
|
redoSteps = 0;
|
|
|
|
|
|
|
|
hdc = CreateCompatibleDC(hDrawingDC);
|
|
|
|
SelectObject(hdc, hBms[currInd]);
|
|
|
|
|
2015-07-07 11:15:24 +00:00
|
|
|
oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, paletteModel.GetBgColor()));
|
|
|
|
oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(paletteModel.GetBgColor()));
|
2015-07-07 11:56:37 +00:00
|
|
|
Rectangle(hdc, 0, 0, nWidth, nHeight);
|
|
|
|
BitBlt(hdc, -nOffsetX, -nOffsetY, GetWidth(), GetHeight(), hDrawingDC, 0, 0, SRCCOPY);
|
2015-05-08 16:02:36 +00:00
|
|
|
DeleteObject(SelectObject(hdc, oldBrush));
|
|
|
|
DeleteObject(SelectObject(hdc, oldPen));
|
|
|
|
DeleteDC(hdc);
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
|
2015-07-07 11:56:37 +00:00
|
|
|
if (GetWidth() != oldWidth || GetHeight() != oldHeight)
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::SaveImage(LPTSTR lpFileName)
|
|
|
|
{
|
2020-04-28 22:44:18 +00:00
|
|
|
SaveDIBToFile(hBms[currInd], lpFileName, hDrawingDC);
|
2015-07-07 11:56:37 +00:00
|
|
|
}
|
|
|
|
|
2021-12-28 00:49:36 +00:00
|
|
|
BOOL ImageModel::IsImageSaved() const
|
2015-07-07 11:56:37 +00:00
|
|
|
{
|
|
|
|
return imageSaved;
|
|
|
|
}
|
|
|
|
|
2021-12-28 00:49:36 +00:00
|
|
|
BOOL ImageModel::HasUndoSteps() const
|
2015-07-07 11:56:37 +00:00
|
|
|
{
|
|
|
|
return undoSteps > 0;
|
|
|
|
}
|
|
|
|
|
2021-12-28 00:49:36 +00:00
|
|
|
BOOL ImageModel::HasRedoSteps() const
|
2015-07-07 11:56:37 +00:00
|
|
|
{
|
|
|
|
return redoSteps > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY)
|
|
|
|
{
|
|
|
|
int oldWidth = GetWidth();
|
|
|
|
int oldHeight = GetHeight();
|
2022-01-09 23:50:37 +00:00
|
|
|
INT newWidth = oldWidth * nStretchPercentX / 100;
|
|
|
|
INT newHeight = oldHeight * nStretchPercentY / 100;
|
2022-02-14 07:18:18 +00:00
|
|
|
if (oldWidth != newWidth || oldHeight != newHeight)
|
|
|
|
{
|
|
|
|
HBITMAP hbm0 = CopyDIBImage(hBms[currInd], newWidth, newHeight);
|
|
|
|
Insert(hbm0);
|
|
|
|
}
|
|
|
|
if (nSkewDegX)
|
|
|
|
{
|
|
|
|
HBITMAP hbm1 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegX, FALSE);
|
|
|
|
Insert(hbm1);
|
|
|
|
}
|
|
|
|
if (nSkewDegY)
|
|
|
|
{
|
|
|
|
HBITMAP hbm2 = SkewDIB(hDrawingDC, hBms[currInd], nSkewDegY, TRUE);
|
|
|
|
Insert(hbm2);
|
|
|
|
}
|
2015-07-07 11:56:37 +00:00
|
|
|
if (GetWidth() != oldWidth || GetHeight() != oldHeight)
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
2021-12-28 00:49:36 +00:00
|
|
|
int ImageModel::GetWidth() const
|
2015-07-07 11:56:37 +00:00
|
|
|
{
|
|
|
|
return GetDIBWidth(hBms[currInd]);
|
|
|
|
}
|
|
|
|
|
2021-12-28 00:49:36 +00:00
|
|
|
int ImageModel::GetHeight() const
|
2015-07-07 11:56:37 +00:00
|
|
|
{
|
|
|
|
return GetDIBHeight(hBms[currInd]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::InvertColors()
|
|
|
|
{
|
|
|
|
RECT rect = {0, 0, GetWidth(), GetHeight()};
|
|
|
|
CopyPrevious();
|
|
|
|
InvertRect(hDrawingDC, &rect);
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::Clear(COLORREF color)
|
|
|
|
{
|
|
|
|
Rectangle(hDrawingDC, 0 - 1, 0 - 1, GetWidth() + 1, GetHeight() + 1);
|
|
|
|
NotifyImageChanged();
|
2015-05-08 16:02:36 +00:00
|
|
|
}
|
2015-07-23 13:52:02 +00:00
|
|
|
|
|
|
|
HDC ImageModel::GetDC()
|
|
|
|
{
|
|
|
|
return hDrawingDC;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::FlipHorizontally()
|
|
|
|
{
|
|
|
|
CopyPrevious();
|
|
|
|
StretchBlt(hDrawingDC, GetWidth() - 1, 0, -GetWidth(), GetHeight(), GetDC(), 0, 0,
|
|
|
|
GetWidth(), GetHeight(), SRCCOPY);
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::FlipVertically()
|
|
|
|
{
|
|
|
|
CopyPrevious();
|
|
|
|
StretchBlt(hDrawingDC, 0, GetHeight() - 1, GetWidth(), -GetHeight(), GetDC(), 0, 0,
|
|
|
|
GetWidth(), GetHeight(), SRCCOPY);
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::RotateNTimes90Degrees(int iN)
|
|
|
|
{
|
2022-02-14 03:08:34 +00:00
|
|
|
switch (iN)
|
2015-07-23 13:52:02 +00:00
|
|
|
{
|
2022-02-14 03:08:34 +00:00
|
|
|
case 1:
|
|
|
|
case 3:
|
|
|
|
DeleteObject(hBms[(currInd + 1) % HISTORYSIZE]);
|
|
|
|
hBms[(currInd + 1) % HISTORYSIZE] = Rotate90DegreeBlt(hDrawingDC, GetWidth(), GetHeight(), iN == 1);
|
|
|
|
currInd = (currInd + 1) % HISTORYSIZE;
|
|
|
|
if (undoSteps < HISTORYSIZE - 1)
|
|
|
|
undoSteps++;
|
|
|
|
redoSteps = 0;
|
|
|
|
SelectObject(hDrawingDC, hBms[currInd]);
|
|
|
|
imageSaved = FALSE;
|
|
|
|
NotifyDimensionsChanged();
|
|
|
|
break;
|
|
|
|
case 2:
|
2015-07-23 13:52:02 +00:00
|
|
|
CopyPrevious();
|
|
|
|
StretchBlt(hDrawingDC, GetWidth() - 1, GetHeight() - 1, -GetWidth(), -GetHeight(), GetDC(),
|
|
|
|
0, 0, GetWidth(), GetHeight(), SRCCOPY);
|
2022-02-14 03:08:34 +00:00
|
|
|
break;
|
2015-07-23 13:52:02 +00:00
|
|
|
}
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
2022-01-30 03:05:23 +00:00
|
|
|
|
|
|
|
void ImageModel::DrawSelectionBackground(COLORREF rgbBG)
|
|
|
|
{
|
|
|
|
if (toolsModel.GetActiveTool() == TOOL_FREESEL)
|
|
|
|
selectionModel.DrawBackgroundPoly(hDrawingDC, rgbBG);
|
|
|
|
else
|
|
|
|
selectionModel.DrawBackgroundRect(hDrawingDC, rgbBG);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::DeleteSelection()
|
|
|
|
{
|
|
|
|
if (selectionWindow.IsWindowVisible())
|
|
|
|
ResetToPrevious();
|
|
|
|
CopyPrevious();
|
|
|
|
if (selectionWindow.IsWindowVisible())
|
|
|
|
Undo(TRUE);
|
|
|
|
DrawSelectionBackground(paletteModel.GetBgColor());
|
|
|
|
selectionWindow.ShowWindow(SW_HIDE);
|
|
|
|
NotifyImageChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ImageModel::Bound(POINT& pt)
|
|
|
|
{
|
|
|
|
pt.x = max(0, min(pt.x, GetWidth()));
|
|
|
|
pt.y = max(0, min(pt.y, GetHeight()));
|
|
|
|
}
|