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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.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 /**
41  * Draw a line from the current position to the given position.\n
42  * @msdn{dd145029}
43  * @param hdc device context
44  * @param nXEnd ending x position
45  * @param nYEnd ending y position
46  * @return nonzero if successful, 0 otherwise
47  */
gdi_rop_color(UINT32 rop,BYTE * pixelPtr,UINT32 pen,UINT32 format)48 static BOOL gdi_rop_color(UINT32 rop, BYTE* pixelPtr, UINT32 pen, UINT32 format)
49 {
50 	const UINT32 srcPixel = ReadColor(pixelPtr, format);
51 	UINT32 dstPixel;
52 
53 	switch (rop)
54 	{
55 		case GDI_R2_BLACK: /* LineTo_BLACK */
56 			dstPixel = FreeRDPGetColor(format, 0, 0, 0, 0xFF);
57 			break;
58 
59 		case GDI_R2_NOTMERGEPEN: /* LineTo_NOTMERGEPEN */
60 			dstPixel = ~(srcPixel | pen);
61 			break;
62 
63 		case GDI_R2_MASKNOTPEN: /* LineTo_MASKNOTPEN */
64 			dstPixel = srcPixel & ~pen;
65 			break;
66 
67 		case GDI_R2_NOTCOPYPEN: /* LineTo_NOTCOPYPEN */
68 			dstPixel = ~pen;
69 			break;
70 
71 		case GDI_R2_MASKPENNOT: /* LineTo_MASKPENNOT */
72 			dstPixel = pen & ~srcPixel;
73 			break;
74 
75 		case GDI_R2_NOT: /* LineTo_NOT */
76 			dstPixel = ~srcPixel;
77 			break;
78 
79 		case GDI_R2_XORPEN: /* LineTo_XORPEN */
80 			dstPixel = srcPixel ^ pen;
81 			break;
82 
83 		case GDI_R2_NOTMASKPEN: /* LineTo_NOTMASKPEN */
84 			dstPixel = ~(srcPixel & pen);
85 			break;
86 
87 		case GDI_R2_MASKPEN: /* LineTo_MASKPEN */
88 			dstPixel = srcPixel & pen;
89 			break;
90 
91 		case GDI_R2_NOTXORPEN: /* LineTo_NOTXORPEN */
92 			dstPixel = ~(srcPixel ^ pen);
93 			break;
94 
95 		case GDI_R2_NOP: /* LineTo_NOP */
96 			dstPixel = srcPixel;
97 			break;
98 
99 		case GDI_R2_MERGENOTPEN: /* LineTo_MERGENOTPEN */
100 			dstPixel = srcPixel | ~pen;
101 			break;
102 
103 		case GDI_R2_COPYPEN: /* LineTo_COPYPEN */
104 			dstPixel = pen;
105 			break;
106 
107 		case GDI_R2_MERGEPENNOT: /* LineTo_MERGEPENNOT */
108 			dstPixel = srcPixel | ~pen;
109 			break;
110 
111 		case GDI_R2_MERGEPEN: /* LineTo_MERGEPEN */
112 			dstPixel = srcPixel | pen;
113 			break;
114 
115 		case GDI_R2_WHITE: /* LineTo_WHITE */
116 			dstPixel = FreeRDPGetColor(format, 0xFF, 0xFF, 0xFF, 0xFF);
117 			break;
118 
119 		default:
120 			return FALSE;
121 	}
122 
123 	return WriteColor(pixelPtr, format, dstPixel);
124 }
125 
gdi_LineTo(HGDI_DC hdc,UINT32 nXEnd,UINT32 nYEnd)126 BOOL gdi_LineTo(HGDI_DC hdc, UINT32 nXEnd, UINT32 nYEnd)
127 {
128 	INT32 x, y;
129 	INT32 x1, y1;
130 	INT32 x2, y2;
131 	INT32 e, e2;
132 	INT32 dx, dy;
133 	INT32 sx, sy;
134 	INT32 bx1, by1;
135 	INT32 bx2, by2;
136 	HGDI_BITMAP bmp;
137 	UINT32 pen;
138 	INT32 rop2 = gdi_GetROP2(hdc);
139 	x1 = hdc->pen->posX;
140 	y1 = hdc->pen->posY;
141 	x2 = nXEnd;
142 	y2 = nYEnd;
143 	dx = (x1 > x2) ? x1 - x2 : x2 - x1;
144 	dy = (y1 > y2) ? y1 - y2 : y2 - y1;
145 	sx = (x1 < x2) ? 1 : -1;
146 	sy = (y1 < y2) ? 1 : -1;
147 	e = dx - dy;
148 	x = x1;
149 	y = y1;
150 	bmp = (HGDI_BITMAP)hdc->selectedObject;
151 
152 	if (hdc->clip->null)
153 	{
154 		bx1 = (x1 < x2) ? x1 : x2;
155 		by1 = (y1 < y2) ? y1 : y2;
156 		bx2 = (x1 > x2) ? x1 : x2;
157 		by2 = (y1 > y2) ? y1 : y2;
158 	}
159 	else
160 	{
161 		bx1 = hdc->clip->x;
162 		by1 = hdc->clip->y;
163 		bx2 = bx1 + hdc->clip->w - 1;
164 		by2 = by1 + hdc->clip->h - 1;
165 	}
166 
167 	bx1 = MAX(bx1, 0);
168 	by1 = MAX(by1, 0);
169 	bx2 = MIN(bx2, bmp->width - 1);
170 	by2 = MIN(by2, bmp->height - 1);
171 
172 	if (!gdi_InvalidateRegion(hdc, bx1, by1, bx2 - bx1 + 1, by2 - by1 + 1))
173 		return FALSE;
174 
175 	pen = gdi_GetPenColor(hdc->pen, bmp->format);
176 
177 	while (1)
178 	{
179 		if (!(x == x2 && y == y2))
180 		{
181 			if ((x >= bx1 && x <= bx2) && (y >= by1 && y <= by2))
182 			{
183 				BYTE* pixel = gdi_GetPointer(bmp, x, y);
184 				gdi_rop_color(rop2, pixel, pen, bmp->format);
185 			}
186 		}
187 		else
188 		{
189 			break;
190 		}
191 
192 		e2 = 2 * e;
193 
194 		if (e2 > -dy)
195 		{
196 			e -= dy;
197 			x += sx;
198 		}
199 
200 		if (e2 < dx)
201 		{
202 			e += dx;
203 			y += sy;
204 		}
205 	}
206 
207 	return TRUE;
208 }
209 
210 /**
211  * Draw one or more straight lines
212  * @param hdc device context
213  * @param lppt array of points
214  * @param cCount number of points
215  * @return nonzero on success, 0 otherwise
216  */
gdi_PolylineTo(HGDI_DC hdc,GDI_POINT * lppt,DWORD cCount)217 BOOL gdi_PolylineTo(HGDI_DC hdc, GDI_POINT* lppt, DWORD cCount)
218 {
219 	DWORD i;
220 
221 	for (i = 0; i < cCount; i++)
222 	{
223 		if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y))
224 			return FALSE;
225 
226 		if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL))
227 			return FALSE;
228 	}
229 
230 	return TRUE;
231 }
232 
233 /**
234  * Draw one or more straight lines
235  * @param hdc device context
236  * @param lppt array of points
237  * @param cPoints number of points
238  * @return nonzero on success, 0 otherwise
239  */
gdi_Polyline(HGDI_DC hdc,GDI_POINT * lppt,UINT32 cPoints)240 BOOL gdi_Polyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32 cPoints)
241 {
242 	if (cPoints > 0)
243 	{
244 		UINT32 i;
245 		GDI_POINT pt;
246 
247 		if (!gdi_MoveToEx(hdc, lppt[0].x, lppt[0].y, &pt))
248 			return FALSE;
249 
250 		for (i = 0; i < cPoints; i++)
251 		{
252 			if (!gdi_LineTo(hdc, lppt[i].x, lppt[i].y))
253 				return FALSE;
254 
255 			if (!gdi_MoveToEx(hdc, lppt[i].x, lppt[i].y, NULL))
256 				return FALSE;
257 		}
258 
259 		if (!gdi_MoveToEx(hdc, pt.x, pt.y, NULL))
260 			return FALSE;
261 	}
262 
263 	return TRUE;
264 }
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  */
gdi_PolyPolyline(HGDI_DC hdc,GDI_POINT * lppt,UINT32 * lpdwPolyPoints,DWORD cCount)274 BOOL gdi_PolyPolyline(HGDI_DC hdc, GDI_POINT* lppt, UINT32* lpdwPolyPoints, DWORD cCount)
275 {
276 	UINT32 cPoints;
277 	DWORD i, j = 0;
278 
279 	for (i = 0; i < cCount; i++)
280 	{
281 		cPoints = lpdwPolyPoints[i];
282 
283 		if (!gdi_Polyline(hdc, &lppt[j], cPoints))
284 			return FALSE;
285 
286 		j += cPoints;
287 	}
288 
289 	return TRUE;
290 }
291 
292 /**
293  * Move pen from the current device context to a new position.
294  * @param hdc device context
295  * @param X x position
296  * @param Y y position
297  * @return nonzero on success, 0 otherwise
298  */
299 
gdi_MoveToEx(HGDI_DC hdc,UINT32 X,UINT32 Y,HGDI_POINT lpPoint)300 BOOL gdi_MoveToEx(HGDI_DC hdc, UINT32 X, UINT32 Y, HGDI_POINT lpPoint)
301 {
302 	if (lpPoint != NULL)
303 	{
304 		lpPoint->x = hdc->pen->posX;
305 		lpPoint->y = hdc->pen->posY;
306 	}
307 
308 	hdc->pen->posX = X;
309 	hdc->pen->posY = Y;
310 	return TRUE;
311 }
312