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