FreeRDP
Loading...
Searching...
No Matches
cache/bitmap.c
1
20#include <freerdp/config.h>
21
22#include <stdio.h>
23
24#include <winpr/crt.h>
25#include <winpr/assert.h>
26#include <winpr/cast.h>
27
28#include <freerdp/freerdp.h>
29#include <freerdp/constants.h>
30#include <winpr/stream.h>
31
32#include <freerdp/log.h>
33#include <freerdp/gdi/bitmap.h>
34
35#include "../gdi/gdi.h"
36#include "../core/graphics.h"
37
38#include "bitmap.h"
39#include "cache.h"
40
41#define TAG FREERDP_TAG("cache.bitmap")
42
43static rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index);
44static BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index,
45 rdpBitmap* bitmap);
46
47static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
48{
49 rdpBitmap* bitmap = nullptr;
50 rdpCache* cache = nullptr;
51
52 cache = context->cache;
53
54 if (memblt->cacheId == 0xFF)
55 bitmap = offscreen_cache_get(cache->offscreen, memblt->cacheIndex);
56 else
57 bitmap = bitmap_cache_get(cache->bitmap, (BYTE)memblt->cacheId, memblt->cacheIndex);
58
59 /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
60 if (bitmap == nullptr)
61 return TRUE;
62
63 memblt->bitmap = bitmap;
64 return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
65}
66
67static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
68{
69 rdpBitmap* bitmap = nullptr;
70 rdpCache* cache = context->cache;
71 rdpBrush* brush = &mem3blt->brush;
72 BOOL ret = TRUE;
73
74 if (mem3blt->cacheId == 0xFF)
75 bitmap = offscreen_cache_get(cache->offscreen, mem3blt->cacheIndex);
76 else
77 bitmap = bitmap_cache_get(cache->bitmap, (BYTE)mem3blt->cacheId, mem3blt->cacheIndex);
78
79 /* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
80 if (!bitmap)
81 return TRUE;
82
83 const BYTE style = WINPR_ASSERTING_INT_CAST(UINT8, brush->style);
84
85 if (brush->style & CACHED_BRUSH)
86 {
87 brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
88
89 if (!brush->data)
90 return FALSE;
91
92 brush->style = 0x03;
93 }
94
95 mem3blt->bitmap = bitmap;
96 IFCALLRET(cache->bitmap->Mem3Blt, ret, context, mem3blt);
97 brush->style = style;
98 return ret;
99}
100
101static BOOL update_gdi_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cacheBitmap)
102{
103 rdpBitmap* bitmap = nullptr;
104 rdpBitmap* prevBitmap = nullptr;
105 rdpCache* cache = context->cache;
106 bitmap = Bitmap_Alloc(context);
107
108 if (!bitmap)
109 return FALSE;
110
111 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapWidth),
112 WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmap->bitmapHeight)))
113 goto fail;
114
115 if (!bitmap->Decompress(context, bitmap, cacheBitmap->bitmapDataStream,
116 cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight,
117 cacheBitmap->bitmapBpp, cacheBitmap->bitmapLength,
118 cacheBitmap->compressed, RDP_CODEC_ID_NONE))
119 goto fail;
120
121 if (!bitmap->New(context, bitmap))
122 goto fail;
123
124 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex);
125 Bitmap_Free(context, prevBitmap);
126 return bitmap_cache_put(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex, bitmap);
127
128fail:
129 Bitmap_Free(context, bitmap);
130 return FALSE;
131}
132
133static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
134
135{
136 rdpBitmap* prevBitmap = nullptr;
137 rdpCache* cache = context->cache;
138 rdpSettings* settings = context->settings;
139 rdpBitmap* bitmap = Bitmap_Alloc(context);
140
141 if (!bitmap)
142 return FALSE;
143
144 const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
145 bitmap->key64 = ((UINT64)cacheBitmapV2->key1 | (((UINT64)cacheBitmapV2->key2) << 32));
146
147 if (!cacheBitmapV2->bitmapBpp)
148 cacheBitmapV2->bitmapBpp = ColorDepth;
149
150 if ((ColorDepth == 15) && (cacheBitmapV2->bitmapBpp == 16))
151 cacheBitmapV2->bitmapBpp = ColorDepth;
152
153 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapWidth),
154 WINPR_ASSERTING_INT_CAST(UINT16, cacheBitmapV2->bitmapHeight)))
155 goto fail;
156
157 if (!bitmap->Decompress(context, bitmap, cacheBitmapV2->bitmapDataStream,
158 cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight,
159 cacheBitmapV2->bitmapBpp, cacheBitmapV2->bitmapLength,
160 cacheBitmapV2->compressed, RDP_CODEC_ID_NONE))
161 goto fail;
162
163 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex);
164
165 if (!bitmap->New(context, bitmap))
166 goto fail;
167
168 Bitmap_Free(context, prevBitmap);
169 return bitmap_cache_put(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex,
170 bitmap);
171
172fail:
173 Bitmap_Free(context, bitmap);
174 return FALSE;
175}
176
177static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cacheBitmapV3)
178{
179 rdpBitmap* bitmap = nullptr;
180 rdpBitmap* prevBitmap = nullptr;
181 BOOL compressed = TRUE;
182 rdpCache* cache = context->cache;
183 rdpSettings* settings = context->settings;
184 BITMAP_DATA_EX* bitmapData = &cacheBitmapV3->bitmapData;
185 bitmap = Bitmap_Alloc(context);
186
187 if (!bitmap)
188 return FALSE;
189
190 const UINT32 ColorDepth = freerdp_settings_get_uint32(settings, FreeRDP_ColorDepth);
191 bitmap->key64 = ((UINT64)cacheBitmapV3->key1 | (((UINT64)cacheBitmapV3->key2) << 32));
192
193 if (!cacheBitmapV3->bpp)
194 cacheBitmapV3->bpp = ColorDepth;
195
196 compressed = (bitmapData->codecID != RDP_CODEC_ID_NONE);
197
198 if (!Bitmap_SetDimensions(bitmap, WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->width),
199 WINPR_ASSERTING_INT_CAST(UINT16, bitmapData->height)))
200 goto fail;
201
202 if (!bitmap->Decompress(context, bitmap, bitmapData->data, bitmapData->width,
203 bitmapData->height, bitmapData->bpp, bitmapData->length, compressed,
204 bitmapData->codecID))
205 goto fail;
206
207 if (!bitmap->New(context, bitmap))
208 goto fail;
209
210 prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex);
211 Bitmap_Free(context, prevBitmap);
212 return bitmap_cache_put(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex,
213 bitmap);
214
215fail:
216 Bitmap_Free(context, bitmap);
217 return FALSE;
218}
219
220rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index)
221{
222 rdpBitmap* bitmap = nullptr;
223
224 if (id >= bitmapCache->maxCells)
225 {
226 WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
227 return nullptr;
228 }
229
230 if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
231 {
232 index = bitmapCache->cells[id].number;
233 }
234 else if (index > bitmapCache->cells[id].number)
235 {
236 WLog_ERR(TAG, "get invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
237 return nullptr;
238 }
239
240 bitmap = bitmapCache->cells[id].entries[index];
241 return bitmap;
242}
243
244BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index, rdpBitmap* bitmap)
245{
246 if (id > bitmapCache->maxCells)
247 {
248 WLog_ERR(TAG, "put invalid bitmap cell id: %" PRIu32 "", id);
249 return FALSE;
250 }
251
252 if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
253 {
254 index = bitmapCache->cells[id].number;
255 }
256 else if (index > bitmapCache->cells[id].number)
257 {
258 WLog_ERR(TAG, "put invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
259 return FALSE;
260 }
261
262 bitmapCache->cells[id].entries[index] = bitmap;
263 return TRUE;
264}
265
266void bitmap_cache_register_callbacks(rdpUpdate* update)
267{
268 rdpCache* cache = nullptr;
269
270 WINPR_ASSERT(update);
271 WINPR_ASSERT(update->context);
272 WINPR_ASSERT(update->context->cache);
273
274 cache = update->context->cache;
275 WINPR_ASSERT(cache);
276
277 if (!freerdp_settings_get_bool(update->context->settings, FreeRDP_DeactivateClientDecoding))
278 {
279 cache->bitmap->MemBlt = update->primary->MemBlt;
280 cache->bitmap->Mem3Blt = update->primary->Mem3Blt;
281 update->primary->MemBlt = update_gdi_memblt;
282 update->primary->Mem3Blt = update_gdi_mem3blt;
283 update->secondary->CacheBitmap = update_gdi_cache_bitmap;
284 update->secondary->CacheBitmapV2 = update_gdi_cache_bitmap_v2;
285 update->secondary->CacheBitmapV3 = update_gdi_cache_bitmap_v3;
286 update->BitmapUpdate = gdi_bitmap_update;
287 }
288}
289
290static int bitmap_cache_save_persistent(rdpBitmapCache* bitmapCache)
291{
292 rdpContext* context = bitmapCache->context;
293 rdpSettings* settings = context->settings;
294
295 const UINT32 version = freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheVersion);
296
297 if (version != 2)
298 return 0; /* persistent bitmap cache already saved in egfx channel */
299
300 if (!freerdp_settings_get_bool(settings, FreeRDP_BitmapCachePersistEnabled))
301 return 0;
302
303 const char* BitmapCachePersistFile =
304 freerdp_settings_get_string(settings, FreeRDP_BitmapCachePersistFile);
305 if (!BitmapCachePersistFile)
306 return 0;
307
308 rdpPersistentCache* persistent = persistent_cache_new();
309
310 if (!persistent)
311 return -1;
312
313 int status = persistent_cache_open(persistent, BitmapCachePersistFile, TRUE, version);
314
315 if (status < 1)
316 goto end;
317
318 if (bitmapCache->cells)
319 {
320 for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
321 {
322 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
323 for (UINT32 j = 0; j < cell->number + 1 && cell->entries; j++)
324 {
325 PERSISTENT_CACHE_ENTRY cacheEntry = WINPR_C_ARRAY_INIT;
326 rdpBitmap* bitmap = cell->entries[j];
327
328 if (!bitmap || !bitmap->key64)
329 continue;
330
331 cacheEntry.key64 = bitmap->key64;
332
333 cacheEntry.width = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->width);
334 cacheEntry.height = WINPR_ASSERTING_INT_CAST(UINT16, bitmap->height);
335 const UINT64 size = 4ULL * bitmap->width * bitmap->height;
336 if (size > UINT32_MAX)
337 continue;
338 cacheEntry.size = (UINT32)size;
339 cacheEntry.flags = 0;
340 cacheEntry.data = bitmap->data;
341
342 if (persistent_cache_write_entry(persistent, &cacheEntry) < 1)
343 {
344 status = -1;
345 goto end;
346 }
347 }
348 }
349 }
350
351 status = 1;
352
353end:
354 persistent_cache_free(persistent);
355 return status;
356}
357
358rdpBitmapCache* bitmap_cache_new(rdpContext* context)
359{
360 rdpSettings* settings = nullptr;
361 rdpBitmapCache* bitmapCache = nullptr;
362
363 WINPR_ASSERT(context);
364
365 settings = context->settings;
366 WINPR_ASSERT(settings);
367
368 bitmapCache = (rdpBitmapCache*)calloc(1, sizeof(rdpBitmapCache));
369
370 if (!bitmapCache)
371 return nullptr;
372
373 const UINT32 BitmapCacheV2NumCells =
374 freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
375 bitmapCache->context = context;
376
377 /* overallocate by 1. older RDP servers do send a off by 1 cache index. */
378 bitmapCache->cells =
379 (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells + 1ull, sizeof(BITMAP_V2_CELL));
380
381 if (!bitmapCache->cells)
382 goto fail;
383 bitmapCache->maxCells = BitmapCacheV2NumCells;
384
385 for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
386 {
387 const BITMAP_CACHE_V2_CELL_INFO* info =
388 freerdp_settings_get_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, i);
389 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
390 UINT32 nr = info->numEntries;
391 /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
392 cell->entries = (rdpBitmap**)calloc((nr + 1), sizeof(rdpBitmap*));
393
394 if (!cell->entries)
395 goto fail;
396 cell->number = nr;
397 }
398
399 /* initialize the overallocated extra slot for old RDP servers that send
400 * cacheId == maxCells; use a minimal allocation since no protocol-negotiated
401 * capacity exists for this slot */
402 {
403 BITMAP_V2_CELL* extra = &bitmapCache->cells[bitmapCache->maxCells];
404 /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
405 extra->entries = (rdpBitmap**)calloc(1, sizeof(rdpBitmap*));
406
407 if (!extra->entries)
408 goto fail;
409 extra->number = 0;
410 }
411
412 return bitmapCache;
413fail:
414 WINPR_PRAGMA_DIAG_PUSH
415 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
416 bitmap_cache_free(bitmapCache);
417 WINPR_PRAGMA_DIAG_POP
418 return nullptr;
419}
420
421void bitmap_cache_free(rdpBitmapCache* bitmapCache)
422{
423 if (!bitmapCache)
424 return;
425
426 bitmap_cache_save_persistent(bitmapCache);
427
428 if (bitmapCache->cells)
429 {
430 /* iterate through maxCells + 1 to also free the overallocated extra slot */
431 for (UINT32 i = 0; i <= bitmapCache->maxCells; i++)
432 {
433 UINT32 j = 0;
434 BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
435
436 if (!cell->entries)
437 continue;
438
439 for (j = 0; j < cell->number + 1; j++)
440 {
441 rdpBitmap* bitmap = cell->entries[j];
442 Bitmap_Free(bitmapCache->context, bitmap);
443 }
444
445 free((void*)cell->entries);
446 }
447
448 free(bitmapCache->cells);
449 }
450
451 persistent_cache_free(bitmapCache->persistent);
452
453 free(bitmapCache);
454}
455
456static void free_bitmap_data(BITMAP_DATA* data, size_t count)
457{
458 if (!data)
459 return;
460
461 for (size_t x = 0; x < count; x++)
462 free(data[x].bitmapDataStream);
463
464 free(data);
465}
466
467static BITMAP_DATA* copy_bitmap_data(const BITMAP_DATA* data, size_t count)
468{
469 BITMAP_DATA* dst = (BITMAP_DATA*)calloc(count, sizeof(BITMAP_DATA));
470
471 if (!dst)
472 goto fail;
473
474 for (size_t x = 0; x < count; x++)
475 {
476 dst[x] = data[x];
477
478 if (data[x].bitmapLength > 0)
479 {
480 dst[x].bitmapDataStream = malloc(data[x].bitmapLength);
481
482 if (!dst[x].bitmapDataStream)
483 goto fail;
484
485 memcpy(dst[x].bitmapDataStream, data[x].bitmapDataStream, data[x].bitmapLength);
486 }
487 }
488
489 return dst;
490fail:
491 free_bitmap_data(dst, count);
492 return nullptr;
493}
494
495void free_bitmap_update(WINPR_ATTR_UNUSED rdpContext* context,
496 WINPR_ATTR_UNUSED BITMAP_UPDATE* pointer)
497{
498 if (!pointer)
499 return;
500
501 free_bitmap_data(pointer->rectangles, pointer->number);
502 free(pointer);
503}
504
505BITMAP_UPDATE* copy_bitmap_update(rdpContext* context, const BITMAP_UPDATE* pointer)
506{
507 BITMAP_UPDATE* dst = calloc(1, sizeof(BITMAP_UPDATE));
508
509 if (!dst || !pointer)
510 goto fail;
511
512 *dst = *pointer;
513 dst->rectangles = copy_bitmap_data(pointer->rectangles, pointer->number);
514
515 if (!dst->rectangles)
516 goto fail;
517
518 return dst;
519fail:
520 WINPR_PRAGMA_DIAG_PUSH
521 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
522 free_bitmap_update(context, dst);
523 WINPR_PRAGMA_DIAG_POP
524 return nullptr;
525}
526
527CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context, const CACHE_BITMAP_ORDER* order)
528{
529 CACHE_BITMAP_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_ORDER));
530
531 if (!dst || !order)
532 goto fail;
533
534 *dst = *order;
535
536 if (order->bitmapLength > 0)
537 {
538 dst->bitmapDataStream = malloc(order->bitmapLength);
539
540 if (!dst->bitmapDataStream)
541 goto fail;
542
543 memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
544 }
545
546 return dst;
547fail:
548 WINPR_PRAGMA_DIAG_PUSH
549 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
550 free_cache_bitmap_order(context, dst);
551 WINPR_PRAGMA_DIAG_POP
552 return nullptr;
553}
554
555void free_cache_bitmap_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_BITMAP_ORDER* order)
556{
557 if (order)
558 free(order->bitmapDataStream);
559
560 free(order);
561}
562
563CACHE_BITMAP_V2_ORDER* copy_cache_bitmap_v2_order(rdpContext* context,
564 const CACHE_BITMAP_V2_ORDER* order)
565{
566 CACHE_BITMAP_V2_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V2_ORDER));
567
568 if (!dst || !order)
569 goto fail;
570
571 *dst = *order;
572
573 if (order->bitmapLength > 0)
574 {
575 dst->bitmapDataStream = malloc(order->bitmapLength);
576
577 if (!dst->bitmapDataStream)
578 goto fail;
579
580 memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
581 }
582
583 return dst;
584fail:
585 WINPR_PRAGMA_DIAG_PUSH
586 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
587 free_cache_bitmap_v2_order(context, dst);
588 WINPR_PRAGMA_DIAG_POP
589 return nullptr;
590}
591
592void free_cache_bitmap_v2_order(WINPR_ATTR_UNUSED rdpContext* context,
593 WINPR_ATTR_UNUSED CACHE_BITMAP_V2_ORDER* order)
594{
595 if (order)
596 free(order->bitmapDataStream);
597
598 free(order);
599}
600
601CACHE_BITMAP_V3_ORDER* copy_cache_bitmap_v3_order(rdpContext* context,
602 const CACHE_BITMAP_V3_ORDER* order)
603{
604 CACHE_BITMAP_V3_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V3_ORDER));
605
606 if (!dst || !order)
607 goto fail;
608
609 *dst = *order;
610
611 if (order->bitmapData.length > 0)
612 {
613 dst->bitmapData.data = malloc(order->bitmapData.length);
614
615 if (!dst->bitmapData.data)
616 goto fail;
617
618 memcpy(dst->bitmapData.data, order->bitmapData.data, order->bitmapData.length);
619 }
620
621 return dst;
622fail:
623 WINPR_PRAGMA_DIAG_PUSH
624 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
625 free_cache_bitmap_v3_order(context, dst);
626 WINPR_PRAGMA_DIAG_POP
627 return nullptr;
628}
629
630void free_cache_bitmap_v3_order(WINPR_ATTR_UNUSED rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
631{
632 if (order)
633 free(order->bitmapData.data);
634
635 free(order);
636}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition persistent.h:70