Coverage Report

Created: 2025-07-01 06:46

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