Coverage Report

Created: 2026-08-31 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/codec/include/bitmap.h
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * RLE Compressed Bitmap Stream
4
 *
5
 * Copyright 2011 Jay Sorg <jay.sorg@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 <winpr/assert.h>
23
#include <winpr/cast.h>
24
#include <winpr/wtypes.h>
25
26
/* do not compile the file directly */
27
28
/**
29
 * Write a foreground/background image to a destination buffer.
30
 */
31
WINPR_ATTR_NODISCARD
32
static inline BYTE* WRITEFGBGIMAGE(BYTE* WINPR_RESTRICT pbDest,
33
                                   const BYTE* WINPR_RESTRICT pbDestEnd, UINT32 rowDelta,
34
                                   BYTE bitmask, PIXEL fgPel, UINT32 cBits)
35
0
{
36
0
  PIXEL xorPixel = 0;
37
0
  BYTE mask = 0x01;
38
39
0
  if (cBits > 8)
40
0
  {
41
0
    WLog_ERR(TAG, "cBits %" PRIu32 " > 8", cBits);
42
0
    return nullptr;
43
0
  }
44
45
0
  if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
46
0
    return nullptr;
47
48
0
  UNROLL(cBits, {
49
0
    PIXEL data = 0;
50
0
    DESTREADPIXEL(xorPixel, pbDest - rowDelta);
51
52
0
    if (bitmask & mask)
53
0
      data = xorPixel ^ fgPel;
54
0
    else
55
0
      data = xorPixel;
56
57
0
    DESTWRITEPIXEL(pbDest, data);
58
0
    mask = WINPR_ASSERTING_INT_CAST(BYTE, (mask << 1) & 0xFF);
59
0
  });
60
0
  return pbDest;
61
0
}
Unexecuted instantiation: interleaved.c:WriteFgBgImage24to24
Unexecuted instantiation: interleaved.c:WriteFgBgImage16to16
Unexecuted instantiation: interleaved.c:WriteFgBgImage8to8
62
63
/**
64
 * Write a foreground/background image to a destination buffer
65
 * for the first line of compressed data.
66
 */
67
WINPR_ATTR_NODISCARD
68
static inline BYTE* WRITEFIRSTLINEFGBGIMAGE(BYTE* WINPR_RESTRICT pbDest,
69
                                            const BYTE* WINPR_RESTRICT pbDestEnd, BYTE bitmask,
70
                                            PIXEL fgPel, UINT32 cBits)
71
0
{
72
0
  BYTE mask = 0x01;
73
74
0
  if (cBits > 8)
75
0
  {
76
0
    WLog_ERR(TAG, "cBits %" PRIu32 " > 8", cBits);
77
0
    return nullptr;
78
0
  }
79
80
0
  if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
81
0
    return nullptr;
82
83
0
  UNROLL(cBits, {
84
0
    PIXEL data;
85
86
0
    if (bitmask & mask)
87
0
      data = fgPel;
88
0
    else
89
0
      data = BLACK_PIXEL;
90
91
0
    DESTWRITEPIXEL(pbDest, data);
92
0
    mask = WINPR_ASSERTING_INT_CAST(BYTE, (mask << 1) & 0xFF);
93
0
  });
94
0
  return pbDest;
95
0
}
Unexecuted instantiation: interleaved.c:WriteFirstLineFgBgImage24to24
Unexecuted instantiation: interleaved.c:WriteFirstLineFgBgImage16to16
Unexecuted instantiation: interleaved.c:WriteFirstLineFgBgImage8to8
96
97
/**
98
 * Decompress an RLE compressed bitmap.
99
 */
100
WINPR_ATTR_NODISCARD
101
static inline BOOL RLEDECOMPRESS(const BYTE* WINPR_RESTRICT pbSrcBuffer, UINT32 cbSrcBuffer,
102
                                 BYTE* WINPR_RESTRICT pbDestBuffer, UINT32 rowDelta, UINT32 width,
103
                                 UINT32 height)
