FreeRDP
rfx.c
1 
22 #include <freerdp/config.h>
23 
24 #include <winpr/assert.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <winpr/crt.h>
30 #include <winpr/tchar.h>
31 #include <winpr/sysinfo.h>
32 #include <winpr/registry.h>
33 
34 #include <freerdp/log.h>
35 #include <freerdp/settings.h>
36 #include <freerdp/codec/rfx.h>
37 #include <freerdp/constants.h>
38 #include <freerdp/primitives.h>
39 #include <freerdp/codec/region.h>
40 #include <freerdp/build-config.h>
41 
42 #include "rfx_constants.h"
43 #include "rfx_types.h"
44 #include "rfx_decode.h"
45 #include "rfx_encode.h"
46 #include "rfx_quantization.h"
47 #include "rfx_dwt.h"
48 #include "rfx_rlgr.h"
49 
50 #include "sse/rfx_sse2.h"
51 #include "neon/rfx_neon.h"
52 
53 #define TAG FREERDP_TAG("codec")
54 
55 #define RFX_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING "\\RemoteFX"
56 
69 static const UINT32 rfx_default_quantization_values[] = { 6, 6, 6, 6, 7, 7, 8, 8, 8, 9 };
70 
71 static INLINE BOOL rfx_write_progressive_tile_simple(RFX_CONTEXT* WINPR_RESTRICT rfx,
72  wStream* WINPR_RESTRICT s,
73  const RFX_TILE* WINPR_RESTRICT tile);
74 
75 static INLINE void rfx_profiler_create(RFX_CONTEXT* WINPR_RESTRICT context)
76 {
77  if (!context || !context->priv)
78  return;
79  PROFILER_CREATE(context->priv->prof_rfx_decode_rgb, "rfx_decode_rgb")
80  PROFILER_CREATE(context->priv->prof_rfx_decode_component, "rfx_decode_component")
81  PROFILER_CREATE(context->priv->prof_rfx_rlgr_decode, "rfx_rlgr_decode")
82  PROFILER_CREATE(context->priv->prof_rfx_differential_decode, "rfx_differential_decode")
83  PROFILER_CREATE(context->priv->prof_rfx_quantization_decode, "rfx_quantization_decode")
84  PROFILER_CREATE(context->priv->prof_rfx_dwt_2d_decode, "rfx_dwt_2d_decode")
85  PROFILER_CREATE(context->priv->prof_rfx_ycbcr_to_rgb, "prims->yCbCrToRGB")
86  PROFILER_CREATE(context->priv->prof_rfx_encode_rgb, "rfx_encode_rgb")
87  PROFILER_CREATE(context->priv->prof_rfx_encode_component, "rfx_encode_component")
88  PROFILER_CREATE(context->priv->prof_rfx_rlgr_encode, "rfx_rlgr_encode")
89  PROFILER_CREATE(context->priv->prof_rfx_differential_encode, "rfx_differential_encode")
90  PROFILER_CREATE(context->priv->prof_rfx_quantization_encode, "rfx_quantization_encode")
91  PROFILER_CREATE(context->priv->prof_rfx_dwt_2d_encode, "rfx_dwt_2d_encode")
92  PROFILER_CREATE(context->priv->prof_rfx_rgb_to_ycbcr, "prims->RGBToYCbCr")
93  PROFILER_CREATE(context->priv->prof_rfx_encode_format_rgb, "rfx_encode_format_rgb")
94 }
95 
96 static INLINE void rfx_profiler_free(RFX_CONTEXT* WINPR_RESTRICT context)
97 {
98  if (!context || !context->priv)
99  return;
100  PROFILER_FREE(context->priv->prof_rfx_decode_rgb)
101  PROFILER_FREE(context->priv->prof_rfx_decode_component)
102  PROFILER_FREE(context->priv->prof_rfx_rlgr_decode)
103  PROFILER_FREE(context->priv->prof_rfx_differential_decode)
104  PROFILER_FREE(context->priv->prof_rfx_quantization_decode)
105  PROFILER_FREE(context->priv->prof_rfx_dwt_2d_decode)
106  PROFILER_FREE(context->priv->prof_rfx_ycbcr_to_rgb)
107  PROFILER_FREE(context->priv->prof_rfx_encode_rgb)
108  PROFILER_FREE(context->priv->prof_rfx_encode_component)
109  PROFILER_FREE(context->priv->prof_rfx_rlgr_encode)
110  PROFILER_FREE(context->priv->prof_rfx_differential_encode)
111  PROFILER_FREE(context->priv->prof_rfx_quantization_encode)
112  PROFILER_FREE(context->priv->prof_rfx_dwt_2d_encode)
113  PROFILER_FREE(context->priv->prof_rfx_rgb_to_ycbcr)
114  PROFILER_FREE(context->priv->prof_rfx_encode_format_rgb)
115 }
116 
117 static INLINE void rfx_profiler_print(RFX_CONTEXT* WINPR_RESTRICT context)
118 {
119  if (!context || !context->priv)
120  return;
121 
122  PROFILER_PRINT_HEADER
123  PROFILER_PRINT(context->priv->prof_rfx_decode_rgb)
124  PROFILER_PRINT(context->priv->prof_rfx_decode_component)
125  PROFILER_PRINT(context->priv->prof_rfx_rlgr_decode)
126  PROFILER_PRINT(context->priv->prof_rfx_differential_decode)
127  PROFILER_PRINT(context->priv->prof_rfx_quantization_decode)
128  PROFILER_PRINT(context->priv->prof_rfx_dwt_2d_decode)
129  PROFILER_PRINT(context->priv->prof_rfx_ycbcr_to_rgb)
130  PROFILER_PRINT(context->priv->prof_rfx_encode_rgb)
131  PROFILER_PRINT(context->priv->prof_rfx_encode_component)
132  PROFILER_PRINT(context->priv->prof_rfx_rlgr_encode)
133  PROFILER_PRINT(context->priv->prof_rfx_differential_encode)
134  PROFILER_PRINT(context->priv->prof_rfx_quantization_encode)
135  PROFILER_PRINT(context->priv->prof_rfx_dwt_2d_encode)
136  PROFILER_PRINT(context->priv->prof_rfx_rgb_to_ycbcr)
137  PROFILER_PRINT(context->priv->prof_rfx_encode_format_rgb)
138  PROFILER_PRINT_FOOTER
139 }
140 
141 static INLINE void rfx_tile_init(void* obj)
142 {
143  RFX_TILE* tile = (RFX_TILE*)obj;
144  if (tile)
145  {
146  tile->x = 0;
147  tile->y = 0;
148  tile->YLen = 0;
149  tile->YData = NULL;
150  tile->CbLen = 0;
151  tile->CbData = NULL;
152  tile->CrLen = 0;
153  tile->CrData = NULL;
154  }
155 }
156 
157 static INLINE void* rfx_decoder_tile_new(const void* val)
158 {
159  const size_t size = 4ULL * 64ULL * 64ULL;
160  RFX_TILE* tile = NULL;
161  WINPR_UNUSED(val);
162 
163  if (!(tile = (RFX_TILE*)winpr_aligned_calloc(1, sizeof(RFX_TILE), 32)))
164  return NULL;
165 
166  if (!(tile->data = (BYTE*)winpr_aligned_malloc(size, 16)))
167  {
168  winpr_aligned_free(tile);
169  return NULL;
170  }
171  memset(tile->data, 0xff, size);
172  tile->allocated = TRUE;
173  return tile;
174 }
175 
176 static INLINE void rfx_decoder_tile_free(void* obj)
177 {
178  RFX_TILE* tile = (RFX_TILE*)obj;
179 
180  if (tile)
181  {
182  if (tile->allocated)
183  winpr_aligned_free(tile->data);
184 
185  winpr_aligned_free(tile);
186  }
187 }
188 
189 static INLINE void* rfx_encoder_tile_new(const void* val)
190 {
191  WINPR_UNUSED(val);
192  return winpr_aligned_calloc(1, sizeof(RFX_TILE), 32);
193 }
194 
195 static INLINE void rfx_encoder_tile_free(void* obj)
196 {
197  winpr_aligned_free(obj);
198 }
199 
200 RFX_CONTEXT* rfx_context_new(BOOL encoder)
201 {
202  return rfx_context_new_ex(encoder, 0);
203 }
204 
205 RFX_CONTEXT* rfx_context_new_ex(BOOL encoder, UINT32 ThreadingFlags)
206 {
207  HKEY hKey = NULL;
208  LONG status = 0;
209  DWORD dwType = 0;
210  DWORD dwSize = 0;
211  DWORD dwValue = 0;
212  SYSTEM_INFO sysinfo;
213  RFX_CONTEXT* context = NULL;
214  wObject* pool = NULL;
215  RFX_CONTEXT_PRIV* priv = NULL;
216  context = (RFX_CONTEXT*)winpr_aligned_calloc(1, sizeof(RFX_CONTEXT), 32);
217 
218  if (!context)
219  return NULL;
220 
221  context->encoder = encoder;
222  context->currentMessage.freeArray = TRUE;
223  context->priv = priv = (RFX_CONTEXT_PRIV*)winpr_aligned_calloc(1, sizeof(RFX_CONTEXT_PRIV), 32);
224 
225  if (!priv)
226  goto fail;
227 
228  priv->log = WLog_Get("com.freerdp.codec.rfx");
229  WLog_OpenAppender(priv->log);
230  priv->TilePool = ObjectPool_New(TRUE);
231 
232  if (!priv->TilePool)
233  goto fail;
234 
235  pool = ObjectPool_Object(priv->TilePool);
236  pool->fnObjectInit = rfx_tile_init;
237 
238  if (context->encoder)
239  {
240  pool->fnObjectNew = rfx_encoder_tile_new;
241  pool->fnObjectFree = rfx_encoder_tile_free;
242  }
243  else
244  {
245  pool->fnObjectNew = rfx_decoder_tile_new;
246  pool->fnObjectFree = rfx_decoder_tile_free;
247  }
248 
249  /*
250  * align buffers to 16 byte boundary (needed for SSE/NEON instructions)
251  *
252  * y_r_buffer, cb_g_buffer, cr_b_buffer: 64 * 64 * sizeof(INT16) = 8192 (0x2000)
253  * dwt_buffer: 32 * 32 * 2 * 2 * sizeof(INT16) = 8192, maximum sub-band width is 32
254  *
255  * Additionally we add 32 bytes (16 in front and 16 at the back of the buffer)
256  * in order to allow optimized functions (SEE, NEON) to read from positions
257  * that are actually in front/beyond the buffer. Offset calculations are
258  * performed at the BufferPool_Take function calls in rfx_encode/decode.c.
259  *
260  * We then multiply by 3 to use a single, partioned buffer for all 3 channels.
261  */
262  priv->BufferPool = BufferPool_New(TRUE, (8192ULL + 32ULL) * 3ULL, 16);
263 
264  if (!priv->BufferPool)
265  goto fail;
266 
267  if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS))
268  {
269  priv->UseThreads = TRUE;
270 
271  GetNativeSystemInfo(&sysinfo);
272  priv->MinThreadCount = sysinfo.dwNumberOfProcessors;
273  priv->MaxThreadCount = 0;
274  status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, RFX_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
275 
276  if (status == ERROR_SUCCESS)
277  {
278  dwSize = sizeof(dwValue);
279 
280  if (RegQueryValueEx(hKey, _T("UseThreads"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
281  ERROR_SUCCESS)
282  priv->UseThreads = dwValue ? 1 : 0;
283 
284  if (RegQueryValueEx(hKey, _T("MinThreadCount"), NULL, &dwType, (BYTE*)&dwValue,
285  &dwSize) == ERROR_SUCCESS)
286  priv->MinThreadCount = dwValue;
287 
288  if (RegQueryValueEx(hKey, _T("MaxThreadCount"), NULL, &dwType, (BYTE*)&dwValue,
289  &dwSize) == ERROR_SUCCESS)
290  priv->MaxThreadCount = dwValue;
291 
292  RegCloseKey(hKey);
293  }
294  }
295  else
296  {
297  priv->UseThreads = FALSE;
298  }
299 
300  if (priv->UseThreads)
301  {
302  /* Call primitives_get here in order to avoid race conditions when using primitives_get */
303  /* from multiple threads. This call will initialize all function pointers correctly */
304  /* before any decoding threads are started */
305  primitives_get();
306  priv->ThreadPool = CreateThreadpool(NULL);
307 
308  if (!priv->ThreadPool)
309  goto fail;
310 
311  InitializeThreadpoolEnvironment(&priv->ThreadPoolEnv);
312  SetThreadpoolCallbackPool(&priv->ThreadPoolEnv, priv->ThreadPool);
313 
314  if (priv->MinThreadCount)
315  if (!SetThreadpoolThreadMinimum(priv->ThreadPool, priv->MinThreadCount))
316  goto fail;
317 
318  if (priv->MaxThreadCount)
319  SetThreadpoolThreadMaximum(priv->ThreadPool, priv->MaxThreadCount);
320  }
321 
322  /* initialize the default pixel format */
323  rfx_context_set_pixel_format(context, PIXEL_FORMAT_BGRX32);
324  /* create profilers for default decoding routines */
325  rfx_profiler_create(context);
326  /* set up default routines */
327  context->quantization_decode = rfx_quantization_decode;
328  context->quantization_encode = rfx_quantization_encode;
329  context->dwt_2d_decode = rfx_dwt_2d_decode;
330  context->dwt_2d_extrapolate_decode = rfx_dwt_2d_extrapolate_decode;
331  context->dwt_2d_encode = rfx_dwt_2d_encode;
332  context->rlgr_decode = rfx_rlgr_decode;
333  context->rlgr_encode = rfx_rlgr_encode;
334  rfx_init_sse2(context);
335  rfx_init_neon(context);
336  context->state = RFX_STATE_SEND_HEADERS;
337  context->expectedDataBlockType = WBT_FRAME_BEGIN;
338  return context;
339 fail:
340  WINPR_PRAGMA_DIAG_PUSH
341  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
342  rfx_context_free(context);
343  WINPR_PRAGMA_DIAG_POP
344  return NULL;
345 }
346 
347 void rfx_context_free(RFX_CONTEXT* context)
348 {
349  RFX_CONTEXT_PRIV* priv = NULL;
350 
351  if (!context)
352  return;
353 
354  WINPR_ASSERT(NULL != context);
355 
356  priv = context->priv;
357  WINPR_ASSERT(NULL != priv);
358  WINPR_ASSERT(NULL != priv->TilePool);
359  WINPR_ASSERT(NULL != priv->BufferPool);
360 
361  /* coverity[address_free] */
362  rfx_message_free(context, &context->currentMessage);
363  winpr_aligned_free(context->quants);
364  rfx_profiler_print(context);
365  rfx_profiler_free(context);
366 
367  if (priv)
368  {
369  ObjectPool_Free(priv->TilePool);
370  if (priv->UseThreads)
371  {
372  if (priv->ThreadPool)
373  CloseThreadpool(priv->ThreadPool);
374  DestroyThreadpoolEnvironment(&priv->ThreadPoolEnv);
375  winpr_aligned_free(priv->workObjects);
376  winpr_aligned_free(priv->tileWorkParams);
377 #ifdef WITH_PROFILER
378  WLog_VRB(
379  TAG,
380  "WARNING: Profiling results probably unusable with multithreaded RemoteFX codec!");
381 #endif
382  }
383 
384  BufferPool_Free(priv->BufferPool);
385  winpr_aligned_free(priv);
386  }
387  winpr_aligned_free(context);
388 }
389 
390 static INLINE RFX_TILE* rfx_message_get_tile(RFX_MESSAGE* WINPR_RESTRICT message, UINT32 index)
391 {
392  WINPR_ASSERT(message);
393  WINPR_ASSERT(message->tiles);
394  WINPR_ASSERT(index < message->numTiles);
395  return message->tiles[index];
396 }
397 
398 static INLINE const RFX_RECT* rfx_message_get_rect_const(const RFX_MESSAGE* WINPR_RESTRICT message,
399  UINT32 index)
400 {
401  WINPR_ASSERT(message);
402  WINPR_ASSERT(message->rects);
403  WINPR_ASSERT(index < message->numRects);
404  return &message->rects[index];
405 }
406 
407 static INLINE RFX_RECT* rfx_message_get_rect(RFX_MESSAGE* WINPR_RESTRICT message, UINT32 index)
408 {
409  WINPR_ASSERT(message);
410  WINPR_ASSERT(message->rects);
411  WINPR_ASSERT(index < message->numRects);
412  return &message->rects[index];
413 }
414 
415 void rfx_context_set_pixel_format(RFX_CONTEXT* WINPR_RESTRICT context, UINT32 pixel_format)
416 {
417  WINPR_ASSERT(context);
418  context->pixel_format = pixel_format;
419  context->bits_per_pixel = FreeRDPGetBitsPerPixel(pixel_format);
420 }
421 
422 UINT32 rfx_context_get_pixel_format(RFX_CONTEXT* WINPR_RESTRICT context)
423 {
424  WINPR_ASSERT(context);
425  return context->pixel_format;
426 }
427 
428 void rfx_context_set_palette(RFX_CONTEXT* WINPR_RESTRICT context,
429  const BYTE* WINPR_RESTRICT palette)
430 {
431  WINPR_ASSERT(context);
432  context->palette = palette;
433 }
434 
435 const BYTE* rfx_context_get_palette(RFX_CONTEXT* WINPR_RESTRICT context)
436 {
437  WINPR_ASSERT(context);
438  return context->palette;
439 }
440 
441 BOOL rfx_context_reset(RFX_CONTEXT* WINPR_RESTRICT context, UINT32 width, UINT32 height)
442 {
443  if (!context)
444  return FALSE;
445 
446  context->width = width;
447  context->height = height;
448  context->state = RFX_STATE_SEND_HEADERS;
449  context->expectedDataBlockType = WBT_FRAME_BEGIN;
450  context->frameIdx = 0;
451  return TRUE;
452 }
453 
454 static INLINE BOOL rfx_process_message_sync(RFX_CONTEXT* WINPR_RESTRICT context,
455  wStream* WINPR_RESTRICT s)
456 {
457  UINT32 magic = 0;
458 
459  WINPR_ASSERT(context);
460  WINPR_ASSERT(context->priv);
461  context->decodedHeaderBlocks &= ~RFX_DECODED_SYNC;
462 
463  /* RFX_SYNC */
464  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 6))
465  return FALSE;
466 
467  Stream_Read_UINT32(s, magic); /* magic (4 bytes), 0xCACCACCA */
468  if (magic != WF_MAGIC)
469  {
470  WLog_Print(context->priv->log, WLOG_ERROR, "invalid magic number 0x%08" PRIX32 "", magic);
471  return FALSE;
472  }
473 
474  Stream_Read_UINT16(s, context->version); /* version (2 bytes), WF_VERSION_1_0 (0x0100) */
475  if (context->version != WF_VERSION_1_0)
476  {
477  WLog_Print(context->priv->log, WLOG_ERROR, "invalid version number 0x%08" PRIX32 "",
478  context->version);
479  return FALSE;
480  }
481 
482  WLog_Print(context->priv->log, WLOG_DEBUG, "version 0x%08" PRIX32 "", context->version);
483  context->decodedHeaderBlocks |= RFX_DECODED_SYNC;
484  return TRUE;
485 }
486 
487 static INLINE BOOL rfx_process_message_codec_versions(RFX_CONTEXT* WINPR_RESTRICT context,
488  wStream* WINPR_RESTRICT s)
489 {
490  BYTE numCodecs = 0;
491 
492  WINPR_ASSERT(context);
493  WINPR_ASSERT(context->priv);
494  context->decodedHeaderBlocks &= ~RFX_DECODED_VERSIONS;
495 
496  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4))
497  return FALSE;
498 
499  Stream_Read_UINT8(s, numCodecs); /* numCodecs (1 byte), must be set to 0x01 */
500  Stream_Read_UINT8(s, context->codec_id); /* codecId (1 byte), must be set to 0x01 */
501  Stream_Read_UINT16(
502  s, context->codec_version); /* version (2 bytes), must be set to WF_VERSION_1_0 (0x0100) */
503 
504  if (numCodecs != 1)
505  {
506  WLog_Print(context->priv->log, WLOG_ERROR, "numCodes is 0x%02" PRIX8 " (must be 0x01)",
507  numCodecs);
508  return FALSE;
509  }
510 
511  if (context->codec_id != 0x01)
512  {
513  WLog_Print(context->priv->log, WLOG_ERROR, "invalid codec id (0x%02" PRIX32 ")",
514  context->codec_id);
515  return FALSE;
516  }
517 
518  if (context->codec_version != WF_VERSION_1_0)
519  {
520  WLog_Print(context->priv->log, WLOG_ERROR, "invalid codec version (0x%08" PRIX32 ")",
521  context->codec_version);
522  return FALSE;
523  }
524 
525  WLog_Print(context->priv->log, WLOG_DEBUG, "id %" PRIu32 " version 0x%" PRIX32 ".",
526  context->codec_id, context->codec_version);
527  context->decodedHeaderBlocks |= RFX_DECODED_VERSIONS;
528  return TRUE;
529 }
530 
531 static INLINE BOOL rfx_process_message_channels(RFX_CONTEXT* WINPR_RESTRICT context,
532  wStream* WINPR_RESTRICT s)
533 {
534  BYTE channelId = 0;
535  BYTE numChannels = 0;
536 
537  WINPR_ASSERT(context);
538  WINPR_ASSERT(context->priv);
539  context->decodedHeaderBlocks &= ~RFX_DECODED_CHANNELS;
540 
541  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 1))
542  return FALSE;
543 
544  Stream_Read_UINT8(s, numChannels); /* numChannels (1 byte), must bet set to 0x01 */
545 
546  /* In RDVH sessions, numChannels will represent the number of virtual monitors
547  * configured and does not always be set to 0x01 as [MS-RDPRFX] said.
548  */
549  if (numChannels < 1)
550  {
551  WLog_Print(context->priv->log, WLOG_ERROR, "no channels announced");
552  return FALSE;
553  }
554 
555  if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(context->priv->log, s, numChannels, 5ull))
556  return FALSE;
557 
558  /* RFX_CHANNELT */
559  Stream_Read_UINT8(s, channelId); /* channelId (1 byte), must be set to 0x00 */
560 
561  if (channelId != 0x00)
562  {
563  WLog_Print(context->priv->log, WLOG_ERROR, "channelId:0x%02" PRIX8 ", expected:0x00",
564  channelId);
565  return FALSE;
566  }
567 
568  Stream_Read_UINT16(s, context->width); /* width (2 bytes) */
569  Stream_Read_UINT16(s, context->height); /* height (2 bytes) */
570 
571  if (!context->width || !context->height)
572  {
573  WLog_Print(context->priv->log, WLOG_ERROR,
574  "invalid channel with/height: %" PRIu16 "x%" PRIu16 "", context->width,
575  context->height);
576  return FALSE;
577  }
578 
579  /* Now, only the first monitor can be used, therefore the other channels will be ignored. */
580  Stream_Seek(s, 5ULL * (numChannels - 1));
581  WLog_Print(context->priv->log, WLOG_DEBUG,
582  "numChannels %" PRIu8 " id %" PRIu8 ", %" PRIu16 "x%" PRIu16 ".", numChannels,
583  channelId, context->width, context->height);
584  context->decodedHeaderBlocks |= RFX_DECODED_CHANNELS;
585  return TRUE;
586 }
587 
588 static INLINE BOOL rfx_process_message_context(RFX_CONTEXT* WINPR_RESTRICT context,
589  wStream* WINPR_RESTRICT s)
590 {
591  BYTE ctxId = 0;
592  UINT16 tileSize = 0;
593  UINT16 properties = 0;
594 
595  WINPR_ASSERT(context);
596  WINPR_ASSERT(context->priv);
597  context->decodedHeaderBlocks &= ~RFX_DECODED_CONTEXT;
598 
599  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 5))
600  return FALSE;
601 
602  Stream_Read_UINT8(s, ctxId); /* ctxId (1 byte), must be set to 0x00 */
603  Stream_Read_UINT16(s, tileSize); /* tileSize (2 bytes), must be set to CT_TILE_64x64 (0x0040) */
604  Stream_Read_UINT16(s, properties); /* properties (2 bytes) */
605  WLog_Print(context->priv->log, WLOG_DEBUG,
606  "ctxId %" PRIu8 " tileSize %" PRIu16 " properties 0x%04" PRIX16 ".", ctxId, tileSize,
607  properties);
608  context->properties = properties;
609  context->flags = (properties & 0x0007);
610 
611  if (context->flags == CODEC_MODE)
612  {
613  WLog_Print(context->priv->log, WLOG_DEBUG, "codec is in image mode.");
614  }
615  else
616  {
617  WLog_Print(context->priv->log, WLOG_DEBUG, "codec is in video mode.");
618  }
619 
620  switch ((properties & 0x1E00) >> 9)
621  {
622  case CLW_ENTROPY_RLGR1:
623  context->mode = RLGR1;
624  WLog_Print(context->priv->log, WLOG_DEBUG, "RLGR1.");
625  break;
626 
627  case CLW_ENTROPY_RLGR3:
628  context->mode = RLGR3;
629  WLog_Print(context->priv->log, WLOG_DEBUG, "RLGR3.");
630  break;
631 
632  default:
633  WLog_Print(context->priv->log, WLOG_ERROR, "unknown RLGR algorithm.");
634  return FALSE;
635  }
636 
637  context->decodedHeaderBlocks |= RFX_DECODED_CONTEXT;
638  return TRUE;
639 }
640 
641 static INLINE BOOL rfx_process_message_frame_begin(RFX_CONTEXT* WINPR_RESTRICT context,
642  RFX_MESSAGE* WINPR_RESTRICT message,
643  wStream* WINPR_RESTRICT s,
644  UINT16* WINPR_RESTRICT pExpectedBlockType)
645 {
646  UINT32 frameIdx = 0;
647  UINT16 numRegions = 0;
648 
649  WINPR_ASSERT(context);
650  WINPR_ASSERT(context->priv);
651  WINPR_ASSERT(message);
652  WINPR_ASSERT(pExpectedBlockType);
653 
654  if (*pExpectedBlockType != WBT_FRAME_BEGIN)
655  {
656  WLog_Print(context->priv->log, WLOG_ERROR, "message unexpected wants WBT_FRAME_BEGIN");
657  return FALSE;
658  }
659 
660  *pExpectedBlockType = WBT_REGION;
661 
662  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 6))
663  return FALSE;
664 
665  Stream_Read_UINT32(
666  s, frameIdx); /* frameIdx (4 bytes), if codec is in video mode, must be ignored */
667  Stream_Read_UINT16(s, numRegions); /* numRegions (2 bytes) */
668  WLog_Print(context->priv->log, WLOG_DEBUG,
669  "RFX_FRAME_BEGIN: frameIdx: %" PRIu32 " numRegions: %" PRIu16 "", frameIdx,
670  numRegions);
671  return TRUE;
672 }
673 
674 static INLINE BOOL rfx_process_message_frame_end(RFX_CONTEXT* WINPR_RESTRICT context,
675  RFX_MESSAGE* WINPR_RESTRICT message,
676  wStream* WINPR_RESTRICT s,
677  UINT16* WINPR_RESTRICT pExpectedBlockType)
678 {
679  WINPR_ASSERT(context);
680  WINPR_ASSERT(context->priv);
681  WINPR_ASSERT(message);
682  WINPR_ASSERT(s);
683  WINPR_ASSERT(pExpectedBlockType);
684 
685  if (*pExpectedBlockType != WBT_FRAME_END)
686  {
687  WLog_Print(context->priv->log, WLOG_ERROR, "message unexpected, wants WBT_FRAME_END");
688  return FALSE;
689  }
690 
691  *pExpectedBlockType = WBT_FRAME_BEGIN;
692  WLog_Print(context->priv->log, WLOG_DEBUG, "RFX_FRAME_END");
693  return TRUE;
694 }
695 
696 static INLINE BOOL rfx_resize_rects(RFX_MESSAGE* WINPR_RESTRICT message)
697 {
698  WINPR_ASSERT(message);
699 
700  RFX_RECT* tmpRects =
701  winpr_aligned_recalloc(message->rects, message->numRects, sizeof(RFX_RECT), 32);
702  if (!tmpRects)
703  return FALSE;
704  message->rects = tmpRects;
705  return TRUE;
706 }
707 
708 static INLINE BOOL rfx_process_message_region(RFX_CONTEXT* WINPR_RESTRICT context,
709  RFX_MESSAGE* WINPR_RESTRICT message,
710  wStream* WINPR_RESTRICT s,
711  UINT16* WINPR_RESTRICT pExpectedBlockType)
712 {
713  UINT16 regionType = 0;
714  UINT16 numTileSets = 0;
715 
716  WINPR_ASSERT(context);
717  WINPR_ASSERT(context->priv);
718  WINPR_ASSERT(message);
719  WINPR_ASSERT(pExpectedBlockType);
720 
721  if (*pExpectedBlockType != WBT_REGION)
722  {
723  WLog_Print(context->priv->log, WLOG_ERROR, "message unexpected wants WBT_REGION");
724  return FALSE;
725  }
726 
727  *pExpectedBlockType = WBT_EXTENSION;
728 
729  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 3))
730  return FALSE;
731 
732  Stream_Seek_UINT8(s); /* regionFlags (1 byte) */
733  Stream_Read_UINT16(s, message->numRects); /* numRects (2 bytes) */
734 
735  if (message->numRects < 1)
736  {
737  /*
738  If numRects is zero the decoder must generate a rectangle with
739  coordinates (0, 0, width, height).
740  See [MS-RDPRFX] (revision >= 17.0) 2.2.2.3.3 TS_RFX_REGION
741  https://msdn.microsoft.com/en-us/library/ff635233.aspx
742  */
743  message->numRects = 1;
744  if (!rfx_resize_rects(message))
745  return FALSE;
746 
747  message->rects->x = 0;
748  message->rects->y = 0;
749  message->rects->width = context->width;
750  message->rects->height = context->height;
751  return TRUE;
752  }
753 
754  if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(context->priv->log, s, message->numRects, 8ull))
755  return FALSE;
756 
757  if (!rfx_resize_rects(message))
758  return FALSE;
759 
760  /* rects */
761  for (UINT16 i = 0; i < message->numRects; i++)
762  {
763  RFX_RECT* rect = rfx_message_get_rect(message, i);
764  /* RFX_RECT */
765  Stream_Read_UINT16(s, rect->x); /* x (2 bytes) */
766  Stream_Read_UINT16(s, rect->y); /* y (2 bytes) */
767  Stream_Read_UINT16(s, rect->width); /* width (2 bytes) */
768  Stream_Read_UINT16(s, rect->height); /* height (2 bytes) */
769  WLog_Print(context->priv->log, WLOG_DEBUG,
770  "rect %" PRIu16 " (x,y=%" PRIu16 ",%" PRIu16 " w,h=%" PRIu16 " %" PRIu16 ").", i,
771  rect->x, rect->y, rect->width, rect->height);
772  }
773 
774  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 4))
775  return FALSE;
776 
777  Stream_Read_UINT16(s, regionType); /*regionType (2 bytes): MUST be set to CBT_REGION (0xCAC1)*/
778  Stream_Read_UINT16(s, numTileSets); /*numTilesets (2 bytes): MUST be set to 0x0001.*/
779 
780  if (regionType != CBT_REGION)
781  {
782  WLog_Print(context->priv->log, WLOG_ERROR, "invalid region type 0x%04" PRIX16 "",
783  regionType);
784  return TRUE;
785  }
786 
787  if (numTileSets != 0x0001)
788  {
789  WLog_Print(context->priv->log, WLOG_ERROR, "invalid number of tilesets (%" PRIu16 ")",
790  numTileSets);
791  return FALSE;
792  }
793 
794  return TRUE;
795 }
796 
797 typedef struct
798 {
799  RFX_TILE* tile;
800  RFX_CONTEXT* context;
801 } RFX_TILE_PROCESS_WORK_PARAM;
802 
803 static INLINE void CALLBACK rfx_process_message_tile_work_callback(PTP_CALLBACK_INSTANCE instance,
804  void* context, PTP_WORK work)
805 {
806  RFX_TILE_PROCESS_WORK_PARAM* param = (RFX_TILE_PROCESS_WORK_PARAM*)context;
807  WINPR_ASSERT(param);
808  rfx_decode_rgb(param->context, param->tile, param->tile->data, 64 * 4);
809 }
810 
811 static INLINE BOOL rfx_allocate_tiles(RFX_MESSAGE* WINPR_RESTRICT message, size_t count,
812  BOOL allocOnly)
813 {
814  WINPR_ASSERT(message);
815 
816  RFX_TILE** tmpTiles = winpr_aligned_recalloc(message->tiles, count, sizeof(RFX_TILE*), 32);
817  if (!tmpTiles && (count != 0))
818  return FALSE;
819 
820  message->tiles = tmpTiles;
821  if (!allocOnly)
822  message->numTiles = count;
823  else
824  {
825  WINPR_ASSERT(message->numTiles <= count);
826  }
827  message->allocatedTiles = count;
828 
829  return TRUE;
830 }
831 
832 static INLINE BOOL rfx_process_message_tileset(RFX_CONTEXT* WINPR_RESTRICT context,
833  RFX_MESSAGE* WINPR_RESTRICT message,
834  wStream* WINPR_RESTRICT s,
835  UINT16* WINPR_RESTRICT pExpectedBlockType)
836 {
837  BOOL rc = 0;
838  size_t close_cnt = 0;
839  BYTE quant = 0;
840  RFX_TILE* tile = NULL;
841  UINT32* quants = NULL;
842  UINT16 subtype = 0;
843  UINT16 numTiles = 0;
844  UINT32 blockLen = 0;
845  UINT32 blockType = 0;
846  UINT32 tilesDataSize = 0;
847  PTP_WORK* work_objects = NULL;
848  RFX_TILE_PROCESS_WORK_PARAM* params = NULL;
849  void* pmem = NULL;
850 
851  WINPR_ASSERT(context);
852  WINPR_ASSERT(context->priv);
853  WINPR_ASSERT(message);
854  WINPR_ASSERT(pExpectedBlockType);
855 
856  if (*pExpectedBlockType != WBT_EXTENSION)
857  {
858  WLog_Print(context->priv->log, WLOG_ERROR, "message unexpected wants a tileset");
859  return FALSE;
860  }
861 
862  *pExpectedBlockType = WBT_FRAME_END;
863 
864  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 14))
865  return FALSE;
866 
867  Stream_Read_UINT16(s, subtype); /* subtype (2 bytes) must be set to CBT_TILESET (0xCAC2) */
868  if (subtype != CBT_TILESET)
869  {
870  WLog_Print(context->priv->log, WLOG_ERROR, "invalid subtype, expected CBT_TILESET.");
871  return FALSE;
872  }
873 
874  Stream_Seek_UINT16(s); /* idx (2 bytes), must be set to 0x0000 */
875  Stream_Seek_UINT16(s); /* properties (2 bytes) */
876  Stream_Read_UINT8(s, context->numQuant); /* numQuant (1 byte) */
877  Stream_Seek_UINT8(s); /* tileSize (1 byte), must be set to 0x40 */
878 
879  if (context->numQuant < 1)
880  {
881  WLog_Print(context->priv->log, WLOG_ERROR, "no quantization value.");
882  return FALSE;
883  }
884 
885  Stream_Read_UINT16(s, numTiles); /* numTiles (2 bytes) */
886  if (numTiles < 1)
887  {
888  /* Windows Server 2012 (not R2) can send empty tile sets */
889  return TRUE;
890  }
891 
892  Stream_Read_UINT32(s, tilesDataSize); /* tilesDataSize (4 bytes) */
893 
894  if (!(pmem =
895  winpr_aligned_recalloc(context->quants, context->numQuant, 10 * sizeof(UINT32), 32)))
896  return FALSE;
897 
898  quants = context->quants = (UINT32*)pmem;
899 
900  /* quantVals */
901  if (!Stream_CheckAndLogRequiredLengthOfSizeWLog(context->priv->log, s, context->numQuant, 5ull))
902  return FALSE;
903 
904  for (size_t i = 0; i < context->numQuant; i++)
905  {
906  /* RFX_CODEC_QUANT */
907  Stream_Read_UINT8(s, quant);
908  *quants++ = (quant & 0x0F);
909  *quants++ = (quant >> 4);
910  Stream_Read_UINT8(s, quant);
911  *quants++ = (quant & 0x0F);
912  *quants++ = (quant >> 4);
913  Stream_Read_UINT8(s, quant);
914  *quants++ = (quant & 0x0F);
915  *quants++ = (quant >> 4);
916  Stream_Read_UINT8(s, quant);
917  *quants++ = (quant & 0x0F);
918  *quants++ = (quant >> 4);
919  Stream_Read_UINT8(s, quant);
920  *quants++ = (quant & 0x0F);
921  *quants++ = (quant >> 4);
922  WLog_Print(context->priv->log, WLOG_DEBUG,
923  "quant %d (%" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32
924  " %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 ").",
925  i, context->quants[i * 10], context->quants[i * 10 + 1],
926  context->quants[i * 10 + 2], context->quants[i * 10 + 3],
927  context->quants[i * 10 + 4], context->quants[i * 10 + 5],
928  context->quants[i * 10 + 6], context->quants[i * 10 + 7],
929  context->quants[i * 10 + 8], context->quants[i * 10 + 9]);
930  }
931 
932  for (size_t i = 0; i < message->numTiles; i++)
933  {
934  ObjectPool_Return(context->priv->TilePool, message->tiles[i]);
935  message->tiles[i] = NULL;
936  }
937 
938  if (!rfx_allocate_tiles(message, numTiles, FALSE))
939  return FALSE;
940 
941  if (context->priv->UseThreads)
942  {
943  work_objects = (PTP_WORK*)winpr_aligned_calloc(message->numTiles, sizeof(PTP_WORK), 32);
944  params = (RFX_TILE_PROCESS_WORK_PARAM*)winpr_aligned_recalloc(
945  NULL, message->numTiles, sizeof(RFX_TILE_PROCESS_WORK_PARAM), 32);
946 
947  if (!work_objects)
948  {
949  winpr_aligned_free(params);
950  return FALSE;
951  }
952 
953  if (!params)
954  {
955  winpr_aligned_free(work_objects);
956  return FALSE;
957  }
958  }
959 
960  /* tiles */
961  close_cnt = 0;
962  rc = FALSE;
963 
964  if (Stream_GetRemainingLength(s) >= tilesDataSize)
965  {
966  rc = TRUE;
967  for (size_t i = 0; i < message->numTiles; i++)
968  {
969  wStream subBuffer;
970  wStream* sub = NULL;
971 
972  if (!(tile = (RFX_TILE*)ObjectPool_Take(context->priv->TilePool)))
973  {
974  WLog_Print(context->priv->log, WLOG_ERROR,
975  "RfxMessageTileSet failed to get tile from object pool");
976  rc = FALSE;
977  break;
978  }
979 
980  message->tiles[i] = tile;
981 
982  /* RFX_TILE */
983  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 6))
984  {
985  WLog_Print(context->priv->log, WLOG_ERROR,
986  "RfxMessageTileSet packet too small to read tile %d/%" PRIu16 "", i,
987  message->numTiles);
988  rc = FALSE;
989  break;
990  }
991 
992  sub = Stream_StaticInit(&subBuffer, Stream_Pointer(s), Stream_GetRemainingLength(s));
993  Stream_Read_UINT16(
994  sub, blockType); /* blockType (2 bytes), must be set to CBT_TILE (0xCAC3) */
995  Stream_Read_UINT32(sub, blockLen); /* blockLen (4 bytes) */
996 
997  if (!Stream_SafeSeek(s, blockLen))
998  {
999  rc = FALSE;
1000  break;
1001  }
1002  if ((blockLen < 6 + 13) ||
1003  (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, sub, blockLen - 6)))
1004  {
1005  WLog_Print(context->priv->log, WLOG_ERROR,
1006  "RfxMessageTileSet not enough bytes to read tile %d/%" PRIu16
1007  " with blocklen=%" PRIu32 "",
1008  i, message->numTiles, blockLen);
1009  rc = FALSE;
1010  break;
1011  }
1012 
1013  if (blockType != CBT_TILE)
1014  {
1015  WLog_Print(context->priv->log, WLOG_ERROR,
1016  "unknown block type 0x%" PRIX32 ", expected CBT_TILE (0xCAC3).",
1017  blockType);
1018  rc = FALSE;
1019  break;
1020  }
1021 
1022  Stream_Read_UINT8(sub, tile->quantIdxY); /* quantIdxY (1 byte) */
1023  Stream_Read_UINT8(sub, tile->quantIdxCb); /* quantIdxCb (1 byte) */
1024  Stream_Read_UINT8(sub, tile->quantIdxCr); /* quantIdxCr (1 byte) */
1025  if (tile->quantIdxY >= context->numQuant)
1026  {
1027  WLog_Print(context->priv->log, WLOG_ERROR,
1028  "quantIdxY %" PRIu8 " >= numQuant %" PRIu8, tile->quantIdxY,
1029  context->numQuant);
1030  rc = FALSE;
1031  break;
1032  }
1033  if (tile->quantIdxCb >= context->numQuant)
1034  {
1035  WLog_Print(context->priv->log, WLOG_ERROR,
1036  "quantIdxCb %" PRIu8 " >= numQuant %" PRIu8, tile->quantIdxCb,
1037  context->numQuant);
1038  rc = FALSE;
1039  break;
1040  }
1041  if (tile->quantIdxCr >= context->numQuant)
1042  {
1043  WLog_Print(context->priv->log, WLOG_ERROR,
1044  "quantIdxCr %" PRIu8 " >= numQuant %" PRIu8, tile->quantIdxCr,
1045  context->numQuant);
1046  rc = FALSE;
1047  break;
1048  }
1049 
1050  Stream_Read_UINT16(sub, tile->xIdx); /* xIdx (2 bytes) */
1051  Stream_Read_UINT16(sub, tile->yIdx); /* yIdx (2 bytes) */
1052  Stream_Read_UINT16(sub, tile->YLen); /* YLen (2 bytes) */
1053  Stream_Read_UINT16(sub, tile->CbLen); /* CbLen (2 bytes) */
1054  Stream_Read_UINT16(sub, tile->CrLen); /* CrLen (2 bytes) */
1055  Stream_GetPointer(sub, tile->YData);
1056  if (!Stream_SafeSeek(sub, tile->YLen))
1057  {
1058  rc = FALSE;
1059  break;
1060  }
1061  Stream_GetPointer(sub, tile->CbData);
1062  if (!Stream_SafeSeek(sub, tile->CbLen))
1063  {
1064  rc = FALSE;
1065  break;
1066  }
1067  Stream_GetPointer(sub, tile->CrData);
1068  if (!Stream_SafeSeek(sub, tile->CrLen))
1069  {
1070  rc = FALSE;
1071  break;
1072  }
1073  tile->x = tile->xIdx * 64;
1074  tile->y = tile->yIdx * 64;
1075 
1076  if (context->priv->UseThreads)
1077  {
1078  if (!params)
1079  {
1080  rc = FALSE;
1081  break;
1082  }
1083 
1084  params[i].context = context;
1085  params[i].tile = message->tiles[i];
1086 
1087  if (!(work_objects[i] =
1088  CreateThreadpoolWork(rfx_process_message_tile_work_callback,
1089  (void*)&params[i], &context->priv->ThreadPoolEnv)))
1090  {
1091  WLog_Print(context->priv->log, WLOG_ERROR, "CreateThreadpoolWork failed.");
1092  rc = FALSE;
1093  break;
1094  }
1095 
1096  SubmitThreadpoolWork(work_objects[i]);
1097  close_cnt = i + 1;
1098  }
1099  else
1100  {
1101  rfx_decode_rgb(context, tile, tile->data, 64 * 4);
1102  }
1103  }
1104  }
1105 
1106  if (context->priv->UseThreads)
1107  {
1108  for (size_t i = 0; i < close_cnt; i++)
1109  {
1110  WaitForThreadpoolWorkCallbacks(work_objects[i], FALSE);
1111  CloseThreadpoolWork(work_objects[i]);
1112  }
1113  }
1114 
1115  winpr_aligned_free(work_objects);
1116  winpr_aligned_free(params);
1117 
1118  for (size_t i = 0; i < message->numTiles; i++)
1119  {
1120  if (!(tile = message->tiles[i]))
1121  continue;
1122 
1123  tile->YLen = tile->CbLen = tile->CrLen = 0;
1124  tile->YData = tile->CbData = tile->CrData = NULL;
1125  }
1126 
1127  return rc;
1128 }
1129 
1130 BOOL rfx_process_message(RFX_CONTEXT* WINPR_RESTRICT context, const BYTE* WINPR_RESTRICT data,
1131  UINT32 length, UINT32 left, UINT32 top, BYTE* WINPR_RESTRICT dst,
1132  UINT32 dstFormat, UINT32 dstStride, UINT32 dstHeight,
1133  REGION16* WINPR_RESTRICT invalidRegion)
1134 {
1135  REGION16 updateRegion = { 0 };
1136  wStream inStream = { 0 };
1137  BOOL ok = TRUE;
1138 
1139  if (!context || !data || !length)
1140  return FALSE;
1141 
1142  WINPR_ASSERT(context->priv);
1143  RFX_MESSAGE* message = &context->currentMessage;
1144 
1145  wStream* s = Stream_StaticConstInit(&inStream, data, length);
1146 
1147  while (ok && Stream_GetRemainingLength(s) > 6)
1148  {
1149  wStream subStreamBuffer = { 0 };
1150  size_t extraBlockLen = 0;
1151  UINT32 blockLen = 0;
1152  UINT32 blockType = 0;
1153 
1154  /* RFX_BLOCKT */
1155  Stream_Read_UINT16(s, blockType); /* blockType (2 bytes) */
1156  Stream_Read_UINT32(s, blockLen); /* blockLen (4 bytes) */
1157  WLog_Print(context->priv->log, WLOG_DEBUG, "blockType 0x%" PRIX32 " blockLen %" PRIu32 "",
1158  blockType, blockLen);
1159 
1160  if (blockLen < 6)
1161  {
1162  WLog_Print(context->priv->log, WLOG_ERROR, "blockLen too small(%" PRIu32 ")", blockLen);
1163  return FALSE;
1164  }
1165 
1166  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, blockLen - 6))
1167  return FALSE;
1168 
1169  if (blockType > WBT_CONTEXT && context->decodedHeaderBlocks != RFX_DECODED_HEADERS)
1170  {
1171  WLog_Print(context->priv->log, WLOG_ERROR, "incomplete header blocks processing");
1172  return FALSE;
1173  }
1174 
1175  if (blockType >= WBT_CONTEXT && blockType <= WBT_EXTENSION)
1176  {
1177  /* RFX_CODEC_CHANNELT */
1178  UINT8 codecId = 0;
1179  UINT8 channelId = 0;
1180 
1181  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 2))
1182  return FALSE;
1183 
1184  extraBlockLen = 2;
1185  Stream_Read_UINT8(s, codecId); /* codecId (1 byte) must be set to 0x01 */
1186  Stream_Read_UINT8(s, channelId); /* channelId (1 byte) 0xFF or 0x00, see below */
1187 
1188  if (codecId != 0x01)
1189  {
1190  WLog_Print(context->priv->log, WLOG_ERROR, "invalid codecId 0x%02" PRIX8 "",
1191  codecId);
1192  return FALSE;
1193  }
1194 
1195  if (blockType == WBT_CONTEXT)
1196  {
1197  /* If the blockType is set to WBT_CONTEXT, then channelId MUST be set to 0xFF.*/
1198  if (channelId != 0xFF)
1199  {
1200  WLog_Print(context->priv->log, WLOG_ERROR,
1201  "invalid channelId 0x%02" PRIX8 " for blockType 0x%08" PRIX32 "",
1202  channelId, blockType);
1203  return FALSE;
1204  }
1205  }
1206  else
1207  {
1208  /* For all other values of blockType, channelId MUST be set to 0x00. */
1209  if (channelId != 0x00)
1210  {
1211  WLog_Print(context->priv->log, WLOG_ERROR,
1212  "invalid channelId 0x%02" PRIX8 " for blockType WBT_CONTEXT",
1213  channelId);
1214  return FALSE;
1215  }
1216  }
1217  }
1218 
1219  const size_t blockLenNoHeader = blockLen - 6;
1220  if (blockLenNoHeader < extraBlockLen)
1221  {
1222  WLog_Print(context->priv->log, WLOG_ERROR,
1223  "blockLen too small(%" PRIu32 "), must be >= 6 + %" PRIu16, blockLen,
1224  extraBlockLen);
1225  return FALSE;
1226  }
1227 
1228  const size_t subStreamLen = blockLenNoHeader - extraBlockLen;
1229  wStream* subStream = Stream_StaticInit(&subStreamBuffer, Stream_Pointer(s), subStreamLen);
1230  Stream_Seek(s, subStreamLen);
1231 
1232  switch (blockType)
1233  {
1234  /* Header messages:
1235  * The stream MUST start with the header messages and any of these headers can appear
1236  * in the stream at a later stage. The header messages can be repeated.
1237  */
1238  case WBT_SYNC:
1239  ok = rfx_process_message_sync(context, subStream);
1240  break;
1241 
1242  case WBT_CONTEXT:
1243  ok = rfx_process_message_context(context, subStream);
1244  break;
1245 
1246  case WBT_CODEC_VERSIONS:
1247  ok = rfx_process_message_codec_versions(context, subStream);
1248  break;
1249 
1250  case WBT_CHANNELS:
1251  ok = rfx_process_message_channels(context, subStream);
1252  break;
1253 
1254  /* Data messages:
1255  * The data associated with each encoded frame or image is always bracketed by the
1256  * TS_RFX_FRAME_BEGIN (section 2.2.2.3.1) and TS_RFX_FRAME_END (section 2.2.2.3.2)
1257  * messages. There MUST only be one TS_RFX_REGION (section 2.2.2.3.3) message per
1258  * frame and one TS_RFX_TILESET (section 2.2.2.3.4) message per TS_RFX_REGION.
1259  */
1260 
1261  case WBT_FRAME_BEGIN:
1262  ok = rfx_process_message_frame_begin(context, message, subStream,
1263  &context->expectedDataBlockType);
1264  break;
1265 
1266  case WBT_REGION:
1267  ok = rfx_process_message_region(context, message, subStream,
1268  &context->expectedDataBlockType);
1269  break;
1270 
1271  case WBT_EXTENSION:
1272  ok = rfx_process_message_tileset(context, message, subStream,
1273  &context->expectedDataBlockType);
1274  break;
1275 
1276  case WBT_FRAME_END:
1277  ok = rfx_process_message_frame_end(context, message, subStream,
1278  &context->expectedDataBlockType);
1279  break;
1280 
1281  default:
1282  WLog_Print(context->priv->log, WLOG_ERROR, "unknown blockType 0x%" PRIX32 "",
1283  blockType);
1284  return FALSE;
1285  }
1286  }
1287 
1288  if (ok)
1289  {
1290  UINT32 nbUpdateRects = 0;
1291  REGION16 clippingRects = { 0 };
1292  const RECTANGLE_16* updateRects = NULL;
1293  const DWORD formatSize = FreeRDPGetBytesPerPixel(context->pixel_format);
1294  const UINT32 dstWidth = dstStride / FreeRDPGetBytesPerPixel(dstFormat);
1295  region16_init(&clippingRects);
1296 
1297  WINPR_ASSERT(dstWidth <= UINT16_MAX);
1298  WINPR_ASSERT(dstHeight <= UINT16_MAX);
1299  for (UINT32 i = 0; i < message->numRects; i++)
1300  {
1301  RECTANGLE_16 clippingRect = { 0 };
1302  const RFX_RECT* rect = &(message->rects[i]);
1303 
1304  WINPR_ASSERT(left + rect->x <= UINT16_MAX);
1305  WINPR_ASSERT(top + rect->y <= UINT16_MAX);
1306  WINPR_ASSERT(clippingRect.left + rect->width <= UINT16_MAX);
1307  WINPR_ASSERT(clippingRect.top + rect->height <= UINT16_MAX);
1308 
1309  clippingRect.left = (UINT16)MIN(left + rect->x, dstWidth);
1310  clippingRect.top = (UINT16)MIN(top + rect->y, dstHeight);
1311  clippingRect.right = (UINT16)MIN(clippingRect.left + rect->width, dstWidth);
1312  clippingRect.bottom = (UINT16)MIN(clippingRect.top + rect->height, dstHeight);
1313  region16_union_rect(&clippingRects, &clippingRects, &clippingRect);
1314  }
1315 
1316  for (UINT32 i = 0; i < message->numTiles; i++)
1317  {
1318  RECTANGLE_16 updateRect = { 0 };
1319  const RFX_TILE* tile = rfx_message_get_tile(message, i);
1320 
1321  WINPR_ASSERT(left + tile->x <= UINT16_MAX);
1322  WINPR_ASSERT(top + tile->y <= UINT16_MAX);
1323 
1324  updateRect.left = (UINT16)left + tile->x;
1325  updateRect.top = (UINT16)top + tile->y;
1326  updateRect.right = updateRect.left + 64;
1327  updateRect.bottom = updateRect.top + 64;
1328  region16_init(&updateRegion);
1329  region16_intersect_rect(&updateRegion, &clippingRects, &updateRect);
1330  updateRects = region16_rects(&updateRegion, &nbUpdateRects);
1331 
1332  for (UINT32 j = 0; j < nbUpdateRects; j++)
1333  {
1334  const UINT32 stride = 64 * formatSize;
1335  const UINT32 nXDst = updateRects[j].left;
1336  const UINT32 nYDst = updateRects[j].top;
1337  const UINT32 nXSrc = nXDst - updateRect.left;
1338  const UINT32 nYSrc = nYDst - updateRect.top;
1339  const UINT32 nWidth = updateRects[j].right - updateRects[j].left;
1340  const UINT32 nHeight = updateRects[j].bottom - updateRects[j].top;
1341 
1342  if (!freerdp_image_copy_no_overlap(dst, dstFormat, dstStride, nXDst, nYDst, nWidth,
1343  nHeight, tile->data, context->pixel_format,
1344  stride, nXSrc, nYSrc, NULL, FREERDP_FLIP_NONE))
1345  {
1346  region16_uninit(&updateRegion);
1347  WLog_Print(context->priv->log, WLOG_ERROR,
1348  "nbUpdateRectx[%" PRIu32 " (%" PRIu32 ")] freerdp_image_copy failed",
1349  j, nbUpdateRects);
1350  return FALSE;
1351  }
1352 
1353  if (invalidRegion)
1354  region16_union_rect(invalidRegion, invalidRegion, &updateRects[j]);
1355  }
1356 
1357  region16_uninit(&updateRegion);
1358  }
1359 
1360  region16_uninit(&clippingRects);
1361  return TRUE;
1362  }
1363  else
1364  {
1365  rfx_message_free(context, message);
1366  context->currentMessage.freeArray = TRUE;
1367  }
1368 
1369  WLog_Print(context->priv->log, WLOG_ERROR, "failed");
1370  return FALSE;
1371 }
1372 
1373 const UINT32* rfx_message_get_quants(const RFX_MESSAGE* WINPR_RESTRICT message,
1374  UINT16* WINPR_RESTRICT numQuantVals)
1375 {
1376  WINPR_ASSERT(message);
1377  if (numQuantVals)
1378  *numQuantVals = message->numQuant;
1379  return message->quantVals;
1380 }
1381 
1382 const RFX_TILE** rfx_message_get_tiles(const RFX_MESSAGE* WINPR_RESTRICT message,
1383  UINT16* WINPR_RESTRICT numTiles)
1384 {
1385  WINPR_ASSERT(message);
1386  if (numTiles)
1387  *numTiles = message->numTiles;
1388 
1389  union
1390  {
1391  RFX_TILE** pp;
1392  const RFX_TILE** ppc;
1393  } cnv;
1394  cnv.pp = message->tiles;
1395  return cnv.ppc;
1396 }
1397 
1398 UINT16 rfx_message_get_tile_count(const RFX_MESSAGE* WINPR_RESTRICT message)
1399 {
1400  WINPR_ASSERT(message);
1401  return message->numTiles;
1402 }
1403 
1404 const RFX_RECT* rfx_message_get_rects(const RFX_MESSAGE* WINPR_RESTRICT message,
1405  UINT16* WINPR_RESTRICT numRects)
1406 {
1407  WINPR_ASSERT(message);
1408  if (numRects)
1409  *numRects = message->numRects;
1410  return message->rects;
1411 }
1412 
1413 UINT16 rfx_message_get_rect_count(const RFX_MESSAGE* WINPR_RESTRICT message)
1414 {
1415  WINPR_ASSERT(message);
1416  return message->numRects;
1417 }
1418 
1419 void rfx_message_free(RFX_CONTEXT* WINPR_RESTRICT context, RFX_MESSAGE* WINPR_RESTRICT message)
1420 {
1421  if (!message)
1422  return;
1423 
1424  winpr_aligned_free(message->rects);
1425 
1426  if (message->tiles)
1427  {
1428  for (size_t i = 0; i < message->numTiles; i++)
1429  {
1430  RFX_TILE* tile = message->tiles[i];
1431  if (!tile)
1432  continue;
1433 
1434  if (tile->YCbCrData)
1435  {
1436  BufferPool_Return(context->priv->BufferPool, tile->YCbCrData);
1437  tile->YCbCrData = NULL;
1438  }
1439 
1440  ObjectPool_Return(context->priv->TilePool, (void*)tile);
1441  }
1442 
1443  rfx_allocate_tiles(message, 0, FALSE);
1444  }
1445 
1446  const BOOL freeArray = message->freeArray;
1447  const RFX_MESSAGE empty = { 0 };
1448  *message = empty;
1449 
1450  if (!freeArray)
1451  winpr_aligned_free(message);
1452 }
1453 
1454 static INLINE void rfx_update_context_properties(RFX_CONTEXT* WINPR_RESTRICT context)
1455 {
1456  UINT16 properties = 0;
1457 
1458  WINPR_ASSERT(context);
1459  /* properties in tilesets: note that this has different format from the one in TS_RFX_CONTEXT */
1460  properties = 1; /* lt */
1461  properties |= (context->flags << 1); /* flags */
1462  properties |= (COL_CONV_ICT << 4); /* cct */
1463  properties |= (CLW_XFORM_DWT_53_A << 6); /* xft */
1464  properties |= ((context->mode == RLGR1 ? CLW_ENTROPY_RLGR1 : CLW_ENTROPY_RLGR3) << 10); /* et */
1465  properties |= (SCALAR_QUANTIZATION << 14); /* qt */
1466  context->properties = properties;
1467 }
1468 
1469 static INLINE void rfx_write_message_sync(const RFX_CONTEXT* WINPR_RESTRICT context,
1470  wStream* WINPR_RESTRICT s)
1471 {
1472  WINPR_ASSERT(context);
1473 
1474  Stream_Write_UINT16(s, WBT_SYNC); /* BlockT.blockType (2 bytes) */
1475  Stream_Write_UINT32(s, 12); /* BlockT.blockLen (4 bytes) */
1476  Stream_Write_UINT32(s, WF_MAGIC); /* magic (4 bytes) */
1477  Stream_Write_UINT16(s, WF_VERSION_1_0); /* version (2 bytes) */
1478 }
1479 
1480 static INLINE void rfx_write_message_codec_versions(const RFX_CONTEXT* WINPR_RESTRICT context,
1481  wStream* WINPR_RESTRICT s)
1482 {
1483  WINPR_ASSERT(context);
1484 
1485  Stream_Write_UINT16(s, WBT_CODEC_VERSIONS); /* BlockT.blockType (2 bytes) */
1486  Stream_Write_UINT32(s, 10); /* BlockT.blockLen (4 bytes) */
1487  Stream_Write_UINT8(s, 1); /* numCodecs (1 byte) */
1488  Stream_Write_UINT8(s, 1); /* codecs.codecId (1 byte) */
1489  Stream_Write_UINT16(s, WF_VERSION_1_0); /* codecs.version (2 bytes) */
1490 }
1491 
1492 static INLINE void rfx_write_message_channels(const RFX_CONTEXT* WINPR_RESTRICT context,
1493  wStream* WINPR_RESTRICT s)
1494 {
1495  WINPR_ASSERT(context);
1496 
1497  Stream_Write_UINT16(s, WBT_CHANNELS); /* BlockT.blockType (2 bytes) */
1498  Stream_Write_UINT32(s, 12); /* BlockT.blockLen (4 bytes) */
1499  Stream_Write_UINT8(s, 1); /* numChannels (1 byte) */
1500  Stream_Write_UINT8(s, 0); /* Channel.channelId (1 byte) */
1501  Stream_Write_UINT16(s, context->width); /* Channel.width (2 bytes) */
1502  Stream_Write_UINT16(s, context->height); /* Channel.height (2 bytes) */
1503 }
1504 
1505 static INLINE void rfx_write_message_context(RFX_CONTEXT* WINPR_RESTRICT context,
1506  wStream* WINPR_RESTRICT s)
1507 {
1508  UINT16 properties = 0;
1509  WINPR_ASSERT(context);
1510 
1511  Stream_Write_UINT16(s, WBT_CONTEXT); /* CodecChannelT.blockType (2 bytes) */
1512  Stream_Write_UINT32(s, 13); /* CodecChannelT.blockLen (4 bytes) */
1513  Stream_Write_UINT8(s, 1); /* CodecChannelT.codecId (1 byte) */
1514  Stream_Write_UINT8(s, 0xFF); /* CodecChannelT.channelId (1 byte) */
1515  Stream_Write_UINT8(s, 0); /* ctxId (1 byte) */
1516  Stream_Write_UINT16(s, CT_TILE_64x64); /* tileSize (2 bytes) */
1517  /* properties */
1518  properties = context->flags; /* flags */
1519  properties |= (COL_CONV_ICT << 3); /* cct */
1520  properties |= (CLW_XFORM_DWT_53_A << 5); /* xft */
1521  properties |= ((context->mode == RLGR1 ? CLW_ENTROPY_RLGR1 : CLW_ENTROPY_RLGR3) << 9); /* et */
1522  properties |= (SCALAR_QUANTIZATION << 13); /* qt */
1523  Stream_Write_UINT16(s, properties); /* properties (2 bytes) */
1524  rfx_update_context_properties(context);
1525 }
1526 
1527 static INLINE BOOL rfx_compose_message_header(RFX_CONTEXT* WINPR_RESTRICT context,
1528  wStream* WINPR_RESTRICT s)
1529 {
1530  WINPR_ASSERT(context);
1531  if (!Stream_EnsureRemainingCapacity(s, 12 + 10 + 12 + 13))
1532  return FALSE;
1533 
1534  rfx_write_message_sync(context, s);
1535  rfx_write_message_context(context, s);
1536  rfx_write_message_codec_versions(context, s);
1537  rfx_write_message_channels(context, s);
1538  return TRUE;
1539 }
1540 
1541 static INLINE size_t rfx_tile_length(const RFX_TILE* WINPR_RESTRICT tile)
1542 {
1543  WINPR_ASSERT(tile);
1544  return 19ull + tile->YLen + tile->CbLen + tile->CrLen;
1545 }
1546 
1547 static INLINE BOOL rfx_write_tile(wStream* WINPR_RESTRICT s, const RFX_TILE* WINPR_RESTRICT tile)
1548 {
1549  const size_t blockLen = rfx_tile_length(tile);
1550  if (blockLen > UINT32_MAX)
1551  return FALSE;
1552  if (!Stream_EnsureRemainingCapacity(s, blockLen))
1553  return FALSE;
1554 
1555  Stream_Write_UINT16(s, CBT_TILE); /* BlockT.blockType (2 bytes) */
1556  Stream_Write_UINT32(s, (UINT32)blockLen); /* BlockT.blockLen (4 bytes) */
1557  Stream_Write_UINT8(s, tile->quantIdxY); /* quantIdxY (1 byte) */
1558  Stream_Write_UINT8(s, tile->quantIdxCb); /* quantIdxCb (1 byte) */
1559  Stream_Write_UINT8(s, tile->quantIdxCr); /* quantIdxCr (1 byte) */
1560  Stream_Write_UINT16(s, tile->xIdx); /* xIdx (2 bytes) */
1561  Stream_Write_UINT16(s, tile->yIdx); /* yIdx (2 bytes) */
1562  Stream_Write_UINT16(s, tile->YLen); /* YLen (2 bytes) */
1563  Stream_Write_UINT16(s, tile->CbLen); /* CbLen (2 bytes) */
1564  Stream_Write_UINT16(s, tile->CrLen); /* CrLen (2 bytes) */
1565  Stream_Write(s, tile->YData, tile->YLen); /* YData */
1566  Stream_Write(s, tile->CbData, tile->CbLen); /* CbData */
1567  Stream_Write(s, tile->CrData, tile->CrLen); /* CrData */
1568  return TRUE;
1569 }
1570 
1571 struct S_RFX_TILE_COMPOSE_WORK_PARAM
1572 {
1573  RFX_TILE* tile;
1574  RFX_CONTEXT* context;
1575 };
1576 
1577 static INLINE void CALLBACK rfx_compose_message_tile_work_callback(PTP_CALLBACK_INSTANCE instance,
1578  void* context, PTP_WORK work)
1579 {
1580  RFX_TILE_COMPOSE_WORK_PARAM* param = (RFX_TILE_COMPOSE_WORK_PARAM*)context;
1581  WINPR_ASSERT(param);
1582  rfx_encode_rgb(param->context, param->tile);
1583 }
1584 
1585 static INLINE BOOL computeRegion(const RFX_RECT* WINPR_RESTRICT rects, size_t numRects,
1586  REGION16* WINPR_RESTRICT region, size_t width, size_t height)
1587 {
1588  const RECTANGLE_16 mainRect = { 0, 0, width, height };
1589 
1590  WINPR_ASSERT(rects);
1591  for (size_t i = 0; i < numRects; i++)
1592  {
1593  const RFX_RECT* rect = &rects[i];
1594  RECTANGLE_16 rect16 = { 0 };
1595  rect16.left = rect->x;
1596  rect16.top = rect->y;
1597  rect16.right = rect->x + rect->width;
1598  rect16.bottom = rect->y + rect->height;
1599 
1600  if (!region16_union_rect(region, region, &rect16))
1601  return FALSE;
1602  }
1603 
1604  return region16_intersect_rect(region, region, &mainRect);
1605 }
1606 
1607 #define TILE_NO(v) ((v) / 64)
1608 
1609 static INLINE BOOL setupWorkers(RFX_CONTEXT* WINPR_RESTRICT context, size_t nbTiles)
1610 {
1611  WINPR_ASSERT(context);
1612 
1613  RFX_CONTEXT_PRIV* priv = context->priv;
1614  WINPR_ASSERT(priv);
1615 
1616  void* pmem = NULL;
1617 
1618  if (!context->priv->UseThreads)
1619  return TRUE;
1620 
1621  if (!(pmem = winpr_aligned_recalloc(priv->workObjects, nbTiles, sizeof(PTP_WORK), 32)))
1622  return FALSE;
1623 
1624  priv->workObjects = (PTP_WORK*)pmem;
1625 
1626  if (!(pmem = winpr_aligned_recalloc(priv->tileWorkParams, nbTiles,
1627  sizeof(RFX_TILE_COMPOSE_WORK_PARAM), 32)))
1628  return FALSE;
1629 
1630  priv->tileWorkParams = (RFX_TILE_COMPOSE_WORK_PARAM*)pmem;
1631  return TRUE;
1632 }
1633 
1634 static INLINE BOOL rfx_ensure_tiles(RFX_MESSAGE* WINPR_RESTRICT message, size_t count)
1635 {
1636  WINPR_ASSERT(message);
1637 
1638  if (message->numTiles + count <= message->allocatedTiles)
1639  return TRUE;
1640 
1641  const size_t alloc = MAX(message->allocatedTiles + 1024, message->numTiles + count);
1642  return rfx_allocate_tiles(message, alloc, TRUE);
1643 }
1644 
1645 RFX_MESSAGE* rfx_encode_message(RFX_CONTEXT* WINPR_RESTRICT context,
1646  const RFX_RECT* WINPR_RESTRICT rects, size_t numRects,
1647  const BYTE* WINPR_RESTRICT data, UINT32 w, UINT32 h, size_t s)
1648 {
1649  const UINT32 width = w;
1650  const UINT32 height = h;
1651  const UINT32 scanline = (UINT32)s;
1652  RFX_MESSAGE* message = NULL;
1653  PTP_WORK* workObject = NULL;
1654  RFX_TILE_COMPOSE_WORK_PARAM* workParam = NULL;
1655  BOOL success = FALSE;
1656  REGION16 rectsRegion = { 0 };
1657  REGION16 tilesRegion = { 0 };
1658  RECTANGLE_16 currentTileRect = { 0 };
1659  const RECTANGLE_16* regionRect = NULL;
1660 
1661  WINPR_ASSERT(data);
1662  WINPR_ASSERT(rects);
1663  WINPR_ASSERT(numRects > 0);
1664  WINPR_ASSERT(w > 0);
1665  WINPR_ASSERT(h > 0);
1666  WINPR_ASSERT(s > 0);
1667 
1668  if (!(message = (RFX_MESSAGE*)winpr_aligned_calloc(1, sizeof(RFX_MESSAGE), 32)))
1669  return NULL;
1670 
1671  region16_init(&tilesRegion);
1672  region16_init(&rectsRegion);
1673 
1674  if (context->state == RFX_STATE_SEND_HEADERS)
1675  rfx_update_context_properties(context);
1676 
1677  message->frameIdx = context->frameIdx++;
1678 
1679  if (!context->numQuant)
1680  {
1681  WINPR_ASSERT(context->quants == NULL);
1682  if (!(context->quants =
1683  (UINT32*)winpr_aligned_malloc(sizeof(rfx_default_quantization_values), 32)))
1684  goto skip_encoding_loop;
1685 
1686  CopyMemory(context->quants, &rfx_default_quantization_values,
1687  sizeof(rfx_default_quantization_values));
1688  context->numQuant = 1;
1689  context->quantIdxY = 0;
1690  context->quantIdxCb = 0;
1691  context->quantIdxCr = 0;
1692  }
1693 
1694  message->numQuant = context->numQuant;
1695  message->quantVals = context->quants;
1696  const UINT32 bytesPerPixel = (context->bits_per_pixel / 8);
1697 
1698  if (!computeRegion(rects, numRects, &rectsRegion, width, height))
1699  goto skip_encoding_loop;
1700 
1701  const RECTANGLE_16* extents = region16_extents(&rectsRegion);
1702  WINPR_ASSERT((INT32)extents->right - extents->left > 0);
1703  WINPR_ASSERT((INT32)extents->bottom - extents->top > 0);
1704  const UINT32 maxTilesX = 1 + TILE_NO(extents->right - 1) - TILE_NO(extents->left);
1705  const UINT32 maxTilesY = 1 + TILE_NO(extents->bottom - 1) - TILE_NO(extents->top);
1706  const UINT32 maxNbTiles = maxTilesX * maxTilesY;
1707 
1708  if (!rfx_ensure_tiles(message, maxNbTiles))
1709  goto skip_encoding_loop;
1710 
1711  if (!setupWorkers(context, maxNbTiles))
1712  goto skip_encoding_loop;
1713 
1714  if (context->priv->UseThreads)
1715  {
1716  workObject = context->priv->workObjects;
1717  workParam = context->priv->tileWorkParams;
1718  }
1719 
1720  UINT32 regionNbRects = 0;
1721  regionRect = region16_rects(&rectsRegion, &regionNbRects);
1722 
1723  if (!(message->rects = winpr_aligned_calloc(regionNbRects, sizeof(RFX_RECT), 32)))
1724  goto skip_encoding_loop;
1725 
1726  message->numRects = regionNbRects;
1727 
1728  for (UINT32 i = 0; i < regionNbRects; i++, regionRect++)
1729  {
1730  RFX_RECT* rfxRect = &message->rects[i];
1731  UINT32 startTileX = regionRect->left / 64;
1732  UINT32 endTileX = (regionRect->right - 1) / 64;
1733  UINT32 startTileY = regionRect->top / 64;
1734  UINT32 endTileY = (regionRect->bottom - 1) / 64;
1735  rfxRect->x = regionRect->left;
1736  rfxRect->y = regionRect->top;
1737  rfxRect->width = (regionRect->right - regionRect->left);
1738  rfxRect->height = (regionRect->bottom - regionRect->top);
1739 
1740  for (UINT32 yIdx = startTileY, gridRelY = startTileY * 64; yIdx <= endTileY;
1741  yIdx++, gridRelY += 64)
1742  {
1743  UINT32 tileHeight = 64;
1744 
1745  if ((yIdx == endTileY) && (gridRelY + 64 > height))
1746  tileHeight = height - gridRelY;
1747 
1748  currentTileRect.top = gridRelY;
1749  currentTileRect.bottom = gridRelY + tileHeight;
1750 
1751  for (UINT32 xIdx = startTileX, gridRelX = startTileX * 64; xIdx <= endTileX;
1752  xIdx++, gridRelX += 64)
1753  {
1754  union
1755  {
1756  const BYTE* cpv;
1757  BYTE* pv;
1758  } cnv;
1759  int tileWidth = 64;
1760 
1761  if ((xIdx == endTileX) && (gridRelX + 64 > width))
1762  tileWidth = width - gridRelX;
1763 
1764  currentTileRect.left = gridRelX;
1765  currentTileRect.right = gridRelX + tileWidth;
1766 
1767  /* checks if this tile is already treated */
1768  if (region16_intersects_rect(&tilesRegion, &currentTileRect))
1769  continue;
1770 
1771  RFX_TILE* tile = (RFX_TILE*)ObjectPool_Take(context->priv->TilePool);
1772  if (!tile)
1773  goto skip_encoding_loop;
1774 
1775  tile->xIdx = xIdx;
1776  tile->yIdx = yIdx;
1777  tile->x = gridRelX;
1778  tile->y = gridRelY;
1779  tile->scanline = scanline;
1780  tile->width = tileWidth;
1781  tile->height = tileHeight;
1782  const UINT32 ax = gridRelX;
1783  const UINT32 ay = gridRelY;
1784 
1785  if (tile->data && tile->allocated)
1786  {
1787  winpr_aligned_free(tile->data);
1788  tile->allocated = FALSE;
1789  }
1790 
1791  /* Cast away const */
1792  cnv.cpv = &data[(ay * scanline) + (ax * bytesPerPixel)];
1793  tile->data = cnv.pv;
1794  tile->quantIdxY = context->quantIdxY;
1795  tile->quantIdxCb = context->quantIdxCb;
1796  tile->quantIdxCr = context->quantIdxCr;
1797  tile->YLen = tile->CbLen = tile->CrLen = 0;
1798 
1799  if (!(tile->YCbCrData = (BYTE*)BufferPool_Take(context->priv->BufferPool, -1)))
1800  goto skip_encoding_loop;
1801 
1802  tile->YData = &(tile->YCbCrData[((8192 + 32) * 0) + 16]);
1803  tile->CbData = &(tile->YCbCrData[((8192 + 32) * 1) + 16]);
1804  tile->CrData = &(tile->YCbCrData[((8192 + 32) * 2) + 16]);
1805 
1806  if (!rfx_ensure_tiles(message, 1))
1807  goto skip_encoding_loop;
1808  message->tiles[message->numTiles++] = tile;
1809 
1810  if (context->priv->UseThreads)
1811  {
1812  workParam->context = context;
1813  workParam->tile = tile;
1814 
1815  if (!(*workObject = CreateThreadpoolWork(rfx_compose_message_tile_work_callback,
1816  (void*)workParam,
1817  &context->priv->ThreadPoolEnv)))
1818  {
1819  goto skip_encoding_loop;
1820  }
1821 
1822  SubmitThreadpoolWork(*workObject);
1823  workObject++;
1824  workParam++;
1825  }
1826  else
1827  {
1828  rfx_encode_rgb(context, tile);
1829  }
1830 
1831  if (!region16_union_rect(&tilesRegion, &tilesRegion, &currentTileRect))
1832  goto skip_encoding_loop;
1833  } /* xIdx */
1834  } /* yIdx */
1835  } /* rects */
1836 
1837  success = TRUE;
1838 skip_encoding_loop:
1839 
1840  /* when using threads ensure all computations are done */
1841  if (success)
1842  {
1843  message->tilesDataSize = 0;
1844  workObject = context->priv->workObjects;
1845 
1846  for (UINT32 i = 0; i < message->numTiles; i++)
1847  {
1848  if (context->priv->UseThreads)
1849  {
1850  if (*workObject)
1851  {
1852  WaitForThreadpoolWorkCallbacks(*workObject, FALSE);
1853  CloseThreadpoolWork(*workObject);
1854  }
1855 
1856  workObject++;
1857  }
1858 
1859  const RFX_TILE* tile = message->tiles[i];
1860  message->tilesDataSize += rfx_tile_length(tile);
1861  }
1862 
1863  region16_uninit(&tilesRegion);
1864  region16_uninit(&rectsRegion);
1865 
1866  return message;
1867  }
1868 
1869  WLog_Print(context->priv->log, WLOG_ERROR, "failed");
1870 
1871  rfx_message_free(context, message);
1872  return NULL;
1873 }
1874 
1875 static INLINE BOOL rfx_clone_rects(RFX_MESSAGE* WINPR_RESTRICT dst,
1876  const RFX_MESSAGE* WINPR_RESTRICT src)
1877 {
1878  WINPR_ASSERT(dst);
1879  WINPR_ASSERT(src);
1880 
1881  WINPR_ASSERT(dst->rects == NULL);
1882  WINPR_ASSERT(dst->numRects == 0);
1883 
1884  if (src->numRects == 0)
1885  return TRUE;
1886 
1887  dst->rects = winpr_aligned_calloc(src->numRects, sizeof(RECTANGLE_16), 32);
1888  if (!dst->rects)
1889  return FALSE;
1890  dst->numRects = src->numRects;
1891  for (size_t x = 0; x < src->numRects; x++)
1892  {
1893  dst->rects[x] = src->rects[x];
1894  }
1895  return TRUE;
1896 }
1897 
1898 static INLINE BOOL rfx_clone_quants(RFX_MESSAGE* WINPR_RESTRICT dst,
1899  const RFX_MESSAGE* WINPR_RESTRICT src)
1900 {
1901  WINPR_ASSERT(dst);
1902  WINPR_ASSERT(src);
1903 
1904  WINPR_ASSERT(dst->quantVals == NULL);
1905  WINPR_ASSERT(dst->numQuant == 0);
1906 
1907  if (src->numQuant == 0)
1908  return TRUE;
1909 
1910  /* quantVals are part of context */
1911  dst->quantVals = src->quantVals;
1912  dst->numQuant = src->numQuant;
1913 
1914  return TRUE;
1915 }
1916 
1917 static INLINE RFX_MESSAGE* rfx_split_message(RFX_CONTEXT* WINPR_RESTRICT context,
1918  RFX_MESSAGE* WINPR_RESTRICT message,
1919  size_t* WINPR_RESTRICT numMessages, size_t maxDataSize)
1920 {
1921  WINPR_ASSERT(context);
1922  WINPR_ASSERT(message);
1923  WINPR_ASSERT(numMessages);
1924 
1925  maxDataSize -= 1024; /* reserve enough space for headers */
1926  *numMessages = ((message->tilesDataSize + maxDataSize) / maxDataSize) * 4ull;
1927 
1928  RFX_MESSAGE* messages =
1929  (RFX_MESSAGE*)winpr_aligned_calloc((*numMessages), sizeof(RFX_MESSAGE), 32);
1930  if (!messages)
1931  return NULL;
1932 
1933  UINT32 j = 0;
1934  for (UINT16 i = 0; i < message->numTiles; i++)
1935  {
1936  RFX_TILE* tile = message->tiles[i];
1937  RFX_MESSAGE* msg = &messages[j];
1938 
1939  WINPR_ASSERT(tile);
1940  WINPR_ASSERT(msg);
1941 
1942  const size_t tileDataSize = rfx_tile_length(tile);
1943 
1944  if ((msg->tilesDataSize + tileDataSize) > ((UINT32)maxDataSize))
1945  j++;
1946 
1947  if (msg->numTiles == 0)
1948  {
1949  msg->frameIdx = message->frameIdx + j;
1950  if (!rfx_clone_quants(msg, message))
1951  goto free_messages;
1952  if (!rfx_clone_rects(msg, message))
1953  goto free_messages;
1954  msg->freeArray = TRUE;
1955  if (!rfx_allocate_tiles(msg, message->numTiles, TRUE))
1956  goto free_messages;
1957  }
1958 
1959  msg->tilesDataSize += tileDataSize;
1960 
1961  WINPR_ASSERT(msg->numTiles < msg->allocatedTiles);
1962  msg->tiles[msg->numTiles++] = message->tiles[i];
1963  message->tiles[i] = NULL;
1964  }
1965 
1966  *numMessages = j + 1ULL;
1967  context->frameIdx += j;
1968  message->numTiles = 0;
1969  return messages;
1970 free_messages:
1971 
1972  for (size_t i = 0; i < j; i++)
1973  rfx_allocate_tiles(&messages[i], 0, FALSE);
1974 
1975  winpr_aligned_free(messages);
1976  return NULL;
1977 }
1978 
1979 const RFX_MESSAGE* rfx_message_list_get(const RFX_MESSAGE_LIST* WINPR_RESTRICT messages, size_t idx)
1980 {
1981  WINPR_ASSERT(messages);
1982  if (idx >= messages->count)
1983  return NULL;
1984  WINPR_ASSERT(messages->list);
1985  return &messages->list[idx];
1986 }
1987 
1988 void rfx_message_list_free(RFX_MESSAGE_LIST* messages)
1989 {
1990  if (!messages)
1991  return;
1992  for (size_t x = 0; x < messages->count; x++)
1993  rfx_message_free(messages->context, &messages->list[x]);
1994  free(messages);
1995 }
1996 
1997 static INLINE RFX_MESSAGE_LIST* rfx_message_list_new(RFX_CONTEXT* WINPR_RESTRICT context,
1998  RFX_MESSAGE* WINPR_RESTRICT messages,
1999  size_t count)
2000 {
2001  WINPR_ASSERT(context);
2002  RFX_MESSAGE_LIST* msg = calloc(1, sizeof(RFX_MESSAGE_LIST));
2003  WINPR_ASSERT(msg);
2004 
2005  msg->context = context;
2006  msg->count = count;
2007  msg->list = messages;
2008  return msg;
2009 }
2010 
2011 RFX_MESSAGE_LIST* rfx_encode_messages(RFX_CONTEXT* WINPR_RESTRICT context,
2012  const RFX_RECT* WINPR_RESTRICT rects, size_t numRects,
2013  const BYTE* WINPR_RESTRICT data, UINT32 width, UINT32 height,
2014  UINT32 scanline, size_t* WINPR_RESTRICT numMessages,
2015  size_t maxDataSize)
2016 {
2017  WINPR_ASSERT(context);
2018  WINPR_ASSERT(numMessages);
2019 
2020  RFX_MESSAGE* message =
2021  rfx_encode_message(context, rects, numRects, data, width, height, scanline);
2022  if (!message)
2023  return NULL;
2024 
2025  RFX_MESSAGE* list = rfx_split_message(context, message, numMessages, maxDataSize);
2026  rfx_message_free(context, message);
2027  if (!list)
2028  return NULL;
2029 
2030  return rfx_message_list_new(context, list, *numMessages);
2031 }
2032 
2033 static INLINE BOOL rfx_write_message_tileset(RFX_CONTEXT* WINPR_RESTRICT context,
2034  wStream* WINPR_RESTRICT s,
2035  const RFX_MESSAGE* WINPR_RESTRICT message)
2036 {
2037  WINPR_ASSERT(context);
2038  WINPR_ASSERT(message);
2039 
2040  const UINT32 blockLen = 22 + (message->numQuant * 5) + message->tilesDataSize;
2041 
2042  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2043  return FALSE;
2044 
2045  Stream_Write_UINT16(s, WBT_EXTENSION); /* CodecChannelT.blockType (2 bytes) */
2046  Stream_Write_UINT32(s, blockLen); /* set CodecChannelT.blockLen (4 bytes) */
2047  Stream_Write_UINT8(s, 1); /* CodecChannelT.codecId (1 byte) */
2048  Stream_Write_UINT8(s, 0); /* CodecChannelT.channelId (1 byte) */
2049  Stream_Write_UINT16(s, CBT_TILESET); /* subtype (2 bytes) */
2050  Stream_Write_UINT16(s, 0); /* idx (2 bytes) */
2051  Stream_Write_UINT16(s, context->properties); /* properties (2 bytes) */
2052  Stream_Write_UINT8(s, message->numQuant); /* numQuant (1 byte) */
2053  Stream_Write_UINT8(s, 0x40); /* tileSize (1 byte) */
2054  Stream_Write_UINT16(s, message->numTiles); /* numTiles (2 bytes) */
2055  Stream_Write_UINT32(s, message->tilesDataSize); /* tilesDataSize (4 bytes) */
2056 
2057  UINT32* quantVals = message->quantVals;
2058  for (size_t i = 0; i < message->numQuant * 5ul; i++)
2059  {
2060  WINPR_ASSERT(quantVals);
2061  Stream_Write_UINT8(s, quantVals[0] + (quantVals[1] << 4));
2062  quantVals += 2;
2063  }
2064 
2065  for (size_t i = 0; i < message->numTiles; i++)
2066  {
2067  RFX_TILE* tile = message->tiles[i];
2068  if (!tile)
2069  return FALSE;
2070 
2071  if (!rfx_write_tile(s, tile))
2072  return FALSE;
2073  }
2074 
2075 #ifdef WITH_DEBUG_RFX
2076  WLog_Print(context->priv->log, WLOG_DEBUG,
2077  "numQuant: %" PRIu16 " numTiles: %" PRIu16 " tilesDataSize: %" PRIu32 "",
2078  message->numQuant, message->numTiles, message->tilesDataSize);
2079 #endif
2080  return TRUE;
2081 }
2082 
2083 static INLINE BOOL rfx_write_message_frame_begin(RFX_CONTEXT* WINPR_RESTRICT context,
2084  wStream* WINPR_RESTRICT s,
2085  const RFX_MESSAGE* WINPR_RESTRICT message)
2086 {
2087  WINPR_ASSERT(context);
2088  WINPR_ASSERT(message);
2089 
2090  if (!Stream_EnsureRemainingCapacity(s, 14))
2091  return FALSE;
2092 
2093  Stream_Write_UINT16(s, WBT_FRAME_BEGIN); /* CodecChannelT.blockType */
2094  Stream_Write_UINT32(s, 14); /* CodecChannelT.blockLen */
2095  Stream_Write_UINT8(s, 1); /* CodecChannelT.codecId */
2096  Stream_Write_UINT8(s, 0); /* CodecChannelT.channelId */
2097  Stream_Write_UINT32(s, message->frameIdx); /* frameIdx */
2098  Stream_Write_UINT16(s, 1); /* numRegions */
2099  return TRUE;
2100 }
2101 
2102 static INLINE BOOL rfx_write_message_region(RFX_CONTEXT* WINPR_RESTRICT context,
2103  wStream* WINPR_RESTRICT s,
2104  const RFX_MESSAGE* WINPR_RESTRICT message)
2105 {
2106  WINPR_ASSERT(context);
2107  WINPR_ASSERT(message);
2108 
2109  const size_t blockLen = 15 + (message->numRects * 8);
2110  if (blockLen > UINT32_MAX)
2111  return FALSE;
2112 
2113  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2114  return FALSE;
2115 
2116  Stream_Write_UINT16(s, WBT_REGION); /* CodecChannelT.blockType (2 bytes) */
2117  Stream_Write_UINT32(s, (UINT32)blockLen); /* set CodecChannelT.blockLen (4 bytes) */
2118  Stream_Write_UINT8(s, 1); /* CodecChannelT.codecId (1 byte) */
2119  Stream_Write_UINT8(s, 0); /* CodecChannelT.channelId (1 byte) */
2120  Stream_Write_UINT8(s, 1); /* regionFlags (1 byte) */
2121  Stream_Write_UINT16(s, message->numRects); /* numRects (2 bytes) */
2122 
2123  for (UINT16 i = 0; i < message->numRects; i++)
2124  {
2125  const RFX_RECT* rect = rfx_message_get_rect_const(message, i);
2126  WINPR_ASSERT(rect);
2127 
2128  /* Clipping rectangles are relative to destLeft, destTop */
2129  Stream_Write_UINT16(s, rect->x); /* x (2 bytes) */
2130  Stream_Write_UINT16(s, rect->y); /* y (2 bytes) */
2131  Stream_Write_UINT16(s, rect->width); /* width (2 bytes) */
2132  Stream_Write_UINT16(s, rect->height); /* height (2 bytes) */
2133  }
2134 
2135  Stream_Write_UINT16(s, CBT_REGION); /* regionType (2 bytes) */
2136  Stream_Write_UINT16(s, 1); /* numTilesets (2 bytes) */
2137  return TRUE;
2138 }
2139 
2140 static INLINE BOOL rfx_write_message_frame_end(RFX_CONTEXT* WINPR_RESTRICT context,
2141  wStream* WINPR_RESTRICT s,
2142  const RFX_MESSAGE* WINPR_RESTRICT message)
2143 {
2144  WINPR_ASSERT(context);
2145  WINPR_ASSERT(message);
2146 
2147  if (!Stream_EnsureRemainingCapacity(s, 8))
2148  return FALSE;
2149 
2150  Stream_Write_UINT16(s, WBT_FRAME_END); /* CodecChannelT.blockType */
2151  Stream_Write_UINT32(s, 8); /* CodecChannelT.blockLen */
2152  Stream_Write_UINT8(s, 1); /* CodecChannelT.codecId */
2153  Stream_Write_UINT8(s, 0); /* CodecChannelT.channelId */
2154  return TRUE;
2155 }
2156 
2157 BOOL rfx_write_message(RFX_CONTEXT* WINPR_RESTRICT context, wStream* WINPR_RESTRICT s,
2158  const RFX_MESSAGE* WINPR_RESTRICT message)
2159 {
2160  WINPR_ASSERT(context);
2161  WINPR_ASSERT(message);
2162 
2163  if (context->state == RFX_STATE_SEND_HEADERS)
2164  {
2165  if (!rfx_compose_message_header(context, s))
2166  return FALSE;
2167 
2168  context->state = RFX_STATE_SEND_FRAME_DATA;
2169  }
2170 
2171  if (!rfx_write_message_frame_begin(context, s, message) ||
2172  !rfx_write_message_region(context, s, message) ||
2173  !rfx_write_message_tileset(context, s, message) ||
2174  !rfx_write_message_frame_end(context, s, message))
2175  {
2176  return FALSE;
2177  }
2178 
2179  return TRUE;
2180 }
2181 
2182 BOOL rfx_compose_message(RFX_CONTEXT* WINPR_RESTRICT context, wStream* WINPR_RESTRICT s,
2183  const RFX_RECT* WINPR_RESTRICT rects, size_t numRects,
2184  const BYTE* WINPR_RESTRICT data, UINT32 width, UINT32 height,
2185  UINT32 scanline)
2186 {
2187  WINPR_ASSERT(context);
2188  RFX_MESSAGE* message =
2189  rfx_encode_message(context, rects, numRects, data, width, height, scanline);
2190  if (!message)
2191  return FALSE;
2192 
2193  const BOOL ret = rfx_write_message(context, s, message);
2194  rfx_message_free(context, message);
2195  return ret;
2196 }
2197 
2198 BOOL rfx_context_set_mode(RFX_CONTEXT* WINPR_RESTRICT context, RLGR_MODE mode)
2199 {
2200  WINPR_ASSERT(context);
2201  context->mode = mode;
2202  return TRUE;
2203 }
2204 
2205 RLGR_MODE rfx_context_get_mode(RFX_CONTEXT* WINPR_RESTRICT context)
2206 {
2207  WINPR_ASSERT(context);
2208  return context->mode;
2209 }
2210 
2211 UINT32 rfx_context_get_frame_idx(const RFX_CONTEXT* WINPR_RESTRICT context)
2212 {
2213  WINPR_ASSERT(context);
2214  return context->frameIdx;
2215 }
2216 
2217 UINT32 rfx_message_get_frame_idx(const RFX_MESSAGE* WINPR_RESTRICT message)
2218 {
2219  WINPR_ASSERT(message);
2220  return message->frameIdx;
2221 }
2222 
2223 static INLINE BOOL rfx_write_progressive_wb_sync(RFX_CONTEXT* WINPR_RESTRICT rfx,
2224  wStream* WINPR_RESTRICT s)
2225 {
2226  const UINT32 blockLen = 12;
2227  WINPR_ASSERT(rfx);
2228  WINPR_ASSERT(s);
2229 
2230  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2231  return FALSE;
2232 
2233  Stream_Write_UINT16(s, PROGRESSIVE_WBT_SYNC); /* blockType (2 bytes) */
2234  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2235  Stream_Write_UINT32(s, 0xCACCACCA); /* magic (4 bytes) */
2236  Stream_Write_UINT16(s, 0x0100); /* version (2 bytes) */
2237  return TRUE;
2238 }
2239 
2240 static INLINE BOOL rfx_write_progressive_wb_context(RFX_CONTEXT* WINPR_RESTRICT rfx,
2241  wStream* WINPR_RESTRICT s)
2242 {
2243  const UINT32 blockLen = 10;
2244  WINPR_ASSERT(rfx);
2245  WINPR_ASSERT(s);
2246 
2247  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2248  return FALSE;
2249 
2250  Stream_Write_UINT16(s, PROGRESSIVE_WBT_CONTEXT); /* blockType (2 bytes) */
2251  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2252  Stream_Write_UINT8(s, 0); /* ctxId (1 byte) */
2253  Stream_Write_UINT16(s, 64); /* tileSize (2 bytes) */
2254  Stream_Write_UINT8(s, 0); /* flags (1 byte) */
2255  return TRUE;
2256 }
2257 
2258 static INLINE BOOL rfx_write_progressive_region(RFX_CONTEXT* WINPR_RESTRICT rfx,
2259  wStream* WINPR_RESTRICT s,
2260  const RFX_MESSAGE* WINPR_RESTRICT msg)
2261 {
2262  /* RFX_REGION */
2263  UINT32 blockLen = 18;
2264  UINT32 tilesDataSize = 0;
2265  const size_t start = Stream_GetPosition(s);
2266 
2267  WINPR_ASSERT(rfx);
2268  WINPR_ASSERT(s);
2269  WINPR_ASSERT(msg);
2270 
2271  blockLen += msg->numRects * 8;
2272  blockLen += msg->numQuant * 5;
2273  tilesDataSize = msg->numTiles * 22UL;
2274  for (UINT16 i = 0; i < msg->numTiles; i++)
2275  {
2276  const RFX_TILE* tile = msg->tiles[i];
2277  WINPR_ASSERT(tile);
2278  tilesDataSize += tile->YLen + tile->CbLen + tile->CrLen;
2279  }
2280  blockLen += tilesDataSize;
2281 
2282  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2283  return FALSE;
2284 
2285  Stream_Write_UINT16(s, PROGRESSIVE_WBT_REGION); /* blockType (2 bytes) */
2286  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2287  Stream_Write_UINT8(s, 64); /* tileSize (1 byte) */
2288  Stream_Write_UINT16(s, msg->numRects); /* numRects (2 bytes) */
2289  WINPR_ASSERT(msg->numQuant <= UINT8_MAX);
2290  Stream_Write_UINT8(s, (UINT8)msg->numQuant); /* numQuant (1 byte) */
2291  Stream_Write_UINT8(s, 0); /* numProgQuant (1 byte) */
2292  Stream_Write_UINT8(s, 0); /* flags (1 byte) */
2293  Stream_Write_UINT16(s, msg->numTiles); /* numTiles (2 bytes) */
2294  Stream_Write_UINT32(s, tilesDataSize); /* tilesDataSize (4 bytes) */
2295 
2296  for (UINT16 i = 0; i < msg->numRects; i++)
2297  {
2298  /* TS_RFX_RECT */
2299  const RFX_RECT* r = &msg->rects[i];
2300  Stream_Write_UINT16(s, r->x); /* x (2 bytes) */
2301  Stream_Write_UINT16(s, r->y); /* y (2 bytes) */
2302  Stream_Write_UINT16(s, r->width); /* width (2 bytes) */
2303  Stream_Write_UINT16(s, r->height); /* height (2 bytes) */
2304  }
2305 
2314  for (UINT16 i = 0; i < msg->numQuant; i++)
2315  {
2316  const UINT32* qv = &msg->quantVals[10ULL * i];
2317  /* RFX_COMPONENT_CODEC_QUANT */
2318  Stream_Write_UINT8(s, (UINT8)(qv[0] + (qv[2] << 4))); /* LL3 (4-bit), HL3 (4-bit) */
2319  Stream_Write_UINT8(s, (UINT8)(qv[1] + (qv[3] << 4))); /* LH3 (4-bit), HH3 (4-bit) */
2320  Stream_Write_UINT8(s, (UINT8)(qv[5] + (qv[4] << 4))); /* HL2 (4-bit), LH2 (4-bit) */
2321  Stream_Write_UINT8(s, (UINT8)(qv[6] + (qv[8] << 4))); /* HH2 (4-bit), HL1 (4-bit) */
2322  Stream_Write_UINT8(s, (UINT8)(qv[7] + (qv[9] << 4))); /* LH1 (4-bit), HH1 (4-bit) */
2323  }
2324 
2325  for (UINT16 i = 0; i < msg->numTiles; i++)
2326  {
2327  const RFX_TILE* tile = msg->tiles[i];
2328  if (!rfx_write_progressive_tile_simple(rfx, s, tile))
2329  return FALSE;
2330  }
2331 
2332  const size_t end = Stream_GetPosition(s);
2333  const size_t used = end - start;
2334  return (used == blockLen);
2335 }
2336 
2337 static INLINE BOOL rfx_write_progressive_frame_begin(RFX_CONTEXT* WINPR_RESTRICT rfx,
2338  wStream* WINPR_RESTRICT s,
2339  const RFX_MESSAGE* WINPR_RESTRICT msg)
2340 {
2341  const UINT32 blockLen = 12;
2342  WINPR_ASSERT(rfx);
2343  WINPR_ASSERT(s);
2344  WINPR_ASSERT(msg);
2345 
2346  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2347  return FALSE;
2348 
2349  Stream_Write_UINT16(s, PROGRESSIVE_WBT_FRAME_BEGIN); /* blockType (2 bytes) */
2350  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2351  Stream_Write_UINT32(s, msg->frameIdx); /* frameIndex (4 bytes) */
2352  Stream_Write_UINT16(s, 1); /* regionCount (2 bytes) */
2353 
2354  return TRUE;
2355 }
2356 
2357 static INLINE BOOL rfx_write_progressive_frame_end(RFX_CONTEXT* WINPR_RESTRICT rfx,
2358  wStream* WINPR_RESTRICT s)
2359 {
2360  const UINT32 blockLen = 6;
2361  WINPR_ASSERT(rfx);
2362  WINPR_ASSERT(s);
2363 
2364  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2365  return FALSE;
2366 
2367  Stream_Write_UINT16(s, PROGRESSIVE_WBT_FRAME_END); /* blockType (2 bytes) */
2368  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2369 
2370  return TRUE;
2371 }
2372 
2373 static INLINE BOOL rfx_write_progressive_tile_simple(RFX_CONTEXT* WINPR_RESTRICT rfx,
2374  wStream* WINPR_RESTRICT s,
2375  const RFX_TILE* WINPR_RESTRICT tile)
2376 {
2377  UINT32 blockLen = 0;
2378  WINPR_ASSERT(rfx);
2379  WINPR_ASSERT(s);
2380  WINPR_ASSERT(tile);
2381 
2382  blockLen = 22 + tile->YLen + tile->CbLen + tile->CrLen;
2383  if (!Stream_EnsureRemainingCapacity(s, blockLen))
2384  return FALSE;
2385 
2386  Stream_Write_UINT16(s, PROGRESSIVE_WBT_TILE_SIMPLE); /* blockType (2 bytes) */
2387  Stream_Write_UINT32(s, blockLen); /* blockLen (4 bytes) */
2388  Stream_Write_UINT8(s, tile->quantIdxY); /* quantIdxY (1 byte) */
2389  Stream_Write_UINT8(s, tile->quantIdxCb); /* quantIdxCb (1 byte) */
2390  Stream_Write_UINT8(s, tile->quantIdxCr); /* quantIdxCr (1 byte) */
2391  Stream_Write_UINT16(s, tile->xIdx); /* xIdx (2 bytes) */
2392  Stream_Write_UINT16(s, tile->yIdx); /* yIdx (2 bytes) */
2393  Stream_Write_UINT8(s, 0); /* flags (1 byte) */
2394  Stream_Write_UINT16(s, tile->YLen); /* YLen (2 bytes) */
2395  Stream_Write_UINT16(s, tile->CbLen); /* CbLen (2 bytes) */
2396  Stream_Write_UINT16(s, tile->CrLen); /* CrLen (2 bytes) */
2397  Stream_Write_UINT16(s, 0); /* tailLen (2 bytes) */
2398  Stream_Write(s, tile->YData, tile->YLen); /* YData */
2399  Stream_Write(s, tile->CbData, tile->CbLen); /* CbData */
2400  Stream_Write(s, tile->CrData, tile->CrLen); /* CrData */
2401 
2402  return TRUE;
2403 }
2404 
2405 const char* rfx_get_progressive_block_type_string(UINT16 blockType)
2406 {
2407  switch (blockType)
2408  {
2409  case PROGRESSIVE_WBT_SYNC:
2410  return "PROGRESSIVE_WBT_SYNC";
2411 
2412  case PROGRESSIVE_WBT_FRAME_BEGIN:
2413  return "PROGRESSIVE_WBT_FRAME_BEGIN";
2414 
2415  case PROGRESSIVE_WBT_FRAME_END:
2416  return "PROGRESSIVE_WBT_FRAME_END";
2417 
2418  case PROGRESSIVE_WBT_CONTEXT:
2419  return "PROGRESSIVE_WBT_CONTEXT";
2420 
2421  case PROGRESSIVE_WBT_REGION:
2422  return "PROGRESSIVE_WBT_REGION";
2423 
2424  case PROGRESSIVE_WBT_TILE_SIMPLE:
2425  return "PROGRESSIVE_WBT_TILE_SIMPLE";
2426 
2427  case PROGRESSIVE_WBT_TILE_FIRST:
2428  return "PROGRESSIVE_WBT_TILE_FIRST";
2429 
2430  case PROGRESSIVE_WBT_TILE_UPGRADE:
2431  return "PROGRESSIVE_WBT_TILE_UPGRADE";
2432 
2433  default:
2434  return "PROGRESSIVE_WBT_UNKNOWN";
2435  }
2436 }
2437 
2438 BOOL rfx_write_message_progressive_simple(RFX_CONTEXT* WINPR_RESTRICT context,
2439  wStream* WINPR_RESTRICT s,
2440  const RFX_MESSAGE* WINPR_RESTRICT msg)
2441 {
2442  WINPR_ASSERT(s);
2443  WINPR_ASSERT(msg);
2444  WINPR_ASSERT(context);
2445 
2446  if (context->mode != RLGR1)
2447  {
2448  WLog_ERR(TAG, "error, RLGR1 mode is required!");
2449  return FALSE;
2450  }
2451 
2452  if (!rfx_write_progressive_wb_sync(context, s))
2453  return FALSE;
2454 
2455  if (!rfx_write_progressive_wb_context(context, s))
2456  return FALSE;
2457 
2458  if (!rfx_write_progressive_frame_begin(context, s, msg))
2459  return FALSE;
2460 
2461  if (!rfx_write_progressive_region(context, s, msg))
2462  return FALSE;
2463 
2464  if (!rfx_write_progressive_frame_end(context, s))
2465  return FALSE;
2466 
2467  return TRUE;
2468 }
Definition: rfx.h:44
Definition: rfx.h:52
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57