git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / formats / bmp / rbmp.c
CommitLineData
3719602c
PC
1/* Copyright (C) 2010-2020 The RetroArch team
2 *
3 * ---------------------------------------------------------------------------------------
4 * The following license statement only applies to this file (rbmp.c).
5 * ---------------------------------------------------------------------------------------
6 *
7 * Permission is hereby granted, free of charge,
8 * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23/* Modified version of stb_image's BMP sources. */
24
25#include <stdio.h>
26#include <stdint.h>
27#include <stdarg.h>
28#include <stddef.h> /* ptrdiff_t on osx */
29#include <stdlib.h>
30#include <string.h>
31
32#include <retro_inline.h>
33
34#include <formats/image.h>
35#include <formats/rbmp.h>
36
37/* truncate int to byte without warnings */
38#define RBMP_BYTECAST(x) ((unsigned char) ((x) & 255))
39
40#define RBMP_COMPUTE_Y(r, g, b) ((unsigned char) ((((r) * 77) + ((g) * 150) + (29 * (b))) >> 8))
41
42typedef struct
43{
44 unsigned char *img_buffer;
45 unsigned char *img_buffer_end;
46 unsigned char *img_buffer_original;
47 int img_n;
48 int img_out_n;
49 int buflen;
50 uint32_t img_x;
51 uint32_t img_y;
52 unsigned char buffer_start[128];
53} rbmp_context;
54
55struct rbmp
56{
57 uint8_t *buff_data;
58 uint32_t *output_image;
59};
60
61static INLINE unsigned char rbmp_get8(rbmp_context *s)
62{
63 if (s->img_buffer < s->img_buffer_end)
64 return *s->img_buffer++;
65
66 return 0;
67}
68
69static void rbmp_skip(rbmp_context *s, int n)
70{
71 if (n < 0)
72 {
73 s->img_buffer = s->img_buffer_end;
74 return;
75 }
76
77 s->img_buffer += n;
78}
79
80static int rbmp_get16le(rbmp_context *s)
81{
82 return rbmp_get8(s) + (rbmp_get8(s) << 8);
83}
84
85#define RBMP_GET32LE(s) (rbmp_get16le(s) + (rbmp_get16le(s) << 16))
86
87static unsigned char *rbmp_convert_format(
88 unsigned char *data,
89 int img_n,
90 int req_comp,
91 unsigned int x,
92 unsigned int y)
93{
94 int i,j;
95 unsigned char *good = (unsigned char *)malloc(req_comp * x * y);
96
97 if (!good)
98 return NULL;
99
100 for (j=0; j < (int) y; ++j)
101 {
102 unsigned char *src = data + j * x * img_n ;
103 unsigned char *dest = good + j * x * req_comp;
104
105 switch (((img_n)*8+(req_comp)))
106 {
107 case 10:
108 for (i = x-1; i >= 0; --i, src += 1, dest += 2)
109 {
110 dest[0]=src[0];
111 dest[1]=255;
112 }
113 break;
114 case 11:
115 for (i = x-1; i >= 0; --i, src += 1, dest += 3)
116 dest[0]=dest[1]=dest[2]=src[0];
117 break;
118 case 12:
119 for (i = x-1; i >= 0; --i, src += 1, dest += 4)
120 {
121 dest[0]=dest[1]=dest[2]=src[0];
122 dest[3]=255;
123 }
124 break;
125 case 17:
126 for (i = x-1; i >= 0; --i, src += 2, dest += 1)
127 dest[0]=src[0];
128 break;
129 case 19:
130 for (i = x-1; i >= 0; --i, src += 2, dest += 3)
131 dest[0]=dest[1]=dest[2]=src[0];
132 break;
133 case 20:
134 for (i = x-1; i >= 0; --i, src += 2, dest += 4)
135 {
136 dest[0]=dest[1]=dest[2]=src[0];
137 dest[3]=src[1];
138 }
139 break;
140 case 28:
141 for (i = x-1; i >= 0; --i, src += 3, dest += 4)
142 {
143 dest[0]=src[0];
144 dest[1]=src[1];
145 dest[2]=src[2];
146 dest[3]=255;
147 }
148 break;
149 case 25:
150 for (i = x-1; i >= 0; --i, src += 3, dest += 1)
151 dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
152 break;
153 case 26:
154 for (i = x-1; i >= 0; --i, src += 3, dest += 2)
155 {
156 dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
157 dest[1] = 255;
158 }
159 break;
160 case 33:
161 for (i = x-1; i >= 0; --i, src += 4, dest += 1)
162 dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
163 break;
164 case 34:
165 for (i = x-1; i >= 0; --i, src += 4, dest += 2)
166 {
167 dest[0] = RBMP_COMPUTE_Y(src[0],src[1],src[2]);
168 dest[1] = src[3];
169 }
170 break;
171 case 35:
172 for (i = x-1; i >= 0; --i, src += 4, dest += 3)
173 {
174 dest[0]=src[0];
175 dest[1]=src[1];
176 dest[2]=src[2];
177 }
178 break;
179 default:
180 break;
181 }
182
183 }
184
185 return good;
186}
187
188/* Microsoft/Windows BMP image */
189
190/* returns 0..31 for the highest set bit */
191static int rbmp_high_bit(unsigned int z)
192{
193 int n=0;
194 if (z == 0)
195 return -1;
196
197 if (z >= 0x10000)
198 {
199 n += 16;
200 z >>= 16;
201 }
202 if (z >= 0x00100)
203 {
204 n += 8;
205 z >>= 8;
206 }
207 if (z >= 0x00010)
208 {
209 n += 4;
210 z >>= 4;
211 }
212 if (z >= 0x00004)
213 {
214 n += 2;
215 z >>= 2;
216 }
217 if (z >= 0x00002)
218 n += 1;
219 return n;
220}
221
222static int rbmp_bitcount(unsigned int a)
223{
224 a = (a & 0x55555555) + ((a >> 1) & 0x55555555); /* max 2 */
225 a = (a & 0x33333333) + ((a >> 2) & 0x33333333); /* max 4 */
226 a = (a + (a >> 4)) & 0x0f0f0f0f; /* max 8 per 4, now 8 bits */
227 a = (a + (a >> 8)); /* max 16 per 8 bits */
228 a = (a + (a >> 16)); /* max 32 per 8 bits */
229 return a & 0xff;
230}
231
232static int rbmp_shiftsigned(int v, int shift, int bits)
233{
234 int result;
235 int z = bits;
236
237 if (shift < 0)
238 v <<= -shift;
239 else
240 v >>= shift;
241
242 result = v;
243
244 while (z < 8)
245 {
246 result += v >> z;
247 z += bits;
248 }
249 return result;
250}
251
252static unsigned char *rbmp_bmp_load(rbmp_context *s, unsigned *x, unsigned *y,
253 int *comp, int req_comp)
254{
255 unsigned char *out;
256 int bpp, flip_vertically, pad, target, offset, hsz;
257 int psize=0,i,j,width;
258 unsigned int mr=0,mg=0,mb=0,ma=0;
259
260 /* Corrupt BMP? */
261 if (rbmp_get8(s) != 'B' || rbmp_get8(s) != 'M')
262 return 0;
263
264 /* discard filesize */
265 rbmp_get16le(s);
266 rbmp_get16le(s);
267 /* discard reserved */
268 rbmp_get16le(s);
269 rbmp_get16le(s);
270
271 offset = (uint32_t)RBMP_GET32LE(s);
272 hsz = (uint32_t)RBMP_GET32LE(s);
273
274 /* BMP type not supported? */
275 if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124)
276 return 0;
277
278 if (hsz == 12)
279 {
280 s->img_x = rbmp_get16le(s);
281 s->img_y = rbmp_get16le(s);
282 }
283 else
284 {
285 s->img_x = (uint32_t)RBMP_GET32LE(s);
286 s->img_y = (uint32_t)RBMP_GET32LE(s);
287 }
288
289 /* Bad BMP? */
290 if (rbmp_get16le(s) != 1)
291 return 0;
292
293 bpp = rbmp_get16le(s);
294
295 /* BMP 1-bit type not supported? */
296 if (bpp == 1)
297 return 0;
298
299 flip_vertically = ((int) s->img_y) > 0;
300 s->img_y = abs((int) s->img_y);
301
302 if (hsz == 12)
303 {
304 if (bpp < 24)
305 psize = (offset - 14 - 24) / 3;
306 }
307 else
308 {
309 int compress = (uint32_t)RBMP_GET32LE(s);
310
311 /* BMP RLE type not supported? */
312 if (compress == 1 || compress == 2)
313 return 0;
314
315 /* discard sizeof */
316 rbmp_get16le(s);
317 rbmp_get16le(s);
318 /* discard hres */
319 rbmp_get16le(s);
320 rbmp_get16le(s);
321 /* discard vres */
322 rbmp_get16le(s);
323 rbmp_get16le(s);
324 /* discard colors used */
325 rbmp_get16le(s);
326 rbmp_get16le(s);
327 /* discard max important */
328 rbmp_get16le(s);
329 rbmp_get16le(s);
330
331 if (hsz == 40 || hsz == 56)
332 {
333 if (hsz == 56)
334 {
335 rbmp_get16le(s);
336 rbmp_get16le(s);
337 rbmp_get16le(s);
338 rbmp_get16le(s);
339 rbmp_get16le(s);
340 rbmp_get16le(s);
341 rbmp_get16le(s);
342 rbmp_get16le(s);
343 }
344 if (bpp == 16 || bpp == 32)
345 {
346 switch (compress)
347 {
348 case 0:
349#if 0
350 if (bpp == 32)
351 {
352 mr = 0xffu << 16;
353 mg = 0xffu << 8;
354 mb = 0xffu << 0;
355 ma = 0xffu << 24;
356 }
357 else
358 {
359 mr = 31u << 10;
360 mg = 31u << 5;
361 mb = 31u << 0;
362 }
363#endif
364 break;
365 case 3:
366 mr = (uint32_t)RBMP_GET32LE(s);
367 mg = (uint32_t)RBMP_GET32LE(s);
368 mb = (uint32_t)RBMP_GET32LE(s);
369 /* not documented, but generated by
370 * Photoshop and handled by MS Paint */
371 /* Bad BMP ?*/
372 if (mr == mg && mg == mb)
373 return 0;
374 break;
375 default:
376#if 0
377 mr = mg = mb = 0;
378#endif
379 break;
380 }
381
382 /* Bad BMP? */
383 return 0;
384 }
385 }
386 else
387 {
388 mr = (uint32_t)RBMP_GET32LE(s);
389 mg = (uint32_t)RBMP_GET32LE(s);
390 mb = (uint32_t)RBMP_GET32LE(s);
391 ma = (uint32_t)RBMP_GET32LE(s);
392 /* Discard color space */
393 rbmp_get16le(s);
394 rbmp_get16le(s);
395 for (i = 0; i < 12; ++i)
396 {
397 /* Discard color space parameters */
398 rbmp_get16le(s);
399 rbmp_get16le(s);
400 }
401 if (hsz == 124)
402 {
403 /* Discard rendering intent */
404 rbmp_get16le(s);
405 rbmp_get16le(s);
406 /* Discard offset of profile data */
407 rbmp_get16le(s);
408 rbmp_get16le(s);
409 /* Discard size of profile data */
410 rbmp_get16le(s);
411 rbmp_get16le(s);
412 /* Discard reserved */
413 rbmp_get16le(s);
414 rbmp_get16le(s);
415 }
416 }
417 if (bpp < 16)
418 psize = (offset - 14 - hsz) >> 2;
419 }
420 s->img_n = ma ? 4 : 3;
421 if (req_comp && req_comp >= 3) /* We can directly decode 3 or 4 */
422 target = req_comp;
423 else
424 target = s->img_n; /* If they want monochrome, we'll post-convert */
425
426 out = (unsigned char *) malloc(target * s->img_x * s->img_y);
427
428 if (!out)
429 return 0;
430
431 if (bpp < 16)
432 {
433 unsigned char pal[256][4];
434 int z=0;
435
436 /* Corrupt BMP? */
437 if (psize == 0 || psize > 256)
438 {
439 free(out);
440 return 0;
441 }
442
443 for (i = 0; i < psize; ++i)
444 {
445 pal[i][2] = rbmp_get8(s);
446 pal[i][1] = rbmp_get8(s);
447 pal[i][0] = rbmp_get8(s);
448 if (hsz != 12)
449 rbmp_get8(s);
450 pal[i][3] = 255;
451 }
452
453 rbmp_skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4));
454 if (bpp == 4)
455 width = (s->img_x + 1) >> 1;
456 else if (bpp == 8)
457 width = s->img_x;
458 else
459 {
460 /* Corrupt BMP */
461 free(out);
462 return 0;
463 }
464
465 pad = (-width)&3;
466 for (j=0; j < (int) s->img_y; ++j)
467 {
468 for (i = 0; i < (int) s->img_x; i += 2)
469 {
470 int v = rbmp_get8(s);
471 int v2 = 0;
472 if (bpp == 4)
473 {
474 v2 = v & 15;
475 v >>= 4;
476 }
477 out[z++] = pal[v][0];
478 out[z++] = pal[v][1];
479 out[z++] = pal[v][2];
480 if (target == 4)
481 out[z++] = 255;
482
483 if (i+1 == (int)s->img_x)
484 break;
485
486 v = (bpp == 8) ? rbmp_get8(s) : v2;
487 out[z++] = pal[v][0];
488 out[z++] = pal[v][1];
489 out[z++] = pal[v][2];
490
491 if (target == 4)
492 out[z++] = 255;
493 }
494 rbmp_skip(s, pad);
495 }
496 }
497 else
498 {
499 int rshift = 0;
500 int gshift = 0;
501 int bshift = 0;
502 int ashift = 0;
503 int rcount = 0;
504 int gcount = 0;
505 int bcount = 0;
506 int acount = 0;
507 int z = 0;
508 int easy = 0;
509
510 rbmp_skip(s, offset - 14 - hsz);
511
512 if (bpp == 24)
513 width = 3 * s->img_x;
514 else if (bpp == 16)
515 width = 2*s->img_x;
516 else /* bpp = 32 and pad = 0 */
517 width=0;
518
519 pad = (-width) & 3;
520
521 switch (bpp)
522 {
523 case 24:
524 easy = 1;
525 break;
526 case 32:
527 if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000)
528 easy = 2;
529 break;
530 default:
531 break;
532 }
533
534 if (!easy)
535 {
536 /* Corrupt BMP? */
537 if (!mr || !mg || !mb)
538 {
539 free(out);
540 return 0;
541 }
542
543 /* right shift amt to put high bit in position #7 */
544 rshift = rbmp_high_bit(mr)-7;
545 rcount = rbmp_bitcount(mr);
546 gshift = rbmp_high_bit(mg)-7;
547 gcount = rbmp_bitcount(mg);
548 bshift = rbmp_high_bit(mb)-7;
549 bcount = rbmp_bitcount(mb);
550 ashift = rbmp_high_bit(ma)-7;
551 acount = rbmp_bitcount(ma);
552 }
553
554 for (j=0; j < (int) s->img_y; ++j)
555 {
556 if (easy)
557 {
558 if (target == 4)
559 {
560 /* Need to apply alpha channel as well */
561 if (easy == 2)
562 {
563 for (i = 0; i < (int) s->img_x; ++i)
564 {
565 out[z+2] = rbmp_get8(s);
566 out[z+1] = rbmp_get8(s);
567 out[z+0] = rbmp_get8(s);
568 z += 3;
569 out[z++] = rbmp_get8(s);
570 }
571 }
572 else
573 {
574 for (i = 0; i < (int) s->img_x; ++i)
575 {
576 out[z+2] = rbmp_get8(s);
577 out[z+1] = rbmp_get8(s);
578 out[z+0] = rbmp_get8(s);
579 z += 3;
580 out[z++] = 255;
581 }
582 }
583 }
584 else
585 {
586 for (i = 0; i < (int) s->img_x; ++i)
587 {
588 out[z+2] = rbmp_get8(s);
589 out[z+1] = rbmp_get8(s);
590 out[z+0] = rbmp_get8(s);
591 z += 3;
592 }
593 }
594 }
595 else
596 {
597 if (target == 4)
598 {
599 /* Need to apply alpha channel as well */
600 if (ma)
601 {
602 if (bpp == 16)
603 {
604 for (i = 0; i < (int) s->img_x; ++i)
605 {
606 uint32_t v = (uint32_t)rbmp_get16le(s);
607 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
608 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
609 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
610 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & ma, ashift, acount));
611 }
612 }
613 else
614 {
615 for (i = 0; i < (int) s->img_x; ++i)
616 {
617 uint32_t v = (uint32_t)RBMP_GET32LE(s);
618 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
619 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
620 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
621 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & ma, ashift, acount));
622 }
623 }
624 }
625 else
626 {
627 if (bpp == 16)
628 {
629 for (i = 0; i < (int) s->img_x; ++i)
630 {
631 uint32_t v = (uint32_t)rbmp_get16le(s);
632 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
633 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
634 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
635 out[z++] = RBMP_BYTECAST(255);
636 }
637 }
638 else
639 {
640 for (i = 0; i < (int) s->img_x; ++i)
641 {
642 uint32_t v = (uint32_t)RBMP_GET32LE(s);
643 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
644 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
645 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
646 out[z++] = RBMP_BYTECAST(255);
647 }
648 }
649 }
650 }
651 else
652 {
653 if (bpp == 16)
654 {
655 for (i = 0; i < (int) s->img_x; ++i)
656 {
657 uint32_t v = (uint32_t)rbmp_get16le(s);
658 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
659 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
660 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
661 }
662 }
663 else
664 {
665 for (i = 0; i < (int) s->img_x; ++i)
666 {
667 uint32_t v = (uint32_t)RBMP_GET32LE(s);
668 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mr, rshift, rcount));
669 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mg, gshift, gcount));
670 out[z++] = RBMP_BYTECAST(rbmp_shiftsigned(v & mb, bshift, bcount));
671 }
672 }
673 }
674 }
675 rbmp_skip(s, pad);
676 }
677 }
678
679 if (flip_vertically)
680 {
681 unsigned char t;
682 for (j=0; j < (int) s->img_y>>1; ++j)
683 {
684 unsigned char *p1 = out + j *s->img_x*target;
685 unsigned char *p2 = out + (s->img_y-1-j)*s->img_x*target;
686 for (i = 0; i < (int) s->img_x*target; ++i)
687 {
688 t = p1[i];
689 p1[i] = p2[i];
690 p2[i] = t;
691 }
692 }
693 }
694
695 if (
696 req_comp
697 && (req_comp >= 1 && req_comp <= 4)
698 && (req_comp != target))
699 {
700 unsigned char *tmp = rbmp_convert_format(out, target, req_comp, s->img_x, s->img_y);
701
702 free(out);
703 out = NULL;
704
705 if (!tmp)
706 return NULL;
707
708 out = tmp;
709 }
710
711 *x = s->img_x;
712 *y = s->img_y;
713
714 if (comp)
715 *comp = s->img_n;
716
717 return out;
718}
719
720static unsigned char *rbmp_load_from_memory(unsigned char const *buffer, int len,
721 unsigned *x, unsigned *y, int *comp, int req_comp)
722{
723 rbmp_context s;
724
725 s.img_buffer = (unsigned char*)buffer;
726 s.img_buffer_original = (unsigned char*)buffer;
727 s.img_buffer_end = (unsigned char*)buffer+len;
728
729 return rbmp_bmp_load(&s,x,y,comp,req_comp);
730}
731
732static void rbmp_convert_frame(uint32_t *frame, unsigned width, unsigned height)
733{
734 uint32_t *end = frame + (width * height * sizeof(uint32_t))/4;
735
736 while (frame < end)
737 {
738 uint32_t pixel = *frame;
739 *frame = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff);
740 frame++;
741 }
742}
743
744int rbmp_process_image(rbmp_t *rbmp, void **buf_data,
745 size_t size, unsigned *width, unsigned *height)
746{
747 int comp;
748
749 if (!rbmp)
750 return IMAGE_PROCESS_ERROR;
751
752 rbmp->output_image = (uint32_t*)rbmp_load_from_memory(rbmp->buff_data,
753 (int)size, width, height, &comp, 4);
754 *buf_data = rbmp->output_image;
755
756 rbmp_convert_frame(rbmp->output_image, *width, *height);
757
758 return IMAGE_PROCESS_END;
759}
760
761bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data)
762{
763 if (!rbmp)
764 return false;
765
766 rbmp->buff_data = (uint8_t*)data;
767
768 return true;
769}
770
771void rbmp_free(rbmp_t *rbmp)
772{
773 if (!rbmp)
774 return;
775
776 free(rbmp);
777}
778
779rbmp_t *rbmp_alloc(void)
780{
781 rbmp_t *rbmp = (rbmp_t*)calloc(1, sizeof(*rbmp));
782 if (!rbmp)
783 return NULL;
784 return rbmp;
785}