104
0
{
105
0
  const BYTE* pbSrc = pbSrcBuffer;
106
0
  BYTE* pbDest = pbDestBuffer;
107
0
  PIXEL temp = 0;
108
0
  PIXEL fgPel = WHITE_PIXEL;
109
0
  BOOL fInsertFgPel = FALSE;
110
0
  BOOL fFirstLine = TRUE;
111
0
  BYTE bitmask = 0;
112
0
  PIXEL pixelA = 0;
113
0
  PIXEL pixelB = 0;
114
0
  UINT32 runLength = 0;
115
0
  UINT32 code = 0;
116
0
  UINT32 advance = 0;
117
0
  RLEEXTRA
118
119
0
  if ((rowDelta == 0) || (rowDelta < width))
120
0
  {
121
0
    WLog_ERR(TAG, "Invalid arguments: rowDelta=%" PRIu32 " == 0 || < width=%" PRIu32, rowDelta,
122
0
             width);
123
0
    return FALSE;
124
0
  }
125
126
0
  if (!pbSrcBuffer || !pbDestBuffer)
127
0
  {
128
0
    WLog_ERR(TAG, "Invalid arguments: pbSrcBuffer=%p, pbDestBuffer=%p",
129
0
             WINPR_CXX_COMPAT_CAST(const void*, pbSrcBuffer),
130
0
             WINPR_CXX_COMPAT_CAST(const void*, pbDestBuffer));
131
0
    return FALSE;
132
0
  }
133
134
0
  const BYTE* pbEnd = &pbSrcBuffer[cbSrcBuffer];
135
0
  const BYTE* pbDestEnd = &pbDestBuffer[1ULL * rowDelta * height];
136
137
0
  while (pbSrc < pbEnd)
138
0
  {
139
    /* Watch out for the end of the first scanline. */
140
0
    if (fFirstLine)
141
0
    {
142
0
      if ((UINT32)(pbDest - pbDestBuffer) >= rowDelta)
143
0
      {
144
0
        fFirstLine = FALSE;
145
0
        fInsertFgPel = FALSE;
146
0
      }
147
0
    }
148
149
    /*
150
       Extract the compression order code ID from the compression
151
       order header.
152
    */
153
0
    code = ExtractCodeId(*pbSrc);
154
155
#if defined(WITH_DEBUG_CODECS)
156
    WLog_VRB(TAG, "pbSrc=%p code=%s, rem=%ld", (const void*)pbSrc, rle_code_str(code),
157
             pbEnd - pbSrc);
158
#endif
159
160
    /* Handle Background Run Orders. */
161
0
    if ((code == REGULAR_BG_RUN) || (code == MEGA_MEGA_BG_RUN))
162
0
    {
163
0
      runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
164
0
      if (advance == 0)
165
0
        return FALSE;
166
0
      pbSrc = pbSrc + advance;
167
168
0
      if (fFirstLine)
169
0
      {
170
0
        if (fInsertFgPel)
171
0
        {
172
0
          if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
173
0
            return FALSE;
174
175
0
          DESTWRITEPIXEL(pbDest, fgPel);
176
0
          runLength = runLength - 1;
177
0
        }
178
179
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
180
0
          return FALSE;
181
182
0
        UNROLL(runLength, { DESTWRITEPIXEL(pbDest, BLACK_PIXEL); });
183
0
      }
184
0
      else
185
0
      {
186
0
        if (fInsertFgPel)
187
0
        {
188
0
          DESTREADPIXEL(temp, pbDest - rowDelta);
189
190
0
          if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
191
0
            return FALSE;
192
193
0
          DESTWRITEPIXEL(pbDest, temp ^ fgPel);
194
0
          runLength--;
195
0
        }
196
197
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
198
0
          return FALSE;
199
200
0
        UNROLL(runLength, {
201
0
          DESTREADPIXEL(temp, pbDest - rowDelta);
202
0
          DESTWRITEPIXEL(pbDest, temp);
203
0
        });
204
0
      }
205
206
      /* A follow-on background run order will need a foreground pel inserted. */
207
0
      fInsertFgPel = TRUE;
208
0
      continue;
209
0
    }
210
211
    /* For any of the other run-types a follow-on background run
212
        order does not need a foreground pel inserted. */
213
0
    fInsertFgPel = FALSE;
214
215
0
    switch (code)
216
0
    {
217
      /* Handle Foreground Run Orders. */
218
0
      case REGULAR_FG_RUN:
219
0
      case MEGA_MEGA_FG_RUN:
220
0
      case LITE_SET_FG_FG_RUN:
221
0
      case MEGA_MEGA_SET_FG_RUN:
222
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
223
0
        if (advance == 0)
224
0
          return FALSE;
225
0
        pbSrc = pbSrc + advance;
226
227
0
        if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN)
228
0
        {
229
0
          if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
230
0
            return FALSE;
231
0
          SRCREADPIXEL(fgPel, pbSrc);
232
0
        }
233
234
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
235
0
          return FALSE;
236
237
0
        if (fFirstLine)
238
0
        {
239
0
          UNROLL(runLength, { DESTWRITEPIXEL(pbDest, fgPel); });
240
0
        }
241
0
        else
242
0
        {
243
0
          UNROLL(runLength, {
244
0
            DESTREADPIXEL(temp, pbDest - rowDelta);
245
0
            DESTWRITEPIXEL(pbDest, temp ^ fgPel);
246
0
          });
247
0
        }
248
249
0
        break;
250
251
      /* Handle Dithered Run Orders. */
252
0
      case LITE_DITHERED_RUN:
253
0
      case MEGA_MEGA_DITHERED_RUN:
254
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
255
0
        if (advance == 0)
256
0
          return FALSE;
257
0
        pbSrc = pbSrc + advance;
258
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
259
0
          return FALSE;
260
0
        SRCREADPIXEL(pixelA, pbSrc);
261
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
262
0
          return FALSE;
263
0
        SRCREADPIXEL(pixelB, pbSrc);
264
265
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength * 2))
266
0
          return FALSE;
