Coverage Report

Created: 2023-09-25 06:56

/src/FreeRDP/libfreerdp/codec/nsc.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * NSCodec Codec
4
 *
5
 * Copyright 2011 Samsung, Author Jiten Pathy
6
 * Copyright 2012 Vic Lee
7
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
8
 * Copyright 2016 Thincast Technologies GmbH
9
 *
10
 * Licensed under the Apache License, Version 2.0 (the "License");
11
 * you may not use this file except in compliance with the License.
12
 * You may obtain a copy of the License at
13
 *
14
 *   http://www.apache.org/licenses/LICENSE-2.0
15
 *
16
 * Unless required by applicable law or agreed to in writing, software
17
 * distributed under the License is distributed on an "AS IS" BASIS,
18
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19
 * See the License for the specific language governing permissions and
20
 * limitations under the License.
21
 */
22
23
#include <freerdp/config.h>
24
25
#include <stdio.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include <winpr/crt.h>
30
31
#include <freerdp/codec/nsc.h>
32
#include <freerdp/codec/color.h>
33
34
#include "nsc_types.h"
35
#include "nsc_encode.h"
36
37
#include "nsc_sse2.h"
38
39
#include <freerdp/log.h>
40
#define TAG FREERDP_TAG("codec.nsc")
41
42
#ifndef NSC_INIT_SIMD
43
#define NSC_INIT_SIMD(_nsc_context) \
44
  do                              \
45
  {                               \
46
  } while (0)
47
#endif
48
49
static BOOL nsc_decode(NSC_CONTEXT* context)
50
0
{
51
0
  UINT16 x;
52
0
  UINT16 y;
53
0
  UINT16 rw;
54
0
  BYTE shift;
55
0
  BYTE* bmpdata;
56
0
  size_t pos = 0;
57
58
0
  if (!context)
59
0
    return FALSE;
60
61
0
  rw = ROUND_UP_TO(context->width, 8);
62
0
  shift = context->ColorLossLevel - 1; /* colorloss recovery + YCoCg shift */
63
0
  bmpdata = context->BitmapData;
64
65
0
  if (!bmpdata)
66
0
    return FALSE;
67
68
0
  for (y = 0; y < context->height; y++)
69
0
  {
70
0
    const BYTE* yplane;
71
0
    const BYTE* coplane;
72
0
    const BYTE* cgplane;
73
0
    const BYTE* aplane = context->priv->PlaneBuffers[3] + y * context->width; /* A */
74
75
0
    if (context->ChromaSubsamplingLevel)
76
0
    {
77
0
      yplane = context->priv->PlaneBuffers[0] + y * rw;                /* Y */
78
0
      coplane = context->priv->PlaneBuffers[1] + (y >> 1) * (rw >> 1); /* Co, supersampled */
79
0
      cgplane = context->priv->PlaneBuffers[2] + (y >> 1) * (rw >> 1); /* Cg, supersampled */
80
0
    }
81
0
    else
82
0
    {
83
0
      yplane = context->priv->PlaneBuffers[0] + y * context->width;  /* Y */
84
0
      coplane = context->priv->PlaneBuffers[1] + y * context->width; /* Co */
85
0
      cgplane = context->priv->PlaneBuffers[2] + y * context->width; /* Cg */
86
0
    }
87
88
0
    for (x = 0; x < context->width; x++)
89
0
    {
90
0
      INT16 y_val = (INT16)*yplane;
91
0
      INT16 co_val = (INT16)(INT8)(*coplane << shift);
92
0
      INT16 cg_val = (INT16)(INT8)(*cgplane << shift);
93
0
      INT16 r_val = y_val + co_val - cg_val;
94
0
      INT16 g_val = y_val + cg_val;
95
0
      INT16 b_val = y_val - co_val - cg_val;
96
97
0
      if (pos + 4 > context->BitmapDataLength)
98
0
        return FALSE;
99
100
0
      pos += 4;
101
0
      *bmpdata++ = MINMAX(b_val, 0, 0xFF);
102
0
      *bmpdata++ = MINMAX(g_val, 0, 0xFF);
103
0
      *bmpdata++ = MINMAX(r_val, 0, 0xFF);
104
0
      *bmpdata++ = *aplane;
105
0
      yplane++;
106
0
      coplane += (context->ChromaSubsamplingLevel ? x % 2 : 1);
107
0
      cgplane += (context->ChromaSubsamplingLevel ? x % 2 : 1);
108
0
      aplane++;
109
0
    }
110
0
  }
111
112
0
  return TRUE;
113
0
}
114
115
static BOOL nsc_rle_decode(const BYTE* in, size_t inSize, BYTE* out, UINT32 outSize,
116
                           UINT32 originalSize)
