Coverage Report

Created: 2026-02-26 06:50

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 NULL;
43
0
  }
44
45
0
  if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
46
0
    return NULL;
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 NULL;
78
0
  }
79
80
0
  if (!ENSURE_CAPACITY(pbDest, pbDestEnd, cBits))
81
0
    return NULL;
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=%" PRIuz, pbSrc, rle_code_str(code), pbEnd - pbSrc);
157
#endif
158
159
    /* Handle Background Run Orders. */
160
0
    if ((code == REGULAR_BG_RUN) || (code == MEGA_MEGA_BG_RUN))
161
0
    {
162
0
      runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
163
0
      if (advance == 0)
164
0
        return FALSE;
165
0
      pbSrc = pbSrc + advance;
166
167
0
      if (fFirstLine)
168
0
      {
169
0
        if (fInsertFgPel)
170
0
        {
171
0
          if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
172
0
            return FALSE;
173
174
0
          DESTWRITEPIXEL(pbDest, fgPel);
175
0
          runLength = runLength - 1;
176
0
        }
177
178
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
179
0
          return FALSE;
180
181
0
        UNROLL(runLength, { DESTWRITEPIXEL(pbDest, BLACK_PIXEL); });
182
0
      }
183
0
      else
184
0
      {
185
0
        if (fInsertFgPel)
186
0
        {
187
0
          DESTREADPIXEL(temp, pbDest - rowDelta);
188
189
0
          if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
190
0
            return FALSE;
191
192
0
          DESTWRITEPIXEL(pbDest, temp ^ fgPel);
193
0
          runLength--;
194
0
        }
195
196
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
197
0
          return FALSE;
198
199
0
        UNROLL(runLength, {
200
0
          DESTREADPIXEL(temp, pbDest - rowDelta);
201
0
          DESTWRITEPIXEL(pbDest, temp);
202
0
        });
203
0
      }
204
205
      /* A follow-on background run order will need a foreground pel inserted. */
206
0
      fInsertFgPel = TRUE;
207
0
      continue;
208
0
    }
209
210
    /* For any of the other run-types a follow-on background run
211
        order does not need a foreground pel inserted. */
212
0
    fInsertFgPel = FALSE;
213
214
0
    switch (code)
215
0
    {
216
      /* Handle Foreground Run Orders. */
217
0
      case REGULAR_FG_RUN:
218
0
      case MEGA_MEGA_FG_RUN:
219
0
      case LITE_SET_FG_FG_RUN:
220
0
      case MEGA_MEGA_SET_FG_RUN:
221
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
222
0
        if (advance == 0)
223
0
          return FALSE;
224
0
        pbSrc = pbSrc + advance;
225
226
0
        if (code == LITE_SET_FG_FG_RUN || code == MEGA_MEGA_SET_FG_RUN)
227
0
        {
228
0
          if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
229
0
            return FALSE;
230
0
          SRCREADPIXEL(fgPel, pbSrc);
231
0
        }
232
233
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
234
0
          return FALSE;
235
236
0
        if (fFirstLine)
237
0
        {
238
0
          UNROLL(runLength, { DESTWRITEPIXEL(pbDest, fgPel); });
239
0
        }
240
0
        else
241
0
        {
242
0
          UNROLL(runLength, {
243
0
            DESTREADPIXEL(temp, pbDest - rowDelta);
244
0
            DESTWRITEPIXEL(pbDest, temp ^ fgPel);
245
0
          });
246
0
        }
247
248
0
        break;
249
250
      /* Handle Dithered Run Orders. */
251
0
      case LITE_DITHERED_RUN:
252
0
      case MEGA_MEGA_DITHERED_RUN:
253
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
254
0
        if (advance == 0)
255
0
          return FALSE;
256
0
        pbSrc = pbSrc + advance;
257
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
258
0
          return FALSE;
259
0
        SRCREADPIXEL(pixelA, pbSrc);
260
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
261
0
          return FALSE;
262
0
        SRCREADPIXEL(pixelB, pbSrc);
263
264
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength * 2))
265
0
          return FALSE;
266
267
0
        UNROLL(runLength, {
268
0
          DESTWRITEPIXEL(pbDest, pixelA);
269
0
          DESTWRITEPIXEL(pbDest, pixelB);
270
0
        });
271
0
        break;
272
273
      /* Handle Color Run Orders. */
274
0
      case REGULAR_COLOR_RUN:
275
0
      case MEGA_MEGA_COLOR_RUN:
276
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
277
0
        if (advance == 0)
278
0
          return FALSE;
279
0
        pbSrc = pbSrc + advance;
280
0
        if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
281
0
          return FALSE;
282
0
        SRCREADPIXEL(pixelA, pbSrc);
283
284
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
285
0
          return FALSE;
286
287
0
        UNROLL(runLength, { DESTWRITEPIXEL(pbDest, pixelA); });
288
0
        break;
289
290
      /* Handle Foreground/Background Image Orders. */
291
0
      case REGULAR_FGBG_IMAGE:
292
0
      case MEGA_MEGA_FGBG_IMAGE:
293
0
      case LITE_SET_FG_FGBG_IMAGE:
294
0
      case MEGA_MEGA_SET_FGBG_IMAGE:
295
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
296
0
        if (advance == 0)
297
0
          return FALSE;
298
0
        pbSrc = pbSrc + advance;
299
300
0
        if (code == LITE_SET_FG_FGBG_IMAGE || code == MEGA_MEGA_SET_FGBG_IMAGE)
