Coverage Report

Created: 2026-03-13 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/codec/planar.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * RDP6 Planar Codec
4
 *
5
 * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2016 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2016 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <freerdp/config.h>
23
24
#include <winpr/crt.h>
25
#include <winpr/wtypes.h>
26
#include <winpr/assert.h>
27
#include <winpr/cast.h>
28
#include <winpr/print.h>
29
30
#include <freerdp/primitives.h>
31
#include <freerdp/log.h>
32
#include <freerdp/codec/bitmap.h>
33
#include <freerdp/codec/planar.h>
34
35
#define TAG FREERDP_TAG("codec")
36
37
#define PLANAR_ALIGN(val, align) \
38
11.5k
  ((val) % (align) == 0) ? (val) : ((val) + (align) - (val) % (align))
39
40
typedef struct
41
{
42
  /**
43
   * controlByte:
44
   * [0-3]: nRunLength
45
   * [4-7]: cRawBytes
46
   */
47
  BYTE controlByte;
48
  BYTE* rawValues;
49
} RDP6_RLE_SEGMENT;
50
51
typedef struct
52
{
53
  UINT32 cSegments;
54
  RDP6_RLE_SEGMENT* segments;
55
} RDP6_RLE_SEGMENTS;
56
57
typedef struct
58
{
59
  /**
60
   * formatHeader:
61
   * [0-2]: Color Loss Level (CLL)
62
   *  [3] : Chroma Subsampling (CS)
63
   *  [4] : Run Length Encoding (RLE)
64
   *  [5] : No Alpha (NA)
65
   * [6-7]: Reserved
66
   */
67
  BYTE formatHeader;
68
} RDP6_BITMAP_STREAM;
69
70
struct S_BITMAP_PLANAR_CONTEXT
71
{
72
  UINT32 maxWidth;
73
  UINT32 maxHeight;
74
  UINT32 maxPlaneSize;
75
76
  BOOL AllowSkipAlpha;
77
  BOOL AllowRunLengthEncoding;
78
  BOOL AllowColorSubsampling;
79
  BOOL AllowDynamicColorFidelity;
80
81
  UINT32 ColorLossLevel;
82
83
  BYTE* planes[4];
84
  BYTE* planesBuffer;
85
86
  BYTE* deltaPlanes[4];
87
  BYTE* deltaPlanesBuffer;
88
89
  BYTE* rlePlanes[4];
90
  BYTE* rlePlanesBuffer;
91
92
  BYTE* pTempData;
93
  UINT32 nTempStep;
94
95
  BOOL bgr;
96
  BOOL topdown;
97
};
98
99
static inline BYTE PLANAR_CONTROL_BYTE(UINT32 nRunLength, UINT32 cRawBytes)
100
0
{
101
0
  return WINPR_ASSERTING_INT_CAST(UINT8, ((nRunLength & 0x0F) | ((cRawBytes & 0x0F) << 4)));
102
0
}
103
104
static inline BYTE PLANAR_CONTROL_BYTE_RUN_LENGTH(UINT32 controlByte)
105
25.1M
{
106
25.1M
  return (controlByte & 0x0F);
107
25.1M
}
108
static inline BYTE PLANAR_CONTROL_BYTE_RAW_BYTES(UINT32 controlByte)
109
25.1M
{
110
25.1M
  return ((controlByte >> 4) & 0x0F);
111
25.1M
}
112
113
static inline UINT32 planar_invert_format(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL alpha,
114
                                          UINT32 DstFormat)
115
12.2k
{
116
12.2k
  WINPR_ASSERT(planar);
117
12.2k
  if (planar->bgr && alpha)
118
0
  {
119
0
    switch (DstFormat)
120
0
    {
121
0
      case PIXEL_FORMAT_ARGB32:
122
0
        DstFormat = PIXEL_FORMAT_ABGR32;
123
0
        break;
124
0
      case PIXEL_FORMAT_XRGB32:
125
0
        DstFormat = PIXEL_FORMAT_XBGR32;
126
0
        break;
127
0
      case PIXEL_FORMAT_ABGR32:
128
0
        DstFormat = PIXEL_FORMAT_ARGB32;
129
0
        break;
130
0
      case PIXEL_FORMAT_XBGR32:
131
0
        DstFormat = PIXEL_FORMAT_XRGB32;
132
0
        break;
133
0
      case PIXEL_FORMAT_BGRA32:
134
0
        DstFormat = PIXEL_FORMAT_RGBA32;
135
0
        break;
136
0
      case PIXEL_FORMAT_BGRX32:
137
0
        DstFormat = PIXEL_FORMAT_RGBX32;
138
0
        break;
139
0
      case PIXEL_FORMAT_RGBA32:
140
0
        DstFormat = PIXEL_FORMAT_BGRA32;
141
0
        break;
142
0
      case PIXEL_FORMAT_RGBX32:
143
0
        DstFormat = PIXEL_FORMAT_BGRX32;
144
0
        break;
145
0
      case PIXEL_FORMAT_RGB24:
146
0
        DstFormat = PIXEL_FORMAT_BGR24;
147
0
        break;
148
0
      case PIXEL_FORMAT_BGR24:
149
0
        DstFormat = PIXEL_FORMAT_RGB24;
150
0
        break;
151
0
      case PIXEL_FORMAT_RGB16:
152
0
        DstFormat = PIXEL_FORMAT_BGR16;
153
0
        break;
154
0
      case PIXEL_FORMAT_BGR16:
155
0
        DstFormat = PIXEL_FORMAT_RGB16;
156
0
        break;
157
0
      case PIXEL_FORMAT_ARGB15:
158
0
        DstFormat = PIXEL_FORMAT_ABGR15;
159
0
        break;
160
0
      case PIXEL_FORMAT_RGB15:
161
0
        DstFormat = PIXEL_FORMAT_BGR15;
162
0
        break;
163
0
      case PIXEL_FORMAT_ABGR15:
164
0
        DstFormat = PIXEL_FORMAT_ARGB15;
165
0
        break;
166
0
      case PIXEL_FORMAT_BGR15:
167
0
        DstFormat = PIXEL_FORMAT_RGB15;
168
0
        break;
169
0
      default:
170
0
        break;
171
0
    }
172
0
  }
173
12.2k
  return DstFormat;
174
12.2k
}
175
176
static inline BOOL freerdp_bitmap_planar_compress_plane_rle(const BYTE* WINPR_RESTRICT inPlane,
177
                                                            UINT32 width, UINT32 height,
178
                                                            BYTE* WINPR_RESTRICT outPlane,
179
                                                            UINT32* WINPR_RESTRICT dstSize);
180
static inline BYTE* freerdp_bitmap_planar_delta_encode_plane(const BYTE* WINPR_RESTRICT inPlane,
181
                                                             UINT32 width, UINT32 height,
182
                                                             BYTE* WINPR_RESTRICT outPlane);
183
184
static inline INT32 planar_skip_plane_rle(const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
185
                                          UINT32 nWidth, UINT32 nHeight)
186
3.32k
{
187
3.32k
  UINT32 used = 0;
188
189
3.32k
  WINPR_ASSERT(pSrcData);
190
191
49.9k
  for (UINT32 y = 0; y < nHeight; y++)
192
48.8k
  {
193
18.5M
    for (UINT32 x = 0; x < nWidth;)
194
18.4M
    {
195
18.4M
      if (used >= SrcSize)
196
459
      {
197
459
        WLog_ERR(TAG, "planar plane used %" PRIu32 " exceeds SrcSize %" PRIu32, used,
198
459
                 SrcSize);
199
459
        return -1;
200
459
      }
201
202
18.4M
      const uint8_t controlByte = pSrcData[used++];
203
18.4M
      uint32_t nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
204
18.4M
      uint32_t cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
205
206
18.4M
      if (nRunLength == 1)
207
26.3k
      {
208
26.3k
        nRunLength = cRawBytes + 16;
209
26.3k
        cRawBytes = 0;
210
26.3k
      }
211
18.4M
      else if (nRunLength == 2)
212
35.6k
      {
213
35.6k
        nRunLength = cRawBytes + 32;
214
35.6k
        cRawBytes = 0;
215
35.6k
      }
216
217
18.4M
      used += cRawBytes;
218
18.4M
      x += cRawBytes;
219
18.4M
      x += nRunLength;
220
221
18.4M
      if (x > nWidth)
222
887
      {
223
887
        WLog_ERR(TAG, "planar plane x %" PRIu32 " exceeds width %" PRIu32, x, nWidth);
224
887
        return -1;
225
887
      }
226
227
18.4M
      if (used > SrcSize)
228
862
      {
229
862
        WLog_ERR(TAG, "planar plane used %" PRIu32 " exceeds SrcSize %" PRId32, used,
230
862
                 INT32_MAX);
231
862
        return -1;
232
862
      }
233
18.4M
    }
234
48.8k
  }
235
236
1.12k
  if (used > INT32_MAX)
237
0
  {
238
0
    WLog_ERR(TAG, "planar plane used %" PRIu32 " exceeds SrcSize %" PRIu32, used, SrcSize);
239
0
    return -1;
240
0
  }
241
1.12k
  return (INT32)used;
242
1.12k
}
243
244
static inline UINT8 clamp(INT16 val)
245
1.03M
{
246
1.03M
  return (UINT8)val;
247
1.03M
}
248
249
static inline INT32 planar_decompress_plane_rle_only(const BYTE* WINPR_RESTRICT pSrcData,
250
                                                     UINT32 SrcSize, BYTE* WINPR_RESTRICT pDstData,
251
                                                     UINT32 nWidth, UINT32 nHeight)