267
268
0
        UNROLL(runLength, {
269
0
          DESTWRITEPIXEL(pbDest, pixelA);
270
0
          DESTWRITEPIXEL(pbDest, pixelB);
271
0
        });
272
0
        break;
273
274
      /* Handle Color Run Orders. */
275
0
      case REGULAR_COLOR_RUN:
276
0
      case MEGA_MEGA_COLOR_RUN:
277
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
278
0
        if (advance == 0)
279
0
          return FALSE;
280
0
        pbSrc = pbSrc + advance;
281
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
282
0
          return FALSE;
283
0
        SRCREADPIXEL(pixelA, pbSrc);
284
285
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
286
0
          return FALSE;
287
288
0
        UNROLL(runLength, { DESTWRITEPIXEL(pbDest, pixelA); });
289
0
        break;
290
291
      /* Handle Foreground/Background Image Orders. */
292
0
      case REGULAR_FGBG_IMAGE:
293
0
      case MEGA_MEGA_FGBG_IMAGE:
294
0
      case LITE_SET_FG_FGBG_IMAGE:
295
0
      case MEGA_MEGA_SET_FGBG_IMAGE:
296
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
297
0
        if (advance == 0)
298
0
          return FALSE;
299
0
        pbSrc = pbSrc + advance;
300
301
0
        if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE)
302
0
        {
303
0
          if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
304
0
            return FALSE;
305
0
          SRCREADPIXEL(fgPel, pbSrc);
306
0
        }
307
308
0
        if (!buffer_within_range(pbSrc, runLength / 8, pbEnd))
309
0
          return FALSE;
310
0
        if (fFirstLine)
311
0
        {
312
0
          while (runLength > 8)
313
0
          {
314
0
            bitmask = *pbSrc;
315
0
            pbSrc = pbSrc + 1;
316
0
            pbDest = WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, bitmask, fgPel, 8);
317
318
0
            if (!pbDest)
319
0
              return FALSE;
320
321
0
            runLength = runLength - 8;
322
0
          }
323
0
        }
324
0
        else
325
0
        {
326
0
          while (runLength > 8)
327
0
          {
328
0
            bitmask = *pbSrc++;
329
330
0
            pbDest = WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, 8);
331
332
0
            if (!pbDest)
333
0
              return FALSE;
334
335
0
            runLength = runLength - 8;
336
0
          }
