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