252
591
{
253
591
  BYTE* previousScanline = nullptr;
254
591
  const BYTE* srcp = pSrcData;
255
256
591
  WINPR_ASSERT(nHeight <= INT32_MAX);
257
591
  WINPR_ASSERT(nWidth <= INT32_MAX);
258
259
16.1k
  for (UINT32 y = 0; y < nHeight; y++)
260
15.5k
  {
261
15.5k
    BYTE* dstp = &pDstData[1ULL * (y)*nWidth];
262
15.5k
    INT16 pixel = 0;
263
15.5k
    BYTE* currentScanline = dstp;
264
265
6.55M
    for (UINT32 x = 0; x < nWidth;)
266
6.54M
    {
267
6.54M
      BYTE controlByte = *srcp;
268
6.54M
      srcp++;
269
270
6.54M
      if ((srcp - pSrcData) > SrcSize * 1ll)
271
0
      {
272
0
        WLog_ERR(TAG, "error reading input buffer");
273
0
        return -1;
274
0
      }
275
276
6.54M
      UINT32 nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
277
6.54M
      UINT32 cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
278
279
6.54M
      if (nRunLength == 1)
280
8.67k
      {
281
8.67k
        nRunLength = cRawBytes + 16;
282
8.67k
        cRawBytes = 0;
283
8.67k
      }
284
6.53M
      else if (nRunLength == 2)
285
8.21k
      {
286
8.21k
        nRunLength = cRawBytes + 32;
287
8.21k
        cRawBytes = 0;
288
8.21k
      }
289
290
6.54M
      if (((dstp + (cRawBytes + nRunLength)) - currentScanline) > nWidth * 1ll)
291
0
      {
292
0
        WLog_ERR(TAG, "too many pixels in scanline");
293
0
        return -1;
294
0
      }
295
296
6.54M
      if (!previousScanline)
297
3.56k
      {
298
        /* first scanline, absolute values */
299
5.10k
        while (cRawBytes > 0)
300
1.53k
        {
301
1.53k
          pixel = *srcp;
302
1.53k
          srcp++;
303
1.53k
          *dstp = clamp(pixel);
304
1.53k
          dstp++;
305
1.53k
          x++;
306
1.53k
          cRawBytes--;
307
1.53k
        }
308
309
17.6k
        while (nRunLength > 0)
310
14.0k
        {
311
14.0k
          *dstp = clamp(pixel);
312
14.0k
          dstp++;
313
14.0k
          x++;
314
14.0k
          nRunLength--;
315
14.0k
        }
316
3.56k
      }
317
6.53M
      else
318
6.53M
      {
319
        /* delta values relative to previous scanline */
320
6.58M
        while (cRawBytes > 0)
321
44.6k
        {
322
44.6k
          UINT8 deltaValue = *srcp;
323
44.6k
          srcp++;
324
325
44.6k
          if (deltaValue & 1)
326
8.20k
          {
327
8.20k
            deltaValue = deltaValue >> 1;
328
8.20k
            deltaValue = deltaValue + 1;
329
8.20k
            pixel = WINPR_ASSERTING_INT_CAST(int16_t, -1 * (int16_t)deltaValue);
330
8.20k
          }
331
36.4k
          else
332
36.4k
          {
333
36.4k
            deltaValue = deltaValue >> 1;
334
36.4k
            pixel = WINPR_ASSERTING_INT_CAST(INT16, deltaValue);
335
36.4k
          }
336
337
44.6k
          const INT16 delta =
338
44.6k
              WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[x] + pixel);
339
44.6k
          *dstp = clamp(delta);
340
44.6k
          dstp++;
341
44.6k
          x++;
342
44.6k
          cRawBytes--;
343
44.6k
        }
344
345
6.96M
        while (nRunLength > 0)
346
428k
        {
347
428k
          const INT16 deltaValue =
348
428k
              WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[x] + pixel);
349
428k
          *dstp = clamp(deltaValue);
350
428k
          dstp++;
351
428k
          x++;
352
428k
          nRunLength--;
353
428k
        }
354
6.53M
      }
355
6.54M
    }
356
357
15.5k
    previousScanline = currentScanline;
358
15.5k
  }
359
360
591
  return (INT32)(srcp - pSrcData);
361
591
}
362
363
static inline INT32 planar_decompress_plane_rle(const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
364
                                                BYTE* WINPR_RESTRICT pDstData, UINT32 nDstStep,
365
                                                UINT32 nXDst, UINT32 nYDst, UINT32 nWidth,
366
                                                UINT32 nHeight, UINT32 nChannel, BOOL vFlip)
367
405
{
368
405
  INT32 beg = 0;
369
405
  INT32 end = 0;
370
405
  INT32 inc = 0;
371
405
  BYTE* previousScanline = nullptr;
372
405
  const BYTE* srcp = pSrcData;
373
374
405
  WINPR_ASSERT(nHeight <= INT32_MAX);
375
405
  WINPR_ASSERT(nWidth <= INT32_MAX);
376
405
  WINPR_ASSERT(nDstStep <= INT32_MAX);
377
378
405
  if (vFlip)
379
0
  {
380
0
    beg = (INT32)nHeight - 1;
381
0
    end = -1;
382
0
    inc = -1;
383
0
  }
384
405
  else
385
405
  {
386
405
    beg = 0;
387
405
    end = (INT32)nHeight;
388
405
    inc = 1;
389
405
  }
390
391
14.9k
  for (INT32 y = beg; y != end; y += inc)
392
14.4k
  {
393
14.4k
    const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (4LL * nXDst) + nChannel * 1LL;
394
14.4k
    BYTE* dstp = &pDstData[off];
395
14.4k
    BYTE* currentScanline = dstp;
396
14.4k
    INT16 pixel = 0;
397
398
136k
    for (INT32 x = 0; x < (INT32)nWidth;)
399
121k
    {
400
121k
      const BYTE controlByte = *srcp;
401
121k
      srcp++;
402
403
121k
      if ((srcp - pSrcData) > SrcSize * 1ll)
404
0
      {
405
0
        WLog_ERR(TAG, "error reading input buffer");
406
0
        return -1;
407
0
      }
408
409
121k
      UINT32 nRunLength = PLANAR_CONTROL_BYTE_RUN_LENGTH(controlByte);
410
121k
      UINT32 cRawBytes = PLANAR_CONTROL_BYTE_RAW_BYTES(controlByte);
411
412
121k
      if (nRunLength == 1)
413
6.77k
      {
414
6.77k
        nRunLength = cRawBytes + 16;
415
6.77k
        cRawBytes = 0;
416
6.77k
      }
417
114k
      else if (nRunLength == 2)
418
9.29k
      {
419
9.29k
        nRunLength = cRawBytes + 32;
420
9.29k
        cRawBytes = 0;
421
9.29k
      }
422
423
121k
      if (((dstp + (cRawBytes + nRunLength)) - currentScanline) > nWidth * 4ll)
424
0
      {
425
0
        WLog_ERR(TAG, "too many pixels in scanline");
426
0
        return -1;
427
0
      }
428
429
121k
      if (!previousScanline)
430
3.26k
      {
431
        /* first scanline, absolute values */
432
5.54k
        while (cRawBytes > 0)
433
2.28k
        {
434
2.28k
          pixel = *srcp;
435
2.28k
          srcp++;
436
2.28k
          *dstp = WINPR_ASSERTING_INT_CAST(BYTE, pixel);
437
2.28k
          dstp += 4;
438
2.28k
          x++;
439
2.28k
          cRawBytes--;
440
2.28k
        }
441
442
15.4k
        while (nRunLength > 0)
443
12.2k
        {
444
12.2k
          *dstp = WINPR_ASSERTING_INT_CAST(BYTE, pixel);
445
12.2k
          dstp += 4;
446
12.2k
          x++;
447
12.2k
          nRunLength--;
448
12.2k
        }
449
3.26k
      }
450
118k
      else
451
118k
      {
452
        /* delta values relative to previous scanline */
453
219k
        while (cRawBytes > 0)
454
100k
        {
455
100k
          BYTE deltaValue = *srcp;
456
100k
          srcp++;
457
458
100k
          if (deltaValue & 1)
459
989
          {
460
989
            deltaValue = deltaValue >> 1;
461
989
            deltaValue = deltaValue + 1;
462
989
            pixel = WINPR_ASSERTING_INT_CAST(int16_t, -deltaValue);
463
989
          }
464
99.6k
          else
465
99.6k
          {
466
99.6k
            deltaValue = deltaValue >> 1;
467
99.6k
            pixel = deltaValue;
468
99.6k
          }
469
470
100k
          const INT16 delta =
471
100k
              WINPR_ASSERTING_INT_CAST(int16_t, previousScanline[4LL * x] + pixel);
472
100k
          *dstp = clamp(delta);
473
100k
          dstp += 4;
474
100k
          x++;
475
100k
          cRawBytes--;
476
100k
        }
477
478
565k
        while (nRunLength > 0)
479
447k
        {
480
447k
          const INT16 deltaValue =
481
447k
              WINPR_ASSERTING_INT_CAST(int16_t, pixel + previousScanline[4LL * x]);
482
447k
          *dstp = clamp(deltaValue);
483
447k
          dstp += 4;
484
447k
          x++;
485
447k
          nRunLength--;
486
447k
        }
487
118k
      }
488
121k
    }
489
490
14.4k
    previousScanline = currentScanline;
491
14.4k
  }
492
493
405
  return (INT32)(srcp - pSrcData);
494
405
}
495
496
static inline INT32 planar_set_plane(BYTE bValue, BYTE* pDstData, UINT32 nDstStep, UINT32 nXDst,
497
                                     UINT32 nYDst, UINT32 nWidth, UINT32 nHeight, UINT32 nChannel,
498
                                     BOOL vFlip)
