1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Graphical Objects
4  *
5  * Copyright 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 <winpr/crt.h>
27 
28 #include <freerdp/log.h>
29 #include <freerdp/gdi/dc.h>
30 #include <freerdp/gdi/shape.h>
31 #include <freerdp/gdi/region.h>
32 #include <freerdp/gdi/bitmap.h>
33 
34 #include "clipping.h"
35 #include "drawing.h"
36 #include "brush.h"
37 #include "graphics.h"
38 
39 #define TAG FREERDP_TAG("gdi")
40 /* Bitmap Class */
41 
gdi_create_bitmap(rdpGdi * gdi,UINT32 nWidth,UINT32 nHeight,UINT32 SrcFormat,BYTE * data)42 HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, UINT32 nWidth, UINT32 nHeight, UINT32 SrcFormat,
43                               BYTE* data)
44 {
45 	UINT32 nSrcStep;
46 	UINT32 nDstStep;
47 	BYTE* pSrcData;
48 	BYTE* pDstData;
49 	HGDI_BITMAP bitmap;
50 
51 	if (!gdi)
52 		return NULL;
53 
54 	nDstStep = nWidth * GetBytesPerPixel(gdi->dstFormat);
55 	pDstData = _aligned_malloc(nHeight * nDstStep * 1ULL, 16);
56 
57 	if (!pDstData)
58 		return NULL;
59 
60 	pSrcData = data;
61 	nSrcStep = nWidth * GetBytesPerPixel(SrcFormat);
62 
63 	if (!freerdp_image_copy(pDstData, gdi->dstFormat, nDstStep, 0, 0, nWidth, nHeight, pSrcData,
64 	                        SrcFormat, nSrcStep, 0, 0, &gdi->palette, FREERDP_FLIP_NONE))
65 	{
66 		_aligned_free(pDstData);
67 		return NULL;
68 	}
69 
70 	bitmap = gdi_CreateBitmap(nWidth, nHeight, gdi->dstFormat, pDstData);
71 	return bitmap;
72 }
73 
gdi_Bitmap_New(rdpContext * context,rdpBitmap * bitmap)74 static BOOL gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap)
75 {
76 	gdiBitmap* gdi_bitmap;
77 	rdpGdi* gdi = context->gdi;
78 	gdi_bitmap = (gdiBitmap*)bitmap;
79 	gdi_bitmap->hdc = gdi_CreateCompatibleDC(gdi->hdc);
80 
81 	if (!gdi_bitmap->hdc)
82 		return FALSE;
83 
84 	if (!bitmap->data)
85 		gdi_bitmap->bitmap = gdi_CreateCompatibleBitmap(gdi->hdc, bitmap->width, bitmap->height);
86 	else
87 	{
88 		UINT32 format = bitmap->format;
89 		gdi_bitmap->bitmap =
90 		    gdi_create_bitmap(gdi, bitmap->width, bitmap->height, format, bitmap->data);
91 	}
92 
93 	if (!gdi_bitmap->bitmap)
94 	{
95 		gdi_DeleteDC(gdi_bitmap->hdc);
96 		gdi_bitmap->hdc = NULL;
97 		return FALSE;
98 	}
99 
100 	gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
101 	gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
102 	gdi_bitmap->org_bitmap = NULL;
103 	return TRUE;
104 }
105 
gdi_Bitmap_Free(rdpContext * context,rdpBitmap * bitmap)106 static void gdi_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap)
107 {
108 	gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
109 
110 	if (gdi_bitmap)
111 	{
112 		if (gdi_bitmap->hdc)
113 			gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap);
114 
115 		gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap);
116 		gdi_DeleteDC(gdi_bitmap->hdc);
117 		_aligned_free(bitmap->data);
118 	}
119 
120 	free(bitmap);
121 }
122 
gdi_Bitmap_Paint(rdpContext * context,rdpBitmap * bitmap)123 static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
124 {
125 	gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
126 	UINT32 width = bitmap->right - bitmap->left + 1;
127 	UINT32 height = bitmap->bottom - bitmap->top + 1;
128 	return gdi_BitBlt(context->gdi->primary->hdc, bitmap->left, bitmap->top, width, height,
129 	                  gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette);
130 }
131 
gdi_Bitmap_Decompress(rdpContext * context,rdpBitmap * bitmap,const BYTE * pSrcData,UINT32 DstWidth,UINT32 DstHeight,UINT32 bpp,UINT32 length,BOOL compressed,UINT32 codecId)132 static BOOL gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData,
133                                   UINT32 DstWidth, UINT32 DstHeight, UINT32 bpp, UINT32 length,
134                                   BOOL compressed, UINT32 codecId)
135 {
136 	UINT32 SrcSize = length;
137 	rdpGdi* gdi = context->gdi;
138 	UINT32 size = DstWidth * DstHeight;
139 	bitmap->compressed = FALSE;
140 	bitmap->format = gdi->dstFormat;
141 
142 	if ((GetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) ||
143 	    (DstWidth > UINT32_MAX / DstHeight) ||
144 	    (size > (UINT32_MAX / GetBytesPerPixel(bitmap->format))))
145 		return FALSE;
146 
147 	size *= GetBytesPerPixel(bitmap->format);
148 	bitmap->length = size;
149 	bitmap->data = (BYTE*)_aligned_malloc(bitmap->length, 16);
150 
151 	if (!bitmap->data)
152 		return FALSE;
153 
154 	if (compressed)
155 	{
156 		if (bpp < 32)
157 		{
158 			if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize, DstWidth,
159 			                            DstHeight, bpp, bitmap->data, bitmap->format, 0, 0, 0,
160 			                            DstWidth, DstHeight, &gdi->palette))
161 				return FALSE;
162 		}
163 		else
164 		{
165 			freerdp_planar_switch_bgr(context->codecs->planar,
166 			                          context->settings->DrawAllowDynamicColorFidelity);
167 			if (!planar_decompress(context->codecs->planar, pSrcData, SrcSize, DstWidth, DstHeight,
168 			                       bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
169 			                       TRUE))
170 				return FALSE;
171 		}
172 	}
173 	else
174 	{
175 		const UINT32 SrcFormat = gdi_get_pixel_format(bpp);
176 		const size_t sbpp = GetBytesPerPixel(SrcFormat);
177 		const size_t dbpp = GetBytesPerPixel(bitmap->format);
178 
179 		if ((sbpp == 0) || (dbpp == 0))
180 			return FALSE;
181 		else
182 		{
183 			const size_t dstSize = SrcSize * dbpp / sbpp;
184 
185 			if (dstSize < bitmap->length)
186 				return FALSE;
187 		}
188 
189 		if (!freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
190 		                        pSrcData, SrcFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL))
191 			return FALSE;
192 	}
193 
194 	return TRUE;
195 }
196 
gdi_Bitmap_SetSurface(rdpContext * context,rdpBitmap * bitmap,BOOL primary)197 static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
198 {
199 	rdpGdi* gdi;
200 
201 	if (!context)
202 		return FALSE;
203 
204 	gdi = context->gdi;
205 
206 	if (!gdi)
207 		return FALSE;
208 
209 	if (primary)
210 		gdi->drawing = gdi->primary;
211 	else
212 		gdi->drawing = (gdiBitmap*)bitmap;
213 
214 	return TRUE;
215 }
216 
217 /* Glyph Class */
gdi_Glyph_New(rdpContext * context,const rdpGlyph * glyph)218 static BOOL gdi_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
219 {
220 	BYTE* data;
221 	gdiGlyph* gdi_glyph;
222 
223 	if (!context || !glyph)
224 		return FALSE;
225 
226 	gdi_glyph = (gdiGlyph*)glyph;
227 	gdi_glyph->hdc = gdi_GetDC();
228 
229 	if (!gdi_glyph->hdc)
230 		return FALSE;
231 
232 	gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
233 	data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj);
234 
235 	if (!data)
236 	{
237 		gdi_DeleteDC(gdi_glyph->hdc);
238 		return FALSE;
239 	}
240 
241 	gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data);
242 
243 	if (!gdi_glyph->bitmap)
244 	{
245 		gdi_DeleteDC(gdi_glyph->hdc);
246 		_aligned_free(data);
247 		return FALSE;
248 	}
249 
250 	gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap);
251 	gdi_glyph->org_bitmap = NULL;
252 	return TRUE;
253 }
254 
gdi_Glyph_Free(rdpContext * context,rdpGlyph * glyph)255 static void gdi_Glyph_Free(rdpContext* context, rdpGlyph* glyph)
256 {
257 	gdiGlyph* gdi_glyph;
258 	gdi_glyph = (gdiGlyph*)glyph;
259 
260 	if (gdi_glyph)
261 	{
262 		gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap);
263 		gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap);
264 		gdi_DeleteDC(gdi_glyph->hdc);
265 		free(glyph->aj);
266 		free(glyph);
267 	}
268 }
269 
gdi_Glyph_Draw(rdpContext * context,const rdpGlyph * glyph,INT32 x,INT32 y,INT32 w,INT32 h,INT32 sx,INT32 sy,BOOL fOpRedundant)270 static BOOL gdi_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, INT32 y, INT32 w,
271                            INT32 h, INT32 sx, INT32 sy, BOOL fOpRedundant)
272 {
273 	gdiGlyph* gdi_glyph;
274 	rdpGdi* gdi;
275 	HGDI_BRUSH brush;
276 	BOOL rc = FALSE;
277 
278 	if (!context || !glyph)
279 		return FALSE;
280 
281 	gdi = context->gdi;
282 	gdi_glyph = (gdiGlyph*)glyph;
283 
284 	if (!fOpRedundant && 0)
285 	{
286 		GDI_RECT rect = { 0 };
287 
288 		if (x > 0)
289 			rect.left = x;
290 
291 		if (y > 0)
292 			rect.top = y;
293 
294 		if (x + w > 0)
295 			rect.right = x + w - 1;
296 
297 		if (y + h > 0)
298 			rect.bottom = y + h - 1;
299 
300 		if ((rect.left < rect.right) && (rect.top < rect.bottom))
301 		{
302 			brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor);
303 
304 			if (!brush)
305 				return FALSE;
306 
307 			gdi_FillRect(gdi->drawing->hdc, &rect, brush);
308 			gdi_DeleteObject((HGDIOBJECT)brush);
309 		}
310 	}
311 
312 	brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor);
313 
314 	if (!brush)
315 		return FALSE;
316 
317 	gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush);
318 	rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER,
319 	                &context->gdi->palette);
320 	gdi_DeleteObject((HGDIOBJECT)brush);
321 	return rc;
322 }
323 
gdi_Glyph_SetBounds(rdpContext * context,INT32 x,INT32 y,INT32 width,INT32 height)324 static BOOL gdi_Glyph_SetBounds(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height)
325 {
326 	rdpGdi* gdi;
327 
328 	if (!context || !context->gdi)
329 		return FALSE;
330 
331 	gdi = context->gdi;
332 
333 	if (!gdi->drawing || !gdi->drawing->hdc)
334 		return FALSE;
335 
336 	return gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height);
337 }
338 
gdi_Glyph_BeginDraw(rdpContext * context,INT32 x,INT32 y,INT32 width,INT32 height,UINT32 bgcolor,UINT32 fgcolor,BOOL fOpRedundant)339 static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
340                                 UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant)
341 {
342 	rdpGdi* gdi;
343 
344 	if (!context || !context->gdi)
345 		return FALSE;
346 
347 	gdi = context->gdi;
348 
349 	if (!gdi->drawing || !gdi->drawing->hdc)
350 		return FALSE;
351 
352 	if (!fOpRedundant)
353 	{
354 		if (!gdi_decode_color(gdi, bgcolor, &bgcolor, NULL))
355 			return FALSE;
356 
357 		if (!gdi_decode_color(gdi, fgcolor, &fgcolor, NULL))
358 			return FALSE;
359 
360 		gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height);
361 		gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
362 		gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
363 
364 		if (1)
365 		{
366 			GDI_RECT rect = { 0 };
367 			HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor);
368 
369 			if (!brush)
370 				return FALSE;
371 
372 			if (x > 0)
373 				rect.left = x;
374 
375 			if (y > 0)
376 				rect.top = y;
377 
378 			rect.right = x + width - 1;
379 			rect.bottom = y + height - 1;
380 
381 			if ((x + width > rect.left) && (y + height > rect.top))
382 				gdi_FillRect(gdi->drawing->hdc, &rect, brush);
383 
384 			gdi_DeleteObject((HGDIOBJECT)brush);
385 		}
386 
387 		return gdi_SetNullClipRgn(gdi->drawing->hdc);
388 	}
389 
390 	return TRUE;
391 }
392 
gdi_Glyph_EndDraw(rdpContext * context,INT32 x,INT32 y,INT32 width,INT32 height,UINT32 bgcolor,UINT32 fgcolor)393 static BOOL gdi_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
394                               UINT32 bgcolor, UINT32 fgcolor)
395 {
396 	rdpGdi* gdi;
397 
398 	if (!context || !context->gdi)
399 		return FALSE;
400 
401 	gdi = context->gdi;
402 
403 	if (!gdi->drawing || !gdi->drawing->hdc)
404 		return FALSE;
405 
406 	gdi_SetNullClipRgn(gdi->drawing->hdc);
407 	return TRUE;
408 }
409 
410 /* Graphics Module */
gdi_register_graphics(rdpGraphics * graphics)411 BOOL gdi_register_graphics(rdpGraphics* graphics)
412 {
413 	rdpBitmap bitmap;
414 	rdpGlyph glyph;
415 	bitmap.size = sizeof(gdiBitmap);
416 	bitmap.New = gdi_Bitmap_New;
417 	bitmap.Free = gdi_Bitmap_Free;
418 	bitmap.Paint = gdi_Bitmap_Paint;
419 	bitmap.Decompress = gdi_Bitmap_Decompress;
420 	bitmap.SetSurface = gdi_Bitmap_SetSurface;
421 	graphics_register_bitmap(graphics, &bitmap);
422 	glyph.size = sizeof(gdiGlyph);
423 	glyph.New = gdi_Glyph_New;
424 	glyph.Free = gdi_Glyph_Free;
425 	glyph.Draw = gdi_Glyph_Draw;
426 	glyph.BeginDraw = gdi_Glyph_BeginDraw;
427 	glyph.EndDraw = gdi_Glyph_EndDraw;
428 	glyph.SetBounds = gdi_Glyph_SetBounds;
429 	graphics_register_glyph(graphics, &glyph);
430 	return TRUE;
431 }
432