337
0
        }
338
339
0
        if (runLength > 0)
340
0
        {
341
0
          if (!buffer_within_range(pbSrc, 1, pbEnd))
342
0
            return FALSE;
343
0
          bitmask = *pbSrc++;
344
345
0
          if (fFirstLine)
346
0
          {
347
0
            pbDest =
348
0
                WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, bitmask, fgPel, runLength);
349
0
          }
350
0
          else
351
0
          {
352
0
            pbDest =
353
0
                WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, runLength);
354
0
          }
355
356
0
          if (!pbDest)
357
0
            return FALSE;
358
0
        }
359
360
0
        break;
361
362
      /* Handle Color Image Orders. */
363
0
      case REGULAR_COLOR_IMAGE:
364
0
      case MEGA_MEGA_COLOR_IMAGE:
365
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
366
0
        if (advance == 0)
367
0
          return FALSE;
368
0
        pbSrc = pbSrc + advance;
369
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
370
0
          return FALSE;
371
0
        if (!ENSURE_CAPACITY(pbSrc, pbEnd, runLength))
372
0
          return FALSE;
373
374
0
        UNROLL(runLength, {
375
0
          SRCREADPIXEL(temp, pbSrc);
376
0
          DESTWRITEPIXEL(pbDest, temp);
377
0
        });
378
0
        break;
379
380
      /* Handle Special Order 1. */
381
0
      case SPECIAL_FGBG_1:
382
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
383
0
          return FALSE;
384
0
        pbSrc = pbSrc + 1;
385
386
0
        if (fFirstLine)
387
0
        {
388
0
          pbDest =
389
0
              WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, g_MaskSpecialFgBg1, fgPel, 8);
390
0
        }
391
0
        else
392
0
        {
393
0
          pbDest =
394
0
              WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, g_MaskSpecialFgBg1, fgPel, 8);
395
0
        }
396
397
0
        if (!pbDest)
398
0
          return FALSE;
399
400
0
        break;
401
402
      /* Handle Special Order 2. */
403
0
      case SPECIAL_FGBG_2:
404
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
405
0
          return FALSE;
406
0
        pbSrc = pbSrc + 1;
407
408
0
        if (fFirstLine)
409
0
        {
410
0
          pbDest =
411
0
              WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, g_MaskSpecialFgBg2, fgPel, 8);
412
0
        }
413
0
        else
414
0
        {
415
0
          pbDest =
416
0
              WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, g_MaskSpecialFgBg2, fgPel, 8);
417
0
        }
418
419
0
        if (!pbDest)
420
0
          return FALSE;
421
422
0
        break;
423
424
      /* Handle White Order. */
425
0
      case SPECIAL_WHITE:
426
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
427
0
          return FALSE;
428
0
        pbSrc = pbSrc + 1;
429
430
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
431
0
          return FALSE;
432
433
0
        DESTWRITEPIXEL(pbDest, WHITE_PIXEL);
434
0
        break;
435
436
      /* Handle Black Order. */
437
0
      case SPECIAL_BLACK:
438
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
439
0
          return FALSE;
440
0
        pbSrc = pbSrc + 1;
441
442
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
443
0
          return FALSE;
444
445
0
        DESTWRITEPIXEL(pbDest, BLACK_PIXEL);
446
0
        break;
447
448
0
      default:
449
0
        WLog_ERR(TAG, "invalid code 0x%08" PRIx32 ", pbSrcBuffer=%p, pbSrc=%p, pbEnd=%p",
450
0
                 code, WINPR_CXX_COMPAT_CAST(const void*, pbSrcBuffer),
451
0
                 WINPR_CXX_COMPAT_CAST(const void*, pbSrc),
452
0
                 WINPR_CXX_COMPAT_CAST(const void*, pbEnd));
453
0
        return FALSE;
454
0
    }
455
0
  }
456
457
0
  return TRUE;
458
0
}
Unexecuted instantiation: interleaved.c:RleDecompress24to24
Unexecuted instantiation: interleaved.c:RleDecompress16to16
Unexecuted instantiation: interleaved.c:RleDecompress8to8