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