1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Bitmap Cache V2
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 
26 #include <winpr/crt.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/cache/bitmap.h>
34 #include <freerdp/gdi/bitmap.h>
35 
36 #include "../gdi/gdi.h"
37 #include "../core/graphics.h"
38 
39 #include "bitmap.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* bitmap_cache, UINT32 id, UINT32 index,
45                              rdpBitmap* bitmap);
46 
update_gdi_memblt(rdpContext * context,MEMBLT_ORDER * memblt)47 static BOOL update_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt)
48 {
49 	rdpBitmap* bitmap;
50 	rdpCache* cache = context->cache;
51 
52 	if (memblt->cacheId == 0xFF)
53 		bitmap = offscreen_cache_get(cache->offscreen, memblt->cacheIndex);
54 	else
55 		bitmap = bitmap_cache_get(cache->bitmap, (BYTE)memblt->cacheId, memblt->cacheIndex);
56 
57 	/* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
58 	if (bitmap == NULL)
59 		return TRUE;
60 
61 	memblt->bitmap = bitmap;
62 	return IFCALLRESULT(TRUE, cache->bitmap->MemBlt, context, memblt);
63 }
64 
update_gdi_mem3blt(rdpContext * context,MEM3BLT_ORDER * mem3blt)65 static BOOL update_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt)
66 {
67 	BYTE style;
68 	rdpBitmap* bitmap;
69 	rdpCache* cache = context->cache;
70 	rdpBrush* brush = &mem3blt->brush;
71 	BOOL ret = TRUE;
72 
73 	if (mem3blt->cacheId == 0xFF)
74 		bitmap = offscreen_cache_get(cache->offscreen, mem3blt->cacheIndex);
75 	else
76 		bitmap = bitmap_cache_get(cache->bitmap, (BYTE)mem3blt->cacheId, mem3blt->cacheIndex);
77 
78 	/* XP-SP2 servers sometimes ask for cached bitmaps they've never defined. */
79 	if (!bitmap)
80 		return TRUE;
81 
82 	style = brush->style;
83 
84 	if (brush->style & CACHED_BRUSH)
85 	{
86 		brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
87 
88 		if (!brush->data)
89 			return FALSE;
90 
91 		brush->style = 0x03;
92 	}
93 
94 	mem3blt->bitmap = bitmap;
95 	IFCALLRET(cache->bitmap->Mem3Blt, ret, context, mem3blt);
96 	brush->style = style;
97 	return ret;
98 }
99 
update_gdi_cache_bitmap(rdpContext * context,const CACHE_BITMAP_ORDER * cacheBitmap)100 static BOOL update_gdi_cache_bitmap(rdpContext* context, const CACHE_BITMAP_ORDER* cacheBitmap)
101 {
102 	rdpBitmap* bitmap;
103 	rdpBitmap* prevBitmap;
104 	rdpCache* cache = context->cache;
105 	bitmap = Bitmap_Alloc(context);
106 
107 	if (!bitmap)
108 		return FALSE;
109 
110 	Bitmap_SetDimensions(bitmap, cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight);
111 
112 	if (!bitmap->Decompress(context, bitmap, cacheBitmap->bitmapDataStream,
113 	                        cacheBitmap->bitmapWidth, cacheBitmap->bitmapHeight,
114 	                        cacheBitmap->bitmapBpp, cacheBitmap->bitmapLength,
115 	                        cacheBitmap->compressed, RDP_CODEC_ID_NONE))
116 	{
117 		Bitmap_Free(context, bitmap);
118 		return FALSE;
119 	}
120 
121 	if (!bitmap->New(context, bitmap))
122 	{
123 		Bitmap_Free(context, bitmap);
124 		return FALSE;
125 	}
126 
127 	prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex);
128 	Bitmap_Free(context, prevBitmap);
129 	return bitmap_cache_put(cache->bitmap, cacheBitmap->cacheId, cacheBitmap->cacheIndex, bitmap);
130 }
131 
update_gdi_cache_bitmap_v2(rdpContext * context,CACHE_BITMAP_V2_ORDER * cacheBitmapV2)132 static BOOL update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cacheBitmapV2)
133 
134 {
135 	rdpBitmap* bitmap;
136 	rdpBitmap* prevBitmap;
137 	rdpCache* cache = context->cache;
138 	rdpSettings* settings = context->settings;
139 	bitmap = Bitmap_Alloc(context);
140 
141 	if (!bitmap)
142 		return FALSE;
143 
144 	if (!cacheBitmapV2->bitmapBpp)
145 		cacheBitmapV2->bitmapBpp = settings->ColorDepth;
146 
147 	if ((settings->ColorDepth == 15) && (cacheBitmapV2->bitmapBpp == 16))
148 		cacheBitmapV2->bitmapBpp = settings->ColorDepth;
149 
150 	Bitmap_SetDimensions(bitmap, cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight);
151 
152 	if (!bitmap->Decompress(context, bitmap, cacheBitmapV2->bitmapDataStream,
153 	                        cacheBitmapV2->bitmapWidth, cacheBitmapV2->bitmapHeight,
154 	                        cacheBitmapV2->bitmapBpp, cacheBitmapV2->bitmapLength,
155 	                        cacheBitmapV2->compressed, RDP_CODEC_ID_NONE))
156 	{
157 		Bitmap_Free(context, bitmap);
158 		return FALSE;
159 	}
160 
161 	prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex);
162 
163 	if (!bitmap->New(context, bitmap))
164 	{
165 		Bitmap_Free(context, bitmap);
166 		return FALSE;
167 	}
168 
169 	Bitmap_Free(context, prevBitmap);
170 	return bitmap_cache_put(cache->bitmap, cacheBitmapV2->cacheId, cacheBitmapV2->cacheIndex,
171 	                        bitmap);
172 }
173 
update_gdi_cache_bitmap_v3(rdpContext * context,CACHE_BITMAP_V3_ORDER * cacheBitmapV3)174 static BOOL update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cacheBitmapV3)
175 {
176 	rdpBitmap* bitmap;
177 	rdpBitmap* prevBitmap;
178 	BOOL compressed = TRUE;
179 	rdpCache* cache = context->cache;
180 	rdpSettings* settings = context->settings;
181 	BITMAP_DATA_EX* bitmapData = &cacheBitmapV3->bitmapData;
182 	bitmap = Bitmap_Alloc(context);
183 
184 	if (!bitmap)
185 		return FALSE;
186 
187 	if (!cacheBitmapV3->bpp)
188 		cacheBitmapV3->bpp = settings->ColorDepth;
189 
190 	compressed = (bitmapData->codecID != RDP_CODEC_ID_NONE);
191 	Bitmap_SetDimensions(bitmap, bitmapData->width, bitmapData->height);
192 
193 	if (!bitmap->Decompress(context, bitmap, bitmapData->data, bitmapData->width,
194 	                        bitmapData->height, bitmapData->bpp, bitmapData->length, compressed,
195 	                        bitmapData->codecID))
196 	{
197 		Bitmap_Free(context, bitmap);
198 		return FALSE;
199 	}
200 
201 	if (!bitmap->New(context, bitmap))
202 	{
203 		Bitmap_Free(context, bitmap);
204 		return FALSE;
205 	}
206 
207 	prevBitmap = bitmap_cache_get(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex);
208 	Bitmap_Free(context, prevBitmap);
209 	return bitmap_cache_put(cache->bitmap, cacheBitmapV3->cacheId, cacheBitmapV3->cacheIndex,
210 	                        bitmap);
211 }
212 
bitmap_cache_get(rdpBitmapCache * bitmapCache,UINT32 id,UINT32 index)213 rdpBitmap* bitmap_cache_get(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index)
214 {
215 	rdpBitmap* bitmap;
216 
217 	if (id >= bitmapCache->maxCells)
218 	{
219 		WLog_ERR(TAG, "get invalid bitmap cell id: %" PRIu32 "", id);
220 		return NULL;
221 	}
222 
223 	if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
224 	{
225 		index = bitmapCache->cells[id].number;
226 	}
227 	else if (index > bitmapCache->cells[id].number)
228 	{
229 		WLog_ERR(TAG, "get invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
230 		return NULL;
231 	}
232 
233 	bitmap = bitmapCache->cells[id].entries[index];
234 	return bitmap;
235 }
236 
bitmap_cache_put(rdpBitmapCache * bitmapCache,UINT32 id,UINT32 index,rdpBitmap * bitmap)237 BOOL bitmap_cache_put(rdpBitmapCache* bitmapCache, UINT32 id, UINT32 index, rdpBitmap* bitmap)
238 {
239 	if (id > bitmapCache->maxCells)
240 	{
241 		WLog_ERR(TAG, "put invalid bitmap cell id: %" PRIu32 "", id);
242 		return FALSE;
243 	}
244 
245 	if (index == BITMAP_CACHE_WAITING_LIST_INDEX)
246 	{
247 		index = bitmapCache->cells[id].number;
248 	}
249 	else if (index > bitmapCache->cells[id].number)
250 	{
251 		WLog_ERR(TAG, "put invalid bitmap index %" PRIu32 " in cell id: %" PRIu32 "", index, id);
252 		return FALSE;
253 	}
254 
255 	bitmapCache->cells[id].entries[index] = bitmap;
256 	return TRUE;
257 }
258 
bitmap_cache_register_callbacks(rdpUpdate * update)259 void bitmap_cache_register_callbacks(rdpUpdate* update)
260 {
261 	rdpCache* cache = update->context->cache;
262 	cache->bitmap->MemBlt = update->primary->MemBlt;
263 	cache->bitmap->Mem3Blt = update->primary->Mem3Blt;
264 	update->primary->MemBlt = update_gdi_memblt;
265 	update->primary->Mem3Blt = update_gdi_mem3blt;
266 	update->secondary->CacheBitmap = update_gdi_cache_bitmap;
267 	update->secondary->CacheBitmapV2 = update_gdi_cache_bitmap_v2;
268 	update->secondary->CacheBitmapV3 = update_gdi_cache_bitmap_v3;
269 	update->BitmapUpdate = gdi_bitmap_update;
270 }
271 
bitmap_cache_new(rdpSettings * settings)272 rdpBitmapCache* bitmap_cache_new(rdpSettings* settings)
273 {
274 	UINT32 i;
275 	rdpBitmapCache* bitmapCache;
276 	bitmapCache = (rdpBitmapCache*)calloc(1, sizeof(rdpBitmapCache));
277 
278 	if (!bitmapCache)
279 		return NULL;
280 
281 	bitmapCache->settings = settings;
282 	bitmapCache->update = ((freerdp*)settings->instance)->update;
283 	bitmapCache->context = bitmapCache->update->context;
284 	bitmapCache->cells =
285 	    (BITMAP_V2_CELL*)calloc(settings->BitmapCacheV2NumCells, sizeof(BITMAP_V2_CELL));
286 
287 	if (!bitmapCache->cells)
288 		goto fail;
289 	bitmapCache->maxCells = settings->BitmapCacheV2NumCells;
290 
291 	for (i = 0; i < bitmapCache->maxCells; i++)
292 	{
293 		BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
294 		UINT32 nr = settings->BitmapCacheV2CellInfo[i].numEntries;
295 		/* allocate an extra entry for BITMAP_CACHE_WAITING_LIST_INDEX */
296 		cell->entries = (rdpBitmap**)calloc((nr + 1), sizeof(rdpBitmap*));
297 
298 		if (!cell->entries)
299 			goto fail;
300 		cell->number = nr;
301 	}
302 
303 	return bitmapCache;
304 fail:
305 
306 	bitmap_cache_free(bitmapCache);
307 	return NULL;
308 }
309 
bitmap_cache_free(rdpBitmapCache * bitmapCache)310 void bitmap_cache_free(rdpBitmapCache* bitmapCache)
311 {
312 	if (bitmapCache)
313 	{
314 		UINT32 i;
315 		for (i = 0; i < bitmapCache->maxCells; i++)
316 		{
317 			UINT32 j;
318 			BITMAP_V2_CELL* cell = &bitmapCache->cells[i];
319 			if (!cell->entries)
320 				continue;
321 			for (j = 0; j < cell->number + 1; j++)
322 			{
323 				rdpBitmap* bitmap = cell->entries[j];
324 				Bitmap_Free(bitmapCache->context, bitmap);
325 			}
326 
327 			free(bitmapCache->cells[i].entries);
328 		}
329 
330 		free(bitmapCache->cells);
331 		free(bitmapCache);
332 	}
333 }
334 
free_bitmap_data(BITMAP_DATA * data,size_t count)335 static void free_bitmap_data(BITMAP_DATA* data, size_t count)
336 {
337 	size_t x;
338 
339 	if (!data)
340 		return;
341 
342 	for (x = 0; x < count; x++)
343 		free(data[x].bitmapDataStream);
344 
345 	free(data);
346 }
347 
copy_bitmap_data(const BITMAP_DATA * data,size_t count)348 static BITMAP_DATA* copy_bitmap_data(const BITMAP_DATA* data, size_t count)
349 {
350 	size_t x;
351 	BITMAP_DATA* dst = (BITMAP_DATA*)calloc(count, sizeof(BITMAP_DATA));
352 
353 	if (!dst)
354 		goto fail;
355 
356 	for (x = 0; x < count; x++)
357 	{
358 		dst[x] = data[x];
359 
360 		if (data[x].bitmapLength > 0)
361 		{
362 			dst[x].bitmapDataStream = malloc(data[x].bitmapLength);
363 
364 			if (!dst[x].bitmapDataStream)
365 				goto fail;
366 
367 			memcpy(dst[x].bitmapDataStream, data[x].bitmapDataStream, data[x].bitmapLength);
368 		}
369 	}
370 
371 	return dst;
372 fail:
373 	free_bitmap_data(dst, count);
374 	return NULL;
375 }
376 
free_bitmap_update(rdpContext * context,BITMAP_UPDATE * pointer)377 void free_bitmap_update(rdpContext* context, BITMAP_UPDATE* pointer)
378 {
379 	if (!pointer)
380 		return;
381 
382 	free_bitmap_data(pointer->rectangles, pointer->number);
383 	free(pointer);
384 }
385 
copy_bitmap_update(rdpContext * context,const BITMAP_UPDATE * pointer)386 BITMAP_UPDATE* copy_bitmap_update(rdpContext* context, const BITMAP_UPDATE* pointer)
387 {
388 	BITMAP_UPDATE* dst = calloc(1, sizeof(BITMAP_UPDATE));
389 
390 	if (!dst || !pointer)
391 		goto fail;
392 
393 	*dst = *pointer;
394 	dst->rectangles = copy_bitmap_data(pointer->rectangles, pointer->number);
395 
396 	if (!dst->rectangles)
397 		goto fail;
398 
399 	return dst;
400 fail:
401 	free_bitmap_update(context, dst);
402 	return NULL;
403 }
404 
copy_cache_bitmap_order(rdpContext * context,const CACHE_BITMAP_ORDER * order)405 CACHE_BITMAP_ORDER* copy_cache_bitmap_order(rdpContext* context, const CACHE_BITMAP_ORDER* order)
406 {
407 	CACHE_BITMAP_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_ORDER));
408 
409 	if (!dst || !order)
410 		goto fail;
411 
412 	*dst = *order;
413 
414 	if (order->bitmapLength > 0)
415 	{
416 		dst->bitmapDataStream = malloc(order->bitmapLength);
417 
418 		if (!dst->bitmapDataStream)
419 			goto fail;
420 
421 		memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
422 	}
423 
424 	return dst;
425 fail:
426 	free_cache_bitmap_order(context, dst);
427 	return NULL;
428 }
429 
free_cache_bitmap_order(rdpContext * context,CACHE_BITMAP_ORDER * order)430 void free_cache_bitmap_order(rdpContext* context, CACHE_BITMAP_ORDER* order)
431 {
432 	if (order)
433 		free(order->bitmapDataStream);
434 
435 	free(order);
436 }
437 
copy_cache_bitmap_v2_order(rdpContext * context,const CACHE_BITMAP_V2_ORDER * order)438 CACHE_BITMAP_V2_ORDER* copy_cache_bitmap_v2_order(rdpContext* context,
439                                                   const CACHE_BITMAP_V2_ORDER* order)
440 {
441 	CACHE_BITMAP_V2_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V2_ORDER));
442 
443 	if (!dst || !order)
444 		goto fail;
445 
446 	*dst = *order;
447 
448 	if (order->bitmapLength > 0)
449 	{
450 		dst->bitmapDataStream = malloc(order->bitmapLength);
451 
452 		if (!dst->bitmapDataStream)
453 			goto fail;
454 
455 		memcpy(dst->bitmapDataStream, order->bitmapDataStream, order->bitmapLength);
456 	}
457 
458 	return dst;
459 fail:
460 	free_cache_bitmap_v2_order(context, dst);
461 	return NULL;
462 }
463 
free_cache_bitmap_v2_order(rdpContext * context,CACHE_BITMAP_V2_ORDER * order)464 void free_cache_bitmap_v2_order(rdpContext* context, CACHE_BITMAP_V2_ORDER* order)
465 {
466 	if (order)
467 		free(order->bitmapDataStream);
468 
469 	free(order);
470 }
471 
copy_cache_bitmap_v3_order(rdpContext * context,const CACHE_BITMAP_V3_ORDER * order)472 CACHE_BITMAP_V3_ORDER* copy_cache_bitmap_v3_order(rdpContext* context,
473                                                   const CACHE_BITMAP_V3_ORDER* order)
474 {
475 	CACHE_BITMAP_V3_ORDER* dst = calloc(1, sizeof(CACHE_BITMAP_V3_ORDER));
476 
477 	if (!dst || !order)
478 		goto fail;
479 
480 	*dst = *order;
481 
482 	if (order->bitmapData.length > 0)
483 	{
484 		dst->bitmapData.data = malloc(order->bitmapData.length);
485 
486 		if (!dst->bitmapData.data)
487 			goto fail;
488 
489 		memcpy(dst->bitmapData.data, order->bitmapData.data, order->bitmapData.length);
490 	}
491 
492 	return dst;
493 fail:
494 	free_cache_bitmap_v3_order(context, dst);
495 	return NULL;
496 }
497 
free_cache_bitmap_v3_order(rdpContext * context,CACHE_BITMAP_V3_ORDER * order)498 void free_cache_bitmap_v3_order(rdpContext* context, CACHE_BITMAP_V3_ORDER* order)
499 {
500 	if (order)
501 		free(order->bitmapData.data);
502 
503 	free(order);
504 }
505