499
135
{
500
135
  INT32 beg = 0;
501
135
  INT32 end = (INT32)nHeight;
502
135
  INT32 inc = 1;
503
504
135
  WINPR_ASSERT(nHeight <= INT32_MAX);
505
135
  WINPR_ASSERT(nWidth <= INT32_MAX);
506
135
  WINPR_ASSERT(nDstStep <= INT32_MAX);
507
508
135
  if (vFlip)
509
0
  {
510
0
    beg = (INT32)nHeight - 1;
511
0
    end = -1;
512
0
    inc = -1;
513
0
  }
514
515
4.96k
  for (INT32 y = beg; y != end; y += inc)
516
4.83k
  {
517
4.83k
    const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (4LL * nXDst) + nChannel * 1LL;
518
4.83k
    BYTE* dstp = &pDstData[off];
519
520
192k
    for (INT32 x = 0; x < (INT32)nWidth; ++x)
521
187k
    {
522
187k
      *dstp = bValue;
523
187k
      dstp += 4;
524
187k
    }
525
4.83k
  }
526
527
135
  return 0;
528
135
}
529
530
static inline BOOL writeLine(BYTE** WINPR_RESTRICT ppRgba, UINT32 DstFormat, UINT32 width,
531
                             const BYTE** WINPR_RESTRICT ppR, const BYTE** WINPR_RESTRICT ppG,
532
                             const BYTE** WINPR_RESTRICT ppB, const BYTE** WINPR_RESTRICT ppA)
533
53.7k
{
534
53.7k
  WINPR_ASSERT(ppRgba);
535
53.7k
  WINPR_ASSERT(ppR);
536
53.7k
  WINPR_ASSERT(ppG);
537
53.7k
  WINPR_ASSERT(ppB);
538
539
53.7k
  switch (DstFormat)
540
53.7k
  {
541
0
    case PIXEL_FORMAT_BGRA32:
542
0
      for (UINT32 x = 0; x < width; x++)
543
0
      {
544
0
        *(*ppRgba)++ = *(*ppB)++;
545
0
        *(*ppRgba)++ = *(*ppG)++;
546
0
        *(*ppRgba)++ = *(*ppR)++;
547
0
        *(*ppRgba)++ = *(*ppA)++;
548
0
      }
549
550
0
      return TRUE;
551
552
53.7k
    case PIXEL_FORMAT_BGRX32:
553
2.68M
      for (UINT32 x = 0; x < width; x++)
554
2.63M
      {
555
2.63M
        *(*ppRgba)++ = *(*ppB)++;
556
2.63M
        *(*ppRgba)++ = *(*ppG)++;
557
2.63M
        *(*ppRgba)++ = *(*ppR)++;
558
2.63M
        *(*ppRgba)++ = 0xFF;
559
2.63M
      }
560
561
53.7k
      return TRUE;
562
563
0
    default:
564
0
      if (ppA)
565
0
      {
566
0
        for (UINT32 x = 0; x < width; x++)
567
0
        {
568
0
          BYTE alpha = *(*ppA)++;
569
0
          UINT32 color =
570
0
              FreeRDPGetColor(DstFormat, *(*ppR)++, *(*ppG)++, *(*ppB)++, alpha);
571
0
          FreeRDPWriteColor(*ppRgba, DstFormat, color);
572
0
          *ppRgba += FreeRDPGetBytesPerPixel(DstFormat);
573
0
        }
574
0
      }
575
0
      else
576
0
      {
577
0
        const BYTE alpha = 0xFF;
578
579
0
        for (UINT32 x = 0; x < width; x++)
580
0
        {
581
0
          UINT32 color =
582
0
              FreeRDPGetColor(DstFormat, *(*ppR)++, *(*ppG)++, *(*ppB)++, alpha);
583
0
          FreeRDPWriteColor(*ppRgba, DstFormat, color);
584
0
          *ppRgba += FreeRDPGetBytesPerPixel(DstFormat);
585
0
        }
586
0
      }
587
588
0
      return TRUE;
589
53.7k
  }
590
53.7k
}
591
592
static inline BOOL planar_decompress_planes_raw(const BYTE* WINPR_RESTRICT pSrcData[4],
593
                                                BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
594
                                                UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst,
595
                                                UINT32 nWidth, UINT32 nHeight, BOOL vFlip,
596
                                                UINT32 totalHeight)
597
1.23k
{
598
1.23k
  INT32 beg = 0;
599
1.23k
  INT32 end = 0;
600
1.23k
  INT32 inc = 0;
601
1.23k
  const BYTE* pR = pSrcData[0];
602
1.23k
  const BYTE* pG = pSrcData[1];
603
1.23k
  const BYTE* pB = pSrcData[2];
604
1.23k
  const BYTE* pA = pSrcData[3];
605
1.23k
  const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat);
606
607
1.23k
  if (vFlip)
608
0
  {
609
0
    beg = WINPR_ASSERTING_INT_CAST(int32_t, nHeight - 1);
610
0
    end = -1;
611
0
    inc = -1;
612
0
  }
613
1.23k
  else
614
1.23k
  {
615
1.23k
    beg = 0;
616
1.23k
    end = WINPR_ASSERTING_INT_CAST(int32_t, nHeight);
617
1.23k
    inc = 1;
618
1.23k
  }
619
620
1.23k
  if (nYDst + nHeight > totalHeight)
621
0
  {
622
0
    WLog_ERR(TAG,
623
0
             "planar plane destination Y %" PRIu32 " + height %" PRIu32
624
0
             " exceeds totalHeight %" PRIu32,
625
0
             nYDst, nHeight, totalHeight);
626
0
    return FALSE;
627
0
  }
628
629
1.23k
  if ((nXDst + nWidth) * bpp > nDstStep)
630
0
  {
631
0
    WLog_ERR(TAG,
632
0
             "planar plane destination (X %" PRIu32 " + width %" PRIu32 ") * bpp %" PRIu32
633
0
             " exceeds stride %" PRIu32,
634
0
             nXDst, nWidth, bpp, nDstStep);
635
0
    return FALSE;
636
0
  }
637
638
54.9k
  for (INT32 y = beg; y != end; y += inc)
639
53.7k
  {
640
53.7k
    BYTE* pRGB = nullptr;
641
642
53.7k
    if (y > WINPR_ASSERTING_INT_CAST(INT64, nHeight))
643
0
    {
644
0
      WLog_ERR(TAG, "planar plane destination Y %" PRId32 " exceeds height %" PRIu32, y,
645
0
               nHeight);
646
0
      return FALSE;
647
0
    }
648
649
53.7k
    const intptr_t off = ((1LL * nYDst + y) * nDstStep) + (1LL * nXDst * bpp);
650
53.7k
    pRGB = &pDstData[off];
651
652
53.7k
    if (!writeLine(&pRGB, DstFormat, nWidth, &pR, &pG, &pB, &pA))
653
0
      return FALSE;
654
53.7k
  }
655
656
1.23k
  return TRUE;
657
1.23k
}
658
659
static BOOL planar_subsample_expand(const BYTE* WINPR_RESTRICT plane, size_t planeLength,
660
                                    UINT32 nWidth, UINT32 nHeight, UINT32 nPlaneWidth,
661
                                    UINT32 nPlaneHeight, BYTE* WINPR_RESTRICT deltaPlane)
662
438
{
663
438
  size_t pos = 0;
664
438
  WINPR_UNUSED(planeLength);
665
666
438
  WINPR_ASSERT(plane);
667
438
  WINPR_ASSERT(deltaPlane);
668
669
438
  if (nWidth > nPlaneWidth * 2)
670
0
  {
671
0
    WLog_ERR(TAG, "planar subsample width %" PRIu32 " > PlaneWidth %" PRIu32 " * 2", nWidth,
672
0
             nPlaneWidth);
673
0
    return FALSE;
674
0
  }
675
676
438
  if (nHeight > nPlaneHeight * 2)
677
0
  {
678
0
    WLog_ERR(TAG, "planar subsample height %" PRIu32 " > PlaneHeight %" PRIu32 " * 2", nHeight,
679
0
             nPlaneHeight);
680
0
    return FALSE;
681
0
  }
682
683
16.7k
  for (size_t y = 0; y < nHeight; y++)
684
16.3k
  {
685
16.3k
    const BYTE* src = plane + y / 2 * nPlaneWidth;
686
687
686k
    for (UINT32 x = 0; x < nWidth; x++)
688
669k
    {
689
669k
      deltaPlane[pos++] = src[x / 2];
690
669k
    }
691
16.3k
  }
692
693
438
  return TRUE;
694
438
}
695
696
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
697
BOOL planar_decompress(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
698
                       const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize, UINT32 nSrcWidth,
699
                       UINT32 nSrcHeight, BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
700
                       UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth,
701
                       UINT32 nDstHeight, BOOL vFlip)
702
11.5k
{
703
11.5k
  return freerdp_bitmap_decompress_planar(planar, pSrcData, SrcSize, nSrcWidth, nSrcHeight,
704
11.5k
                                          pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth,
705
11.5k
                                          nDstHeight, vFlip);
706
11.5k
}
707
#endif
708
709
BOOL freerdp_bitmap_decompress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
710
                                      const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize,
711
                                      UINT32 nSrcWidth, UINT32 nSrcHeight,
712
                                      BYTE* WINPR_RESTRICT pDstData, UINT32 DstFormat,
713
                                      UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, UINT32 nDstWidth,
714
                                      UINT32 nDstHeight, BOOL vFlip)
