#ifdef HAVE_CONFIG_H # include #endif #include #ifdef HAVE_EVIL # include #endif #include #include "evas_common.h" #include "evas_private.h" typedef struct _BMP_Header BMP_Header; struct _BMP_Header { unsigned int bmpsize; unsigned short res1; unsigned short res2; unsigned int offset; unsigned int head_size; int width; int height; unsigned short bit_count; int comp; // hdpi // vdpi int palette_size; // important_colors unsigned int rmask; unsigned int gmask; unsigned int bmask; unsigned int amask; Eina_Bool hasa; }; typedef struct _Evas_Loader_Internal Evas_Loader_Internal; struct _Evas_Loader_Internal { Eina_File *f; Evas_Image_Load_Opts *opts; }; static Eina_Bool read_short(unsigned char *map, size_t length, size_t *position, short *ret) { unsigned char b[2]; if (*position + 2 > length) return EINA_FALSE; b[0] = map[(*position)++]; b[1] = map[(*position)++]; *ret = (b[1] << 8) | b[0]; return EINA_TRUE; } static Eina_Bool read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret) { unsigned char b[2]; if (*position + 2 > length) return EINA_FALSE; b[0] = map[(*position)++]; b[1] = map[(*position)++]; *ret = (b[1] << 8) | b[0]; return EINA_TRUE; } static Eina_Bool read_int(unsigned char *map, size_t length, size_t *position, int *ret) { unsigned char b[4]; int i; if (*position + 4 > length) return EINA_FALSE; for (i = 0; i < 4; i++) b[i] = map[(*position)++]; *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]); return EINA_TRUE; } static Eina_Bool read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret) { unsigned char b[4]; int i; if (*position + 4 > length) return EINA_FALSE; for (i = 0; i < 4; i++) b[i] = map[(*position)++]; *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]); return EINA_TRUE; } static Eina_Bool read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret) { if (*position + 1 > length) return EINA_FALSE; *ret = map[(*position)++]; return EINA_TRUE; } static Eina_Bool read_skip(size_t length, size_t *position, int skip) { if (*position + skip > length) return EINA_FALSE; *position += skip; return EINA_TRUE; } static Eina_Bool read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size) { if (*position + size > length) return EINA_FALSE; memcpy(buffer, map + *position, size); *position += size; return EINA_TRUE; } static Eina_Bool _evas_image_load_file_header(void *map, size_t fsize, size_t *position, int *image_size, BMP_Header *header, int *error) { if (strncmp(map, "BM", 2)) return EINA_FALSE; // magic number *position += 2; *error = EVAS_LOAD_ERROR_CORRUPT_FILE; if (!read_uint(map, fsize, position, &header->bmpsize)) return EINA_FALSE; if (!read_ushort(map, fsize, position, &header->res1)) return EINA_FALSE; if (!read_ushort(map, fsize, position, &header->res2)) return EINA_FALSE; if (!read_uint(map, fsize, position, &header->offset)) return EINA_FALSE; if (!read_uint(map, fsize, position, &header->head_size)) return EINA_FALSE; if (header->offset > fsize) return EINA_FALSE; switch (header->head_size) { case 12: // OS/2 V1 + Windows 3.0 { short tmp; if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->width = tmp; // width if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->height = tmp; // height if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; //planes = tmp; // must be 1 if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->bit_count = tmp; // bits per pixel: 1, 4, 8 & 24 break; } case 64: // OS/2 V2 { short tmp; int tmp2; if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->width = tmp2; // width if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->height = tmp2; // height if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; //planes = tmp; // must be 1 if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->comp = tmp2; // compression method if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; if (tmp2 <= *image_size) *image_size = tmp2; // bitmap data size, GIMP can handle image size error if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //important_colors = tmp2; // number of important colors - 0 if all if (!read_skip(fsize, position, 24)) return EINA_FALSE; // skip unused header if (*image_size == 0) *image_size = fsize - header->offset; break; } case 40: // Windows 3.0 + (v3) { short tmp; int tmp2; if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->width = tmp2; // width if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->height = tmp2; // height if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; //planes = tmp; // must be 1 if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->comp = tmp2; // compression method if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; if (tmp2 <= *image_size) *image_size = tmp2; // bitmap data size, GIMP can handle image size error if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //important_colors = tmp2; // number of important colors - 0 if all if (*image_size == 0) *image_size = fsize - header->offset; if ((header->comp == 0) && (header->bit_count == 32)) header->hasa = 1; // GIMP seems to store it this way break; } case 108: // Windows 95/NT4 + (v4) { short tmp; int tmp2; if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->width = tmp2; // width if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->height = tmp2; // height if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; //planes = tmp; // must be 1 if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->comp = tmp2; // compression method if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; if (tmp2 <= *image_size) *image_size = tmp2; // bitmap data size, GIMP can handle image size error if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //important_colors = tmp2; // number of important colors - 0 if all if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->rmask = tmp2; // red mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->gmask = tmp2; // green mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->bmask = tmp2; // blue mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->amask = tmp2; // alpha mask if (!read_skip(fsize, position, 36)) return EINA_FALSE; // skip unused cie if (!read_skip(fsize, position, 12)) return EINA_FALSE; // skip unused gamma if (*image_size == 0) *image_size = fsize - header->offset; if ((header->amask) && (header->bit_count == 32)) header->hasa = 1; break; } case 124: // Windows 98/2000 + (v5) { short tmp; int tmp2; if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->width = tmp2; // width if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->height = tmp2; // height if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; //planes = tmp; // must be 1 if (!read_short(map, fsize, position, &tmp)) return EINA_FALSE; header->bit_count = tmp; // bits per pixel: 1, 4, 8, 16, 24 & 32 if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->comp = tmp2; // compression method if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; if (tmp2 <= *image_size) *image_size = tmp2; // bitmap data size, GIMP can handle image size error if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //hdpi = (tmp2 * 254) / 10000; // horizontal pixels/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //vdpi = (tmp2 * 254) / 10000; // vertical pixles/meter if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->palette_size = tmp2; // number of palette colors power (2^n - so 0 - 8) if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; //important_colors = tmp2; // number of important colors - 0 if all if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->rmask = tmp2; // red mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->gmask = tmp2; // green mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->bmask = tmp2; // blue mask if (!read_int(map, fsize, position, &tmp2)) return EINA_FALSE; header->amask = tmp2; // alpha mask if (!read_skip(fsize, position, 36)) return EINA_FALSE; // skip unused cie if (!read_skip(fsize, position, 12)) return EINA_FALSE; // skip unused gamma if (!read_skip(fsize, position, 16)) return EINA_FALSE; // skip others if (*image_size == 0) *image_size = fsize - header->offset; if ((header->amask) && (header->bit_count == 32)) header->hasa = 1; break; } default: return EINA_FALSE; } return EINA_TRUE; } static void * evas_image_load_file_open_bmp(Eina_File *f, const char *key EINA_UNUSED, Evas_Image_Load_Opts *opts, Evas_Image_Animated *animated EINA_UNUSED, int *error) { Evas_Loader_Internal *loader; loader = calloc(1, sizeof (Evas_Loader_Internal)); if (!loader) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; return NULL; } loader->f = f; loader->opts = opts; return loader; } static void evas_image_load_file_close_bmp(void *loader_data) { free(loader_data); } static Eina_Bool evas_image_load_file_head_bmp(void *loader_data, Evas_Image_Property *prop, int *error) { Evas_Loader_Internal *loader; Evas_Image_Load_Opts *load_opts; Eina_File *f; void *map = NULL; size_t position = 0; BMP_Header header; int image_size = 0; size_t fsize; Eina_Bool r = EINA_FALSE; loader = loader_data; f = loader->f; load_opts = loader->opts; *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; fsize = eina_file_size_get(f); if (fsize < 2) goto close_file; map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); if (!map) goto close_file; memset(&header, 0, sizeof (header)); if (!_evas_image_load_file_header(map, fsize, &position, &image_size, &header, error)) goto close_file; if (header.height < 0) { header.height = -header.height; //right_way_up = 1; } if ((header.width < 1) || (header.height < 1) || (header.width > IMG_MAX_SIZE) || (header.height > IMG_MAX_SIZE) || IMG_TOO_BIG(header.width, header.height)) { if (IMG_TOO_BIG(header.width, header.height)) *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; else *error = EVAS_LOAD_ERROR_GENERIC; goto close_file; } /* It is not bad idea that bmp loader support scale down decoding * because of memory issue in mobile world.*/ if (load_opts->scale_down_by > 1) { header.width /= load_opts->scale_down_by; header.height /= load_opts->scale_down_by; } if (header.bit_count < 16) { //if ((palette_size < 0) || (palette_size > 256)) pal_num = 256; //else pal_num = palette_size; if (header.bit_count == 1) { if (header.comp == 0) // no compression { } else goto close_file; } else if (header.bit_count == 4) { if (header.comp == 0) // no compression { } else if (header.comp == 2) // rle 4bit/pixel { } else goto close_file; } else if (header.bit_count == 8) { if (header.comp == 0) // no compression { } else if (header.comp == 1) // rle 8bit/pixel { } else goto close_file; } } else if ((header.bit_count == 16) || (header.bit_count == 24) || (header.bit_count == 32)) { if (header.comp == 0) // no compression { // handled } else if (header.comp == 3) // bit field { // handled } else if (header.comp == 4) // jpeg - only printer drivers goto close_file; else if (header.comp == 3) // png - only printer drivers goto close_file; else goto close_file; } else goto close_file; prop->w = header.width; prop->h = header.height; if (header.hasa) prop->alpha = 1; *error = EVAS_LOAD_ERROR_NONE; r = EINA_TRUE; close_file: if (map) eina_file_map_free(f, map); return r; } static Eina_Bool evas_image_load_file_data_bmp(void *loader_data, Evas_Image_Property *prop, void *pixels, int *error) { Evas_Loader_Internal *loader; Evas_Image_Load_Opts *opts; Eina_File *f; BMP_Header header; void *map = NULL; size_t position = 0; unsigned char *buffer = NULL, *buffer_end = NULL, *p; int x = 0, y = 0, image_size = 0; unsigned int *pal = NULL, pal_num = 0, *pix = NULL, fix, *surface = pixels; int right_way_up = 0; unsigned char r, g, b, a; size_t fsize; /* for scale decoding */ unsigned int *scale_surface = NULL, *scale_pix = NULL; int scale_ratio = 1, image_w = 0, image_h = 0; int row_size = 0; /* Row size is rounded up to a multiple of 4bytes */ int read_line = 0; /* total read line */ loader = loader_data; f = loader->f; opts = loader->opts; *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; fsize = eina_file_size_get(f); if (fsize < 2) goto close_file; map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); if (!map) goto close_file; memset(&header, 0, sizeof (header)); header.palette_size = -1; if (!_evas_image_load_file_header(map, fsize, &position, &image_size, &header, error)) goto close_file; if (header.height < 0) { header.height = -header.height; right_way_up = 1; } if ((header.width < 1) || (header.height < 1) || (header.width > IMG_MAX_SIZE) || (header.height > IMG_MAX_SIZE) || IMG_TOO_BIG(header.width, header.height)) { if (IMG_TOO_BIG(header.width, header.height)) *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; else *error = EVAS_LOAD_ERROR_GENERIC; goto close_file; } /* It is not bad idea that bmp loader support scale down decoding * because of memory issue in mobile world. */ if (opts->scale_down_by > 1) scale_ratio = opts->scale_down_by; image_w = header.width; image_h = header.height; if (scale_ratio > 1) { header.width /= scale_ratio; header.height /= scale_ratio; if ((header.width < 1) || (header.height < 1) ) { *error = EVAS_LOAD_ERROR_GENERIC; goto close_file; } } if ((header.width != (int)prop->w) || (header.height != (int)prop->h)) { *error = EVAS_LOAD_ERROR_GENERIC; goto close_file; } row_size = ceil((double)(image_w * header.bit_count) / 32) * 4; if (image_size != row_size * header.height) image_size = row_size * header.height; if (header.bit_count < 16) { unsigned int i; if (header.bit_count == 1) { if ((header.palette_size <= 0) || (header.palette_size > 2)) pal_num = 2; else pal_num = header.palette_size; } else if (header.bit_count == 4) { if ((header.palette_size <= 0) || (header.palette_size > 16)) pal_num = 16; else pal_num = header.palette_size; } else if (header.bit_count == 8) { if ((header.palette_size <= 0) || (header.palette_size > 256)) pal_num = 256; else pal_num = header.palette_size; } pal = alloca(256 * 4); for (i = 0; i < pal_num; i++) { if (!read_uchar(map, fsize, &position, &b)) goto close_file; if (!read_uchar(map, fsize, &position, &g)) goto close_file; if (!read_uchar(map, fsize, &position, &r)) goto close_file; if ((header.head_size != 12) /*&& (palette_size != 0)*/) { // OS/2 V1 doesn't do the pad byte if (!read_uchar(map, fsize, &position, &a)) goto close_file; } a = 0xff; // fillin a as solid for paletted images pal[i] = ARGB_JOIN(a, r, g, b); } position = header.offset; if ((scale_ratio == 1) || (header.comp !=0)) buffer = malloc(image_size + 8); // add 8 for padding to avoid checks else { scale_surface = malloc(image_w * sizeof(DATA32)); //for one line decoding if (!scale_surface) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; goto close_file; } buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line } if (!buffer) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; goto close_file; } if ((scale_ratio == 1) || (header.comp !=0)) buffer_end = buffer + image_size; else buffer_end = buffer + row_size; p = buffer; if ((scale_ratio == 1) || (header.comp !=0)) { if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; } else { if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; } if (header.bit_count == 1) { if (header.comp == 0) // no compression { pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); if (scale_ratio > 1) pix = scale_surface; // one line decoding for (x = 0; x < image_w; x++) { if ((x & 0x7) == 0x0) { *pix = pal[*p >> 7]; } else if ((x & 0x7) == 0x1) { *pix = pal[(*p >> 6) & 0x1]; } else if ((x & 0x7) == 0x2) { *pix = pal[(*p >> 5) & 0x1]; } else if ((x & 0x7) == 0x3) { *pix = pal[(*p >> 4) & 0x1]; } else if ((x & 0x7) == 0x4) { *pix = pal[(*p >> 3) & 0x1]; } else if ((x & 0x7) == 0x5) { *pix = pal[(*p >> 2) & 0x1]; } else if ((x & 0x7) == 0x6) { *pix = pal[(*p >> 1) & 0x1]; } else { *pix = pal[*p & 0x1]; p++; } if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { if (!right_way_up) scale_pix = surface + ((header.height - 1 - y) * header.width); else scale_pix = surface + (y * header.width); pix = scale_surface; for (x = 0; x < header.width; x++) { *scale_pix = *pix; scale_pix ++; pix += scale_ratio; } read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { if ((x & 0x7) != 0) p++; fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else goto close_file; } else if (header.bit_count == 4) { if (header.comp == 0) // no compression { pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); if (scale_ratio > 1) pix = scale_surface; // one line decoding for (x = 0; x < image_w; x++) { if ((x & 0x1) == 0x1) { *pix = pal[*p & 0x0f]; p++; } else { *pix = pal[*p >> 4]; } if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { if (!right_way_up) scale_pix = surface + ((header.height - 1 - y) * header.width); else scale_pix = surface + (y * header.width); pix = scale_surface; for (x = 0; x < header.width; x++) { *scale_pix = *pix; scale_pix ++; pix += scale_ratio; } read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { if ((x & 0x1) != 0) p++; fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if (header.comp == 2) // rle 4bit/pixel { int count = 0, done = 0, wpad; int scale_x = 0, scale_y = 0; Eina_Bool scale_down_line = EINA_TRUE; pix = surface; if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); wpad = ((image_w + 1) / 2) * 2; while (p < buffer_end) { if (p[0]) { if (scale_down_line) { if ((x + p[0]) <= wpad) { unsigned int col1 = pal[p[1] >> 4]; unsigned int col2 = pal[p[1] & 0xf]; count = p[0] / 2; while (count > 0) { if (x < header.width) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = col1; pix++; scale_x++; } x++; } if (x < header.width) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = col2; pix++; scale_x++; } x++; } count--; } if (p[0] & 0x1) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = col1; pix++; scale_x++; } x++; } } } p += 2; } else { switch (p[1]) { case 0: // EOL x = 0; scale_x = 0; y++; if ((y % scale_ratio) == 0) { scale_y++; scale_down_line = EINA_TRUE; if (!right_way_up) pix = surface + ((header.height - 1 - scale_y) * header.width); else pix = surface + (scale_y * header.width); } else scale_down_line = EINA_FALSE; if (scale_y >= header.height) { p = buffer_end; } p += 2; break; case 1: // EOB p = buffer_end; break; case 2: // DELTA x += p[2]; y += p[3]; scale_x = x / scale_ratio; scale_y = y / scale_ratio; if ((scale_x >= header.width) || (scale_y >= header.height)) { p = buffer_end; } if (!right_way_up) pix = surface + scale_x + ((header.height - 1 - scale_y) * header.width); else pix = surface + scale_x + (scale_y * header.width); p += 4; break; default: count = p[1]; if (((p + count) > buffer_end) || ((x + count) > header.width)) { p = buffer_end; break; } p += 2; done = count; count /= 2; while (count > 0) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = pal[*p >> 4]; pix++; scale_x++; } x++; if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = pal[*p & 0xf]; pix++; scale_x++; } x++; p++; count--; } if (done & 0x1) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = pal[*p >> 4]; scale_x++; } x++; p++; } if ((done & 0x3) == 0x1) p += 2; else if ((done & 0x3) == 0x2) p += 1; break; } } } } else goto close_file; } else if (header.bit_count == 8) { if (header.comp == 0) // no compression { pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { *pix = pal[*p]; p += scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if (header.comp == 1) // rle 8bit/pixel { int count = 0, done = 0; int scale_x = 0, scale_y = 0; Eina_Bool scale_down_line = EINA_TRUE; pix = surface; if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); while (p < buffer_end) { if (p[0]) { if (scale_down_line) { if ((x + p[0]) <= image_w) { unsigned int col = pal[p[1]]; count = p[0]; while (count > 0) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = col; pix++; scale_x ++; } x++; count--; } } } p += 2; } else { switch (p[1]) { case 0: // EOL x = 0; scale_x = 0; y++; if ((y % scale_ratio) == 0) { scale_y++; scale_down_line = EINA_TRUE; if (!right_way_up) pix = surface + ((header.height - 1 - scale_y) * header.width); else pix = surface + (scale_y * header.width); } else scale_down_line = EINA_FALSE; if (scale_y >= header.height) { p = buffer_end; } p += 2; break; case 1: // EOB p = buffer_end; break; case 2: // DELTA x += p[2]; y += p[3]; scale_x = x / scale_ratio; scale_y = y / scale_ratio; if ((scale_x >= header.width) || (scale_y >= header.height)) { p = buffer_end; } if (!right_way_up) pix = surface + scale_x + ((header.height - 1 - scale_y) * header.width); else pix = surface + scale_x + (scale_y * header.width); p += 4; break; default: count = p[1]; if (((p + count) > buffer_end) || ((x + count) > image_w)) { p = buffer_end; break; } p += 2; done = count; while (count > 0) { if (((x % scale_ratio) == 0) && (scale_x < header.width)) { *pix = pal[*p]; pix++; scale_x ++; } p++; x++; count--; } if (done & 0x1) p++; break; } } } } else goto close_file; } } else if ((header.bit_count == 16) || (header.bit_count == 24) || (header.bit_count == 32)) { if (header.comp == 0) // no compression { position = header.offset; if (scale_ratio == 1) buffer = malloc(image_size + 8); // add 8 for padding to avoid checks else buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line if (!buffer) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; goto close_file; } if (scale_ratio == 1) buffer_end = buffer + image_size; else buffer_end = buffer + row_size; p = buffer; if (scale_ratio == 1) { if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; } else { if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; } if (header.bit_count == 16) { unsigned short tmp; pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { tmp = *((unsigned short *)(p)); r = (tmp >> 7) & 0xf8; r |= r >> 5; g = (tmp >> 2) & 0xf8; g |= g >> 5; b = (tmp << 3) & 0xf8; b |= b >> 5; *pix = ARGB_JOIN(0xff, r, g, b); p += 2 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if (header.bit_count == 24) { pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { b = p[0]; g = p[1]; r = p[2]; *pix = ARGB_JOIN(0xff, r, g, b); p += 3 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if (header.bit_count == 32) { int none_zero_alpha = 0; pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { b = p[0]; g = p[1]; r = p[2]; a = p[3]; if (a) none_zero_alpha = 1; if (!header.hasa) a = 0xff; *pix = ARGB_JOIN(a, r, g, b); p += 4 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } if (!none_zero_alpha) { prop->alpha = 0; if (header.hasa) { unsigned int *pixend = surface + (header.width * header.height); for (pix = surface; pix < pixend; pix++) A_VAL(pix) = 0xff; } } } else goto close_file; } else if (header.comp == 3) // bit field { if (!read_uint(map, fsize, &position, &header.rmask)) goto close_file; if (!read_uint(map, fsize, &position, &header.gmask)) goto close_file; if (!read_uint(map, fsize, &position, &header.bmask)) goto close_file; position = header.offset; if (scale_ratio == 1) buffer = malloc(image_size + 8); // add 8 for padding to avoid checks else buffer = malloc(row_size); // scale down is usually set because of memory issue, so read line by line if (!buffer) { *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; goto close_file; } if (scale_ratio == 1) buffer_end = buffer + image_size; else buffer_end = buffer + row_size; p = buffer; if (scale_ratio == 1) { if (!read_mem(map, fsize, &position, buffer, image_size)) goto close_file; } else { if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; } if ((header.bit_count == 16) && (header.rmask == 0xf800) && (header.gmask == 0x07e0) && (header.bmask == 0x001f) ) { unsigned short tmp; pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { tmp = *((unsigned short *)(p)); r = (tmp >> 8) & 0xf8; r |= r >> 5; g = (tmp >> 3) & 0xfc; g |= g >> 6; b = (tmp << 3) & 0xf8; b |= b >> 5; *pix = ARGB_JOIN(0xff, r, g, b); p += 2 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if ((header.bit_count == 16) && (header.rmask == 0x7c00) && (header.gmask == 0x03e0) && (header.bmask == 0x001f) ) { unsigned short tmp; pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { tmp = *((unsigned short *)(p)); r = (tmp >> 7) & 0xf8; r |= r >> 5; g = (tmp >> 2) & 0xf8; g |= g >> 5; b = (tmp << 3) & 0xf8; b |= b >> 5; *pix = ARGB_JOIN(0xff, r, g, b); p += 2 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer_end, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else if (header.bit_count == 32) { pix = surface; for (y = 0; y < header.height; y++) { if (!right_way_up) pix = surface + ((header.height - 1 - y) * header.width); for (x = 0; x < header.width; x++) { b = p[0]; g = p[1]; r = p[2]; a = p[3]; if (!header.hasa) a = 0xff; *pix = ARGB_JOIN(a, r, g, b); p += 4 * scale_ratio; if (p >= buffer_end) break; pix++; } if (scale_ratio > 1) { read_line += scale_ratio; if (read_line >= image_h) break; position += row_size * (scale_ratio - 1); if (!read_mem(map, fsize, &position, buffer, row_size)) goto close_file; p = buffer; buffer_end = buffer + row_size; } else { fix = (int)(((uintptr_t)p) & 0x3); if (fix > 0) p += 4 - fix; // align row read if (p >= buffer_end) break; } } } else goto close_file; } else if (header.comp == 4) // jpeg - only printer drivers { goto close_file; } else if (header.comp == 3) // png - only printer drivers { goto close_file; } else goto close_file; } else goto close_file; if (buffer) free(buffer); if (scale_surface) free(scale_surface); eina_file_map_free(f, map); prop->premul = EINA_TRUE; *error = EVAS_LOAD_ERROR_NONE; return EINA_TRUE; close_file: if (buffer) free(buffer); if (scale_surface) free(scale_surface); if (map) eina_file_map_free(f, map); return EINA_FALSE; } static Evas_Image_Load_Func evas_image_load_bmp_func = { EINA_TRUE, evas_image_load_file_open_bmp, evas_image_load_file_close_bmp, evas_image_load_file_head_bmp, evas_image_load_file_data_bmp, NULL, EINA_FALSE }; static int module_open(Evas_Module *em) { if (!em) return 0; em->functions = (void *)(&evas_image_load_bmp_func); return 1; } static void module_close(Evas_Module *em EINA_UNUSED) { } static Evas_Module_Api evas_modapi = { EVAS_MODULE_API_VERSION, "bmp", "none", { module_open, module_close } }; EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, bmp); #ifndef EVAS_STATIC_BUILD_BMP EVAS_EINA_MODULE_DEFINE(image_loader, bmp); #endif