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