FreeRDP
Loading...
Searching...
No Matches
shadow_capture.c
1
19#include <freerdp/config.h>
20
21#include <winpr/crt.h>
22#include <winpr/print.h>
23
24#include <freerdp/log.h>
25
26#include "shadow_surface.h"
27
28#include "shadow_capture.h"
29
30int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip)
31{
32 int dx = 0;
33 int dy = 0;
34 dx = (rect->left % 16);
35
36 if (dx != 0)
37 {
38 rect->left -= dx;
39 rect->right += dx;
40 }
41
42 dx = (rect->right % 16);
43
44 if (dx != 0)
45 {
46 rect->right += (16 - dx);
47 }
48
49 dy = (rect->top % 16);
50
51 if (dy != 0)
52 {
53 rect->top -= dy;
54 rect->bottom += dy;
55 }
56
57 dy = (rect->bottom % 16);
58
59 if (dy != 0)
60 {
61 rect->bottom += (16 - dy);
62 }
63
64 if (rect->left < clip->left)
65 rect->left = clip->left;
66
67 if (rect->top < clip->top)
68 rect->top = clip->top;
69
70 if (rect->right > clip->right)
71 rect->right = clip->right;
72
73 if (rect->bottom > clip->bottom)
74 rect->bottom = clip->bottom;
75
76 return 1;
77}
78
79#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
80int shadow_capture_compare(const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UINT32 nWidth,
81 UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 nStep2,
82 RECTANGLE_16* WINPR_RESTRICT rect)
83{
84 return shadow_capture_compare_with_format(pData1, PIXEL_FORMAT_BGRX32, nStep1, nWidth, nHeight,
85 pData2, PIXEL_FORMAT_BGRX32, nStep2, rect);
86}
87#endif
88
89WINPR_ATTR_NODISCARD
90static BOOL color_equal_no_alpha(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB)
91{
92 BYTE ar = 0;
93 BYTE ag = 0;
94 BYTE ab = 0;
95 BYTE br = 0;
96 BYTE bg = 0;
97 BYTE bb = 0;
98 FreeRDPSplitColor(colorA, formatA, &ar, &ag, &ab, nullptr, nullptr);
99 FreeRDPSplitColor(colorB, formatB, &br, &bg, &bb, nullptr, nullptr);
100
101 if (ar != br)
102 return FALSE;
103 if (ag != bg)
104 return FALSE;
105 if (ab != bb)
106 return FALSE;
107 return TRUE;
108}
109
110WINPR_ATTR_NODISCARD
111static BOOL pixel_equal_no_alpha(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
112 const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count)
113{
114 const size_t bppA = FreeRDPGetBytesPerPixel(formatA);
115 const size_t bppB = FreeRDPGetBytesPerPixel(formatB);
116
117 for (size_t x = 0; x < count; x++)
118 {
119 const UINT32 colorA = FreeRDPReadColor(&a[bppA * x], formatA);
120 const UINT32 colorB = FreeRDPReadColor(&b[bppB * x], formatB);
121 if (!color_equal_no_alpha(colorA, formatA, colorB, formatB))
122 return FALSE;
123 }
124
125 return TRUE;
126}
127
128WINPR_ATTR_NODISCARD
129static BOOL pixel_equal_same_format(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
130 const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count)
131{
132 if (formatA != formatB)
133 return FALSE;
134 const size_t bppA = FreeRDPGetBytesPerPixel(formatA);
135 return memcmp(a, b, count * bppA) == 0;
136}
137
138/* 32 bpp formats that differ only in having alpha (e.g. BGRX32 and BGRA32) keep the colour
139 * channels at the same bits, so compare the colours under a mask of those bits, never the alpha
140 * or padding byte: an X server leaves it 0 in a depth 24 image, while the X format of the
141 * surface reads as opaque. */
142WINPR_ATTR_NODISCARD
143static BOOL pixel_equal_32_ignore_alpha(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
144 const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count)
145{
146 const UINT32 mask = FreeRDPGetColor(formatA, 0xFF, 0xFF, 0xFF, 0x00);
147
148 for (size_t x = 0; x < count; x++)
149 {
150 const UINT32 colorA = FreeRDPReadColor(&a[4 * x], formatA);
151 const UINT32 colorB = FreeRDPReadColor(&b[4 * x], formatB);
152 if (((colorA ^ colorB) & mask) != 0)
153 return FALSE;
154 }
155 return TRUE;
156}
157
158typedef BOOL (*pixel_equal_fn_t)(const BYTE* WINPR_RESTRICT a, UINT32 formatA,
159 const BYTE* WINPR_RESTRICT b, UINT32 formatB, size_t count);
160
161WINPR_ATTR_NODISCARD
162static pixel_equal_fn_t get_comparison_fn(DWORD format1, DWORD format2)
163{
164
165 if (format1 == format2)
166 return pixel_equal_same_format;
167
168 const UINT32 bpp1 = FreeRDPGetBitsPerPixel(format1);
169
170 if (!FreeRDPColorHasAlpha(format1) || !FreeRDPColorHasAlpha(format2))
171 {
172 /* RGBA32 and RGBX32 or similar: the alpha or padding byte differs (0 vs opaque), so
173 * comparing it would report every pixel as changed. Compare the colour bytes only. */
174 if ((bpp1 == 32) && FreeRDPAreColorFormatsEqualNoAlpha(format1, format2))
175 {
176 switch (format1)
177 {
178 case PIXEL_FORMAT_ARGB32:
179 case PIXEL_FORMAT_XRGB32:
180 case PIXEL_FORMAT_ABGR32:
181 case PIXEL_FORMAT_XBGR32:
182 case PIXEL_FORMAT_RGBA32:
183 case PIXEL_FORMAT_RGBX32:
184 case PIXEL_FORMAT_BGRA32:
185 case PIXEL_FORMAT_BGRX32:
186 return pixel_equal_32_ignore_alpha;
187 default:
188 break;
189 }
190 }
191 return pixel_equal_no_alpha;
192 }
193 else
194 return pixel_equal_no_alpha;
195}
196
197int shadow_capture_compare_with_format(const BYTE* WINPR_RESTRICT pData1, UINT32 format1,
198 UINT32 nStep1, UINT32 nWidth, UINT32 nHeight,
199 const BYTE* WINPR_RESTRICT pData2, UINT32 format2,
200 UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect)
201{
202 pixel_equal_fn_t pixel_equal_fn = get_comparison_fn(format1, format2);
203 BOOL allEqual = TRUE;
204 UINT32 tw = 0;
205 const UINT32 nrow = (nHeight + 15) / 16;
206 const UINT32 ncol = (nWidth + 15) / 16;
207 UINT32 l = ncol + 1;
208 UINT32 t = nrow + 1;
209 UINT32 r = 0;
210 UINT32 b = 0;
211 const size_t bppA = FreeRDPGetBytesPerPixel(format1);
212 const size_t bppB = FreeRDPGetBytesPerPixel(format2);
213 const RECTANGLE_16 empty = WINPR_C_ARRAY_INIT;
214 WINPR_ASSERT(rect);
215
216 *rect = empty;
217
218 for (size_t ty = 0; ty < nrow; ty++)
219 {
220 BOOL rowEqual = TRUE;
221 size_t th = ((ty + 1) == nrow) ? (nHeight % 16) : 16;
222
223 if (!th)
224 th = 16;
225
226 for (size_t tx = 0; tx < ncol; tx++)
227 {
228 BOOL equal = TRUE;
229 tw = ((tx + 1) == ncol) ? (nWidth % 16) : 16;
230
231 if (!tw)
232 tw = 16;
233
234 const BYTE* p1 = &pData1[(ty * 16ULL * nStep1) + (tx * 16ull * bppA)];
235 const BYTE* p2 = &pData2[(ty * 16ULL * nStep2) + (tx * 16ull * bppB)];
236
237 for (size_t k = 0; k < th; k++)
238 {
239 if (!pixel_equal_fn(p1, format1, p2, format2, tw))
240 {
241 equal = FALSE;
242 break;
243 }
244
245 p1 += nStep1;
246 p2 += nStep2;
247 }
248
249 if (!equal)
250 {
251 rowEqual = FALSE;
252 if (l > tx)
253 l = (UINT32)tx;
254
255 if (r < tx)
256 r = (UINT32)tx;
257 }
258 }
259
260 if (!rowEqual)
261 {
262 allEqual = FALSE;
263
264 if (t > ty)
265 t = (UINT32)ty;
266
267 if (b < ty)
268 b = (UINT32)ty;
269 }
270 }
271
272 if (allEqual)
273 return 0;
274
275 WINPR_ASSERT(l * 16 <= UINT16_MAX);
276 WINPR_ASSERT(t * 16 <= UINT16_MAX);
277 WINPR_ASSERT((r + 1) * 16 <= UINT16_MAX);
278 WINPR_ASSERT((b + 1) * 16 <= UINT16_MAX);
279 rect->left = (UINT16)l * 16;
280 rect->top = (UINT16)t * 16;
281 rect->right = (UINT16)(r + 1) * 16;
282 rect->bottom = (UINT16)(b + 1) * 16;
283
284 WINPR_ASSERT(nWidth <= UINT16_MAX);
285 if (rect->right > nWidth)
286 rect->right = (UINT16)nWidth;
287
288 WINPR_ASSERT(nHeight <= UINT16_MAX);
289 if (rect->bottom > nHeight)
290 rect->bottom = (UINT16)nHeight;
291
292 return 1;
293}
294
295rdpShadowCapture* shadow_capture_new(rdpShadowServer* server)
296{
297 WINPR_ASSERT(server);
298
299 rdpShadowCapture* capture = (rdpShadowCapture*)calloc(1, sizeof(rdpShadowCapture));
300
301 if (!capture)
302 return nullptr;
303
304 capture->server = server;
305
306 if (!InitializeCriticalSectionAndSpinCount(&(capture->lock), 4000))
307 {
308 WINPR_PRAGMA_DIAG_PUSH
309 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
310 shadow_capture_free(capture);
311 WINPR_PRAGMA_DIAG_POP
312 return nullptr;
313 }
314
315 return capture;
316}
317
318void shadow_capture_free(rdpShadowCapture* capture)
319{
320 if (!capture)
321 return;
322
323 DeleteCriticalSection(&(capture->lock));
324 free(capture);
325}