Coverage Report

Created: 2026-10-28 06:50

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