reactos/base/applications/mspaint/selectionmodel.cpp
Katayama Hirofumi MZ dfd06ee8fc
[MSPAINT] Implement skew (#4362)
- Add SkewDIB helper function in dib.cpp.
- Implement Stretch and Skew feature by SelectionModel::StretchSkew and ImageModel::StretchSkew.
- Move ColorKeyedMaskBlt function.
CORE-16634
2022-02-14 16:18:18 +09:00

405 lines
14 KiB
C++

/*
* PROJECT: PAINT for ReactOS
* LICENSE: LGPL
* FILE: base/applications/mspaint/selectionmodel.cpp
* PURPOSE: Keep track of selection parameters, notify listeners
* PROGRAMMERS: Benedikt Freisen
* Katayama Hirofumi MZ
*/
/* INCLUDES *********************************************************/
#include "precomp.h"
/* FUNCTIONS ********************************************************/
SelectionModel::SelectionModel()
: m_hDC(CreateCompatibleDC(NULL))
, m_hBm(NULL)
, m_hMask(NULL)
, m_ptStack(NULL)
, m_iPtSP(0)
{
SetRectEmpty(&m_rcSrc);
SetRectEmpty(&m_rcDest);
}
SelectionModel::~SelectionModel()
{
DeleteDC(m_hDC);
ResetPtStack();
if (m_hBm)
{
DeleteObject(m_hBm);
}
if (m_hMask)
{
DeleteObject(m_hMask);
}
}
void SelectionModel::ResetPtStack()
{
if (m_ptStack != NULL)
HeapFree(GetProcessHeap(), 0, m_ptStack);
m_ptStack = NULL;
m_iPtSP = 0;
}
void SelectionModel::PushToPtStack(LONG x, LONG y)
{
if (m_iPtSP % 1024 == 0)
{
if (m_ptStack)
m_ptStack = (POINT*) HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, m_ptStack, sizeof(POINT) * (m_iPtSP + 1024));
else
m_ptStack = (POINT*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * 1024);
}
m_ptStack[m_iPtSP].x = x;
m_ptStack[m_iPtSP].y = y;
m_iPtSP++;
}
void SelectionModel::CalculateBoundingBoxAndContents(HDC hDCImage)
{
int i;
m_rcSrc.left = m_rcSrc.top = MAXLONG;
m_rcSrc.right = m_rcSrc.bottom = 0;
for (i = 0; i < m_iPtSP; i++)
{
if (m_ptStack[i].x < m_rcSrc.left)
m_rcSrc.left = m_ptStack[i].x;
if (m_ptStack[i].y < m_rcSrc.top)
m_rcSrc.top = m_ptStack[i].y;
if (m_ptStack[i].x > m_rcSrc.right)
m_rcSrc.right = m_ptStack[i].x;
if (m_ptStack[i].y > m_rcSrc.bottom)
m_rcSrc.bottom = m_ptStack[i].y;
}
m_rcSrc.right += 1;
m_rcSrc.bottom += 1;
m_rcDest.left = m_rcSrc.left;
m_rcDest.top = m_rcSrc.top;
m_rcDest.right = m_rcSrc.right;
m_rcDest.bottom = m_rcSrc.bottom;
if (m_iPtSP > 1)
{
DeleteObject(m_hMask);
m_hMask = CreateBitmap(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 1, 1, NULL);
DeleteObject(SelectObject(m_hDC, m_hMask));
POINT *m_ptStackCopy = (POINT*) HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(POINT) * m_iPtSP);
for (i = 0; i < m_iPtSP; i++)
{
m_ptStackCopy[i].x = m_ptStack[i].x - m_rcSrc.left;
m_ptStackCopy[i].y = m_ptStack[i].y - m_rcSrc.top;
}
Poly(m_hDC, m_ptStackCopy, m_iPtSP, 0x00ffffff, 0x00ffffff, 1, 2, TRUE, FALSE);
HeapFree(GetProcessHeap(), 0, m_ptStackCopy);
SelectObject(m_hDC, m_hBm = CreateDIBWithProperties(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc)));
imageModel.ResetToPrevious();
MaskBlt(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), hDCImage, m_rcSrc.left,
m_rcSrc.top, m_hMask, 0, 0, MAKEROP4(SRCCOPY, WHITENESS));
}
}
void SelectionModel::CalculateContents(HDC hDCImage)
{
DeleteObject(m_hMask);
m_hMask = CreateBitmap(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 1, 1, NULL);
DeleteObject(SelectObject(m_hDC, m_hMask));
Rect(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), 0x00ffffff, 0x00ffffff, 1, 2);
SelectObject(m_hDC, m_hBm = CreateDIBWithProperties(RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc)));
BitBlt(m_hDC, 0, 0, RECT_WIDTH(m_rcSrc), RECT_HEIGHT(m_rcSrc), hDCImage, m_rcSrc.left,
m_rcSrc.top, SRCCOPY);
}
void SelectionModel::DrawBackgroundPoly(HDC hDCImage, COLORREF crBg)
{
Poly(hDCImage, m_ptStack, m_iPtSP, crBg, crBg, 1, 2, TRUE, FALSE);
}
void SelectionModel::DrawBackgroundRect(HDC hDCImage, COLORREF crBg)
{
Rect(hDCImage, m_rcSrc.left, m_rcSrc.top, m_rcSrc.right, m_rcSrc.bottom, crBg, crBg, 0, 1);
}
void SelectionModel::DrawSelection(HDC hDCImage, COLORREF crBg, BOOL bBgTransparent)
{
if (!bBgTransparent)
MaskBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest),
m_hDC, 0, 0, m_hMask, 0, 0, MAKEROP4(SRCCOPY, SRCAND));
else
ColorKeyedMaskBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest),
m_hDC, 0, 0, m_hMask, 0, 0, MAKEROP4(SRCCOPY, SRCAND), crBg);
}
void SelectionModel::DrawSelectionStretched(HDC hDCImage)
{
StretchBlt(hDCImage, m_rcDest.left, m_rcDest.top, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0, GetDIBWidth(m_hBm), GetDIBHeight(m_hBm), SRCCOPY);
}
void SelectionModel::ScaleContentsToFit()
{
HDC hTempDC;
HBITMAP hTempBm;
hTempDC = CreateCompatibleDC(m_hDC);
hTempBm = CreateDIBWithProperties(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest));
SelectObject(hTempDC, hTempBm);
SelectObject(m_hDC, m_hBm);
StretchBlt(hTempDC, 0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0,
GetDIBWidth(m_hBm), GetDIBHeight(m_hBm), SRCCOPY);
DeleteObject(m_hBm);
m_hBm = hTempBm;
hTempBm = CreateBitmap(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), 1, 1, NULL);
SelectObject(hTempDC, hTempBm);
SelectObject(m_hDC, m_hMask);
StretchBlt(hTempDC, 0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC, 0, 0,
GetDIBWidth(m_hMask), GetDIBHeight(m_hMask), SRCCOPY);
DeleteObject(m_hMask);
m_hMask = hTempBm;
SelectObject(m_hDC, m_hBm);
DeleteDC(hTempDC);
}
void SelectionModel::InsertFromHBITMAP(HBITMAP hBm, INT x, INT y)
{
HDC hTempDC;
HBITMAP hTempMask;
m_hBm = CopyDIBImage(hBm);
DeleteObject(SelectObject(m_hDC, m_hBm));
SetRectEmpty(&m_rcSrc);
m_rcDest.left = x;
m_rcDest.top = y;
m_rcDest.right = m_rcDest.left + GetDIBWidth(m_hBm);
m_rcDest.bottom = m_rcDest.top + GetDIBHeight(m_hBm);
hTempDC = CreateCompatibleDC(m_hDC);
hTempMask = CreateBitmap(RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), 1, 1, NULL);
SelectObject(hTempDC, hTempMask);
Rect(hTempDC, 0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), 0x00ffffff, 0x00ffffff, 1, 1);
DeleteObject(m_hMask);
m_hMask = hTempMask;
DeleteDC(hTempDC);
}
void SelectionModel::FlipHorizontally()
{
SelectObject(m_hDC, m_hMask);
StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, 0, -RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
SelectObject(m_hDC, m_hBm);
StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, 0, -RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
NotifyRefreshNeeded();
}
void SelectionModel::FlipVertically()
{
SelectObject(m_hDC, m_hMask);
StretchBlt(m_hDC, 0, RECT_HEIGHT(m_rcDest) - 1, RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
SelectObject(m_hDC, m_hBm);
StretchBlt(m_hDC, 0, RECT_HEIGHT(m_rcDest) - 1, RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
NotifyRefreshNeeded();
}
void SelectionModel::RotateNTimes90Degrees(int iN)
{
HBITMAP hbm;
switch (iN)
{
case 1:
case 3:
imageModel.DeleteSelection();
imageModel.CopyPrevious();
SelectObject(m_hDC, m_hBm);
hbm = Rotate90DegreeBlt(m_hDC, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), iN == 1);
InsertFromHBITMAP(hbm, m_rcDest.left, m_rcDest.top);
DeleteObject(hbm);
selectionWindow.ShowWindow(SW_SHOWNOACTIVATE);
selectionWindow.ForceRefreshSelectionContents();
placeSelWin();
break;
case 2:
SelectObject(m_hDC, m_hMask);
StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, RECT_HEIGHT(m_rcDest) - 1, -RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
SelectObject(m_hDC, m_hBm);
StretchBlt(m_hDC, RECT_WIDTH(m_rcDest) - 1, RECT_HEIGHT(m_rcDest) - 1, -RECT_WIDTH(m_rcDest), -RECT_HEIGHT(m_rcDest), m_hDC,
0, 0, RECT_WIDTH(m_rcDest), RECT_HEIGHT(m_rcDest), SRCCOPY);
break;
}
NotifyRefreshNeeded();
}
void SelectionModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY)
{
if (nStretchPercentX == 100 && nStretchPercentY == 100 && nSkewDegX == 0 && nSkewDegY == 0)
return;
imageModel.DeleteSelection();
imageModel.CopyPrevious();
INT oldWidth = RECT_WIDTH(m_rcDest);
INT oldHeight = RECT_HEIGHT(m_rcDest);
INT newWidth = oldWidth * nStretchPercentX / 100;
INT newHeight = oldHeight * nStretchPercentY / 100;
if (oldWidth != newWidth || oldHeight != newHeight)
{
SelectObject(m_hDC, m_hBm);
HBITMAP hbm0 = CopyDIBImage(m_hBm, newWidth, newHeight);
InsertFromHBITMAP(hbm0, m_rcDest.left, m_rcDest.top);
DeleteObject(hbm0);
}
if (nSkewDegX)
{
SelectObject(m_hDC, m_hBm);
HBITMAP hbm1 = SkewDIB(m_hDC, m_hBm, nSkewDegX, FALSE);
InsertFromHBITMAP(hbm1, m_rcDest.left, m_rcDest.top);
DeleteObject(hbm1);
}
if (nSkewDegY)
{
SelectObject(m_hDC, m_hBm);
HBITMAP hbm2 = SkewDIB(m_hDC, m_hBm, nSkewDegY, TRUE);
InsertFromHBITMAP(hbm2, m_rcDest.left, m_rcDest.top);
DeleteObject(hbm2);
}
selectionWindow.ShowWindow(SW_SHOWNOACTIVATE);
selectionWindow.ForceRefreshSelectionContents();
placeSelWin();
NotifyRefreshNeeded();
}
HBITMAP SelectionModel::GetBitmap() const
{
return m_hBm;
}
int SelectionModel::PtStackSize() const
{
return m_iPtSP;
}
void SelectionModel::DrawFramePoly(HDC hDCImage)
{
Poly(hDCImage, m_ptStack, m_iPtSP, 0, 0, 2, 0, FALSE, TRUE); /* draw the freehand selection inverted/xored */
}
void SelectionModel::SetSrcAndDestRectFromPoints(const POINT& ptFrom, const POINT& ptTo)
{
m_rcDest.left = m_rcSrc.left = min(ptFrom.x, ptTo.x);
m_rcDest.top = m_rcSrc.top = min(ptFrom.y, ptTo.y);
m_rcDest.right = m_rcSrc.right = max(ptFrom.x, ptTo.x);
m_rcDest.bottom = m_rcSrc.bottom = max(ptFrom.y, ptTo.y);
}
void SelectionModel::SetSrcRectSizeToZero()
{
m_rcSrc.right = m_rcSrc.left;
m_rcSrc.bottom = m_rcSrc.top;
}
BOOL SelectionModel::IsSrcRectSizeNonzero() const
{
return (RECT_WIDTH(m_rcSrc) != 0) && (RECT_HEIGHT(m_rcSrc) != 0);
}
void SelectionModel::ModifyDestRect(POINT& ptDelta, int iAction)
{
POINT ptDeltaUsed;
switch (iAction)
{
case ACTION_MOVE: /* move selection */
ptDeltaUsed.x = ptDelta.x;
ptDeltaUsed.y = ptDelta.y;
OffsetRect(&m_rcDest, ptDeltaUsed.x, ptDeltaUsed.y);
break;
case ACTION_RESIZE_TOP_LEFT: /* resize at upper left corner */
ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
m_rcDest.left += ptDeltaUsed.x;
m_rcDest.top += ptDeltaUsed.y;
break;
case ACTION_RESIZE_TOP: /* resize at top edge */
ptDeltaUsed.x = ptDelta.x;
ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
m_rcDest.top += ptDeltaUsed.y;
break;
case ACTION_RESIZE_TOP_RIGHT: /* resize at upper right corner */
ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
ptDeltaUsed.y = min(ptDelta.y, RECT_HEIGHT(m_rcDest) - 1);
m_rcDest.top += ptDeltaUsed.y;
m_rcDest.right += ptDeltaUsed.x;
break;
case ACTION_RESIZE_LEFT: /* resize at left edge */
ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
ptDeltaUsed.y = ptDelta.y;
m_rcDest.left += ptDeltaUsed.x;
break;
case ACTION_RESIZE_RIGHT: /* resize at right edge */
ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
ptDeltaUsed.y = ptDelta.y;
m_rcDest.right += ptDeltaUsed.x;
break;
case ACTION_RESIZE_BOTTOM_LEFT: /* resize at lower left corner */
ptDeltaUsed.x = min(ptDelta.x, RECT_WIDTH(m_rcDest) - 1);
ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
m_rcDest.left += ptDeltaUsed.x;
m_rcDest.bottom += ptDeltaUsed.y;
break;
case ACTION_RESIZE_BOTTOM: /* resize at bottom edge */
ptDeltaUsed.x = ptDelta.x;
ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
m_rcDest.bottom += ptDeltaUsed.y;
break;
case ACTION_RESIZE_BOTTOM_RIGHT: /* resize at lower right corner */
ptDeltaUsed.x = max(ptDelta.x, -(RECT_WIDTH(m_rcDest) - 1));
ptDeltaUsed.y = max(ptDelta.y, -(RECT_HEIGHT(m_rcDest) - 1));
m_rcDest.right += ptDeltaUsed.x;
m_rcDest.bottom += ptDeltaUsed.y;
break;
}
ptDelta.x -= ptDeltaUsed.x;
ptDelta.y -= ptDeltaUsed.y;
}
LONG SelectionModel::GetDestRectWidth() const
{
return m_rcDest.right - m_rcDest.left;
}
LONG SelectionModel::GetDestRectHeight() const
{
return m_rcDest.bottom - m_rcDest.top;
}
LONG SelectionModel::GetDestRectLeft() const
{
return m_rcDest.left;
}
LONG SelectionModel::GetDestRectTop() const
{
return m_rcDest.top;
}
void SelectionModel::NotifyRefreshNeeded()
{
selectionWindow.SendMessage(WM_SELECTIONMODELREFRESHNEEDED);
}
void SelectionModel::GetRect(LPRECT prc) const
{
*prc = m_rcDest;
}