Reactos
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
12void
13Line(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
22void
23Rect(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
37void
38Ellp(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
52void
53RRect(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
67void
68Poly(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
92void
93Bezier(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
106void
107Fill(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
114void
115Erase(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
131void
132Replace(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
152void
153Airbrush(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
165void
166Brush(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
232void
233RectSel(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
253void
254Text(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
286BOOL
287ColorKeyedMaskBlt(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
362void 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}