1 /*
2  * PROJECT:     PAINT for ReactOS
3  * LICENSE:     LGPL
4  * FILE:        base/applications/mspaint/drawing.cpp
5  * PURPOSE:     The drawing functions used by the tools
6  * PROGRAMMERS: Benedikt Freisen
7  */
8 
9 /* INCLUDES *********************************************************/
10 
11 #include "precomp.h"
12 
13 /* FUNCTIONS ********************************************************/
14 
15 void
16 Line(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, int thickness)
17 {
18     HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, thickness, color));
19     MoveToEx(hdc, x1, y1, NULL);
20     LineTo(hdc, x2, y2);
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 a, b;
120     HPEN oldPen;
121     HBRUSH oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(color));
122 
123     b = max(1, max(abs(x2 - x1), abs(y2 - y1)));
124     oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, color));
125     for(a = 0; a <= b; a++)
126         Rectangle(hdc,
127                   (x1 * (b - a) + x2 * a) / b - radius,
128                   (y1 * (b - a) + y2 * a) / b - radius,
129                   (x1 * (b - a) + x2 * a) / b + radius,
130                   (y1 * (b - a) + y2 * a) / b + radius);
131     DeleteObject(SelectObject(hdc, oldBrush));
132     DeleteObject(SelectObject(hdc, oldPen));
133 }
134 
135 void
136 Replace(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LONG radius)
137 {
138     LONG a, b, x, y;
139     b = max(1, max(abs(x2 - x1), abs(y2 - y1)));
140 
141     for(a = 0; a <= b; a++)
142         for(y = (y1 * (b - a) + y2 * a) / b - radius + 1;
143             y < (y1 * (b - a) + y2 * a) / b + radius + 1; y++)
144             for(x = (x1 * (b - a) + x2 * a) / b - radius + 1;
145                 x < (x1 * (b - a) + x2 * a) / b + radius + 1; x++)
146                 if (GetPixel(hdc, x, y) == fg)
147                     SetPixel(hdc, x, y, bg);
148 }
149 
150 void
151 Airbrush(HDC hdc, LONG x, LONG y, COLORREF color, LONG r)
152 {
153     LONG a, b;
154 
155     for(b = -r; b <= r; b++)
156         for(a = -r; a <= r; a++)
157             if ((a * a + b * b <= r * r) && (rand() % 4 == 0))
158                 SetPixel(hdc, x + a, y + b, color);
159 }
160 
161 void
162 Brush(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF color, LONG style)
163 {
164     HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_SOLID, 1, color));
165     HBRUSH oldBrush = (HBRUSH) SelectObject(hdc, CreateSolidBrush(color));
166     LONG a, b;
167     b = max(1, max(abs(x2 - x1), abs(y2 - y1)));
168     switch (style)
169     {
170         case 0:
171             for(a = 0; a <= b; a++)
172                 Ellipse(hdc, (x1 * (b - a) + x2 * a) / b - 3, (y1 * (b - a) + y2 * a) / b - 3,
173                         (x1 * (b - a) + x2 * a) / b + 4, (y1 * (b - a) + y2 * a) / b + 4);
174             break;
175         case 1:
176             for(a = 0; a <= b; a++)
177                 Ellipse(hdc,
178                         (x1 * (b - a) + x2 * a) / b - 2,
179                         (y1 * (b - a) + y2 * a) / b - 2,
180                         (x1 * (b - a) + x2 * a) / b + 2,
181                         (y1 * (b - a) + y2 * a) / b + 2);
182             break;
183         case 2:
184             MoveToEx(hdc, x1, y1, NULL);
185             LineTo(hdc, x2, y2);
186             SetPixel(hdc, x2, y2, color);
187             break;
188         case 3:
189             for(a = 0; a <= b; a++)
190                 Rectangle(hdc,
191                           (x1 * (b - a) + x2 * a) / b - 4,
192                           (y1 * (b - a) + y2 * a) / b - 4,
193                           (x1 * (b - a) + x2 * a) / b + 4,
194                           (y1 * (b - a) + y2 * a) / b + 4);
195             break;
196         case 4:
197             for(a = 0; a <= b; a++)
198                 Rectangle(hdc, (x1 * (b - a) + x2 * a) / b - 2, (y1 * (b - a) + y2 * a) / b - 2,
199                           (x1 * (b - a) + x2 * a) / b + 3, (y1 * (b - a) + y2 * a) / b + 3);
200             break;
201         case 5:
202             for(a = 0; a <= b; a++)
203                 Rectangle(hdc, (x1 * (b - a) + x2 * a) / b - 1, (y1 * (b - a) + y2 * a) / b - 1,
204                           (x1 * (b - a) + x2 * a) / b + 1, (y1 * (b - a) + y2 * a) / b + 1);
205             break;
206         case 6:
207         case 7:
208         case 8:
209         case 9:
210         case 10:
211         case 11:
212         {
213             POINT offsTop[] = {{3, -3}, {2, -2}, {0, 0},
214                                {-4, -4}, {-2, -2}, {-1, 0}};
215             POINT offsBtm[] = {{-3, 3}, {-2, 2}, {-1, 1},
216                                {3, 3}, {2, 2}, {0, 1}};
217             LONG idx = style - 6;
218             POINT pts[4];
219             pts[0].x = x1 + offsTop[idx].x;
220             pts[0].y = y1 + offsTop[idx].y;
221             pts[1].x = x1 + offsBtm[idx].x;
222             pts[1].y = y1 + offsBtm[idx].y;
223             pts[2].x = x2 + offsBtm[idx].x;
224             pts[2].y = y2 + offsBtm[idx].y;
225             pts[3].x = x2 + offsTop[idx].x;
226             pts[3].y = y2 + offsTop[idx].y;
227             Polygon(hdc, pts, 4);
228             break;
229         }
230     }
231     DeleteObject(SelectObject(hdc, oldBrush));
232     DeleteObject(SelectObject(hdc, oldPen));
233 }
234 
235 void
236 RectSel(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2)
237 {
238     HBRUSH oldBrush;
239     LOGBRUSH logbrush;
240     HPEN oldPen = (HPEN) SelectObject(hdc, CreatePen(PS_DOT, 1, GetSysColor(COLOR_HIGHLIGHT)));
241     UINT oldRop = GetROP2(hdc);
242 
243     SetROP2(hdc, R2_NOTXORPEN);
244 
245     logbrush.lbStyle = BS_HOLLOW;
246     logbrush.lbColor = 0;
247     logbrush.lbHatch = 0;
248     oldBrush = (HBRUSH) SelectObject(hdc, CreateBrushIndirect(&logbrush));
249     Rectangle(hdc, x1, y1, x2, y2);
250     DeleteObject(SelectObject(hdc, oldBrush));
251     DeleteObject(SelectObject(hdc, oldPen));
252 
253     SetROP2(hdc, oldRop);
254 }
255 
256 void
257 Text(HDC hdc, LONG x1, LONG y1, LONG x2, LONG y2, COLORREF fg, COLORREF bg, LPCTSTR lpchText, HFONT font, LONG style)
258 {
259     INT iSaveDC = SaveDC(hdc); // We will modify the clipping region. Save now.
260 
261     RECT rc;
262     SetRect(&rc, x1, y1, x2, y2);
263 
264     if (style == 0) // Transparent
265     {
266         SetBkMode(hdc, TRANSPARENT);
267         GetBkColor(hdc);
268     }
269     else // Opaque
270     {
271         SetBkMode(hdc, OPAQUE);
272         SetBkColor(hdc, bg);
273 
274         HBRUSH hbr = CreateSolidBrush(bg);
275         FillRect(hdc, &rc, hbr); // Fill the background
276         DeleteObject(hbr);
277     }
278 
279     IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
280 
281     HGDIOBJ hFontOld = SelectObject(hdc, font);
282     SetTextColor(hdc, fg);
283     const UINT uFormat = DT_LEFT | DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP |
284                          DT_EXPANDTABS | DT_WORDBREAK;
285     DrawText(hdc, lpchText, -1, &rc, uFormat);
286     SelectObject(hdc, hFontOld);
287 
288     RestoreDC(hdc, iSaveDC); // Restore
289 }
290 
291 BOOL
292 ColorKeyedMaskBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight,
293                   HDC hdcSrc, int nXSrc, int nYSrc, int nSrcWidth, int nSrcHeight,
294                   HBITMAP hbmMask, COLORREF keyColor)
295 {
296     HDC hTempDC1, hTempDC2;
297     HBITMAP hbmTempColor, hbmTempMask;
298     HGDIOBJ hbmOld1, hbmOld2;
299 
300     if (hbmMask == NULL)
301     {
302         if (keyColor == CLR_INVALID)
303         {
304             ::StretchBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
305                          hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY);
306         }
307         else
308         {
309             ::GdiTransparentBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
310                                 hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor);
311         }
312         return TRUE;
313     }
314     else if (nWidth == nSrcWidth && nHeight == nSrcHeight && keyColor == CLR_INVALID)
315     {
316         ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight,
317                   hdcSrc, nXSrc, nYSrc, hbmMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
318         return TRUE;
319     }
320 
321     hTempDC1 = ::CreateCompatibleDC(hdcDest);
322     hTempDC2 = ::CreateCompatibleDC(hdcDest);
323     hbmTempMask = ::CreateBitmap(nWidth, nHeight, 1, 1, NULL);
324     hbmTempColor = CreateColorDIB(nWidth, nHeight, RGB(255, 255, 255));
325 
326     // hbmTempMask <-- hbmMask (stretched)
327     hbmOld1 = ::SelectObject(hTempDC1, hbmMask);
328     hbmOld2 = ::SelectObject(hTempDC2, hbmTempMask);
329     ::StretchBlt(hTempDC2, 0, 0, nWidth, nHeight, hTempDC1, 0, 0, nSrcWidth, nSrcHeight, SRCCOPY);
330     ::SelectObject(hTempDC2, hbmOld2);
331     ::SelectObject(hTempDC1, hbmOld1);
332 
333     hbmOld1 = ::SelectObject(hTempDC1, hbmTempColor);
334     if (keyColor == CLR_INVALID)
335     {
336         // hbmTempColor <-- hdcSrc (stretched)
337         ::StretchBlt(hTempDC1, 0, 0, nWidth, nHeight,
338                      hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, SRCCOPY);
339 
340         // hdcDest <-- hbmTempColor (masked)
341         ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0,
342                   hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
343     }
344     else
345     {
346         // hbmTempColor <-- hdcDest
347         ::BitBlt(hTempDC1, 0, 0, nWidth, nHeight, hdcDest, nXDest, nYDest, SRCCOPY);
348 
349         // hbmTempColor <-- hdcSrc (color key)
350         ::GdiTransparentBlt(hTempDC1, 0, 0, nWidth, nHeight,
351                             hdcSrc, nXSrc, nYSrc, nSrcWidth, nSrcHeight, keyColor);
352 
353         // hdcDest <-- hbmTempColor (masked)
354         ::MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hTempDC1, 0, 0,
355                   hbmTempMask, 0, 0, MAKEROP4(SRCCOPY, 0xAA0029));
356     }
357     ::SelectObject(hTempDC1, hbmOld1);
358 
359     ::DeleteObject(hbmTempColor);
360     ::DeleteObject(hbmTempMask);
361     ::DeleteDC(hTempDC2);
362     ::DeleteDC(hTempDC1);
363 
364     return TRUE;
365 }
366 
367 void DrawXorRect(HDC hdc, const RECT *prc)
368 {
369     HGDIOBJ oldPen = ::SelectObject(hdc, ::CreatePen(PS_SOLID, 0, RGB(255, 255, 255)));
370     HGDIOBJ oldBrush = ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH));
371     INT oldRop2 = SetROP2(hdc, R2_XORPEN);
372     ::Rectangle(hdc, prc->left, prc->top, prc->right, prc->bottom);
373     ::SetROP2(hdc, oldRop2);
374     ::SelectObject(hdc, oldBrush);
375     ::DeleteObject(::SelectObject(hdc, oldPen));
376 }
377