Reactos
at master 515 lines 14 kB view raw
1/* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: Keep track of selection parameters, notify listeners 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 * Copyright 2019 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 7 */ 8 9#include "precomp.h" 10 11SelectionModel selectionModel; 12 13/* FUNCTIONS ********************************************************/ 14 15SelectionModel::SelectionModel() 16 : m_hbmColor(NULL) 17 , m_hbmMask(NULL) 18 , m_rgbBack(RGB(255, 255, 255)) 19 , m_bShow(FALSE) 20 , m_bContentChanged(FALSE) 21{ 22 m_rc.SetRectEmpty(); 23 m_rcOld.SetRectEmpty(); 24 m_ptHit = { -1, -1 }; 25} 26 27SelectionModel::~SelectionModel() 28{ 29 ClearColorImage(); 30 ClearMaskImage(); 31} 32 33void SelectionModel::DrawBackgroundPoly(HDC hDCImage, COLORREF crBg) 34{ 35 if (m_rcOld.IsRectEmpty()) 36 return; 37 38 HGDIOBJ hbrOld = ::SelectObject(hDCImage, ::GetStockObject(DC_BRUSH)); 39 ::SetDCBrushColor(hDCImage, crBg); 40 ::MaskBlt(hDCImage, m_rcOld.left, m_rcOld.top, m_rcOld.Width(), m_rcOld.Height(), 41 hDCImage, m_rcOld.left, m_rcOld.top, m_hbmMask, 0, 0, MAKEROP4(PATCOPY, SRCCOPY)); 42 ::SelectObject(hDCImage, hbrOld); 43} 44 45void SelectionModel::DrawBackgroundRect(HDC hDCImage, COLORREF crBg) 46{ 47 if (m_rcOld.IsRectEmpty()) 48 return; 49 50 Rect(hDCImage, m_rcOld.left, m_rcOld.top, m_rcOld.right, m_rcOld.bottom, crBg, crBg, 0, 1); 51} 52 53void SelectionModel::DrawBackground(HDC hDCImage, COLORREF crBg) 54{ 55 if (toolsModel.GetActiveTool() == TOOL_FREESEL) 56 DrawBackgroundPoly(hDCImage, crBg); 57 else 58 DrawBackgroundRect(hDCImage, crBg); 59} 60 61void 62SelectionModel::DrawSelection(HDC hDCImage, COLORREF crBg, BOOL bBgTransparent, const CRect& rc, 63 HBITMAP hbm) 64{ 65 if (rc.IsRectEmpty()) 66 return; 67 68 BITMAP bm; 69 if (!GetObjectW(hbm, sizeof(BITMAP), &bm)) 70 return; 71 72 COLORREF keyColor = (bBgTransparent ? crBg : CLR_INVALID); 73 74 HDC hMemDC = CreateCompatibleDC(hDCImage); 75 HGDIOBJ hbmOld = SelectObject(hMemDC, hbm); 76 ColorKeyedMaskBlt(hDCImage, rc.left, rc.top, rc.Width(), rc.Height(), 77 hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, m_hbmMask, keyColor); 78 SelectObject(hMemDC, hbmOld); 79 DeleteDC(hMemDC); 80} 81 82void SelectionModel::setMask(const CRect& rc, HBITMAP hbmMask) 83{ 84 if (m_hbmMask) 85 ::DeleteObject(m_hbmMask); 86 87 m_hbmMask = hbmMask; 88 m_rc = m_rcOld = rc; 89} 90 91HBITMAP SelectionModel::GetSelectionContents() 92{ 93 HBITMAP hbmWhole = imageModel.LockBitmap(); 94 HBITMAP hbmPart = getSubImage(hbmWhole, (IsLanded() ? m_rc : m_rcOld)); 95 imageModel.UnlockBitmap(hbmWhole); 96 if (!hbmPart) 97 return NULL; 98 99 CRect rc = { 0, 0, m_rc.Width(), m_rc.Height() }; 100 101 HDC hdcMem = ::CreateCompatibleDC(NULL); 102 HBITMAP hbmNew = CreateColorDIB(rc.Width(), rc.Height(), paletteModel.GetBgColor()); 103 HGDIOBJ hbmOld = ::SelectObject(hdcMem, hbmNew); 104 selectionModel.DrawSelection(hdcMem, paletteModel.GetBgColor(), TRUE, rc, hbmPart); 105 ::SelectObject(hdcMem, hbmOld); 106 ::DeleteDC(hdcMem); 107 108 ::DeleteObject(hbmPart); 109 return hbmNew; 110} 111 112BOOL SelectionModel::IsLanded() const 113{ 114 return !m_hbmColor; 115} 116 117BOOL SelectionModel::TakeOff() 118{ 119 if (!IsLanded() || m_rc.IsRectEmpty()) 120 return FALSE; 121 122 // The background color is needed for transparency of selection 123 m_rgbBack = paletteModel.GetBgColor(); 124 125 // Get the contents of the selection area 126 ClearColorImage(); 127 m_hbmColor = GetSelectionContents(); 128 129 // RectSel doesn't need the mask image 130 if (toolsModel.GetActiveTool() == TOOL_RECTSEL) 131 ClearMaskImage(); 132 133 // Save the selection area 134 m_rcOld = m_rc; 135 136 imageModel.NotifyImageChanged(); 137 return TRUE; 138} 139 140void SelectionModel::Landing() 141{ 142 if (IsLanded() && !m_bShow) 143 { 144 imageModel.NotifyImageChanged(); 145 return; 146 } 147 148 if (m_bContentChanged || 149 (!m_rc.EqualRect(m_rcOld) && !m_rc.IsRectEmpty() && !m_rcOld.IsRectEmpty())) 150 { 151 CRect rc; 152 rc.UnionRect(m_rc, m_rcOld); 153 imageModel.PushImageForUndo(rc); 154 155 canvasWindow.m_drawing = FALSE; 156 toolsModel.OnDrawOverlayOnImage(imageModel.GetDC()); 157 } 158 159 HideSelection(); 160} 161 162void SelectionModel::InsertFromHBITMAP(HBITMAP hbmColor, INT x, INT y, HBITMAP hbmMask) 163{ 164 ::DeleteObject(m_hbmColor); 165 m_hbmColor = hbmColor; 166 167 m_rc.left = x; 168 m_rc.top = y; 169 m_rc.right = x + GetDIBWidth(hbmColor); 170 m_rc.bottom = y + GetDIBHeight(hbmColor); 171 172 if (hbmMask) 173 { 174 ::DeleteObject(m_hbmMask); 175 m_hbmMask = hbmMask; 176 } 177 else 178 { 179 ClearMaskImage(); 180 } 181 182 NotifyContentChanged(); 183} 184 185void SelectionModel::FlipHorizontally() 186{ 187 TakeOff(); 188 189 HDC hdcMem = ::CreateCompatibleDC(NULL); 190 if (m_hbmMask) 191 { 192 ::SelectObject(hdcMem, m_hbmMask); 193 ::StretchBlt(hdcMem, m_rc.Width() - 1, 0, -m_rc.Width(), m_rc.Height(), 194 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 195 } 196 if (m_hbmColor) 197 { 198 ::SelectObject(hdcMem, m_hbmColor); 199 ::StretchBlt(hdcMem, m_rc.Width() - 1, 0, -m_rc.Width(), m_rc.Height(), 200 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 201 } 202 ::DeleteDC(hdcMem); 203 204 NotifyContentChanged(); 205} 206 207void SelectionModel::FlipVertically() 208{ 209 TakeOff(); 210 211 HDC hdcMem = ::CreateCompatibleDC(NULL); 212 if (m_hbmMask) 213 { 214 ::SelectObject(hdcMem, m_hbmMask); 215 ::StretchBlt(hdcMem, 0, m_rc.Height() - 1, m_rc.Width(), -m_rc.Height(), 216 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 217 } 218 if (m_hbmColor) 219 { 220 ::SelectObject(hdcMem, m_hbmColor); 221 ::StretchBlt(hdcMem, 0, m_rc.Height() - 1, m_rc.Width(), -m_rc.Height(), 222 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 223 } 224 ::DeleteDC(hdcMem); 225 226 NotifyContentChanged(); 227} 228 229void SelectionModel::RotateNTimes90Degrees(int iN) 230{ 231 HBITMAP hbm; 232 HGDIOBJ hbmOld; 233 HDC hdcMem = ::CreateCompatibleDC(NULL); 234 235 switch (iN) 236 { 237 case 1: /* rotate 90 degrees */ 238 case 3: /* rotate 270 degrees */ 239 TakeOff(); 240 241 if (m_hbmColor) 242 { 243 hbmOld = ::SelectObject(hdcMem, m_hbmColor); 244 hbm = Rotate90DegreeBlt(hdcMem, m_rc.Width(), m_rc.Height(), iN == 1, FALSE); 245 ::SelectObject(hdcMem, hbmOld); 246 ::DeleteObject(m_hbmColor); 247 m_hbmColor = hbm; 248 } 249 if (m_hbmMask) 250 { 251 hbmOld = ::SelectObject(hdcMem, m_hbmMask); 252 hbm = Rotate90DegreeBlt(hdcMem, m_rc.Width(), m_rc.Height(), iN == 1, TRUE); 253 ::SelectObject(hdcMem, hbmOld); 254 ::DeleteObject(m_hbmMask); 255 m_hbmMask = hbm; 256 } 257 258 SwapWidthAndHeight(); 259 break; 260 261 case 2: /* rotate 180 degrees */ 262 TakeOff(); 263 264 if (m_hbmColor) 265 { 266 hbmOld = ::SelectObject(hdcMem, m_hbmColor); 267 ::StretchBlt(hdcMem, m_rc.Width() - 1, m_rc.Height() - 1, -m_rc.Width(), -m_rc.Height(), 268 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 269 ::SelectObject(hdcMem, hbmOld); 270 } 271 if (m_hbmMask) 272 { 273 hbmOld = ::SelectObject(hdcMem, m_hbmMask); 274 ::StretchBlt(hdcMem, m_rc.Width() - 1, m_rc.Height() - 1, -m_rc.Width(), -m_rc.Height(), 275 hdcMem, 0, 0, m_rc.Width(), m_rc.Height(), SRCCOPY); 276 ::SelectObject(hdcMem, hbmOld); 277 } 278 break; 279 } 280 281 ::DeleteDC(hdcMem); 282 NotifyContentChanged(); 283} 284 285static void AttachHBITMAP(HBITMAP *phbm, HBITMAP hbmNew) 286{ 287 if (hbmNew == NULL) 288 return; 289 ::DeleteObject(*phbm); 290 *phbm = hbmNew; 291} 292 293void SelectionModel::StretchSkew(int nStretchPercentX, int nStretchPercentY, int nSkewDegX, int nSkewDegY) 294{ 295 if (nStretchPercentX == 100 && nStretchPercentY == 100 && nSkewDegX == 0 && nSkewDegY == 0) 296 return; 297 298 TakeOff(); 299 300 INT oldWidth = m_rc.Width(), oldHeight = m_rc.Height(); 301 INT newWidth = oldWidth * nStretchPercentX / 100; 302 INT newHeight = oldHeight * nStretchPercentY / 100; 303 304 HBITMAP hbmColor = m_hbmColor, hbmMask = m_hbmMask; 305 306 if (hbmMask == NULL) 307 hbmMask = CreateMonoBitmap(oldWidth, oldHeight, TRUE); 308 309 if (oldWidth != newWidth || oldHeight != newHeight) 310 { 311 AttachHBITMAP(&hbmColor, CopyDIBImage(hbmColor, newWidth, newHeight)); 312 AttachHBITMAP(&hbmMask, CopyMonoImage(hbmMask, newWidth, newHeight)); 313 } 314 315 HGDIOBJ hbmOld; 316 HDC hDC = ::CreateCompatibleDC(NULL); 317 318 if (nSkewDegX) 319 { 320 hbmOld = ::SelectObject(hDC, hbmColor); 321 AttachHBITMAP(&hbmColor, SkewDIB(hDC, hbmColor, nSkewDegX, FALSE)); 322 ::SelectObject(hDC, hbmMask); 323 AttachHBITMAP(&hbmMask, SkewDIB(hDC, hbmMask, nSkewDegX, FALSE, TRUE)); 324 ::SelectObject(hDC, hbmOld); 325 } 326 327 if (nSkewDegY) 328 { 329 hbmOld = ::SelectObject(hDC, hbmColor); 330 AttachHBITMAP(&hbmColor, SkewDIB(hDC, hbmColor, nSkewDegY, TRUE)); 331 ::SelectObject(hDC, hbmMask); 332 AttachHBITMAP(&hbmMask, SkewDIB(hDC, hbmMask, nSkewDegY, TRUE, TRUE)); 333 ::SelectObject(hDC, hbmOld); 334 } 335 336 ::DeleteDC(hDC); 337 338 InsertFromHBITMAP(hbmColor, m_rc.left, m_rc.top, hbmMask); 339 340 m_bShow = TRUE; 341 NotifyContentChanged(); 342} 343 344void SelectionModel::SetRectFromPoints(const POINT& ptFrom, const POINT& ptTo) 345{ 346 m_rc = CRect(ptFrom, ptTo); 347 m_rc.NormalizeRect(); 348} 349 350void SelectionModel::Dragging(HITTEST hit, POINT pt) 351{ 352 switch (hit) 353 { 354 case HIT_NONE: 355 break; 356 case HIT_UPPER_LEFT: 357 m_rc.left += pt.x - m_ptHit.x; 358 m_rc.top += pt.y - m_ptHit.y; 359 break; 360 case HIT_UPPER_CENTER: 361 m_rc.top += pt.y - m_ptHit.y; 362 break; 363 case HIT_UPPER_RIGHT: 364 m_rc.right += pt.x - m_ptHit.x; 365 m_rc.top += pt.y - m_ptHit.y; 366 break; 367 case HIT_MIDDLE_LEFT: 368 m_rc.left += pt.x - m_ptHit.x; 369 break; 370 case HIT_MIDDLE_RIGHT: 371 m_rc.right += pt.x - m_ptHit.x; 372 break; 373 case HIT_LOWER_LEFT: 374 m_rc.left += pt.x - m_ptHit.x; 375 m_rc.bottom += pt.y - m_ptHit.y; 376 break; 377 case HIT_LOWER_CENTER: 378 m_rc.bottom += pt.y - m_ptHit.y; 379 break; 380 case HIT_LOWER_RIGHT: 381 m_rc.right += pt.x - m_ptHit.x; 382 m_rc.bottom += pt.y - m_ptHit.y; 383 break; 384 case HIT_BORDER: 385 case HIT_INNER: 386 m_rc.OffsetRect(pt.x - m_ptHit.x, pt.y - m_ptHit.y); 387 break; 388 } 389 m_ptHit = pt; 390} 391 392void SelectionModel::ClearMaskImage() 393{ 394 if (m_hbmMask) 395 { 396 ::DeleteObject(m_hbmMask); 397 m_hbmMask = NULL; 398 } 399} 400 401void SelectionModel::ClearColorImage() 402{ 403 if (m_hbmColor) 404 { 405 ::DeleteObject(m_hbmColor); 406 m_hbmColor = NULL; 407 } 408} 409 410void SelectionModel::HideSelection() 411{ 412 m_bShow = m_bContentChanged = FALSE; 413 ClearColorImage(); 414 ClearMaskImage(); 415 m_rc.SetRectEmpty(); 416 m_rcOld.SetRectEmpty(); 417 imageModel.NotifyImageChanged(); 418} 419 420void SelectionModel::DeleteSelection() 421{ 422 if (!m_bShow) 423 return; 424 425 TakeOff(); 426 imageModel.PushImageForUndo(); 427 DrawBackground(imageModel.GetDC(), paletteModel.GetBgColor()); 428 429 HideSelection(); 430} 431 432void SelectionModel::InvertSelection() 433{ 434 TakeOff(); 435 436 BITMAP bm; 437 ::GetObjectW(m_hbmColor, sizeof(bm), &bm); 438 439 HDC hdc = ::CreateCompatibleDC(NULL); 440 HGDIOBJ hbmOld = ::SelectObject(hdc, m_hbmColor); 441 RECT rc = { 0, 0, bm.bmWidth, bm.bmHeight }; 442 ::InvertRect(hdc, &rc); 443 ::SelectObject(hdc, hbmOld); 444 ::DeleteDC(hdc); 445 446 NotifyContentChanged(); 447} 448 449void SelectionModel::NotifyContentChanged() 450{ 451 m_bContentChanged = TRUE; 452 imageModel.NotifyImageChanged(); 453} 454 455void SelectionModel::SwapWidthAndHeight() 456{ 457 INT cx = m_rc.Width(), cy = m_rc.Height(); 458 m_rc.right = m_rc.left + cy; 459 m_rc.bottom = m_rc.top + cx; 460} 461 462HITTEST SelectionModel::hitTest(POINT ptCanvas) 463{ 464 if (!m_bShow) 465 return HIT_NONE; 466 467 CRect rcSelection = m_rc; 468 canvasWindow.ImageToCanvas(rcSelection); 469 rcSelection.InflateRect(GRIP_SIZE, GRIP_SIZE); 470 return getSizeBoxHitTest(ptCanvas, &rcSelection); 471} 472 473void SelectionModel::drawFrameOnCanvas(HDC hCanvasDC) 474{ 475 if (!m_bShow) 476 return; 477 478 CRect rcSelection = m_rc; 479 canvasWindow.ImageToCanvas(rcSelection); 480 rcSelection.InflateRect(GRIP_SIZE, GRIP_SIZE); 481 drawSizeBoxes(hCanvasDC, &rcSelection, TRUE); 482} 483 484void SelectionModel::moveSelection(INT xDelta, INT yDelta) 485{ 486 if (!m_bShow) 487 return; 488 489 TakeOff(); 490 m_rc.OffsetRect(xDelta, yDelta); 491 canvasWindow.Invalidate(); 492} 493 494void SelectionModel::StretchSelection(BOOL bShrink) 495{ 496 if (!m_bShow) 497 return; 498 499 TakeOff(); 500 501 INT cx = m_rc.Width(), cy = m_rc.Height(); 502 503 if (bShrink) 504 m_rc.InflateRect(-cx / 4, -cy / 4); 505 else 506 m_rc.InflateRect(+cx / 2, +cy / 2); 507 508 // The selection area must exist there 509 if (m_rc.Width() <= 0) 510 m_rc.right = m_rc.left + 1; 511 if (m_rc.Height() <= 0) 512 m_rc.bottom = m_rc.top + 1; 513 514 imageModel.NotifyImageChanged(); 515}