Coverage Report

Created: 2025-10-10 06:50

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 = { 0 };
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 == 0)
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 == 0)
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 = ((0x3 << 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 = ((0x3 << 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 = ((0x4 << 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_SetPosition(in_data, 0);
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 = ((0x4 << 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_SetPosition(in_data, 0);
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 = ((0x1 << 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 = ((0x1 << 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 = ((0x2 << 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 = ((0x2 << 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 start_line, wStream* WINPR_RESTRICT temp_s,
455
                                                 UINT32 e)
456
0
{
457
0
  uint8_t fom_mask[8192] = { 0 }; /* 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 = { 0 };
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
  const char* line = start + 4ULL * width * start_line;
470
0
  const char* last_line = NULL;
471
472
0
  while ((line >= start) && (out_count < 32768))
473
0
  {
474
0
    size_t i = Stream_GetPosition(s) + 3ULL * count;
475
476
0
    if ((i - (3ULL * counts.color_count) >= byte_limit) &&
477
0
        (i - (3ULL * counts.bicolor_count) >= byte_limit) &&
478
0
        (i - (3ULL * counts.fill_count) >= byte_limit) &&
479
0
        (i - (3ULL * counts.mix_count) >= byte_limit) &&
480
0
        (i - (3ULL * counts.fom_count) >= byte_limit))
481
0
    {
482
0
      break;
483
0
    }
484
485
0
    out_count += end * 3;
486
487
0
    for (UINT32 j = 0; j < end; j++)
488
0
    {
489
      /* read next pixel */
490
0
      const UINT32 pixel = IN_PIXEL32(line, j, 0, width, last_pixel);
491
0
      const UINT32 ypixel = IN_PIXEL32(last_line, j, 0, width, last_ypixel);
492
493
0
      if (!TEST_FILL)
494
0
      {
495
0
        if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
496
0
            counts.fill_count >= counts.bicolor_count &&
497
0
            counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
498
0
        {
499
0
          if (counts.fill_count > count)
500
0
            return -1;
501
502
0
          count -= counts.fill_count;
503
0
          count = out_copy_count_3(count, s, temp_s);
504
0
          counts.fill_count = out_fill_count_3(counts.fill_count, s);
505
0
          reset_counts(&counts);
506
0
        }
507
508
0
        counts.fill_count = 0;
509
0
      }
510
511
0
      if (!TEST_MIX)
512
0
      {
513
0
        if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
514
0
            counts.mix_count >= counts.bicolor_count &&
515
0
            counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
516
0
        {
517
0
          if (counts.mix_count > count)
518
0
            return -1;
519
520
0
          count -= counts.mix_count;
521
0
          count = out_copy_count_3(count, s, temp_s);
522
0
          counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
523
0
          reset_counts(&counts);
524
0
        }
525
526
0
        counts.mix_count = 0;
527
0
      }
528
529
0
      if (!(TEST_COLOR))
530
0
      {
531
0
        if (counts.color_count > 3 && counts.color_count >= counts.fill_count &&
532
0
            counts.color_count >= counts.bicolor_count &&
533
0
            counts.color_count >= counts.mix_count &&
534
0
            counts.color_count >= counts.fom_count)
535
0
        {
536
0
          if (counts.color_count > count)
537
0
            return -1;
538
539
0
          count -= counts.color_count;
540
0
          count = out_copy_count_3(count, s, temp_s);
541
0
          counts.color_count = out_color_count_3(counts.color_count, s, last_pixel);
542
0
          reset_counts(&counts);
543
0
        }
544
545
0
        counts.color_count = 0;
546
0
      }
547
548
0
      if (!test_bicolor(&counts))
549
0
      {
550
0
        if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.fill_count &&
551
0
            counts.bicolor_count >= counts.color_count &&
552
0
            counts.bicolor_count >= counts.mix_count &&
553
0
            counts.bicolor_count >= counts.fom_count)
554
0
        {
555
0
          if ((counts.bicolor_count % 2) != 0)
556
0
            counts.bicolor_count--;
557
558
0
          if (counts.bicolor_count > count)
559
0
            return -1;
560
561
0
          count -= counts.bicolor_count;
562
0
          count = out_copy_count_3(count, s, temp_s);
563
0
          counts.bicolor_count =
564
0
              out_bicolor_count_3(counts.bicolor_count, s, bicolor2, bicolor1);
565
0
          reset_counts(&counts);
566
0
        }
567
568
0
        counts.bicolor_count = 0;
569
0
        bicolor1 = last_pixel;
570
0
        bicolor2 = pixel;
571
0
        counts.bicolor_spin = FALSE;
572
0
      }
573
574
0
      if (!(TEST_FOM))
575
0
      {
576
0
        if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
577
0
            counts.fom_count >= counts.color_count &&
578
0
            counts.fom_count >= counts.mix_count &&
579
0
            counts.fom_count >= counts.bicolor_count)
580
0
        {
581
0
          if (counts.fom_count > count)
582
0
            return -1;
583
584
0
          count -= counts.fom_count;
585
0
          count = out_copy_count_3(count, s, temp_s);
586
0
          counts.fom_count =
587
0
              out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
588
0
          reset_counts(&counts);
589
0
        }
590
591
0
        counts.fom_count = 0;
592
0
        counts.fom_mask_len = 0;
593
0
      }
594
595
0
      if (TEST_FILL)
596
0
      {
597
0
        counts.fill_count++;
598
0
      }
599
600
0
      if (TEST_MIX)
601
0
      {
602
0
        counts.mix_count++;
603
0
      }
604
605
0
      if (TEST_COLOR)
606
0
      {
607
0
        counts.color_count++;
608
0
      }
609
610
0
      if (test_bicolor(&counts))
611
0
      {
612
0
        counts.bicolor_spin = !counts.bicolor_spin;
613
0
        counts.bicolor_count++;
614
0
      }
615
616
0
      if (TEST_FOM)
617
0
      {
618
0
        if ((counts.fom_count % 8) == 0)
619
0
        {
620
0
          fom_mask[counts.fom_mask_len] = 0;
621
0
          counts.fom_mask_len++;
622
0
        }
623
624
0
        if (pixel == (ypixel ^ mix))
625
0
        {
626
0
          const uint8_t tmp = (1 << (counts.fom_count % 8)) & 0xFF;
627
0
          const uint8_t val = fom_mask[counts.fom_mask_len - 1] | tmp;
628
0
          fom_mask[counts.fom_mask_len - 1] = val;
629
0
        }
630
631
0
        counts.fom_count++;
632
0
      }
633
634
0
      Stream_Write_UINT8(temp_s, pixel & 0xff);
635
0
      Stream_Write_UINT8(temp_s, (pixel >> 8) & 0xff);
636
0
      Stream_Write_UINT8(temp_s, (pixel >> 16) & 0xff);
637
0
      count++;
638
0
      last_pixel = pixel;
639
0
      last_ypixel = ypixel;
640
0
    }
641
642
    /* can't take fix, mix, or fom past first line */
643
0
    if (last_line == 0)
644
0
    {
645
0
      if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
646
0
          counts.fill_count >= counts.bicolor_count &&
647
0
          counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
648
0
      {
649
0
        if (counts.fill_count > count)
650
0
          return -1;
651
652
0
        count -= counts.fill_count;
653
0
        count = out_copy_count_3(count, s, temp_s);
654
0
        counts.fill_count = out_fill_count_3(counts.fill_count, s);
655
0
        reset_counts(&counts);
656
0
      }
657
658
0
      counts.fill_count = 0;
659
660
0
      if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
661
0
          counts.mix_count >= counts.bicolor_count &&
662
0
          counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
663
0
      {
664
0
        if (counts.mix_count > count)
665
0
          return -1;
666
667
0
        count -= counts.mix_count;
668
0
        count = out_copy_count_3(count, s, temp_s);
669
0
        counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
670
0
        reset_counts(&counts);
671
0
      }
672
673
0
      counts.mix_count = 0;
674
675
0
      if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
676
0
          counts.fom_count >= counts.color_count && counts.fom_count >= counts.mix_count &&
677
0
          counts.fom_count >= counts.bicolor_count)
678
0
      {
679
0
        if (counts.fom_count > count)
680
0
          return -1;
681
682
0
        count -= counts.fom_count;
683
0
        count = out_copy_count_3(count, s, temp_s);
684
0
        counts.fom_count =
685
0
            out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
686
0
        reset_counts(&counts);
687
0
      }
688
689
0
      counts.fom_count = 0;
690
0
      counts.fom_mask_len = 0;
691
0
    }
692
693
0
    last_line = line;
694
0
    line = line - 4ULL * width;
695
0
    start_line--;
696
0
    lines_sent++;
697
0
  }
698
699
0
  Stream_SetPosition(temp_s, 0);
700
701
0
  if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
702
0
      counts.fill_count >= counts.bicolor_count && counts.fill_count >= counts.mix_count &&
703
0
      counts.fill_count >= counts.fom_count)
704
0
  {
705
0
    if (counts.fill_count > count)
706
0
      return -1;
707
708
0
    count -= counts.fill_count;
709
0
    (void)out_copy_count_3(count, s, temp_s);
710
0
    counts.fill_count = out_fill_count_3(counts.fill_count, s);
711
0
  }
712
0
  else if (counts.mix_count > 3 && counts.mix_count >= counts.color_count &&
713
0
           counts.mix_count >= counts.bicolor_count && counts.mix_count >= counts.fill_count &&
714
0
           counts.mix_count >= counts.fom_count)
715
0
  {
716
0
    if (counts.mix_count > count)
717
0
      return -1;
718
719
0
    count -= counts.mix_count;
720
0
    (void)out_copy_count_3(count, s, temp_s);
721
0
    counts.mix_count = out_counts_mix_count_3(counts.mix_count, s);
722
0
  }
723
0
  else if (counts.color_count > 3 && counts.color_count >= counts.mix_count &&
724
0
           counts.color_count >= counts.bicolor_count &&
725
0
           counts.color_count >= counts.fill_count && counts.color_count >= counts.fom_count)
726
0
  {
727
0
    if (counts.color_count > count)
728
0
      return -1;
729
730
0
    count -= counts.color_count;
731
0
    (void)out_copy_count_3(count, s, temp_s);
732
0
    counts.color_count = out_color_count_3(counts.color_count, s, last_pixel);
733
0
  }
734
0
  else if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.mix_count &&
735
0
           counts.bicolor_count >= counts.color_count &&
736
0
           counts.bicolor_count >= counts.fill_count && counts.bicolor_count >= counts.fom_count)
737
0
  {
738
0
    if ((counts.bicolor_count % 2) != 0)
739
0
      counts.bicolor_count--;
740
741
0
    if (counts.bicolor_count > count)
742
0
      return -1;
743
744
0
    count -= counts.bicolor_count;
745
0
    count = out_copy_count_3(count, s, temp_s);
746
0
    counts.bicolor_count = out_bicolor_count_3(counts.bicolor_count, s, bicolor2, bicolor1);
747
748
0
    if (counts.bicolor_count > count)
749
0
      return -1;
750
751
0
    count -= counts.bicolor_count;
752
0
    (void)out_copy_count_3(count, s, temp_s);
753
0
    counts.bicolor_count = out_bicolor_count_3(counts.bicolor_count, s, bicolor1, bicolor2);
754
0
  }
755
0
  else if (counts.fom_count > 3 && counts.fom_count >= counts.mix_count &&
756
0
           counts.fom_count >= counts.color_count && counts.fom_count >= counts.fill_count &&
757
0
           counts.fom_count >= counts.bicolor_count)
758
0
  {
759
0
    if (counts.fom_count > count)
760
0
      return -1;
761
762
0
    count -= counts.fom_count;
763
0
    (void)out_copy_count_3(count, s, temp_s);
764
0
    counts.fom_count = out_from_count_3(counts.fom_count, s, fom_mask, counts.fom_mask_len);
765
0
  }
766
0
  else
767
0
  {
768
0
    (void)out_copy_count_3(count, s, temp_s);
769
0
  }
770
771
0
  return lines_sent;
772
0
}
773
774
static inline SSIZE_T freerdp_bitmap_compress_16(const void* WINPR_RESTRICT srcData, UINT32 width,
775
                                                 WINPR_ATTR_UNUSED UINT32 height,
776
                                                 wStream* WINPR_RESTRICT s, UINT32 bpp,
777
                                                 UINT32 byte_limit, UINT32 start_line,
778
                                                 wStream* WINPR_RESTRICT temp_s, UINT32 e)
779
0
{
780
0
  uint8_t fom_mask[8192] = { 0 }; /* good for up to 64K bitmap */
781
0
  SSIZE_T lines_sent = 0;
782
0
  UINT16 count = 0;
783
0
  UINT16 last_pixel = 0;
784
0
  UINT16 last_ypixel = 0;
785
0
  struct count counts = { 0 };
786
0
  UINT16 bicolor1 = 0;
787
0
  UINT16 bicolor2 = 0;
788
0
  UINT32 end = width + e;
789
0
  UINT32 out_count = end * 2;
790
0
  const UINT32 mix = (bpp == 15) ? 0xBA1F : 0xFFFF;
791
0
  const char* start = (const char*)srcData;
792
0
  const char* line = start + 2ULL * width * start_line;
793
0
  const char* last_line = NULL;
794
795
0
  while ((line >= start) && (out_count < 32768))
796
0
  {
797
0
    size_t i = Stream_GetPosition(s) + 2ULL * count;
798
799
0
    if ((i - (2ULL * counts.color_count) >= byte_limit) &&
800
0
        (i - (2ULL * counts.bicolor_count) >= byte_limit) &&
801
0
        (i - (2ULL * counts.fill_count) >= byte_limit) &&
802
0
        (i - (2ULL * counts.mix_count) >= byte_limit) &&
803
0
        (i - (2ULL * counts.fom_count) >= byte_limit))
804
0
    {
805
0
      break;
806
0
    }
807
808
0
    out_count += end * 2;
809
810
0
    for (UINT32 j = 0; j < end; j++)
811
0
    {
812
      /* read next pixel */
813
0
      const UINT16 pixel = IN_PIXEL16(line, j, 0, width, last_pixel);
814
0
      const UINT16 ypixel = IN_PIXEL16(last_line, j, 0, width, last_ypixel);
815
816
0
      if (!TEST_FILL)
817
0
      {
818
0
        if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
819
0
            counts.fill_count >= counts.bicolor_count &&
820
0
            counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
821
0
        {
822
0
          if (counts.fill_count > count)
823
0
            return -1;
824
825
0
          count -= counts.fill_count;
826
0
          count = out_copy_count_2(count, s, temp_s);
827
0
          counts.fill_count = out_fill_count_2(counts.fill_count, s);
828
0
          reset_counts(&counts);
829
0
        }
830
831
0
        counts.fill_count = 0;
832
0
      }
833
834
0
      if (!TEST_MIX)
835
0
      {
836
0
        if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
837
0
            counts.mix_count >= counts.bicolor_count &&
838
0
            counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
839
0
        {
840
0
          if (counts.mix_count > count)
841
0
            return -1;
842
843
0
          count -= counts.mix_count;
844
0
          count = out_copy_count_2(count, s, temp_s);
845
0
          counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
846
0
          reset_counts(&counts);
847
0
        }
848
849
0
        counts.mix_count = 0;
850
0
      }
851
852
0
      if (!(TEST_COLOR))
853
0
      {
854
0
        if (counts.color_count > 3 && counts.color_count >= counts.fill_count &&
855
0
            counts.color_count >= counts.bicolor_count &&
856
0
            counts.color_count >= counts.mix_count &&
857
0
            counts.color_count >= counts.fom_count)
858
0
        {
859
0
          if (counts.color_count > count)
860
0
            return -1;
861
862
0
          count -= counts.color_count;
863
0
          count = out_copy_count_2(count, s, temp_s);
864
0
          counts.color_count = out_color_count_2(counts.color_count, s, last_pixel);
865
0
          reset_counts(&counts);
866
0
        }
867
868
0
        counts.color_count = 0;
869
0
      }
870
871
0
      if (!test_bicolor(&counts))
872
0
      {
873
0
        if ((counts.bicolor_count > 3) && (counts.bicolor_count >= counts.fill_count) &&
874
0
            (counts.bicolor_count >= counts.color_count) &&
875
0
            (counts.bicolor_count >= counts.mix_count) &&
876
0
            (counts.bicolor_count >= counts.fom_count))
877
0
        {
878
0
          if ((counts.bicolor_count % 2) != 0)
879
0
            counts.bicolor_count--;
880
881
0
          if (counts.bicolor_count > count)
882
0
            return -1;
883
884
0
          count -= counts.bicolor_count;
885
0
          count = out_copy_count_2(count, s, temp_s);
886
0
          counts.bicolor_count =
887
0
              out_bicolor_count_2(counts.bicolor_count, s, bicolor2, bicolor1);
888
0
          reset_counts(&counts);
889
0
        }
890
891
0
        counts.bicolor_count = 0;
892
0
        bicolor1 = last_pixel;
893
0
        bicolor2 = pixel;
894
0
        counts.bicolor_spin = FALSE;
895
0
      }
896
897
0
      if (!(TEST_FOM))
898
0
      {
899
0
        if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
900
0
            counts.fom_count >= counts.color_count &&
901
0
            counts.fom_count >= counts.mix_count &&
902
0
            counts.fom_count >= counts.bicolor_count)
903
0
        {
904
0
          if (counts.fom_count > count)
905
0
            return -1;
906
907
0
          count -= counts.fom_count;
908
0
          count = out_copy_count_2(count, s, temp_s);
909
0
          counts.fom_count =
910
0
              out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
911
0
          reset_counts(&counts);
912
0
        }
913
914
0
        counts.fom_count = 0;
915
0
        counts.fom_mask_len = 0;
916
0
      }
917
918
0
      if (TEST_FILL)
919
0
      {
920
0
        counts.fill_count++;
921
0
      }
922
923
0
      if (TEST_MIX)
924
0
      {
925
0
        counts.mix_count++;
926
0
      }
927
928
0
      if (TEST_COLOR)
929
0
      {
930
0
        counts.color_count++;
931
0
      }
932
933
0
      if (test_bicolor(&counts))
934
0
      {
935
0
        counts.bicolor_spin = !counts.bicolor_spin;
936
0
        counts.bicolor_count++;
937
0
      }
938
939
0
      if (TEST_FOM)
940
0
      {
941
0
        if ((counts.fom_count % 8) == 0)
942
0
        {
943
0
          fom_mask[counts.fom_mask_len] = 0;
944
0
          counts.fom_mask_len++;
945
0
        }
946
947
0
        if (pixel == (ypixel ^ mix))
948
0
        {
949
0
          const uint8_t tmp = (1 << (counts.fom_count % 8)) & 0xFF;
950
0
          const uint8_t val = fom_mask[counts.fom_mask_len - 1] | tmp;
951
0
          fom_mask[counts.fom_mask_len - 1] = val;
952
0
        }
953
954
0
        counts.fom_count++;
955
0
      }
956
957
0
      Stream_Write_UINT16(temp_s, pixel);
958
0
      count++;
959
0
      last_pixel = pixel;
960
0
      last_ypixel = ypixel;
961
0
    }
962
963
    /* can't take fix, mix, or fom past first line */
964
0
    if (last_line == 0)
965
0
    {
966
0
      if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
967
0
          counts.fill_count >= counts.bicolor_count &&
968
0
          counts.fill_count >= counts.mix_count && counts.fill_count >= counts.fom_count)
969
0
      {
970
0
        if (counts.fill_count > count)
971
0
          return -1;
972
973
0
        count -= counts.fill_count;
974
0
        count = out_copy_count_2(count, s, temp_s);
975
0
        counts.fill_count = out_fill_count_2(counts.fill_count, s);
976
0
        reset_counts(&counts);
977
0
      }
978
979
0
      counts.fill_count = 0;
980
981
0
      if (counts.mix_count > 3 && counts.mix_count >= counts.fill_count &&
982
0
          counts.mix_count >= counts.bicolor_count &&
983
0
          counts.mix_count >= counts.color_count && counts.mix_count >= counts.fom_count)
984
0
      {
985
0
        if (counts.mix_count > count)
986
0
          return -1;
987
988
0
        count -= counts.mix_count;
989
0
        count = out_copy_count_2(count, s, temp_s);
990
0
        counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
991
0
        reset_counts(&counts);
992
0
      }
993
994
0
      counts.mix_count = 0;
995
996
0
      if (counts.fom_count > 3 && counts.fom_count >= counts.fill_count &&
997
0
          counts.fom_count >= counts.color_count && counts.fom_count >= counts.mix_count &&
998
0
          counts.fom_count >= counts.bicolor_count)
999
0
      {
1000
0
        if (counts.fom_count > count)
1001
0
          return -1;
1002
1003
0
        count -= counts.fom_count;
1004
0
        count = out_copy_count_2(count, s, temp_s);
1005
0
        counts.fom_count =
1006
0
            out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
1007
0
        reset_counts(&counts);
1008
0
      }
1009
1010
0
      counts.fom_count = 0;
1011
0
      counts.fom_mask_len = 0;
1012
0
    }
1013
1014
0
    last_line = line;
1015
0
    line = line - 2ULL * width;
1016
0
    start_line--;
1017
0
    lines_sent++;
1018
0
  }
1019
1020
0
  Stream_SetPosition(temp_s, 0);
1021
1022
0
  if (counts.fill_count > 3 && counts.fill_count >= counts.color_count &&
1023
0
      counts.fill_count >= counts.bicolor_count && counts.fill_count >= counts.mix_count &&
1024
0
      counts.fill_count >= counts.fom_count)
1025
0
  {
1026
0
    if (counts.fill_count > count)
1027
0
      return -1;
1028
1029
0
    count -= counts.fill_count;
1030
0
    (void)out_copy_count_2(count, s, temp_s);
1031
0
    counts.fill_count = out_fill_count_2(counts.fill_count, s);
1032
0
  }
1033
0
  else if (counts.mix_count > 3 && counts.mix_count >= counts.color_count &&
1034
0
           counts.mix_count >= counts.bicolor_count && counts.mix_count >= counts.fill_count &&
1035
0
           counts.mix_count >= counts.fom_count)
1036
0
  {
1037
0
    if (counts.mix_count > count)
1038
0
      return -1;
1039
1040
0
    count -= counts.mix_count;
1041
0
    (void)out_copy_count_2(count, s, temp_s);
1042
0
    counts.mix_count = out_counts_mix_count_2(counts.mix_count, s);
1043
0
  }
1044
0
  else if (counts.color_count > 3 && counts.color_count >= counts.mix_count &&
1045
0
           counts.color_count >= counts.bicolor_count &&
1046
0
           counts.color_count >= counts.fill_count && counts.color_count >= counts.fom_count)
1047
0
  {
1048
0
    if (counts.color_count > count)
1049
0
      return -1;
1050
1051
0
    count -= counts.color_count;
1052
0
    (void)out_copy_count_2(count, s, temp_s);
1053
0
    counts.color_count = out_color_count_2(counts.color_count, s, last_pixel);
1054
0
  }
1055
0
  else if (counts.bicolor_count > 3 && counts.bicolor_count >= counts.mix_count &&
1056
0
           counts.bicolor_count >= counts.color_count &&
1057
0
           counts.bicolor_count >= counts.fill_count && counts.bicolor_count >= counts.fom_count)
1058
0
  {
1059
0
    if ((counts.bicolor_count % 2) != 0)
1060
0
      counts.bicolor_count--;
1061
1062
0
    if (counts.bicolor_count > count)
1063
0
      return -1;
1064
1065
0
    count -= counts.bicolor_count;
1066
0
    count = out_copy_count_2(count, s, temp_s);
1067
0
    counts.bicolor_count = out_bicolor_count_2(counts.bicolor_count, s, bicolor2, bicolor1);
1068
1069
0
    if (counts.bicolor_count > count)
1070
0
      return -1;
1071
1072
0
    count -= counts.bicolor_count;
1073
0
    (void)out_copy_count_2(count, s, temp_s);
1074
0
    counts.bicolor_count = out_bicolor_count_2(counts.bicolor_count, s, bicolor1, bicolor2);
1075
0
  }
1076
0
  else if (counts.fom_count > 3 && counts.fom_count >= counts.mix_count &&
1077
0
           counts.fom_count >= counts.color_count && counts.fom_count >= counts.fill_count &&
1078
0
           counts.fom_count >= counts.bicolor_count)
1079
0
  {
1080
0
    if (counts.fom_count > count)
1081
0
      return -1;
1082
1083
0
    count -= counts.fom_count;
1084
0
    (void)out_copy_count_2(count, s, temp_s);
1085
0
    counts.fom_count = out_from_count_2(counts.fom_count, s, fom_mask, counts.fom_mask_len);
1086
0
  }
1087
0
  else
1088
0
  {
1089
0
    (void)out_copy_count_2(count, s, temp_s);
1090
0
  }
1091
1092
0
  return lines_sent;
1093
0
}
1094
1095
SSIZE_T freerdp_bitmap_compress(const void* WINPR_RESTRICT srcData, UINT32 width, UINT32 height,
1096
                                wStream* WINPR_RESTRICT s, UINT32 bpp, UINT32 byte_limit,
1097
                                UINT32 start_line, wStream* WINPR_RESTRICT temp_s, UINT32 e)
1098
0
{
1099
0
  Stream_SetPosition(temp_s, 0);
1100
1101
0
  switch (bpp)
1102
0
  {
1103
0
    case 15:
1104
0
    case 16:
1105
0
      return freerdp_bitmap_compress_16(srcData, width, height, s, bpp, byte_limit,
1106
0
                                        start_line, temp_s, e);
1107
1108
0
    case 24:
1109
0
      return freerdp_bitmap_compress_24(srcData, width, height, s, byte_limit, start_line,
1110
0
                                        temp_s, e);
1111
1112
0
    default:
1113
0
      return -1;
1114
0
  }
1115
0
}