117
0
{
118
0
  UINT32 left = originalSize;
119
120
0
  while (left > 4)
121
0
  {
122
0
    if (inSize < 1)
123
0
      return FALSE;
124
0
    inSize--;
125
126
0
    const BYTE value = *in++;
127
0
    UINT32 len = 0;
128
129
0
    if (left == 5)
130
0
    {
131
0
      if (outSize < 1)
132
0
        return FALSE;
133
134
0
      outSize--;
135
0
      *out++ = value;
136
0
      left--;
137
0
    }
138
0
    else if (inSize < 1)
139
0
      return FALSE;
140
0
    else if (value == *in)
141
0
    {
142
0
      inSize--;
143
0
      in++;
144
145
0
      if (inSize < 1)
146
0
        return FALSE;
147
0
      else if (*in < 0xFF)
148
0
      {
149
0
        inSize--;
150
0
        len = (UINT32)*in++;
151
0
        len += 2;
152
0
      }
153
0
      else
154
0
      {
155
0
        if (inSize < 5)
156
0
          return FALSE;
157
0
        inSize -= 5;
158
0
        in++;
159
0
        len = ((UINT32)(*in++));
160
0
        len |= ((UINT32)(*in++)) << 8U;
161
0
        len |= ((UINT32)(*in++)) << 16U;
162
0
        len |= ((UINT32)(*in++)) << 24U;
163
0
      }
164
165
0
      if (outSize < len)
166
0
        return FALSE;
167
168
0
      outSize -= len;
169
0
      FillMemory(out, len, value);
170
0
      out += len;
171
0
      left -= len;
172
0
    }
173
0
    else
174
0
    {
175
0
      if (outSize < 1)
176
0
        return FALSE;
177
178
0
      outSize--;
179
0
      *out++ = value;
180
0
      left--;
181
0
    }
182
0
  }
183
184
0
  if ((outSize < 4) || (left < 4))
185
0
    return FALSE;
186
187
0
  if (inSize < 4)
188
0
    return FALSE;
189
0
  memcpy(out, in, 4);
190
0
  return TRUE;
191
0
}
192
193
static BOOL nsc_rle_decompress_data(NSC_CONTEXT* context)
194
0
{
195
0
  if (!context)
196
0
    return FALSE;
197
198
0
  const BYTE* rle = context->Planes;
199
0
  size_t rleSize = context->PlanesSize;
200
0
  WINPR_ASSERT(rle);
201
202
0
  for (size_t i = 0; i < 4; i++)
203
0
  {
204
0
    const UINT32 originalSize = context->OrgByteCount[i];
205
0
    const UINT32 planeSize = context->PlaneByteCount[i];
206
207
0
    if (rleSize < planeSize)
208
0
      return FALSE;
209
210
0
    if (planeSize == 0)
211
0
    {
212
0
      if (context->priv->PlaneBuffersLength < originalSize)
213
0
        return FALSE;
214
215
0
      FillMemory(context->priv->PlaneBuffers[i], originalSize, 0xFF);
216
0
    }
217
0
    else if (planeSize < originalSize)
218
0
    {
219
0
      if (!nsc_rle_decode(rle, rleSize, context->priv->PlaneBuffers[i],
220
0
                          context->priv->PlaneBuffersLength, originalSize))
221
0
        return FALSE;
222
0
    }
223
0
    else
224
0
    {
225
0
      if (context->priv->PlaneBuffersLength < originalSize)
226
0
        return FALSE;
227
228
0
      if (rleSize < originalSize)
229
0
        return FALSE;
230
231
0
      CopyMemory(context->priv->PlaneBuffers[i], rle, originalSize);
232
0
    }
233
234
0
    rle += planeSize;
235
0
  }
236
237
0
  return TRUE;
238
0
}
239
240
static BOOL nsc_stream_initialize(NSC_CONTEXT* context, wStream* s)
241
0
{
242
0
  WINPR_ASSERT(context);
243
0
  WINPR_ASSERT(context->priv);
244
0
  if (!Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, 20))