715
11.5k
{
716
11.5k
  BOOL useAlpha = FALSE;
717
11.5k
  INT32 status = 0;
718
11.5k
  INT32 rleSizes[4] = { 0, 0, 0, 0 };
719
11.5k
  UINT32 rawSizes[4] = WINPR_C_ARRAY_INIT;
720
11.5k
  UINT32 rawWidths[4] = WINPR_C_ARRAY_INIT;
721
11.5k
  UINT32 rawHeights[4] = WINPR_C_ARRAY_INIT;
722
11.5k
  const BYTE* planes[4] = WINPR_C_ARRAY_INIT;
723
11.5k
  const UINT32 w = MIN(nSrcWidth, nDstWidth);
724
11.5k
  const UINT32 h = MIN(nSrcHeight, nDstHeight);
725
11.5k
  const primitives_t* prims = primitives_get();
726
727
11.5k
  WINPR_ASSERT(planar);
728
11.5k
  WINPR_ASSERT(prims);
729
730
11.5k
  if (planar->maxWidth < nSrcWidth)
731
0
    return FALSE;
732
11.5k
  if (planar->maxHeight < nSrcHeight)
733
0
    return FALSE;
734
735
11.5k
  const UINT32 bpp = FreeRDPGetBytesPerPixel(DstFormat);
736
11.5k
  if (nDstStep <= 0)
737
11.5k
    nDstStep = nDstWidth * bpp;
738
739
11.5k
  const BYTE* srcp = pSrcData;
740
741
11.5k
  if (!pSrcData || (SrcSize < 1))
742
0
  {
743
0
    WLog_ERR(TAG, "Invalid argument pSrcData=%p [size=%" PRIu32 "]",
744
0
             WINPR_CXX_COMPAT_CAST(const void*, pSrcData), SrcSize);
745
0
    return FALSE;
746
0
  }
747
748
11.5k
  if (!pDstData)
749
0
  {
750
0
    WLog_ERR(TAG, "Invalid argument pDstData=nullptr");
751
0
    return FALSE;
752
0
  }
753
754
11.5k
  const BYTE FormatHeader = *srcp++;
755
11.5k
  const BYTE cll = (FormatHeader & PLANAR_FORMAT_HEADER_CLL_MASK);
756
11.5k
  const BYTE cs = (FormatHeader & PLANAR_FORMAT_HEADER_CS) != 0;
757
11.5k
  const BYTE rle = (FormatHeader & PLANAR_FORMAT_HEADER_RLE) != 0;
758
11.5k
  const BYTE alpha = !(FormatHeader & PLANAR_FORMAT_HEADER_NA);
759
760
11.5k
  DstFormat = planar_invert_format(planar, alpha, DstFormat);
761
762
11.5k
  if (alpha)
763
5.79k
    useAlpha = FreeRDPColorHasAlpha(DstFormat);
764
765
  // WLog_INFO(TAG, "CLL: %"PRIu32" CS: %"PRIu8" RLE: %"PRIu8" ALPHA: %"PRIu8"", cll, cs, rle,
766
  // alpha);
767
768
11.5k
  if (!cll && cs)
769
282
  {
770
282
    WLog_ERR(TAG, "Chroma subsampling requires YCoCg and does not work with RGB data");
771
282
    return FALSE; /* Chroma subsampling requires YCoCg */
772
282
  }
773
774
11.2k
  const UINT32 subWidth = (nSrcWidth / 2) + (nSrcWidth % 2);
775
11.2k
  const UINT32 subHeight = (nSrcHeight / 2) + (nSrcHeight % 2);
776
11.2k
  const UINT32 planeSize = nSrcWidth * nSrcHeight;
777
11.2k
  const UINT32 subSize = subWidth * subHeight;
778
779
11.2k
  if (!cs)
780
9.72k
  {
781
9.72k
    rawSizes[0] = planeSize; /* LumaOrRedPlane */
782
9.72k
    rawWidths[0] = nSrcWidth;
783
9.72k
    rawHeights[0] = nSrcHeight;
784
9.72k
    rawSizes[1] = planeSize; /* OrangeChromaOrGreenPlane */
785
9.72k
    rawWidths[1] = nSrcWidth;
786
9.72k
    rawHeights[1] = nSrcHeight;
787
9.72k
    rawSizes[2] = planeSize; /* GreenChromaOrBluePlane */
788
9.72k
    rawWidths[2] = nSrcWidth;
789
9.72k
    rawHeights[2] = nSrcHeight;
790
9.72k
    rawSizes[3] = planeSize; /* AlphaPlane */
791
9.72k
    rawWidths[3] = nSrcWidth;
792
9.72k
    rawHeights[3] = nSrcHeight;
793
9.72k
  }
794
1.53k
  else /* Chroma Subsampling */
795
1.53k
  {
796
1.53k
    rawSizes[0] = planeSize; /* LumaOrRedPlane */
797
1.53k
    rawWidths[0] = nSrcWidth;
798
1.53k
    rawHeights[0] = nSrcHeight;
799
1.53k
    rawSizes[1] = subSize; /* OrangeChromaOrGreenPlane */
800
1.53k
    rawWidths[1] = subWidth;
801
1.53k
    rawHeights[1] = subHeight;
802
1.53k
    rawSizes[2] = subSize; /* GreenChromaOrBluePlane */
803
1.53k
    rawWidths[2] = subWidth;
804
1.53k
    rawHeights[2] = subHeight;
805
1.53k
    rawSizes[3] = planeSize; /* AlphaPlane */
806
1.53k
    rawWidths[3] = nSrcWidth;
807
1.53k
    rawHeights[3] = nSrcHeight;
808
1.53k
  }
809
810
11.2k
  const size_t diff = WINPR_ASSERTING_INT_CAST(size_t, (intptr_t)(srcp - pSrcData));
811
11.2k
  if (SrcSize < diff)
812
0
  {
813
0
    WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
814
0
    return FALSE;
815
0
  }
816
817
11.2k
  if (!rle) /* RAW */
818
8.71k
  {
819
820
8.71k
    UINT32 base = planeSize * 3;
821
8.71k
    if (cs)
822
620
      base = planeSize + planeSize / 2;
823
824
8.71k
    if (alpha)
825
5.22k
    {
826
5.22k
      if ((SrcSize - diff) < (planeSize + base))
827
4.67k
      {
828
4.67k
        WLog_ERR(TAG, "Alpha plane size mismatch %" PRIuz " < %" PRIu32, SrcSize - diff,
829
4.67k
                 (planeSize + base));
830
4.67k
        return FALSE;
831
4.67k
      }
832
833
550
      planes[3] = srcp;                    /* AlphaPlane */
834
550
      planes[0] = planes[3] + rawSizes[3]; /* LumaOrRedPlane */
835
550
      planes[1] = planes[0] + rawSizes[0]; /* OrangeChromaOrGreenPlane */
836
550
      planes[2] = planes[1] + rawSizes[1]; /* GreenChromaOrBluePlane */
837
838
550
      if ((planes[2] + rawSizes[2]) > &pSrcData[SrcSize])
839
0
      {
840
0
        WLog_ERR(TAG, "plane size mismatch %p + %" PRIu32 " > %p",
841
0
                 WINPR_CXX_COMPAT_CAST(const void*, planes[2]), rawSizes[2],
842
0
                 WINPR_CXX_COMPAT_CAST(const void*, &pSrcData[SrcSize]));
843
0
        return FALSE;
844
0
      }
845
550
    }
846
3.48k
    else
847
3.48k
    {
848
3.48k
      if ((SrcSize - diff) < base)
849
3.00k
      {
850
3.00k
        WLog_ERR(TAG, "plane size mismatch %" PRIuz " < %" PRIu32, SrcSize - diff, base);
851
3.00k
        return FALSE;
852
3.00k
      }
853
854
485
      planes[0] = srcp;                    /* LumaOrRedPlane */
855
485
      planes[1] = planes[0] + rawSizes[0]; /* OrangeChromaOrGreenPlane */
856
485
      planes[2] = planes[1] + rawSizes[1]; /* GreenChromaOrBluePlane */
857
858
485
      if ((planes[2] + rawSizes[2]) > &pSrcData[SrcSize])
859
0
      {
860
0
        WLog_ERR(TAG, "plane size mismatch %p + %" PRIu32 " > %p",
861
0
                 WINPR_CXX_COMPAT_CAST(const void*, planes[2]), rawSizes[2],
862
0
                 WINPR_CXX_COMPAT_CAST(const void*, &pSrcData[SrcSize]));
863
0
        return FALSE;
864
0
      }
865
485
    }
866
8.71k
  }
867
2.54k
  else /* RLE */
868
2.54k
  {
869
2.54k
    if (alpha)
870
462
    {
871
462
      planes[3] = srcp;
872
462
      rleSizes[3] = planar_skip_plane_rle(planes[3], (UINT32)(SrcSize - diff), rawWidths[3],
873
462
                                          rawHeights[3]); /* AlphaPlane */
874
875
462
      if (rleSizes[3] < 0)
876
425
        return FALSE;
877
878
37
      planes[0] = planes[3] + rleSizes[3];
879
37
    }
880
2.07k
    else
881
2.07k
      planes[0] = srcp;
882
883
2.11k
    const size_t diff0 = WINPR_ASSERTING_INT_CAST(size_t, (intptr_t)(planes[0] - pSrcData));
884
2.11k
    if (SrcSize < diff0)
885
0
    {
886
0
      WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff0);
887
0
      return FALSE;
888
0
    }
889
2.11k
    rleSizes[0] = planar_skip_plane_rle(planes[0], (UINT32)(SrcSize - diff0), rawWidths[0],
890
2.11k
                                        rawHeights[0]); /* RedPlane */
891
892
2.11k
    if (rleSizes[0] < 0)
893
1.72k
      return FALSE;
894
895
393
    planes[1] = planes[0] + rleSizes[0];
896
897
393
    const size_t diff1 = WINPR_ASSERTING_INT_CAST(size_t, (intptr_t)(planes[1] - pSrcData));
898
393
    if (SrcSize < diff1)
899
0
    {
900
0
      WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff1);
901
0
      return FALSE;
902
0
    }
903
393
    rleSizes[1] = planar_skip_plane_rle(planes[1], (UINT32)(SrcSize - diff1), rawWidths[1],
904
393
                                        rawHeights[1]); /* GreenPlane */
905
906
393
    if (rleSizes[1] < 1)
907
35
      return FALSE;
908
909
358
    planes[2] = planes[1] + rleSizes[1];
910
358
    const size_t diff2 = WINPR_ASSERTING_INT_CAST(size_t, (intptr_t)(planes[2] - pSrcData));
911
358
    if (SrcSize < diff2)
912
0
    {
913
0
      WLog_ERR(TAG, "Size mismatch %" PRIu32 " < %" PRIuz, SrcSize, diff);
914
0
      return FALSE;
915
0
    }
916
358
    rleSizes[2] = planar_skip_plane_rle(planes[2], (UINT32)(SrcSize - diff2), rawWidths[2],
917
358
                                        rawHeights[2]); /* BluePlane */
918
919
358
    if (rleSizes[2] < 1)
920
26
      return FALSE;
921
358
  }