301
0
        {
302
0
          if (!buffer_within_range(pbSrc, PIXEL_SIZE, pbEnd))
303
0
            return FALSE;
304
0
          SRCREADPIXEL(fgPel, pbSrc);
305
0
        }
306
307
0
        if (!buffer_within_range(pbSrc, runLength / 8, pbEnd))
308
0
          return FALSE;
309
0
        if (fFirstLine)
310
0
        {
311
0
          while (runLength > 8)
312
0
          {
313
0
            bitmask = *pbSrc;
314
0
            pbSrc = pbSrc + 1;
315
0
            pbDest = WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, bitmask, fgPel, 8);
316
317
0
            if (!pbDest)
318
0
              return FALSE;
319
320
0
            runLength = runLength - 8;
321
0
          }
322
0
        }
323
0
        else
324
0
        {
325
0
          while (runLength > 8)
326
0
          {
327
0
            bitmask = *pbSrc++;
328
329
0
            pbDest = WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, 8);
330
331
0
            if (!pbDest)
332
0
              return FALSE;
333
334
0
            runLength = runLength - 8;
335
0
          }
336
0
        }
337
338
0
        if (runLength > 0)
339
0
        {
340
0
          if (!buffer_within_range(pbSrc, 1, pbEnd))
341
0
            return FALSE;
342
0
          bitmask = *pbSrc++;
343
344
0
          if (fFirstLine)
345
0
          {
346
0
            pbDest =
347
0
                WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, bitmask, fgPel, runLength);
348
0
          }
349
0
          else
350
0
          {
351
0
            pbDest =
352
0
                WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, bitmask, fgPel, runLength);
353
0
          }
354
355
0
          if (!pbDest)
356
0
            return FALSE;
357
0
        }
358
359
0
        break;
360
361
      /* Handle Color Image Orders. */
362
0
      case REGULAR_COLOR_IMAGE:
363
0
      case MEGA_MEGA_COLOR_IMAGE:
364
0
        runLength = ExtractRunLength(code, pbSrc, pbEnd, &advance);
365
0
        if (advance == 0)
366
0
          return FALSE;
367
0
        pbSrc = pbSrc + advance;
368
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, runLength))
369
0
          return FALSE;
370
0
        if (!ENSURE_CAPACITY(pbSrc, pbEnd, runLength))
371
0
          return FALSE;
372
373
0
        UNROLL(runLength, {
374
0
          SRCREADPIXEL(temp, pbSrc);
375
0
          DESTWRITEPIXEL(pbDest, temp);
376
0
        });
377
0
        break;
378
379
      /* Handle Special Order 1. */
380
0
      case SPECIAL_FGBG_1:
381
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
382
0
          return FALSE;
383
0
        pbSrc = pbSrc + 1;
384
385
0
        if (fFirstLine)
386
0
        {
387
0
          pbDest =
388
0
              WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, g_MaskSpecialFgBg1, fgPel, 8);
389
0
        }
390
0
        else
391
0
        {
392
0
          pbDest =
393
0
              WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, g_MaskSpecialFgBg1, fgPel, 8);
394
0
        }
395
396
0
        if (!pbDest)
397
0
          return FALSE;
398
399
0
        break;
400
401
      /* Handle Special Order 2. */
402
0
      case SPECIAL_FGBG_2:
403
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
404
0
          return FALSE;
405
0
        pbSrc = pbSrc + 1;
406
407
0
        if (fFirstLine)
408
0
        {
409
0
          pbDest =
410
0
              WRITEFIRSTLINEFGBGIMAGE(pbDest, pbDestEnd, g_MaskSpecialFgBg2, fgPel, 8);
411
0
        }
412
0
        else
413
0
        {
414
0
          pbDest =
415
0
              WRITEFGBGIMAGE(pbDest, pbDestEnd, rowDelta, g_MaskSpecialFgBg2, fgPel, 8);
416
0
        }
417
418
0
        if (!pbDest)
419
0
          return FALSE;
420
421
0
        break;
422
423
      /* Handle White Order. */
424
0
      case SPECIAL_WHITE:
425
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
426
0
          return FALSE;
427
0
        pbSrc = pbSrc + 1;
428
429
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
430
0
          return FALSE;
431
432
0
        DESTWRITEPIXEL(pbDest, WHITE_PIXEL);
433
0
        break;
434
435
      /* Handle Black Order. */
436
0
      case SPECIAL_BLACK:
437
0
        if (!buffer_within_range(pbSrc, 1, pbEnd))
438
0
          return FALSE;
439
0
        pbSrc = pbSrc + 1;
440
441
0
        if (!ENSURE_CAPACITY(pbDest, pbDestEnd, 1))
442
0
          return FALSE;
443
444
0
        DESTWRITEPIXEL(pbDest, BLACK_PIXEL);
445
0
        break;
446
447
0
      default:
448
0
        WLog_ERR(TAG, "invalid code 0x%08" PRIx32 ", pbSrcBuffer=%p, pbSrc=%p, pbEnd=%p",
449
0
                 code, WINPR_CXX_COMPAT_CAST(const void*, pbSrcBuffer),
450
0
                 WINPR_CXX_COMPAT_CAST(const void*, pbSrc),
451
0
                 WINPR_CXX_COMPAT_CAST(const void*, pbEnd));
452
0
        return FALSE;
453
0
    }
454
0
  }
455
456
0
  return TRUE;
457
0
}
Unexecuted instantiation: interleaved.c:RleDecompress24to24
Unexecuted instantiation: interleaved.c:RleDecompress16to16
Unexecuted instantiation: interleaved.c:RleDecompress8to8