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