FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecInterleaved.c
1
2#include <freerdp/config.h>
3
4#include <math.h>
5
6#include <winpr/crt.h>
7#include <winpr/print.h>
8#include <winpr/json.h>
9#include <winpr/path.h>
10
11#include <freerdp/freerdp.h>
12#include <freerdp/codec/color.h>
13#include <freerdp/codec/bitmap.h>
14#include <freerdp/codec/interleaved.h>
15#include <winpr/crypto.h>
16#include <freerdp/utils/profiler.h>
17
18#include "TestFreeRDPHelpers.h"
19
20// #define CREATE_TEST_OUTPUT
21
22static bool run_encode_decode_single(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder,
23 BITMAP_INTERLEAVED_CONTEXT* decoder
24#if defined(WITH_PROFILER)
25 ,
26 PROFILER* profiler_comp, PROFILER* profiler_decomp
27#endif
28)
29{
30 bool rc2 = false;
31 bool rc = 0;
32 const UINT32 w = 64;
33 const UINT32 h = 64;
34 const UINT32 x = 0;
35 const UINT32 y = 0;
36 const UINT32 format = PIXEL_FORMAT_RGBX32;
37 const UINT32 bstep = FreeRDPGetBytesPerPixel(format);
38 const size_t step = (13ULL + w) * 4ULL;
39 const size_t SrcSize = step * h;
40 const int maxDiff = 4 * ((bpp < 24) ? 2 : 1);
41 UINT32 DstSize = SrcSize;
42 BYTE* pSrcData = calloc(1, SrcSize);
43 BYTE* pDstData = calloc(1, SrcSize);
44 BYTE* tmp = calloc(1, SrcSize);
45
46 if (!pSrcData || !pDstData || !tmp)
47 goto fail;
48
49 if (winpr_RAND(pSrcData, SrcSize) < 0)
50 goto fail;
51
52 if (!bitmap_interleaved_context_reset(encoder) || !bitmap_interleaved_context_reset(decoder))
53 goto fail;
54
55 PROFILER_ENTER(profiler_comp)
56 rc = interleaved_compress(encoder, tmp, &DstSize, w, h, pSrcData, format, step, x, y, nullptr,
57 bpp);
58 PROFILER_EXIT(profiler_comp)
59
60 if (!rc)
61 goto fail;
62
63 PROFILER_ENTER(profiler_decomp)
64 rc = interleaved_decompress(decoder, tmp, DstSize, w, h, bpp, pDstData, format, step, x, y, w,
65 h, nullptr);
66 PROFILER_EXIT(profiler_decomp)
67
68 if (!rc)
69 goto fail;
70
71 for (UINT32 i = 0; i < h; i++)
72 {
73 const BYTE* srcLine = &pSrcData[i * step];
74 const BYTE* dstLine = &pDstData[i * step];
75
76 for (UINT32 j = 0; j < w; j++)
77 {
78 BYTE r = 0;
79 BYTE g = 0;
80 BYTE b = 0;
81 BYTE dr = 0;
82 BYTE dg = 0;
83 BYTE db = 0;
84 const UINT32 srcColor = FreeRDPReadColor(&srcLine[1ULL * j * bstep], format);
85 const UINT32 dstColor = FreeRDPReadColor(&dstLine[1ULL * j * bstep], format);
86 FreeRDPSplitColor(srcColor, format, &r, &g, &b, nullptr, nullptr);
87 FreeRDPSplitColor(dstColor, format, &dr, &dg, &db, nullptr, nullptr);
88
89 if (abs(r - dr) > maxDiff)
90 goto fail;
91
92 if (abs(g - dg) > maxDiff)
93 goto fail;
94
95 if (abs(b - db) > maxDiff)
96 goto fail;
97 }
98 }
99
100 rc2 = true;
101fail:
102 free(pSrcData);
103 free(pDstData);
104 free(tmp);
105 return rc2;
106}
107
108static const char* get_profiler_name(bool encode, UINT16 bpp)
109{
110 switch (bpp)
111 {
112 case 24:
113 if (encode)
114 return "interleaved_compress 24bpp";
115 else
116 return "interleaved_decompress 24bpp";
117
118 case 16:
119 if (encode)
120 return "interleaved_compress 16bpp";
121 else
122 return "interleaved_decompress 16bpp";
123
124 case 15:
125 if (encode)
126 return "interleaved_compress 15bpp";
127 else
128 return "interleaved_decompress 15bpp";
129
130 default:
131 return "configuration error!";
132 }
133}
134
135static bool run_encode_decode(UINT16 bpp, BITMAP_INTERLEAVED_CONTEXT* encoder,
136 BITMAP_INTERLEAVED_CONTEXT* decoder)
137{
138 bool rc = false;
139 PROFILER_DEFINE(profiler_comp)
140 PROFILER_DEFINE(profiler_decomp)
141 PROFILER_CREATE(profiler_comp, get_profiler_name(true, bpp))
142 PROFILER_CREATE(profiler_decomp, get_profiler_name(false, bpp))
143
144 for (UINT32 x = 0; x < 50; x++)
145 {
146 if (!run_encode_decode_single(bpp, encoder, decoder
147#if defined(WITH_PROFILER)
148 ,
149 profiler_comp, profiler_decomp
150#endif
151 ))
152 goto fail;
153 }
154
155 rc = true;
156fail:
157 PROFILER_PRINT_HEADER
158 PROFILER_PRINT(profiler_comp)
159 PROFILER_PRINT(profiler_decomp)
160 PROFILER_PRINT_FOOTER
161 PROFILER_FREE(profiler_comp)
162 PROFILER_FREE(profiler_decomp)
163 return rc;
164}
165
166static bool TestColorConversion(void)
167{
168 const UINT32 formats[] = { PIXEL_FORMAT_RGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_ABGR15,
169 PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR16, PIXEL_FORMAT_RGB16 };
170
171 /* Check color conversion 15/16 -> 32bit maps to proper values */
172 for (UINT32 x = 0; x < ARRAYSIZE(formats); x++)
173 {
174 const UINT32 dstFormat = PIXEL_FORMAT_RGBA32;
175 const UINT32 format = formats[x];
176 const UINT32 colorLow = FreeRDPGetColor(format, 0, 0, 0, 255);
177 const UINT32 colorHigh = FreeRDPGetColor(format, 255, 255, 255, 255);
178 const UINT32 colorLow32 = FreeRDPConvertColor(colorLow, format, dstFormat, nullptr);
179 const UINT32 colorHigh32 = FreeRDPConvertColor(colorHigh, format, dstFormat, nullptr);
180 BYTE r = 0;
181 BYTE g = 0;
182 BYTE b = 0;
183 BYTE a = 0;
184 FreeRDPSplitColor(colorLow32, dstFormat, &r, &g, &b, &a, nullptr);
185 if ((r != 0) || (g != 0) || (b != 0))
186 return false;
187
188 FreeRDPSplitColor(colorHigh32, dstFormat, &r, &g, &b, &a, nullptr);
189 if ((r != 255) || (g != 255) || (b != 255))
190 return false;
191 }
192
193 return true;
194}
195
196static bool RunEncoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height,
197 uint32_t step, uint32_t bpp)
198{
199 bool rc = false;
200 void* data = nullptr;
201 void* encdata = nullptr;
202 BITMAP_INTERLEAVED_CONTEXT* encoder = bitmap_interleaved_context_new(true);
203 if (!encoder)
204 goto fail;
205
206 size_t srclen = 0;
207 data = test_codec_helper_read_data("interleaved", "bmp", name, &srclen);
208 if (!data)
209 goto fail;
210
211 encdata = calloc(srclen, 1);
212 if (!encdata)
213 goto fail;
214
215 for (size_t x = 0; x < 42; x++)
216 {
217 uint32_t enclen = WINPR_ASSERTING_INT_CAST(uint32_t, srclen);
218 if (!interleaved_compress(encoder, encdata, &enclen, width, height, data, format, step, 0,
219 0, nullptr, bpp))
220 goto fail;
221
222 char encname[128] = WINPR_C_ARRAY_INIT;
223 (void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp);
224#if defined(CREATE_TEST_OUTPUT)
225 test_codec_helper_write_data("interleaved", encname, name, encdata, enclen);
226#else
227 if (!test_codec_helper_compare("interleaved", encname, name, encdata, enclen))
228 goto fail;
229#endif
230 }
231
232 rc = true;
233
234fail:
235 free(data);
236 free(encdata);
237 bitmap_interleaved_context_free(encoder);
238 return rc;
239}
240
241static bool RunDecoderTest(const char* name, uint32_t format, uint32_t width, uint32_t height,
242 uint32_t step, uint32_t bpp)
243{
244 bool rc = false;
245 void* data = nullptr;
246 void* decdata = nullptr;
247 BITMAP_INTERLEAVED_CONTEXT* decoder = bitmap_interleaved_context_new(false);
248 if (!decoder)
249 goto fail;
250
251 char encname[128] = WINPR_C_ARRAY_INIT;
252 (void)_snprintf(encname, sizeof(encname), "enc-%" PRIu32, bpp);
253
254 size_t srclen = 0;
255 data = test_codec_helper_read_data("interleaved", encname, name, &srclen);
256 if (!data)
257 goto fail;
258
259 const size_t declen = 1ULL * step * height;
260 decdata = calloc(step, height);
261 if (!decdata)
262 goto fail;
263
264 for (size_t x = 0; x < 42; x++)
265 {
266 if (!interleaved_decompress(decoder, data, WINPR_ASSERTING_INT_CAST(uint32_t, srclen),
267 width, height, bpp, decdata, format, step, 0, 0, width, height,
268 nullptr))
269 goto fail;
270
271 char decname[128] = WINPR_C_ARRAY_INIT;
272 (void)_snprintf(decname, sizeof(decname), "dec-%s", encname);
273#if defined(CREATE_TEST_OUTPUT)
274 test_codec_helper_write_data("interleaved", decname, name, decdata, declen);
275#else
276 if (!test_codec_helper_compare("interleaved", decname, name, decdata, declen))
277 goto fail;
278#endif
279 }
280
281 rc = true;
282
283fail:
284 free(data);
285 free(decdata);
286 bitmap_interleaved_context_free(decoder);
287 return rc;
288}
289
290/* The encoder expects a JSON that describes a test cast:
291 *
292 * [
293 * {
294 * "name": "somestring",
295 * "format": "somestring",
296 * "width": <someint>,
297 * "height": <someint>,
298 * "step": <someint>,
299 * "bpp": <someint>
300 * },
301 * {...},
302 * ...
303 * ]
304 */
305static bool isObjectValid(const WINPR_JSON* obj)
306{
307 if (!obj || !WINPR_JSON_IsObject(obj))
308 return false;
309
310 const char* strvalues[] = { "name", "format" };
311 for (size_t x = 0; x < ARRAYSIZE(strvalues); x++)
312 {
313 const char* val = strvalues[x];
314
315 if (!WINPR_JSON_HasObjectItem(obj, val))
316 return false;
317
318 WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
319 if (!jval)
320 return false;
321
322 if (!WINPR_JSON_IsString(jval))
323 return false;
324 }
325
326 const char* values[] = { "width", "height", "step" };
327 for (size_t x = 0; x < ARRAYSIZE(values); x++)
328 {
329 const char* val = values[x];
330 if (!WINPR_JSON_HasObjectItem(obj, val))
331 return false;
332 WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
333 if (!jval)
334 return false;
335 if (!WINPR_JSON_IsNumber(jval))
336 return false;
337 const double dval = WINPR_JSON_GetNumberValue(jval);
338 if (dval <= 0.0)
339 return false;
340 }
341
342 {
343 const char* val = "bpp";
344
345 if (!WINPR_JSON_HasObjectItem(obj, val))
346 return false;
347
348 WINPR_JSON* jval = WINPR_JSON_GetObjectItem(obj, val);
349 if (!jval)
350 return false;
351
352 if (!WINPR_JSON_IsArray(jval))
353 return false;
354
355 for (size_t x = 0; x < WINPR_JSON_GetArraySize(jval); x++)
356 {
357 WINPR_JSON* aval = WINPR_JSON_GetArrayItem(jval, x);
358 if (!jval || !WINPR_JSON_IsNumber(aval))
359 return false;
360 }
361 }
362 return true;
363}
364
365static bool TestEncoder(void)
366{
367 bool rc = false;
368 WINPR_JSON* json = nullptr;
369 char* file = nullptr;
370 char* path = GetCombinedPath(CMAKE_CURRENT_SOURCE_DIR, "interleaved");
371 if (!path)
372 goto fail;
373 file = GetCombinedPath(path, "encoder.json");
374 if (!file)
375 goto fail;
376
377 json = WINPR_JSON_ParseFromFile(file);
378 if (!json)
379 goto fail;
380
381 if (!WINPR_JSON_IsArray(json))
382 goto fail;
383
384 for (size_t x = 0; x < WINPR_JSON_GetArraySize(json); x++)
385 {
386 WINPR_JSON* obj = WINPR_JSON_GetArrayItem(json, x);
387 if (!isObjectValid(obj))
388 goto fail;
389
390 const char* name = WINPR_JSON_GetStringValue(WINPR_JSON_GetObjectItem(obj, "name"));
391 const uint32_t format = WINPR_ASSERTING_INT_CAST(
392 uint32_t, FreeRDPGetColorFromatFromName(
394 const uint32_t width = WINPR_ASSERTING_INT_CAST(
395 uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "width")));
396 const uint32_t height = WINPR_ASSERTING_INT_CAST(
397 uint32_t, WINPR_JSON_GetNumberValue(WINPR_JSON_GetObjectItem(obj, "height")));
398 const uint32_t step = WINPR_ASSERTING_INT_CAST(
400
401 WINPR_JSON* jbpp = WINPR_JSON_GetObjectItem(obj, "bpp");
402 for (size_t x = 0; x < WINPR_JSON_GetArraySize(jbpp); x++)
403 {
404 const uint32_t bpp = WINPR_ASSERTING_INT_CAST(
406 if (!RunEncoderTest(name, format, width, height, step, bpp))
407 goto fail;
408 if (!RunDecoderTest(name, format, width, height, step, bpp))
409 goto fail;
410 }
411 }
412
413 rc = true;
414fail:
415 WINPR_JSON_Delete(json);
416 free(path);
417 free(file);
418 return rc;
419}
420
421int TestFreeRDPCodecInterleaved(int argc, char* argv[])
422{
423 BITMAP_INTERLEAVED_CONTEXT* encoder = nullptr;
424 BITMAP_INTERLEAVED_CONTEXT* decoder = nullptr;
425 int rc = -1;
426 WINPR_UNUSED(argc);
427 WINPR_UNUSED(argv);
428 encoder = bitmap_interleaved_context_new(true);
429 decoder = bitmap_interleaved_context_new(false);
430
431 if (!encoder || !decoder)
432 goto fail;
433
434 if (!run_encode_decode(24, encoder, decoder))
435 goto fail;
436
437 if (!run_encode_decode(16, encoder, decoder))
438 goto fail;
439
440 if (!run_encode_decode(15, encoder, decoder))
441 goto fail;
442
443 if (!TestColorConversion())
444 goto fail;
445
446 if (!TestEncoder())
447 goto fail;
448
449 rc = 0;
450fail:
451 bitmap_interleaved_context_free(encoder);
452 bitmap_interleaved_context_free(decoder);
453 return rc;
454}
WINPR_API BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON *object, const char *string)
Check if JSON has an object matching the name.
Definition c-json.c:132
WINPR_API WINPR_JSON * WINPR_JSON_GetObjectItem(const WINPR_JSON *object, const char *string)
Return a pointer to an JSON object item.
Definition c-json.c:122
WINPR_API BOOL WINPR_JSON_IsString(const WINPR_JSON *item)
Check if JSON item is of type String.
Definition c-json.c:182
WINPR_API double WINPR_JSON_GetNumberValue(const WINPR_JSON *item)
Return the Number value of a JSON item.
Definition c-json.c:147
WINPR_ATTR_NODISCARD WINPR_API WINPR_JSON * WINPR_JSON_ParseFromFile(const char *filename)
Parse a JSON string read from a file filename.
Definition json.c:27
WINPR_API BOOL WINPR_JSON_IsNumber(const WINPR_JSON *item)
Check if JSON item is of type Number.
Definition c-json.c:177
WINPR_API WINPR_JSON * WINPR_JSON_GetArrayItem(const WINPR_JSON *array, size_t index)
Return a pointer to an item in the array.
Definition c-json.c:108
WINPR_API BOOL WINPR_JSON_IsObject(const WINPR_JSON *item)
Check if JSON item is of type Object.
Definition c-json.c:192
WINPR_API const char * WINPR_JSON_GetStringValue(WINPR_JSON *item)
Return the String value of a JSON item.
Definition c-json.c:142
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
Definition c-json.c:103
WINPR_API size_t WINPR_JSON_GetArraySize(const WINPR_JSON *array)
Get the number of arrayitems from an array.
Definition c-json.c:114
WINPR_API BOOL WINPR_JSON_IsArray(const WINPR_JSON *item)
Check if JSON item is of type Array.
Definition c-json.c:187