f63df39833e960e9b48d95898e501d71a6ad7c28
[platform/upstream/freerdp.git] / libfreerdp / gdi / graphics.c
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
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, 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
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                 return FALSE;
97         }
98
99         gdi_bitmap->hdc->format = gdi_bitmap->bitmap->format;
100         gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->bitmap);
101         gdi_bitmap->org_bitmap = NULL;
102         return TRUE;
103 }
104
105 static void gdi_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap)
106 {
107         gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
108
109         if (gdi_bitmap)
110         {
111                 if (gdi_bitmap->hdc)
112                         gdi_SelectObject(gdi_bitmap->hdc, (HGDIOBJECT)gdi_bitmap->org_bitmap);
113
114                 gdi_DeleteObject((HGDIOBJECT)gdi_bitmap->bitmap);
115                 gdi_DeleteDC(gdi_bitmap->hdc);
116                 _aligned_free(bitmap->data);
117         }
118
119         free(bitmap);
120 }
121
122 static BOOL gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap)
123 {
124         gdiBitmap* gdi_bitmap = (gdiBitmap*)bitmap;
125         UINT32 width = bitmap->right - bitmap->left + 1;
126         UINT32 height = bitmap->bottom - bitmap->top + 1;
127         return gdi_BitBlt(context->gdi->primary->hdc, bitmap->left, bitmap->top, width, height,
128                           gdi_bitmap->hdc, 0, 0, GDI_SRCCOPY, &context->gdi->palette);
129 }
130
131 static BOOL gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, const BYTE* pSrcData,
132                                   UINT32 DstWidth, UINT32 DstHeight, UINT32 bpp, UINT32 length,
133                                   BOOL compressed, UINT32 codecId)
134 {
135         UINT32 SrcSize = length;
136         rdpGdi* gdi = context->gdi;
137         UINT32 size = DstWidth * DstHeight;
138         bitmap->compressed = FALSE;
139         bitmap->format = gdi->dstFormat;
140
141         if ((GetBytesPerPixel(bitmap->format) == 0) || (DstWidth == 0) || (DstHeight == 0) ||
142             (DstWidth > UINT32_MAX / DstHeight) ||
143             (size > (UINT32_MAX / GetBytesPerPixel(bitmap->format))))
144                 return FALSE;
145
146         size *= GetBytesPerPixel(bitmap->format);
147         bitmap->length = size;
148         bitmap->data = (BYTE*)_aligned_malloc(bitmap->length, 16);
149
150         if (!bitmap->data)
151                 return FALSE;
152
153         if (compressed)
154         {
155                 if (bpp < 32)
156                 {
157                         if (!interleaved_decompress(context->codecs->interleaved, pSrcData, SrcSize, DstWidth,
158                                                     DstHeight, bpp, bitmap->data, bitmap->format, 0, 0, 0,
159                                                     DstWidth, DstHeight, &gdi->palette))
160                                 return FALSE;
161                 }
162                 else
163                 {
164                         if (!planar_decompress(context->codecs->planar, pSrcData, SrcSize, DstWidth, DstHeight,
165                                                bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
166                                                TRUE))
167                                 return FALSE;
168                 }
169         }
170         else
171         {
172                 const UINT32 SrcFormat = gdi_get_pixel_format(bpp);
173                 const size_t sbpp = GetBytesPerPixel(SrcFormat);
174                 const size_t dbpp = GetBytesPerPixel(bitmap->format);
175
176                 if ((sbpp == 0) || (dbpp == 0))
177                         return FALSE;
178                 else
179                 {
180                         const size_t dstSize = SrcSize * dbpp / sbpp;
181
182                         if (dstSize < bitmap->length)
183                                 return FALSE;
184                 }
185
186                 if (!freerdp_image_copy(bitmap->data, bitmap->format, 0, 0, 0, DstWidth, DstHeight,
187                                         pSrcData, SrcFormat, 0, 0, 0, &gdi->palette, FREERDP_FLIP_VERTICAL))
188                         return FALSE;
189         }
190
191         return TRUE;
192 }
193
194 static BOOL gdi_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, BOOL primary)
195 {
196         rdpGdi* gdi;
197
198         if (!context)
199                 return FALSE;
200
201         gdi = context->gdi;
202
203         if (!gdi)
204                 return FALSE;
205
206         if (primary)
207                 gdi->drawing = gdi->primary;
208         else
209                 gdi->drawing = (gdiBitmap*)bitmap;
210
211         return TRUE;
212 }
213
214 /* Glyph Class */
215 static BOOL gdi_Glyph_New(rdpContext* context, const rdpGlyph* glyph)
216 {
217         BYTE* data;
218         gdiGlyph* gdi_glyph;
219
220         if (!context || !glyph)
221                 return FALSE;
222
223         gdi_glyph = (gdiGlyph*)glyph;
224         gdi_glyph->hdc = gdi_GetDC();
225
226         if (!gdi_glyph->hdc)
227                 return FALSE;
228
229         gdi_glyph->hdc->format = PIXEL_FORMAT_MONO;
230         data = freerdp_glyph_convert(glyph->cx, glyph->cy, glyph->aj);
231
232         if (!data)
233         {
234                 gdi_DeleteDC(gdi_glyph->hdc);
235                 return FALSE;
236         }
237
238         gdi_glyph->bitmap = gdi_CreateBitmap(glyph->cx, glyph->cy, PIXEL_FORMAT_MONO, data);
239
240         if (!gdi_glyph->bitmap)
241         {
242                 gdi_DeleteDC(gdi_glyph->hdc);
243                 _aligned_free(data);
244                 return FALSE;
245         }
246
247         gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->bitmap);
248         gdi_glyph->org_bitmap = NULL;
249         return TRUE;
250 }
251
252 static void gdi_Glyph_Free(rdpContext* context, rdpGlyph* glyph)
253 {
254         gdiGlyph* gdi_glyph;
255         gdi_glyph = (gdiGlyph*)glyph;
256
257         if (gdi_glyph)
258         {
259                 gdi_SelectObject(gdi_glyph->hdc, (HGDIOBJECT)gdi_glyph->org_bitmap);
260                 gdi_DeleteObject((HGDIOBJECT)gdi_glyph->bitmap);
261                 gdi_DeleteDC(gdi_glyph->hdc);
262                 free(glyph->aj);
263                 free(glyph);
264         }
265 }
266
267 static BOOL gdi_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, INT32 y, INT32 w,
268                            INT32 h, INT32 sx, INT32 sy, BOOL fOpRedundant)
269 {
270         gdiGlyph* gdi_glyph;
271         rdpGdi* gdi;
272         HGDI_BRUSH brush;
273         BOOL rc = FALSE;
274
275         if (!context || !glyph)
276                 return FALSE;
277
278         gdi = context->gdi;
279         gdi_glyph = (gdiGlyph*)glyph;
280
281         if (!fOpRedundant && 0)
282         {
283                 GDI_RECT rect = { 0 };
284
285                 if (x > 0)
286                         rect.left = x;
287
288                 if (y > 0)
289                         rect.top = y;
290
291                 if (x + w > 0)
292                         rect.right = x + w - 1;
293
294                 if (y + h > 0)
295                         rect.bottom = y + h - 1;
296
297                 if ((rect.left < rect.right) && (rect.top < rect.bottom))
298                 {
299                         brush = gdi_CreateSolidBrush(gdi->drawing->hdc->bkColor);
300
301                         if (!brush)
302                                 return FALSE;
303
304                         gdi_FillRect(gdi->drawing->hdc, &rect, brush);
305                         gdi_DeleteObject((HGDIOBJECT)brush);
306                 }
307         }
308
309         brush = gdi_CreateSolidBrush(gdi->drawing->hdc->textColor);
310
311         if (!brush)
312                 return FALSE;
313
314         gdi_SelectObject(gdi->drawing->hdc, (HGDIOBJECT)brush);
315         rc = gdi_BitBlt(gdi->drawing->hdc, x, y, w, h, gdi_glyph->hdc, sx, sy, GDI_GLYPH_ORDER,
316                         &context->gdi->palette);
317         gdi_DeleteObject((HGDIOBJECT)brush);
318         return rc;
319 }
320
321 static BOOL gdi_Glyph_SetBounds(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height)
322 {
323         rdpGdi* gdi;
324
325         if (!context || !context->gdi)
326                 return FALSE;
327
328         gdi = context->gdi;
329
330         if (!gdi->drawing || !gdi->drawing->hdc)
331                 return FALSE;
332
333         return gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height);
334 }
335
336 static BOOL gdi_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
337                                 UINT32 bgcolor, UINT32 fgcolor, BOOL fOpRedundant)
338 {
339         rdpGdi* gdi;
340
341         if (!context || !context->gdi)
342                 return FALSE;
343
344         gdi = context->gdi;
345
346         if (!gdi->drawing || !gdi->drawing->hdc)
347                 return FALSE;
348
349         if (!fOpRedundant)
350         {
351                 if (!gdi_decode_color(gdi, bgcolor, &bgcolor, NULL))
352                         return FALSE;
353
354                 if (!gdi_decode_color(gdi, fgcolor, &fgcolor, NULL))
355                         return FALSE;
356
357                 gdi_SetClipRgn(gdi->drawing->hdc, x, y, width, height);
358                 gdi_SetTextColor(gdi->drawing->hdc, bgcolor);
359                 gdi_SetBkColor(gdi->drawing->hdc, fgcolor);
360
361                 if (1)
362                 {
363                         GDI_RECT rect = { 0 };
364                         HGDI_BRUSH brush = gdi_CreateSolidBrush(fgcolor);
365
366                         if (!brush)
367                                 return FALSE;
368
369                         if (x > 0)
370                                 rect.left = x;
371
372                         if (y > 0)
373                                 rect.top = y;
374
375                         rect.right = x + width - 1;
376                         rect.bottom = y + height - 1;
377
378                         if ((x + width > rect.left) && (y + height > rect.top))
379                                 gdi_FillRect(gdi->drawing->hdc, &rect, brush);
380
381                         gdi_DeleteObject((HGDIOBJECT)brush);
382                 }
383
384                 return gdi_SetNullClipRgn(gdi->drawing->hdc);
385         }
386
387         return TRUE;
388 }
389
390 static BOOL gdi_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y, INT32 width, INT32 height,
391                               UINT32 bgcolor, UINT32 fgcolor)
392 {
393         rdpGdi* gdi;
394
395         if (!context || !context->gdi)
396                 return FALSE;
397
398         gdi = context->gdi;
399
400         if (!gdi->drawing || !gdi->drawing->hdc)
401                 return FALSE;
402
403         gdi_SetNullClipRgn(gdi->drawing->hdc);
404         return TRUE;
405 }
406
407 /* Graphics Module */
408 BOOL gdi_register_graphics(rdpGraphics* graphics)
409 {
410         rdpBitmap bitmap;
411         rdpGlyph glyph;
412         bitmap.size = sizeof(gdiBitmap);
413         bitmap.New = gdi_Bitmap_New;
414         bitmap.Free = gdi_Bitmap_Free;
415         bitmap.Paint = gdi_Bitmap_Paint;
416         bitmap.Decompress = gdi_Bitmap_Decompress;
417         bitmap.SetSurface = gdi_Bitmap_SetSurface;
418         graphics_register_bitmap(graphics, &bitmap);
419         glyph.size = sizeof(gdiGlyph);
420         glyph.New = gdi_Glyph_New;
421         glyph.Free = gdi_Glyph_Free;
422         glyph.Draw = gdi_Glyph_Draw;
423         glyph.BeginDraw = gdi_Glyph_BeginDraw;
424         glyph.EndDraw = gdi_Glyph_EndDraw;
425         glyph.SetBounds = gdi_Glyph_SetBounds;
426         graphics_register_glyph(graphics, &glyph);
427         return TRUE;
428 }