/src/FreeRDP/libfreerdp/gdi/line.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * GDI Line Functions |
4 | | * |
5 | | * Copyright 2010-2011 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * Copyright 2016 Armin Novak <armin.novak@thincast.com> |
7 | | * Copyright 2016 Thincast Technologies GmbH |
8 | | * |
9 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
10 | | * you may not use this file except in compliance with the License. |
11 | | * You may obtain a copy of the License at |
12 | | * |
13 | | * http://www.apache.org/licenses/LICENSE-2.0 |
14 | | * |
15 | | * Unless required by applicable law or agreed to in writing, software |
16 | | * distributed under the License is distributed on an "AS IS" BASIS, |
17 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
18 | | * See the License for the specific language governing permissions and |
19 | | * limitations under the License. |
20 | | */ |
21 | | |
22 | | #include <freerdp/config.h> |
23 | | |
24 | | #include <stdio.h> |
25 | | #include <string.h> |
26 | | #include <stdlib.h> |
27 | | |
28 | | #include <winpr/assert.h> |
29 | | |
30 | | #include <freerdp/freerdp.h> |
31 | | #include <freerdp/gdi/gdi.h> |
32 | | #include <freerdp/gdi/pen.h> |
33 | | #include <freerdp/gdi/bitmap.h> |
34 | | #include <freerdp/gdi/region.h> |
35 | | |
36 | | #include "drawing.h" |
37 | | #include "clipping.h" |
38 | | #include "line.h" |
39 | | |
40 | | static BOOL gdi_rop_color(INT32 rop, BYTE* pixelPtr, UINT32 pen, UINT32 format) |
41 | 0 | { |
42 | 0 | WINPR_ASSERT(pixelPtr); |
43 | 0 | const UINT32 srcPixel = FreeRDPReadColor(pixelPtr, format); |
44 | 0 | UINT32 dstPixel = 0; |
45 | |
|
46 | 0 | switch (rop) |
47 | 0 | { |
48 | 0 | case GDI_R2_BLACK: /* LineTo_BLACK */ |
49 | 0 | dstPixel = FreeRDPGetColor(format, 0, 0, 0, 0xFF); |
50 | 0 | break; |
51 | | |
52 | 0 | case GDI_R2_NOTMERGEPEN: /* LineTo_NOTMERGEPEN */ |
53 | 0 | dstPixel = ~(srcPixel | pen); |
54 | 0 | break; |
55 | | |
56 | 0 | case GDI_R2_MASKNOTPEN: /* LineTo_MASKNOTPEN */ |
57 | 0 | dstPixel = srcPixel & ~pen; |
58 | 0 | break; |
59 | | |
60 | 0 | case GDI_R2_NOTCOPYPEN: /* LineTo_NOTCOPYPEN */ |
61 | 0 | dstPixel = ~pen; |
62 | 0 | break; |
63 | | |
64 | 0 | case GDI_R2_MASKPENNOT: /* LineTo_MASKPENNOT */ |
65 | 0 | dstPixel = pen & ~srcPixel; |
66 | 0 | break; |
67 | | |
68 | 0 | case GDI_R2_NOT: /* LineTo_NOT */ |
69 | 0 | dstPixel = ~srcPixel; |
70 | 0 | break; |
71 | | |
72 | 0 | case GDI_R2_XORPEN: /* LineTo_XORPEN */ |
73 | 0 | dstPixel = srcPixel ^ pen; |
74 | 0 | break; |
75 | | |
76 | 0 | case GDI_R2_NOTMASKPEN: /* LineTo_NOTMASKPEN */ |
77 | 0 | dstPixel = ~(srcPixel & pen); |
78 | 0 | break; |
79 | | |
80 | 0 | case GDI_R2_MASKPEN: /* LineTo_MASKPEN */ |
81 | 0 | dstPixel = srcPixel & pen; |
82 | 0 | break; |
83 | | |
84 | 0 | case GDI_R2_NOTXORPEN: /* LineTo_NOTXORPEN */ |
85 | 0 | dstPixel = ~(srcPixel ^ pen); |
86 | 0 | break; |
87 | | |
88 | 0 | case GDI_R2_NOP: /* LineTo_NOP */ |
89 | 0 | dstPixel = srcPixel; |
90 | 0 | break; |
91 | | |
92 | 0 | case GDI_R2_MERGENOTPEN: /* LineTo_MERGENOTPEN */ |
93 | 0 | dstPixel = srcPixel | ~pen; |
94 | 0 | break; |
95 | | |
96 | 0 | case GDI_R2_COPYPEN: /* LineTo_COPYPEN */ |
97 | 0 | dstPixel = pen; |
98 | 0 | break; |
99 | | |
100 | 0 | case GDI_R2_MERGEPENNOT: /* LineTo_MERGEPENNOT */ |
101 | 0 | dstPixel = srcPixel | ~pen; |
102 | 0 | break; |
103 | | |
104 | 0 | case GDI_R2_MERGEPEN: /* LineTo_MERGEPEN */ |
105 | 0 | dstPixel = srcPixel | pen; |
106 | 0 | break; |
107 | | |
108 | 0 | case GDI_R2_WHITE: /* LineTo_WHITE */ |
109 | 0 | dstPixel = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF); |
110 | 0 | break; |
111 | | |
112 | 0 | default: |
113 | 0 | return FALSE; |
114 | 0 | } |
115 | | |
116 | 0 | return FreeRDPWriteColor(pixelPtr, format, dstPixel); |
117 | 0 | } |
118 | | |
119 | | BOOL gdi_LineTo(HGDI_DC hdc, INT32 nXEnd, INT32 nYEnd) |
120 | 0 | { |
121 | 0 | INT32 e2 = 0; |
122 | 0 | UINT32 pen = 0; |
123 | |
|
124 | 0 | WINPR_ASSERT(hdc); |
125 | 0 | const INT32 rop2 = gdi_GetROP2(hdc); |
126 | |
|
127 | 0 | const INT32 x1 = hdc->pen->posX; |
128 | 0 | const INT32 y1 = hdc->pen->posY; |
129 | 0 | const INT32 x2 = WINPR_ASSERTING_INT_CAST(int32_t, nXEnd); |
130 | 0 | const INT32 y2 = WINPR_ASSERTING_INT_CAST(int32_t, nYEnd); |
131 | 0 | const INT32 dx = (x1 > x2) ? x1 - x2 : x2 - x1; |
132 | 0 | const INT32 dy = (y1 > y2) ? y1 - y2 : y2 - y1; |
133 | 0 | const INT32 sx = (x1 < x2) ? 1 : -1; |
134 | 0 | const INT32 sy = (y1 < y2) ? 1 : -1; |
135 | 0 | INT32 e = dx - dy; |
136 | 0 | INT32 x = x1; |
137 | 0 | INT32 y = y1; |
138 | |
|
139 | 0 | WINPR_ASSERT(hdc->clip); |
140 | 0 | INT32 bx1 = 0; |
141 | 0 | INT32 by1 = 0; |
142 | 0 | INT32 bx2 = 0; |
143 | 0 | INT32 by2 = 0; |
144 | 0 | if (hdc->clip->null) |
145 | 0 | { |
146 | 0 | bx1 = (x1 < x2) ? x1 : x2; |
147 | 0 | by1 = (y1 < y2) ? y1 : y2; |
148 | 0 | bx2 = (x1 > x2) ? x1 : x2; |
149 | 0 | by2 = (y1 > y2) ? y1 : y2; |
150 | 0 | } |
151 | 0 | else |
152 | 0 | { |
153 | 0 | bx1 = hdc->clip->x; |
154 | 0 | by1 = hdc->clip->y; |
155 | 0 | bx2 = bx1 + hdc->clip->w - 1; |
156 | 0 | by2 = by1 + hdc->clip->h - 1; |
157 | 0 | } |
158 | |
|
159 | 0 | HGDI_BITMAP bmp = (HGDI_BITMAP)hdc->selectedObject; |
160 | 0 | WINPR_ASSERT(bmp); |
161 | | |
162 | 0 | bx1 = MAX(bx1, 0); |
163 | 0 | by1 = MAX(by1, 0); |
164 | 0 | bx2 = MIN(bx2, bmp->width - 1); |
165 | 0 | by2 = MIN(by2, bmp->height - 1); |
166 | |
|
167 | 0 | if (!gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1)) |
168 | 0 | return FALSE; |
169 | | |
170 | 0 | pen = gdi_GetPenColor(hdc->pen, bmp->format); |
171 | |
|
172 | 0 | while (1) |
173 | 0 | { |
174 | 0 | if (!(x == x2 && y == y2)) |
175 | 0 | { |
176 | 0 | if ((x >= bx1 && x <= bx2) && (y >= by1 && y <= by2)) |
177 | 0 | { |
178 | 0 | BYTE* pixel = gdi_GetPointer(bmp, WINPR_ASSERTING_INT_CAST(uint32_t, x), |
179 | 0 | WINPR_ASSERTING_INT_CAST(uint32_t, y)); |
180 | 0 | WINPR_ASSERT(pixel); |
181 | 0 | gdi_rop_color(rop2, pixel, pen, bmp->format); |
182 | 0 | } |
183 | 0 | } |
184 | 0 | else |
185 | 0 | { |
186 | 0 | break; |
187 | 0 | } |
188 | | |
189 | 0 | e2 = 2 * e; |
190 | |
|
191 | 0 | if (e2 > -dy) |
192 | 0 | { |
193 | 0 | e -= dy; |
194 | 0 | x += sx; |
195 | 0 | } |
196 | |
|
197 | 0 | if (e2 < dx) |
198 | 0 | { |
199 | 0 | e += dx; |
200 | 0 | y += sy; |
201 | 0 | } |
202 | 0 | } |
203 | | |
204 | 0 | return TRUE; |
205 | 0 | } |
206 | | |
207 | | /** |
208 | | * Draw one or more straight lines |
209 | | * @param hdc device context |
210 | | * @param lppt array of points |
211 | | * @param cCount number of points |
212 | | * @return nonzero on success, 0 otherwise |
213 | | */ |
214 | | BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount) |
215 | 0 | { |
216 | 0 | WINPR_ASSERT(hdc); |
217 | 0 | WINPR_ASSERT(lppt || (cCount == 0)); |
218 | | |
219 | 0 | for (DWORD i = 0; i < cCount; i++) |
220 | 0 | { |
221 | 0 | if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) |
222 | 0 | return FALSE; |
223 | | |
224 | 0 | if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) |
225 | 0 | return FALSE; |
226 | 0 | } |
227 | | |
228 | 0 | return TRUE; |
229 | 0 | } |
230 | | |
231 | | /** |
232 | | * Draw one or more straight lines |
233 | | * @param hdc device context |
234 | | * @param lppt array of points |
235 | | * @param cPoints number of points |
236 | | * @return nonzero on success, 0 otherwise |
237 | | */ |
238 | | BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints) |
239 | 0 | { |
240 | 0 | WINPR_ASSERT(hdc); |
241 | 0 | WINPR_ASSERT(lppt || (cPoints == 0)); |
242 | | |
243 | 0 | if (cPoints > 0) |
244 | 0 | { |
245 | 0 | GDI_POINT pt = { 0 }; |
246 | |
|
247 | 0 | if (!gdi_MoveToEx(hdc, lppt[0].x, lppt[0].y, &pt)) |
248 | 0 | return FALSE; |
249 | | |
250 | 0 | for (UINT32 i = 0; i < cPoints; i++) |
251 | 0 | { |
252 | 0 | if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y)) |
253 | 0 | return FALSE; |
254 | | |
255 | 0 | if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL)) |
256 | 0 | return FALSE; |
257 | 0 | } |
258 | | |
259 | 0 | if (!gdi_MoveToEx(hdc, pt.x, pt.y, NULL)) |
260 | 0 | return FALSE; |
261 | 0 | } |
262 | | |
263 | 0 | return TRUE; |
264 | 0 | } |
265 | | |
266 | | /** |
267 | | * Draw multiple series of connected line segments |
268 | | * @param hdc device context |
269 | | * @param lppt array of points |
270 | | * @param lpdwPolyPoints array of numbers of points per series |
271 | | * @param cCount count of entries in lpdwPolyPoints |
272 | | * @return nonzero on success, 0 otherwise |
273 | | */ |
274 | | BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, const UINT32* lpdwPolyPoints, DWORD cCount) |
275 | 0 | { |
276 | 0 | DWORD j = 0; |
277 | |
|
278 | 0 | WINPR_ASSERT(hdc); |
279 | 0 | WINPR_ASSERT(lppt || (cCount == 0)); |
280 | 0 | WINPR_ASSERT(lpdwPolyPoints || (cCount == 0)); |
281 | | |
282 | 0 | for (DWORD i = 0; i < cCount; i++) |
283 | 0 | { |
284 | 0 | const UINT32 cPoints = lpdwPolyPoints[i]; |
285 | |
|
286 | 0 | if (!gdi_Polyline(hdc, &lppt[j], cPoints)) |
287 | 0 | return FALSE; |
288 | | |
289 | 0 | j += cPoints; |
290 | 0 | } |
291 | | |
292 | 0 | return TRUE; |
293 | 0 | } |
294 | | |
295 | | /** |
296 | | * Move pen from the current device context to a new position. |
297 | | * @param hdc device context |
298 | | * @param X x position |
299 | | * @param Y y position |
300 | | * @return nonzero on success, 0 otherwise |
301 | | */ |
302 | | |
303 | | BOOL gdi_MoveToEx(HGDI_DC hdc, INT32 X, INT32 Y, HGDI_POINT lpPoint) |
304 | 0 | { |
305 | 0 | WINPR_ASSERT(hdc); |
306 | | |
307 | 0 | if (lpPoint != NULL) |
308 | 0 | { |
309 | 0 | lpPoint->x = hdc->pen->posX; |
310 | 0 | lpPoint->y = hdc->pen->posY; |
311 | 0 | } |
312 | |
|
313 | 0 | hdc->pen->posX = X; |
314 | 0 | hdc->pen->posY = Y; |
315 | 0 | return TRUE; |
316 | 0 | } |