922
923
1.36k
  if (!cll) /* RGB */
924
696
  {
925
696
    UINT32 TempFormat = 0;
926
696
    BYTE* pTempData = pDstData;
927
696
    UINT32 nTempStep = nDstStep;
928
696
    UINT32 nTotalHeight = nYDst + nDstHeight;
929
930
696
    if (useAlpha)
931
0
      TempFormat = PIXEL_FORMAT_BGRA32;
932
696
    else
933
696
      TempFormat = PIXEL_FORMAT_BGRX32;
934
935
696
    TempFormat = planar_invert_format(planar, alpha, TempFormat);
936
937
696
    if ((TempFormat != DstFormat) || (nSrcWidth != nDstWidth) || (nSrcHeight != nDstHeight))
938
696
    {
939
696
      pTempData = planar->pTempData;
940
696
      nTempStep = planar->nTempStep;
941
696
      nTotalHeight = planar->maxHeight;
942
696
    }
943
944
696
    if (!rle) /* RAW */
945
561
    {
946
561
      if (!planar_decompress_planes_raw(planes, pTempData, TempFormat, nTempStep, nXDst,
947
561
                                        nYDst, nSrcWidth, nSrcHeight, vFlip, nTotalHeight))
948
0
        return FALSE;
949
950
561
      if (alpha)
951
288
        srcp += rawSizes[0] + rawSizes[1] + rawSizes[2] + rawSizes[3];
952
273
      else /* NoAlpha */
953
273
        srcp += rawSizes[0] + rawSizes[1] + rawSizes[2];
954
955
561
      if ((SrcSize - (srcp - pSrcData)) == 1)
956
0
        srcp++; /* pad */
957
561
    }
958
135
    else /* RLE */
959
135
    {
960
135
      if (nYDst + nSrcHeight > nTotalHeight)
961
0
      {
962
0
        WLog_ERR(TAG,
963
0
                 "planar plane destination Y %" PRIu32 " + height %" PRIu32
964
0
                 " exceeds totalHeight %" PRIu32,
965
0
                 nYDst, nSrcHeight, nTotalHeight);
966
0
        return FALSE;
967
0
      }
968
969
135
      if ((nXDst + nSrcWidth) * bpp > nDstStep)
970
0
      {
971
0
        WLog_ERR(TAG,
972
0
                 "planar plane destination (X %" PRIu32 " + width %" PRIu32
973
0
                 ") * bpp %" PRIu32 " exceeds stride %" PRIu32,
974
0
                 nXDst, nSrcWidth, bpp, nDstStep);
975
0
        return FALSE;
976
0
      }
977
978
135
      status = planar_decompress_plane_rle(
979
135
          planes[0], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[0]), pTempData, nTempStep,
980
135
          nXDst, nYDst, nSrcWidth, nSrcHeight, 2, vFlip); /* RedPlane */
981
982
135
      if (status < 0)
983
0
        return FALSE;
984
985
135
      status = planar_decompress_plane_rle(
986
135
          planes[1], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[1]), pTempData, nTempStep,
987
135
          nXDst, nYDst, nSrcWidth, nSrcHeight, 1, vFlip); /* GreenPlane */
988
989
135
      if (status < 0)
990
0
        return FALSE;
991
992
135
      status = planar_decompress_plane_rle(
993
135
          planes[2], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[2]), pTempData, nTempStep,
994
135
          nXDst, nYDst, nSrcWidth, nSrcHeight, 0, vFlip); /* BluePlane */
995
996
135
      if (status < 0)
997
0
        return FALSE;
998
999
135
      srcp += rleSizes[0] + rleSizes[1] + rleSizes[2];
1000
1001
135
      if (useAlpha)
1002
0
      {
1003
0
        status = planar_decompress_plane_rle(
1004
0
            planes[3], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[3]), pTempData,
1005
0
            nTempStep, nXDst, nYDst, nSrcWidth, nSrcHeight, 3, vFlip); /* AlphaPlane */
1006
0
      }
1007
135
      else
1008
135
        status = planar_set_plane(0xFF, pTempData, nTempStep, nXDst, nYDst, nSrcWidth,
1009
135
                                  nSrcHeight, 3, vFlip);
1010
1011
135
      if (status < 0)
1012
0
        return FALSE;
1013
1014
135
      if (alpha)
1015
8
        srcp += rleSizes[3];
1016
135
    }
1017
1018
696
    if (pTempData != pDstData)
1019
696
    {
1020
696
      if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, w, h,
1021
696
                                         pTempData, TempFormat, nTempStep, nXDst, nYDst,
1022
696
                                         nullptr, FREERDP_FLIP_NONE))
1023
696
      {
1024
696
        WLog_ERR(TAG, "planar image copy failed");
1025
696
        return FALSE;
1026
696
      }
1027
696
    }
1028
696
  }
1029
671
  else /* YCoCg */
1030
671
  {
1031
671
    UINT32 TempFormat = 0;
1032
671
    BYTE* pTempData = planar->pTempData;
1033
671
    UINT32 nTempStep = planar->nTempStep;
1034
671
    UINT32 nTotalHeight = planar->maxHeight;
1035
671
    BYTE* dst = &pDstData[nXDst * FreeRDPGetBytesPerPixel(DstFormat) + nYDst * nDstStep];
1036
1037
671
    if (useAlpha)
1038
0
      TempFormat = PIXEL_FORMAT_BGRA32;
1039
671
    else
1040
671
      TempFormat = PIXEL_FORMAT_BGRX32;
1041
1042
671
    if (!pTempData)
1043
0
      return FALSE;
1044
1045
671
    if (rle) /* RLE encoded data. Decode and handle it like raw data. */
1046
197
    {
1047
197
      BYTE* rleBuffer[4] = WINPR_C_ARRAY_INIT;
1048
1049
197
      if (!planar->rlePlanesBuffer)
1050
0
        return FALSE;
1051
1052
197
      rleBuffer[3] = planar->rlePlanesBuffer;  /* AlphaPlane */
1053
197
      rleBuffer[0] = rleBuffer[3] + planeSize; /* LumaOrRedPlane */
1054
197
      rleBuffer[1] = rleBuffer[0] + planeSize; /* OrangeChromaOrGreenPlane */
1055
197
      rleBuffer[2] = rleBuffer[1] + planeSize; /* GreenChromaOrBluePlane */
1056
197
      if (useAlpha)
1057
0
      {
1058
0
        status = planar_decompress_plane_rle_only(
1059
0
            planes[3], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[3]), rleBuffer[3],
1060
0
            rawWidths[3], rawHeights[3]); /* AlphaPlane */
1061
1062
0
        if (status < 0)
1063
0
          return FALSE;
1064
0
      }
1065
1066
197
      if (alpha)
1067
7
        srcp += rleSizes[3];
1068
1069
197
      status = planar_decompress_plane_rle_only(
1070
197
          planes[0], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[0]), rleBuffer[0],
1071
197
          rawWidths[0], rawHeights[0]); /* LumaPlane */
1072
1073
197
      if (status < 0)
1074
0
        return FALSE;
1075
1076
197
      status = planar_decompress_plane_rle_only(
1077
197
          planes[1], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[1]), rleBuffer[1],
1078
197
          rawWidths[1], rawHeights[1]); /* OrangeChromaPlane */
1079
1080
197
      if (status < 0)
1081
0
        return FALSE;
1082
1083
197
      status = planar_decompress_plane_rle_only(
1084
197
          planes[2], WINPR_ASSERTING_INT_CAST(uint32_t, rleSizes[2]), rleBuffer[2],
1085
197
          rawWidths[2], rawHeights[2]); /* GreenChromaPlane */
1086
1087
197
      if (status < 0)
1088
0
        return FALSE;
1089
1090
197
      planes[0] = rleBuffer[0];
1091
197
      planes[1] = rleBuffer[1];
1092
197
      planes[2] = rleBuffer[2];
1093
197
      planes[3] = rleBuffer[3];
1094
197
    }
1095
1096
    /* RAW */
1097
671
    {
1098
671
      if (cs)
1099
219
      { /* Chroma subsampling for Co and Cg:
1100
         * Each pixel contains the value that should be expanded to
1101
         * [2x,2y;2x+1,2y;2x+1,2y+1;2x;2y+1] */
1102
219
        if (!planar_subsample_expand(planes[1], rawSizes[1], nSrcWidth, nSrcHeight,
1103
219
                                     rawWidths[1], rawHeights[1], planar->deltaPlanes[0]))
1104
0
          return FALSE;
1105
1106
219
        planes[1] = planar->deltaPlanes[0];
1107
219
        rawSizes[1] = planeSize; /* OrangeChromaOrGreenPlane */
1108
219
        rawWidths[1] = nSrcWidth;
1109
219
        rawHeights[1] = nSrcHeight;
1110
1111
219
        if (!planar_subsample_expand(planes[2], rawSizes[2], nSrcWidth, nSrcHeight,
1112
219
                                     rawWidths[2], rawHeights[2], planar->deltaPlanes[1]))
1113
0
          return FALSE;
1114
1115
219
        planes[2] = planar->deltaPlanes[1];
1116
219
        rawSizes[2] = planeSize; /* GreenChromaOrBluePlane */
1117
219
        rawWidths[2] = nSrcWidth;
1118
219
        rawHeights[2] = nSrcHeight;
1119
219
      }
1120
1121
671
      if (!planar_decompress_planes_raw(planes, pTempData, TempFormat, nTempStep, nXDst,
1122
671
                                        nYDst, nSrcWidth, nSrcHeight, vFlip, nTotalHeight))
1123
0
        return FALSE;
1124
1125
671
      if (alpha)
1126
269
        srcp += rawSizes[0] + rawSizes[1] + rawSizes[2] + rawSizes[3];
1127
402
      else /* NoAlpha */
1128
402
        srcp += rawSizes[0] + rawSizes[1] + rawSizes[2];
1129
1130
671
      if ((SrcSize - (srcp - pSrcData)) == 1)
1131
0
        srcp++; /* pad */
1132
671
    }
