Coverage Report

Created: 2026-10-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/codec/bitmap.c
Line
Count
Source
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Bitmap Compression
4
 *
5
 * Copyright 2004-2012 Jay Sorg <jay.sorg@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/assert.h>
21
#include <winpr/cast.h>
22
23
#include <freerdp/config.h>
24
25
#include <freerdp/codec/bitmap.h>
26
#include <freerdp/codec/planar.h>
27
28
struct count
29
{
30
  ALIGN64 UINT16 bicolor_count;
31
  ALIGN64 UINT16 fill_count;
32
  ALIGN64 UINT16 color_count;
33
  ALIGN64 UINT16 mix_count;
34
  ALIGN64 UINT16 fom_count;
35
  ALIGN64 size_t fom_mask_len;
36
  ALIGN64 BOOL bicolor_spin;
37
};
38
39
static inline void reset_counts(struct count* counts)
40
0
{
41
0
  const struct count empty = WINPR_C_ARRAY_INIT;
42
0
  WINPR_ASSERT(counts);
43
0
  *counts = empty;
44
0
}
45
46
static inline UINT16 GETPIXEL16(const void* WINPR_RESTRICT d, UINT32 x, UINT32 y, UINT32 w)
47
0
{
48
0
  const BYTE* WINPR_RESTRICT src = (const BYTE*)d + ((y * w + x) * sizeof(UINT16));
49
0
  return WINPR_ASSERTING_INT_CAST(UINT16, ((UINT16)src[1] << 8) | (UINT16)src[0]);
50
0
}
51
52
static inline UINT32 GETPIXEL32(const void* WINPR_RESTRICT d, UINT32 x, UINT32 y, UINT32 w)
53
0
{
54
0
  const BYTE* WINPR_RESTRICT src = (const BYTE*)d + ((y * w + x) * sizeof(UINT32));
55
0
  return (((UINT32)src[3]) << 24) | (((UINT32)src[2]) << 16) | (((UINT32)src[1]) << 8) |
56
0
         (src[0] & 0xFF);
57
0
}
58
59
/*****************************************************************************/
60
static inline UINT16 IN_PIXEL16(const void* WINPR_RESTRICT in_ptr, UINT32 in_x, UINT32 in_y,
61
                                UINT32 in_w, UINT16 in_last_pixel)
62
0
{
63
0
  if (in_ptr == nullptr)
64
0
    return 0;
65
0
  else if (in_x < in_w)
66
0
    return GETPIXEL16(in_ptr, in_x, in_y, in_w);
67
0
  else
68
0
    return in_last_pixel;
69
0
}
70
71
/*****************************************************************************/
72
static inline UINT32 IN_PIXEL32(const void* WINPR_RESTRICT in_ptr, UINT32 in_x, UINT32 in_y,
73
                                UINT32 in_w, UINT32 in_last_pixel)
74
0
{
75
0
  if (in_ptr == nullptr)
76
0
    return 0;
77
0
  else if (in_x < in_w)
78
0
    return GETPIXEL32(in_ptr, in_x, in_y, in_w);
79
0
  else
80
0
    return in_last_pixel;
81
0
}
82
83
/*****************************************************************************/
84
/* color */
85
static inline UINT16 out_color_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
86
                                       UINT16 in_data)
87
0
{
88
0
  if (in_count > 0)
89
0
  {
90
0
    if (in_count < 32)
91
0
    {
92
0
      const BYTE temp = ((0x3u << 5) | in_count) & 0xFF;
93
0
      Stream_Write_UINT8(in_s, temp);
94
0
    }
95
0
    else if (in_count < 256 + 32)
96
0
    {
97
0
      const BYTE temp = (in_count - 32) & 0xFF;
98
0
      Stream_Write_UINT8(in_s, 0x60);
99
0
      Stream_Write_UINT8(in_s, temp);
100
0
    }
101
0
    else
102
0
    {
103
0
      Stream_Write_UINT8(in_s, 0xf3);
104
0
      Stream_Write_UINT16(in_s, in_count);
105
0
    }
106
107
0
    Stream_Write_UINT16(in_s, in_data);
108
0
  }
109
110
0
  return 0;
111
0
}
112
113
/*****************************************************************************/
114
/* color */
115
static inline UINT16 out_color_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
116
                                       UINT32 in_data)
117
0
{
118
0
  if (in_count > 0)
119
0
  {
120
0
    if (in_count < 32)
121
0
    {
122
0
      const BYTE temp = ((0x3u << 5) | in_count) & 0xFF;
123
0
      Stream_Write_UINT8(in_s, temp);
124
0
    }
125
0
    else if (in_count < 256 + 32)
126
0
    {
127
0
      const BYTE temp = (in_count - 32) & 0xFF;
128
0
      Stream_Write_UINT8(in_s, 0x60);
129
0
      Stream_Write_UINT8(in_s, temp);
130
0
    }
131
0
    else
132
0
    {
133
0
      Stream_Write_UINT8(in_s, 0xf3);
134
0
      Stream_Write_UINT16(in_s, in_count);
135
0
    }
136
137
0
    Stream_Write_UINT8(in_s, in_data & 0xFF);
138
139
0
    Stream_Write_UINT8(in_s, (in_data >> 8) & 0xFF);
140
0
    Stream_Write_UINT8(in_s, (in_data >> 16) & 0xFF);
141
0
  }
142
143
0
  return 0;
144
0
}
145
146
/*****************************************************************************/
147
/* copy */
148
static inline UINT16 out_copy_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
149
                                      wStream* WINPR_RESTRICT in_data)