245
0
    return FALSE;
246
247
0
  size_t total = 0;
248
0
  for (size_t i = 0; i < 4; i++)
249
0
  {
250
0
    Stream_Read_UINT32(s, context->PlaneByteCount[i]);
251
0
    total += context->PlaneByteCount[i];
252
0
  }
253
254
0
  Stream_Read_UINT8(s, context->ColorLossLevel);         /* ColorLossLevel (1 byte) */
255
0
  Stream_Read_UINT8(s, context->ChromaSubsamplingLevel); /* ChromaSubsamplingLevel (1 byte) */
256
0
  Stream_Seek(s, 2);                                     /* Reserved (2 bytes) */
257
0
  context->Planes = Stream_Pointer(s);
258
0
  context->PlanesSize = total;
259
0
  return Stream_CheckAndLogRequiredLengthWLog(context->priv->log, s, total);
260
0
}
261
262
static BOOL nsc_context_initialize(NSC_CONTEXT* context, wStream* s)
263
0
{
264
0
  if (!nsc_stream_initialize(context, s))
265
0
    return FALSE;
266
267
0
  const size_t blength = context->width * context->height * 4ull;
268
269
0
  if (!context->BitmapData || (blength > context->BitmapDataLength))
270
0
  {
271
0
    void* tmp = winpr_aligned_recalloc(context->BitmapData, blength + 16, sizeof(BYTE), 32);
272
273
0
    if (!tmp)
274
0
      return FALSE;
275
276
0
    context->BitmapData = tmp;
277
0
    context->BitmapDataLength = blength;
278
0
  }
279
280
0
  const UINT32 tempWidth = ROUND_UP_TO(context->width, 8);
281
0
  const UINT32 tempHeight = ROUND_UP_TO(context->height, 2);
282
  /* The maximum length a decoded plane can reach in all cases */
283
0
  const size_t plength = 1ull * tempWidth * tempHeight;
284
285
0
  if (plength > context->priv->PlaneBuffersLength)
286
0
  {
287
0
    for (size_t i = 0; i < 4; i++)
288
0
    {
289
0
      void* tmp = (BYTE*)winpr_aligned_recalloc(context->priv->PlaneBuffers[i], plength,
290
0
                                                sizeof(BYTE), 32);
291
292
0
      if (!tmp)
293
0
        return FALSE;
294
295
0
      context->priv->PlaneBuffers[i] = tmp;
296
0
    }
297
298
0
    context->priv->PlaneBuffersLength = plength;
299
0
  }
300
301
0
  for (size_t i = 0; i < 4; i++)
302
0
    context->OrgByteCount[i] = context->width * context->height;
303
304
0
  if (context->ChromaSubsamplingLevel)
305
0
  {
306
0
    context->OrgByteCount[0] = tempWidth * context->height;
307
0
    context->OrgByteCount[1] = (tempWidth >> 1) * (tempHeight >> 1);
308
0
    context->OrgByteCount[2] = context->OrgByteCount[1];
309
0
  }
310
311
0
  return TRUE;
312
0
}
313
314
static void nsc_profiler_print(NSC_CONTEXT_PRIV* priv)
315
0
{
316
0
  WINPR_UNUSED(priv);
317
318
0
  PROFILER_PRINT_HEADER
319
0
  PROFILER_PRINT(priv->prof_nsc_rle_decompress_data)
320
0
  PROFILER_PRINT(priv->prof_nsc_decode)
321
0
  PROFILER_PRINT(priv->prof_nsc_rle_compress_data)
322
0
  PROFILER_PRINT(priv->prof_nsc_encode)
323
0
  PROFILER_PRINT_FOOTER
324
0
}
325
326
BOOL nsc_context_reset(NSC_CONTEXT* context, UINT32 width, UINT32 height)
327
0
{
328
0
  if (!context)
329
0
    return FALSE;
330
331
0
  if ((width > UINT16_MAX) || (height > UINT16_MAX))
332
0
    return FALSE;
333
334
0
  context->width = (UINT16)width;
335
0
  context->height = (UINT16)height;
336
0
  return TRUE;
337
0
}
338
339
NSC_CONTEXT* nsc_context_new(void)
340
0
{
341
0
  NSC_CONTEXT* context = (NSC_CONTEXT*)winpr_aligned_calloc(1, sizeof(NSC_CONTEXT), 32);
342
343
0
  if (!context)
344
0
    return NULL;
345
346
0
  context->priv = (NSC_CONTEXT_PRIV*)winpr_aligned_calloc(1, sizeof(NSC_CONTEXT_PRIV), 32);
347
348
0
  if (!context->priv)
349
0
    goto error;
350
351
0
  context->priv->log = WLog_Get("com.freerdp.codec.nsc");
352
0
  WLog_OpenAppender(context->priv->log);
353
0
  context->BitmapData = NULL;
354
0
  context->decode = nsc_decode;
355
0
  context->encode = nsc_encode;
356
357
0
  PROFILER_CREATE(context->priv->prof_nsc_rle_decompress_data, "nsc_rle_decompress_data")
358
0
  PROFILER_CREATE(context->priv->prof_nsc_decode, "nsc_decode")
359
0
  PROFILER_CREATE(context->priv->prof_nsc_rle_compress_data, "nsc_rle_compress_data")
360
0
  PROFILER_CREATE(context->priv->prof_nsc_encode, "nsc_encode")
361
  /* Default encoding parameters */
362
0
  context->ColorLossLevel = 3;
363
0
  context->ChromaSubsamplingLevel = 1;
364
  /* init optimized methods */
365
0
  NSC_INIT_SIMD(context);
366
0
  return context;
367
0
error:
368
0
  nsc_context_free(context);
369
0
  return NULL;
370
0
}
371
372
void nsc_context_free(NSC_CONTEXT* context)
373
0
{
374
0
  if (!context)
375
0
    return;
376
377
0
  if (context->priv)
378
0
  {
379
0
    for (size_t i = 0; i < 5; i++)
380
0
      winpr_aligned_free(context->priv->PlaneBuffers[i]);
381
382
0
    nsc_profiler_print(context->priv);
383
0
    PROFILER_FREE(context->priv->prof_nsc_rle_decompress_data)
384
0
    PROFILER_FREE(context->priv->prof_nsc_decode)
385
0
    PROFILER_FREE(context->priv->prof_nsc_rle_compress_data)
386
0
    PROFILER_FREE(context->priv->prof_nsc_encode)
387
0
    winpr_aligned_free(context->priv);
388
0
  }
389
390
0
  winpr_aligned_free(context->BitmapData);
391
0
  winpr_aligned_free(context);
392
0
}
393
394
#if defined(WITH_FREERDP_DEPRECATED)
395
BOOL nsc_context_set_pixel_format(NSC_CONTEXT* context, UINT32 pixel_format)
396
{
397
  return nsc_context_set_parameters(context, NSC_COLOR_FORMAT, pixel_format);
398
}
399
#endif
400
401
BOOL nsc_context_set_parameters(NSC_CONTEXT* context, NSC_PARAMETER what, UINT32 value)
402
0
{
403
0
  if (!context)
404
0
    return FALSE;
405
406
0
  switch (what)
407
0
  {
408
0
    case NSC_COLOR_LOSS_LEVEL:
409
0
      context->ColorLossLevel = value;
410
0
      break;
411
0
    case NSC_ALLOW_SUBSAMPLING:
412
0
      context->ChromaSubsamplingLevel = value;
413
0
      break;
414
0
    case NSC_DYNAMIC_COLOR_FIDELITY:
415
0
      context->DynamicColorFidelity = value != 0;
416
0
      break;
417
0
    case NSC_COLOR_FORMAT:
418
0
      context->format = value;
419
0
      break;
420
0
    default:
421
0
      return FALSE;
422
0
  }
423
0
  return TRUE;
424
0
}
425
426
BOOL nsc_process_message(NSC_CONTEXT* context, UINT16 bpp, UINT32 width, UINT32 height,
427
                         const BYTE* data, UINT32 length, BYTE* pDstData, UINT32 DstFormat,
428
                         UINT32 nDstStride, UINT32 nXDst, UINT32 nYDst, UINT32 nWidth,
429
                         UINT32 nHeight, UINT32 flip)
