1 /* 2 * PROJECT: PAINT for ReactOS 3 * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) 4 * PURPOSE: The drawing functions used by the tools 5 * COPYRIGHT: Copyright 2015 Benedikt Freisen <b.freisen@gmx.net> 6 */ 7 8 /* INCLUDES *********************************************************/ 9 10 #include "precomp.h" 11 12 /* FUNCTIONS ********************************************************/ 13 14 void 15 Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, int thickness) 16 { 17 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, color)); 18 MoveToEx(hdc, x1, y1, NULL); 19 LineTo(hdc, x2, y2); 20 SetPixelV(hdc, x2, y2, color); 21 DeleteObject(SelectObject(hdc, oldPen)); 22 } 23 24 void 25 Rect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style) 26 { 27 HBRUSH oldBrush; 28 LOGBRUSH logbrush; 29 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg)); 30 logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID; 31 logbrush.lbColor = (style == 2) ? fg : bg; 32 logbrush.lbHatch = 0; 33 oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush)); 34 Rectangle(hdc, x1, y1, x2, y2); 35 DeleteObject(SelectObject(hdc, oldBrush)); 36 DeleteObject(SelectObject(hdc, oldPen)); 37 } 38 39 void 40 Ellp(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style) 41 { 42 HBRUSH oldBrush; 43 LOGBRUSH logbrush; 44 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg)); 45 logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID; 46 logbrush.lbColor = (style == 2) ? fg : bg; 47 logbrush.lbHatch = 0; 48 oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush)); 49 Ellipse(hdc, x1, y1, x2, y2); 50 DeleteObject(SelectObject(hdc, oldBrush)); 51 DeleteObject(SelectObject(hdc, oldPen)); 52 } 53 54 void 55 RRect(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, int thickness, int style) 56 { 57 LOGBRUSH logbrush; 58 HBRUSH oldBrush; 59 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg)); 60 logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID; 61 logbrush.lbColor = (style == 2) ? fg : bg; 62 logbrush.lbHatch = 0; 63 oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush)); 64 RoundRect(hdc, x1, y1, x2, y2, 16, 16); 65 DeleteObject(SelectObject(hdc, oldBrush)); 66 DeleteObject(SelectObject(hdc, oldPen)); 67 } 68 69 void 70 Poly(HDC hdc, POINT * lpPoints, int nCount, COLORREF fg, COLORREF bg, int thickness, int style, BOOL closed, BOOL inverted) 71 { 72 LOGBRUSH logbrush; 73 HBRUSH oldBrush; 74 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, fg)); 75 UINT oldRop = GetROP2(hdc); 76 77 if (inverted) 78 SetROP2(hdc, R2_NOTXORPEN); 79 80 logbrush.lbStyle = (style == 0) ? BS_HOLLOW : BS_SOLID; 81 logbrush.lbColor = (style == 2) ? fg : bg; 82 logbrush.lbHatch = 0; 83 oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush)); 84 if (closed) 85 Polygon(hdc, lpPoints, nCount); 86 else 87 Polyline(hdc, lpPoints, nCount); 88 DeleteObject(SelectObject(hdc, oldBrush)); 89 DeleteObject(SelectObject(hdc, oldPen)); 90 91 SetROP2(hdc, oldRop); 92 } 93 94 void 95 Bezier(HDC hdc, POINT p1, POINT p2, POINT p3, POINT p4, COLORREF color, int thickness) 96 { 97 HPEN oldPen; 98 POINT fourPoints[4]; 99 fourPoints[0] = p1; 100 fourPoints[1] = p2; 101 fourPoints[2] = p3; 102 fourPoints[3] = p4; 103 oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, color)); 104 PolyBezier(hdc, fourPoints, 4); 105 DeleteObject(SelectObject(hdc, oldPen)); 106 } 107 108 void 109 Fill(HDC hdc, LONG x, LONG y, COLORREF color) 110 { 111 HBRUSH oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(color)); 112 ExtFloodFill(hdc, x, y, GetPixel(hdc, x, y), FLOODFILLSURFACE); 113 DeleteObject(SelectObject(hdc, oldBrush)); 114 } 115 116 void 117 Erase(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG radius) 118 { 119 LONG b = max(1, max(labs(x2 - x1), labs(y2 - y1))); 120 HBRUSH hbr = ::CreateSolidBrush(color); 121 122 for (LONG a = 0; a <= b; a++) 123 { 124 LONG cx = (x1 * (b - a) + x2 * a) / b; 125 LONG cy = (y1 * (b - a) + y2 * a) / b; 126 RECT rc = { cx - radius, cy - radius, cx + radius, cy + radius }; 127 ::FillRect(hdc, &rc, hbr); 128 } 129 130 ::DeleteObject(hbr); 131 } 132 133 void 134 Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LONG radius) 135 { 136 LONG b = max(1, max(labs(x2 - x1), labs(y2 - y1))); 137 138 for (LONG a = 0; a <= b; a++) 139 { 140 LONG cx = (x1 * (b - a) + x2 * a) / b; 141 LONG cy = (y1 * (b - a) + y2 * a) / b; 142 RECT rc = { cx - radius, cy - radius, cx + radius, cy + radius }; 143 for (LONG y = rc.top; y < rc.bottom; ++y) 144 { 145 for (LONG x = rc.left; x < rc.right; ++x) 146 { 147 if (::GetPixel(hdc, x, y) == fg) 148 ::SetPixelV(hdc, x, y, bg); 149 } 150 } 151 } 152 } 153 154 void 155 Airbrush(HDC hdc, LONG x, LONG y, COLORREF color, LONG r) 156 { 157 for (LONG dy = -r; dy <= r; dy++) 158 { 159 for (LONG dx = -r; dx <= r; dx++) 160 { 161 if ((dx * dx + dy * dy <= r * r) && (rand() % r == 0)) 162 ::SetPixelV(hdc, x + dx, y + dy, color); 163 } 164 } 165 } 166 167 void 168 Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG style, INT thickness) 169 { 170 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, color)); 171 HBRUSH oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(color)); 172 173 if (thickness <= 1) 174 { 175 Line(hdc, x1, y1, x2, y2, color, thickness); 176 } 177 else 178 { 179 LONG a, b = max(1, max(labs(x2 - x1), labs(y2 - y1))); 180 switch ((BrushStyle)style) 181 { 182 case BrushStyleRound: 183 for (a = 0; a <= b; a++) 184 { 185 Ellipse(hdc, 186 (x1 * (b - a) + x2 * a) / b - (thickness / 2), 187 (y1 * (b - a) + y2 * a) / b - (thickness / 2), 188 (x1 * (b - a) + x2 * a) / b + (thickness / 2), 189 (y1 * (b - a) + y2 * a) / b + (thickness / 2)); 190 } 191 break; 192 193 case BrushStyleSquare: 194 for (a = 0; a <= b; a++) 195 { 196 Rectangle(hdc, 197 (x1 * (b - a) + x2 * a) / b - (thickness / 2), 198 (y1 * (b - a) + y2 * a) / b - (thickness / 2), 199 (x1 * (b - a) + x2 * a) / b + (thickness / 2), 200 (y1 * (b - a) + y2 * a) / b + (thickness / 2)); 201 } 202 break; 203 204 case BrushStyleForeSlash: 205 case BrushStyleBackSlash: 206 { 207 POINT offsetTop, offsetBottom; 208 if ((BrushStyle)style == BrushStyleForeSlash) 209 { 210 offsetTop = { (thickness - 1) / 2, -(thickness - 1) / 2 }; 211 offsetBottom = { -thickness / 2, thickness / 2 }; 212 } 213 else 214 { 215 offsetTop = { -thickness / 2, -thickness / 2 }; 216 offsetBottom = { (thickness - 1) / 2, (thickness - 1) / 2 }; 217 } 218 POINT points[4] = 219 { 220 { x1 + offsetTop.x, y1 + offsetTop.y }, 221 { x1 + offsetBottom.x, y1 + offsetBottom.y }, 222 { x2 + offsetBottom.x, y2 + offsetBottom.y }, 223 { x2 + offsetTop.x, y2 + offsetTop.y }, 224 }; 225 Polygon(hdc, points, _countof(points)); 226 break; 227 } 228 } 229 } 230 DeleteObject(SelectObject(hdc, oldBrush)); 231 DeleteObject(SelectObject(hdc, oldPen)); 232 } 233 234 void 235 RectSel(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2) 236 { 237 HBRUSH oldBrush; 238 LOGBRUSH logbrush; 239 HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_DOT, 1, GetSysColor(COLOR_HIGHLIGHT))); 240 UINT oldRop = GetROP2(hdc); 241 242 SetROP2(hdc, R2_NOTXORPEN); 243 244 logbrush.lbStyle = BS_HOLLOW; 245 logbrush.lbColor = 0; 246 logbrush.lbHatch = 0; 247 oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush)); 248 Rectangle(hdc, x1, y1, x2, y2); 249 DeleteObject(SelectObject(hdc, oldBrush)); 250 DeleteObject(SelectObject(hdc, oldPen)); 251 252 SetROP2(hdc, oldRop); 253 } 254 255 void 256 Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCWSTR lpchText, HFONT font, LONG style) 257 { 258 INT iSaveDC = ::SaveDC(hdc); // We will modify the clipping region. Save now. 259 260 RECT rc; 261 ::SetRect(&rc, x1, y1, x2, y2); 262 263 if (style == 0) // Transparent 264 { 265 ::SetBkMode(hdc, TRANSPARENT); 266 } 267 else // Opaque 268 { 269 ::SetBkMode(hdc, OPAQUE); 270 ::SetBkColor(hdc, bg); 271 272 HBRUSH hbr = ::CreateSolidBrush(bg); 273 ::FillRect(hdc, &rc, hbr); // Fill the background 274 ::DeleteObject(hbr); 275 } 276 277 IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); 278 279 HGDIOBJ hFontOld = ::SelectObject(hdc, font); 280 ::SetTextColor(hdc, fg); 281 const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP | 282 DT_EXPANDTABS | DT_WORDBREAK; 283 ::DrawTextW(hdc, lpchText, -1, &rc, uFormat); 284 ::SelectObject(hdc, hFontOld); 285 286 ::RestoreDC(hdc, iSaveDC); // Restore 287 } 288 289 BOOL 290 ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, 291 HDC hdcSrc, int nXSrc, int nYSrc, int nSrcWidth, int nSrcHeight, 292 HBITMAP hbmMask, COLORREF keyColor) 293 { 294 HDC hTempDC1, hTempDC2; 295 HBITMAP hbmTempColor, hbmTempMask; 296 HGDIOBJ hbmOld1, hbmOld2; 297 298 if (hbmMask == NULL) 299 { 300 if (keyColor == CLR_INVALID) 301 { 302 ::StretchBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, 303 hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY); 304 } 305 else 306 { 307 ::GdiTransparentBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, 308 hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor); 309 } 310 return TRUE; 311 } 312 else if (nWidth == nSrcWidth && nHeight == nSrcHeight && keyColor == CLR_INVALID) 313 { 314 ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, 315 hdcSrc, nXSrc, nYSrc, hbmMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029)); 316 return TRUE; 317 } 318 319 hTempDC1 = ::CreateCompatibleDC(hdcDest); 320 hTempDC2 = ::CreateCompatibleDC(hdcDest); 321 hbmTempMask = ::CreateBitmap(nWidth, nHeight, 1, 1, NULL); 322 hbmTempColor = CreateColorDIB(nWidth, nHeight, RGB(255, 255, 255)); 323 324 // hbmTempMask <-- hbmMask (stretched) 325 hbmOld1 = ::SelectObject(hTempDC1, hbmMask); 326 hbmOld2 = ::SelectObject(hTempDC2, hbmTempMask); 327 ::StretchBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC1, 0, 0, nSrcWidth, nSrcHeight, SRCCOPY); 328 ::SelectObject(hTempDC2, hbmOld2); 329 ::SelectObject(hTempDC1, hbmOld1); 330 331 hbmOld1 = ::SelectObject(hTempDC1, hbmTempColor); 332 if (keyColor == CLR_INVALID) 333 { 334 // hbmTempColor <-- hdcSrc (stretched) 335 ::StretchBlt(hTempDC1, 0, 0, nWidth, nHeight, 336 hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY); 337 338 // hdcDest <-- hbmTempColor (masked) 339 ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0, 340 hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029)); 341 } 342 else 343 { 344 // hbmTempColor <-- hdcDest 345 ::BitBlt(hTempDC1, 0, 0, nWidth, nHeight, hdcDest, nXDest, nYDest, SRCCOPY); 346 347 // hbmTempColor <-- hdcSrc (color key) 348 ::GdiTransparentBlt(hTempDC1, 0, 0, nWidth, nHeight, 349 hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor); 350 351 // hdcDest <-- hbmTempColor (masked) 352 ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0, 353 hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029)); 354 } 355 ::SelectObject(hTempDC1, hbmOld1); 356 357 ::DeleteObject(hbmTempColor); 358 ::DeleteObject(hbmTempMask); 359 ::DeleteDC(hTempDC2); 360 ::DeleteDC(hTempDC1); 361 362 return TRUE; 363 } 364 365 void DrawXorRect(HDC hdc, const RECT *prc) 366 { 367 HGDIOBJ oldPen = ::SelectObject(hdc, ::CreatePen(PS_SOLID, 0, RGB(0, 0, 0))); 368 HGDIOBJ oldBrush = ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH)); 369 INT oldRop2 = SetROP2(hdc, R2_NOTXORPEN); 370 ::Rectangle(hdc, prc->left, prc->top, prc->right, prc->bottom); 371 ::SetROP2(hdc, oldRop2); 372 ::SelectObject(hdc, oldBrush); 373 ::DeleteObject(::SelectObject(hdc, oldPen)); 374 } 375