1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id$ */
3
4 /* libticalcs - Ti Calculator library, a part of the TiLP project
5 * Copyright (C) 1999-2005 Romain Liévin
6 * Copyright (C) 2015 Lionel Debroux
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "ticalcs.h"
27 #include "gettext.h"
28 #include "internal.h"
29 #include "logging.h"
30 #include "error.h"
31
32 /**
33 * ticalcs_screen_convert_bw_to_rgb888:
34 * @src: source bitmap (1-bit pixels) to be converted.
35 * @width: width of the bitmap in pixels, assumed to be be a multiple of 8.
36 * @height: height of the bitmap in pixels
37 * @dst: storage space for the converted bitmap, must be at least 3 * width * height bytes large
38 *
39 * Convert 1-bit B/W bitmap to RGB888, the output bitmap uses black and white pixels.
40 *
41 * Return value: 0 if successful, an error code otherwise.
42 **/
ticalcs_screen_convert_bw_to_rgb888(const uint8_t * src,unsigned int width,unsigned int height,uint8_t * dst)43 TIEXPORT3 int TICALL ticalcs_screen_convert_bw_to_rgb888(const uint8_t * src, unsigned int width, unsigned int height, uint8_t * dst)
44 {
45 unsigned int i;
46
47 VALIDATE_NONNULL(src);
48 VALIDATE_SCREENWIDTH(width);
49 VALIDATE_SCREENHEIGHT(height);
50 VALIDATE_NONNULL(dst);
51
52 for (i = 0; i < height; i++)
53 {
54 unsigned int j;
55 for (j = 0; j < (width >> 3); j++)
56 {
57 uint8_t data = *src++;
58 uint8_t mask = 0x80;
59 unsigned int bit;
60 for (bit = 0; bit < 8; bit++)
61 {
62 if (data & mask)
63 {
64 *dst++ = 0x00;
65 *dst++ = 0x00;
66 *dst++ = 0x00;
67 }
68 else
69 {
70 *dst++ = 0xFF;
71 *dst++ = 0xFF;
72 *dst++ = 0xFF;
73 }
74 mask >>= 1;
75 }
76 }
77 }
78
79 return 0;
80 }
81
82 /**
83 * ticalcs_screen_convert_bw_to_blurry_rgb888:
84 * @src: source bitmap (1-bit pixels) to be converted.
85 * @width: width of the bitmap in pixels, assumed to be be a multiple of 8.
86 * @height: height of the bitmap in pixels
87 * @dst: storage space for the converted bitmap, must be at least 3 * width * height bytes large
88 *
89 * Convert 1-bit B/W bitmap to RGB888, the output bitmap uses a pair of colors mimicking old TI-Z80 / TI-68k screens.
90 *
91 * Return value: 0 if successful, an error code otherwise.
92 **/
ticalcs_screen_convert_bw_to_blurry_rgb888(const uint8_t * src,unsigned int width,unsigned int height,uint8_t * dst)93 TIEXPORT3 int TICALL ticalcs_screen_convert_bw_to_blurry_rgb888(const uint8_t * src, unsigned int width, unsigned int height, uint8_t * dst)
94 {
95 unsigned int i;
96
97 VALIDATE_NONNULL(src);
98 VALIDATE_SCREENWIDTH(width);
99 VALIDATE_SCREENHEIGHT(height);
100 VALIDATE_NONNULL(dst);
101
102 for (i = 0; i < height; i++)
103 {
104 unsigned int j;
105 for (j = 0; j < (width >> 3); j++)
106 {
107 uint8_t data = *src++;
108 uint8_t mask = 0x80;
109 unsigned int bit;
110 for (bit = 0; bit < 8; bit++)
111 {
112 if (data & mask)
113 {
114 *dst++ = 0x00;
115 *dst++ = 0x00;
116 *dst++ = 0x34;
117 }
118 else
119 {
120 *dst++ = 0xA8;
121 *dst++ = 0xB4;
122 *dst++ = 0xA8;
123 }
124 mask >>= 1;
125 }
126 }
127 }
128
129 return 0;
130 }
131
132 /**
133 * ticalcs_screen_convert_gs4_to_rgb888:
134 * @src: source bitmap (4-bit pixels) to be converted.
135 * @width: width of the bitmap in pixels, assumed to be even.
136 * @height: height of the bitmap in pixels
137 * @dst: storage space for the converted bitmap, must be at least 3 * width * height bytes large
138 *
139 * Convert 4-bit grayscale bitmap to RGB888.
140 *
141 * Return value: 0 if successful, an error code otherwise.
142 **/
ticalcs_screen_convert_gs4_to_rgb888(const uint8_t * src,unsigned int width,unsigned int height,uint8_t * dst)143 TIEXPORT3 int TICALL ticalcs_screen_convert_gs4_to_rgb888(const uint8_t * src, unsigned int width, unsigned int height, uint8_t * dst)
144 {
145 unsigned int i;
146
147 VALIDATE_NONNULL(src);
148 VALIDATE_SCREENWIDTH(width);
149 VALIDATE_SCREENHEIGHT(height);
150 VALIDATE_NONNULL(dst);
151
152 for (i = 0; i < height; i++)
153 {
154 unsigned int j;
155 for (j = 0; j < width / 2; j++)
156 {
157 uint8_t data = *src++;
158 uint8_t hi = data >> 4;
159 uint8_t lo = data & 0x0f;
160
161 *dst++ = hi << 4;
162 *dst++ = hi << 4;
163 *dst++ = hi << 4;
164
165 *dst++ = lo << 4;
166 *dst++ = lo << 4;
167 *dst++ = lo << 4;
168 }
169 }
170
171 return 0;
172 }
173
174 /**
175 * ticalcs_screen_convert_rgb565le_to_rgb888:
176 * @src: source bitmap (16-bit pixels) to be converted.
177 * @width: width of the bitmap in pixels
178 * @height: height of the bitmap in pixels
179 * @dst: storage space for the converted bitmap, must be at least 3 * width * height bytes large
180 *
181 * Convert 16-bit little-endian RGB565 bitmap to RGB888.
182 *
183 * Return value: 0 if successful, an error code otherwise.
184 **/
ticalcs_screen_convert_rgb565le_to_rgb888(const uint8_t * src,unsigned int width,unsigned int height,uint8_t * dst)185 TIEXPORT3 int TICALL ticalcs_screen_convert_rgb565le_to_rgb888(const uint8_t * src, unsigned int width, unsigned int height, uint8_t * dst)
186 {
187 unsigned int i;
188
189 VALIDATE_NONNULL(src);
190 VALIDATE_SCREENWIDTH(width);
191 VALIDATE_SCREENHEIGHT(height);
192 VALIDATE_NONNULL(dst);
193
194 for (i = 0; i < height; i++)
195 {
196 unsigned int j;
197 for (j = 0; j < width; j++)
198 {
199 uint16_t data = (((uint16_t)(src[1])) << 8) | ((uint16_t)(src[0]));
200
201 src += 2;
202 *dst++ = ((data & 0xF800) >> 11) << 3;
203 *dst++ = ((data & 0x07E0) >> 5) << 2;
204 *dst++ = ((data & 0x001F) >> 0) << 3;
205 }
206 }
207
208 return 0;
209 }
210
211 /**
212 * ticalcs_screen_convert_native_to_rgb888:
213 * @format: pixel format of the input data.
214 * @src: source bitmap (16-bit pixels) to be converted.
215 * @width: width of the bitmap in pixels
216 * @height: height of the bitmap in pixels
217 * @dst: storage space for the converted bitmap, must be at least 3 * width * height bytes large
218 *
219 * Convert a bitmap in the calculator's native encoding to RGB888.
220 *
221 * Return value: 0 if successful, an error code otherwise.
222 **/
ticalcs_screen_convert_native_to_rgb888(CalcPixelFormat format,const uint8_t * src,unsigned int width,unsigned int height,uint8_t * dst)223 TIEXPORT3 int TICALL ticalcs_screen_convert_native_to_rgb888(CalcPixelFormat format, const uint8_t * src, unsigned int width, unsigned int height, uint8_t * dst)
224 {
225 int ret;
226
227 VALIDATE_NONNULL(src);
228 // width and height are validated by other ticalcs_screen_convert_* functions.
229 VALIDATE_NONNULL(dst);
230
231 switch (format)
232 {
233 case CALC_PIXFMT_MONO:
234 {
235 ret = ticalcs_screen_convert_bw_to_rgb888(src, width, height, dst);
236 }
237 break;
238
239 case CALC_PIXFMT_GRAY_4:
240 {
241 ret = ticalcs_screen_convert_gs4_to_rgb888(src, width, height, dst);
242 }
243 break;
244
245 case CALC_PIXFMT_RGB_565_LE:
246 {
247 ret = ticalcs_screen_convert_rgb565le_to_rgb888(src, width, height, dst);
248 }
249 break;
250
251 default:
252 {
253 ticalcs_critical(_("Unknown pixel format %d\n"), format);
254 ret = ERR_INVALID_PARAMETER;
255 }
256 }
257
258 return ret;
259 }
260
261 /**
262 * ticalcs_screen_nspire_rle_uncompress:
263 * @format: pixel format of the input data.
264 * @src: source bitmap to be converted.
265 * @input_size: size of the input data.
266 * @dst: storage space for the uncompressed bitmap, must be at least width * height / 2 bytes large for classic Nspire and 2 * width * height bytes large for color Nspire.
267 * @max_output_size: size of the output data.
268 *
269 * Uncompress a RLE-compressed Nspire screenshot.
270 *
271 * Return value: 0 if successful, an error code otherwise.
272 */
ticalcs_screen_nspire_rle_uncompress(CalcPixelFormat format,const uint8_t * src,uint32_t input_size,uint8_t * dst,uint32_t max_output_size)273 TIEXPORT3 int TICALL ticalcs_screen_nspire_rle_uncompress(CalcPixelFormat format, const uint8_t *src, uint32_t input_size, uint8_t * dst, uint32_t max_output_size)
274 {
275 int ret;
276
277 VALIDATE_NONNULL(src);
278 VALIDATE_NONNULL(dst);
279
280 switch (format)
281 {
282 case CALC_PIXFMT_GRAY_4:
283 {
284 uint8_t *q;
285 uint32_t i, j;
286
287 for (i = 0, j = 0, q = dst; i < input_size;)
288 {
289 int8_t rec = src[i++];
290
291 if (rec >= 0)
292 {
293 // Positive count: "repeat 8-bit value" block.
294 uint8_t cnt = ((uint8_t)rec) + 1;
295 uint8_t val = src[i++];
296
297 if (j + cnt > max_output_size)
298 {
299 ret = ERR_INVALID_SCREENSHOT;
300 break;
301 }
302 memset(q, val, cnt);
303 q += cnt;
304 j += cnt;
305 }
306 else
307 {
308 // Negative count: "verbatim" block of 8-bit values.
309 uint8_t cnt = ((uint8_t)-rec) + 1;
310
311 if (j + cnt > max_output_size)
312 {
313 ret = ERR_INVALID_SCREENSHOT;
314 break;
315 }
316 memcpy(q, src+i, cnt);
317 q += cnt;
318 i += cnt;
319 j += cnt;
320 }
321 }
322
323 ret = 0;
324 }
325 break;
326
327 case CALC_PIXFMT_RGB_565_LE:
328 {
329 uint8_t *q;
330 uint32_t i, j;
331
332 for (i = 0, j = 0, q = dst; i < input_size;)
333 {
334 int8_t rec = src[i++];
335
336 if (rec >= 0)
337 {
338 // Positive count: "repeat 32-bit value" block.
339 uint8_t cnt = ((uint8_t)rec) + 1;
340 uint32_t val;
341 uint8_t k;
342
343 if (j + cnt * 4 > max_output_size)
344 {
345 ret = ERR_INVALID_SCREENSHOT;
346 break;
347 }
348 memcpy(&val, src + i, sizeof(uint32_t));
349 for (k = 0; k < cnt; k++)
350 {
351 memcpy(q, &val, 4);
352 q += 4;
353 }
354 i += 4;
355 j += cnt * 4;
356 }
357 else
358 {
359 // Negative count: "verbatim" block of 32-bit values.
360 uint8_t cnt = ((uint8_t)-rec) + 1;
361
362 if (j + cnt * 4 > max_output_size)
363 {
364 ret = ERR_INVALID_SCREENSHOT;
365 break;
366 }
367 memcpy(q, src + i, cnt * 4);
368 q += cnt * 4;
369 i += cnt * 4;
370 j += cnt * 4;
371 }
372 }
373
374 ret = 0;
375 }
376 break;
377
378 default:
379 {
380 ticalcs_critical(_("Unknown pixel format %d\n"), format);
381 ret = ERR_INVALID_PARAMETER;
382 }
383 }
384
385 return ret;
386 }
387
388 /**
389 * ticalcs_screen_84pcse_rle_uncompress:
390 * @src: source bitmap to be converted.
391 * @src_length: size of the input data.
392 * @dst: storage space for the uncompressed bitmap, must be at least width * height / 2 bytes large for classic Nspire and 2 * width * height bytes large for color Nspire.
393 * @dst_length: size of the output data.
394 *
395 * Uncompress a RLE-compressed 84+CSE screenshot.
396 *
397 * Return value: 0 if successful, an error code otherwise.
398 */
ticalcs_screen_84pcse_rle_uncompress(const uint8_t * src,uint32_t src_length,uint8_t * dst,uint32_t dst_length)399 TIEXPORT3 int TICALL ticalcs_screen_84pcse_rle_uncompress(const uint8_t *src, uint32_t src_length, uint8_t *dst, uint32_t dst_length)
400 {
401 const uint8_t *palette;
402 unsigned int palette_size, i, c, n;
403
404 VALIDATE_NONNULL(src);
405 VALIDATE_NONNULL(dst);
406
407 if (src[0] != 1)
408 {
409 return ERR_INVALID_SCREENSHOT;
410 }
411 src++;
412 src_length--;
413
414 palette_size = src[src_length - 1];
415 if (src_length <= palette_size * 2 + 1)
416 {
417 return ERR_INVALID_SCREENSHOT;
418 }
419
420 src_length -= palette_size * 2 + 1;
421 palette = src + src_length - 2;
422
423 while (src_length > 0)
424 {
425 if ((src[0] & 0xf0) != 0)
426 {
427 for (i = 0; i < 2; i ++)
428 {
429 c = (i == 0 ? src[0] >> 4 : src[0] & 0x0f);
430 if (c == 0)
431 {
432 break;
433 }
434
435 if (c > palette_size)
436 {
437 return ERR_INVALID_SCREENSHOT;
438 }
439
440 if (dst_length < 2)
441 {
442 return ERR_INVALID_SCREENSHOT;
443 }
444
445 dst[0] = palette[2 * c];
446 dst[1] = palette[2 * c + 1];
447 dst += 2;
448 dst_length -= 2;
449 }
450 src++;
451 src_length--;
452 }
453 else if (src_length >= 2 && (src[0] & 0x0f) != 0)
454 {
455 c = src[0];
456 n = src[1];
457
458 if (c > palette_size)
459 {
460 return ERR_INVALID_SCREENSHOT;
461 }
462
463 if (dst_length < 2 * n)
464 {
465 return ERR_INVALID_SCREENSHOT;
466 }
467
468 for (i = 0; i < n; i++)
469 {
470 dst[0] = palette[2 * c];
471 dst[1] = palette[2 * c + 1];
472 dst += 2;
473 dst_length -= 2;
474 }
475
476 src += 2;
477 src_length -= 2;
478 }
479 else if (src_length >= 2 && src[0] == 0 && src[1] == 0)
480 {
481 src += 2;
482 src_length -= 2;
483 goto byte_mode;
484 }
485 else
486 {
487 return ERR_INVALID_SCREENSHOT;
488 }
489 }
490 goto finish;
491
492 byte_mode:
493 while (src_length > 0)
494 {
495 if (src[0] != 0)
496 {
497 c = src[0];
498
499 if (c > palette_size)
500 {
501 return ERR_INVALID_SCREENSHOT;
502 }
503
504 if (dst_length < 2)
505 {
506 return ERR_INVALID_SCREENSHOT;
507 }
508
509 dst[0] = palette[2 * c];
510 dst[1] = palette[2 * c + 1];
511 dst += 2;
512 dst_length -= 2;
513
514 src++;
515 src_length--;
516 }
517 else if (src_length >= 3 && src[1] != 0)
518 {
519 c = src[1];
520 n = src[2];
521
522 if (c > palette_size)
523 {
524 return ERR_INVALID_SCREENSHOT;
525 }
526
527 if (dst_length < 2 * n)
528 {
529 return ERR_INVALID_SCREENSHOT;
530 }
531
532 for (i = 0; i < n; i++)
533 {
534 dst[0] = palette[2 * c];
535 dst[1] = palette[2 * c + 1];
536 dst += 2;
537 dst_length -= 2;
538 }
539
540 src += 3;
541 src_length -= 3;
542 }
543 else if (src_length >= 3 && src[0] == 0 && src[1] == 0 && src[2] == 0)
544 {
545 src += 3;
546 src_length -= 3;
547 goto word_mode;
548 }
549 else
550 {
551 return ERR_INVALID_SCREENSHOT;
552 }
553 }
554 goto finish;
555
556 word_mode:
557 while (src_length > 0)
558 {
559 if (src_length < 2)
560 {
561 return ERR_INVALID_SCREENSHOT;
562 }
563
564 if (src[0] != 0x01 || src[1] != 0x00)
565 {
566 if (dst_length < 2)
567 {
568 return ERR_INVALID_SCREENSHOT;
569 }
570
571 dst[0] = src[0];
572 dst[1] = src[1];
573 dst += 2;
574 dst_length -= 2;
575 src += 2;
576 src_length -= 2;
577 }
578 else
579 {
580 if (src_length < 5)
581 {
582 return ERR_INVALID_SCREENSHOT;
583 }
584
585 n = src[4];
586
587 if (dst_length < 2 * n)
588 {
589 return ERR_INVALID_SCREENSHOT;
590 }
591
592 for (i = 0; i < n; i++)
593 {
594 dst[0] = src[2];
595 dst[1] = src[3];
596 dst += 2;
597 dst_length -= 2;
598 }
599
600 src += 5;
601 src_length -= 5;
602 }
603 }
604
605 finish:
606 if (src_length != 0 || dst_length != 0)
607 {
608 return ERR_INVALID_SCREENSHOT;
609 }
610 else
611 {
612 return 0;
613 }
614 }
615