430
0
{
431
0
  wStream* s;
432
0
  wStream sbuffer = { 0 };
433
0
  BOOL ret;
434
0
  if (!context || !data || !pDstData)
435
0
    return FALSE;
436
437
0
  s = Stream_StaticConstInit(&sbuffer, data, length);
438
439
0
  if (!s)
440
0
    return FALSE;
441
442
0
  if (nDstStride == 0)
443
0
    nDstStride = nWidth * FreeRDPGetBytesPerPixel(DstFormat);
444
445
0
  switch (bpp)
446
0
  {
447
0
    case 32:
448
0
      context->format = PIXEL_FORMAT_BGRA32;
449
0
      break;
450
451
0
    case 24:
452
0
      context->format = PIXEL_FORMAT_BGR24;
453
0
      break;
454
455
0
    case 16:
456
0
      context->format = PIXEL_FORMAT_BGR16;
457
0
      break;
458
459
0
    case 8:
460
0
      context->format = PIXEL_FORMAT_RGB8;
461
0
      break;
462
463
0
    case 4:
464
0
      context->format = PIXEL_FORMAT_A4;
465
0
      break;
466
467
0
    default:
468
0
      return FALSE;
469
0
  }
470
471
0
  context->width = width;
472
0
  context->height = height;
473
0
  ret = nsc_context_initialize(context, s);
474
475
0
  if (!ret)
476
0
    return FALSE;
477
478
  /* RLE decode */
479
0
  {
480
0
    BOOL rc;
481
0
    PROFILER_ENTER(context->priv->prof_nsc_rle_decompress_data)
482
0
    rc = nsc_rle_decompress_data(context);
483
0
    PROFILER_EXIT(context->priv->prof_nsc_rle_decompress_data)
484
485
0
    if (!rc)
486
0
      return FALSE;
487
0
  }
488
  /* Colorloss recover, Chroma supersample and AYCoCg to ARGB Conversion in one step */
489
0
  {
490
0
    BOOL rc;
491
0
    PROFILER_ENTER(context->priv->prof_nsc_decode)
492
0
    rc = context->decode(context);
493
0
    PROFILER_EXIT(context->priv->prof_nsc_decode)
494
495
0
    if (!rc)
496
0
      return FALSE;
497
0
  }
498
499
0
  if (!freerdp_image_copy(pDstData, DstFormat, nDstStride, nXDst, nYDst, width, height,
500
0
                          context->BitmapData, PIXEL_FORMAT_BGRA32, 0, 0, 0, NULL, flip))
501
0
    return FALSE;
502
503
0
  return TRUE;
504
0
}