/src/FreeRDP/libfreerdp/codec/interleaved.c
Line | Count | Source |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * Interleaved RLE Bitmap Codec |
4 | | * |
5 | | * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * Copyright 2015 Thincast Technologies GmbH |
7 | | * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> |
8 | | * Copyright 2016 Armin Novak <armin.novak@thincast.com> |
9 | | * Copyright 2016 Thincast Technologies GmbH |
10 | | * |
11 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
12 | | * you may not use this file except in compliance with the License. |
13 | | * You may obtain a copy of the License at |
14 | | * |
15 | | * http://www.apache.org/licenses/LICENSE-2.0 |
16 | | * |
17 | | * Unless required by applicable law or agreed to in writing, software |
18 | | * distributed under the License is distributed on an "AS IS" BASIS, |
19 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
20 | | * See the License for the specific language governing permissions and |
21 | | * limitations under the License. |
22 | | */ |
23 | | |
24 | | #include <winpr/assert.h> |
25 | | #include <winpr/cast.h> |
26 | | #include <freerdp/config.h> |
27 | | |
28 | | #include <freerdp/codec/interleaved.h> |
29 | | #include <freerdp/log.h> |
30 | | |
31 | | #define TAG FREERDP_TAG("codec") |
32 | | |
33 | | #define UNROLL_BODY(_exp, _count) \ |
34 | 2.40M | do \ |
35 | 2.40M | { \ |
36 | 16.6M | for (size_t x = 0; x < (_count); x++) \ |
37 | 14.1M | { \ |
38 | 14.1M | do \ |
39 | 14.1M | { \ |
40 | 49.8M | _exp \ |
41 | 14.1M | } while (FALSE); \ |
42 | 14.1M | } \ |
43 | 2.40M | } while (FALSE) |
44 | | |
45 | | #define UNROLL_MULTIPLE(_condition, _exp, _count) \ |
46 | 1.85M | do \ |
47 | 1.85M | { \ |
48 | 4.25M | while ((_condition) >= (_count)) \ |
49 | 2.40M | { \ |
50 | 2.40M | UNROLL_BODY(_exp, _count); \ |
51 | 2.40M | (_condition) -= (_count); \ |
52 | 2.40M | } \ |
53 | 1.85M | } while (FALSE) |
54 | | |
55 | | #define UNROLL(_condition, _exp) \ |
56 | 617k | do \ |
57 | 617k | { \ |
58 | 617k | UNROLL_MULTIPLE(_condition, _exp, 16); \ |
59 | 617k | UNROLL_MULTIPLE(_condition, _exp, 4); \ |
60 | 617k | UNROLL_MULTIPLE(_condition, _exp, 1); \ |
61 | 617k | } while (FALSE) |
62 | | |
63 | | /* |
64 | | RLE Compressed Bitmap Stream (RLE_BITMAP_STREAM) |
65 | | http://msdn.microsoft.com/en-us/library/cc240895%28v=prot.10%29.aspx |
66 | | pseudo-code |
67 | | http://msdn.microsoft.com/en-us/library/dd240593%28v=prot.10%29.aspx |
68 | | */ |
69 | | |
70 | 873k | #define REGULAR_BG_RUN 0x00 |
71 | 139k | #define MEGA_MEGA_BG_RUN 0xF0 |
72 | 423k | #define REGULAR_FG_RUN 0x01 |
73 | 33.0k | #define MEGA_MEGA_FG_RUN 0xF1 |
74 | 160k | #define LITE_SET_FG_FG_RUN 0x0C |
75 | 83.0k | #define MEGA_MEGA_SET_FG_RUN 0xF6 |
76 | 42.8k | #define LITE_DITHERED_RUN 0x0E |
77 | 18.9k | #define MEGA_MEGA_DITHERED_RUN 0xF8 |
78 | 441k | #define REGULAR_COLOR_RUN 0x03 |
79 | 30.9k | #define MEGA_MEGA_COLOR_RUN 0xF3 |
80 | 26.0k | #define REGULAR_FGBG_IMAGE 0x02 |
81 | 23.9k | #define MEGA_MEGA_FGBG_IMAGE 0xF2 |
82 | 61.9k | #define LITE_SET_FG_FGBG_IMAGE 0x0D |
83 | 45.6k | #define MEGA_MEGA_SET_FGBG_IMAGE 0xF7 |
84 | 439k | #define REGULAR_COLOR_IMAGE 0x04 |
85 | 25.0k | #define MEGA_MEGA_COLOR_IMAGE 0xF4 |
86 | 2.37k | #define SPECIAL_FGBG_1 0xF9 |
87 | 3.10k | #define SPECIAL_FGBG_2 0xFA |
88 | 3.11k | #define SPECIAL_WHITE 0xFD |
89 | 2.61k | #define SPECIAL_BLACK 0xFE |
90 | | |
91 | | #define BLACK_PIXEL 0x000000 |
92 | | |
93 | | typedef UINT32 PIXEL; |
94 | | |
95 | | static const BYTE g_MaskSpecialFgBg1 = 0x03; |
96 | | static const BYTE g_MaskSpecialFgBg2 = 0x05; |
97 | | |
98 | | static const BYTE g_MaskRegularRunLength = 0x1F; |
99 | | static const BYTE g_MaskLiteRunLength = 0x0F; |
100 | | |
101 | | #if defined(WITH_DEBUG_CODECS) |
102 | | static const char* rle_code_str(UINT32 code) |
103 | | { |
104 | | switch (code) |
105 | | { |
106 | | case REGULAR_BG_RUN: |
107 | | return "REGULAR_BG_RUN"; |
108 | | case MEGA_MEGA_BG_RUN: |
109 | | return "MEGA_MEGA_BG_RUN"; |
110 | | case REGULAR_FG_RUN: |
111 | | return "REGULAR_FG_RUN"; |
112 | | case MEGA_MEGA_FG_RUN: |
113 | | return "MEGA_MEGA_FG_RUN"; |
114 | | case LITE_SET_FG_FG_RUN: |
115 | | return "LITE_SET_FG_FG_RUN"; |
116 | | case MEGA_MEGA_SET_FG_RUN: |
117 | | return "MEGA_MEGA_SET_FG_RUN"; |
118 | | case LITE_DITHERED_RUN: |
119 | | return "LITE_DITHERED_RUN"; |
120 | | case MEGA_MEGA_DITHERED_RUN: |
121 | | return "MEGA_MEGA_DITHERED_RUN"; |
122 | | case REGULAR_COLOR_RUN: |
123 | | return "REGULAR_COLOR_RUN"; |
124 | | case MEGA_MEGA_COLOR_RUN: |
125 | | return "MEGA_MEGA_COLOR_RUN"; |
126 | | case REGULAR_FGBG_IMAGE: |
127 | | return "REGULAR_FGBG_IMAGE"; |
128 | | case MEGA_MEGA_FGBG_IMAGE: |
129 | | return "MEGA_MEGA_FGBG_IMAGE"; |
130 | | case LITE_SET_FG_FGBG_IMAGE: |
131 | | return "LITE_SET_FG_FGBG_IMAGE"; |
132 | | case MEGA_MEGA_SET_FGBG_IMAGE: |
133 | | return "MEGA_MEGA_SET_FGBG_IMAGE"; |
134 | | case REGULAR_COLOR_IMAGE: |
135 | | return "REGULAR_COLOR_IMAGE"; |
136 | | case MEGA_MEGA_COLOR_IMAGE: |
137 | | return "MEGA_MEGA_COLOR_IMAGE"; |
138 | | case SPECIAL_FGBG_1: |
139 | | return "SPECIAL_FGBG_1"; |
140 | | case SPECIAL_FGBG_2: |
141 | | return "SPECIAL_FGBG_2"; |
142 | | case SPECIAL_WHITE: |
143 | | return "SPECIAL_WHITE"; |
144 | | case SPECIAL_BLACK: |
145 | | return "SPECIAL_BLACK"; |
146 | | default: |
147 | | return "UNKNOWN"; |
148 | | } |
149 | | } |
150 | | #endif |
151 | | |
152 | | #define buffer_within_range(pbSrc, size, pbEnd) \ |
153 | 838k | buffer_within_range_((pbSrc), (size), (pbEnd), __func__, __FILE__, __LINE__) |
154 | | static inline BOOL buffer_within_range_(const void* pbSrc, size_t size, const void* pbEnd, |
155 | | const char* fkt, const char* file, size_t line) |
156 | 838k | { |
157 | 838k | WINPR_UNUSED(file); |
158 | 838k | WINPR_ASSERT(pbSrc); |
159 | 838k | WINPR_ASSERT(pbEnd); |
160 | | |
161 | 838k | if ((const char*)pbSrc + size > (const char*)pbEnd) |
162 | 3.47k | { |
163 | 3.47k | WLog_ERR(TAG, "[%s:%" PRIuz "] pbSrc=%p + %" PRIuz " > pbEnd=%p", fkt, line, pbSrc, size, |
164 | 3.47k | pbEnd); |
165 | 3.47k | return FALSE; |
166 | 3.47k | } |
167 | 834k | return TRUE; |
168 | 838k | } |
169 | | |
170 | | /** |
171 | | * Reads the supplied order header and extracts the compression |
172 | | * order code ID. |
173 | | */ |
174 | | static inline UINT32 ExtractCodeId(BYTE bOrderHdr) |
175 | 506k | { |
176 | 506k | if ((bOrderHdr & 0xC0U) != 0xC0U) |
177 | 443k | { |
178 | | /* REGULAR orders |
179 | | * (000x xxxx, 001x xxxx, 010x xxxx, 011x xxxx, 100x xxxx) |
180 | | */ |
181 | 443k | return bOrderHdr >> 5; |
182 | 443k | } |
183 | 62.3k | else if ((bOrderHdr & 0xF0U) == 0xF0U) |
184 | 28.5k | { |
185 | | /* MEGA and SPECIAL orders (0xF*) */ |
186 | 28.5k | return bOrderHdr; |
187 | 28.5k | } |
188 | 33.8k | else |
189 | 33.8k | { |
190 | | /* LITE orders |
191 | | * 1100 xxxx, 1101 xxxx, 1110 xxxx) |
192 | | */ |
193 | 33.8k | return bOrderHdr >> 4; |
194 | 33.8k | } |
195 | 506k | } |
196 | | |
197 | | /** |
198 | | * Extract the run length of a compression order. |
199 | | */ |
200 | | static UINT ExtractRunLengthRegularFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) |
201 | 13.0k | { |
202 | 13.0k | UINT runLength = 0; |
203 | | |
204 | 13.0k | WINPR_ASSERT(pbOrderHdr); |
205 | 13.0k | WINPR_ASSERT(pbEnd); |
206 | 13.0k | WINPR_ASSERT(advance); |
207 | | |
208 | 13.0k | runLength = (*pbOrderHdr) & g_MaskRegularRunLength; |
209 | 13.0k | if (runLength == 0) |
210 | 4.80k | { |
211 | 4.80k | if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) |
212 | 17 | { |
213 | 17 | *advance = 0; |
214 | 17 | return 0; |
215 | 17 | } |
216 | 4.78k | runLength = *(pbOrderHdr + 1) + 1; |
217 | 4.78k | (*advance)++; |
218 | 4.78k | } |
219 | 8.20k | else |
220 | 8.20k | runLength = runLength * 8; |
221 | | |
222 | 12.9k | return runLength; |
223 | 13.0k | } |
224 | | |
225 | | static UINT ExtractRunLengthLiteFgBg(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) |
226 | 3.67k | { |
227 | 3.67k | UINT runLength = 0; |
228 | | |
229 | 3.67k | WINPR_ASSERT(pbOrderHdr); |
230 | 3.67k | WINPR_ASSERT(pbEnd); |
231 | 3.67k | WINPR_ASSERT(advance); |
232 | | |
233 | 3.67k | runLength = *pbOrderHdr & g_MaskLiteRunLength; |
234 | 3.67k | if (runLength == 0) |
235 | 315 | { |
236 | 315 | if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) |
237 | 13 | { |
238 | 13 | *advance = 0; |
239 | 13 | return 0; |
240 | 13 | } |
241 | 302 | runLength = *(pbOrderHdr + 1) + 1; |
242 | 302 | (*advance)++; |
243 | 302 | } |
244 | 3.35k | else |
245 | 3.35k | runLength = runLength * 8; |
246 | | |
247 | 3.65k | return runLength; |
248 | 3.67k | } |
249 | | |
250 | | static UINT ExtractRunLengthRegular(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) |
251 | 429k | { |
252 | 429k | UINT runLength = 0; |
253 | | |
254 | 429k | WINPR_ASSERT(pbOrderHdr); |
255 | 429k | WINPR_ASSERT(pbEnd); |
256 | 429k | WINPR_ASSERT(advance); |
257 | | |
258 | 429k | runLength = *pbOrderHdr & g_MaskRegularRunLength; |
259 | 429k | if (runLength == 0) |
260 | 206k | { |
261 | 206k | if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) |
262 | 598 | { |
263 | 598 | *advance = 0; |
264 | 598 | return 0; |
265 | 598 | } |
266 | 206k | runLength = *(pbOrderHdr + 1) + 32; |
267 | 206k | (*advance)++; |
268 | 206k | } |
269 | | |
270 | 428k | return runLength; |
271 | 429k | } |
272 | | |
273 | | static UINT ExtractRunLengthMegaMega(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) |
274 | 12.2k | { |
275 | 12.2k | UINT runLength = 0; |
276 | | |
277 | 12.2k | WINPR_ASSERT(pbOrderHdr); |
278 | 12.2k | WINPR_ASSERT(pbEnd); |
279 | 12.2k | WINPR_ASSERT(advance); |
280 | | |
281 | 12.2k | if (!buffer_within_range(pbOrderHdr, 3, pbEnd)) |
282 | 361 | { |
283 | 361 | *advance = 0; |
284 | 361 | return 0; |
285 | 361 | } |
286 | | |
287 | 11.8k | runLength = ((UINT16)pbOrderHdr[1]) | ((((UINT16)pbOrderHdr[2]) << 8) & 0xFF00); |
288 | 11.8k | (*advance) += 2; |
289 | | |
290 | 11.8k | return runLength; |
291 | 12.2k | } |
292 | | |
293 | | static UINT ExtractRunLengthLite(const BYTE* pbOrderHdr, const BYTE* pbEnd, UINT32* advance) |
294 | 30.1k | { |
295 | 30.1k | UINT runLength = 0; |
296 | | |
297 | 30.1k | WINPR_ASSERT(pbOrderHdr); |
298 | 30.1k | WINPR_ASSERT(pbEnd); |
299 | 30.1k | WINPR_ASSERT(advance); |
300 | | |
301 | 30.1k | runLength = *pbOrderHdr & g_MaskLiteRunLength; |
302 | 30.1k | if (runLength == 0) |
303 | 4.93k | { |
304 | 4.93k | if (!buffer_within_range(pbOrderHdr, 2, pbEnd)) |
305 | 17 | { |
306 | 17 | *advance = 0; |
307 | 17 | return 0; |
308 | 17 | } |
309 | 4.92k | runLength = *(pbOrderHdr + 1) + 16; |
310 | 4.92k | (*advance)++; |
311 | 4.92k | } |
312 | 30.1k | return runLength; |
313 | 30.1k | } |
314 | | |
315 | | static inline UINT32 ExtractRunLength(UINT32 code, const BYTE* pbOrderHdr, const BYTE* pbEnd, |
316 | | UINT32* advance) |
317 | 488k | { |
318 | 488k | UINT32 runLength = 0; |
319 | 488k | UINT32 ladvance = 1; |
320 | | |
321 | 488k | WINPR_ASSERT(pbOrderHdr); |
322 | 488k | WINPR_ASSERT(pbEnd); |
323 | 488k | WINPR_ASSERT(advance); |
324 | | |
325 | | #if defined(WITH_DEBUG_CODECS) |
326 | | WLog_VRB(TAG, "extracting %s", rle_code_str(code)); |
327 | | #endif |
328 | | |
329 | 488k | *advance = 0; |
330 | 488k | if (!buffer_within_range(pbOrderHdr, 0, pbEnd)) |
331 | 0 | return 0; |
332 | | |
333 | 488k | switch (code) |
334 | 488k | { |
335 | 13.0k | case REGULAR_FGBG_IMAGE: |
336 | 13.0k | runLength = ExtractRunLengthRegularFgBg(pbOrderHdr, pbEnd, &ladvance); |
337 | 13.0k | break; |
338 | | |
339 | 3.67k | case LITE_SET_FG_FGBG_IMAGE: |
340 | 3.67k | runLength = ExtractRunLengthLiteFgBg(pbOrderHdr, pbEnd, &ladvance); |
341 | 3.67k | break; |
342 | | |
343 | 367k | case REGULAR_BG_RUN: |
344 | 395k | case REGULAR_FG_RUN: |
345 | 418k | case REGULAR_COLOR_RUN: |
346 | 429k | case REGULAR_COLOR_IMAGE: |
347 | 429k | runLength = ExtractRunLengthRegular(pbOrderHdr, pbEnd, &ladvance); |
348 | 429k | break; |
349 | | |
350 | 17.4k | case LITE_SET_FG_FG_RUN: |
351 | 30.1k | case LITE_DITHERED_RUN: |
352 | 30.1k | runLength = ExtractRunLengthLite(pbOrderHdr, pbEnd, &ladvance); |
353 | 30.1k | break; |
354 | | |
355 | 1.42k | case MEGA_MEGA_BG_RUN: |
356 | 3.28k | case MEGA_MEGA_FG_RUN: |
357 | 4.26k | case MEGA_MEGA_SET_FG_RUN: |
358 | 5.26k | case MEGA_MEGA_DITHERED_RUN: |
359 | 6.59k | case MEGA_MEGA_COLOR_RUN: |
360 | 8.75k | case MEGA_MEGA_FGBG_IMAGE: |
361 | 9.76k | case MEGA_MEGA_SET_FGBG_IMAGE: |
362 | 12.2k | case MEGA_MEGA_COLOR_IMAGE: |
363 | 12.2k | runLength = ExtractRunLengthMegaMega(pbOrderHdr, pbEnd, &ladvance); |
364 | 12.2k | break; |
365 | | |
366 | 0 | default: |
367 | 0 | runLength = 0; |
368 | 0 | ladvance = 0; |
369 | 0 | break; |
370 | 488k | } |
371 | | |
372 | 488k | *advance = ladvance; |
373 | 488k | return runLength; |
374 | 488k | } |
375 | | |
376 | | #define ensure_capacity(start, end, size, base) \ |
377 | 967k | ensure_capacity_((start), (end), (size), (base), __func__, __FILE__, __LINE__) |
378 | | static inline BOOL ensure_capacity_(const BYTE* start, const BYTE* end, size_t size, size_t base, |
379 | | const char* fkt, WINPR_ATTR_UNUSED const char* file, |
380 | | size_t line) |
381 | 967k | { |
382 | 967k | const size_t available = (uintptr_t)end - (uintptr_t)start; |
383 | 967k | const BOOL rc = available >= size * base; |
384 | 967k | const BOOL res = rc && (start <= end); |
385 | | |
386 | 967k | if (!res) |
387 | 3.19k | WLog_ERR(TAG, |
388 | 967k | "[%s:%" PRIuz "] failed: start=%p <= end=%p, available=%" PRIuz " >= size=%" PRIuz |
389 | 967k | " * base=%" PRIuz, |
390 | 967k | fkt, line, WINPR_CXX_COMPAT_CAST(const void*, start), |
391 | 967k | WINPR_CXX_COMPAT_CAST(const void*, end), available, size, base); |
392 | 967k | return res; |
393 | 967k | } |
394 | | |
395 | | static inline void write_pixel_8(BYTE* _buf, BYTE _pix) |
396 | 0 | { |
397 | 0 | WINPR_ASSERT(_buf); |
398 | 0 | *_buf = _pix; |
399 | 0 | } |
400 | | |
401 | | static inline void write_pixel_24(BYTE* _buf, UINT32 _pix) |
402 | 4.70M | { |
403 | 4.70M | WINPR_ASSERT(_buf); |
404 | 4.70M | (_buf)[0] = (BYTE)(_pix); |
405 | 4.70M | (_buf)[1] = (BYTE)((_pix) >> 8); |
406 | 4.70M | (_buf)[2] = (BYTE)((_pix) >> 16); |
407 | 4.70M | } |
408 | | |
409 | | static inline void write_pixel_16(BYTE* _buf, UINT16 _pix) |
410 | 10.4M | { |
411 | 10.4M | WINPR_ASSERT(_buf); |
412 | 10.4M | _buf[0] = _pix & 0xFF; |
413 | 10.4M | _buf[1] = (_pix >> 8) & 0xFF; |
414 | 10.4M | } |
415 | | |
416 | | #undef DESTWRITEPIXEL |
417 | | #undef DESTREADPIXEL |
418 | | #undef SRCREADPIXEL |
419 | | #undef WRITEFGBGIMAGE |
420 | | #undef WRITEFIRSTLINEFGBGIMAGE |
421 | | #undef RLEDECOMPRESS |
422 | | #undef RLEEXTRA |
423 | | #undef WHITE_PIXEL |
424 | | #undef PIXEL_SIZE |
425 | | #undef PIXEL |
426 | | #define PIXEL_SIZE 1 |
427 | 0 | #define PIXEL BYTE |
428 | 0 | #define WHITE_PIXEL 0xFF |
429 | | #define DESTWRITEPIXEL(_buf, _pix) \ |
430 | 0 | do \ |
431 | 0 | { \ |
432 | 0 | write_pixel_8(_buf, _pix); \ |
433 | 0 | (_buf) += 1; \ |
434 | 0 | } while (0) |
435 | 0 | #define DESTREADPIXEL(_pix, _buf) _pix = (_buf)[0] |
436 | | #define SRCREADPIXEL(_pix, _buf) \ |
437 | 0 | do \ |
438 | 0 | { \ |
439 | 0 | (_pix) = (_buf)[0]; \ |
440 | 0 | (_buf) += 1; \ |
441 | 0 | } while (0) |
442 | | |
443 | 0 | #define WRITEFGBGIMAGE WriteFgBgImage8to8 |
444 | 0 | #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage8to8 |
445 | | #define RLEDECOMPRESS RleDecompress8to8 |
446 | | #define RLEEXTRA |
447 | | #undef ENSURE_CAPACITY |
448 | 0 | #define ENSURE_CAPACITY(_start, _end, _size) ensure_capacity(_start, _end, _size, 1) |
449 | | #include "include/bitmap.h" |
450 | | |
451 | | #undef DESTWRITEPIXEL |
452 | | #undef DESTREADPIXEL |
453 | | #undef SRCREADPIXEL |
454 | | #undef WRITEFGBGIMAGE |
455 | | #undef WRITEFIRSTLINEFGBGIMAGE |
456 | | #undef RLEDECOMPRESS |
457 | | #undef RLEEXTRA |
458 | | #undef WHITE_PIXEL |
459 | | #undef PIXEL_SIZE |
460 | | #undef PIXEL |
461 | | #define PIXEL_SIZE 2 |
462 | 120k | #define PIXEL UINT16 |
463 | 11.7k | #define WHITE_PIXEL 0xFFFF |
464 | | #define DESTWRITEPIXEL(_buf, _pix) \ |
465 | 224k | do \ |
466 | 224k | { \ |
467 | 224k | write_pixel_16(_buf, _pix); \ |
468 | 224k | (_buf) += 2; \ |
469 | 224k | } while (0) |
470 | 202k | #define DESTREADPIXEL(_pix, _buf) _pix = ((UINT16*)(_buf))[0] |
471 | | #define SRCREADPIXEL(_pix, _buf) \ |
472 | 49.5k | do \ |
473 | 49.5k | { \ |
474 | 49.5k | (_pix) = WINPR_ASSERTING_INT_CAST(UINT16, (_buf)[0] | (((_buf)[1] << 8) & 0xFF00)); \ |
475 | 49.5k | (_buf) += 2; \ |
476 | 49.5k | } while (0) |
477 | 73.1k | #define WRITEFGBGIMAGE WriteFgBgImage16to16 |
478 | 35.7k | #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage16to16 |
479 | | #define RLEDECOMPRESS RleDecompress16to16 |
480 | | #define RLEEXTRA |
481 | | #undef ENSURE_CAPACITY |
482 | 657k | #define ENSURE_CAPACITY(_start, _end, _size) ensure_capacity(_start, _end, _size, 2) |
483 | | #include "include/bitmap.h" |
484 | | |
485 | | #undef DESTWRITEPIXEL |
486 | | #undef DESTREADPIXEL |
487 | | #undef SRCREADPIXEL |
488 | | #undef WRITEFGBGIMAGE |
489 | | #undef WRITEFIRSTLINEFGBGIMAGE |
490 | | #undef RLEDECOMPRESS |
491 | | #undef RLEEXTRA |
492 | | #undef WHITE_PIXEL |
493 | | #undef PIXEL_SIZE |
494 | | #undef PIXEL |
495 | | #define PIXEL_SIZE 3 |
496 | 48.4k | #define PIXEL UINT32 |
497 | 5.89k | #define WHITE_PIXEL 0xffffff |
498 | | #define DESTWRITEPIXEL(_buf, _pix) \ |
499 | 109k | do \ |
500 | 109k | { \ |
501 | 109k | write_pixel_24(_buf, _pix); \ |
502 | 109k | (_buf) += 3; \ |
503 | 109k | } while (0) |
504 | | #define DESTREADPIXEL(_pix, _buf) \ |
505 | 98.5k | _pix = (_buf)[0] | (((_buf)[1] << 8) & 0xFF00) | (((_buf)[2] << 16) & 0xFF0000) |
506 | | #define SRCREADPIXEL(_pix, _buf) \ |
507 | 23.2k | do \ |
508 | 23.2k | { \ |
509 | 23.2k | (_pix) = (_buf)[0] | (((_buf)[1] << 8) & 0xFF00) | (((_buf)[2] << 16) & 0xFF0000); \ |
510 | 23.2k | (_buf) += 3; \ |
511 | 23.2k | } while (0) |
512 | | |
513 | 24.8k | #define WRITEFGBGIMAGE WriteFgBgImage24to24 |
514 | 20.6k | #define WRITEFIRSTLINEFGBGIMAGE WriteFirstLineFgBgImage24to24 |
515 | | #define RLEDECOMPRESS RleDecompress24to24 |
516 | | #define RLEEXTRA |
517 | | #undef ENSURE_CAPACITY |
518 | 310k | #define ENSURE_CAPACITY(_start, _end, _size) ensure_capacity(_start, _end, _size, 3) |
519 | | #include "include/bitmap.h" |
520 | | |
521 | | struct S_BITMAP_INTERLEAVED_CONTEXT |
522 | | { |
523 | | BOOL Compressor; |
524 | | |
525 | | UINT32 TempSize; |
526 | | BYTE* TempBuffer; |
527 | | |
528 | | wStream* bts; |
529 | | }; |
530 | | |
531 | | BOOL interleaved_decompress(BITMAP_INTERLEAVED_CONTEXT* WINPR_RESTRICT interleaved, |
532 | | const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize, UINT32 nSrcWidth, |
533 | | UINT32 nSrcHeight, UINT32 bpp, BYTE* WINPR_RESTRICT pDstData, |
534 | | UINT32 DstFormat, UINT32 nDstStep, UINT32 nXDst, UINT32 nYDst, |
535 | | UINT32 nDstWidth, UINT32 nDstHeight, |
536 | | const gdiPalette* WINPR_RESTRICT palette) |
537 | 17.6k | { |
538 | 17.6k | UINT32 scanline = 0; |
539 | 17.6k | UINT32 SrcFormat = 0; |
540 | | |
541 | 17.6k | if (!interleaved || !pSrcData || !pDstData) |
542 | 0 | { |
543 | 0 | WLog_ERR(TAG, "invalid arguments: interleaved=%p, pSrcData=%p, pDstData=%p", |
544 | 0 | WINPR_CXX_COMPAT_CAST(const void*, interleaved), |
545 | 0 | WINPR_CXX_COMPAT_CAST(const void*, pSrcData), |
546 | 0 | WINPR_CXX_COMPAT_CAST(const void*, pDstData)); |
547 | 0 | return FALSE; |
548 | 0 | } |
549 | | |
550 | 17.6k | if ((nSrcWidth == 0) || (nSrcHeight == 0)) |
551 | 0 | return FALSE; |
552 | 17.6k | if ((nDstWidth == 0) || (nDstHeight == 0)) |
553 | 0 | return FALSE; |
554 | | |
555 | 17.6k | switch (bpp) |
556 | 17.6k | { |
557 | 5.89k | case 24: |
558 | 5.89k | if (nSrcWidth > UINT32_MAX / 3) |
559 | 0 | return FALSE; |
560 | 5.89k | scanline = nSrcWidth * 3; |
561 | 5.89k | SrcFormat = PIXEL_FORMAT_BGR24; |
562 | 5.89k | break; |
563 | | |
564 | 5.89k | case 16: |
565 | 5.89k | if (nSrcWidth > UINT32_MAX / 2) |
566 | 0 | return FALSE; |
567 | 5.89k | scanline = nSrcWidth * 2; |
568 | 5.89k | SrcFormat = PIXEL_FORMAT_RGB16; |
569 | 5.89k | break; |
570 | | |
571 | 5.89k | case 15: |
572 | 5.89k | if (nSrcWidth > UINT32_MAX / 2) |
573 | 0 | return FALSE; |
574 | 5.89k | scanline = nSrcWidth * 2; |
575 | 5.89k | SrcFormat = PIXEL_FORMAT_RGB15; |
576 | 5.89k | break; |
577 | | |
578 | 0 | case 8: |
579 | 0 | scanline = nSrcWidth; |
580 | 0 | SrcFormat = PIXEL_FORMAT_RGB8; |
581 | 0 | break; |
582 | | |
583 | 0 | default: |
584 | 0 | WLog_ERR(TAG, "Invalid color depth %" PRIu32 "", bpp); |
585 | 0 | return FALSE; |
586 | 17.6k | } |
587 | | |
588 | 17.6k | if (scanline > UINT32_MAX / nSrcHeight) |
589 | 0 | return FALSE; |
590 | | |
591 | 17.6k | const UINT32 BufferSize = scanline * nSrcHeight; |
592 | | |
593 | 17.6k | if (BufferSize > interleaved->TempSize) |
594 | 0 | { |
595 | 0 | interleaved->TempBuffer = |
596 | 0 | winpr_aligned_recalloc(interleaved->TempBuffer, BufferSize, sizeof(BYTE), 16); |
597 | 0 | interleaved->TempSize = BufferSize; |
598 | 0 | } |
599 | | |
600 | 17.6k | if (!interleaved->TempBuffer) |
601 | 0 | { |
602 | 0 | WLog_ERR(TAG, "interleaved->TempBuffer=nullptr"); |
603 | 0 | return FALSE; |
604 | 0 | } |
605 | | |
606 | 17.6k | switch (bpp) |
607 | 17.6k | { |
608 | 5.89k | case 24: |
609 | 5.89k | if (!RleDecompress24to24(pSrcData, SrcSize, interleaved->TempBuffer, scanline, |
610 | 5.89k | nSrcWidth, nSrcHeight)) |
611 | 4.42k | { |
612 | 4.42k | WLog_ERR(TAG, "RleDecompress24to24 failed"); |
613 | 4.42k | return FALSE; |
614 | 4.42k | } |
615 | | |
616 | 1.46k | break; |
617 | | |
618 | 5.89k | case 16: |
619 | 11.7k | case 15: |
620 | 11.7k | if (!RleDecompress16to16(pSrcData, SrcSize, interleaved->TempBuffer, scanline, |
621 | 11.7k | nSrcWidth, nSrcHeight)) |
622 | 8.89k | { |
623 | 8.89k | WLog_ERR(TAG, "RleDecompress16to16 failed"); |
624 | 8.89k | return FALSE; |
625 | 8.89k | } |
626 | | |
627 | 2.89k | break; |
628 | | |
629 | 2.89k | case 8: |
630 | 0 | if (!RleDecompress8to8(pSrcData, SrcSize, interleaved->TempBuffer, scanline, nSrcWidth, |
631 | 0 | nSrcHeight)) |
632 | 0 | { |
633 | 0 | WLog_ERR(TAG, "RleDecompress8to8 failed"); |
634 | 0 | return FALSE; |
635 | 0 | } |
636 | | |
637 | 0 | break; |
638 | | |
639 | 0 | default: |
640 | 0 | WLog_ERR(TAG, "Invalid color depth %" PRIu32 "", bpp); |
641 | 0 | return FALSE; |
642 | 17.6k | } |
643 | | |
644 | 4.35k | if (!freerdp_image_copy_no_overlap(pDstData, DstFormat, nDstStep, nXDst, nYDst, nDstWidth, |
645 | 4.35k | nDstHeight, interleaved->TempBuffer, SrcFormat, scanline, 0, |
646 | 4.35k | 0, palette, FREERDP_FLIP_VERTICAL | FREERDP_KEEP_DST_ALPHA)) |
647 | 0 | { |
648 | 0 | WLog_ERR(TAG, "freerdp_image_copy failed"); |
649 | 0 | return FALSE; |
650 | 0 | } |
651 | 4.35k | return TRUE; |
652 | 4.35k | } |
653 | | |
654 | | BOOL interleaved_compress(BITMAP_INTERLEAVED_CONTEXT* WINPR_RESTRICT interleaved, |
655 | | BYTE* WINPR_RESTRICT pDstData, UINT32* WINPR_RESTRICT pDstSize, |
656 | | UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pSrcData, |
657 | | UINT32 SrcFormat, UINT32 nSrcStep, UINT32 nXSrc, UINT32 nYSrc, |
658 | | const gdiPalette* WINPR_RESTRICT palette, UINT32 bpp) |
659 | 0 | { |
660 | 0 | BOOL status = 0; |
661 | 0 | wStream* s = nullptr; |
662 | 0 | UINT32 DstFormat = 0; |
663 | 0 | const UINT32 maxSize = 64 * 64 * 4; |
664 | |
|
665 | 0 | if (!interleaved || !pDstData || !pSrcData) |
666 | 0 | return FALSE; |
667 | | |
668 | 0 | if ((nWidth == 0) || (nHeight == 0)) |
669 | 0 | return FALSE; |
670 | | |
671 | 0 | if (nWidth % 4) |
672 | 0 | { |
673 | 0 | WLog_ERR(TAG, "interleaved_compress: width is not a multiple of 4"); |
674 | 0 | return FALSE; |
675 | 0 | } |
676 | | |
677 | 0 | if ((nWidth > 64) || (nHeight > 64)) |
678 | 0 | { |
679 | 0 | WLog_ERR(TAG, |
680 | 0 | "interleaved_compress: width (%" PRIu32 ") or height (%" PRIu32 |
681 | 0 | ") is greater than 64", |
682 | 0 | nWidth, nHeight); |
683 | 0 | return FALSE; |
684 | 0 | } |
685 | | |
686 | 0 | switch (bpp) |
687 | 0 | { |
688 | 0 | case 24: |
689 | 0 | DstFormat = PIXEL_FORMAT_BGRX32; |
690 | 0 | break; |
691 | | |
692 | 0 | case 16: |
693 | 0 | DstFormat = PIXEL_FORMAT_RGB16; |
694 | 0 | break; |
695 | | |
696 | 0 | case 15: |
697 | 0 | DstFormat = PIXEL_FORMAT_RGB15; |
698 | 0 | break; |
699 | | |
700 | 0 | default: |
701 | 0 | return FALSE; |
702 | 0 | } |
703 | | |
704 | 0 | if (!freerdp_image_copy_no_overlap(interleaved->TempBuffer, DstFormat, 0, 0, 0, nWidth, nHeight, |
705 | 0 | pSrcData, SrcFormat, nSrcStep, nXSrc, nYSrc, palette, |
706 | 0 | FREERDP_KEEP_DST_ALPHA)) |
707 | 0 | return FALSE; |
708 | | |
709 | 0 | s = Stream_New(pDstData, *pDstSize); |
710 | |
|
711 | 0 | if (!s) |
712 | 0 | return FALSE; |
713 | | |
714 | 0 | Stream_ResetPosition(interleaved->bts); |
715 | |
|
716 | 0 | status = (freerdp_bitmap_compress(interleaved->TempBuffer, nWidth, nHeight, s, bpp, maxSize, |
717 | 0 | nHeight - 1, interleaved->bts, 0) >= 0); |
718 | |
|
719 | 0 | Stream_SealLength(s); |
720 | 0 | *pDstSize = (UINT32)Stream_Length(s); |
721 | 0 | Stream_Free(s, FALSE); |
722 | 0 | return status; |
723 | 0 | } |
724 | | |
725 | | BOOL bitmap_interleaved_context_reset(BITMAP_INTERLEAVED_CONTEXT* WINPR_RESTRICT interleaved) |
726 | 17.6k | { |
727 | 17.6k | return (interleaved != nullptr); |
728 | 17.6k | } |
729 | | |
730 | | BITMAP_INTERLEAVED_CONTEXT* bitmap_interleaved_context_new(WINPR_ATTR_UNUSED BOOL Compressor) |
731 | 5.89k | { |
732 | 5.89k | BITMAP_INTERLEAVED_CONTEXT* interleaved = nullptr; |
733 | 5.89k | interleaved = (BITMAP_INTERLEAVED_CONTEXT*)winpr_aligned_recalloc( |
734 | 5.89k | nullptr, 1, sizeof(BITMAP_INTERLEAVED_CONTEXT), 32); |
735 | | |
736 | 5.89k | if (interleaved) |
737 | 5.89k | { |
738 | 5.89k | interleaved->TempSize = 64 * 64 * 4; |
739 | 5.89k | interleaved->TempBuffer = winpr_aligned_calloc(interleaved->TempSize, sizeof(BYTE), 16); |
740 | | |
741 | 5.89k | if (!interleaved->TempBuffer) |
742 | 0 | goto fail; |
743 | | |
744 | 5.89k | interleaved->bts = Stream_New(nullptr, interleaved->TempSize); |
745 | | |
746 | 5.89k | if (!interleaved->bts) |
747 | 0 | goto fail; |
748 | 5.89k | } |
749 | | |
750 | 5.89k | return interleaved; |
751 | | |
752 | 0 | fail: |
753 | 0 | WINPR_PRAGMA_DIAG_PUSH |
754 | 0 | WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC |
755 | 0 | bitmap_interleaved_context_free(interleaved); |
756 | 0 | WINPR_PRAGMA_DIAG_POP |
757 | 0 | return nullptr; |
758 | 5.89k | } |
759 | | |
760 | | void bitmap_interleaved_context_free(BITMAP_INTERLEAVED_CONTEXT* WINPR_RESTRICT interleaved) |
761 | 5.89k | { |
762 | 5.89k | if (!interleaved) |
763 | 0 | return; |
764 | | |
765 | 5.89k | winpr_aligned_free(interleaved->TempBuffer); |
766 | 5.89k | Stream_Free(interleaved->bts, TRUE); |
767 | 5.89k | winpr_aligned_free(interleaved); |
768 | 5.89k | } |