Something went wrong. Try again.
Reactos
Something went wrong. Try again.
13 kB · 515 lines
C++
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516/* * PROJECT: PAINT for ReactOS * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) * PURPOSE: Keep track of selection parameters, notify listeners * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> * Copyright 2019 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> */
#include "precomp.h"
SelectionModel selectionModel;
/* FUNCTIONS ********************************************************/
SelectionModel::SelectionModel() : m_hbmColor(NULL) , m_hbmMask(NULL) , m_rgbBack(RGB(255, 255, 255)) , m_bShow(FALSE) , m_bContentChanged(FALSE){ m_rc.SetRectEmpty(); m_rcOld.SetRectEmpty(); m_ptHit = { -1, -1 };}
SelectionModel::~SelectionModel(){ ClearColorImage(); ClearMaskImage();}
void SelectionModel::DrawBackgroundPoly(HDC hDCImage, COLORREF crBg){ if (m_rcOld.IsRectEmpty()) return;
HGDIOBJ hbrOld = ::SelectObject(hDCImage, ::GetStockObject(DC_BRUSH)); ::SetDCBrushColor(hDCImage, crBg); ::MaskBlt(hDCImage, m_rcOld.left, m_rcOld.top, m_rcOld.Width(), m_rcOld.Height(), hDCImage, m_rcOld.left, m_rcOld.top, m_hbmMask, 0, 0, MAKEROP4(PATCOPY, SRCCOPY)); ::SelectObject(hDCImage, hbrOld);}
void SelectionModel::DrawBackgroundRect(HDC hDCImage, COLORREF crBg){ if (m_rcOld.IsRectEmpty()) return;
Rect(hDCImage, m_rcOld.left, m_rcOld.top, m_rcOld.right, m_rcOld.bottom, crBg, crBg, 0, 1);}
void SelectionModel::DrawBackground(HDC hDCImage, COLORREF crBg){ if (toolsModel.GetActiveTool() == TOOL_FREESEL) DrawBackgroundPoly(hDCImage, crBg); else DrawBackgroundRect(hDCImage, crBg);}
voidSelectionModel::DrawSelection(HDC hDCImage, COLORREF crBg, BOOL bBgTransparent, const CRect& rc, HBITMAP hbm){ if (rc.IsRectEmpty()) return;
BITMAP bm; if (!GetObjectW(hbm, sizeof(BITMAP), &bm)) return;
COLORREF keyColor = (bBgTransparent ? crBg : CLR_INVALID);
HDC hMemDC = CreateCompatibleDC(hDCImage); HGDIOBJ hbmOld = SelectObject(hMemDC, hbm); ColorKeyedMaskBlt(hDCImage, rc.left, rc.top, rc.Width(), rc.Height(), hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, m_hbmMask, keyColor); SelectObject(hMemDC, hbmOld); DeleteDC(hMemDC);}
void SelectionModel::setMask(const CRect& rc, HBITMAP hbmMask){ if (m_hbmMask) ::DeleteObject(m_hbmMask);
m_hbmMask = hbmMask; m_rc = m_rcOld = rc;}
HBITMAP SelectionModel::GetSelectionContents(){ HBITMAP hbmWhole = imageModel.LockBitmap(); HBITMAP hbmPart = getSubImage(hbmWhole, (IsLanded() ? m_rc : m_rcOld)); imageModel.UnlockBitmap(hbmWhole); if (!hbmPart) return NULL;
CRect rc = { 0, 0, m_rc.Width(), m_rc.Height() };
HDC hdcMem = ::CreateCompatibleDC(NULL); HBITMAP hbmNew = CreateColorDIB(rc.Width(), rc.Height(), paletteModel.GetBgColor()); HGDIOBJ hbmOld = ::SelectObject(hdcMem, hbmNew); selectionModel.DrawSelection(hdcMem, paletteModel.GetBgColor(), TRUE, rc, hbmPart); ::SelectObject(hdcMem, hbmOld); ::DeleteDC(hdcMem);
::DeleteObject(hbmPart); return hbmNew;}
BOOL SelectionModel::IsLanded() const{ return !m_hbmColor;}
BOOL SelectionModel::TakeOff(){ if (!IsLanded() || m_rc.IsRectEmpty()) return FALSE;
// The background color is needed for transparency of selection m_rgbBack = paletteModel.GetBgColor();
// Get the contents of the selection area ClearColorImage(); m_hbmColor = GetSelectionContents();
// RectSel doesn't need the mask image if (toolsModel.GetActiveTool() == TOOL_RECTSEL) ClearMaskImage();
// Save the selection area m_rcOld = m_rc;
imageModel.NotifyImageChanged(); return TRUE;}
void SelectionModel::Landing(){ if (IsLanded() && !m_bShow) { imageModel.NotifyImageChanged(); return; }
if (m_bContentChanged || (!m_rc.EqualRect(m_rcOld) && !m_rc.IsRectEmpty() && !m_rcOld.IsRectEmpty())) { CRect rc; rc.UnionRect(m_rc, m_rcOld); imageModel.PushImageForUndo(rc);
canvasWindow.m_drawing = FALSE; toolsModel.OnDrawOverlayOnImage(imageModel.GetDC()); }
HideSelection();}
void SelectionModel::InsertFromHBITMAP(HBITMAP hbmColor, INT x, INT y, HBITMAP hbmMask){ ::DeleteObject(m_hbmColor); m_hbmColor = hbmColor;
m_rc.left = x; m_rc.top = y; m_rc.right = x + GetDIBWidth(hbmColor); m_rc.bottom = y + GetDIBHeight(hbmColor);
if (hbmMask) { ::DeleteObject(m_hbmMask); m_hbmMask = hbmMask; } else { ClearMaskImage(); }
NotifyContentChanged();}
void SelectionModel::FlipHorizontally(){ TakeOff();
HDC hdcMem = ::CreateCompatibleDC(NULL); if (m_hbmMask) { ::SelectObject(hdcMem, m_hbmMask); ::StretchBlt(hdcMem, m_rc.Width() - 1, 0, -m_rc.Width(), m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); } if (m_hbmColor) { ::SelectObject(hdcMem, m_hbmColor); ::StretchBlt(hdcMem, m_rc.Width() - 1, 0, -m_rc.Width(), m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); } ::DeleteDC(hdcMem);
NotifyContentChanged();}
void SelectionModel::FlipVertically(){ TakeOff();
HDC hdcMem = ::CreateCompatibleDC(NULL); if (m_hbmMask) { ::SelectObject(hdcMem, m_hbmMask); ::StretchBlt(hdcMem, 0, m_rc.Height() - 1, m_rc.Width(), -m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); } if (m_hbmColor) { ::SelectObject(hdcMem, m_hbmColor); ::StretchBlt(hdcMem, 0, m_rc.Height() - 1, m_rc.Width(), -m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); } ::DeleteDC(hdcMem);
NotifyContentChanged();}
void SelectionModel::RotateNTimes90Degrees(int iN){ HBITMAP hbm; HGDIOBJ hbmOld; HDC hdcMem = ::CreateCompatibleDC(NULL);
switch (iN) { case 1: /* rotate 90 degrees */ case 3: /* rotate 270 degrees */ TakeOff();
if (m_hbmColor) { hbmOld = ::SelectObject(hdcMem, m_hbmColor); hbm = Rotate90DegreeBlt(hdcMem, m_rc.Width(), m_rc.Height(), iN == 1, FALSE); ::SelectObject(hdcMem, hbmOld); ::DeleteObject(m_hbmColor); m_hbmColor = hbm; } if (m_hbmMask) { hbmOld = ::SelectObject(hdcMem, m_hbmMask); hbm = Rotate90DegreeBlt(hdcMem, m_rc.Width(), m_rc.Height(), iN == 1, TRUE); ::SelectObject(hdcMem, hbmOld); ::DeleteObject(m_hbmMask); m_hbmMask = hbm; }
SwapWidthAndHeight(); break;
case 2: /* rotate 180 degrees */ TakeOff();
if (m_hbmColor) { hbmOld = ::SelectObject(hdcMem, m_hbmColor); ::StretchBlt(hdcMem, m_rc.Width() - 1, m_rc.Height() - 1, -m_rc.Width(), -m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); ::SelectObject(hdcMem, hbmOld); } if (m_hbmMask) { hbmOld = ::SelectObject(hdcMem, m_hbmMask); ::StretchBlt(hdcMem, m_rc.Width() - 1, m_rc.Height() - 1, -m_rc.Width(), -m_rc.Height(), hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); ::SelectObject(hdcMem, hbmOld); } break; }
::DeleteDC(hdcMem); NotifyContentChanged();}
static void AttachHBITMAP(HBITMAP *phbm, HBITMAP hbmNew){ if (hbmNew == NULL) return; ::DeleteObject(*phbm); *phbm = hbmNew;}
void SelectionModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY){ if (nStretchPercentX == 100 && nStretchPercentY == 100 && nSkewDegX == 0 && nSkewDegY == 0) return;
TakeOff();
INT oldWidth = m_rc.Width(), oldHeight = m_rc.Height(); INT newWidth = oldWidth * nStretchPercentX / 100; INT newHeight = oldHeight * nStretchPercentY / 100;
HBITMAP hbmColor = m_hbmColor, hbmMask = m_hbmMask;
if (hbmMask == NULL) hbmMask = CreateMonoBitmap(oldWidth, oldHeight, TRUE);
if (oldWidth != newWidth || oldHeight != newHeight) { AttachHBITMAP(&hbmColor, CopyDIBImage(hbmColor, newWidth, newHeight)); AttachHBITMAP(&hbmMask, CopyMonoImage(hbmMask, newWidth, newHeight)); }
HGDIOBJ hbmOld; HDC hDC = ::CreateCompatibleDC(NULL);
if (nSkewDegX) { hbmOld = ::SelectObject(hDC, hbmColor); AttachHBITMAP(&hbmColor, SkewDIB(hDC, hbmColor, nSkewDegX, FALSE)); ::SelectObject(hDC, hbmMask); AttachHBITMAP(&hbmMask, SkewDIB(hDC, hbmMask, nSkewDegX, FALSE, TRUE)); ::SelectObject(hDC, hbmOld); }
if (nSkewDegY) { hbmOld = ::SelectObject(hDC, hbmColor); AttachHBITMAP(&hbmColor, SkewDIB(hDC, hbmColor, nSkewDegY, TRUE)); ::SelectObject(hDC, hbmMask); AttachHBITMAP(&hbmMask, SkewDIB(hDC, hbmMask, nSkewDegY, TRUE, TRUE)); ::SelectObject(hDC, hbmOld); }
::DeleteDC(hDC);
InsertFromHBITMAP(hbmColor, m_rc.left, m_rc.top, hbmMask);
m_bShow = TRUE; NotifyContentChanged();}
void SelectionModel::SetRectFromPoints(const POINT& ptFrom, const POINT& ptTo){ m_rc = CRect(ptFrom, ptTo); m_rc.NormalizeRect();}
void SelectionModel::Dragging(HITTEST hit, POINT pt){ switch (hit) { case HIT_NONE: break; case HIT_UPPER_LEFT: m_rc.left += pt.x - m_ptHit.x; m_rc.top += pt.y - m_ptHit.y; break; case HIT_UPPER_CENTER: m_rc.top += pt.y - m_ptHit.y; break; case HIT_UPPER_RIGHT: m_rc.right += pt.x - m_ptHit.x; m_rc.top += pt.y - m_ptHit.y; break; case HIT_MIDDLE_LEFT: m_rc.left += pt.x - m_ptHit.x; break; case HIT_MIDDLE_RIGHT: m_rc.right += pt.x - m_ptHit.x; break; case HIT_LOWER_LEFT: m_rc.left += pt.x - m_ptHit.x; m_rc.bottom += pt.y - m_ptHit.y; break; case HIT_LOWER_CENTER: m_rc.bottom += pt.y - m_ptHit.y; break; case HIT_LOWER_RIGHT: m_rc.right += pt.x - m_ptHit.x; m_rc.bottom += pt.y - m_ptHit.y; break; case HIT_BORDER: case HIT_INNER: m_rc.OffsetRect(pt.x - m_ptHit.x, pt.y - m_ptHit.y); break; } m_ptHit = pt;}
void SelectionModel::ClearMaskImage(){ if (m_hbmMask) { ::DeleteObject(m_hbmMask); m_hbmMask = NULL; }}
void SelectionModel::ClearColorImage(){ if (m_hbmColor) { ::DeleteObject(m_hbmColor); m_hbmColor = NULL; }}
void SelectionModel::HideSelection(){ m_bShow = m_bContentChanged = FALSE; ClearColorImage(); ClearMaskImage(); m_rc.SetRectEmpty(); m_rcOld.SetRectEmpty(); imageModel.NotifyImageChanged();}
void SelectionModel::DeleteSelection(){ if (!m_bShow) return;
TakeOff(); imageModel.PushImageForUndo(); DrawBackground(imageModel.GetDC(), paletteModel.GetBgColor());
HideSelection();}
void SelectionModel::InvertSelection(){ TakeOff();
BITMAP bm; ::GetObjectW(m_hbmColor, sizeof(bm), &bm);
HDC hdc = ::CreateCompatibleDC(NULL); HGDIOBJ hbmOld = ::SelectObject(hdc, m_hbmColor); RECT rc = { 0, 0, bm.bmWidth, bm.bmHeight }; ::InvertRect(hdc, &rc); ::SelectObject(hdc, hbmOld); ::DeleteDC(hdc);
NotifyContentChanged();}
void SelectionModel::NotifyContentChanged(){ m_bContentChanged = TRUE; imageModel.NotifyImageChanged();}
void SelectionModel::SwapWidthAndHeight(){ INT cx = m_rc.Width(), cy = m_rc.Height(); m_rc.right = m_rc.left + cy; m_rc.bottom = m_rc.top + cx;}
HITTEST SelectionModel::hitTest(POINT ptCanvas){ if (!m_bShow) return HIT_NONE;
CRect rcSelection = m_rc; canvasWindow.ImageToCanvas(rcSelection); rcSelection.InflateRect(GRIP_SIZE, GRIP_SIZE); return getSizeBoxHitTest(ptCanvas, &rcSelection);}
void SelectionModel::drawFrameOnCanvas(HDC hCanvasDC){ if (!m_bShow) return;
CRect rcSelection = m_rc; canvasWindow.ImageToCanvas(rcSelection); rcSelection.InflateRect(GRIP_SIZE, GRIP_SIZE); drawSizeBoxes(hCanvasDC, &rcSelection, TRUE);}
void SelectionModel::moveSelection(INT xDelta, INT yDelta){ if (!m_bShow) return;
TakeOff(); m_rc.OffsetRect(xDelta, yDelta); canvasWindow.Invalidate();}
void SelectionModel::StretchSelection(BOOL bShrink){ if (!m_bShow) return;
TakeOff();
INT cx = m_rc.Width(), cy = m_rc.Height();
if (bShrink) m_rc.InflateRect(-cx / 4, -cy / 4); else m_rc.InflateRect(+cx / 2, +cy / 2);
// The selection area must exist there if (m_rc.Width() <= 0) m_rc.right = m_rc.left + 1; if (m_rc.Height() <= 0) m_rc.bottom = m_rc.top + 1;
imageModel.NotifyImageChanged();}