150
151
0
{
152
0
  if (in_count > 0)
153
0
  {
154
0
    if (in_count < 32)
155
0
    {
156
0
      const BYTE temp = ((0x4u << 5) | in_count) & 0xFF;
157
0
      Stream_Write_UINT8(in_s, temp);
158
0
    }
159
0
    else if (in_count < 256 + 32)
160
0
    {
161
0
      const BYTE temp = (in_count - 32) & 0xFF;
162
0
      Stream_Write_UINT8(in_s, 0x80);
163
0
      Stream_Write_UINT8(in_s, temp);
164
0
    }
165
0
    else
166
0
    {
167
0
      Stream_Write_UINT8(in_s, 0xf4);
168
0
      Stream_Write_UINT16(in_s, in_count);
169
0
    }
170
171
0
    Stream_Write(in_s, Stream_Buffer(in_data), 2ULL * in_count);
172
0
  }
173
174
0
  Stream_ResetPosition(in_data);
175
0
  return 0;
176
0
}
177
178
/*****************************************************************************/
179
/* copy */
180
static inline UINT16 out_copy_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
181
                                      wStream* WINPR_RESTRICT in_data)
182
0
{
183
0
  if (in_count > 0)
184
0
  {
185
0
    if (in_count < 32)
186
0
    {
187
0
      const BYTE temp = ((0x4u << 5) | in_count) & 0xFF;
188
0
      Stream_Write_UINT8(in_s, temp);
189
0
    }
190
0
    else if (in_count < 256 + 32)
191
0
    {
192
0
      const BYTE temp = (in_count - 32) & 0xFF;
193
0
      Stream_Write_UINT8(in_s, 0x80);
194
0
      Stream_Write_UINT8(in_s, temp);
195
0
    }
196
0
    else
197
0
    {
198
0
      Stream_Write_UINT8(in_s, 0xf4);
199
0
      Stream_Write_UINT16(in_s, in_count);
200
0
    }
201
202
0
    Stream_Write(in_s, Stream_Pointer(in_data), 3ULL * in_count);
203
0
  }
204
205
0
  Stream_ResetPosition(in_data);
206
0
  return 0;
207
0
}
208
209
/*****************************************************************************/
210
/* bicolor */
211
static inline UINT16 out_bicolor_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
212
                                         UINT16 in_color1, UINT16 in_color2)
213
0
{
214
0
  if (in_count > 0)
215
0
  {
216
0
    if (in_count / 2 < 16)
217
0
    {
218
0
      const BYTE temp = ((0xe << 4) | (in_count / 2)) & 0xFF;
219
0
      Stream_Write_UINT8(in_s, temp);
220
0
    }
221
0
    else if (in_count / 2 < 256 + 16)
222
0
    {
223
0
      const BYTE temp = (in_count / 2 - 16) & 0xFF;
224
0
      Stream_Write_UINT8(in_s, 0xe0);
225
0
      Stream_Write_UINT8(in_s, temp);
226
0
    }
227
0
    else
228
0
    {
229
0
      Stream_Write_UINT8(in_s, 0xf8);
230
0
      Stream_Write_UINT16(in_s, in_count / 2);
231
0
    }
232
233
0
    Stream_Write_UINT16(in_s, in_color1);
234
0
    Stream_Write_UINT16(in_s, in_color2);
235
0
  }
236
237
0
  return 0;
238
0
}
239
240
/*****************************************************************************/
241
/* bicolor */
242
static inline UINT16 out_bicolor_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
243
                                         UINT32 in_color1, UINT32 in_color2)