1133
1134
671
    WINPR_ASSERT(prims->YCoCgToRGB_8u_AC4R);
1135
671
    int rc = prims->YCoCgToRGB_8u_AC4R(
1136
671
        pTempData, WINPR_ASSERTING_INT_CAST(int32_t, nTempStep), dst, DstFormat,
1137
671
        WINPR_ASSERTING_INT_CAST(int32_t, nDstStep), w, h, cll, useAlpha);
1138
671
    if (rc != PRIMITIVES_SUCCESS)
1139
0
    {
1140
0
      WLog_ERR(TAG, "YCoCgToRGB_8u_AC4R failed with %d", rc);
1141
0
      return FALSE;
1142
0
    }
1143
671
  }
1144
1145
671
  WINPR_UNUSED(srcp);
1146
671
  return TRUE;
1147
1.36k
}
1148
1149
static inline BOOL freerdp_split_color_planes(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar,
1150
                                              const BYTE* WINPR_RESTRICT data, UINT32 format,
1151
                                              UINT32 width, UINT32 height, UINT32 scanline,
1152
                                              BYTE* WINPR_RESTRICT planes[4])
1153
0
{
1154
0
  WINPR_ASSERT(planar);
1155
1156
0
  if ((width > INT32_MAX) || (height > INT32_MAX) || (scanline > INT32_MAX))
1157
0
    return FALSE;
1158
1159
0
  if (scanline == 0)
1160
0
    scanline = width * FreeRDPGetBytesPerPixel(format);
1161
1162
0
  if (planar->topdown)
1163
0
  {
1164
0
    UINT32 k = 0;
1165
0
    for (UINT32 i = 0; i < height; i++)
1166
0
    {
1167
0
      const BYTE* pixel = &data[1ULL * scanline * i];
1168
1169
0
      for (UINT32 j = 0; j < width; j++)
1170
0
      {
1171
0
        const UINT32 color = FreeRDPReadColor(pixel, format);
1172
0
        pixel += FreeRDPGetBytesPerPixel(format);
1173
0
        FreeRDPSplitColor(color, format, &planes[1][k], &planes[2][k], &planes[3][k],
1174
0
                          &planes[0][k], nullptr);
1175
0
        k++;
1176
0
      }
1177
0
    }
1178
0
  }
1179
0
  else
1180
0
  {
1181
0
    UINT32 k = 0;
1182
1183
0
    for (INT64 i = (INT64)height - 1; i >= 0; i--)
1184
0
    {
1185
0
      const BYTE* pixel = &data[1ULL * scanline * (UINT32)i];
1186
1187
0
      for (UINT32 j = 0; j < width; j++)
1188
0
      {
1189
0
        const UINT32 color = FreeRDPReadColor(pixel, format);
1190
0
        pixel += FreeRDPGetBytesPerPixel(format);
1191
0
        FreeRDPSplitColor(color, format, &planes[1][k], &planes[2][k], &planes[3][k],
1192
0
                          &planes[0][k], nullptr);
1193
0
        k++;
1194
0
      }
1195
0
    }
1196
0
  }
1197
0
  return TRUE;
1198
0
}
1199
1200
static inline UINT32 freerdp_bitmap_planar_write_rle_bytes(const BYTE* WINPR_RESTRICT pInBuffer,
1201
                                                           UINT32 cRawBytes, UINT32 nRunLength,
1202
                                                           BYTE* WINPR_RESTRICT pOutBuffer,
1203
                                                           UINT32 outBufferSize)
1204
0
{
1205
0
  const BYTE* pInput = pInBuffer;
1206
0
  BYTE* pOutput = pOutBuffer;
1207
0
  BYTE controlByte = 0;
1208
0
  UINT32 nBytesToWrite = 0;
1209
1210
0
  if (!cRawBytes && !nRunLength)
1211
0
    return 0;
1212
1213
0
  if (nRunLength < 3)
1214
0
  {
1215
0
    cRawBytes += nRunLength;
1216
0
    nRunLength = 0;
1217
0
  }
1218
1219
0
  while (cRawBytes)
1220
0
  {
1221
0
    if (cRawBytes < 16)
1222
0
    {
1223
0
      if (nRunLength > 15)
1224
0
      {
1225
0
        if (nRunLength < 18)
1226
0
        {
1227
0
          controlByte = PLANAR_CONTROL_BYTE(13, cRawBytes);
1228
0
          nRunLength -= 13;
1229
0
          cRawBytes = 0;
1230
0
        }
1231
0
        else
1232
0
        {
1233
0
          controlByte = PLANAR_CONTROL_BYTE(15, cRawBytes);
1234
0
          nRunLength -= 15;
1235
0
          cRawBytes = 0;
1236
0
        }
1237
0
      }
1238
0
      else
1239
0
      {
1240
0
        controlByte = PLANAR_CONTROL_BYTE(nRunLength, cRawBytes);
1241
0
        nRunLength = 0;
1242
0
        cRawBytes = 0;
1243
0
      }
1244
0
    }
1245
0
    else
1246
0
    {
1247
0
      controlByte = PLANAR_CONTROL_BYTE(0, 15);
1248
0
      cRawBytes -= 15;
1249
0
    }
1250
1251
0
    if (outBufferSize < 1)
1252
0
      return 0;
1253
1254
0
    outBufferSize--;
1255
0
    *pOutput = controlByte;
1256
0
    pOutput++;
1257
0
    nBytesToWrite = (controlByte >> 4);
1258
1259
0
    if (nBytesToWrite)
1260
0
    {
1261
0
      if (outBufferSize < nBytesToWrite)
1262
0
        return 0;
1263
1264
0
      outBufferSize -= nBytesToWrite;
1265
0
      CopyMemory(pOutput, pInput, nBytesToWrite);
1266
0
      pOutput += nBytesToWrite;
1267
0
      pInput += nBytesToWrite;
1268
0
    }
1269
0
  }
1270
1271
0
  while (nRunLength)
1272
0
  {
1273
0
    if (nRunLength > 47)
1274
0
    {
1275
0
      if (nRunLength < 50)
1276
0
      {
1277
0
        controlByte = PLANAR_CONTROL_BYTE(2, 13);
1278
0
        nRunLength -= 45;
1279
0
      }
1280
0
      else
1281
0
      {
1282
0
        controlByte = PLANAR_CONTROL_BYTE(2, 15);
1283
0
        nRunLength -= 47;
1284
0
      }
1285
0
    }
1286
0
    else if (nRunLength > 31)
1287
0
    {
1288
0
      controlByte = PLANAR_CONTROL_BYTE(2, (nRunLength - 32));
1289
0
      nRunLength = 0;
1290
0
    }
1291
0
    else if (nRunLength > 15)
1292
0
    {
1293
0
      controlByte = PLANAR_CONTROL_BYTE(1, (nRunLength - 16));
1294
0
      nRunLength = 0;
1295
0
    }
1296
0
    else
1297
0
    {
1298
0
      controlByte = PLANAR_CONTROL_BYTE(nRunLength, 0);
1299
0
      nRunLength = 0;
1300
0
    }
1301
1302
0
    if (outBufferSize < 1)
1303
0
      return 0;
1304
1305
0
    --outBufferSize;
1306
0
    *pOutput = controlByte;
1307
0
    pOutput++;
1308
0
  }
1309
1310
0
  const intptr_t diff = (pOutput - pOutBuffer);
1311
0
  if ((diff < 0) || (diff > UINT32_MAX))
1312
0
    return 0;
1313
0
  return (UINT32)diff;
1314
0
}
1315
1316
static inline UINT32 freerdp_bitmap_planar_encode_rle_bytes(const BYTE* WINPR_RESTRICT pInBuffer,
1317
                                                            UINT32 inBufferSize,
1318
                                                            BYTE* WINPR_RESTRICT pOutBuffer,
1319
                                                            UINT32 outBufferSize)
1320
0
{
1321
0
  BYTE symbol = 0;
1322
0
  const BYTE* pInput = pInBuffer;
1323
0
  BYTE* pOutput = pOutBuffer;
1324
0
  const BYTE* pBytes = nullptr;
1325
0
  UINT32 cRawBytes = 0;
1326
0
  UINT32 nRunLength = 0;
1327
0
  UINT32 nBytesWritten = 0;
1328
0
  UINT32 nTotalBytesWritten = 0;
1329
1330
0
  if (!outBufferSize)
1331
0
    return 0;
1332
1333
0
  do
1334
0
  {
1335
0
    if (!inBufferSize)
1336
0
      break;
1337
1338
0
    const UINT32 bSymbolMatch = (symbol == *pInput) != 0;
1339
0
    symbol = *pInput;
1340
0
    pInput++;
1341
0
    inBufferSize--;
1342
1343
0
    if (nRunLength && !bSymbolMatch)
1344
0
    {
1345
0
      if (nRunLength < 3)
1346
0
      {
1347
0
        cRawBytes += nRunLength;
1348
0
        nRunLength = 0;
1349
0
      }
1350
0
      else
1351
0
      {
1352
0
        pBytes = pInput - (cRawBytes + nRunLength + 1);
1353
0
        nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, cRawBytes, nRunLength,
1354
0
                                                              pOutput, outBufferSize);
1355
0
        nRunLength = 0;
1356
1357
0
        if (!nBytesWritten || (nBytesWritten > outBufferSize))
1358
0
          return nRunLength;
1359
1360
0
        nTotalBytesWritten += nBytesWritten;
1361
0
        outBufferSize -= nBytesWritten;
1362
0
        pOutput += nBytesWritten;
1363
0
        cRawBytes = 0;
1364
0
      }
1365
0
    }
1366
1367
0
    nRunLength += bSymbolMatch;
1368
0
    cRawBytes += (!bSymbolMatch) != 0;
1369
0
  } while (outBufferSize);
1370
1371
0
  if (cRawBytes || nRunLength)
