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