FreeRDP
TestFreeRDPCodecProgressive.c
1 #include <errno.h>
2 
3 #include <winpr/wtypes.h>
4 #include <winpr/crt.h>
5 #include <winpr/string.h>
6 #include <winpr/path.h>
7 #include <winpr/image.h>
8 #include <winpr/print.h>
9 #include <winpr/wlog.h>
10 #include <winpr/sysinfo.h>
11 #include <winpr/file.h>
12 
13 #include <freerdp/codec/region.h>
14 
15 #include <freerdp/codecs.h>
16 #include <freerdp/utils/gfx.h>
17 
18 #include <freerdp/codec/progressive.h>
19 #include <freerdp/channels/rdpgfx.h>
20 #include <freerdp/crypto/crypto.h>
21 
22 #include "../progressive.h"
23 
138 typedef struct
139 {
140  BYTE* buffer;
141  size_t size;
142 } EGFX_SAMPLE_FILE;
143 
144 static int g_Width = 0;
145 static int g_Height = 0;
146 static int g_DstStep = 0;
147 static BYTE* g_DstData = NULL;
148 
149 static void sample_file_free(EGFX_SAMPLE_FILE* file)
150 {
151  if (!file)
152  return;
153 
154  free(file->buffer);
155  file->buffer = NULL;
156  file->size = 0;
157 }
158 
159 static void test_fill_image_alpha_channel(BYTE* data, int width, int height, BYTE value)
160 {
161  UINT32* pixel = NULL;
162 
163  for (int i = 0; i < height; i++)
164  {
165  for (int j = 0; j < width; j++)
166  {
167  pixel = (UINT32*)&data[((1ULL * i * width) + j) * 4ULL];
168  *pixel = ((*pixel & 0x00FFFFFF) | (value << 24));
169  }
170  }
171 }
172 
173 static void* test_image_memset32(UINT32* ptr, UINT32 fill, size_t length)
174 {
175  while (length--)
176  {
177  *ptr++ = fill;
178  }
179 
180  return (void*)ptr;
181 }
182 
183 static int test_image_fill(BYTE* pDstData, int nDstStep, int nXDst, int nYDst, int nWidth,
184  int nHeight, UINT32 color)
185 {
186  UINT32* pDstPixel = NULL;
187 
188  if (nDstStep < 0)
189  nDstStep = 4 * nWidth;
190 
191  for (int y = 0; y < nHeight; y++)
192  {
193  pDstPixel = (UINT32*)&pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
194  test_image_memset32(pDstPixel, color, nWidth);
195  }
196 
197  return 1;
198 }
199 
200 static int test_image_fill_quarter(BYTE* pDstData, int nDstStep, int nWidth, int nHeight,
201  UINT32 color, int quarter)
202 {
203  int x = 0;
204  int y = 0;
205  int width = 0;
206  int height = 0;
207 
208  switch (quarter)
209  {
210  case 0:
211  x = 0;
212  y = 0;
213  width = nWidth / 2;
214  height = nHeight / 2;
215  break;
216 
217  case 1:
218  x = nWidth / 2;
219  y = nHeight / 2;
220  width = nWidth / 2;
221  height = nHeight / 2;
222  break;
223 
224  case 2:
225  x = 0;
226  y = nHeight / 2;
227  width = nWidth / 2;
228  height = nHeight / 2;
229  break;
230 
231  case 3:
232  x = nWidth / 2;
233  y = 0;
234  width = nWidth / 2;
235  height = nHeight / 2;
236  break;
237  default:
238  return -1;
239  }
240 
241  test_image_fill(pDstData, nDstStep, x, y, width, height, 0xFF000000);
242  return 1;
243 }
244 
245 static int test_image_fill_unused_quarters(BYTE* pDstData, int nDstStep, int nWidth, int nHeight,
246  UINT32 color, int quarter)
247 {
248  return 1;
249 
250  if (quarter == 0)
251  {
252  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
253  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
254  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
255  }
256  else if (quarter == 1)
257  {
258  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
259  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
260  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
261  }
262  else if (quarter == 2)
263  {
264  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
265  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
266  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 3);
267  }
268  else if (quarter == 3)
269  {
270  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 0);
271  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 1);
272  test_image_fill_quarter(pDstData, nDstStep, nWidth, nHeight, color, 2);
273  }
274 
275  return 1;
276 }
277 
278 static BYTE* test_progressive_load_file(const char* path, const char* file, size_t* size)
279 {
280  char* filename = GetCombinedPath(path, file);
281 
282  if (!filename)
283  return NULL;
284 
285  FILE* fp = winpr_fopen(filename, "r");
286  free(filename);
287 
288  if (!fp)
289  return NULL;
290 
291  (void)_fseeki64(fp, 0, SEEK_END);
292  const INT64 pos = _ftelli64(fp);
293  WINPR_ASSERT(pos >= 0);
294  WINPR_ASSERT(pos <= SIZE_MAX);
295  *size = (size_t)pos;
296  (void)_fseeki64(fp, 0, SEEK_SET);
297  BYTE* buffer = (BYTE*)malloc(*size);
298 
299  if (!buffer)
300  {
301  (void)fclose(fp);
302  return NULL;
303  }
304 
305  if (fread(buffer, *size, 1, fp) != 1)
306  {
307  free(buffer);
308  (void)fclose(fp);
309  return NULL;
310  }
311 
312  (void)fclose(fp);
313  return buffer;
314 }
315 
316 static int test_progressive_load_files(char* ms_sample_path, EGFX_SAMPLE_FILE files[3][4][4])
317 {
318  int imageNo = 0;
319  int quarterNo = 0;
320  int passNo = 0;
321  /* image 1 */
322  files[imageNo][quarterNo][passNo].buffer =
323  test_progressive_load_file(ms_sample_path, "compress/enc_0_0_025_sampleimage1.bin",
324  &(files[imageNo][quarterNo][passNo].size));
325  passNo = (passNo + 1) % 4;
326  files[imageNo][quarterNo][passNo].buffer =
327  test_progressive_load_file(ms_sample_path, "compress/enc_0_0_050_sampleimage1.bin",
328  &(files[imageNo][quarterNo][passNo].size));
329  passNo = (passNo + 1) % 4;
330  files[imageNo][quarterNo][passNo].buffer =
331  test_progressive_load_file(ms_sample_path, "compress/enc_0_0_075_sampleimage1.bin",
332  &(files[imageNo][quarterNo][passNo].size));
333  passNo = (passNo + 1) % 4;
334  files[imageNo][quarterNo][passNo].buffer =
335  test_progressive_load_file(ms_sample_path, "compress/enc_0_0_100_sampleimage1.bin",
336  &(files[imageNo][quarterNo][passNo].size));
337  passNo = (passNo + 1) % 4;
338  quarterNo = (quarterNo + 1) % 4;
339  files[imageNo][quarterNo][passNo].buffer =
340  test_progressive_load_file(ms_sample_path, "compress/enc_0_1_025_sampleimage1.bin",
341  &(files[imageNo][quarterNo][passNo].size));
342  passNo = (passNo + 1) % 4;
343  files[imageNo][quarterNo][passNo].buffer =
344  test_progressive_load_file(ms_sample_path, "compress/enc_0_1_050_sampleimage1.bin",
345  &(files[imageNo][quarterNo][passNo].size));
346  passNo = (passNo + 1) % 4;
347  files[imageNo][quarterNo][passNo].buffer =
348  test_progressive_load_file(ms_sample_path, "compress/enc_0_1_075_sampleimage1.bin",
349  &(files[imageNo][quarterNo][passNo].size));
350  passNo = (passNo + 1) % 4;
351  files[imageNo][quarterNo][passNo].buffer =
352  test_progressive_load_file(ms_sample_path, "compress/enc_0_1_100_sampleimage1.bin",
353  &(files[imageNo][quarterNo][passNo].size));
354  passNo = (passNo + 1) % 4;
355  quarterNo = (quarterNo + 1) % 4;
356  files[imageNo][quarterNo][passNo].buffer =
357  test_progressive_load_file(ms_sample_path, "compress/enc_0_2_025_sampleimage1.bin",
358  &(files[imageNo][quarterNo][passNo].size));
359  passNo = (passNo + 1) % 4;
360  files[imageNo][quarterNo][passNo].buffer =
361  test_progressive_load_file(ms_sample_path, "compress/enc_0_2_050_sampleimage1.bin",
362  &(files[imageNo][quarterNo][passNo].size));
363  passNo = (passNo + 1) % 4;
364  files[imageNo][quarterNo][passNo].buffer =
365  test_progressive_load_file(ms_sample_path, "compress/enc_0_2_075_sampleimage1.bin",
366  &(files[imageNo][quarterNo][passNo].size));
367  passNo = (passNo + 1) % 4;
368  files[imageNo][quarterNo][passNo].buffer =
369  test_progressive_load_file(ms_sample_path, "compress/enc_0_2_100_sampleimage1.bin",
370  &(files[imageNo][quarterNo][passNo].size));
371  passNo = (passNo + 1) % 4;
372  quarterNo = (quarterNo + 1) % 4;
373  files[imageNo][quarterNo][passNo].buffer =
374  test_progressive_load_file(ms_sample_path, "compress/enc_0_3_025_sampleimage1.bin",
375  &(files[imageNo][quarterNo][passNo].size));
376  passNo = (passNo + 1) % 4;
377  files[imageNo][quarterNo][passNo].buffer =
378  test_progressive_load_file(ms_sample_path, "compress/enc_0_3_050_sampleimage1.bin",
379  &(files[imageNo][quarterNo][passNo].size));
380  passNo = (passNo + 1) % 4;
381  files[imageNo][quarterNo][passNo].buffer =
382  test_progressive_load_file(ms_sample_path, "compress/enc_0_3_075_sampleimage1.bin",
383  &(files[imageNo][quarterNo][passNo].size));
384  passNo = (passNo + 1) % 4;
385  files[imageNo][quarterNo][passNo].buffer =
386  test_progressive_load_file(ms_sample_path, "compress/enc_0_3_100_sampleimage1.bin",
387  &(files[imageNo][quarterNo][passNo].size));
388  passNo = (passNo + 1) % 4;
389  imageNo++;
390  /* image 2 */
391  files[imageNo][quarterNo][passNo].buffer =
392  test_progressive_load_file(ms_sample_path, "compress/enc_1_0_025_sampleimage2.bin",
393  &(files[imageNo][quarterNo][passNo].size));
394  passNo = (passNo + 1) % 4;
395  files[imageNo][quarterNo][passNo].buffer =
396  test_progressive_load_file(ms_sample_path, "compress/enc_1_0_050_sampleimage2.bin",
397  &(files[imageNo][quarterNo][passNo].size));
398  passNo = (passNo + 1) % 4;
399  files[imageNo][quarterNo][passNo].buffer =
400  test_progressive_load_file(ms_sample_path, "compress/enc_1_0_075_sampleimage2.bin",
401  &(files[imageNo][quarterNo][passNo].size));
402  passNo = (passNo + 1) % 4;
403  files[imageNo][quarterNo][passNo].buffer =
404  test_progressive_load_file(ms_sample_path, "compress/enc_1_0_100_sampleimage2.bin",
405  &(files[imageNo][quarterNo][passNo].size));
406  passNo = (passNo + 1) % 4;
407  quarterNo = (quarterNo + 1) % 4;
408  files[imageNo][quarterNo][passNo].buffer =
409  test_progressive_load_file(ms_sample_path, "compress/enc_1_1_025_sampleimage2.bin",
410  &(files[imageNo][quarterNo][passNo].size));
411  passNo = (passNo + 1) % 4;
412  files[imageNo][quarterNo][passNo].buffer =
413  test_progressive_load_file(ms_sample_path, "compress/enc_1_1_050_sampleimage2.bin",
414  &(files[imageNo][quarterNo][passNo].size));
415  passNo = (passNo + 1) % 4;
416  files[imageNo][quarterNo][passNo].buffer =
417  test_progressive_load_file(ms_sample_path, "compress/enc_1_1_075_sampleimage2.bin",
418  &(files[imageNo][quarterNo][passNo].size));
419  passNo = (passNo + 1) % 4;
420  files[imageNo][quarterNo][passNo].buffer =
421  test_progressive_load_file(ms_sample_path, "compress/enc_1_1_100_sampleimage2.bin",
422  &(files[imageNo][quarterNo][passNo].size));
423  passNo = (passNo + 1) % 4;
424  quarterNo = (quarterNo + 1) % 4;
425  files[imageNo][quarterNo][passNo].buffer =
426  test_progressive_load_file(ms_sample_path, "compress/enc_1_2_025_sampleimage2.bin",
427  &(files[imageNo][quarterNo][passNo].size));
428  passNo = (passNo + 1) % 4;
429  files[imageNo][quarterNo][passNo].buffer =
430  test_progressive_load_file(ms_sample_path, "compress/enc_1_2_050_sampleimage2.bin",
431  &(files[imageNo][quarterNo][passNo].size));
432  passNo = (passNo + 1) % 4;
433  files[imageNo][quarterNo][passNo].buffer =
434  test_progressive_load_file(ms_sample_path, "compress/enc_1_2_075_sampleimage2.bin",
435  &(files[imageNo][quarterNo][passNo].size));
436  passNo = (passNo + 1) % 4;
437  files[imageNo][quarterNo][passNo].buffer =
438  test_progressive_load_file(ms_sample_path, "compress/enc_1_2_100_sampleimage2.bin",
439  &(files[imageNo][quarterNo][passNo].size));
440  passNo = (passNo + 1) % 4;
441  quarterNo = (quarterNo + 1) % 4;
442  files[imageNo][quarterNo][passNo].buffer =
443  test_progressive_load_file(ms_sample_path, "compress/enc_1_3_025_sampleimage2.bin",
444  &(files[imageNo][quarterNo][passNo].size));
445  passNo = (passNo + 1) % 4;
446  files[imageNo][quarterNo][passNo].buffer =
447  test_progressive_load_file(ms_sample_path, "compress/enc_1_3_050_sampleimage2.bin",
448  &(files[imageNo][quarterNo][passNo].size));
449  passNo = (passNo + 1) % 4;
450  files[imageNo][quarterNo][passNo].buffer =
451  test_progressive_load_file(ms_sample_path, "compress/enc_1_3_075_sampleimage2.bin",
452  &(files[imageNo][quarterNo][passNo].size));
453  passNo = (passNo + 1) % 4;
454  files[imageNo][quarterNo][passNo].buffer =
455  test_progressive_load_file(ms_sample_path, "compress/enc_1_3_100_sampleimage2.bin",
456  &(files[imageNo][quarterNo][passNo].size));
457  passNo = (passNo + 1) % 4;
458  imageNo++;
459  /* image 3 */
460  files[imageNo][quarterNo][passNo].buffer =
461  test_progressive_load_file(ms_sample_path, "compress/enc_2_0_025_sampleimage3.bin",
462  &(files[imageNo][quarterNo][passNo].size));
463  passNo = (passNo + 1) % 4;
464  files[imageNo][quarterNo][passNo].buffer =
465  test_progressive_load_file(ms_sample_path, "compress/enc_2_0_050_sampleimage3.bin",
466  &(files[imageNo][quarterNo][passNo].size));
467  passNo = (passNo + 1) % 4;
468  files[imageNo][quarterNo][passNo].buffer =
469  test_progressive_load_file(ms_sample_path, "compress/enc_2_0_075_sampleimage3.bin",
470  &(files[imageNo][quarterNo][passNo].size));
471  passNo = (passNo + 1) % 4;
472  files[imageNo][quarterNo][passNo].buffer =
473  test_progressive_load_file(ms_sample_path, "compress/enc_2_0_100_sampleimage3.bin",
474  &(files[imageNo][quarterNo][passNo].size));
475  passNo = (passNo + 1) % 4;
476  quarterNo = (quarterNo + 1) % 4;
477  files[imageNo][quarterNo][passNo].buffer =
478  test_progressive_load_file(ms_sample_path, "compress/enc_2_1_025_sampleimage3.bin",
479  &(files[imageNo][quarterNo][passNo].size));
480  passNo = (passNo + 1) % 4;
481  files[imageNo][quarterNo][passNo].buffer =
482  test_progressive_load_file(ms_sample_path, "compress/enc_2_1_050_sampleimage3.bin",
483  &(files[imageNo][quarterNo][passNo].size));
484  passNo = (passNo + 1) % 4;
485  files[imageNo][quarterNo][passNo].buffer =
486  test_progressive_load_file(ms_sample_path, "compress/enc_2_1_075_sampleimage3.bin",
487  &(files[imageNo][quarterNo][passNo].size));
488  passNo = (passNo + 1) % 4;
489  files[imageNo][quarterNo][passNo].buffer =
490  test_progressive_load_file(ms_sample_path, "compress/enc_2_1_100_sampleimage3.bin",
491  &(files[imageNo][quarterNo][passNo].size));
492  passNo = (passNo + 1) % 4;
493  quarterNo = (quarterNo + 1) % 4;
494  files[imageNo][quarterNo][passNo].buffer =
495  test_progressive_load_file(ms_sample_path, "compress/enc_2_2_025_sampleimage3.bin",
496  &(files[imageNo][quarterNo][passNo].size));
497  passNo = (passNo + 1) % 4;
498  files[imageNo][quarterNo][passNo].buffer =
499  test_progressive_load_file(ms_sample_path, "compress/enc_2_2_050_sampleimage3.bin",
500  &(files[imageNo][quarterNo][passNo].size));
501  passNo = (passNo + 1) % 4;
502  files[imageNo][quarterNo][passNo].buffer =
503  test_progressive_load_file(ms_sample_path, "compress/enc_2_2_075_sampleimage3.bin",
504  &(files[imageNo][quarterNo][passNo].size));
505  passNo = (passNo + 1) % 4;
506  files[imageNo][quarterNo][passNo].buffer =
507  test_progressive_load_file(ms_sample_path, "compress/enc_2_2_100_sampleimage3.bin",
508  &(files[imageNo][quarterNo][passNo].size));
509  passNo = (passNo + 1) % 4;
510  quarterNo = (quarterNo + 1) % 4;
511  files[imageNo][quarterNo][passNo].buffer =
512  test_progressive_load_file(ms_sample_path, "compress/enc_2_3_025_sampleimage3.bin",
513  &(files[imageNo][quarterNo][passNo].size));
514  passNo = (passNo + 1) % 4;
515  files[imageNo][quarterNo][passNo].buffer =
516  test_progressive_load_file(ms_sample_path, "compress/enc_2_3_050_sampleimage3.bin",
517  &(files[imageNo][quarterNo][passNo].size));
518  passNo = (passNo + 1) % 4;
519  files[imageNo][quarterNo][passNo].buffer =
520  test_progressive_load_file(ms_sample_path, "compress/enc_2_3_075_sampleimage3.bin",
521  &(files[imageNo][quarterNo][passNo].size));
522  passNo = (passNo + 1) % 4;
523  files[imageNo][quarterNo][passNo].buffer =
524  test_progressive_load_file(ms_sample_path, "compress/enc_2_3_100_sampleimage3.bin",
525  &(files[imageNo][quarterNo][passNo].size));
526 
527  /* check if all test data has been loaded */
528 
529  for (imageNo = 0; imageNo < 3; imageNo++)
530  {
531  for (quarterNo = 0; quarterNo < 4; quarterNo++)
532  {
533  for (passNo = 0; passNo < 4; passNo++)
534  {
535  if (!files[imageNo][quarterNo][passNo].buffer)
536  return -1;
537  }
538  }
539  }
540 
541  return 1;
542 }
543 
544 static BYTE* test_progressive_load_bitmap(char* path, char* file, size_t* size, int quarter)
545 {
546  int status = 0;
547  BYTE* buffer = NULL;
548  wImage* image = NULL;
549  char* filename = NULL;
550  filename = GetCombinedPath(path, file);
551 
552  if (!filename)
553  return NULL;
554 
555  image = winpr_image_new();
556 
557  if (!image)
558  return NULL;
559 
560  status = winpr_image_read(image, filename);
561 
562  if (status < 0)
563  return NULL;
564 
565  buffer = image->data;
566  *size = 1ULL * image->height * image->scanline;
567  test_fill_image_alpha_channel(image->data, image->width, image->height, 0xFF);
568  test_image_fill_unused_quarters(image->data, image->scanline, image->width, image->height,
569  quarter, 0xFF000000);
570  winpr_image_free(image, FALSE);
571  free(filename);
572  return buffer;
573 }
574 
575 static int test_progressive_load_bitmaps(char* ms_sample_path, EGFX_SAMPLE_FILE bitmaps[3][4][4])
576 {
577  int imageNo = 0;
578  int quarterNo = 0;
579  int passNo = 0;
580  /* image 1 */
581  bitmaps[imageNo][quarterNo][passNo].buffer =
582  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_025_sampleimage1.bmp",
583  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
584  passNo = (passNo + 1) % 4;
585  bitmaps[imageNo][quarterNo][passNo].buffer =
586  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_050_sampleimage1.bmp",
587  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
588  passNo = (passNo + 1) % 4;
589  bitmaps[imageNo][quarterNo][passNo].buffer =
590  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_075_sampleimage1.bmp",
591  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
592  passNo = (passNo + 1) % 4;
593  bitmaps[imageNo][quarterNo][passNo].buffer =
594  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_0_100_sampleimage1.bmp",
595  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
596  passNo = (passNo + 1) % 4;
597  quarterNo = (quarterNo + 1) % 4;
598  bitmaps[imageNo][quarterNo][passNo].buffer =
599  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_025_sampleimage1.bmp",
600  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
601  passNo = (passNo + 1) % 4;
602  bitmaps[imageNo][quarterNo][passNo].buffer =
603  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_050_sampleimage1.bmp",
604  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
605  passNo = (passNo + 1) % 4;
606  bitmaps[imageNo][quarterNo][passNo].buffer =
607  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_075_sampleimage1.bmp",
608  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
609  passNo = (passNo + 1) % 4;
610  bitmaps[imageNo][quarterNo][passNo].buffer =
611  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_1_100_sampleimage1.bmp",
612  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
613  passNo = (passNo + 1) % 4;
614  quarterNo = (quarterNo + 1) % 4;
615  bitmaps[imageNo][quarterNo][passNo].buffer =
616  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_025_sampleimage1.bmp",
617  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
618  passNo = (passNo + 1) % 4;
619  bitmaps[imageNo][quarterNo][passNo].buffer =
620  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_050_sampleimage1.bmp",
621  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
622  passNo = (passNo + 1) % 4;
623  bitmaps[imageNo][quarterNo][passNo].buffer =
624  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_075_sampleimage1.bmp",
625  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
626  passNo = (passNo + 1) % 4;
627  bitmaps[imageNo][quarterNo][passNo].buffer =
628  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_2_100_sampleimage1.bmp",
629  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
630  passNo = (passNo + 1) % 4;
631  quarterNo = (quarterNo + 1) % 4;
632  bitmaps[imageNo][quarterNo][passNo].buffer =
633  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_025_sampleimage1.bmp",
634  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
635  passNo = (passNo + 1) % 4;
636  bitmaps[imageNo][quarterNo][passNo].buffer =
637  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_050_sampleimage1.bmp",
638  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
639  passNo = (passNo + 1) % 4;
640  bitmaps[imageNo][quarterNo][passNo].buffer =
641  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_075_sampleimage1.bmp",
642  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
643  passNo = (passNo + 1) % 4;
644  bitmaps[imageNo][quarterNo][passNo].buffer =
645  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_0_3_100_sampleimage1.bmp",
646  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
647  passNo = (passNo + 1) % 4;
648  imageNo++;
649  /* image 2 */
650  bitmaps[imageNo][quarterNo][passNo].buffer =
651  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_025_sampleimage2.bmp",
652  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
653  passNo = (passNo + 1) % 4;
654  bitmaps[imageNo][quarterNo][passNo].buffer =
655  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_050_sampleimage2.bmp",
656  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
657  passNo = (passNo + 1) % 4;
658  bitmaps[imageNo][quarterNo][passNo].buffer =
659  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_075_sampleimage2.bmp",
660  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
661  passNo = (passNo + 1) % 4;
662  bitmaps[imageNo][quarterNo][passNo].buffer =
663  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_0_100_sampleimage2.bmp",
664  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
665  passNo = (passNo + 1) % 4;
666  quarterNo = (quarterNo + 1) % 4;
667  bitmaps[imageNo][quarterNo][passNo].buffer =
668  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_025_sampleimage2.bmp",
669  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
670  passNo = (passNo + 1) % 4;
671  bitmaps[imageNo][quarterNo][passNo].buffer =
672  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_050_sampleimage2.bmp",
673  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
674  passNo = (passNo + 1) % 4;
675  bitmaps[imageNo][quarterNo][passNo].buffer =
676  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_075_sampleimage2.bmp",
677  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
678  passNo = (passNo + 1) % 4;
679  bitmaps[imageNo][quarterNo][passNo].buffer =
680  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_1_100_sampleimage2.bmp",
681  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
682  passNo = (passNo + 1) % 4;
683  quarterNo = (quarterNo + 1) % 4;
684  bitmaps[imageNo][quarterNo][passNo].buffer =
685  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_025_sampleimage2.bmp",
686  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
687  passNo = (passNo + 1) % 4;
688  bitmaps[imageNo][quarterNo][passNo].buffer =
689  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_050_sampleimage2.bmp",
690  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
691  passNo = (passNo + 1) % 4;
692  bitmaps[imageNo][quarterNo][passNo].buffer =
693  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_075_sampleimage2.bmp",
694  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
695  passNo = (passNo + 1) % 4;
696  bitmaps[imageNo][quarterNo][passNo].buffer =
697  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_2_100_sampleimage2.bmp",
698  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
699  passNo = (passNo + 1) % 4;
700  quarterNo = (quarterNo + 1) % 4;
701  bitmaps[imageNo][quarterNo][passNo].buffer =
702  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_025_sampleimage2.bmp",
703  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
704  passNo = (passNo + 1) % 4;
705  bitmaps[imageNo][quarterNo][passNo].buffer =
706  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_050_sampleimage2.bmp",
707  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
708  passNo = (passNo + 1) % 4;
709  bitmaps[imageNo][quarterNo][passNo].buffer =
710  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_075_sampleimage2.bmp",
711  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
712  passNo = (passNo + 1) % 4;
713  bitmaps[imageNo][quarterNo][passNo].buffer =
714  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_1_3_100_sampleimage2.bmp",
715  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
716  passNo = (passNo + 1) % 4;
717  imageNo++;
718  /* image 3 */
719  bitmaps[imageNo][quarterNo][passNo].buffer =
720  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_025_sampleimage3.bmp",
721  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
722  passNo = (passNo + 1) % 4;
723  bitmaps[imageNo][quarterNo][passNo].buffer =
724  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_050_sampleimage3.bmp",
725  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
726  passNo = (passNo + 1) % 4;
727  bitmaps[imageNo][quarterNo][passNo].buffer =
728  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_075_sampleimage3.bmp",
729  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
730  passNo = (passNo + 1) % 4;
731  bitmaps[imageNo][quarterNo][passNo].buffer =
732  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_0_100_sampleimage3.bmp",
733  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
734  passNo = (passNo + 1) % 4;
735  quarterNo = (quarterNo + 1) % 4;
736  bitmaps[imageNo][quarterNo][passNo].buffer =
737  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_025_sampleimage3.bmp",
738  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
739  passNo = (passNo + 1) % 4;
740  bitmaps[imageNo][quarterNo][passNo].buffer =
741  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_050_sampleimage3.bmp",
742  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
743  passNo = (passNo + 1) % 4;
744  bitmaps[imageNo][quarterNo][passNo].buffer =
745  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_075_sampleimage3.bmp",
746  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
747  passNo = (passNo + 1) % 4;
748  bitmaps[imageNo][quarterNo][passNo].buffer =
749  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_1_100_sampleimage3.bmp",
750  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
751  passNo = (passNo + 1) % 4;
752  quarterNo = (quarterNo + 1) % 4;
753  bitmaps[imageNo][quarterNo][passNo].buffer =
754  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_025_sampleimage3.bmp",
755  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
756  passNo = (passNo + 1) % 4;
757  bitmaps[imageNo][quarterNo][passNo].buffer =
758  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_050_sampleimage3.bmp",
759  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
760  passNo = (passNo + 1) % 4;
761  bitmaps[imageNo][quarterNo][passNo].buffer =
762  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_075_sampleimage3.bmp",
763  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
764  passNo = (passNo + 1) % 4;
765  bitmaps[imageNo][quarterNo][passNo].buffer =
766  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_2_100_sampleimage3.bmp",
767  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
768  passNo = (passNo + 1) % 4;
769  quarterNo = (quarterNo + 1) % 4;
770  bitmaps[imageNo][quarterNo][passNo].buffer =
771  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_025_sampleimage3.bmp",
772  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
773  passNo = (passNo + 1) % 4;
774  bitmaps[imageNo][quarterNo][passNo].buffer =
775  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_050_sampleimage3.bmp",
776  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
777  passNo = (passNo + 1) % 4;
778  bitmaps[imageNo][quarterNo][passNo].buffer =
779  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_075_sampleimage3.bmp",
780  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
781  passNo = (passNo + 1) % 4;
782  bitmaps[imageNo][quarterNo][passNo].buffer =
783  test_progressive_load_bitmap(ms_sample_path, "decompress/dec_2_3_100_sampleimage3.bmp",
784  &(bitmaps[imageNo][quarterNo][passNo].size), quarterNo);
785 
786  /* check if all test data has been loaded */
787 
788  for (imageNo = 0; imageNo < 3; imageNo++)
789  {
790  for (quarterNo = 0; quarterNo < 4; quarterNo++)
791  {
792  for (passNo = 0; passNo < 4; passNo++)
793  {
794  if (!bitmaps[imageNo][quarterNo][passNo].buffer)
795  return -1;
796  }
797  }
798  }
799 
800  return 1;
801 }
802 
803 static size_t test_memcmp_count(const BYTE* mem1, const BYTE* mem2, size_t size, int margin)
804 {
805  size_t count = 0;
806 
807  for (size_t index = 0; index < size; index++)
808  {
809  if (*mem1 != *mem2)
810  {
811  const int error = (*mem1 > *mem2) ? *mem1 - *mem2 : *mem2 - *mem1;
812 
813  if (error > margin)
814  count++;
815  }
816 
817  mem1++;
818  mem2++;
819  }
820 
821  return count;
822 }
823 
824 static int test_progressive_decode(PROGRESSIVE_CONTEXT* progressive, EGFX_SAMPLE_FILE files[4],
825  EGFX_SAMPLE_FILE bitmaps[4], int quarter, int count)
826 {
827  int nXSrc = 0;
828  int nYSrc = 0;
829 
830  RECTANGLE_16 clippingRect = { 0 };
831  clippingRect.right = g_Width;
832  clippingRect.bottom = g_Height;
833 
834  for (int pass = 0; pass < count; pass++)
835  {
836  const int status =
837  progressive_decompress(progressive, files[pass].buffer, files[pass].size, g_DstData,
838  PIXEL_FORMAT_XRGB32, g_DstStep, 0, 0, NULL, 0, 0);
839  printf("ProgressiveDecompress: status: %d pass: %d\n", status, pass + 1);
840  PROGRESSIVE_BLOCK_REGION* region = &(progressive->region);
841 
842  switch (quarter)
843  {
844  case 0:
845  clippingRect.left = 0;
846  clippingRect.top = 0;
847  clippingRect.right = g_Width / 2;
848  clippingRect.bottom = g_Height / 2;
849  break;
850 
851  case 1:
852  clippingRect.left = g_Width / 2;
853  clippingRect.top = g_Height / 2;
854  clippingRect.right = g_Width;
855  clippingRect.bottom = g_Height;
856  break;
857 
858  case 2:
859  clippingRect.left = 0;
860  clippingRect.top = g_Height / 2;
861  clippingRect.right = g_Width / 2;
862  clippingRect.bottom = g_Height;
863  break;
864 
865  case 3:
866  clippingRect.left = g_Width / 2;
867  clippingRect.top = 0;
868  clippingRect.right = g_Width;
869  clippingRect.bottom = g_Height / 2;
870  break;
871  default:
872  return -1;
873  }
874 
875  for (UINT16 index = 0; index < region->numTiles; index++)
876  {
877  RFX_PROGRESSIVE_TILE* tile = region->tiles[index];
878 
879  const RECTANGLE_16 tileRect = { tile->x, tile->y, tile->x + tile->width,
880  tile->y + tile->height };
881  RECTANGLE_16 updateRect = { 0 };
882  rectangles_intersection(&tileRect, &clippingRect, &updateRect);
883  const UINT16 nXDst = updateRect.left;
884  const UINT16 nYDst = updateRect.top;
885  const UINT16 nWidth = updateRect.right - updateRect.left;
886  const UINT16 nHeight = updateRect.bottom - updateRect.top;
887 
888  if ((nWidth <= 0) || (nHeight <= 0))
889  continue;
890 
891  nXSrc = nXDst - tile->x;
892  nYSrc = nYDst - tile->y;
893  freerdp_image_copy(g_DstData, PIXEL_FORMAT_XRGB32, g_DstStep, nXDst, nYDst, nWidth,
894  nHeight, tile->data, PIXEL_FORMAT_XRGB32, 64 * 4, nXSrc, nYSrc, NULL,
895  FREERDP_FLIP_NONE);
896  }
897 
898  const size_t size = bitmaps[pass].size;
899  const size_t cnt = test_memcmp_count(g_DstData, bitmaps[pass].buffer, size, 1);
900 
901  if (cnt)
902  {
903  const float rate = ((float)cnt) / ((float)size) * 100.0f;
904  printf("Progressive RemoteFX decompression failure\n");
905  printf("Actual, Expected (%" PRIuz "/%" PRIuz " = %.3f%%):\n", cnt, size, rate);
906  }
907 
908  // WLog_Image(progressive->log, WLOG_TRACE, g_DstData, g_Width, g_Height, 32);
909  }
910 
911  return 1;
912 }
913 
914 static int test_progressive_ms_sample(char* ms_sample_path)
915 {
916  int count = 0;
917  int status = 0;
918  EGFX_SAMPLE_FILE files[3][4][4] = { 0 };
919  EGFX_SAMPLE_FILE bitmaps[3][4][4] = { 0 };
920  PROGRESSIVE_CONTEXT* progressive = NULL;
921  g_Width = 1920;
922  g_Height = 1080;
923  g_DstStep = g_Width * 4;
924  status = test_progressive_load_files(ms_sample_path, files);
925 
926  if (status < 0)
927  {
928  for (int i = 0; i < 3; i++)
929  {
930  for (int j = 0; j < 4; j++)
931  {
932  for (int k = 0; k < 4; k++)
933  sample_file_free(&files[i][j][k]);
934  }
935  }
936 
937  return -1;
938  }
939 
940  status = test_progressive_load_bitmaps(ms_sample_path, bitmaps);
941 
942  if (status < 0)
943  {
944  for (int i = 0; i < 3; i++)
945  {
946  for (int j = 0; j < 4; j++)
947  {
948  for (int k = 0; k < 4; k++)
949  sample_file_free(&files[i][j][k]);
950  }
951  }
952 
953  return -1;
954  }
955 
956  count = 4;
957  progressive = progressive_context_new(FALSE);
958  g_DstData = winpr_aligned_malloc(1LL * g_DstStep * g_Height, 16);
959  progressive_create_surface_context(progressive, 0, g_Width, g_Height);
960 
961  /* image 1 */
962 
963  if (1)
964  {
965  printf("\nSample Image 1\n");
966  test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
967  test_progressive_decode(progressive, files[0][0], bitmaps[0][0], 0, count);
968  test_progressive_decode(progressive, files[0][1], bitmaps[0][1], 1, count);
969  test_progressive_decode(progressive, files[0][2], bitmaps[0][2], 2, count);
970  test_progressive_decode(progressive, files[0][3], bitmaps[0][3], 3, count);
971  }
972 
973  /* image 2 */
974 
975  if (0)
976  {
977  printf("\nSample Image 2\n"); /* sample data is in incorrect order */
978  test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
979  test_progressive_decode(progressive, files[1][0], bitmaps[1][0], 0, count);
980  test_progressive_decode(progressive, files[1][1], bitmaps[1][1], 1, count);
981  test_progressive_decode(progressive, files[1][2], bitmaps[1][2], 2, count);
982  test_progressive_decode(progressive, files[1][3], bitmaps[1][3], 3, count);
983  }
984 
985  /* image 3 */
986 
987  if (0)
988  {
989  printf("\nSample Image 3\n"); /* sample data is in incorrect order */
990  test_image_fill(g_DstData, g_DstStep, 0, 0, g_Width, g_Height, 0xFF000000);
991  test_progressive_decode(progressive, files[2][0], bitmaps[2][0], 0, count);
992  test_progressive_decode(progressive, files[2][1], bitmaps[2][1], 1, count);
993  test_progressive_decode(progressive, files[2][2], bitmaps[2][2], 2, count);
994  test_progressive_decode(progressive, files[2][3], bitmaps[2][3], 3, count);
995  }
996 
997  progressive_context_free(progressive);
998 
999  for (int i = 0; i < 3; i++)
1000  {
1001  for (int j = 0; j < 4; j++)
1002  {
1003  for (int k = 0; k < 4; k++)
1004  {
1005  sample_file_free(&bitmaps[i][j][k]);
1006  sample_file_free(&files[i][j][k]);
1007  }
1008  }
1009  }
1010 
1011  winpr_aligned_free(g_DstData);
1012  return 0;
1013 }
1014 
1015 static BOOL diff(BYTE a, BYTE b)
1016 {
1017  BYTE big = MAX(a, b);
1018  BYTE little = MIN(a, b);
1019  if (big - little <= 0x25)
1020  return TRUE;
1021  return FALSE;
1022 }
1023 
1024 static BOOL colordiff(UINT32 format, UINT32 a, UINT32 b)
1025 {
1026  BYTE ar = 0;
1027  BYTE ag = 0;
1028  BYTE ab = 0;
1029  BYTE aa = 0;
1030  BYTE br = 0;
1031  BYTE bg = 0;
1032  BYTE bb = 0;
1033  BYTE ba = 0;
1034  FreeRDPSplitColor(a, format, &ar, &ag, &ab, &aa, NULL);
1035  FreeRDPSplitColor(b, format, &br, &bg, &bb, &ba, NULL);
1036  if (!diff(aa, ba) || !diff(ar, br) || !diff(ag, bg) || !diff(ab, bb))
1037  return FALSE;
1038  return TRUE;
1039 }
1040 
1041 static BOOL test_encode_decode(const char* path)
1042 {
1043  BOOL res = FALSE;
1044  int rc = 0;
1045  BYTE* resultData = NULL;
1046  BYTE* dstData = NULL;
1047  UINT32 dstSize = 0;
1048  UINT32 ColorFormat = PIXEL_FORMAT_BGRX32;
1049  REGION16 invalidRegion = { 0 };
1050  wImage* image = winpr_image_new();
1051  wImage* dstImage = winpr_image_new();
1052  char* name = GetCombinedPath(path, "progressive.bmp");
1053  PROGRESSIVE_CONTEXT* progressiveEnc = progressive_context_new(TRUE);
1054  PROGRESSIVE_CONTEXT* progressiveDec = progressive_context_new(FALSE);
1055 
1056  region16_init(&invalidRegion);
1057  if (!image || !dstImage || !name || !progressiveEnc || !progressiveDec)
1058  goto fail;
1059 
1060  rc = winpr_image_read(image, name);
1061  if (rc <= 0)
1062  goto fail;
1063 
1064  resultData = calloc(image->scanline, image->height);
1065  if (!resultData)
1066  goto fail;
1067 
1068  // Progressive encode
1069  rc = progressive_compress(progressiveEnc, image->data, image->scanline * image->height,
1070  ColorFormat, image->width, image->height, image->scanline, NULL,
1071  &dstData, &dstSize);
1072  if (rc < 0)
1073  goto fail;
1074 
1075  // Progressive decode
1076  rc = progressive_create_surface_context(progressiveDec, 0, image->width, image->height);
1077  if (rc <= 0)
1078  goto fail;
1079 
1080  rc = progressive_decompress(progressiveDec, dstData, dstSize, resultData, ColorFormat,
1081  image->scanline, 0, 0, &invalidRegion, 0, 0);
1082  if (rc < 0)
1083  goto fail;
1084 
1085  // Compare result
1086  if (0) // Dump result image for manual inspection
1087  {
1088  *dstImage = *image;
1089  dstImage->data = resultData;
1090  winpr_image_write(dstImage, "/tmp/test.bmp");
1091  }
1092  for (size_t y = 0; y < image->height; y++)
1093  {
1094  const BYTE* orig = &image->data[y * image->scanline];
1095  const BYTE* dec = &resultData[y * image->scanline];
1096  for (size_t x = 0; x < image->width; x++)
1097  {
1098  const BYTE* po = &orig[x * 4];
1099  const BYTE* pd = &dec[x * 4];
1100 
1101  const DWORD a = FreeRDPReadColor(po, ColorFormat);
1102  const DWORD b = FreeRDPReadColor(pd, ColorFormat);
1103  if (!colordiff(ColorFormat, a, b))
1104  {
1105  printf("xxxxxxx [%u:%u] [%s] %08X != %08X\n", x, y,
1106  FreeRDPGetColorFormatName(ColorFormat), a, b);
1107  goto fail;
1108  }
1109  }
1110  }
1111  res = TRUE;
1112 fail:
1113  region16_uninit(&invalidRegion);
1114  progressive_context_free(progressiveEnc);
1115  progressive_context_free(progressiveDec);
1116  winpr_image_free(image, TRUE);
1117  winpr_image_free(dstImage, FALSE);
1118  free(resultData);
1119  free(name);
1120  return res;
1121 }
1122 
1123 static BOOL read_cmd(FILE* fp, RDPGFX_SURFACE_COMMAND* cmd, UINT32* frameId)
1124 {
1125  WINPR_ASSERT(fp);
1126  WINPR_ASSERT(cmd);
1127  WINPR_ASSERT(frameId);
1128 
1129  // NOLINTBEGIN(cert-err34-c)
1130  if (1 != fscanf(fp, "frameid: %" PRIu32 "\n", frameId))
1131  return FALSE;
1132  if (1 != fscanf(fp, "surfaceId: %" PRIu32 "\n", &cmd->surfaceId))
1133  return FALSE;
1134  if (1 != fscanf(fp, "codecId: %" PRIu32 "\n", &cmd->codecId))
1135  return FALSE;
1136  if (1 != fscanf(fp, "contextId: %" PRIu32 "\n", &cmd->contextId))
1137  return FALSE;
1138  if (1 != fscanf(fp, "format: %" PRIu32 "\n", &cmd->format))
1139  return FALSE;
1140  if (1 != fscanf(fp, "left: %" PRIu32 "\n", &cmd->left))
1141  return FALSE;
1142  if (1 != fscanf(fp, "top: %" PRIu32 "\n", &cmd->top))
1143  return FALSE;
1144  if (1 != fscanf(fp, "right: %" PRIu32 "\n", &cmd->right))
1145  return FALSE;
1146  if (1 != fscanf(fp, "bottom: %" PRIu32 "\n", &cmd->bottom))
1147  return FALSE;
1148  if (1 != fscanf(fp, "width: %" PRIu32 "\n", &cmd->width))
1149  return FALSE;
1150  if (1 != fscanf(fp, "height: %" PRIu32 "\n", &cmd->height))
1151  return FALSE;
1152  if (1 != fscanf(fp, "length: %" PRIu32 "\n", &cmd->length))
1153  return FALSE;
1154  // NOLINTEND(cert-err34-c)
1155 
1156  char* data = NULL;
1157 
1158  size_t dlen = SIZE_MAX;
1159  SSIZE_T slen = GetLine(&data, &slen, fp);
1160  if (slen < 0)
1161  return FALSE;
1162 
1163  if (slen >= 7)
1164  {
1165  const char* b64 = &data[6];
1166  slen -= 7;
1167  crypto_base64_decode(b64, slen, &cmd->data, &dlen);
1168  }
1169  free(data);
1170 
1171  return cmd->length == dlen;
1172 }
1173 
1174 static void free_cmd(RDPGFX_SURFACE_COMMAND* cmd)
1175 {
1176  free(cmd->data);
1177 }
1178 
1179 static WINPR_NORETURN(void usage(const char* name))
1180 {
1181  FILE* fp = stdout;
1182  (void)fprintf(fp, "%s <directory> <width> <height>\n", name);
1183  exit(-1);
1184 }
1185 
1186 static void print_codec_stats(const char* name, UINT64 timeNS)
1187 {
1188  const double dectimems = timeNS / 1000000.0;
1189  (void)fprintf(stderr, "[%s] took %lf ms to decode\n", name, dectimems);
1190 }
1191 
1192 static int test_dump(int argc, char* argv[])
1193 {
1194  int success = -1;
1195  UINT32 count = 0;
1196 
1197  UINT64 CAPROGRESSIVE_dectime = 0;
1198  UINT64 UNCOMPRESSED_dectime = 0;
1199  UINT64 CAVIDEO_dectime = 0;
1200  UINT64 CLEARCODEC_dectime = 0;
1201  UINT64 PLANAR_dectime = 0;
1202  UINT64 AVC420_dectime = 0;
1203  UINT64 ALPHA_dectime = 0;
1204  UINT64 AVC444_dectime = 0;
1205  UINT64 AVC444v2_dectime = 0;
1206  UINT64 copytime = 0;
1207 
1208  if (argc < 4)
1209  usage(argv[0]);
1210 
1211  const char* path = argv[1];
1212  errno = 0;
1213  const unsigned long width = strtoul(argv[2], NULL, 0);
1214  if ((errno != 0) || (width <= 0))
1215  usage(argv[0]);
1216  const unsigned long height = strtoul(argv[3], NULL, 0);
1217  if ((errno != 0) || (height <= 0))
1218  usage(argv[0]);
1219 
1220  rdpCodecs* codecs = freerdp_client_codecs_new(0);
1221  if (!codecs)
1222  return -2;
1223 
1224  UINT32 DstFormat = PIXEL_FORMAT_BGRA32;
1225  const UINT32 stride = (width + 16) * FreeRDPGetBytesPerPixel(DstFormat);
1226 
1227  BYTE* dst = calloc(stride, height);
1228  BYTE* output = calloc(stride, height);
1229  if (!dst || !output)
1230  goto fail;
1231 
1232  if (!freerdp_client_codecs_prepare(codecs, FREERDP_CODEC_ALL, width, height))
1233  goto fail;
1234 
1235  success = 0;
1236  while (success >= 0)
1237  {
1238  char* fname = NULL;
1239  size_t flen = 0;
1240  winpr_asprintf(&fname, &flen, "%s/%08" PRIx32 ".raw", path, count++);
1241  FILE* fp = fopen(fname, "r");
1242  free(fname);
1243 
1244  if (!fp)
1245  break;
1246 
1247  UINT32 frameId = 0;
1248  RDPGFX_SURFACE_COMMAND cmd = { 0 };
1249 
1250  if (read_cmd(fp, &cmd, &frameId))
1251  {
1252  REGION16 invalid = { 0 };
1253  region16_init(&invalid);
1254 
1255  const char* cname = rdpgfx_get_codec_id_string(cmd.codecId);
1256  switch (cmd.codecId)
1257  {
1258  case RDPGFX_CODECID_CAPROGRESSIVE:
1259  {
1260  const UINT64 start = winpr_GetTickCount64NS();
1261  success = progressive_create_surface_context(codecs->progressive, cmd.surfaceId,
1262  width, height);
1263  if (success >= 0)
1264  success = progressive_decompress(codecs->progressive, cmd.data, cmd.length,
1265  dst, DstFormat, 0, cmd.left, cmd.top,
1266  &invalid, cmd.surfaceId, frameId);
1267  const UINT64 end = winpr_GetTickCount64NS();
1268  const UINT64 diff = end - start;
1269  const double ddiff = diff / 1000000.0;
1270  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1271  ddiff);
1272  CAPROGRESSIVE_dectime += diff;
1273  }
1274  break;
1275 
1276  case RDPGFX_CODECID_UNCOMPRESSED:
1277  {
1278  const UINT64 start = winpr_GetTickCount64NS();
1279  if (!freerdp_image_copy_no_overlap(dst, DstFormat, stride, cmd.left, cmd.top,
1280  cmd.width, cmd.height, cmd.data, cmd.format,
1281  0, 0, 0, NULL, FREERDP_FLIP_NONE))
1282  success = -1;
1283 
1284  RECTANGLE_16 invalidRect = { .left = (UINT16)MIN(UINT16_MAX, cmd.left),
1285  .top = (UINT16)MIN(UINT16_MAX, cmd.top),
1286  .right = (UINT16)MIN(UINT16_MAX, cmd.right),
1287  .bottom = (UINT16)MIN(UINT16_MAX, cmd.bottom) };
1288  region16_union_rect(&invalid, &invalid, &invalidRect);
1289  const UINT64 end = winpr_GetTickCount64NS();
1290  const UINT64 diff = end - start;
1291  const double ddiff = diff / 1000000.0;
1292  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1293  ddiff);
1294  UNCOMPRESSED_dectime += diff;
1295  }
1296  break;
1297  case RDPGFX_CODECID_CAVIDEO:
1298  {
1299  const UINT64 start = winpr_GetTickCount64NS();
1300  if (!rfx_process_message(codecs->rfx, cmd.data, cmd.length, cmd.left, cmd.top,
1301  dst, DstFormat, stride, height, &invalid))
1302  success = -1;
1303 
1304  const UINT64 end = winpr_GetTickCount64NS();
1305  const UINT64 diff = end - start;
1306  const double ddiff = diff / 1000000.0;
1307  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1308  ddiff);
1309  CAVIDEO_dectime += diff;
1310  }
1311  break;
1312  case RDPGFX_CODECID_CLEARCODEC:
1313  {
1314  const UINT64 start = winpr_GetTickCount64NS();
1315  success = clear_decompress(codecs->clear, cmd.data, cmd.length, cmd.width,
1316  cmd.height, dst, DstFormat, stride, cmd.left,
1317  cmd.top, width, height, NULL);
1318 
1319  const RECTANGLE_16 invalidRect = { .left = (UINT16)MIN(UINT16_MAX, cmd.left),
1320  .top = (UINT16)MIN(UINT16_MAX, cmd.top),
1321  .right = (UINT16)MIN(UINT16_MAX, cmd.right),
1322  .bottom =
1323  (UINT16)MIN(UINT16_MAX, cmd.bottom) };
1324  region16_union_rect(&invalid, &invalid, &invalidRect);
1325  const UINT64 end = winpr_GetTickCount64NS();
1326  const UINT64 diff = end - start;
1327  const double ddiff = diff / 1000000.0;
1328  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1329  ddiff);
1330  CLEARCODEC_dectime += diff;
1331  }
1332  break;
1333  case RDPGFX_CODECID_PLANAR:
1334  {
1335  const UINT64 start = winpr_GetTickCount64NS();
1336 
1337  if (!planar_decompress(codecs->planar, cmd.data, cmd.length, cmd.width,
1338  cmd.height, dst, DstFormat, stride, cmd.left, cmd.top,
1339  cmd.width, cmd.height, FALSE))
1340  success = -1;
1341 
1342  const RECTANGLE_16 invalidRect = { .left = (UINT16)MIN(UINT16_MAX, cmd.left),
1343  .top = (UINT16)MIN(UINT16_MAX, cmd.top),
1344  .right = (UINT16)MIN(UINT16_MAX, cmd.right),
1345  .bottom =
1346  (UINT16)MIN(UINT16_MAX, cmd.bottom) };
1347  region16_union_rect(&invalid, &invalid, &invalidRect);
1348 
1349  const UINT64 end = winpr_GetTickCount64NS();
1350  const UINT64 diff = end - start;
1351  const double ddiff = diff / 1000000.0;
1352  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1353  ddiff);
1354  PLANAR_dectime += diff;
1355  }
1356  break;
1357  case RDPGFX_CODECID_AVC420:
1358  {
1359  const UINT64 start = winpr_GetTickCount64NS();
1360 
1361  const UINT64 end = winpr_GetTickCount64NS();
1362  const UINT64 diff = end - start;
1363  const double ddiff = diff / 1000000.0;
1364  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1365  ddiff);
1366  AVC420_dectime += diff;
1367  success = -1;
1368  }
1369  break;
1370  case RDPGFX_CODECID_ALPHA:
1371  {
1372  const UINT64 start = winpr_GetTickCount64NS();
1373 
1374  const UINT64 end = winpr_GetTickCount64NS();
1375  const UINT64 diff = end - start;
1376  const double ddiff = diff / 1000000.0;
1377  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1378  ddiff);
1379  ALPHA_dectime += diff;
1380  success = -1;
1381  }
1382  break;
1383  case RDPGFX_CODECID_AVC444:
1384  {
1385  const UINT64 start = winpr_GetTickCount64NS();
1386 
1387  const UINT64 end = winpr_GetTickCount64NS();
1388  const UINT64 diff = end - start;
1389  const double ddiff = diff / 1000000.0;
1390  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1391  ddiff);
1392  AVC444_dectime += diff;
1393  success = -1;
1394  }
1395  break;
1396  case RDPGFX_CODECID_AVC444v2:
1397  {
1398  const UINT64 start = winpr_GetTickCount64NS();
1399 
1400  const UINT64 end = winpr_GetTickCount64NS();
1401  const UINT64 diff = end - start;
1402  const double ddiff = diff / 1000000.0;
1403  (void)fprintf(stderr, "frame [%s] %" PRIu32 " took %lf ms\n", cname, frameId,
1404  ddiff);
1405  AVC444v2_dectime += diff;
1406  success = -1;
1407  }
1408  break;
1409  default:
1410  (void)fprintf(stderr, "unexpected codec %s [0x%08" PRIx32 "]",
1411  rdpgfx_get_codec_id_string(cmd.codecId), cmd.codecId);
1412  success = -1;
1413  break;
1414  }
1415 
1416  if (success >= 0)
1417  {
1418  UINT32 nbRects = 0;
1419  const UINT64 start = winpr_GetTickCount64NS();
1420 
1421  const RECTANGLE_16* rects = region16_rects(&invalid, &nbRects);
1422  for (size_t x = 0; x < nbRects; x++)
1423  {
1424  RECTANGLE_16* rect = &rects[x];
1425  const UINT32 w = rect->right - rect->left;
1426  const UINT32 h = rect->bottom - rect->top;
1427  if (!freerdp_image_copy_no_overlap(output, DstFormat, stride, rect->left,
1428  rect->top, w, h, dst, DstFormat, stride,
1429  rect->left, rect->top, NULL, 0))
1430  success = -42;
1431  }
1432  const UINT64 end = winpr_GetTickCount64NS();
1433  const UINT64 diff = end - start;
1434  const double ddiff = diff / 1000000.0;
1435  (void)fprintf(stderr, "frame %" PRIu32 " copy took %lf ms\n", frameId, ddiff);
1436  copytime += diff;
1437  }
1438  region16_clear(&invalid);
1439  }
1440  free_cmd(&cmd);
1441  (void)fclose(fp);
1442  }
1443 
1444 fail:
1445  freerdp_client_codecs_free(codecs);
1446  free(output);
1447  free(dst);
1448 
1449  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_UNCOMPRESSED),
1450  UNCOMPRESSED_dectime);
1451  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_CAPROGRESSIVE),
1452  CAPROGRESSIVE_dectime);
1453  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_CAVIDEO), CAVIDEO_dectime);
1454  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_CLEARCODEC), CLEARCODEC_dectime);
1455  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_PLANAR), PLANAR_dectime);
1456  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_AVC420), AVC420_dectime);
1457  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_AVC444), AVC444_dectime);
1458  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_AVC444v2), AVC444v2_dectime);
1459  print_codec_stats(rdpgfx_get_codec_id_string(RDPGFX_CODECID_ALPHA), ALPHA_dectime);
1460 
1461  const UINT64 decodetime = UNCOMPRESSED_dectime + CAPROGRESSIVE_dectime + CAVIDEO_dectime +
1462  CLEARCODEC_dectime + PLANAR_dectime + AVC420_dectime +
1463  AVC444_dectime + AVC444v2_dectime + ALPHA_dectime;
1464  print_codec_stats("surface copy", copytime);
1465  print_codec_stats("total decode", decodetime);
1466  print_codec_stats("total", decodetime + copytime);
1467 
1468  return success;
1469 }
1470 
1471 int TestFreeRDPCodecProgressive(int argc, char* argv[])
1472 {
1473  if (argc > 1)
1474  return test_dump(argc, argv);
1475 
1476  int rc = -1;
1477  char* ms_sample_path = NULL;
1478  char name[8192];
1479  SYSTEMTIME systemTime;
1480  WINPR_UNUSED(argc);
1481  WINPR_UNUSED(argv);
1482 
1483  GetSystemTime(&systemTime);
1484  (void)sprintf_s(name, sizeof(name),
1485  "EGFX_PROGRESSIVE_MS_SAMPLE-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16
1486  "%02" PRIu16 "%02" PRIu16 "%04" PRIu16,
1487  systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
1488  systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
1489  ms_sample_path = _strdup(CMAKE_CURRENT_SOURCE_DIR);
1490 
1491  if (!ms_sample_path)
1492  {
1493  printf("Memory allocation failed\n");
1494  goto fail;
1495  }
1496 
1497  if (winpr_PathFileExists(ms_sample_path))
1498  {
1499  /*
1500  if (test_progressive_ms_sample(ms_sample_path) < 0)
1501  goto fail;
1502  */
1503  if (!test_encode_decode(ms_sample_path))
1504  goto fail;
1505  rc = 0;
1506  }
1507 
1508 fail:
1509  free(ms_sample_path);
1510  return rc;
1511 }