1372
0
  {
1373
0
    pBytes = pInput - (cRawBytes + nRunLength);
1374
0
    nBytesWritten = freerdp_bitmap_planar_write_rle_bytes(pBytes, cRawBytes, nRunLength,
1375
0
                                                          pOutput, outBufferSize);
1376
1377
0
    if (!nBytesWritten)
1378
0
      return 0;
1379
1380
0
    nTotalBytesWritten += nBytesWritten;
1381
0
  }
1382
1383
0
  if (inBufferSize)
1384
0
    return 0;
1385
1386
0
  return nTotalBytesWritten;
1387
0
}
1388
1389
BOOL freerdp_bitmap_planar_compress_plane_rle(const BYTE* WINPR_RESTRICT inPlane, UINT32 width,
1390
                                              UINT32 height, BYTE* WINPR_RESTRICT outPlane,
1391
                                              UINT32* WINPR_RESTRICT dstSize)
1392
0
{
1393
0
  if (!outPlane)
1394
0
    return FALSE;
1395
1396
0
  UINT32 index = 0;
1397
0
  const BYTE* pInput = inPlane;
1398
0
  BYTE* pOutput = outPlane;
1399
0
  UINT32 outBufferSize = *dstSize;
1400
0
  UINT32 nTotalBytesWritten = 0;
1401
1402
0
  while (outBufferSize > 0)
1403
0
  {
1404
0
    const UINT32 nBytesWritten =
1405
0
        freerdp_bitmap_planar_encode_rle_bytes(pInput, width, pOutput, outBufferSize);
1406
1407
0
    if ((!nBytesWritten) || (nBytesWritten > outBufferSize))
1408
0
      return FALSE;
1409
1410
0
    outBufferSize -= nBytesWritten;
1411
0
    nTotalBytesWritten += nBytesWritten;
1412
0
    pOutput += nBytesWritten;
1413
0
    pInput += width;
1414
0
    index++;
1415
1416
0
    if (index >= height)
1417
0
      break;
1418
0
  }
1419
1420
0
  *dstSize = nTotalBytesWritten;
1421
0
  return TRUE;
1422
0
}
1423
1424
static inline BOOL freerdp_bitmap_planar_compress_planes_rle(BYTE* WINPR_RESTRICT inPlanes[4],
1425
                                                             UINT32 width, UINT32 height,
1426
                                                             BYTE* WINPR_RESTRICT outPlanes,
1427
                                                             UINT32* WINPR_RESTRICT dstSizes,
1428
                                                             BOOL skipAlpha)
1429
0
{
1430
0
  UINT32 outPlanesSize = width * height * 4;
1431
1432
  /* AlphaPlane */
1433
0
  if (skipAlpha)
1434
0
  {
1435
0
    dstSizes[0] = 0;
1436
0
  }
1437
0
  else
1438
0
  {
1439
0
    dstSizes[0] = outPlanesSize;
1440
1441
0
    if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[0], width, height, outPlanes,
1442
0
                                                  &dstSizes[0]))
1443
0
      return FALSE;
1444
1445
0
    outPlanes += dstSizes[0];
1446
0
    outPlanesSize -= dstSizes[0];
1447
0
  }
1448
1449
  /* LumaOrRedPlane */
1450
0
  dstSizes[1] = outPlanesSize;
1451
1452
0
  if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[1], width, height, outPlanes,
1453
0
                                                &dstSizes[1]))
1454
0
    return FALSE;
1455
1456
0
  outPlanes += dstSizes[1];
1457
0
  outPlanesSize -= dstSizes[1];
1458
  /* OrangeChromaOrGreenPlane */
1459
0
  dstSizes[2] = outPlanesSize;
1460
1461
0
  if (!freerdp_bitmap_planar_compress_plane_rle(inPlanes[2], width, height, outPlanes,
1462
0
                                                &dstSizes[2]))
1463
0
    return FALSE;
1464
1465
0
  outPlanes += dstSizes[2];
1466
0
  outPlanesSize -= dstSizes[2];
1467
  /* GreenChromeOrBluePlane */
1468
0
  dstSizes[3] = outPlanesSize;
1469
1470
0
  return (freerdp_bitmap_planar_compress_plane_rle(inPlanes[3], width, height, outPlanes,
1471
0
                                                   &dstSizes[3]));
1472
0
}
1473
1474
BYTE* freerdp_bitmap_planar_delta_encode_plane(const BYTE* WINPR_RESTRICT inPlane, UINT32 width,
1475
                                               UINT32 height, BYTE* WINPR_RESTRICT outPlane)
1476
0
{
1477
0
  if (!outPlane)
1478
0
  {
1479
0
    if (width * height == 0)
1480
0
      return nullptr;
1481
1482
0
    outPlane = (BYTE*)calloc(height, width);
1483
0
    if (!outPlane)
1484
0
      return nullptr;
1485
0
  }
1486
1487
  // first line is copied as is
1488
0
  CopyMemory(outPlane, inPlane, width);
1489
1490
0
  for (UINT32 y = 1; y < height; y++)
1491
0
  {
1492
0
    const size_t off = 1ull * width * y;
1493
0
    BYTE* outPtr = &outPlane[off];
1494
0
    const BYTE* srcPtr = &inPlane[off];
1495
0
    const BYTE* prevLinePtr = &inPlane[off - width];
1496
0
    for (UINT32 x = 0; x < width; x++)
1497
0
    {
1498
0
      const int delta = (int)srcPtr[x] - (int)prevLinePtr[x];
1499
0
      const int s2c1i = (delta >= 0) ? delta : (INT_MAX + delta) + 1;
1500
0
      const int8_t s2c1 = WINPR_CXX_COMPAT_CAST(int8_t, s2c1i);
1501
0
      const uint32_t s2c =
1502
0
          (s2c1 >= 0) ? ((UINT32)s2c1 << 1) : (((UINT32)(~(s2c1) + 1) << 1) - 1);
1503
0
      outPtr[x] = (BYTE)s2c;
1504
0
    }
1505
0
  }
1506
1507
0
  return outPlane;
1508
0
}
1509
1510
static inline BOOL freerdp_bitmap_planar_delta_encode_planes(BYTE* WINPR_RESTRICT inPlanes[4],
1511
                                                             UINT32 width, UINT32 height,
1512
                                                             BYTE* WINPR_RESTRICT outPlanes[4])
1513
0
{
1514
0
  for (UINT32 i = 0; i < 4; i++)
1515
0
  {
1516
0
    outPlanes[i] =
1517
0
        freerdp_bitmap_planar_delta_encode_plane(inPlanes[i], width, height, outPlanes[i]);
1518
1519
0
    if (!outPlanes[i])
1520
0
      return FALSE;
1521
0
  }
1522
1523
0
  return TRUE;
1524
0
}
1525
1526
BYTE* freerdp_bitmap_compress_planar(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT context,
1527
                                     const BYTE* WINPR_RESTRICT data, UINT32 format, UINT32 width,
1528
                                     UINT32 height, UINT32 scanline, BYTE* WINPR_RESTRICT dstData,
1529
                                     UINT32* WINPR_RESTRICT pDstSize)
1530
0
{
1531
0
  UINT32 size = 0;
1532
0
  BYTE* dstp = nullptr;
1533
0
  UINT32 dstSizes[4] = WINPR_C_ARRAY_INIT;
1534
0
  BYTE FormatHeader = 0;
1535
1536
0
  if (!context || !context->rlePlanesBuffer)
1537
0
    return nullptr;
1538
1539
0
  if (context->AllowSkipAlpha)
1540
0
    FormatHeader |= PLANAR_FORMAT_HEADER_NA;
1541
1542
0
  const UINT32 planeSize = width * height;
1543
1544
0
  if (!context->AllowSkipAlpha)
1545
0
    format = planar_invert_format(context, TRUE, format);
1546
1547
0
  if (!freerdp_split_color_planes(context, data, format, width, height, scanline,
1548
0
                                  context->planes))
1549
0
    return nullptr;
1550
1551
0
  if (context->AllowRunLengthEncoding)
1552
0
  {
1553
0
    if (!freerdp_bitmap_planar_delta_encode_planes(context->planes, width, height,
1554
0
                                                   context->deltaPlanes))
1555
0
      return nullptr;
1556
1557
0
    if (!freerdp_bitmap_planar_compress_planes_rle(context->deltaPlanes, width, height,
1558
0
                                                   context->rlePlanesBuffer, dstSizes,
1559
0
                                                   context->AllowSkipAlpha))
1560
0
      return nullptr;
1561
1562
0
    {
1563
0
      uint32_t offset = 0;
1564
0
      FormatHeader |= PLANAR_FORMAT_HEADER_RLE;
1565
0
      context->rlePlanes[0] = &context->rlePlanesBuffer[offset];
1566
0
      offset += dstSizes[0];
1567
0
      context->rlePlanes[1] = &context->rlePlanesBuffer[offset];
1568
0
      offset += dstSizes[1];
1569
0
      context->rlePlanes[2] = &context->rlePlanesBuffer[offset];
1570
0
      offset += dstSizes[2];
1571
0
      context->rlePlanes[3] = &context->rlePlanesBuffer[offset];
1572
1573
#if defined(WITH_DEBUG_CODECS)
1574
      WLog_DBG(TAG,
1575
               "R: [%" PRIu32 "/%" PRIu32 "] G: [%" PRIu32 "/%" PRIu32 "] B: [%" PRIu32
1576
               " / %" PRIu32 "] ",
1577
               dstSizes[1], planeSize, dstSizes[2], planeSize, dstSizes[3], planeSize);
1578
#endif
1579
0
    }
1580
0
  }
1581
1582
0
  if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1583
0
  {
1584
0
    if (!context->AllowRunLengthEncoding)
1585
0
      return nullptr;
1586
1587
0
    if (context->rlePlanes[0] == nullptr)
1588
0
      return nullptr;
1589
1590
0
    if (context->rlePlanes[1] == nullptr)
1591
0
      return nullptr;
1592
1593
0
    if (context->rlePlanes[2] == nullptr)
1594
0
      return nullptr;
1595
1596
0
    if (context->rlePlanes[3] == nullptr)
1597
0
      return nullptr;
1598
0
  }
1599
1600
0
  if (!dstData)
1601
0
  {
1602
0
    size = 1;
1603
1604
0
    if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA))
