FreeRDP
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 
43 static rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index);
44 static BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index,
45  rdpBitmap* bitmap);
46 
47 static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
48 {
49  rdpBitmap* bitmap = NULL;
50  rdpCache* cache = NULL;
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 == NULL)
61  return TRUE;
62 
63  memblt->bitmap = bitmap;
64  return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
65 }
66 
67 static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
68 {
69  rdpBitmap* bitmap = NULL;
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 
101 static BOOL update_gdi_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cacheBitmap)
102 {
103  rdpBitmap* bitmap = NULL;
104  rdpBitmap* prevBitmap = NULL;
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 
128 fail:
129  Bitmap_Free(context, bitmap);
130  return FALSE;
131 }
132 
133 static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
134 
135 {
136  rdpBitmap* prevBitmap = NULL;
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 
172 fail:
173  Bitmap_Free(context, bitmap);
174  return FALSE;
175 }
176 
177 static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cacheBitmapV3)
178 {
179  rdpBitmap* bitmap = NULL;
180  rdpBitmap* prevBitmap = NULL;
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 
215 fail:
216  Bitmap_Free(context, bitmap);
217  return FALSE;
218 }
219 
220 rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index)
221 {
222  rdpBitmap* bitmap = NULL;
223 
224  if (id >= bitmapCache->maxCells)
225  {
226  WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
227  return NULL;
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 NULL;
238  }
239 
240  bitmap = bitmapCache->cells[id].entries[index];
241  return bitmap;
242 }
243 
244 BOOL 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 
266 void bitmap_cache_register_callbacks(rdpUpdate* update)
267 {
268  rdpCache* cache = NULL;
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 
290 static 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 = { 0 };
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 
353 end:
354  persistent_cache_free(persistent);
355  return status;
356 }
357 
358 rdpBitmapCache* bitmap_cache_new(rdpContext* context)
359 {
360  rdpSettings* settings = NULL;
361  rdpBitmapCache* bitmapCache = NULL;
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 NULL;
372 
373  const UINT32 BitmapCacheV2NumCells =
374  freerdp_settings_get_uint32(settings, FreeRDP_BitmapCacheV2NumCells);
375  bitmapCache->context = context;
376  bitmapCache->cells = (BITMAP_V2_CELL*)calloc(BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL));
377 
378  if (!bitmapCache->cells)
379  goto fail;
380  bitmapCache->maxCells = BitmapCacheV2NumCells;
381 
382  for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
383  {
384  const BITMAP_CACHE_V2_CELL_INFO* info =
385  freerdp_settings_get_pointer_array(settings, FreeRDP_BitmapCacheV2CellInfo, i);
386  BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
387  UINT32 nr = info->numEntries;
388  /* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
389  cell->entries = (rdpBitmap**)calloc((nr + 1), sizeof(rdpBitmap*));
390 
391  if (!cell->entries)
392  goto fail;
393  cell->number = nr;
394  }
395 
396  return bitmapCache;
397 fail:
398  WINPR_PRAGMA_DIAG_PUSH
399  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
400  bitmap_cache_free(bitmapCache);
401  WINPR_PRAGMA_DIAG_POP
402  return NULL;
403 }
404 
405 void bitmap_cache_free(rdpBitmapCache* bitmapCache)
406 {
407  if (!bitmapCache)
408  return;
409 
410  bitmap_cache_save_persistent(bitmapCache);
411 
412  if (bitmapCache->cells)
413  {
414  for (UINT32 i = 0; i < bitmapCache->maxCells; i++)
415  {
416  UINT32 j = 0;
417  BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
418 
419  if (!cell->entries)
420  continue;
421 
422  for (j = 0; j < cell->number + 1; j++)
423  {
424  rdpBitmap* bitmap = cell->entries[j];
425  Bitmap_Free(bitmapCache->context, bitmap);
426  }
427 
428  free((void*)cell->entries);
429  }
430 
431  free(bitmapCache->cells);
432  }
433 
434  persistent_cache_free(bitmapCache->persistent);
435 
436  free(bitmapCache);
437 }
438 
439 static void free_bitmap_data(BITMAP_DATA* data, size_t count)
440 {
441  if (!data)
442  return;
443 
444  for (size_t x = 0; x < count; x++)
445  free(data[x].bitmapDataStream);
446 
447  free(data);
448 }
449 
450 static BITMAP_DATA* copy_bitmap_data(const BITMAP_DATA* data, size_t count)
451 {
452  BITMAP_DATA* dst = (BITMAP_DATA*)calloc(count, sizeof(BITMAP_DATA));
453 
454  if (!dst)
455  goto fail;
456 
457  for (size_t x = 0; x < count; x++)
458  {
459  dst[x] = data[x];
460 
461  if (data[x].bitmapLength > 0)
462  {
463  dst[x].bitmapDataStream = malloc(data[x].bitmapLength);
464 
465  if (!dst[x].bitmapDataStream)
466  goto fail;
467 
468  memcpy(dst[x].bitmapDataStream, data[x].bitmapDataStream, data[x].bitmapLength);
469  }
470  }
471 
472  return dst;
473 fail:
474  free_bitmap_data(dst, count);
475  return NULL;
476 }
477 
478 void free_bitmap_update(rdpContext* context, BITMAP_UPDATE* pointer)
479 {
480  if (!pointer)
481  return;
482 
483  free_bitmap_data(pointer->rectangles, pointer->number);
484  free(pointer);
485 }
486 
487 BITMAP_UPDATE* copy_bitmap_update(rdpContext* context, const BITMAP_UPDATE* pointer)
488 {
489  BITMAP_UPDATE* dst = calloc(1, sizeof(BITMAP_UPDATE));
490 
491  if (!dst || !pointer)
492  goto fail;
493 
494  *dst = *pointer;
495  dst->rectangles = copy_bitmap_data(pointer->rectangles, pointer->number);
496 
497  if (!dst->rectangles)
498  goto fail;
499 
500  return dst;
501 fail:
502  WINPR_PRAGMA_DIAG_PUSH
503  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
504  free_bitmap_update(context, dst);
505  WINPR_PRAGMA_DIAG_POP
506  return NULL;
507 }
508 
509 CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context, const CACHE_BITMAP_ORDER* order)
510 {
511  CACHE_BITMAP_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_ORDER));
512 
513  if (!dst || !order)
514  goto fail;
515 
516  *dst = *order;
517 
518  if (order->bitmapLength > 0)
519  {
520  dst->bitmapDataStream = malloc(order->bitmapLength);
521 
522  if (!dst->bitmapDataStream)
523  goto fail;
524 
525  memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
526  }
527 
528  return dst;
529 fail:
530  WINPR_PRAGMA_DIAG_PUSH
531  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
532  free_cache_bitmap_order(context, dst);
533  WINPR_PRAGMA_DIAG_POP
534  return NULL;
535 }
536 
537 void free_cache_bitmap_order(rdpContext* context, CACHE_BITMAP_ORDER* order)
538 {
539  if (order)
540  free(order->bitmapDataStream);
541 
542  free(order);
543 }
544 
545 CACHE_BITMAP_V2_ORDER* copy_cache_bitmap_v2_order(rdpContext* context,
546  const CACHE_BITMAP_V2_ORDER* order)
547 {
548  CACHE_BITMAP_V2_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V2_ORDER));
549 
550  if (!dst || !order)
551  goto fail;
552 
553  *dst = *order;
554 
555  if (order->bitmapLength > 0)
556  {
557  dst->bitmapDataStream = malloc(order->bitmapLength);
558 
559  if (!dst->bitmapDataStream)
560  goto fail;
561 
562  memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
563  }
564 
565  return dst;
566 fail:
567  WINPR_PRAGMA_DIAG_PUSH
568  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
569  free_cache_bitmap_v2_order(context, dst);
570  WINPR_PRAGMA_DIAG_POP
571  return NULL;
572 }
573 
574 void free_cache_bitmap_v2_order(rdpContext* context, CACHE_BITMAP_V2_ORDER* order)
575 {
576  if (order)
577  free(order->bitmapDataStream);
578 
579  free(order);
580 }
581 
582 CACHE_BITMAP_V3_ORDER* copy_cache_bitmap_v3_order(rdpContext* context,
583  const CACHE_BITMAP_V3_ORDER* order)
584 {
585  CACHE_BITMAP_V3_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V3_ORDER));
586 
587  if (!dst || !order)
588  goto fail;
589 
590  *dst = *order;
591 
592  if (order->bitmapData.length > 0)
593  {
594  dst->bitmapData.data = malloc(order->bitmapData.length);
595 
596  if (!dst->bitmapData.data)
597  goto fail;
598 
599  memcpy(dst->bitmapData.data, order->bitmapData.data, order->bitmapData.length);
600  }
601 
602  return dst;
603 fail:
604  WINPR_PRAGMA_DIAG_PUSH
605  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
606  free_cache_bitmap_v3_order(context, dst);
607  WINPR_PRAGMA_DIAG_POP
608  return NULL;
609 }
610 
611 void free_cache_bitmap_v3_order(rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
612 {
613  if (order)
614  free(order->bitmapData.data);
615 
616  free(order);
617 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
Definition: persistent.h:70