244
0
{
245
0
  if (in_count > 0)
246
0
  {
247
0
    if (in_count / 2 < 16)
248
0
    {
249
0
      const BYTE temp = ((0xe << 4) | (in_count / 2)) & 0xFF;
250
0
      Stream_Write_UINT8(in_s, temp);
251
0
    }
252
0
    else if (in_count / 2 < 256 + 16)
253
0
    {
254
0
      const BYTE temp = (in_count / 2 - 16) & 0xFF;
255
0
      Stream_Write_UINT8(in_s, 0xe0);
256
0
      Stream_Write_UINT8(in_s, temp);
257
0
    }
258
0
    else
259
0
    {
260
0
      Stream_Write_UINT8(in_s, 0xf8);
261
0
      Stream_Write_UINT16(in_s, in_count / 2);
262
0
    }
263
264
0
    Stream_Write_UINT8(in_s, in_color1 & 0xFF);
265
0
    Stream_Write_UINT8(in_s, (in_color1 >> 8) & 0xFF);
266
0
    Stream_Write_UINT8(in_s, (in_color1 >> 16) & 0xFF);
267
0
    Stream_Write_UINT8(in_s, in_color2 & 0xFF);
268
0
    Stream_Write_UINT8(in_s, (in_color2 >> 8) & 0xFF);
269
0
    Stream_Write_UINT8(in_s, (in_color2 >> 16) & 0xFF);
270
0
  }
271
272
0
  return 0;
273
0
}
274
275
/*****************************************************************************/
276
/* fill */
277
static inline UINT16 out_fill_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s)
278
0
{
279
0
  if (in_count > 0)
280
0
  {
281
0
    if (in_count < 32)
282
0
    {
283
0
      Stream_Write_UINT8(in_s, in_count & 0xFF);
284
0
    }
285
0
    else if (in_count < 256 + 32)
286
0
    {
287
0
      const BYTE temp = (in_count - 32) & 0xFF;
288
0
      Stream_Write_UINT8(in_s, 0x0);
289
0
      Stream_Write_UINT8(in_s, temp);
290
0
    }
291
0
    else
292
0
    {
293
0
      Stream_Write_UINT8(in_s, 0xf0);
294
0
      Stream_Write_UINT16(in_s, in_count);
295
0
    }
296
0
  }
297
298
0
  return 0;
299
0
}
300
301
/*****************************************************************************/
302
/* fill */
303
static inline UINT16 out_fill_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s)
304
0
{
305
0
  if (in_count > 0)
306
0
  {
307
0
    if (in_count < 32)
308
0
    {
309
0
      Stream_Write_UINT8(in_s, in_count & 0xFF);
310
0
    }
311
0
    else if (in_count < 256 + 32)
312
0
    {
313
0
      const BYTE temp = (in_count - 32) & 0xFF;
314
0
      Stream_Write_UINT8(in_s, 0x0);
315
0
      Stream_Write_UINT8(in_s, temp);
316
0
    }
317
0
    else
318
0
    {
319
0
      Stream_Write_UINT8(in_s, 0xf0);
320
0
      Stream_Write_UINT16(in_s, in_count);
321
0
    }
322
0
  }
323
324
0
  return 0;
325
0
}
326
327
/*****************************************************************************/
328
/* mix */
329
static inline UINT16 out_counts_mix_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s)
330
0
{
331
0
  if (in_count > 0)
332
0
  {
333
0
    if (in_count < 32)
334
0
    {
335
0
      const BYTE temp = ((0x1u << 5) | in_count) & 0xFF;
336
0
      Stream_Write_UINT8(in_s, temp);
337
0
    }
338
0
    else if (in_count < 256 + 32)
339
0
    {
340
0
      const BYTE temp = (in_count - 32) & 0xFF;
341
0
      Stream_Write_UINT8(in_s, 0x20);
342
0
      Stream_Write_UINT8(in_s, temp);
343
0
    }
344
0
    else
345
0
    {
346
0
      Stream_Write_UINT8(in_s, 0xf1);
347
0
      Stream_Write_UINT16(in_s, in_count);
348
0
    }
349
0
  }
350
351
0
  return 0;
352
0
}
353
354
/*****************************************************************************/
355
/* mix */
356
static inline UINT16 out_counts_mix_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s)
357
0
{
358
0
  if (in_count > 0)
359
0
  {
360
0
    if (in_count < 32)
361
0
    {
362
0
      const BYTE temp = ((0x1u << 5) | in_count) & 0xFF;
363
0
      Stream_Write_UINT8(in_s, temp);
364
0
    }
365
0
    else if (in_count < 256 + 32)
366
0
    {
367
0
      const BYTE temp = (in_count - 32) & 0xFF;
368
0
      Stream_Write_UINT8(in_s, 0x20);
369
0
      Stream_Write_UINT8(in_s, temp);
370
0
    }
371
0
    else
372
0
    {
373
0
      Stream_Write_UINT8(in_s, 0xf1);
374
0
      Stream_Write_UINT16(in_s, in_count);
375
0
    }
376
0
  }
377
378
0
  return 0;
379
0
}
380
381
/*****************************************************************************/
382
/* fom */
383
static inline UINT16 out_from_count_2(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
384
                                      const uint8_t* WINPR_RESTRICT in_mask, size_t in_mask_len)
385
0
{
386
0
  if (in_count > 0)
387
0
  {
388
0
    if ((in_count % 8) == 0 && in_count < 249)
389
0
    {
390
0
      const BYTE temp = ((0x2u << 5) | (in_count / 8)) & 0xFF;
391
0
      Stream_Write_UINT8(in_s, temp);
392
0
    }
393
0
    else if (in_count < 256)
394
0
    {
395
0
      const BYTE temp = (in_count - 1) & 0xFF;
396
0
      Stream_Write_UINT8(in_s, 0x40);
397
0
      Stream_Write_UINT8(in_s, temp);
398
0
    }
399
0
    else
400
0
    {
401
0
      Stream_Write_UINT8(in_s, 0xf2);
402
0
      Stream_Write_UINT16(in_s, in_count);
403
0
    }
404
405
0
    Stream_Write(in_s, in_mask, in_mask_len);
406
0
  }
407
408
0
  return 0;
409
0
}
410
411
/*****************************************************************************/
412
/* fill or mix (fom) */
413
static inline UINT16 out_from_count_3(UINT16 in_count, wStream* WINPR_RESTRICT in_s,
414
                                      const uint8_t* WINPR_RESTRICT in_mask, size_t in_mask_len)
415
0
{
416
0
  if (in_count > 0)
417
0
  {
418
0
    if ((in_count % 8) == 0 && in_count < 249)
419
0
    {
420
0
      const BYTE temp = ((0x2u << 5) | (in_count / 8)) & 0xFF;
421
0
      Stream_Write_UINT8(in_s, temp);
422
0
    }
423
0
    else if (in_count < 256)
424
0
    {
425
0
      const BYTE temp = (in_count - 1) & 0xFF;
426
0
      Stream_Write_UINT8(in_s, 0x40);
427
0
      Stream_Write_UINT8(in_s, temp);
428
0
    }
429
0
    else
430
0
    {
431
0
      Stream_Write_UINT8(in_s, 0xf2);
432
0
      Stream_Write_UINT16(in_s, in_count);
433
0
    }
434
435
0
    Stream_Write(in_s, in_mask, in_mask_len);
436
0
  }
437
438
0
  return 0;
439
0
}
440
441
0
#define TEST_FILL ((last_line == 0 && pixel == 0) || (last_line != 0 && pixel == ypixel))
442
0
#define TEST_MIX ((last_line == 0 && pixel == mix) || (last_line != 0 && pixel == (ypixel ^ mix)))
443
0
#define TEST_FOM TEST_FILL || TEST_MIX
444
0
#define TEST_COLOR pixel == last_pixel
445
446
#define test_bicolor(counts)                                                          \
447
0
  ((pixel != last_pixel) &&                                                         \
448
0
   ((!(counts)->bicolor_spin && (pixel == bicolor1) && (last_pixel == bicolor2)) || \
449
0
    ((counts)->bicolor_spin && (pixel == bicolor2) && (last_pixel == bicolor1))))
