From 4309793b1dd0ae02d66b7c8e53d53afb0fe91a9a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Tue, 17 May 2016 21:21:37 +0200 Subject: [PATCH] Add rbmp.c --- Makefile.common | 4 +- griffin/griffin.c | 1 + libretro-common/formats/bmp/rbmp.c | 623 +++++++++++++++++++++++++ libretro-common/formats/jpeg/rjpeg.c | 3 +- libretro-common/include/formats/rbmp.h | 11 + 5 files changed, 639 insertions(+), 3 deletions(-) create mode 100644 libretro-common/formats/bmp/rbmp.c diff --git a/Makefile.common b/Makefile.common index 83cf8a1f55..5f3fc542b5 100644 --- a/Makefile.common +++ b/Makefile.common @@ -920,7 +920,9 @@ ifeq ($(HAVE_RJPEG), 1) OBJ += libretro-common/formats/jpeg/rjpeg.o endif -OBJ += libretro-common/formats/bmp/rbmp_encode.o \ +OBJ += \ + libretro-common/formats/bmp/rbmp.o \ + libretro-common/formats/bmp/rbmp_encode.o \ libretro-common/formats/json/jsonsax.o \ libretro-common/formats/image_transfer.o diff --git a/griffin/griffin.c b/griffin/griffin.c index 9a083112c5..27ce3d6c7d 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -244,6 +244,7 @@ VIDEO IMAGE #ifdef HAVE_RJPEG #include "../libretro-common/formats/jpeg/rjpeg.c" #endif +#include "../libretro-common/formats/bmp/rbmp.c" #include "../libretro-common/formats/bmp/rbmp_encode.c" /*============================================================ diff --git a/libretro-common/formats/bmp/rbmp.c b/libretro-common/formats/bmp/rbmp.c new file mode 100644 index 0000000000..acfbb9b79e --- /dev/null +++ b/libretro-common/formats/bmp/rbmp.c @@ -0,0 +1,623 @@ +/* Copyright (C) 2010-2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (rbmp.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Modified version of stb_image's BMP sources. */ + +#include +#include +#include +#include /* ptrdiff_t on osx */ +#include +#include + +#include +#include + +#include +#include + +typedef struct +{ + int (*read) (void *user,char *data,int size); /* fill 'data' with 'size' bytes. return number of bytes actually read */ + void (*skip) (void *user,int n); /* skip the next 'n' bytes, or 'unget' the last -n bytes if negative */ + int (*eof) (void *user); /* returns nonzero if we are at end of file/data */ +} rbmp_io_callbacks; + +typedef struct +{ + uint32_t img_x, img_y; + int img_n, img_out_n; + + rbmp_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + unsigned char buffer_start[128]; + + unsigned char *img_buffer, *img_buffer_end; + unsigned char *img_buffer_original; +} rbmp__context; + +struct rbmp +{ + uint8_t *buff_data; + uint32_t *output_image; + void *empty; +}; + +static void rbmp__refill_buffer(rbmp__context *s); + +/* initialize a memory-decode context */ +static void rbmp__start_mem(rbmp__context *s, unsigned char const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (unsigned char *) buffer; + s->img_buffer_end = (unsigned char *) buffer+len; +} + +static unsigned char *rbmp__bmp_load(rbmp__context *s, unsigned *x, unsigned *y, int *comp, int req_comp); + +#define rbmp__err(x,y) 0 +#define rbmp__errpf(x,y) ((float *) (rbmp__err(x,y)?NULL:NULL)) +#define rbmp__errpuc(x,y) ((unsigned char *) (rbmp__err(x,y)?NULL:NULL)) + +static unsigned char *rbmp_load_from_memory(unsigned char const *buffer, int len, unsigned *x, unsigned *y, int *comp, int req_comp) +{ + rbmp__context s; + rbmp__start_mem(&s,buffer,len); + return rbmp__bmp_load(&s,x,y,comp,req_comp); +} + +static void rbmp__refill_buffer(rbmp__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) + { + /* at end of file, treat same as if from memory, but need to handle case + * where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file */ + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } + else + { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +static INLINE unsigned char rbmp__get8(rbmp__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + + if (s->read_from_callbacks) + { + rbmp__refill_buffer(s); + return *s->img_buffer++; + } + + return 0; +} + +static void rbmp__skip(rbmp__context *s, int n) +{ + if (n < 0) + { + s->img_buffer = s->img_buffer_end; + return; + } + + if (s->io.read) + { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) + { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int rbmp__get16le(rbmp__context *s) +{ + int z = rbmp__get8(s); + return z + (rbmp__get8(s) << 8); +} + +static uint32_t rbmp__get32le(rbmp__context *s) +{ + uint32_t z = rbmp__get16le(s); + return z + (rbmp__get16le(s) << 16); +} + +#define RBMP__BYTECAST(x) ((unsigned char) ((x) & 255)) /* truncate int to byte without warnings */ + +static unsigned char rbmp__compute_y(int r, int g, int b) +{ + return (unsigned char) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *rbmp__convert_format( + unsigned char *data, + int img_n, + int req_comp, + unsigned int x, + unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + retro_assert(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) malloc(req_comp * x * y); + if (good == NULL) + { + free(data); + return rbmp__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) + { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + switch (((img_n)*8+(req_comp))) + { + case ((1)*8+(2)): + for(i=x-1; i >= 0; --i, src += 1, dest += 2) + dest[0]=src[0], dest[1]=255; + break; + case ((1)*8+(3)): + for(i=x-1; i >= 0; --i, src += 1, dest += 3) + dest[0]=dest[1]=dest[2]=src[0]; + break; + case ((1)*8+(4)): + for(i=x-1; i >= 0; --i, src += 1, dest += 4) + dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; + break; + case ((2)*8+(1)): + for(i=x-1; i >= 0; --i, src += 2, dest += 1) + dest[0]=src[0]; + break; + case ((2)*8+(3)): + for(i=x-1; i >= 0; --i, src += 2, dest += 3) + dest[0]=dest[1]=dest[2]=src[0]; + break; + case ((2)*8+(4)): + for(i=x-1; i >= 0; --i, src += 2, dest += 4) + dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; + break; + case ((3)*8+(4)): + for(i=x-1; i >= 0; --i, src += 3, dest += 4) + dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; + break; + case ((3)*8+(1)): + for(i=x-1; i >= 0; --i, src += 3, dest += 1) + dest[0]=rbmp__compute_y(src[0],src[1],src[2]); + break; + case ((3)*8+(2)): + for(i=x-1; i >= 0; --i, src += 3, dest += 2) + dest[0]=rbmp__compute_y(src[0],src[1],src[2]), dest[1] = 255; + break; + case ((4)*8+(1)): + for(i=x-1; i >= 0; --i, src += 4, dest += 1) + dest[0]=rbmp__compute_y(src[0],src[1],src[2]); + break; + case ((4)*8+(2)): + for(i=x-1; i >= 0; --i, src += 4, dest += 2) + dest[0]=rbmp__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; + break; + case ((4)*8+(3)): + for(i=x-1; i >= 0; --i, src += 4, dest += 3) + dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; + break; + default: + retro_assert(0); + break; + } + + } + + free(data); + return good; +} + +/* Microsoft/Windows BMP image */ + +/* returns 0..31 for the highest set bit */ +static int rbmp__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int rbmp__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); /* max 2 */ + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); /* max 4 */ + a = (a + (a >> 4)) & 0x0f0f0f0f; /* max 8 per 4, now 8 bits */ + a = (a + (a >> 8)); /* max 16 per 8 bits */ + a = (a + (a >> 16)); /* max 32 per 8 bits */ + return a & 0xff; +} + +static int rbmp__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) + v <<= -shift; + else + v >>= shift; + + result = v; + z = bits; + + while (z < 8) + { + result += v >> z; + z += bits; + } + return result; +} + +static unsigned char *rbmp__bmp_load(rbmp__context *s, unsigned *x, unsigned *y, int *comp, int req_comp) +{ + unsigned char *out; + unsigned int mr=0,mg=0,mb=0,ma=0; + unsigned char pal[256][4]; + int psize=0,i,j,compress=0,width; + int bpp, flip_vertically, pad, target, offset, hsz; + + if (rbmp__get8(s) != 'B' || rbmp__get8(s) != 'M') + return rbmp__errpuc("not BMP", "Corrupt BMP"); + + rbmp__get32le(s); /* discard filesize */ + rbmp__get16le(s); /* discard reserved */ + rbmp__get16le(s); /* discard reserved */ + offset = rbmp__get32le(s); + hsz = rbmp__get32le(s); + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) + return rbmp__errpuc("unknown BMP", "BMP type not supported: unknown"); + + if (hsz == 12) + { + s->img_x = rbmp__get16le(s); + s->img_y = rbmp__get16le(s); + } + else + { + s->img_x = rbmp__get32le(s); + s->img_y = rbmp__get32le(s); + } + if (rbmp__get16le(s) != 1) + return rbmp__errpuc("bad BMP", "bad BMP"); + bpp = rbmp__get16le(s); + if (bpp == 1) + return rbmp__errpuc("monochrome", "BMP type not supported: 1-bit"); + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (hsz == 12) + { + if (bpp < 24) + psize = (offset - 14 - 24) / 3; + } + else + { + compress = rbmp__get32le(s); + + if (compress == 1 || compress == 2) + return rbmp__errpuc("BMP RLE", "BMP type not supported: RLE"); + + rbmp__get32le(s); /* discard sizeof */ + rbmp__get32le(s); /* discard hres */ + rbmp__get32le(s); /* discard vres */ + rbmp__get32le(s); /* discard colors used */ + rbmp__get32le(s); /* discard max important */ + if (hsz == 40 || hsz == 56) + { + if (hsz == 56) + { + rbmp__get32le(s); + rbmp__get32le(s); + rbmp__get32le(s); + rbmp__get32le(s); + } + if (bpp == 16 || bpp == 32) + { + mr = mg = mb = 0; + + switch (compress) + { + case 0: + if (bpp == 32) + { + mr = 0xffu << 16; + mg = 0xffu << 8; + mb = 0xffu << 0; + ma = 0xffu << 24; + } + else + { + mr = 31u << 10; + mg = 31u << 5; + mb = 31u << 0; + } + break; + case 3: + mr = rbmp__get32le(s); + mg = rbmp__get32le(s); + mb = rbmp__get32le(s); + /* not documented, but generated by photoshop and handled by mspaint */ + if (mr == mg && mg == mb) + return rbmp__errpuc("bad BMP", "bad BMP"); + break; + default: + break; + } + return rbmp__errpuc("bad BMP", "bad BMP"); + } + } + else + { + retro_assert(hsz == 108 || hsz == 124); + mr = rbmp__get32le(s); + mg = rbmp__get32le(s); + mb = rbmp__get32le(s); + ma = rbmp__get32le(s); + rbmp__get32le(s); /* discard color space */ + for (i=0; i < 12; ++i) + rbmp__get32le(s); /* discard color space parameters */ + if (hsz == 124) + { + rbmp__get32le(s); /* discard rendering intent */ + rbmp__get32le(s); /* discard offset of profile data */ + rbmp__get32le(s); /* discard size of profile data */ + rbmp__get32le(s); /* discard reserved */ + } + } + if (bpp < 16) + psize = (offset - 14 - hsz) >> 2; + } + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) /* we can directly decode 3 or 4 */ + target = req_comp; + else + target = s->img_n; /* if they want monochrome, we'll post-convert */ + out = (unsigned char *) malloc(target * s->img_x * s->img_y); + if (!out) + return rbmp__errpuc("outofmem", "Out of memory"); + if (bpp < 16) + { + int z=0; + if (psize == 0 || psize > 256) + { + free(out); + return rbmp__errpuc("invalid", "Corrupt BMP"); + } + + for (i=0; i < psize; ++i) + { + pal[i][2] = rbmp__get8(s); + pal[i][1] = rbmp__get8(s); + pal[i][0] = rbmp__get8(s); + if (hsz != 12) rbmp__get8(s); + pal[i][3] = 255; + } + + rbmp__skip(s, offset - 14 - hsz - psize * (hsz == 12 ? 3 : 4)); + if (bpp == 4) + width = (s->img_x + 1) >> 1; + else if (bpp == 8) + width = s->img_x; + else + { + free(out); + return rbmp__errpuc("bad bpp", "Corrupt BMP"); + } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) + { + for (i=0; i < (int) s->img_x; i += 2) + { + int v=rbmp__get8(s),v2=0; + if (bpp == 4) + { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (bpp == 8) ? rbmp__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + rbmp__skip(s, pad); + } + } + else + { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + rbmp__skip(s, offset - 14 - hsz); + if (bpp == 24) width = 3 * s->img_x; + else if (bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + + switch (bpp) + { + case 24: + easy = 1; + break; + case 32: + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + break; + default: + break; + } + + if (!easy) + { + if (!mr || !mg || !mb) + { + free(out); + return rbmp__errpuc("bad masks", "Corrupt BMP"); + } + /* right shift amt to put high bit in position #7 */ + rshift = rbmp__high_bit(mr)-7; rcount = rbmp__bitcount(mr); + gshift = rbmp__high_bit(mg)-7; gcount = rbmp__bitcount(mg); + bshift = rbmp__high_bit(mb)-7; bcount = rbmp__bitcount(mb); + ashift = rbmp__high_bit(ma)-7; acount = rbmp__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) + { + if (easy) + { + for (i=0; i < (int) s->img_x; ++i) + { + unsigned char a; + out[z+2] = rbmp__get8(s); + out[z+1] = rbmp__get8(s); + out[z+0] = rbmp__get8(s); + z += 3; + a = (easy == 2 ? rbmp__get8(s) : 255); + if (target == 4) out[z++] = a; + } + } + else + { + for (i=0; i < (int) s->img_x; ++i) + { + uint32_t v = (bpp == 16 ? (uint32_t) rbmp__get16le(s) : rbmp__get32le(s)); + int a; + out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mr, rshift, rcount)); + out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mg, gshift, gcount)); + out[z++] = RBMP__BYTECAST(rbmp__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? rbmp__shiftsigned(v & ma, ashift, acount) : 255); + if (target == 4) out[z++] = RBMP__BYTECAST(a); + } + } + rbmp__skip(s, pad); + } + } + if (flip_vertically) + { + unsigned char t; + for (j=0; j < (int) s->img_y>>1; ++j) + { + unsigned char *p1 = out + j *s->img_x*target; + unsigned char *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) + { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) + { + out = rbmp__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) + return out; /* rbmp__convert_format frees input on failure */ + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} + +int rbmp_process_image(rbmp_t *rbmp, void **buf_data, + size_t size, unsigned *width, unsigned *height) +{ + int comp; + unsigned size_tex = 0; + + if (!rbmp) + return IMAGE_PROCESS_ERROR; + + rbmp->output_image = (uint32_t*)rbmp_load_from_memory(rbmp->buff_data, size, width, height, &comp, 4); + *buf_data = rbmp->output_image; + size_tex = (*width) * (*height); + + /* Convert RGBA to ARGB */ + do + { + unsigned int texel = rbmp->output_image[size_tex]; + unsigned int A = texel & 0xFF000000; + unsigned int B = texel & 0x00FF0000; + unsigned int G = texel & 0x0000FF00; + unsigned int R = texel & 0x000000FF; + ((unsigned int*)rbmp->output_image)[size_tex] = A | (R << 16) | G | (B >> 16); + }while(size_tex--); + + return IMAGE_PROCESS_END; +} + +bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data) +{ + if (!rbmp) + return false; + + rbmp->buff_data = (uint8_t*)data; + + return true; +} + +void rbmp_free(rbmp_t *rbmp) +{ + if (!rbmp) + return; + + free(rbmp); +} + +rbmp_t *rbmp_alloc(void) +{ + rbmp_t *rbmp = (rbmp_t*)calloc(1, sizeof(*rbmp)); + if (!rbmp) + return NULL; + return rbmp; +} diff --git a/libretro-common/formats/jpeg/rjpeg.c b/libretro-common/formats/jpeg/rjpeg.c index 18204f5622..de2fbd67b1 100644 --- a/libretro-common/formats/jpeg/rjpeg.c +++ b/libretro-common/formats/jpeg/rjpeg.c @@ -20,8 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/* Modified version of stb_image's JPEG sources. - */ +/* Modified version of stb_image's JPEG sources. */ #include #include diff --git a/libretro-common/include/formats/rbmp.h b/libretro-common/include/formats/rbmp.h index 4b2a51c1e5..bc0702758f 100644 --- a/libretro-common/include/formats/rbmp.h +++ b/libretro-common/include/formats/rbmp.h @@ -38,6 +38,8 @@ enum rbmp_source_type RBMP_SOURCE_TYPE_ARGB8888 }; +typedef struct rbmp rbmp_t; + bool rbmp_save_image( const char *filename, const void *frame, @@ -46,6 +48,15 @@ bool rbmp_save_image( unsigned pitch, enum rbmp_source_type type); +int rbmp_process_image(rbmp_t *rbmp, void **buf, + size_t size, unsigned *width, unsigned *height); + +bool rbmp_set_buf_ptr(rbmp_t *rbmp, void *data); + +void rbmp_free(rbmp_t *rbmp); + +rbmp_t *rbmp_alloc(void); + RETRO_END_DECLS #endif