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
Line(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF color,int thickness)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
Rect(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF fg,COLORREF bg,int thickness,int style)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
Ellp(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF fg,COLORREF bg,int thickness,int style)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
RRect(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF fg,COLORREF bg,int thickness,int style)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
Poly(HDC hdc,POINT * lpPoints,int nCount,COLORREF fg,COLORREF bg,int thickness,int style,BOOL closed,BOOL inverted)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
Bezier(HDC hdc,POINT p1,POINT p2,POINT p3,POINT p4,COLORREF color,int thickness)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
Fill(HDC hdc,LONG x,LONG y,COLORREF color)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
Erase(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF color,LONG radius)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
Replace(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF fg,COLORREF bg,LONG radius)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
Airbrush(HDC hdc,LONG x,LONG y,COLORREF color,LONG r)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
Brush(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF color,LONG style,INT thickness)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
RectSel(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2)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
Text(HDC hdc,LONG x1,LONG y1,LONG x2,LONG y2,COLORREF fg,COLORREF bg,LPCWSTR lpchText,HFONT font,LONG style)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
ColorKeyedMaskBlt(HDC hdcDest,int nXDest,int nYDest,int nWidth,int nHeight,HDC hdcSrc,int nXSrc,int nYSrc,int nSrcWidth,int nSrcHeight,HBITMAP hbmMask,COLORREF keyColor)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
DrawXorRect(HDC hdc,const RECT * prc)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