450
451
static inline SSIZE_T freerdp_bitmap_compress_24(const void* WINPR_RESTRICT srcData, UINT32 width,
452
                                                 WINPR_ATTR_UNUSED UINT32 height,
453
                                                 wStream* WINPR_RESTRICT s, UINT32 byte_limit,
454
                                                 UINT32 sline, wStream* WINPR_RESTRICT temp_s,
455
                                                 UINT32 e)
456
0
{
457
0
  uint8_t fom_mask[8192] = WINPR_C_ARRAY_INIT; /* good for up to 64K bitmap */
458
0
  SSIZE_T lines_sent = 0;
459
0
  UINT16 count = 0;
460
0
  UINT32 last_pixel = 0;
461
0
  UINT32 last_ypixel = 0;
462
0
  struct count counts = WINPR_C_ARRAY_INIT;
463
0
  UINT32 bicolor1 = 0;
464
0
  UINT32 bicolor2 = 0;
465
0
  UINT32 end = width + e;
466
0
  UINT32 out_count = end * 3;
467
0
  const UINT32 mix = 0xFFFFFF;
468
0
  const char* start = (const char*)srcData;
469
0
  if (!start)
470
0
    return -1;
471
0
  const char* last_line = nullptr;
472
473
0
  INT64 start_line = sline;
474
0
  while ((start_line >= 0) && (out_count < 32768))
475
0
  {
476
0
    const char* line = &start[4ULL * width * WINPR_ASSERTING_INT_CAST(size_t, start_line)];
477
0
    size_t i = Stream_GetPosition(s) + 3ULL * count;
478
479
0
    if ((i - (3ULL * counts.color_count) >= byte_limit) &&
480
0
        (i - (3ULL * counts.bicolor_count) >= byte_limit) &&
481
0
        (i - (3ULL * counts.fill_count) >= byte_limit) &&
482
0
        (i - (3ULL * counts.mix_count) >= byte_limit) &&
483
0
        (i - (3ULL * counts.fom_count) >= byte_limit))
484
0
    {
485
0
      break;
486
0
    }
487
488
0
    out_count += end * 3;
489
490
0
    for (UINT32 j = 0; j < end; j++)
491
0
    {
492
      /* read next pixel */
493
0
      const UINT32 pixel = IN_PIXEL32(line, j, 0, width, last_pixel);
494
0
      const UINT32 ypixel = IN_PIXEL32(last_line, j, 0, width, last_ypixel);
495
496
0
      if (!TEST_FILL)
497
0
      {
498
0
        if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
499
0
            counts.fill_count >= counts.bicolor_count &&
500
0
            counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
501
0
        {
502
0
          if (counts.fill_count > count)
503
0
            return -1;
504
505
0
          count -= counts.fill_count;
506
0
          count = out_copy_count_3(count, s, temp_s);
507
0
          counts.fill_count = out_fill_count_3(counts.fill_count, s);
508
0
          reset_counts(&counts);
509
0
        }
510
511
0
        counts.fill_count = 0;
512
0
      }
513
514
0
      if (!TEST_MIX)
515
0
      {
516
0
        if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
517
0
            counts.mix_count >= counts.bicolor_count &&
518
0
            counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
519
0
        {
520
0
          if (counts.mix_count > count)
521
0
            return -1;
522
523
0
          count -= counts.mix_count;
524
0
          count = out_copy_count_3(count, s, temp_s);
525
0
          counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
526
0
          reset_counts(&counts);
527
0
        }
528
529
0
        counts.mix_count = 0;
530
0
      }
531
532
0
      if (!(TEST_COLOR))
533
0
      {
534
0
        if (counts.color_count > 3 && counts.color_count >= counts.fill_count &&
535
0
            counts.color_count >= counts.bicolor_count &&
536
0
            counts.color_count >= counts.mix_count &&
537
0
            counts.color_count >= counts.fom_count)
538
0
        {
539
0
          if (counts.color_count > count)
540
0
            return -1;
541
542
0
          count -= counts.color_count;
543
0
          count = out_copy_count_3(count, s, temp_s);
544
0
          counts.color_count = out_color_count_3(counts.color_count, s, last_pixel);
545
0
          reset_counts(&counts);
546
0
        }
547
548
0
        counts.color_count = 0;
549
0
      }
550
551
0
      if (!test_bicolor(&counts))
552
0
      {
553
0
        if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.fill_count &&
554
0
            counts.bicolor_count >= counts.color_count &&
555
0
            counts.bicolor_count >= counts.mix_count &&
556
0
            counts.bicolor_count >= counts.fom_count)
557
0
        {
558
0
          if ((counts.bicolor_count % 2) != 0)
559
0
            counts.bicolor_count--;
560
561
0
          if (counts.bicolor_count > count)
562
0
            return -1;
563
564
0
          count -= counts.bicolor_count;
565
0
          count = out_copy_count_3(count, s, temp_s);
566
0
          counts.bicolor_count =
567
0
              out_bicolor_count_3(counts.bicolor_count, s, bicolor2, bicolor1);
568
0
          reset_counts(&counts);
569
0
        }
570
571
0
        counts.bicolor_count = 0;
572
0
        bicolor1 = last_pixel;
573
0
        bicolor2 = pixel;
574
0
        counts.bicolor_spin = FALSE;
575
0
      }
576
577
0
      if (!(TEST_FOM))
578
0
      {
579
0
        if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
580
0
            counts.fom_count >= counts.color_count &&
581
0
            counts.fom_count >= counts.mix_count &&
582
0
            counts.fom_count >= counts.bicolor_count)
583
0
        {
584
0
          if (counts.fom_count > count)
585
0
            return -1;
586
587
0
          count -= counts.fom_count;
588
0
          count = out_copy_count_3(count, s, temp_s);
589
0
          counts.fom_count =
590
0
              out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
591
0
          reset_counts(&counts);
592
0
        }
593
594
0
        counts.fom_count = 0;
595
0
        counts.fom_mask_len = 0;
596
0
      }
597
598
0
      if (TEST_FILL)
599
0
      {
600
0
        counts.fill_count++;
601
0
      }
602
603
0
      if (TEST_MIX)
604
0
      {
605
0
        counts.mix_count++;
606
0
      }
607
608
0
      if (TEST_COLOR)
609
0
      {
610
0
        counts.color_count++;
611
0
      }
612
613
0
      if (test_bicolor(&counts))
614
0
      {
615
0
        counts.bicolor_spin = !counts.bicolor_spin;
616
0
        counts.bicolor_count++;
617
0
      }
618
619
0
      if (TEST_FOM)
620
0
      {
621
0
        if ((counts.fom_count % 8) == 0)
622
0
        {
623
0
          fom_mask[counts.fom_mask_len] = 0;
624
0
          counts.fom_mask_len++;
625
0
        }
626
627
0
        if (pixel == (ypixel ^ mix))
628
0
        {
629
0
          const uint8_t tmp = (1u << (counts.fom_count % 8)) & 0xFF;
630
0
          const uint8_t val = fom_mask[counts.fom_mask_len - 1] | tmp;
631
0
          fom_mask[counts.fom_mask_len - 1] = val;
632
0
        }
633
634
0
        counts.fom_count++;
635
0
      }
636
637
0
      Stream_Write_UINT8(temp_s, pixel & 0xff);
638
0
      Stream_Write_UINT8(temp_s, (pixel >> 8) & 0xff);
639
0
      Stream_Write_UINT8(temp_s, (pixel >> 16) & 0xff);
640
0
      count++;
641
0
      last_pixel = pixel;
642
0
      last_ypixel = ypixel;
643
0
    }
