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