1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Brush Cache
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 #include <winpr/crt.h>
26 
27 #include <freerdp/log.h>
28 #include <freerdp/update.h>
29 #include <freerdp/freerdp.h>
30 #include <winpr/stream.h>
31 
32 #include <freerdp/cache/brush.h>
33 
34 #include "brush.h"
35 
36 #define TAG FREERDP_TAG("cache.brush")
37 
38 struct _BRUSH_ENTRY
39 {
40 	UINT32 bpp;
41 	void* entry;
42 };
43 typedef struct _BRUSH_ENTRY BRUSH_ENTRY;
44 
45 struct rdp_brush_cache
46 {
47 	pPatBlt PatBlt;          /* 0 */
48 	pCacheBrush CacheBrush;  /* 1 */
49 	pPolygonSC PolygonSC;    /* 2 */
50 	pPolygonCB PolygonCB;    /* 3 */
51 	UINT32 paddingA[16 - 4]; /* 4 */
52 
53 	UINT32 maxEntries;        /* 16 */
54 	UINT32 maxMonoEntries;    /* 17 */
55 	BRUSH_ENTRY* entries;     /* 18 */
56 	BRUSH_ENTRY* monoEntries; /* 19 */
57 	UINT32 paddingB[32 - 20]; /* 20 */
58 
59 	/* internal */
60 
61 	rdpSettings* settings;
62 };
63 
update_gdi_patblt(rdpContext * context,PATBLT_ORDER * patblt)64 static BOOL update_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt)
65 {
66 	BYTE style;
67 	BOOL ret = TRUE;
68 	rdpBrush* brush = &patblt->brush;
69 	const rdpCache* cache = context->cache;
70 	style = brush->style;
71 
72 	if (brush->style & CACHED_BRUSH)
73 	{
74 		brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
75 		brush->style = 0x03;
76 	}
77 
78 	IFCALLRET(cache->brush->PatBlt, ret, context, patblt);
79 	brush->style = style;
80 	return ret;
81 }
82 
update_gdi_polygon_sc(rdpContext * context,const POLYGON_SC_ORDER * polygon_sc)83 static BOOL update_gdi_polygon_sc(rdpContext* context, const POLYGON_SC_ORDER* polygon_sc)
84 {
85 	rdpCache* cache = context->cache;
86 	return IFCALLRESULT(TRUE, cache->brush->PolygonSC, context, polygon_sc);
87 }
88 
update_gdi_polygon_cb(rdpContext * context,POLYGON_CB_ORDER * polygon_cb)89 static BOOL update_gdi_polygon_cb(rdpContext* context, POLYGON_CB_ORDER* polygon_cb)
90 {
91 	BYTE style;
92 	rdpBrush* brush = &polygon_cb->brush;
93 	rdpCache* cache = context->cache;
94 	BOOL ret = TRUE;
95 	style = brush->style;
96 
97 	if (brush->style & CACHED_BRUSH)
98 	{
99 		brush->data = brush_cache_get(cache->brush, brush->index, &brush->bpp);
100 		brush->style = 0x03;
101 	}
102 
103 	IFCALLRET(cache->brush->PolygonCB, ret, context, polygon_cb);
104 	brush->style = style;
105 	return ret;
106 }
107 
update_gdi_cache_brush(rdpContext * context,const CACHE_BRUSH_ORDER * cacheBrush)108 static BOOL update_gdi_cache_brush(rdpContext* context, const CACHE_BRUSH_ORDER* cacheBrush)
109 {
110 	UINT32 length;
111 	void* data = NULL;
112 	rdpCache* cache = context->cache;
113 	length = cacheBrush->bpp * 64 / 8;
114 	data = malloc(length);
115 
116 	if (!data)
117 		return FALSE;
118 
119 	CopyMemory(data, cacheBrush->data, length);
120 	brush_cache_put(cache->brush, cacheBrush->index, data, cacheBrush->bpp);
121 	return TRUE;
122 }
123 
brush_cache_get(rdpBrushCache * brushCache,UINT32 index,UINT32 * bpp)124 void* brush_cache_get(rdpBrushCache* brushCache, UINT32 index, UINT32* bpp)
125 {
126 	void* entry;
127 
128 	if (!brushCache)
129 		return NULL;
130 
131 	if (!bpp)
132 		return NULL;
133 
134 	if (*bpp == 1)
135 	{
136 		if (index >= brushCache->maxMonoEntries)
137 		{
138 			WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", *bpp, index);
139 			return NULL;
140 		}
141 
142 		*bpp = brushCache->monoEntries[index].bpp;
143 		entry = brushCache->monoEntries[index].entry;
144 	}
145 	else
146 	{
147 		if (index >= brushCache->maxEntries)
148 		{
149 			WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", *bpp, index);
150 			return NULL;
151 		}
152 
153 		*bpp = brushCache->entries[index].bpp;
154 		entry = brushCache->entries[index].entry;
155 	}
156 
157 	if (entry == NULL)
158 	{
159 		WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) at index: 0x%08" PRIX32 "", *bpp, index);
160 		return NULL;
161 	}
162 
163 	return entry;
164 }
165 
brush_cache_put(rdpBrushCache * brushCache,UINT32 index,void * entry,UINT32 bpp)166 void brush_cache_put(rdpBrushCache* brushCache, UINT32 index, void* entry, UINT32 bpp)
167 {
168 	if (bpp == 1)
169 	{
170 		if (index >= brushCache->maxMonoEntries)
171 		{
172 			WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", bpp, index);
173 			free(entry);
174 			return;
175 		}
176 
177 		free(brushCache->monoEntries[index].entry);
178 		brushCache->monoEntries[index].bpp = bpp;
179 		brushCache->monoEntries[index].entry = entry;
180 	}
181 	else
182 	{
183 		if (index >= brushCache->maxEntries)
184 		{
185 			WLog_ERR(TAG, "invalid brush (%" PRIu32 " bpp) index: 0x%08" PRIX32 "", bpp, index);
186 			free(entry);
187 			return;
188 		}
189 
190 		free(brushCache->entries[index].entry);
191 		brushCache->entries[index].bpp = bpp;
192 		brushCache->entries[index].entry = entry;
193 	}
194 }
195 
brush_cache_register_callbacks(rdpUpdate * update)196 void brush_cache_register_callbacks(rdpUpdate* update)
197 {
198 	rdpCache* cache = update->context->cache;
199 	cache->brush->PatBlt = update->primary->PatBlt;
200 	cache->brush->PolygonSC = update->primary->PolygonSC;
201 	cache->brush->PolygonCB = update->primary->PolygonCB;
202 	update->primary->PatBlt = update_gdi_patblt;
203 	update->primary->PolygonSC = update_gdi_polygon_sc;
204 	update->primary->PolygonCB = update_gdi_polygon_cb;
205 	update->secondary->CacheBrush = update_gdi_cache_brush;
206 }
207 
brush_cache_new(rdpSettings * settings)208 rdpBrushCache* brush_cache_new(rdpSettings* settings)
209 {
210 	rdpBrushCache* brushCache;
211 	brushCache = (rdpBrushCache*)calloc(1, sizeof(rdpBrushCache));
212 
213 	if (!brushCache)
214 		return NULL;
215 
216 	brushCache->settings = settings;
217 	brushCache->maxEntries = 64;
218 	brushCache->maxMonoEntries = 64;
219 	brushCache->entries = (BRUSH_ENTRY*)calloc(brushCache->maxEntries, sizeof(BRUSH_ENTRY));
220 
221 	if (!brushCache->entries)
222 		goto error_entries;
223 
224 	brushCache->monoEntries = (BRUSH_ENTRY*)calloc(brushCache->maxMonoEntries, sizeof(BRUSH_ENTRY));
225 
226 	if (!brushCache->monoEntries)
227 		goto error_mono;
228 
229 	return brushCache;
230 error_mono:
231 	free(brushCache->entries);
232 error_entries:
233 	free(brushCache);
234 	return NULL;
235 }
236 
brush_cache_free(rdpBrushCache * brushCache)237 void brush_cache_free(rdpBrushCache* brushCache)
238 {
239 	int i;
240 
241 	if (brushCache)
242 	{
243 		if (brushCache->entries)
244 		{
245 			for (i = 0; i < (int)brushCache->maxEntries; i++)
246 				free(brushCache->entries[i].entry);
247 
248 			free(brushCache->entries);
249 		}
250 
251 		if (brushCache->monoEntries)
252 		{
253 			for (i = 0; i < (int)brushCache->maxMonoEntries; i++)
254 				free(brushCache->monoEntries[i].entry);
255 
256 			free(brushCache->monoEntries);
257 		}
258 
259 		free(brushCache);
260 	}
261 }
262 
free_cache_brush_order(rdpContext * context,CACHE_BRUSH_ORDER * order)263 void free_cache_brush_order(rdpContext* context, CACHE_BRUSH_ORDER* order)
264 {
265 	free(order);
266 }
267 
copy_cache_brush_order(rdpContext * context,const CACHE_BRUSH_ORDER * order)268 CACHE_BRUSH_ORDER* copy_cache_brush_order(rdpContext* context, const CACHE_BRUSH_ORDER* order)
269 {
270 	CACHE_BRUSH_ORDER* dst = calloc(1, sizeof(CACHE_BRUSH_ORDER));
271 
272 	if (!dst || !order)
273 		goto fail;
274 
275 	*dst = *order;
276 	return dst;
277 fail:
278 	free_cache_brush_order(context, dst);
279 	return NULL;
280 }
281