644
645
    /* can't take fix, mix, or fom past first line */
646
0
    if (last_line == nullptr)
647
0
    {
648
0
      if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
649
0
          counts.fill_count >= counts.bicolor_count &&
650
0
          counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
651
0
      {
652
0
        if (counts.fill_count > count)
653
0
          return -1;
654
655
0
        count -= counts.fill_count;
656
0
        count = out_copy_count_3(count, s, temp_s);
657
0
        counts.fill_count = out_fill_count_3(counts.fill_count, s);
658
0
        reset_counts(&counts);
659
0
      }
660
661
0
      counts.fill_count = 0;
662
663
0
      if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
664
0
          counts.mix_count >= counts.bicolor_count &&
665
0
          counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
666
0
      {
667
0
        if (counts.mix_count > count)
668
0
          return -1;
669
670
0
        count -= counts.mix_count;
671
0
        count = out_copy_count_3(count, s, temp_s);
672
0
        counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
673
0
        reset_counts(&counts);
674
0
      }
675
676
0
      counts.mix_count = 0;
677
678
0
      if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
679
0
          counts.fom_count >= counts.color_count && counts.fom_count >= counts.mix_count &&
680
0
          counts.fom_count >= counts.bicolor_count)
681
0
      {
682
0
        if (counts.fom_count > count)
683
0
          return -1;
684
685
0
        count -= counts.fom_count;
686
0
        count = out_copy_count_3(count, s, temp_s);
687
0
        counts.fom_count =
688
0
            out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
689
0
        reset_counts(&counts);
690
0
      }
691
692
0
      counts.fom_count = 0;
693
0
      counts.fom_mask_len = 0;
694
0
    }
695
696
0
    last_line = line;
697
698
0
    start_line--;
699
0
    lines_sent++;
700
0
  }
701
702
0
  Stream_ResetPosition(temp_s);
703
704
0
  if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
705
0
      counts.fill_count >= counts.bicolor_count && counts.fill_count >= counts.mix_count &&
706
0
      counts.fill_count >= counts.fom_count)
707
0
  {
708
0
    if (counts.fill_count > count)
709
0
      return -1;
710
711
0
    count -= counts.fill_count;
712
0
    (void)out_copy_count_3(count, s, temp_s);
713
0
    counts.fill_count = out_fill_count_3(counts.fill_count, s);
714
0
  }
715
0
  else if (counts.mix_count > 3 && counts.mix_count >= counts.color_count &&
716
0
           counts.mix_count >= counts.bicolor_count && counts.mix_count >= counts.fill_count &&
717
0
           counts.mix_count >= counts.fom_count)