1605
0
    {
1606
0
      if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1607
0
        size += dstSizes[0];
1608
0
      else
1609
0
        size += planeSize;
1610
0
    }
1611
1612
0
    if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1613
0
      size += (dstSizes[1] + dstSizes[2] + dstSizes[3]);
1614
0
    else
1615
0
      size += (planeSize * 3);
1616
1617
0
    if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE))
1618
0
      size++;
1619
1620
0
    dstData = malloc(size);
1621
1622
0
    if (!dstData)
1623
0
      return nullptr;
1624
1625
0
    *pDstSize = size;
1626
0
  }
1627
1628
0
  dstp = dstData;
1629
0
  *dstp = FormatHeader; /* FormatHeader */
1630
0
  dstp++;
1631
1632
  /* AlphaPlane */
1633
1634
0
  if (!(FormatHeader & PLANAR_FORMAT_HEADER_NA))
1635
0
  {
1636
0
    if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1637
0
    {
1638
0
      CopyMemory(dstp, context->rlePlanes[0], dstSizes[0]); /* Alpha */
1639
0
      dstp += dstSizes[0];
1640
0
    }
1641
0
    else
1642
0
    {
1643
0
      CopyMemory(dstp, context->planes[0], planeSize); /* Alpha */
1644
0
      dstp += planeSize;
1645
0
    }
1646
0
  }
1647
1648
  /* LumaOrRedPlane */
1649
1650
0
  if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1651
0
  {
1652
0
    CopyMemory(dstp, context->rlePlanes[1], dstSizes[1]); /* Red */
1653
0
    dstp += dstSizes[1];
1654
0
  }
1655
0
  else
1656
0
  {
1657
0
    CopyMemory(dstp, context->planes[1], planeSize); /* Red */
1658
0
    dstp += planeSize;
1659
0
  }
1660
1661
  /* OrangeChromaOrGreenPlane */
1662
1663
0
  if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1664
0
  {
1665
0
    CopyMemory(dstp, context->rlePlanes[2], dstSizes[2]); /* Green */
1666
0
    dstp += dstSizes[2];
1667
0
  }
1668
0
  else
1669
0
  {
1670
0
    CopyMemory(dstp, context->planes[2], planeSize); /* Green */
1671
0
    dstp += planeSize;
1672
0
  }
1673
1674
  /* GreenChromeOrBluePlane */
1675
1676
0
  if (FormatHeader & PLANAR_FORMAT_HEADER_RLE)
1677
0
  {
1678
0
    CopyMemory(dstp, context->rlePlanes[3], dstSizes[3]); /* Blue */
1679
0
    dstp += dstSizes[3];
1680
0
  }
1681
0
  else
1682
0
  {
1683
0
    CopyMemory(dstp, context->planes[3], planeSize); /* Blue */
1684
0
    dstp += planeSize;
1685
0
  }
1686
1687
  /* Pad1 (1 byte) */
1688
1689
0
  if (!(FormatHeader & PLANAR_FORMAT_HEADER_RLE))
1690
0
  {
1691
0
    *dstp = 0;
1692
0
    dstp++;
1693
0
  }
1694
1695
0
  const intptr_t diff = (dstp - dstData);
1696
0
  if ((diff < 0) || (diff > UINT32_MAX))
1697
0
  {
1698
0
    free(dstData);
1699
0
    return nullptr;
1700
0
  }
1701
0
  size = (UINT32)diff;
1702
0
  *pDstSize = size;
1703
0
  return dstData;
1704
0
}
1705
1706
BOOL freerdp_bitmap_planar_context_reset(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT context,
1707
                                         UINT32 width, UINT32 height)
1708
5.76k
{
1709
5.76k
  if (!context)
1710
0
    return FALSE;
1711
1712
5.76k
  context->bgr = FALSE;
1713
5.76k
  context->maxWidth = PLANAR_ALIGN(width, 4);
1714
5.76k
  context->maxHeight = PLANAR_ALIGN(height, 4);
1715
5.76k
  {
1716
5.76k
    const UINT64 tmp = (UINT64)context->maxWidth * context->maxHeight;
1717
5.76k
    if (tmp > UINT32_MAX)
1718
0
      return FALSE;
1719
5.76k
    context->maxPlaneSize = (UINT32)tmp;
1720
5.76k
  }
1721
1722
5.76k
  if (context->maxWidth > UINT32_MAX / 4)
1723
0
    return FALSE;
1724
5.76k
  context->nTempStep = context->maxWidth * 4;
1725
1726
5.76k
  memset((void*)context->planes, 0, sizeof(context->planes));
1727
5.76k
  memset((void*)context->rlePlanes, 0, sizeof(context->rlePlanes));
1728
5.76k
  memset((void*)context->deltaPlanes, 0, sizeof(context->deltaPlanes));
1729
1730
5.76k
  if (context->maxPlaneSize > 0)
1731
5.76k
  {
1732
5.76k
    void* tmp = winpr_aligned_recalloc(context->planesBuffer, context->maxPlaneSize, 4, 32);
1733
5.76k
    if (!tmp)
1734
0
      return FALSE;
1735
5.76k
    context->planesBuffer = tmp;
1736
1737
5.76k
    tmp = winpr_aligned_recalloc(context->pTempData, context->maxPlaneSize, 6, 32);
1738
5.76k
    if (!tmp)
1739
0
      return FALSE;
1740
5.76k
    context->pTempData = tmp;
1741
1742
5.76k
    tmp = winpr_aligned_recalloc(context->deltaPlanesBuffer, context->maxPlaneSize, 4, 32);
1743
5.76k
    if (!tmp)
1744
0
      return FALSE;
1745
5.76k
    context->deltaPlanesBuffer = tmp;
1746
1747
5.76k
    tmp = winpr_aligned_recalloc(context->rlePlanesBuffer, context->maxPlaneSize, 4, 32);
1748
5.76k
    if (!tmp)
1749
0
      return FALSE;
1750
5.76k
    context->rlePlanesBuffer = tmp;
1751
1752
5.76k
    context->planes[0] = &context->planesBuffer[0ULL * context->maxPlaneSize];
1753
5.76k
    context->planes[1] = &context->planesBuffer[1ULL * context->maxPlaneSize];
1754
5.76k
    context->planes[2] = &context->planesBuffer[2ULL * context->maxPlaneSize];
1755
5.76k
    context->planes[3] = &context->planesBuffer[3ULL * context->maxPlaneSize];
1756
5.76k
    context->deltaPlanes[0] = &context->deltaPlanesBuffer[0ULL * context->maxPlaneSize];
1757
5.76k
    context->deltaPlanes[1] = &context->deltaPlanesBuffer[1ULL * context->maxPlaneSize];
1758
5.76k
    context->deltaPlanes[2] = &context->deltaPlanesBuffer[2ULL * context->maxPlaneSize];
1759
5.76k
    context->deltaPlanes[3] = &context->deltaPlanesBuffer[3ULL * context->maxPlaneSize];
1760
5.76k
  }
1761
5.76k
  return TRUE;
1762
5.76k
}
1763
1764
BITMAP_PLANAR_CONTEXT* freerdp_bitmap_planar_context_new(DWORD flags, UINT32 maxWidth,
1765
                                                         UINT32 maxHeight)
1766
5.76k
{
1767
5.76k
  BITMAP_PLANAR_CONTEXT* context =
1768
5.76k
      (BITMAP_PLANAR_CONTEXT*)winpr_aligned_calloc(1, sizeof(BITMAP_PLANAR_CONTEXT), 32);
1769
1770
5.76k
  if (!context)
1771
0
    return nullptr;
1772
1773
5.76k
  if (flags & PLANAR_FORMAT_HEADER_NA)
1774
5.76k
    context->AllowSkipAlpha = TRUE;
1775
1776
5.76k
  if (flags & PLANAR_FORMAT_HEADER_RLE)
1777
5.76k
    context->AllowRunLengthEncoding = TRUE;
1778
1779
5.76k
  if (flags & PLANAR_FORMAT_HEADER_CS)
1780
0
    context->AllowColorSubsampling = TRUE;
1781
1782
5.76k
  context->ColorLossLevel = flags & PLANAR_FORMAT_HEADER_CLL_MASK;
1783
1784
5.76k
  if (context->ColorLossLevel)
1785
0
    context->AllowDynamicColorFidelity = TRUE;
1786
1787
5.76k
  if (!freerdp_bitmap_planar_context_reset(context, maxWidth, maxHeight))
1788
0
  {
1789
0
    WINPR_PRAGMA_DIAG_PUSH
1790
0
    WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
1791
0
    freerdp_bitmap_planar_context_free(context);
1792
0
    WINPR_PRAGMA_DIAG_POP
1793
0
    return nullptr;
1794
0
  }
1795
1796
5.76k
  return context;
1797
5.76k
}
1798
1799
void freerdp_bitmap_planar_context_free(BITMAP_PLANAR_CONTEXT* context)
1800
5.76k
{
1801
5.76k
  if (!context)
1802
0
    return;
1803
1804
5.76k
  winpr_aligned_free(context->pTempData);
1805
5.76k
  winpr_aligned_free(context->planesBuffer);
1806
5.76k
  winpr_aligned_free(context->deltaPlanesBuffer);
1807
5.76k
  winpr_aligned_free(context->rlePlanesBuffer);
1808
5.76k
  winpr_aligned_free(context);
1809
5.76k
}
1810
1811
void freerdp_planar_switch_bgr(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL bgr)
1812
0
{
1813
0
  WINPR_ASSERT(planar);
1814
0
  planar->bgr = bgr;
1815
0
}
1816
1817
void freerdp_planar_topdown_image(BITMAP_PLANAR_CONTEXT* WINPR_RESTRICT planar, BOOL topdown)
1818
0
{
1819
0
  WINPR_ASSERT(planar);
1820
0
  planar->topdown = topdown;
1821
0
}