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