718
0
  {
719
0
    if (counts.mix_count > count)
720
0
      return -1;
721
722
0
    count -= counts.mix_count;
723
0
    (void)out_copy_count_3(count, s, temp_s);
724
0
    counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
725
0
  }
726
0
  else if (counts.color_count > 3 && counts.color_count >= counts.mix_count &&
727
0
           counts.color_count >= counts.bicolor_count &&
728
0
           counts.color_count >= counts.fill_count && counts.color_count >= counts.fom_count)
729
0
  {
730
0
    if (counts.color_count > count)
731
0
      return -1;
732
733
0
    count -= counts.color_count;
734
0
    (void)out_copy_count_3(count, s, temp_s);
735
0
    counts.color_count = out_color_count_3(counts.color_count, s, last_pixel);
736
0
  }
737
0
  else if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.mix_count &&
738
0
           counts.bicolor_count >= counts.color_count &&
739
0
           counts.bicolor_count >= counts.fill_count && counts.bicolor_count >= counts.fom_count)
740
0
  {
741
0
    if ((counts.bicolor_count % 2) != 0)
742
0
      counts.bicolor_count--;
743
744
0
    if (counts.bicolor_count > count)
745
0
      return -1;
746
747
0
    count -= counts.bicolor_count;
748
0
    count = out_copy_count_3(count, s, temp_s);
749
0
    counts.bicolor_count = out_bicolor_count_3(counts.bicolor_count, s, bicolor2, bicolor1);
750
751
0
    if (counts.bicolor_count > count)
752
0
      return -1;
753
754
0
    count -= counts.bicolor_count;
755
0
    (void)out_copy_count_3(count, s, temp_s);
756
0
    counts.bicolor_count = out_bicolor_count_3(counts.bicolor_count, s, bicolor1, bicolor2);
757
0
  }
758
0
  else if (counts.fom_count > 3 && counts.fom_count >= counts.mix_count &&
759
0
           counts.fom_count >= counts.color_count && counts.fom_count >= counts.fill_count &&
760
0
           counts.fom_count >= counts.bicolor_count)
761
0
  {
762
0
    if (counts.fom_count > count)
763
0
      return -1;
764
765
0
    count -= counts.fom_count;
766
0
    (void)out_copy_count_3(count, s, temp_s);
767
0
    counts.fom_count = out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
768
0
  }
769
0
  else
770
0
  {
771
0
    (void)out_copy_count_3(count, s, temp_s);
772
0
  }
773
774
0
  return lines_sent;
775
0
}
776
777
static inline SSIZE_T freerdp_bitmap_compress_16(const void* WINPR_RESTRICT srcData, UINT32 width,
778
                                                 WINPR_ATTR_UNUSED UINT32 height,
779
                                                 wStream* WINPR_RESTRICT s, UINT32 bpp,
780
                                                 UINT32 byte_limit, UINT32 sline,
781
                                                 wStream* WINPR_RESTRICT temp_s, UINT32 e)
782
0
{
783
0
  uint8_t fom_mask[8192] = WINPR_C_ARRAY_INIT; /* good for up to 64K bitmap */
784
0
  SSIZE_T lines_sent = 0;
785
0
  UINT16 count = 0;
786
0
  UINT16 last_pixel = 0;
787
0
  UINT16 last_ypixel = 0;
788
0
  struct count counts = WINPR_C_ARRAY_INIT;
789
0
  UINT16 bicolor1 = 0;
790
0
  UINT16 bicolor2 = 0;
791
0
  UINT32 end = width + e;
792
0
  UINT32 out_count = end * 2;
793
0
  const UINT32 mix = (bpp == 15) ? 0xBA1F : 0xFFFF;
794
0
  const char* start = (const char*)srcData;
795
0
  if (!start)
796
0
    return -1;
797
798
0
  const char* last_line = nullptr;
799
800
0
  INT64 start_line = sline;
801
0
  while ((start_line >= 0) && (out_count < 32768))
802
0
  {
803
0
    const char* line = &start[2ULL * width * WINPR_ASSERTING_INT_CAST(size_t, start_line)];
804
0
    size_t i = Stream_GetPosition(s) + 2ULL * count;
805
806
0
    if ((i - (2ULL * counts.color_count) >= byte_limit) &&
807
0
        (i - (2ULL * counts.bicolor_count) >= byte_limit) &&
808
0
        (i - (2ULL * counts.fill_count) >= byte_limit) &&
809
0
        (i - (2ULL * counts.mix_count) >= byte_limit) &&
810
0
        (i - (2ULL * counts.fom_count) >= byte_limit))
811
0
    {
812
0
      break;
813
0
    }
814
815
0
    out_count += end * 2;
816
817
0
    for (UINT32 j = 0; j < end; j++)
818
0
    {
819
      /* read next pixel */
820
0
      const UINT16 pixel = IN_PIXEL16(line, j, 0, width, last_pixel);
821
0
      const UINT16 ypixel = IN_PIXEL16(last_line, j, 0, width, last_ypixel);
822
823
0
      if (!TEST_FILL)
824
0
      {
825
0
        if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
826
0
            counts.fill_count >= counts.bicolor_count &&
827
0
            counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
828
0
        {
829
0
          if (counts.fill_count > count)
830
0
            return -1;
831
832
0
          count -= counts.fill_count;
833
0
          count = out_copy_count_2(count, s, temp_s);
834
0
          counts.fill_count = out_fill_count_2(counts.fill_count, s);
835
0
          reset_counts(&counts);
836
0
        }
837
838
0
        counts.fill_count = 0;
839
0
      }
840
841
0
      if (!TEST_MIX)
842
0
      {
843
0
        if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
844
0
            counts.mix_count >= counts.bicolor_count &&
845
0
            counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
846
0
        {
847
0
          if (counts.mix_count > count)
848
0
            return -1;
849
850
0
          count -= counts.mix_count;
851
0
          count = out_copy_count_2(count, s, temp_s);
852
0
          counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
853
0
          reset_counts(&counts);
854
0
        }
855
856
0
        counts.mix_count = 0;
857
0
      }
858
859
0
      if (!(TEST_COLOR))
860
0
      {
861
0
        if (counts.color_count > 3 && counts.color_count >= counts.fill_count &&
862
0
            counts.color_count >= counts.bicolor_count &&
863
0
            counts.color_count >= counts.mix_count &&
864
0
            counts.color_count >= counts.fom_count)
865
0
        {
866
0
          if (counts.color_count > count)
867
0
            return -1;
868
869
0
          count -= counts.color_count;
870
0
          count = out_copy_count_2(count, s, temp_s);
871
0
          counts.color_count = out_color_count_2(counts.color_count, s, last_pixel);
872
0
          reset_counts(&counts);
873
0
        }
874
875
0
        counts.color_count = 0;
876
0
      }
877
878
0
      if (!test_bicolor(&counts))
879
0
      {
880
0
        if ((counts.bicolor_count > 3) && (counts.bicolor_count >= counts.fill_count) &&
881
0
            (counts.bicolor_count >= counts.color_count) &&
882
0
            (counts.bicolor_count >= counts.mix_count) &&
883
0
            (counts.bicolor_count >= counts.fom_count))
884
0
        {
885
0
          if ((counts.bicolor_count % 2) != 0)
886
0
            counts.bicolor_count--;
887
888
0
          if (counts.bicolor_count > count)
889
0
            return -1;
890
891
0
          count -= counts.bicolor_count;
892
0
          count = out_copy_count_2(count, s, temp_s);
893
0
          counts.bicolor_count =
894
0
              out_bicolor_count_2(counts.bicolor_count, s, bicolor2, bicolor1);
895
0
          reset_counts(&counts);
896
0
        }
897
898
0
        counts.bicolor_count = 0;
899
0
        bicolor1 = last_pixel;
900
0
        bicolor2 = pixel;
901
0
        counts.bicolor_spin = FALSE;
902
0
      }
903
904
0
      if (!(TEST_FOM))
905
0
      {
906
0
        if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
907
0
            counts.fom_count >= counts.color_count &&
908
0
            counts.fom_count >= counts.mix_count &&
909
0
            counts.fom_count >= counts.bicolor_count)
910
0
        {
911
0
          if (counts.fom_count > count)
912
0
            return -1;
913
914
0
          count -= counts.fom_count;
915
0
          count = out_copy_count_2(count, s, temp_s);
916
0
          counts.fom_count =
917
0
              out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
918
0
          reset_counts(&counts);
919
0
        }
920
921
0
        counts.fom_count = 0;
922
0
        counts.fom_mask_len = 0;
923
0
      }
924
925
0
      if (TEST_FILL)
926
0
      {
927
0
        counts.fill_count++;
928
0
      }
929
930
0
      if (TEST_MIX)
931
0
      {
932
0
        counts.mix_count++;
933
0
      }
934
935
0
      if (TEST_COLOR)
936
0
      {
937
0
        counts.color_count++;
938
0
      }
939
940
0
      if (test_bicolor(&counts))
941
0
      {
942
0
        counts.bicolor_spin = !counts.bicolor_spin;
943
0
        counts.bicolor_count++;
944
0
      }
945
946
0
      if (TEST_FOM)
947
0
      {
948
0
        if ((counts.fom_count % 8) == 0)
949
0
        {
950
0
          fom_mask[counts.fom_mask_len] = 0;
951
0
          counts.fom_mask_len++;
952
0
        }
953
954
0
        if (pixel == (ypixel ^ mix))
955
0
        {
956
0
          const uint8_t tmp = (1u << (counts.fom_count % 8)) & 0xFF;
957
0
          const uint8_t val = fom_mask[counts.fom_mask_len - 1] | tmp;
958
0
          fom_mask[counts.fom_mask_len - 1] = val;
959
0
        }
960
961
0
        counts.fom_count++;
962
0
      }
963
964
0
      Stream_Write_UINT16(temp_s, pixel);
965
0
      count++;
966
0
      last_pixel = pixel;
967
0
      last_ypixel = ypixel;
968
0
    }
969
970
    /* can't take fix, mix, or fom past first line */
971
0
    if (last_line == nullptr)
972
0
    {
973
0
      if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
974
0
          counts.fill_count >= counts.bicolor_count &&
975
0
          counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
976
0
      {
977
0
        if (counts.fill_count > count)
978
0
          return -1;
979
980
0
        count -= counts.fill_count;
981
0
        count = out_copy_count_2(count, s, temp_s);
982
0
        counts.fill_count = out_fill_count_2(counts.fill_count, s);
983
0
        reset_counts(&counts);
984
0
      }
985
986
0
      counts.fill_count = 0;
987
988
0
      if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
989
0
          counts.mix_count >= counts.bicolor_count &&
990
0
          counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
991
0
      {
992
0
        if (counts.mix_count > count)
993
0
          return -1;
994
995
0
        count -= counts.mix_count;
996
0
        count = out_copy_count_2(count, s, temp_s);
997
0
        counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
998
0
        reset_counts(&counts);
999
0
      }
1000
1001
0
      counts.mix_count = 0;
1002
1003
0
      if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
1004
0
          counts.fom_count >= counts.color_count && counts.fom_count >= counts.mix_count &&
1005
0
          counts.fom_count >= counts.bicolor_count)
1006
0
      {
1007
0
        if (counts.fom_count > count)
1008
0
          return -1;
1009
1010
0
        count -= counts.fom_count;
1011
0
        count = out_copy_count_2(count, s, temp_s);
1012
0
        counts.fom_count =
1013
0
            out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
1014
0
        reset_counts(&counts);
1015
0
      }
1016
1017
0
      counts.fom_count = 0;
1018
0
      counts.fom_mask_len = 0;
1019
0
    }
1020
1021
0
    last_line = line;
1022
1023
0
    start_line--;
1024
0
    lines_sent++;
1025
0
  }
1026
1027
0
  Stream_ResetPosition(temp_s);
1028
1029
0
  if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
1030
0
      counts.fill_count >= counts.bicolor_count && counts.fill_count >= counts.mix_count &&
1031
0
      counts.fill_count >= counts.fom_count)
1032
0
  {
1033
0
    if (counts.fill_count > count)
1034
0
      return -1;
1035
1036
0
    count -= counts.fill_count;
1037
0
    (void)out_copy_count_2(count, s, temp_s);
1038
0
    counts.fill_count = out_fill_count_2(counts.fill_count, s);
1039
0
  }
1040
0
  else if (counts.mix_count > 3 && counts.mix_count >= counts.color_count &&
1041
0
           counts.mix_count >= counts.bicolor_count && counts.mix_count >= counts.fill_count &&
1042
0
           counts.mix_count >= counts.fom_count)
1043
0
  {
1044
0
    if (counts.mix_count > count)
1045
0
      return -1;
1046
1047
0
    count -= counts.mix_count;
1048
0
    (void)out_copy_count_2(count, s, temp_s);
1049
0
    counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
1050
0
  }
1051
0
  else if (counts.color_count > 3 && counts.color_count >= counts.mix_count &&
1052
0
           counts.color_count >= counts.bicolor_count &&
1053
0
           counts.color_count >= counts.fill_count && counts.color_count >= counts.fom_count)
1054
0
  {
1055
0
    if (counts.color_count > count)
1056
0
      return -1;
1057
1058
0
    count -= counts.color_count;
1059
0
    (void)out_copy_count_2(count, s, temp_s);
1060
0
    counts.color_count = out_color_count_2(counts.color_count, s, last_pixel);
1061
0
  }
1062
0
  else if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.mix_count &&
1063
0
           counts.bicolor_count >= counts.color_count &&
1064
0
           counts.bicolor_count >= counts.fill_count && counts.bicolor_count >= counts.fom_count)
1065
0
  {
1066
0
    if ((counts.bicolor_count % 2) != 0)
1067
0
      counts.bicolor_count--;
1068
1069
0
    if (counts.bicolor_count > count)
1070
0
      return -1;
1071
1072
0
    count -= counts.bicolor_count;
1073
0
    count = out_copy_count_2(count, s, temp_s);
1074
0
    counts.bicolor_count = out_bicolor_count_2(counts.bicolor_count, s, bicolor2, bicolor1);
1075
1076
0
    if (counts.bicolor_count > count)
1077
0
      return -1;
1078
1079
0
    count -= counts.bicolor_count;
1080
0
    (void)out_copy_count_2(count, s, temp_s);
1081
0
    counts.bicolor_count = out_bicolor_count_2(counts.bicolor_count, s, bicolor1, bicolor2);
1082
0
  }
1083
0
  else if (counts.fom_count > 3 && counts.fom_count >= counts.mix_count &&
1084
0
           counts.fom_count >= counts.color_count && counts.fom_count >= counts.fill_count &&
1085
0
           counts.fom_count >= counts.bicolor_count)
1086
0
  {
1087
0
    if (counts.fom_count > count)
1088
0
      return -1;
1089
1090
0
    count -= counts.fom_count;
1091
0
    (void)out_copy_count_2(count, s, temp_s);
1092
0
    counts.fom_count = out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
1093
0
  }
1094
0
  else
1095
0
  {
1096
0
    (void)out_copy_count_2(count, s, temp_s);
1097
0
  }
1098
1099
0
  return lines_sent;
1100
0
}
1101
1102
SSIZE_T freerdp_bitmap_compress(const void* WINPR_RESTRICT srcData, UINT32 width, UINT32 height,
1103
                                wStream* WINPR_RESTRICT s, UINT32 bpp, UINT32 byte_limit,
1104
                                UINT32 start_line, wStream* WINPR_RESTRICT temp_s, UINT32 e)
1105
0
{
1106
0
  Stream_ResetPosition(temp_s);
1107
1108
0
  switch (bpp)
1109
0
  {
1110
0
    case 15:
1111
0
    case 16:
1112
0
      return freerdp_bitmap_compress_16(srcData, width, height, s, bpp, byte_limit,
1113
0
                                        start_line, temp_s, e);
1114
1115
0
    case 24:
1116
0
      return freerdp_bitmap_compress_24(srcData, width, height, s, byte_limit, start_line,
1117
0
                                        temp_s, e);
1118
1119
0
    default:
1120
0
      return -1;
1121
0
  }
1122
0
}