diff options
author | Bogdan Devichev <b.devichev@samsung.com> | 2014-11-26 18:50:43 +0200 |
---|---|---|
committer | Cedric BAIL <cedric@osg.samsung.com> | 2014-12-23 21:13:43 +0100 |
commit | 043055fc8eb3d7db11856f4d142f5e0e3262b3a6 (patch) | |
tree | 4da5d613fc0d859a8e5c588a072f00f1a8bf520e /src/modules/evas/image_loaders | |
parent | a14e0b8b3283ff4ac34f13258268326882e4b1ec (diff) | |
download | efl-043055fc8eb3d7db11856f4d142f5e0e3262b3a6.tar.gz |
evas: preparation of places for model_saver_loader separated from image_saver_loader.
Diffstat (limited to 'src/modules/evas/image_loaders')
19 files changed, 11273 insertions, 0 deletions
diff --git a/src/modules/evas/image_loaders/bmp/evas_image_load_bmp.c b/src/modules/evas/image_loaders/bmp/evas_image_load_bmp.c new file mode 100644 index 0000000000..d54cfdc8f6 --- /dev/null +++ b/src/modules/evas/image_loaders/bmp/evas_image_load_bmp.c @@ -0,0 +1,1411 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include <math.h> + +#include "evas_common_private.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, Eina_Stringshare *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_RANDOM); + 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 = +{ + 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_TRUE, + 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 diff --git a/src/modules/evas/image_loaders/dds/evas_image_load_dds.c b/src/modules/evas/image_loaders/dds/evas_image_load_dds.c new file mode 100644 index 0000000000..d0a1c55d14 --- /dev/null +++ b/src/modules/evas/image_loaders/dds/evas_image_load_dds.c @@ -0,0 +1,569 @@ +/* @file evas_image_load_dds.c + * @author Jean-Philippe ANDRE <jpeg@videolan.org> + * + * Load Microsoft DirectDraw Surface files. + * Decode S3TC image format. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "Evas_Loader.h" +#include "s3tc.h" + +#ifdef _WIN32 +# include <ddraw.h> +#endif + +#define DDS_HEADER_SIZE 128 + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + + Evas_Colorspace format; + unsigned int stride, block_size, data_size; + + struct { + unsigned int flags; + unsigned int fourcc; + unsigned int rgb_bitcount; + unsigned int r_mask; + unsigned int g_mask; + unsigned int b_mask; + unsigned int a_mask; + // TODO: check mipmaps to load faster a small image :) + } pf; // pixel format +}; + +#undef FOURCC +#ifndef WORDS_BIGENDIAN +# define FOURCC(a,b,c,d) ((d << 24) | (c << 16) | (b << 8) | a) +#else +# define FOURCC(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d) +#endif + +#ifndef DIRECTDRAW_VERSION +// DIRECTDRAW_VERSION is defined in ddraw.h +// These definitions are from the MSDN reference. + +enum DDSFlags { + DDSD_CAPS = 0x1, + DDSD_HEIGHT = 0x2, + DDSD_WIDTH = 0x4, + DDSD_PITCH = 0x8, + DDSD_PIXELFORMAT = 0x1000, + DDSD_MIPMAPCOUNT = 0x20000, + DDSD_LINEARSIZE = 0x80000, + DDSD_DEPTH = 0x800000 +}; + +enum DDSPixelFormatFlags { + DDPF_ALPHAPIXELS = 0x1, + DDPF_ALPHA = 0x2, + DDPF_FOURCC = 0x4, + DDPF_RGB = 0x40, + DDPF_YUV = 0x200, + DDPF_LUMINANCE = 0x20000 +}; + +enum DDSCaps { + DDSCAPS_COMPLEX = 0x8, + DDSCAPS_MIPMAP = 0x400000, + DDSCAPS_TEXTURE = 0x1000 +}; + +#endif + +static const Evas_Colorspace cspaces_s3tc_dxt1_rgb[] = { + EVAS_COLORSPACE_RGB_S3TC_DXT1, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_s3tc_dxt1_rgba[] = { + //EVAS_COLORSPACE_RGBA_S3TC_DXT1, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_s3tc_dxt2[] = { + EVAS_COLORSPACE_RGBA_S3TC_DXT2, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_s3tc_dxt3[] = { + //EVAS_COLORSPACE_RGBA_S3TC_DXT3, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_s3tc_dxt4[] = { + EVAS_COLORSPACE_RGBA_S3TC_DXT4, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_s3tc_dxt5[] = { + //EVAS_COLORSPACE_RGBA_S3TC_DXT5, + EVAS_COLORSPACE_ARGB8888 +}; + +static void * +evas_image_load_file_open_dds(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error) +{ + Evas_Loader_Internal *loader; + + if (eina_file_size_get(f) <= DDS_HEADER_SIZE) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return NULL; + } + + loader = calloc(1, sizeof (Evas_Loader_Internal)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + + loader->f = eina_file_dup(f); + if (!loader->f) + { + free(loader); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + return loader; +} + +static void +evas_image_load_file_close_dds(void *loader_data) +{ + Evas_Loader_Internal *loader = loader_data; + + eina_file_close(loader->f); + free(loader); +} + +static inline unsigned int +_dword_read(const char **m) +{ + unsigned int val = *((unsigned int *) *m); + *m += 4; + return val; +} + +#define FAIL() do { /*fprintf(stderr, "DDS: ERROR at %s:%d\n", __FUNCTION__, __LINE__);*/ goto on_error; } while (0) + +static Eina_Bool +evas_image_load_file_head_dds(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + static const unsigned int base_flags = /* 0x1007 */ + DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + + Evas_Loader_Internal *loader = loader_data; + unsigned int flags, height, width, pitchOrLinearSize, caps, caps2; + Eina_Bool has_linearsize, has_mipmapcount; + const char *m; + char *map; + + map = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL); + if (!map) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + m = map; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (strncmp(m, "DDS ", 4) != 0) + // TODO: Add support for DX10 + goto on_error; + m += 4; + + // Read DDS_HEADER + if (_dword_read(&m) != 124) + FAIL(); + + flags = _dword_read(&m); + if ((flags & base_flags) != (base_flags)) + FAIL(); + + if ((flags & ~(DDSD_MIPMAPCOUNT | DDSD_LINEARSIZE)) != base_flags) + { + // TODO: A lot of modes are not supported. + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + FAIL(); + } + + has_linearsize = !!(flags & DDSD_LINEARSIZE); + if (!has_linearsize) + FAIL(); + + has_mipmapcount = !!(flags & DDSD_MIPMAPCOUNT); + (void) has_mipmapcount; // We don't really care about it. + + height = _dword_read(&m); + width = _dword_read(&m); + pitchOrLinearSize = _dword_read(&m); + if (!width || !height) + FAIL(); + + // Skip depth & mipmap count + reserved[11] + m += 13 * sizeof(unsigned int); + // Entering DDS_PIXELFORMAT ddspf + if (_dword_read(&m) != 32) + FAIL(); + loader->pf.flags = _dword_read(&m); + if (!(loader->pf.flags & DDPF_FOURCC)) + FAIL(); // Unsupported (uncompressed formats may not have a FOURCC) + loader->pf.fourcc = _dword_read(&m); + loader->block_size = 16; + switch (loader->pf.fourcc) + { + case FOURCC('D', 'X', 'T', '1'): + loader->block_size = 8; + if ((loader->pf.flags & DDPF_ALPHAPIXELS) == 0) + { + prop->alpha = EINA_FALSE; + prop->cspaces = cspaces_s3tc_dxt1_rgb; + loader->format = EVAS_COLORSPACE_RGB_S3TC_DXT1; + } + else + { + prop->alpha = EINA_TRUE; + prop->cspaces = cspaces_s3tc_dxt1_rgba; + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT1; + } + break; + case FOURCC('D', 'X', 'T', '2'): + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT2; + prop->alpha = EINA_TRUE; + prop->cspaces = cspaces_s3tc_dxt2; + break; + case FOURCC('D', 'X', 'T', '3'): + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT3; + prop->alpha = EINA_TRUE; + prop->cspaces = cspaces_s3tc_dxt5; + break; + case FOURCC('D', 'X', 'T', '4'): + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT4; + prop->alpha = EINA_TRUE; + prop->cspaces = cspaces_s3tc_dxt4; + break; + case FOURCC('D', 'X', 'T', '5'): + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT5; + prop->alpha = EINA_TRUE; + prop->cspaces = cspaces_s3tc_dxt5; + break; + case FOURCC('D', 'X', '1', '0'): + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + FAIL(); + default: + // TODO: Implement decoding support for uncompressed formats + FAIL(); + } + loader->pf.rgb_bitcount = _dword_read(&m); + loader->pf.r_mask = _dword_read(&m); + loader->pf.g_mask = _dword_read(&m); + loader->pf.b_mask = _dword_read(&m); + loader->pf.a_mask = _dword_read(&m); + caps = _dword_read(&m); + if ((caps & DDSCAPS_TEXTURE) == 0) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + FAIL(); + } + caps2 = _dword_read(&m); + if (caps2 != 0) + { + // Cube maps not supported + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + FAIL(); + } + // Since the rest is unused, just ignore it. + + loader->stride = ((width + 3) >> 2) * loader->block_size; + loader->data_size = loader->stride * ((height + 3) >> 2); + if (loader->data_size != pitchOrLinearSize) + FAIL(); // Invalid size! + + // Check file size + if (eina_file_size_get(loader->f) < (DDS_HEADER_SIZE + loader->data_size)) + FAIL(); + + prop->h = height; + prop->w = width; + prop->borders.l = 4; + prop->borders.t = 4; + prop->borders.r = 4 - (prop->w & 0x3); + prop->borders.b = 4 - (prop->h & 0x3); + *error = EVAS_LOAD_ERROR_NONE; + +on_error: + eina_file_map_free(loader->f, map); + return (*error == EVAS_LOAD_ERROR_NONE); +} + +static Eina_Bool +_dds_data_load(Evas_Loader_Internal *loader, Evas_Image_Property *prop, + unsigned char *map, void *pixels, int *error) +{ + const unsigned char *src; + int bsize = 16, srcstride, dststride, w, h; + unsigned char *dst; + + void (* flip) (unsigned char *, const unsigned char *, int) = NULL; + + *error = EVAS_LOAD_ERROR_GENERIC; + + if (loader->format != prop->cspace) + FAIL(); + + switch (loader->format) + { + case EVAS_COLORSPACE_RGB_S3TC_DXT1: + case EVAS_COLORSPACE_RGBA_S3TC_DXT1: + flip = s3tc_encode_dxt1_flip; + bsize = 8; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT2: + flip = s3tc_encode_dxt2_rgba_flip; + bsize = 16; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT3: + flip = s3tc_encode_dxt3_rgba_flip; + bsize = 16; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT4: + flip = s3tc_encode_dxt4_rgba_flip; + bsize = 16; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT5: + flip = s3tc_encode_dxt5_rgba_flip; + bsize = 16; + break; + default: FAIL(); + } + + src = map + DDS_HEADER_SIZE; + w = prop->w; + h = prop->h; + srcstride = ((prop->w + 3) / 4) * bsize; + dststride = ((prop->w + prop->borders.l + prop->borders.r) / 4) * bsize; + + // asserts + EINA_SAFETY_ON_FALSE_GOTO(prop->borders.l == 4, on_error); + EINA_SAFETY_ON_FALSE_GOTO(prop->borders.t == 4, on_error); + EINA_SAFETY_ON_FALSE_GOTO(prop->borders.r == (4 - (w & 0x3)), on_error); + EINA_SAFETY_ON_FALSE_GOTO(prop->borders.b == (4 - (h & 0x3)), on_error); + + if (eina_file_size_get(loader->f) < + (size_t) (DDS_HEADER_SIZE + srcstride * h / 4)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + // First, copy the real data + for (int y = 0; y < h; y += 4) + { + dst = ((unsigned char *) pixels) + ((y / 4) + 1) * dststride + bsize; + memcpy(dst, src, srcstride); + src += srcstride; + } + // Top + for (int x = 0; x < w; x += 4) + { + src = map + DDS_HEADER_SIZE + (x / 4) * bsize; + dst = ((unsigned char *) pixels) + ((x / 4) + 1) * bsize; + flip(dst, src, EINA_TRUE); + } + // Left + for (int y = 0; y < h; y += 4) + { + src = map + DDS_HEADER_SIZE + (y / 4) * srcstride; + dst = ((unsigned char *) pixels) + ((y / 4) + 1) * dststride; + flip(dst, src, EINA_FALSE); + } + // Top-left + dst = pixels; + src = dst + bsize; + flip(dst, src, EINA_FALSE); + // Right + if ((prop->w & 0x3) == 0) + { + for (int y = 0; y < h; y += 4) + { + src = map + DDS_HEADER_SIZE + ((y / 4) + 1) * srcstride - bsize; + dst = ((unsigned char *) pixels) + ((y / 4) + 2) * dststride - bsize; + flip(dst, src, EINA_FALSE); + } + // Top-right + dst = ((unsigned char *) pixels) + dststride - bsize; + src = dst - bsize; + flip(dst, src, EINA_FALSE); + } + // Bottom + if ((prop->h & 0x3) == 0) + { + for (int x = 0; x < w; x += 4) + { + src = map + DDS_HEADER_SIZE + ((h / 4) - 1) * srcstride + (x / 4) * bsize; + dst = ((unsigned char *) pixels) + ((h / 4) + 1) * dststride + ((x / 4) + 1) * bsize; + flip(dst, src, EINA_TRUE); + } + // Bottom-left + dst = ((unsigned char *) pixels) + ((h / 4) + 1) * dststride; + src = dst + bsize; + flip(dst, src, EINA_FALSE); + if ((prop->w & 0x3) == 0) + { + // Bottom-right + dst = ((unsigned char *) pixels) + ((h / 4) + 2) * dststride - bsize; + src = dst - bsize; + flip(dst, src, EINA_FALSE); + } + } + + *error = EVAS_LOAD_ERROR_NONE; + +on_error: + eina_file_map_free(loader->f, (void *) map); + return (*error == EVAS_LOAD_ERROR_NONE); +} + +Eina_Bool +evas_image_load_file_data_dds(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + void (*func) (unsigned int *bgra, const unsigned char *s3tc) = NULL; + Evas_Loader_Internal *loader = loader_data; + unsigned int *pix = pixels; + unsigned char *map = NULL; + const unsigned char *src; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + map = eina_file_map_all(loader->f, EINA_FILE_WILLNEED); + if (!map) + return EINA_FALSE; + + src = map + DDS_HEADER_SIZE; + if (eina_file_size_get(loader->f) < (DDS_HEADER_SIZE + loader->data_size)) + FAIL(); + + if (prop->cspace != EVAS_COLORSPACE_ARGB8888) + return _dds_data_load(loader, prop, map, pixels, error); + + // Decode to BGRA + switch (loader->format) + { + case EVAS_COLORSPACE_RGB_S3TC_DXT1: + func = s3tc_decode_dxt1_rgb; + prop->premul = EINA_FALSE; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT1: + func = s3tc_decode_dxt1_rgba; + prop->premul = EINA_FALSE; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT2: + func = s3tc_decode_dxt2_rgba; + prop->premul = EINA_FALSE; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT3: + func = s3tc_decode_dxt3_rgba; + prop->premul = EINA_TRUE; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT4: + func = s3tc_decode_dxt4_rgba; + prop->premul = EINA_FALSE; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT5: + func = s3tc_decode_dxt5_rgba; + prop->premul = EINA_TRUE; + break; + default: + FAIL(); + } + if (!func) FAIL(); + + for (unsigned int y = 0; y < prop->h; y += 4) + { + int blockh = prop->h - y; + if (blockh > 4) blockh = 4; + + for (unsigned int x = 0; x < prop->w; x += 4) + { + unsigned int bgra[16]; + int k, j; + + func(bgra, src); + src += loader->block_size; + + j = prop->w - x; + if (j > 4) j = 4; + for (k = 0; k < blockh; k++) + { + memcpy(pix + (((y + k) * prop->w) + x), bgra + (k * 4), + j * sizeof (unsigned int)); + }; + } + } + + *error = EVAS_LOAD_ERROR_NONE; + +on_error: + eina_file_map_free(loader->f, (void *) map); + return (*error == EVAS_LOAD_ERROR_NONE); +} + +Evas_Image_Load_Func evas_image_load_dds_func = +{ + evas_image_load_file_open_dds, + evas_image_load_file_close_dds, + evas_image_load_file_head_dds, + evas_image_load_file_data_dds, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_dds_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "dds", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, dds); + +#ifndef EVAS_STATIC_BUILD_DDS +EVAS_EINA_MODULE_DEFINE(image_loader, dds); +#endif diff --git a/src/modules/evas/image_loaders/dds/s3tc.h b/src/modules/evas/image_loaders/dds/s3tc.h new file mode 100644 index 0000000000..3bc965db48 --- /dev/null +++ b/src/modules/evas/image_loaders/dds/s3tc.h @@ -0,0 +1,17 @@ +#ifndef EFL_S3TC_H +#define EFL_S3TC_H + +void s3tc_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt1_rgba(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt2_rgba(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt3_rgba(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt4_rgba(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt5_rgba(unsigned int *bgra, const unsigned char *s3tc); + +void s3tc_encode_dxt1_flip(unsigned char *dest, const unsigned char *orig, int vflip); +void s3tc_encode_dxt2_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip); +void s3tc_encode_dxt3_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip); +void s3tc_encode_dxt4_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip); +void s3tc_encode_dxt5_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip); + +#endif // EFL_S3TC_H diff --git a/src/modules/evas/image_loaders/dds/s3tc_decoder.c b/src/modules/evas/image_loaders/dds/s3tc_decoder.c new file mode 100644 index 0000000000..f934fbd4a3 --- /dev/null +++ b/src/modules/evas/image_loaders/dds/s3tc_decoder.c @@ -0,0 +1,219 @@ +#include "s3tc.h" + +// For INTERP_256 and INTERP_RGB_256 +#include "evas_common_private.h" +#include "evas_blend_ops.h" + +// From evas_convert_colorspace.c +#define CONVERT_RGB_565_TO_RGB_888(s) \ + (((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | \ + ((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | \ + ((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000))) + +#define ALPHA4(a) (((a) << 4) | (a)) + +static void +_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc, + unsigned int amask, Eina_Bool dxt1, Eina_Bool alpha) +{ + unsigned short color0, color1; + unsigned int colors[4]; + unsigned int bits; + + color0 = s3tc[0] | (s3tc[1] << 8); + color1 = s3tc[2] | (s3tc[3] << 8); + + colors[0] = amask | CONVERT_RGB_565_TO_RGB_888(color0); + colors[1] = amask | CONVERT_RGB_565_TO_RGB_888(color1); + if (!dxt1 || (color0 > color1)) + { + // This is what's not supported by S2TC. + colors[2] = amask | INTERP_RGB_256((2*256)/3, colors[0], colors[1]); + colors[3] = amask | INTERP_RGB_256((1*256)/3, colors[0], colors[1]); + } + else + { + colors[2] = amask | INTERP_RGB_256(128, colors[0], colors[1]); + colors[3] = (alpha ? 0x00000000 : amask); + } + + bits = s3tc[4] + ((s3tc[5] + ((s3tc[6] + (s3tc[7] << 8)) << 8)) << 8); + for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) + { + int idx = bits & 0x3; + bgra[(j * 4) + i] = colors[idx]; + bits >>= 2; + } +} + +static void +_decode_alpha4(unsigned int *bgra, const unsigned char *s3tc) +{ + for (int k = 0; k < 16; k += 2) + { + unsigned int a0 = ALPHA4((*s3tc) & 0x0F); + unsigned int a1 = ALPHA4(((*s3tc) & 0xF0) >> 4); + *bgra++ |= (a0 << 24); + *bgra++ |= (a1 << 24); + s3tc++; + } +} + +static void +_decode_dxt_alpha(unsigned int *bgra, const unsigned char *s3tc) +{ + unsigned char a0 = s3tc[0]; + unsigned char a1 = s3tc[1]; + unsigned long long bits = 0ull; + unsigned char alpha[8]; + + for (int k = 5; k >= 0; k--) + bits = (bits << 8) | s3tc[k + 2]; + + alpha[0] = a0; + alpha[1] = a1; + + if (a0 > a1) + { + for (int k = 0; k < 6; k++) + alpha[2 + k] = ((6 - k) * a0 + (k + 1) * a1) / 7; + } + else + { + for (int k = 0; k < 4; k++) + alpha[2 + k] = ((4 - k) * a0 + (k + 1) * a1) / 5; + alpha[6] = 0; + alpha[7] = 255; + } + + for (int k = 0; k < 16; k++) + { + int index = (int) (bits & 0x7ull); + *bgra++ |= (alpha[index] << 24); + bits >>= 3; + } +} + +void +s3tc_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc, 0xFF000000, EINA_TRUE, EINA_FALSE); +} + +void +s3tc_decode_dxt1_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc, 0xFF000000, EINA_TRUE, EINA_TRUE); +} + +void +s3tc_decode_dxt2_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc + 8, 0x0, EINA_FALSE, EINA_FALSE); + _decode_alpha4(bgra, s3tc); +} + +void +s3tc_decode_dxt3_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc + 8, 0x0, EINA_FALSE, EINA_FALSE); + _decode_alpha4(bgra, s3tc); +} + +void +s3tc_decode_dxt4_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc + 8, 0x0, EINA_FALSE, EINA_FALSE); + _decode_dxt_alpha(bgra, s3tc); +} + +void +s3tc_decode_dxt5_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc + 8, 0x0, EINA_FALSE, EINA_FALSE); + _decode_dxt_alpha(bgra, s3tc); +} + +/* Fast re-encode functions to flip S3TC blocks */ + +static inline unsigned char +_byte_2222_flip(unsigned char src) +{ + return ((src & (0x3 << 6)) >> 6) | + ((src & (0x3 << 4)) >> 2) | + ((src & (0x3 << 2)) << 2) | + ((src & 0x3) << 6); +} + +static inline unsigned char +_byte_44_flip(unsigned char src) +{ + return ((src & 0xF0) >> 4) | ((src & 0x0F) << 4); +} + +void +s3tc_encode_dxt1_flip(unsigned char *dest, const unsigned char *orig, int vflip) +{ + dest[0] = orig[0]; + dest[1] = orig[1]; + dest[2] = orig[2]; + dest[3] = orig[3]; + + if (vflip) + { + dest[4] = orig[7]; + dest[5] = orig[6]; + dest[6] = orig[5]; + dest[7] = orig[4]; + } + else + { + dest[4] = _byte_2222_flip(orig[4]); + dest[5] = _byte_2222_flip(orig[5]); + dest[6] = _byte_2222_flip(orig[6]); + dest[7] = _byte_2222_flip(orig[7]); + } +} + +void +s3tc_encode_dxt2_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip) +{ + if (vflip) + { + dest[0] = orig[6]; + dest[1] = orig[7]; + dest[2] = orig[4]; + dest[3] = orig[5]; + dest[4] = orig[2]; + dest[5] = orig[3]; + dest[6] = orig[0]; + dest[7] = orig[1]; + } + else + { + for (int k = 0; k < 8; k += 2) + { + dest[0+k] = _byte_44_flip(orig[1+k]); + dest[1+k] = _byte_44_flip(orig[0+k]); + } + } + s3tc_encode_dxt1_flip(dest + 8, orig + 8, vflip); +} + +void +s3tc_encode_dxt3_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip) +{ + s3tc_encode_dxt2_rgba_flip(dest, orig, vflip); +} + +void s3tc_encode_dxt4_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip) +{ + s3tc_encode_dxt1_flip(dest, orig, vflip); + s3tc_encode_dxt1_flip(dest + 8, orig + 8, vflip); +} + +void s3tc_encode_dxt5_rgba_flip(unsigned char *dest, const unsigned char *orig, int vflip) +{ + s3tc_encode_dxt4_rgba_flip(dest, orig, vflip); +} diff --git a/src/modules/evas/image_loaders/eet/evas_image_load_eet.c b/src/modules/evas/image_loaders/eet/evas_image_load_eet.c new file mode 100644 index 0000000000..3c8e49bcb3 --- /dev/null +++ b/src/modules/evas/image_loaders/eet/evas_image_load_eet.c @@ -0,0 +1,245 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" /* so that EAPI in Eet.h is correctly defined */ +#endif + +#include <Eet.h> + +#include "evas_common_private.h" +#include "evas_private.h" + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eet_File *ef; + const char *key; +}; + +static void * +evas_image_load_file_open_eet(Eina_File *f, Eina_Stringshare *key, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error) +{ + Evas_Loader_Internal *loader; + + if (!key) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return NULL; + } + + loader = calloc(1, sizeof (Evas_Loader_Internal)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + loader->ef = eet_mmap(f); + if (!loader->ef) + { + free(loader); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return NULL; + } + + loader->key = eina_stringshare_ref(key); + return loader; +} + +static void +evas_image_load_file_close_eet(void *loader_data) +{ + Evas_Loader_Internal *loader = loader_data; + + eet_close(loader->ef); + eina_stringshare_del(loader->key); + free(loader); +} + +static inline Eina_Bool +_evas_image_load_return_error(int err, int *error) +{ + *error = err; + return EINA_FALSE; +} + +static const Evas_Colorspace cspaces_etc1[2] = { + EVAS_COLORSPACE_ETC1, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_etc1_alpha[] = { + EVAS_COLORSPACE_ETC1_ALPHA, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_etc2_rgb[] = { + EVAS_COLORSPACE_RGB8_ETC2, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_etc2_rgba[] = { + EVAS_COLORSPACE_RGBA8_ETC2_EAC, + EVAS_COLORSPACE_ARGB8888 +}; + +static Eina_Bool +evas_image_load_file_head_eet(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + int a, compression, quality; + Eet_Image_Encoding lossy; + const Eet_Colorspace *cspaces = NULL; + int ok; + + ok = eet_data_image_header_read(loader->ef, loader->key, + &prop->w, &prop->h, &a, &compression, &quality, &lossy); + if (!ok) + return _evas_image_load_return_error(EVAS_LOAD_ERROR_DOES_NOT_EXIST, error); + if (IMG_TOO_BIG(prop->w, prop->h)) + return _evas_image_load_return_error(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED, error); + + if (eet_data_image_colorspace_get(loader->ef, loader->key, NULL, &cspaces)) + { + unsigned int i; + + if (cspaces != NULL) + { + for (i = 0; cspaces[i] != EET_COLORSPACE_ARGB8888; i++) + { + if (cspaces[i] == EET_COLORSPACE_ETC1) + { + prop->cspaces = cspaces_etc1; + break; + } + else if (cspaces[i] == EET_COLORSPACE_ETC1_ALPHA) + { + prop->cspaces = cspaces_etc1_alpha; + break; + } + else if (cspaces[i] == EET_COLORSPACE_RGB8_ETC2) + { + prop->cspaces = cspaces_etc2_rgb; + break; + } + else if (cspaces[i] == EET_COLORSPACE_RGBA8_ETC2_EAC) + { + prop->cspaces = cspaces_etc2_rgba; + break; + } + } + } + } + + prop->alpha = !!a; + *error = EVAS_LOAD_ERROR_NONE; + + return EINA_TRUE; +} + +Eina_Bool +evas_image_load_file_data_eet(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + int alpha, compression, quality, ok; + Eet_Image_Encoding lossy; + DATA32 *body, *p, *end; + DATA32 nas = 0; + Eet_Colorspace cspace; + + switch (prop->cspace) + { + case EVAS_COLORSPACE_ETC1: cspace = EET_COLORSPACE_ETC1; break; + case EVAS_COLORSPACE_ETC1_ALPHA: cspace = EET_COLORSPACE_ETC1_ALPHA; break; + case EVAS_COLORSPACE_RGB8_ETC2: cspace = EET_COLORSPACE_RGB8_ETC2; break; + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: cspace = EET_COLORSPACE_RGBA8_ETC2_EAC; break; + case EVAS_COLORSPACE_ARGB8888: cspace = EET_COLORSPACE_ARGB8888; break; + default: + return _evas_image_load_return_error(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED, error); + } + + ok = eet_data_image_read_to_cspace_surface_cipher(loader->ef, loader->key, NULL, 0, 0, + pixels, prop->w, prop->h, prop->w * 4, + cspace, + &alpha, &compression, &quality, &lossy); + if (!ok) + return _evas_image_load_return_error(EVAS_LOAD_ERROR_GENERIC, error); + + if (alpha) + { + prop->alpha = 1; + body = pixels; + + end = body + (prop->w * prop->h); + for (p = body; p < end; p++) + { + DATA32 r, g, b, a; + + a = A_VAL(p); + r = R_VAL(p); + g = G_VAL(p); + b = B_VAL(p); + if ((a == 0) || (a == 255)) nas++; + if (r > a) r = a; + if (g > a) g = a; + if (b > a) b = a; + *p = ARGB_JOIN(a, r, g, b); + } + if ((ALPHA_SPARSE_INV_FRACTION * nas) >= (prop->w * prop->h)) + prop->alpha_sparse = 1; + } +// result is already premultiplied now if u compile with edje +// evas_common_image_premul(im); + *error = EVAS_LOAD_ERROR_NONE; + + return EINA_TRUE; +} + +Evas_Image_Load_Func evas_image_load_eet_func = +{ + evas_image_load_file_open_eet, + evas_image_load_file_close_eet, + evas_image_load_file_head_eet, + evas_image_load_file_data_eet, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + eet_init(); + em->functions = (void *)(&evas_image_load_eet_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + eet_shutdown(); +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "eet", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, eet); + +#ifndef EVAS_STATIC_BUILD_EET +EVAS_EINA_MODULE_DEFINE(image_loader, eet); +#endif diff --git a/src/modules/evas/image_loaders/generic/evas_image_load_generic.c b/src/modules/evas/image_loaders/generic/evas_image_load_generic.c new file mode 100644 index 0000000000..78488d1036 --- /dev/null +++ b/src/modules/evas/image_loaders/generic/evas_image_load_generic.c @@ -0,0 +1,479 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +#include <stdio.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + const char *key; + Evas_Image_Load_Opts *opts; +}; + +static Eina_Bool +illegal_char(const char *str) +{ + const char *p; + + for (p = str; *p; p++) + { + if (*p < '-') return EINA_TRUE; + if (*p == '/') return EINA_TRUE; + if (*p == ';') return EINA_TRUE; + if (*p == ':') return EINA_TRUE; + if (*p == '<') return EINA_TRUE; + if (*p == '>') return EINA_TRUE; + if (*p == '?') return EINA_TRUE; + if (*p == '[') return EINA_TRUE; + if (*p == '\\') return EINA_TRUE; + if (*p == ']') return EINA_TRUE; + if (*p == '`') return EINA_TRUE; + if (*p == '{') return EINA_TRUE; + if (*p == '|') return EINA_TRUE; + if (*p == '}') return EINA_TRUE; + if (*p == '~') return EINA_TRUE; + if (*p == 0x7f) return EINA_TRUE; + } + return EINA_FALSE; +} + +static void +escape_copy(const char *src, char *dst) +{ + const char *s; + char *d; + + for (s = src, d = dst; *s; s++, d++) + { + // FIXME: escape tab, newline linefeed and friends + if ((*s == ' ') || + (*s == '!') || + (*s == '"') || + (*s == '#') || + (*s == '$') || + (*s == '%') || + (*s == '&') || + (*s == '\'') || + (*s == '(') || + (*s == ')') || + (*s == '*') || + (*s == '[') || + (*s == '\\') || + (*s == ']') || + (*s == '`') || + (*s == '{') || + (*s == '|') || + (*s == '}') || + (*s == '~')) + { + *d = '\\'; + d++; + } + *d = *s; + } + *d = 0; +} + +static void +dotcat(char *dest, const char *src) +{ + int len = strlen(dest); + const char *s; + char *d; + + for (d = dest + len, s = src; *s; d++, s++) *d = tolower(*s); + *d = 0; +} + +static Eina_Bool +_load(Eina_File *ef, const char *key, + Evas_Image_Property *prop, + Evas_Image_Load_Opts *opts, + void *pixels, + int *error, Eina_Bool get_data) +{ + Eina_Bool res = EINA_FALSE; + int w = 0, h = 0, alpha = 0; + const char *dot1 = NULL, *dot2 = NULL, *end, *p; + char *cmd = NULL, decoders[3][128], buf[4096]; + char *loader = "/evas/utils/evas_image_loader"; + char *img_loader = NULL; + const char *libdir; + // eg $libdir/evas/generic_loaders + int cmd_len, len, decoders_num = 0, try_count = 0; + int read_data = 0; + char *tmpfname = NULL, *shmfname = NULL; + DATA32 *body; + FILE *f = NULL; + + libdir = _evas_module_libdir_get(); + cmd_len = strlen(libdir); + cmd_len += strlen(loader); + img_loader = alloca(cmd_len + 1); + strcpy(img_loader, libdir); + strcat(img_loader, loader); + + // params excluding file, key and loadopts + cmd_len += 1024; + cmd_len += strlen(eina_file_filename_get(ef)) * 2; + if (key) cmd_len += strlen(key) * 2; + cmd = alloca(cmd_len + 1); + + len = strlen(eina_file_filename_get(ef)); + if (len < 1) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + end = eina_file_filename_get(ef) + len; + for (p = end - 1; p >= eina_file_filename_get(ef); p--) + { + if ((!dot1) && (*p == '.')) dot1 = p; + else if ((!dot2) && (*p == '.')) dot2 = p; + else if ((dot1) && (dot2)) break; + } + if (dot2) + { + // double extn not too long + if (((end - dot2) <= 10) && (!illegal_char(dot2))) + { + strncpy(&(decoders[decoders_num][0]), img_loader, 127); + decoders[decoders_num][127] = 0; + dotcat(&(decoders[decoders_num][0]), dot2); + decoders_num++; + } + // single extn not too long + if (((end - dot1) <= 5) && (!illegal_char(dot1))) + { + strncpy(&(decoders[decoders_num][0]), img_loader, 127); + decoders[decoders_num][127] = 0; + dotcat(&(decoders[decoders_num][0]), dot1); + decoders_num++; + } + strncpy(decoders[decoders_num], img_loader, 127); + decoders[decoders_num][127] = 0; + decoders_num++; + } + else if (dot1) + { + // single extn not too long + if (((end - dot1) <= 5) && (!illegal_char(dot1))) + { + strncpy(&(decoders[decoders_num][0]), img_loader, 127); + decoders[decoders_num][127] = 0; + dotcat(&(decoders[decoders_num][0]), dot1); + decoders_num++; + } + strncpy(decoders[decoders_num], img_loader, 127); + decoders[decoders_num][127] = 0; + decoders_num++; + } + else + { + strncpy(decoders[decoders_num], img_loader, 127); + decoders[decoders_num][127] = 0; + decoders_num++; + } + + for (try_count = 0; try_count < decoders_num; try_count++) + { + // FIXME: strcats could be more efficient, not that it matters much + // here as we are about to build a cmd to exec via a shell that + // will interpret shell stuff and path hunt that will then exec the + // program itself that will dynamically link that will again + // parse the arguments and finally do something... + if (access(decoders[try_count], X_OK)) continue; + + strcpy(cmd, decoders[try_count]); + strcat(cmd, " "); + // filename first arg + len = strlen(cmd); + escape_copy(eina_file_filename_get(ef), cmd + len); + if (!get_data) + { + strcat(cmd, " -head "); + } + if (key) + { + strcat(cmd, " -key "); + len = strlen(cmd); + escape_copy(key, cmd + len); + } + if (opts->scale_down_by > 1) + { + strcat(cmd, " -opt-scale-down-by "); + snprintf(buf, sizeof(buf), "%i", opts->scale_down_by); + strcat(cmd, buf); + } + if (opts->dpi > 0.0) + { + strcat(cmd, " -opt-dpi "); + snprintf(buf, sizeof(buf), "%i", (int)(opts->dpi * 1000.0)); + strcat(cmd, buf); + } + if ((opts->w > 0) && + (opts->h > 0)) + { + strcat(cmd, " -opt-size "); + snprintf(buf, sizeof(buf), "%i %i", opts->w, opts->h); + strcat(cmd, buf); + } + f = popen(cmd, "r"); + if (f) break; + } + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + while (fgets(buf, sizeof(buf), f)) + { + len = strlen(buf); + if (len > 0) + { + if (buf[len - 1] == '\n') buf[len - 1] = 0; + if (!strncmp(buf, "size ", 5)) + { + int tw = 0, th = 0; + + len = sscanf(buf, "%*s %i %i", &tw, &th); + if (len == 2) + { + if ((tw > 0) && (th > 0)) + { + w = tw; + h = th; + } + } + } + else if (!strncmp(buf, "alpha ", 6)) + { + int ta; + + len = sscanf(buf, "%*s %i", &ta); + if (len == 1) + { + alpha = ta; + } + } + else if (!strncmp(buf, "tmpfile ", 8)) + { + tmpfname = buf + 8; + goto getdata; + } +#ifdef HAVE_SHM_OPEN + else if (!strncmp(buf, "shmfile ", 8)) + { + shmfname = buf + 8; + goto getdata; + } +#endif + else if (!strncmp(buf, "data", 4)) + { + read_data = 1; + goto getdata; + } + else if (!strncmp(buf, "done", 4)) + { + read_data = 2; + goto getdata; + } + } + } +getdata: + if ((!read_data) && (!tmpfname) && (!shmfname)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + if (!get_data) + { + if (alpha) prop->alpha = 1; + prop->w = w; + prop->h = h; + } + else + { + if ((int)prop->w != w || + (int)prop->h != h) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + body = pixels; + + if ((tmpfname) || (shmfname)) + { + int fd = -1; + + // open + if (tmpfname) + fd = open(tmpfname, O_RDONLY, S_IRUSR); +#ifdef HAVE_SHM_OPEN + else if (shmfname) + fd = shm_open(shmfname, O_RDONLY, S_IRUSR); +#endif + if (fd >= 0) + { + void *addr; + + eina_mmap_safety_enabled_set(EINA_TRUE); + + // mmap + addr = mmap(NULL, w * h * sizeof(DATA32), + PROT_READ, MAP_SHARED, fd, 0); + if (addr != MAP_FAILED) + { + memcpy(body, addr, w * h * sizeof(DATA32)); + munmap(addr, w * h * sizeof(DATA32)); + } + // close + if (tmpfname) + { + close(fd); + unlink(tmpfname); + } +#ifdef HAVE_SHM_OPEN + else if (shmfname) + { + close(fd); + shm_unlink(shmfname); + } +#endif + } + else + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + } + else if (read_data) + { + if (fread(body, w * h * sizeof(DATA32), 1, f) != 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + } + } + + res = EINA_TRUE; + *error = EVAS_LOAD_ERROR_NONE; + + on_error: + if (f) pclose(f); + return res; +} + +static void * +evas_image_load_file_open_generic(Eina_File *f, Eina_Stringshare *key, + 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->key = eina_stringshare_ref(key); + loader->opts = opts; + return loader; +} + +static void +evas_image_load_file_close_generic(void *loader_data) +{ + Evas_Loader_Internal *loader = loader_data; + + eina_stringshare_del(loader->key); + free(loader); +} + +static Eina_Bool +evas_image_load_file_head_generic(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + + return _load(loader->f, loader->key, prop, loader->opts, NULL, error, EINA_FALSE); +} + +static Eina_Bool +evas_image_load_file_data_generic(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + + return _load(loader->f, loader->key, prop, loader->opts, pixels, error, EINA_TRUE); +} + +Evas_Image_Load_Func evas_image_load_generic_func = +{ + evas_image_load_file_open_generic, + evas_image_load_file_close_generic, + evas_image_load_file_head_generic, + evas_image_load_file_data_generic, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_generic_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "generic", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, generic); + +#ifndef EVAS_STATIC_BUILD_GENERIC +EVAS_EINA_MODULE_DEFINE(image_loader, generic); +#endif diff --git a/src/modules/evas/image_loaders/gif/evas_image_load_gif.c b/src/modules/evas/image_loaders/gif/evas_image_load_gif.c new file mode 100644 index 0000000000..acdf5ab8e1 --- /dev/null +++ b/src/modules/evas/image_loaders/gif/evas_image_load_gif.c @@ -0,0 +1,914 @@ +#include "evas_common_private.h" +#include "evas_private.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include <gif_lib.h> + +typedef struct _Frame_Info Frame_Info; +typedef struct _Loader_Info Loader_Info; +typedef struct _File_Info File_Info; + +struct _File_Info +{ + unsigned char *map; + int pos, len; // yes - gif uses ints for file sizes. +}; + +struct _Loader_Info +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; + Evas_Image_Animated *animated; + GifFileType *gif; + int imgnum; + File_Info fi; +}; + +struct _Frame_Info +{ + int x, y, w, h; + unsigned short delay; // delay time in 1/100ths of a sec + short transparent : 10; // -1 == not, anything else == index + short dispose : 6; // 0, 1, 2, 3 (others invalid) + short interlace : 1; // interlaced or not +}; + +#ifndef MIN +# define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#define LOADERR(x) \ +do { \ + *error = (x); \ + goto on_error; \ +} while (0) +#define PIX(_x, _y) rows[yin + _y][xin + _x] +#define CMAP(_v) cmap->Colors[_v] +#define PIXLK(_p) ARGB_JOIN(0xff, CMAP(_p).Red, CMAP(_p).Green, CMAP(_p).Blue) + +// utility funcs... + +// brute force find frame index - gifs are normally saml so ok for now +static Image_Entry_Frame * +_find_frame(Evas_Image_Animated *animated, int index) +{ + Eina_List *l; + Image_Entry_Frame *frame; + + if (!animated->frames) return NULL; + EINA_LIST_FOREACH(animated->frames, l, frame) + { + if (frame->index == index) return frame; + } + return NULL; +} + +// fill in am image with a specific rgba color value +static void +_fill_image(DATA32 *data, int rowpix, DATA32 val, int x, int y, int w, int h) +{ + int xx, yy; + DATA32 *p; + + for (yy = 0; yy < h; yy++) + { + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) + { + *p = val; + p++; + } + } +} + +// fix coords and work out an x and y inset in orig data if out of image bounds +static void +_clip_coords(int imw, int imh, int *xin, int *yin, + int x0, int y0, int w0, int h0, + int *x, int *y, int *w, int *h) +{ + if (x0 < 0) + { + w0 += x0; + *xin = -x0; + x0 = 0; + } + if ((x0 + w0) > imw) w0 = imw - x0; + if (y0 < 0) + { + h0 += y0; + *yin = -y0; + y0 = 0; + } + if ((y0 + h0) > imh) h0 = imh - y0; + *x = x0; + *y = y0; + *w = w0; + *h = h0; +} + +// file a rgba data pixle blob with a frame color (bg or trans) depending... +static void +_fill_frame(DATA32 *data, int rowpix, GifFileType *gif, Frame_Info *finfo, + int x, int y, int w, int h) +{ + // solid color fill for pre frame region + if (finfo->transparent < 0) + { + ColorMapObject *cmap; + int bg; + + // work out color to use from cmap + if (gif->Image.ColorMap) cmap = gif->Image.ColorMap; + else cmap = gif->SColorMap; + bg = gif->SBackGroundColor; + // and do the fill + _fill_image + (data, rowpix, + ARGB_JOIN(0xff, CMAP(bg).Red, CMAP(bg).Green, CMAP(bg).Blue), + x, y, w, h); + } + // fill in region with 0 (transparent) + else + _fill_image(data, rowpix, 0, x, y, w, h); +} + +// store common fields from gif file info into frame info +static void +_store_frame_info(GifFileType *gif, Frame_Info *finfo) +{ + finfo->x = gif->Image.Left; + finfo->y = gif->Image.Top; + finfo->w = gif->Image.Width; + finfo->h = gif->Image.Height; + finfo->interlace = gif->Image.Interlace; +} + +// check if image fills "screen space" and if so, if it is transparent +// at all then the image could be transparent - OR if image doesnt fill, +// then it could be trasnparent (full coverage of screen). some gifs will +// be recognized as solid here for faster rendering, but not all. +static void +_check_transparency(Eina_Bool *full, Frame_Info *finfo, int w, int h) +{ + if ((finfo->x == 0) && (finfo->y == 0) && + (finfo->w == w) && (finfo->h == h)) + { + if (finfo->transparent >= 0) *full = EINA_FALSE; + } + else *full = EINA_FALSE; +} + +// allocate frame and frame info and append to list and store fields +static Frame_Info * +_new_frame(Evas_Image_Animated *animated, + int transparent, int dispose, int delay, + int index) +{ + Image_Entry_Frame *frame; + Frame_Info *finfo; + + // allocate frame and frame info data (MUSt be separate) + frame = calloc(1, sizeof(Image_Entry_Frame)); + if (!frame) return NULL; + finfo = calloc(1, sizeof(Frame_Info)); + if (!finfo) + { + free(frame); + return NULL; + } + // record transparent index to be used or -1 if none + // for this SPECIFIC frame + finfo->transparent = transparent; + // record dispose mode (3 bits) + finfo->dispose = dispose; + // record delay (2 bytes so max 65546 /100 sec) + finfo->delay = delay; + // record the index number we are at + frame->index = index; + // that frame is stored AT image/screen size + frame->info = finfo; + animated->frames = eina_list_append(animated->frames, frame); + return finfo; +} + +// decode a gif image into rows then expand to 32bit into the destination +// data pointer +static Eina_Bool +_decode_image(GifFileType *gif, DATA32 *data, int rowpix, int xin, int yin, + int transparent, int x, int y, int w, int h, Eina_Bool fill) +{ + int intoffset[] = { 0, 4, 2, 1 }; + int intjump[] = { 8, 8, 4, 2 }; + int i, xx, yy, pix; + GifRowType *rows; + Eina_Bool ret = EINA_FALSE; + ColorMapObject *cmap; + DATA32 *p; + + // build a blob of memory to have pointers to rows of pixels + // AND store the decoded gif pixels (1 byte per pixel) as welll + rows = malloc((h * sizeof(GifRowType)) + (w * h * sizeof(GifPixelType))); + if (!rows) goto on_error; + + // fill in the pointers at the start + for (yy = 0; yy < h; yy++) + { + rows[yy] = ((unsigned char *)rows) + (h * sizeof(GifRowType)) + + (yy * w * sizeof(GifPixelType)); + } + + // if give is interlaced, walk interlace pattern and decode into rows + if (gif->Image.Interlace) + { + for (i = 0; i < 4; i++) + { + for (yy = intoffset[i]; yy < h; yy += intjump[i]) + { + if (DGifGetLine(gif, rows[yy], w) != GIF_OK) + goto on_error; + } + } + } + // normal top to bottom - decode into rows + else + { + for (yy = 0; yy < h; yy++) + { + if (DGifGetLine(gif, rows[yy], w) != GIF_OK) + goto on_error; + } + } + + // work out what colormap to use + if (gif->Image.ColorMap) cmap = gif->Image.ColorMap; + else cmap = gif->SColorMap; + + // if we need to deal with transparent pixels at all... + if (transparent >= 0) + { + // if we are told to FILL (overwrite with transparency kept) + if (fill) + { + for (yy = 0; yy < h; yy++) + { + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) + { + pix = PIX(xx, yy); + if (pix != transparent) *p = PIXLK(pix); + else *p = 0; + p++; + } + } + } + // paste on top with transparent pixels untouched + else + { + for (yy = 0; yy < h; yy++) + { + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) + { + pix = PIX(xx, yy); + if (pix != transparent) *p = PIXLK(pix); + p++; + } + } + } + } + else + { + // walk pixels without worring about transparency at all + for (yy = 0; yy < h; yy++) + { + p = data + ((y + yy) * rowpix) + x; + for (xx = 0; xx < w; xx++) + { + pix = PIX(xx, yy); + *p = PIXLK(pix); + p++; + } + } + } + ret = EINA_TRUE; + +on_error: + free(rows); + return ret; +} + +// flush out older rgba frame images to save memory but skip current frame +// and previous frame (prev needed for dispose mode 3) +static void +_flush_older_frames(Evas_Image_Animated *animated, + int w, int h, + Image_Entry_Frame *thisframe, + Image_Entry_Frame *prevframe) +{ + Eina_List *l; + Image_Entry_Frame *frame; + // target is the amount of memory we want to be under for stored frames + int total = 0, target = 512 * 1024; + + // total up the amount of memory used by stored frames for this image + EINA_LIST_FOREACH(animated->frames, l, frame) + { + if (frame->data) total++; + } + total *= (w * h * sizeof(DATA32)); + // if we use less than target (512k) for frames - dont flush + if (total < target) return; + // clean oldest frames first and go until below target or until we loop + // around back to this frame (curent) + EINA_LIST_FOREACH(animated->frames, l, frame) + { + if (frame == thisframe) break; + } + if (!l) return; + // start on next frame after thisframe + l = l->next; + // handle wrap to start + if (!l) l = animated->frames; + // now walk until we hit thisframe again... then stop walk. + while (l) + { + frame = l->data; + if (frame == thisframe) break; + if (frame->data) + { + if ((frame != thisframe) && (frame != prevframe)) + { + free(frame->data); + frame->data = NULL; + // subtract memory used and if below target - stop flush + total -= (w * h * sizeof(DATA32)); + if (total < target) break; + } + } + // go to next - handle wrap to start + l = l->next; + if (!l) l = animated->frames; + } +} + +static int +_file_read(GifFileType *gft, GifByteType *buf, int len) +{ + File_Info *fi = gft->UserData; + + if (fi->pos >= fi->len) return 0; // if at or past end - no + if ((fi->pos + len) >= fi->len) len = fi->len - fi->pos; + memcpy(buf, fi->map + fi->pos, len); + fi->pos += len; + return len; +} + +static Eina_Bool +evas_image_load_file_head_gif2(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; + Eina_File *f = loader->f; + Eina_Bool ret = EINA_FALSE; + File_Info fi; + GifRecordType rec; + GifFileType *gif = NULL; + // it is possible which gif file have error midle of frames, + // in that case we should play gif file until meet error frame. + int imgnum = 0; + int loop_count = -1; + Frame_Info *finfo = NULL; + Eina_Bool full = EINA_TRUE; + + // init prop struct with some default null values + prop->w = 0; + prop->h = 0; + + // map the file and store/track info + fi.map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); + fi.len = eina_file_size_get(f); + fi.pos = 0; + + // actually ask libgif to open the file +#if GIFLIB_MAJOR >= 5 + gif = DGifOpen(&fi, _file_read, NULL); +#else + gif = DGifOpen(&fi, _file_read); +#endif + if (!gif) LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + // get the gif "screen size" (the actual image size) + prop->w = gif->SWidth; + prop->h = gif->SHeight; + // if size is invalid - abort here + if ((prop->w < 1) || (prop->h < 1) || + (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) || + IMG_TOO_BIG(prop->w, prop->h)) + { + if (IMG_TOO_BIG(prop->w, prop->h)) + LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED); + LOADERR(EVAS_LOAD_ERROR_GENERIC); + } + // walk through gif records in file to figure out info + do + { + if (DGifGetRecordType(gif, &rec) == GIF_ERROR) + { + // if we have a gif that ends part way through a sequence + // (or animation) consider it valid and just break - no error + if (imgnum > 1) break; + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + } + // get image description section + if (rec == IMAGE_DESC_RECORD_TYPE) + { + int img_code; + GifByteType *img; + + // get image desc + if (DGifGetImageDesc(gif) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + // skip decoding and just walk image to next + if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + // skip till next... + while (img) + { + img = NULL; + DGifGetCodeNext(gif, &img); + } + // store geometry in the last frame info data + if (finfo) + { + _store_frame_info(gif, finfo); + _check_transparency(&full, finfo, prop->w, prop->h); + } + // or if we dont have a finfo entry - create one even for stills + else + { + // allocate and save frame with field data + finfo = _new_frame(animated, -1, 0, 0, imgnum + 1); + if (!finfo) + LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED); + // store geometry info from gif image + _store_frame_info(gif, finfo); + // check for transparency/alpha + _check_transparency(&full, finfo, prop->w, prop->h); + } + imgnum++; + } + // we have an extension code block - for animated gifs for sure + else if (rec == EXTENSION_RECORD_TYPE) + { + int ext_code; + GifByteType *ext; + + ext = NULL; + // get the first extension entry + DGifGetExtension(gif, &ext_code, &ext); + while (ext) + { + // graphic control extension - for animated gif data + // and transparent index + flag + if (ext_code == 0xf9) + { + // create frame and store it in image + finfo = _new_frame + (animated, + (ext[1] & 1) ? ext[4] : -1, // transparency index + (ext[1] >> 2) & 0x7, // dispose mode + ((int)ext[3] << 8) | (int)ext[2], // delay + imgnum + 1); + if (!finfo) + LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED); + } + // netscape extension indicating loop count... + else if (ext_code == 0xff) /* application extension */ + { + if (!strncmp((char *)(&ext[1]), "NETSCAPE2.0", 11) || + !strncmp((char *)(&ext[1]), "ANIMEXTS1.0", 11)) + { + ext = NULL; + DGifGetExtensionNext(gif, &ext); + if (ext[1] == 0x01) + { + loop_count = ((int)ext[3] << 8) | (int)ext[2]; + if (loop_count > 0) loop_count++; + } + } + } + // and continue onto the next extension entry + ext = NULL; + DGifGetExtensionNext(gif, &ext); + } + } + } + while (rec != TERMINATE_RECORD_TYPE); + + // if the gif main says we have more than one image or our image counting + // says so, then this image is animated - indicate this + if ((gif->ImageCount > 1) || (imgnum > 1)) + { + animated->animated = 1; + animated->loop_count = loop_count; + animated->loop_hint = EVAS_IMAGE_ANIMATED_HINT_LOOP; + animated->frame_count = MIN(gif->ImageCount, imgnum); + } + if (!full) prop->alpha = 1; + animated->cur_frame = 1; + + // no errors in header scan etc. so set err and return value + *error = EVAS_LOAD_ERROR_NONE; + ret = EINA_TRUE; + +on_error: // jump here on any errors to clean up +#if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1)) + if (gif) DGifCloseFile(gif, NULL); +#else + if (gif) DGifCloseFile(gif); +#endif + if (fi.map) eina_file_map_free(f, fi.map); + return ret; +} + +static Eina_Bool +evas_image_load_file_data_gif2(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; + Eina_File *f = loader->f; + Eina_Bool ret = EINA_FALSE; + GifRecordType rec; + GifFileType *gif = NULL; + Image_Entry_Frame *frame; + int index = 0, imgnum = 0; + Frame_Info *finfo; + + // XXX: this is so wrong - storing current frame IN the image + // so we have to load multiple times to animate. what if the + // same image is shared/loaded in 2 ore more places AND animated + // there? + + // use index stored in image (XXX: yuk!) + index = animated->cur_frame; + // if index is invalid for animated image - error out + if ((animated->animated) && + ((index <= 0) || (index > animated->frame_count))) + LOADERR(EVAS_LOAD_ERROR_GENERIC); + // find the given frame index + frame = _find_frame(animated, index); + if (frame) + { + if ((frame->loaded) && (frame->data)) + { + // frame is already there and decoded - jump to end + goto on_ok; + } + } + else + LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); + +open_file: + // actually ask libgif to open the file + gif = loader->gif; + if (!gif) + { + // there was no file previously opened + // map the file and store/track info + loader->fi.map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!loader->fi.map) LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); + loader->fi.len = eina_file_size_get(f); + loader->fi.pos = 0; + +#if GIFLIB_MAJOR >= 5 + gif = DGifOpen(&(loader->fi), _file_read, NULL); +#else + gif = DGifOpen(&(loader->fi), _file_read); +#endif + // if gif open failed... get out of here + if (!gif) + { + if ((loader->fi.map) && (loader->f)) + eina_file_map_free(loader->f, loader->fi.map); + loader->fi.map = NULL; + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + } + loader->gif = gif; + loader->imgnum = 1; + } + + // if we want to go backwards, we likely need/want to re-decode from the + // start as we have nothnig to build on + if ((index > 0) && (index < loader->imgnum) && (animated->animated)) + { +#if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1)) + if (loader->gif) DGifCloseFile(loader->gif, NULL); +#else + if (loader->gif) DGifCloseFile(loader->gif); +#endif + if ((loader->fi.map) && (loader->f)) + eina_file_map_free(loader->f, loader->fi.map); + loader->gif = NULL; + loader->fi.map = NULL; + loader->imgnum = 0; + goto open_file; + } + + // our current position is the previous frame we decoded from the file + imgnum = loader->imgnum; + + // walk through gif records in file to figure out info + do + { + if (DGifGetRecordType(gif, &rec) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + if (rec == EXTENSION_RECORD_TYPE) + { + int ext_code; + GifByteType *ext; + + ext = NULL; + DGifGetExtension(gif, &ext_code, &ext); + while (ext) + { + ext = NULL; + DGifGetExtensionNext(gif, &ext); + } + } + // get image description section + else if (rec == IMAGE_DESC_RECORD_TYPE) + { + int xin = 0, yin = 0, x = 0, y = 0, w = 0, h = 0; + int img_code; + GifByteType *img; + Image_Entry_Frame *prevframe = NULL; + Image_Entry_Frame *thisframe = NULL; + + // get image desc + if (DGifGetImageDesc(gif) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + // get the previous frame entry AND the current one to fill in + prevframe = _find_frame(animated, imgnum - 1); + thisframe = _find_frame(animated, imgnum); + // if we have a frame AND we're animated AND we have no data... + if ((thisframe) && (!thisframe->data) && (animated->animated)) + { + Eina_Bool first = EINA_FALSE; + + // allocate it + thisframe->data = + malloc(prop->w * prop->h * sizeof(DATA32)); + if (!thisframe->data) + LOADERR(EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED); + // if we have no prior frame OR prior frame data... empty + if ((!prevframe) || (!prevframe->data)) + { + first = EINA_TRUE; + finfo = thisframe->info; + memset(thisframe->data, 0, + prop->w * prop->h * sizeof(DATA32)); + } + // we have a prior frame to copy data from... + else + { + finfo = prevframe->info; + + // fix coords of sub image in case it goes out... + _clip_coords(prop->w, prop->h, &xin, &yin, + finfo->x, finfo->y, finfo->w, finfo->h, + &x, &y, &w, &h); + // if dispose mode is not restore - then copy pre frame + if (finfo->dispose != 3) // GIF_DISPOSE_RESTORE + memcpy(thisframe->data, prevframe->data, + prop->w * prop->h * sizeof(DATA32)); + // if dispose mode is "background" then fill with bg + if (finfo->dispose == 2) // GIF_DISPOSE_BACKGND + _fill_frame(thisframe->data, prop->w, gif, + finfo, x, y, w, h); + else if (finfo->dispose == 3) // GIF_DISPOSE_RESTORE + { + Image_Entry_Frame *prevframe2; + + // we need to copy data from one frame back + // from the prev frame into the current frame + // (copy the whole image - at least the sample + // GifWin.cpp from libgif indicates this is what + // needs doing + prevframe2 = _find_frame(animated, imgnum - 2); + if (prevframe2) + memcpy(thisframe->data, prevframe2->data, + prop->w * prop->h * sizeof(DATA32)); + } + } + // now draw this frame on top + finfo = thisframe->info; + _clip_coords(prop->w, prop->h, &xin, &yin, + finfo->x, finfo->y, finfo->w, finfo->h, + &x, &y, &w, &h); + if (!_decode_image(gif, thisframe->data, prop->w, + xin, yin, finfo->transparent, + x, y, w, h, first)) + LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); + // mark as loaded and done + thisframe->loaded = EINA_TRUE; + // and flush old memory if needed (too much) + _flush_older_frames(animated, prop->w, prop->h, + thisframe, prevframe); + } + // if we hve a frame BUT the image is not animated... different + // path + else if ((thisframe) && (!thisframe->data) && + (!animated->animated)) + { + // if we don't have the data decoded yet - decode it + if ((!thisframe->loaded) || (!thisframe->data)) + { + // use frame info but we WONT allocate frame pixels + finfo = thisframe->info; + _clip_coords(prop->w, prop->h, &xin, &yin, + finfo->x, finfo->y, finfo->w, finfo->h, + &x, &y, &w, &h); + // clear out all pixels + _fill_frame(pixels, prop->w, gif, + finfo, 0, 0, prop->w, prop->h); + // and decode the gif with overwriting + if (!_decode_image(gif, pixels, prop->w, + xin, yin, finfo->transparent, + x, y, w, h, EINA_TRUE)) + LOADERR(EVAS_LOAD_ERROR_CORRUPT_FILE); + // mark as loaded and done + thisframe->loaded = EINA_TRUE; + } + // flush mem we don't need (at expense of decode cpu) + } + else + { + // skip decoding and just walk image to next + if (DGifGetCode(gif, &img_code, &img) == GIF_ERROR) + LOADERR(EVAS_LOAD_ERROR_UNKNOWN_FORMAT); + while (img) + { + img = NULL; + DGifGetCodeNext(gif, &img); + } + } + imgnum++; + // if we found the image we wanted - get out of here + if (imgnum > index) break; + } + } + while (rec != TERMINATE_RECORD_TYPE); + + // if we are at the end of the animation or not animated, close file + loader->imgnum = imgnum; + if ((animated->frame_count <= 1) || (rec == TERMINATE_RECORD_TYPE)) + { +#if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1)) + if (loader->gif) DGifCloseFile(loader->gif, NULL); +#else + if (loader->gif) DGifCloseFile(loader->gif); +#endif + if ((loader->fi.map) && (loader->f)) + eina_file_map_free(loader->f, loader->fi.map); + loader->gif = NULL; + loader->fi.map = NULL; + loader->imgnum = 0; + } + +on_ok: + // no errors in header scan etc. so set err and return value + *error = EVAS_LOAD_ERROR_NONE; + ret = EINA_TRUE; + + // if it was an animated image we need to copy the data to the + // pixels for the image from the frame holding the data + if (animated->animated && frame->data) + memcpy(pixels, frame->data, prop->w * prop->h * sizeof(DATA32)); + prop->premul = EINA_TRUE; + +on_error: // jump here on any errors to clean up + return ret; +} + +// get the time between 2 frames in the timeline +static double +evas_image_load_frame_duration_gif2(void *loader_data, + int start_frame, + int frame_num) +{ + Loader_Info *loader = loader_data; + Evas_Image_Animated *animated = loader->animated; + Image_Entry_Frame *frame; + int i, total = 0; + + // if its not animated or requested frame data is invalid + if (!animated->animated) return -1.0; + if ((start_frame + frame_num) > animated->frame_count) return -1.0; + if (frame_num < 0) return -1.0; + + if (frame_num < 1) frame_num = 1; + // walk frames from start frame though and total up delays + for (i = start_frame; i < (start_frame + frame_num); i++) + { + Frame_Info *finfo; + + // find the frame + frame = _find_frame(animated, i); + // no frame? barf - bad file or i/o? + if (!frame) return -1.0; + // get delay and total it up + finfo = frame->info; + // if delay is sensible - use it else assume 10/100ths of a sec + if (finfo->delay > 0) total += finfo->delay; + else total += 10; + } + // return delay in seconds (since timing in gifs is in 1/100ths of a sec) + return (double)total / 100.0; +} + +// called on opening of a file load +static void * +evas_image_load_file_open_gif2(Eina_File *f, + Eina_Stringshare *key EINA_UNUSED, // XXX: we need to use key for frame # + Evas_Image_Load_Opts *opts, + Evas_Image_Animated *animated, + int *error) +{ + Loader_Info *loader = calloc(1, sizeof (Loader_Info)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + loader->f = f; + loader->opts = opts; + loader->animated = animated; + return loader; +} + +// called on closing of an image file load (end of load) +static void +evas_image_load_file_close_gif2(void *loader_data) +{ + Loader_Info *loader = loader_data; +#if (GIFLIB_MAJOR > 5) || ((GIFLIB_MAJOR == 5) && (GIFLIB_MINOR >= 1)) + if (loader->gif) DGifCloseFile(loader->gif, NULL); +#else + if (loader->gif) DGifCloseFile(loader->gif); +#endif + if ((loader->fi.map) && (loader->f)) + eina_file_map_free(loader->f, loader->fi.map); + free(loader); +} + +// general module delcaration stuff +static Evas_Image_Load_Func evas_image_load_gif_func = +{ + evas_image_load_file_open_gif2, + evas_image_load_file_close_gif2, + evas_image_load_file_head_gif2, + evas_image_load_file_data_gif2, + evas_image_load_frame_duration_gif2, + EINA_TRUE, + EINA_FALSE +}; + +// raw module api that the rest of the world sees +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_gif_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "gif", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, gif); + +#ifndef EVAS_STATIC_BUILD_GIF +EVAS_EINA_MODULE_DEFINE(image_loader, gif); +#endif diff --git a/src/modules/evas/image_loaders/ico/evas_image_load_ico.c b/src/modules/evas/image_loaders/ico/evas_image_load_ico.c new file mode 100644 index 0000000000..7645f2a6f5 --- /dev/null +++ b/src/modules/evas/image_loaders/ico/evas_image_load_ico.c @@ -0,0 +1,844 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +static int _evas_loader_ico_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_ico_log_dom, __VA_ARGS__) + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + const char *key; + Evas_Image_Load_Opts *opts; +}; + +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_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret) +{ + unsigned char b[4]; + unsigned 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_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; +} + +enum +{ + SMALLEST, + BIGGEST, + SMALLER, + BIGGER +}; + +enum +{ + ICON = 1, + CURSOR = 2 +}; + +static void * +evas_image_load_file_open_ico(Eina_File *f, Eina_Stringshare *key, + 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->key = eina_stringshare_ref(key); + loader->opts = opts; + + return loader; +} + +static void +evas_image_load_file_close_ico(void *loader_data) +{ + Evas_Loader_Internal *loader = loader_data; + + eina_stringshare_del(loader->key); + free(loader); +} + +static Eina_Bool +evas_image_load_file_head_ico(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + const char *key; + Eina_File *f; + + void *map = NULL; + size_t position = 0; + unsigned short word; + unsigned char byte; + unsigned wanted_w = 0, wanted_h = 0; + int cols, i, planes = 0, + bpp = 0, pdelta, search = -1, have_choice = 0, + hasa = 1, icount; + unsigned int bmoffset, bmsize, fsize; + unsigned short reserved, type, count; + struct { + int pdelta; + int w, h; + int cols; + int bpp, planes; + int hot_x, hot_y; + unsigned int bmoffset, bmsize; + } chosen = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Eina_Bool r = EINA_FALSE; + + opts = loader->opts; + f = loader->f; + key = loader->key; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < (6 + 16 + 40)) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) goto close_file; + + // key: + // NULL == highest res + // biggest == highest res + // smallest == lowest res + // + // smaller == next size SMALLER than load opts WxH (if possible) + // bigger == next size BIGGER than load opts WxH (if possible) + // more ? + + search = BIGGEST; + if ((opts->w > 0) && (opts->h > 0)) + { + wanted_w = opts->w; + wanted_h = opts->h; + search = SMALLER; + } + + if (!read_ushort(map, fsize, &position, &reserved)) goto close_file; + if (!read_ushort(map, fsize, &position, &type)) goto close_file; + if (!read_ushort(map, fsize, &position, &count)) goto close_file; + icount = count; + if (!((reserved == 0) && + ((type == ICON) || (type == CURSOR)) && + (icount > 0) && (icount <= 10000))) // between 1 and 10000 images + goto close_file; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + if (key) + { + if (!strcmp(key, "biggest")) + { + wanted_w = 0; + wanted_h = 0; + search = BIGGEST; + chosen.pdelta = 0; + } + else if (!strcmp(key, "smallest")) + { + wanted_w = 1; + wanted_h = 1; + search = SMALLEST; + chosen.pdelta = 0x7fffffff; + } + else if (!strcmp(key, "smaller")) + { + chosen.pdelta = 0x7fffffff; + search = SMALLER; + } + else if (!strcmp(key, "bigger")) + { + chosen.pdelta = 0x7fffffff; + search = BIGGER; + } + } + for (i = 0; i < icount; i++) + { + unsigned char tw = 0, th = 0, tcols = 0; + if (!read_uchar(map, fsize, &position, &tw)) goto close_file; + prop->w = tw; + if (prop->w <= 0) prop->w = 256; + if (!read_uchar(map, fsize, &position, &th)) goto close_file; + prop->h = th; + if (prop->h <= 0) prop->h = 256; + if (!read_uchar(map, fsize, &position, &tcols)) goto close_file; + cols = tcols; + if (cols <= 0) cols = 256; + if (!read_uchar(map, fsize, &position, &byte)) goto close_file; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == CURSOR) planes = word; + //else hot_x = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == CURSOR) bpp = word; + //else hot_y = word; + if (!read_uint(map, fsize, &position, &bmsize)) goto close_file; + if (!read_uint(map, fsize, &position, &bmoffset)) goto close_file; + if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)) goto close_file; + if (search == BIGGEST) + { + pdelta = prop->w * prop->h; + if ((!have_choice) || + ((pdelta >= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = prop->w; + chosen.h = prop->h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else + { + if (search == SMALLEST) + { + pdelta = prop->w * prop->h; + if ((!have_choice) || + ((pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = prop->w; + chosen.h = prop->h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == SMALLER) + { + pdelta = (wanted_w * wanted_h) - (prop->w * prop->h); + if ((!have_choice) || + ((prop->w <= wanted_w) && (prop->h <= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = prop->w; + chosen.h = prop->h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == BIGGER) + { + pdelta = (prop->w * prop->h) - (wanted_w * wanted_h); + if ((!have_choice) || + ((prop->w >= wanted_w) && (prop->h >= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = prop->w; + chosen.h = prop->h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + } + } + if (chosen.bmoffset == 0) goto close_file; + position = chosen.bmoffset; + + prop->w = chosen.w; + prop->h = chosen.h; + if ((prop->w > 256) || (prop->h > 256)) goto close_file; + if ((prop->w < 1) || (prop->h < 1) || + (prop->w > IMG_MAX_SIZE) || (prop->h > IMG_MAX_SIZE) || + IMG_TOO_BIG(prop->w, prop->h)) + { + if (IMG_TOO_BIG(prop->w, prop->h)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + + if (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_ico(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + const char *key; + Eina_File *f; + + void *map = NULL; + size_t position = 0; + unsigned short word; + unsigned char byte; + unsigned int dword; + int wanted_w = 0, wanted_h = 0, w, h, cols, i, planes = 0, + bpp = 0, pdelta, search = -1, have_choice = 0, + stride, pstride, j, right_way_up = 0, diff_size = 0, cols2, icount; + unsigned int bmoffset, bmsize, bitcount, fsize, + *pal, *surface, *pix, none_zero_alpha = 0; + unsigned short reserved, type, count; + unsigned char *maskbuf, *pixbuf, *p; + struct { + int pdelta; + int w, h; + int cols; + int bpp, planes; + int hot_x, hot_y; + unsigned int bmoffset, bmsize; + } chosen = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + Eina_Bool res = EINA_FALSE; + + opts = loader->opts; + key = loader->key; + f = loader->f; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + fsize = eina_file_size_get(f); + if (fsize < (6 + 16 + 40)) goto close_file; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) goto close_file; + + // key: + // NULL == highest res + // biggest == highest res + // smallest == lowest res + // + // smaller == next size SMALLER than load opts WxH (if possible) + // bigger == next size BIGGER than load opts WxH (if possible) + // more ? + + search = BIGGEST; + if ((opts->w > 0) && (opts->h > 0)) + { + wanted_w = opts->w; + wanted_h = opts->h; + search = SMALLER; + } + + if (!read_ushort(map, fsize, &position, &reserved)) goto close_file; + if (!read_ushort(map, fsize, &position, &type)) goto close_file; + if (!read_ushort(map, fsize, &position, &count)) goto close_file; + icount = count; + if (!((reserved == 0) && + ((type == ICON) || (type == CURSOR)) && + (icount > 0) && (icount <= 10000))) // between 1 and 10000 images + goto close_file; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + if (key) + { + if (!strcmp(key, "biggest")) + { + wanted_w = 0; + wanted_h = 0; + search = BIGGEST; + chosen.pdelta = 0; + } + else if (!strcmp(key, "smallest")) + { + wanted_w = 1; + wanted_h = 1; + search = SMALLEST; + chosen.pdelta = 0x7fffffff; + } + else if (!strcmp(key, "smaller")) + { + chosen.pdelta = 0x7fffffff; + search = SMALLER; + } + else if (!strcmp(key, "bigger")) + { + chosen.pdelta = 0x7fffffff; + search = BIGGER; + } + } + for (i = 0; i < icount; i++) + { + unsigned char tw = 0, th = 0, tcols = 0; + if (!read_uchar(map, fsize, &position, &tw)) goto close_file; + w = tw; + if (w <= 0) w = 256; + if (!read_uchar(map, fsize, &position, &th)) goto close_file; + h = th; + if (h <= 0) h = 256; + if (!read_uchar(map, fsize, &position, &tcols)) goto close_file; + cols = tcols; + if (cols <= 0) cols = 256; + if (!read_uchar(map, fsize, &position, &byte)) goto close_file; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == 1) planes = word; + //else hot_x = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; + if (type == 1) bpp = word; + //else hot_y = word; + if (!read_uint(map, fsize, &position, &bmsize)) goto close_file; + if (!read_uint(map, fsize, &position, &bmoffset)) goto close_file; + if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)) goto close_file; + if (search == BIGGEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta >= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else + { + if (search == SMALLEST) + { + pdelta = w * h; + if ((!have_choice) || + ((pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == SMALLER) + { + pdelta = (wanted_w * wanted_h) - (w * h); + if ((!have_choice) || + ((w <= wanted_w) && (h <= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + else if (search == BIGGER) + { + pdelta = (w * h) - (wanted_w * wanted_h); + if ((!have_choice) || + ((w >= wanted_w) && (h >= wanted_h) && + (pdelta <= chosen.pdelta) && + (((bpp >= 3) && (bpp >= chosen.bpp)) || + ((bpp < 3) && (cols >= chosen.cols))))) + { + have_choice = 1; + if (pdelta < 0) pdelta = 0x7fffffff; + chosen.pdelta = pdelta; + chosen.w = w; + chosen.h = h; + chosen.cols = cols; + chosen.bpp = bpp; + chosen.planes = planes; + chosen.bmsize = bmsize; + chosen.bmoffset = bmoffset; + } + } + } + } + if (chosen.bmoffset == 0) goto close_file; + position = chosen.bmoffset; + + w = chosen.w; + h = chosen.h; + cols = chosen.cols; + + // changed since we loaded header? + if (((int)prop->w != w) || ((int)prop->h != h)) goto close_file; + + // read bmp header time... let's do some checking + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // headersize - dont care + if (dword != 40) // must be 40 if bmp entry - if not, something else + { + ERR("ICO at %i offset, size %i in %s is not a standard ico bmp " + " file entry. It may be PNG (new as of Vista - not in original spec)", + (int)position, (int)chosen.bmsize, + eina_file_filename_get(f)); + } + else + { + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // width + if (dword > 0) + { + if ((int)dword != w) + { + w = dword; + diff_size = 1; + } + } + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // height + if (dword > 0) + { + if ((int)dword != (h * 2)) + { + h = dword / 2; + diff_size = 1; + } + } + if (diff_size) + { + ERR("Broken ICO file: %s - " + " Reporting size of %ix%i in index, but bitmap is %ix%i. " + " May be expanded or cropped.", + eina_file_filename_get(f), prop->w, prop->h, w, h); + } + if (!read_ushort(map, fsize, &position, &word)) goto close_file; // planes + //planes2 = word; + if (!read_ushort(map, fsize, &position, &word)) goto close_file; // bitcount + bitcount = word; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // compression + //compression = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // imagesize + //imagesize = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // z pixels per m + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // y pizels per m + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // colors used + //colorsused = dword; + if (!read_uint(map, fsize, &position, &dword)) goto close_file; // colors important + //colorsimportant = dword; + + surface = pixels; + memset(surface, 0, prop->w * prop->h * 4); + + if (!((bitcount == 1) || (bitcount == 4) || (bitcount == 8) || + (bitcount == 24) || (bitcount == 32))) + goto close_file; + if (bitcount <= 8) + { + cols2 = 1 << bitcount; + if (cols == 0) cols = cols2; + if (cols > cols2) cols = cols2; + } + else cols = 0; + if (bitcount > 8) cols = 0; + + pal = alloca(256 * 4); + for (i = 0; i < cols; i++) + { + unsigned char a, r, g, b; + + 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 (!read_uchar(map, fsize, &position, &a)) goto close_file; + a = 0xff; + pal[i] = ARGB_JOIN(a, r, g, b); + } + stride = ((w + 31) / 32); + maskbuf = alloca(stride * h); + pixbuf = alloca(stride * 32 * 4); // more than enough + if (bitcount == 1) + { + pstride = stride * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= h) continue; + for (j = 0; j < w; j++) + { + if (j >= w) break; + if ((j & 0x7) == 0x0) *pix = pal[*p >> 7]; + else if ((j & 0x7) == 0x1) *pix = pal[(*p >> 6) & 0x1]; + else if ((j & 0x7) == 0x2) *pix = pal[(*p >> 5) & 0x1]; + else if ((j & 0x7) == 0x3) *pix = pal[(*p >> 4) & 0x1]; + else if ((j & 0x7) == 0x4) *pix = pal[(*p >> 3) & 0x1]; + else if ((j & 0x7) == 0x5) *pix = pal[(*p >> 2) & 0x1]; + else if ((j & 0x7) == 0x6) *pix = pal[(*p >> 1) & 0x1]; + else + { + *pix = pal[*p & 0x1]; + p++; + } + pix++; + } + } + } + else if (bitcount == 4) + { + pstride = ((w + 7) / 8) * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= h) continue; + for (j = 0; j < w; j++) + { + if (j >= w) break; + if ((j & 0x1) == 0x1) + { + *pix = pal[*p & 0x0f]; + p++; + } + else + { + *pix = pal[*p >> 4]; + } + pix++; + } + } + } + else if (bitcount == 8) + { + pstride = ((w + 3) / 4) * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= h) continue; + for (j = 0; j < w; j++) + { + if (j >= w) break; + *pix = pal[*p]; + p++; + pix++; + } + } + } + else if (bitcount == 24) + { + pstride = w * 3; + for (i = 0; i < h; i++) + { + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= h) continue; + for (j = 0; j < w; j++) + { + unsigned char a, r, g, b; + + if (j >= w) break; + b = p[0]; g = p[1]; r = p[2]; + p += 3; + a = 0xff; + *pix = ARGB_JOIN(a, r, g, b); + pix++; + } + } + } + else if (bitcount == 32) + { + pstride = w * 4; + for (i = 0; i < h; i++) + { + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + if (!read_mem(map, fsize, &position, pixbuf, pstride)) goto close_file; + p = pixbuf; + if (i >= h) continue; + for (j = 0; j < w; j++) + { + unsigned char a, r, g, b; + + if (j >= w) break; + b = p[0]; g = p[1]; r = p[2]; a = p[3]; + p += 4; + if (a) none_zero_alpha = 1; + *pix = ARGB_JOIN(a, r, g, b); + pix++; + } + } + } + if (!none_zero_alpha) + { + if (!read_mem(map, fsize, &position, maskbuf, stride * 4 * h)) goto close_file; + // apply mask + pix = surface; + for (i = 0; i < h; i++) + { + unsigned char *m; + + pix = surface + (i * w); + if (!right_way_up) pix = surface + ((h - 1 - i) * w); + m = maskbuf + (stride * i * 4); + if (i >= h) continue; + for (j = 0; j < w; j++) + { + if (j >= w) break; + if (*m & (1 << (7 - (j & 0x7)))) *pix = 0; + else A_VAL(pix) = 0xff; + if ((j & 0x7) == 0x7) m++; + pix++; + } + } + } + } + + + prop->premul = EINA_TRUE; + *error = EVAS_LOAD_ERROR_NONE; + + res = EINA_TRUE; + + close_file: + if (map) eina_file_map_free(f, map); + + return res; +} + +static Evas_Image_Load_Func evas_image_load_ico_func = +{ + evas_image_load_file_open_ico, + evas_image_load_file_close_ico, + evas_image_load_file_head_ico, + evas_image_load_file_data_ico, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + _evas_loader_ico_log_dom = eina_log_domain_register + ("evas-ico", EVAS_DEFAULT_LOG_COLOR); + if (_evas_loader_ico_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + em->functions = (void *)(&evas_image_load_ico_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + eina_log_domain_unregister(_evas_loader_ico_log_dom); +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "ico", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, ico); + +#ifndef EVAS_STATIC_BUILD_ICO +EVAS_EINA_MODULE_DEFINE(image_loader, ico); +#endif diff --git a/src/modules/evas/image_loaders/jp2k/evas_image_load_jp2k.c b/src/modules/evas/image_loaders/jp2k/evas_image_load_jp2k.c new file mode 100644 index 0000000000..ececb6d027 --- /dev/null +++ b/src/modules/evas/image_loaders/jp2k/evas_image_load_jp2k.c @@ -0,0 +1,409 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include <openjpeg.h> + +#include "Evas_Loader.h" + +static int _evas_loader_jp2k_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_jp2k_log_dom, __VA_ARGS__) + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; +}; + +static void +_jp2k_error_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) +{ +// ERR("OpenJPEG internal error: '%s'.", msg); +} + +static void +_jp2k_warning_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) +{ +// WRN("OpenJPEG internal warning: '%s'.", msg); +} + +static void +_jp2k_info_cb(const char *msg EINA_UNUSED, void *data EINA_UNUSED) +{ +// INF("OpenJPEG internal information: '%s'.", msg); +} + +static Eina_Bool +evas_image_load_file_head_jp2k_internal(unsigned int *w, unsigned int *h, + unsigned char *alpha, + Evas_Image_Load_Opts *opts EINA_UNUSED, + void *map, size_t length, + int *error) +{ + opj_event_mgr_t event_mgr; + opj_dparameters_t params; + opj_dinfo_t *info; + opj_cio_t *cio; + opj_image_t *image; + int format; + int k; + + if (length < 2) + { + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + + if (((unsigned char *)map)[0] == 0xFF && ((unsigned char *)map)[1] == 0x4F) + format = CODEC_J2K; + else + format = CODEC_JP2; + + memset(&event_mgr, 0, sizeof(event_mgr)); + event_mgr.error_handler = _jp2k_error_cb; + event_mgr.warning_handler = _jp2k_warning_cb; + event_mgr.info_handler = _jp2k_info_cb; + + opj_set_default_decoder_parameters(¶ms); + info = opj_create_decompress(format); + if (!info) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + opj_set_event_mgr((opj_common_ptr)info, &event_mgr, NULL); + opj_setup_decoder(info, ¶ms); + + cio = opj_cio_open((opj_common_ptr)info, map, length); + if (!cio) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + image = opj_decode(info, cio); + if (!image) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + for (k = 1; k < image->numcomps; k++) + { + if (image->comps[k].w != image->comps[0].w) + goto free_image; + if (image->comps[k].h != image->comps[0].h) + goto free_image; + if (image->comps[k].prec > 8) + goto free_image; + } + + *w = image->comps[0].w; + *h = image->comps[0].h; + *alpha = ((image->numcomps == 4) || (image->numcomps == 2)) ? 1 : 0; + *error = EVAS_LOAD_ERROR_NONE; + + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + return EINA_TRUE; + + free_image: + *error = EVAS_LOAD_ERROR_GENERIC; + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + return EINA_FALSE; +} + +static Eina_Bool +evas_image_load_file_data_jp2k_internal(Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Property *prop EINA_UNUSED, + void *pixels, + void *map, size_t length, + int *error) +{ + opj_dparameters_t params; + opj_dinfo_t *info; + opj_cio_t *cio; + opj_image_t *image; + unsigned int *iter; + int format; + int idx; + + if (((unsigned char *)map)[0] == 0xFF && ((unsigned char *)map)[1] == 0x4F) + format = CODEC_J2K; + else + format = CODEC_JP2; + + opj_set_default_decoder_parameters(¶ms); + info = opj_create_decompress(format); + opj_set_event_mgr((opj_common_ptr)info, NULL, NULL); + opj_setup_decoder(info, ¶ms); + cio = opj_cio_open((opj_common_ptr)info, map, length); + image = opj_decode(info, cio); + + iter = pixels; + idx = 0; + + /* + * FIXME: + * image->numcomps == 4, image->color_space == CLRSPC_SYCC : YUV + */ + /* BGR(A) */ + if ((image->numcomps >= 3) && + (image->comps[0].dx == image->comps[1].dx) && + (image->comps[1].dx == image->comps[2].dx) && + (image->comps[0].dy == image->comps[1].dy) && + (image->comps[1].dy == image->comps[2].dy)) + { + int a; + int r; + int g; + int b; + int i; + int j; + + for (j = 0; j < image->comps[0].h; j++) + { + for (i = 0; i < image->comps[0].w; i++, idx++, iter++) + { + r = image->comps[0].data[idx]; + r+= (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + if (r > 255) r = 255; + if (r < 0) r = 0; + + g = image->comps[1].data[idx]; + g+= (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + if (g > 255) g = 255; + if (g < 0) g = 0; + + b = image->comps[2].data[idx]; + b+= (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); + if (b > 255) b = 255; + if (b < 0) b = 0; + + if (image->numcomps == 4) + { + a = image->comps[3].data[idx]; + a+= (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); + if (a > 255) a = 255; + if (a < 0) a = 0; + } + else + a = 255; + + *iter = a << 24 | r << 16 | g << 8 | b; + } + } + } + /* *GRAY(A) */ + else if (((image->numcomps == 1) || (image->numcomps == 2)) && + (image->comps[0].dx == image->comps[1].dx) && + (image->comps[1].dx == image->comps[2].dx) && + (image->comps[0].dy == image->comps[1].dy) && + (image->comps[1].dy == image->comps[2].dy)) + { + int a; + int g; + int i; + int j; + + for (j = 0; j < image->comps[0].h; j++) + { + for (i = 0; i < image->comps[0].w; i++, idx++, iter++) + { + g = image->comps[0].data[idx]; + g+= (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); + if (g > 255) g = 255; + if (g < 0) g = 0; + + if (image->numcomps == 2) + { + a = image->comps[1].data[idx]; + a+= (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); + if (a > 255) a = 255; + if (a < 0) a = 0; + } + else + a = 255; + + *iter = a << 24 | g << 16 | g << 8 | g; + } + } + } + + opj_image_destroy(image); + opj_cio_close(cio); + opj_destroy_decompress(info); + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static void * +evas_image_load_file_open_jp2k(Eina_File *f, Eina_Stringshare *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_jp2k(void *loader_data) +{ + free(loader_data); +} + +static Eina_Bool +evas_image_load_file_head_jp2k(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val; + + opts = loader->opts; + f = loader->f; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + val = evas_image_load_file_head_jp2k_internal(&prop->w, &prop->h, + &prop->alpha, + opts, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + return val; +} + +static Eina_Bool +evas_image_load_file_data_jp2k(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + f = loader->f; + opts = loader->opts; + + map = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + goto on_error; + } + + val = evas_image_load_file_data_jp2k_internal(opts, prop, pixels, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + on_error: + return val; +} + +static Evas_Image_Load_Func evas_image_load_jp2k_func = +{ + evas_image_load_file_open_jp2k, + evas_image_load_file_close_jp2k, + evas_image_load_file_head_jp2k, + evas_image_load_file_data_jp2k, + NULL, + EINA_TRUE, + EINA_TRUE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + + _evas_loader_jp2k_log_dom = eina_log_domain_register("evas-jp2k", EINA_COLOR_BLUE); + if (_evas_loader_jp2k_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + + em->functions = (void *)(&evas_image_load_jp2k_func); + + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + eina_log_domain_unregister(_evas_loader_jp2k_log_dom); + _evas_loader_jp2k_log_dom = -1; +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "jp2k", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, jp2k); + +#ifndef EVAS_STATIC_BUILD_JP2K +EVAS_EINA_MODULE_DEFINE(image_loader, jp2k); +#endif + diff --git a/src/modules/evas/image_loaders/jpeg/evas_image_load_jpeg.c b/src/modules/evas/image_loaders/jpeg/evas_image_load_jpeg.c new file mode 100644 index 0000000000..c0047a3ea8 --- /dev/null +++ b/src/modules/evas/image_loaders/jpeg/evas_image_load_jpeg.c @@ -0,0 +1,1500 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#ifdef _WIN32 +# define XMD_H /* This prevents libjpeg to redefine INT32 */ +#endif + +#include <setjmp.h> +#include <jpeglib.h> + +#include "evas_common_private.h" +#include "evas_private.h" + +typedef struct _JPEG_error_mgr *emptr; +struct _JPEG_error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; +}; + + +static void _JPEGFatalErrorHandler(j_common_ptr cinfo); +static void _JPEGErrorHandler(j_common_ptr cinfo); +static void _JPEGErrorHandler2(j_common_ptr cinfo, int msg_level); +static Eina_Bool _get_next_app0(unsigned char *map, size_t fsize, size_t *position); +static Eina_Bool _get_orientation_app1(unsigned char *map, size_t fsize, size_t *position, + int *orientation, Eina_Bool *flipped); +static int _get_orientation(void *map, size_t length, Eina_Bool *flipped); + +#if 0 /* not used at the moment */ +static int evas_image_load_file_data_jpeg_alpha_internal(Image_Entry *ie, FILE *f) EINA_ARG_NONNULL(1, 2); +#endif + +static void +_JPEGFatalErrorHandler(j_common_ptr cinfo) +{ + emptr errmgr; + + errmgr = (emptr) cinfo->err; + /* cinfo->err->output_message(cinfo);*/ + longjmp(errmgr->setjmp_buffer, 1); + return; +} + +static void +_JPEGErrorHandler(j_common_ptr cinfo EINA_UNUSED) +{ +/* emptr errmgr; */ + +/* errmgr = (emptr) cinfo->err; */ + /* cinfo->err->output_message(cinfo);*/ + /* longjmp(errmgr->setjmp_buffer, 1);*/ + return; +} + +static void +_JPEGErrorHandler2(j_common_ptr cinfo EINA_UNUSED, int msg_level EINA_UNUSED) +{ +/* emptr errmgr; */ + +/* errmgr = (emptr) cinfo->err; */ + /* cinfo->err->output_message(cinfo);*/ + /* longjmp(errmgr->setjmp_buffer, 1);*/ + return; +} + +struct jpeg_membuf_src +{ + struct jpeg_source_mgr pub; + + const unsigned char *buf; + size_t len; + struct jpeg_membuf_src *self; +}; + +static void +_evas_jpeg_membuf_src_init(j_decompress_ptr cinfo EINA_UNUSED) +{ +} + +static boolean +_evas_jpeg_membuf_src_fill(j_decompress_ptr cinfo) +{ + static const JOCTET jpeg_eoi[2] = { 0xFF, JPEG_EOI }; + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + + src->pub.bytes_in_buffer = sizeof(jpeg_eoi); + src->pub.next_input_byte = jpeg_eoi; + + return TRUE; +} + +static void +_evas_jpeg_membuf_src_skip(j_decompress_ptr cinfo, + long num_bytes) +{ + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + + if ((((long)src->pub.bytes_in_buffer - (long)src->len) > num_bytes) || + ((long)src->pub.bytes_in_buffer < num_bytes)) + { + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo)); + return; + } + src->pub.bytes_in_buffer -= num_bytes; + src->pub.next_input_byte += num_bytes; +} + +static void +_evas_jpeg_membuf_src_term(j_decompress_ptr cinfo) +{ + struct jpeg_membuf_src *src = (struct jpeg_membuf_src *)cinfo->src; + if (!src) return; + free(src); + cinfo->src = NULL; +} + +static int +_evas_jpeg_membuf_src(j_decompress_ptr cinfo, + void *map, size_t length) +{ + struct jpeg_membuf_src *src; + + src = calloc(1, sizeof(*src)); + if (!src) + return -1; + + src->self = src; + + cinfo->src = &src->pub; + src->buf = map; + src->len = length; + src->pub.init_source = _evas_jpeg_membuf_src_init; + src->pub.fill_input_buffer = _evas_jpeg_membuf_src_fill; + src->pub.skip_input_data = _evas_jpeg_membuf_src_skip; + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = _evas_jpeg_membuf_src_term; + src->pub.bytes_in_buffer = src->len; + src->pub.next_input_byte = src->buf; + + return 0; +} + +/*! Magic number for EXIF header, App0, App1*/ +static const unsigned char ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; +static const unsigned char JfifHeader[] = {0x4A, 0x46, 0x49, 0x46, 0x00}; +static const unsigned char JfxxHeader[] = {0x4A, 0x46, 0x58, 0x58, 0x00}; +static const unsigned char App0[] = {0xff, 0xe0}; +static const unsigned char App1[] = {0xff, 0xe1}; +static const unsigned char II[] = {0x49, 0x49}; +static const unsigned char MM[] = {0x4d, 0x4d}; +typedef enum { + EXIF_BYTE_ALIGN_II, + EXIF_BYTE_ALIGN_MM +} ExifByteAlign; + +static Eina_Bool +_get_next_app0(unsigned char *map, size_t fsize, size_t *position) +{ + unsigned short length = 0; + unsigned int w = 0, h = 0; + unsigned int format = 0; + unsigned int data_size = 0; + unsigned char *app0_head, *p; + + /* header_mark:2, length:2, identifier:5 version:2, unit:1, den=4 thum=2 */ + if ((*position + 16) >= fsize) return EINA_FALSE; + app0_head = map + *position; + + /* p is appn's start pointer excluding app0 marker */ + p = app0_head + 2; + + length = ((*p << 8) + *(p + 1)); + + /* JFIF segment format */ + if (!memcmp(p + 2, JfifHeader, sizeof (JfifHeader))) + { + format = 3; + w = *(p + 14); + h = *(p + 15); + } + else if (!memcmp(p + 2, JfxxHeader, sizeof (JfxxHeader))) + { + if (*(p + 7) == 0x11) + format = 1; + else + format = 3; + w = *(p + 8); + h = *(p + 9); + } + + data_size = format * w * h; + + if ((*position + 2+ length + data_size) > fsize) + return EINA_FALSE; + + *position = *position + 2 + length + data_size; + + return EINA_TRUE; +} + +/* If app1 data is abnormal, returns EINA_FALSE. + If app1 data is normal, returns EINA_TRUE. + If app1 data is normal but not orientation data, orientation value is -1. + */ + +static Eina_Bool +_get_orientation_app1(unsigned char *map, size_t fsize, size_t *position, + int *orientation_res, Eina_Bool *flipped) +{ + unsigned char *app1_head, *buf; + unsigned char orientation[2]; + ExifByteAlign byte_align; + unsigned int num_directory = 0; + unsigned int i, j; + int direction; + unsigned int data_size = 0; + + /* app1 mark:2, data_size:2, exif:6 tiff:8 */ + if ((*position + 18) >= fsize) return EINA_FALSE; + app1_head = map + *position; + buf = app1_head; + + data_size = ((*(buf + 2) << 8) + *(buf + 3)); + if ((*position + 2 + data_size) > fsize) return EINA_FALSE; + + if (memcmp(buf + 4, ExifHeader, sizeof (ExifHeader))) + { + *position = *position + 2 + data_size; + *orientation_res = -1; + return EINA_TRUE; + } + + /* 2. get 10&11 byte get info of "II(0x4949)" or "MM(0x4d4d)" */ + /* 3. get [18]&[19] get directory entry # */ + if (!memcmp(buf + 10, MM, sizeof (MM))) + { + byte_align = EXIF_BYTE_ALIGN_MM; + num_directory = ((*(buf + 18) << 8) + *(buf + 19)); + orientation[0] = 0x01; + orientation[1] = 0x12; + } + else if (!memcmp(buf + 10, II, sizeof (II))) + { + byte_align = EXIF_BYTE_ALIGN_II; + num_directory = ((*(buf + 19) << 8) + *(buf + 18)); + orientation[0] = 0x12; + orientation[1] = 0x01; + } + else return EINA_FALSE; + + /* check num_directory data */ + if ((*position + (12 * num_directory + 20)) > fsize) return EINA_FALSE; + + buf = app1_head + 20; + + j = 0; + + for (i = 0; i < num_directory; i++ ) + { + if (!memcmp(buf + j, orientation, 2)) + { + /*get orientation tag */ + if (byte_align == EXIF_BYTE_ALIGN_MM) + direction = *(buf+ j + 9); + else direction = *(buf+ j + 8); + switch (direction) + { + case 3: + *orientation_res = 180; + *flipped = EINA_FALSE; + return EINA_TRUE; + case 4: + *orientation_res = 180; + *flipped = EINA_TRUE; + return EINA_TRUE; + case 6: + *orientation_res = 90; + *flipped = EINA_FALSE; + return EINA_TRUE; + case 7: + *orientation_res = 90; + *flipped = EINA_TRUE; + return EINA_TRUE; + case 5: + *orientation_res = 270; + *flipped = EINA_TRUE; + return EINA_TRUE; + case 8: + *orientation_res = 270; + *flipped = EINA_FALSE; + return EINA_TRUE; + case 2: + *orientation_res = 0; + *flipped = EINA_TRUE; + return EINA_TRUE; + default: + *orientation_res = 0; + *flipped = EINA_FALSE; + return EINA_TRUE; + } + } + else + j = j + 12; + } + return EINA_FALSE; +} + +static int +_get_orientation(void *map, size_t length, Eina_Bool *flipped) +{ + unsigned char *buf; + size_t position = 0; + int orientation = -1; + Eina_Bool res = EINA_FALSE; + + *flipped = EINA_FALSE; + + /* open file and get 22 byte frome file */ + if (!map) return 0; + /* 1. read 22byte */ + if (length < 22) return 0; + buf = (unsigned char *)map; + + position = 2; + /* 2. check 2,3 bypte with APP0(0xFFE0) or APP1(0xFFE1) */ + while((length - position) > 0) + { + if (!memcmp(buf + position, App0, sizeof (App0))) + { + res = _get_next_app0(map, length, &position); + if (!res) break; + } + else if (!memcmp(buf + position, App1, sizeof (App1))) + { + res = _get_orientation_app1(map, length, &position, &orientation, flipped); + if (!res) break; + if (orientation != -1) return orientation; + } + else break; + } + return 0; +} + +static void +_rotate_region(unsigned int *r_x, unsigned int *r_y, unsigned int *r_w, unsigned int *r_h, + unsigned int x, unsigned int y, unsigned int w, unsigned int h, + unsigned int output_w, unsigned int output_h, + int degree, Eina_Bool flipped) +{ + switch (degree) + { + case 90: + if (flipped) + { + *r_x = output_w - (y + h); + *r_y = output_h - (x + w); + *r_w = h; + *r_h = w; + } + else + { + *r_x = y; + *r_y = output_h - (x + y); + *r_w = h; + *r_h = w; + } + break; + case 180: + if (flipped) + { + *r_y = output_h - (y + h); + } + else + { + *r_x = output_w - (x + w); + *r_y = output_h - (y + h); + } + break; + case 270: + if (flipped) + { + *r_x = y; + *r_y = x; + *r_w = h; + *r_h = w; + } + else + { + *r_x = output_w - (y + h); + *r_y = x; + *r_w = h; + *r_h = w; + } + break; + default: + if (flipped) + *r_x = output_w - (x + w); + break; + } +} + +static Eina_Bool +evas_image_load_file_head_jpeg_internal(unsigned int *w, unsigned int *h, + unsigned char *scale, + unsigned char *rotated, + Eina_Bool *flipped, + Evas_Image_Load_Opts *opts, + void *map, size_t length, + int *error) +{ + unsigned int scalew, scaleh; + struct jpeg_decompress_struct cinfo; + struct _JPEG_error_mgr jerr; + + /* for rotation decoding */ + int degree = 0; + Eina_Bool change_wh = EINA_FALSE; + unsigned int load_opts_w = 0, load_opts_h = 0; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = _JPEGFatalErrorHandler; + jerr.pub.emit_message = _JPEGErrorHandler2; + jerr.pub.output_message = _JPEGErrorHandler; + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + if (cinfo.saw_JFIF_marker) + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + else + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, length)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss) + cinfo.dither_mode = JDITHER_ORDERED; + cinfo.buffered_image = TRUE; // buffered mode in case jpg is progressive + jpeg_start_decompress(&cinfo); + + /* rotation decoding */ + if (opts->orientation) + { + degree = _get_orientation(map, length, flipped); + if (degree != 0 || *flipped) + { + opts->degree = degree; + *rotated = EINA_TRUE; + + if (degree == 90 || degree == 270) + change_wh = EINA_TRUE; + } + + } + + /* head decoding */ + *w = cinfo.output_width; + *h = cinfo.output_height; + if ((*w < 1) || (*h < 1) || (*w > IMG_MAX_SIZE) || (*h > IMG_MAX_SIZE) || + (IMG_TOO_BIG(*w, *h))) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + if (IMG_TOO_BIG(*w, *h)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + if (opts->scale_down_by > 1) + { + *w /= opts->scale_down_by; + *h /= opts->scale_down_by; + } + else if (opts->dpi > 0.0) + { + *w = (*w * opts->dpi) / 90.0; + *h = (*h * opts->dpi) / 90.0; + } + else if ((opts->w > 0) && (opts->h > 0)) + { + unsigned int w2 = *w, h2 = *h; + /* user set load_opts' w,h on the assumption + that image already rotated according to it's orientation info */ + if (change_wh) + { + load_opts_w = opts->w; + load_opts_h = opts->h; + opts->w = load_opts_h; + opts->h = load_opts_w; + } + + if (opts->w > 0) + { + w2 = opts->w; + h2 = (opts->w * *h) / *w; + if ((opts->h > 0) && (h2 > opts->h)) + { + unsigned int w3; + h2 = opts->h; + w3 = (opts->h * *w) / *h; + if (w3 > w2) + w2 = w3; + } + } + else if (opts->h > 0) + { + h2 = opts->h; + w2 = (opts->h * *w) / *h; + } + *w = w2; + *h = h2; + if (change_wh) + { + opts->w = load_opts_w; + opts->h = load_opts_h; + } + } + if (*w < 1) *w = 1; + if (*h < 1) *h = 1; + + if ((*w != cinfo.output_width) || (*h != cinfo.output_height)) + { + scalew = cinfo.output_width / *w; + scaleh = cinfo.output_height / *h; + + *scale = scalew; + if (scaleh < scalew) *scale = scaleh; + + if (*scale > 8) *scale = 8; + else if (*scale < 1) *scale = 1; + + if (*scale == 3) *scale = 2; + else if (*scale == 5) *scale = 4; + else if (*scale == 6) *scale = 4; + else if (*scale == 7) *scale = 4; + } + + if (*scale > 1) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, length)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.scale_num = 1; + cinfo.scale_denom = *scale; + cinfo.buffered_image = TRUE; // buffered mode in case jpg is progressive + jpeg_calc_output_dimensions(&(cinfo)); + jpeg_start_decompress(&cinfo); + } + + *w = cinfo.output_width; + *h = cinfo.output_height; + + // be nice and clip region to image. if its totally outside, fail load + if ((opts->region.w > 0) && (opts->region.h > 0)) + { + unsigned int load_region_x = 0, load_region_y = 0; + unsigned int load_region_w = 0, load_region_h = 0; + if (*rotated) + { + load_region_x = opts->region.x; + load_region_y = opts->region.y; + load_region_w = opts->region.w; + load_region_h = opts->region.h; + + _rotate_region(&opts->region.x, &opts->region.y, &opts->region.w, &opts->region.h, + load_region_x, load_region_y, load_region_w, load_region_h, + *w, *h, degree, *flipped); + } + RECTS_CLIP_TO_RECT(opts->region.x, opts->region.y, + opts->region.w, opts->region.h, + 0, 0, *w, *h); + if ((opts->region.w <= 0) || (opts->region.h <= 0)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + *w = opts->region.w; + *h = opts->region.h; + if (*rotated) + { + opts->region.x = load_region_x; + opts->region.y = load_region_y; + opts->region.w = load_region_w; + opts->region.h = load_region_h; + } + } +/* end head decoding */ + + if (change_wh) + { + unsigned int tmp; + tmp = *w; + *w = *h; + *h = tmp; + } + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +/* +static double +get_time(void) +{ + struct timeval timev; + + gettimeofday(&timev, NULL); + return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); +} +*/ + +static void +_rotate_180(DATA32 *data, int w, int h) +{ + DATA32 *p1, *p2; + DATA32 pt; + int x; + + p1 = data; + p2 = data + (h * w) - 1; + for (x = (w * h) / 2; --x >= 0;) + { + pt = *p1; + *p1 = *p2; + *p2 = pt; + p1++; + p2--; + } +} + +static void +_flip_horizontal(DATA32 *data, int w, int h) +{ + DATA32 *p1, *p2; + DATA32 pt; + int x, y; + + for (y = 0; y < h; y++) + { + p1 = data + (y * w); + p2 = data + ((y + 1) * w) - 1; + for (x = 0; x < (w >> 1); x++) + { + pt = *p1; + *p1 = *p2; + *p2 = pt; + p1++; + p2--; + } + } +} + +static void +_flip_vertical(DATA32 *data, int w, int h) +{ + DATA32 *p1, *p2; + DATA32 pt; + int x, y; + + for (y = 0; y < (h >> 1); y++) + { + p1 = data + (y * w); + p2 = data + ((h - 1 - y) * w); + for (x = 0; x < w; x++) + { + pt = *p1; + *p1 = *p2; + *p2 = pt; + p1++; + p2++; + } + } +} + +static void +_rotate_change_wh(DATA32 *to, DATA32 *from, + int w, int h, + int dx, int dy) +{ + int x, y; + + for (x = h; --x >= 0;) + { + for (y = w; --y >= 0;) + { + *to = *from; + from++; + to += dy; + } + to += dx; + } +} + +static Eina_Bool +evas_image_load_file_data_jpeg_internal(Evas_Image_Load_Opts *opts, + Evas_Image_Property *prop, + void *pixels, + void *map, size_t size, + int *error) +{ + unsigned int w, h; + struct jpeg_decompress_struct cinfo; + struct _JPEG_error_mgr jerr; + DATA8 *ptr, *line[16], *data; + DATA32 *ptr2, *ptr_rotate = NULL; + unsigned int x, y, l, i, scans; + int region = 0; + /* rotation setting */ + unsigned int ie_w = 0, ie_h = 0; + unsigned int load_region_x = 0, load_region_y = 0; + unsigned int load_region_w = 0, load_region_h = 0; + volatile int degree = 0; + volatile Eina_Bool change_wh = EINA_FALSE; + Eina_Bool line_done = EINA_FALSE; + + memset(&cinfo, 0, sizeof(cinfo)); + if (prop->rotated) + { + degree = opts->degree; + if (degree == 90 || degree == 270) + change_wh = EINA_TRUE; + } + + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = _JPEGFatalErrorHandler; + jerr.pub.emit_message = _JPEGErrorHandler2; + jerr.pub.output_message = _JPEGErrorHandler; + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + jpeg_create_decompress(&cinfo); + + if (_evas_jpeg_membuf_src(&cinfo, map, size)) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return 0; + } + + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + cinfo.dct_method = JDCT_ISLOW; // JDCT_FLOAT JDCT_IFAST(quality loss) + cinfo.dither_mode = JDITHER_ORDERED; + + if (prop->scale > 1) + { + cinfo.scale_num = 1; + cinfo.scale_denom = prop->scale; + } + + /* Colorspace conversion options */ + /* libjpeg can do the following conversions: */ + /* GRAYSCLAE => RGB YCbCr => RGB and YCCK => CMYK */ + switch (cinfo.jpeg_color_space) + { + case JCS_UNKNOWN: + break; + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo.out_color_space = JCS_CMYK; + break; + default: + cinfo.out_color_space = JCS_RGB; + break; + } + +/* head decoding */ + jpeg_calc_output_dimensions(&(cinfo)); + jpeg_start_decompress(&cinfo); + + w = cinfo.output_width; + h = cinfo.output_height; + + if (change_wh) + { + ie_w = prop->h; + ie_h = prop->w; + } + else + { + ie_w = prop->w; + ie_h = prop->h; + } + + if ((opts->region.w > 0) && (opts->region.h > 0)) + { + region = 1; + + if (prop->rotated) + { + load_region_x = opts->region.x; + load_region_y = opts->region.y; + load_region_w = opts->region.w; + load_region_h = opts->region.h; + + _rotate_region(&opts->region.x, &opts->region.y, &opts->region.w, &opts->region.h, + load_region_x, load_region_y, load_region_w, load_region_h, + w, h, degree, prop->flipped); + } +#ifdef BUILD_LOADER_JPEG_REGION + cinfo.region_x = opts->region.x; + cinfo.region_y = opts->region.y; + cinfo.region_w = opts->region.w; + cinfo.region_h = opts->region.h; +#endif + } + if ((!region) && ((w != ie_w) || (h != ie_h))) + { + // race condition, the file could have change from when we call header + // this test will not solve the problem with region code. + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + if ((region) && + ((ie_w != opts->region.w) || (ie_h != opts->region.h))) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_GENERIC; + return EINA_FALSE; + /* ie_w = opts->region.w; */ + /* ie_h = opts->region.h; */ + /* if (change_wh) */ + /* { */ + /* ie->w = ie_h; */ + /* ie->h = ie_w; */ + /* } */ + /* else */ + /* { */ + /* ie->w = ie_w; */ + /* ie->h = ie_h; */ + /* } */ + } + + if (!(((cinfo.out_color_space == JCS_RGB) && + ((cinfo.output_components == 3) || (cinfo.output_components == 1))) || + ((cinfo.out_color_space == JCS_CMYK) && (cinfo.output_components == 4)))) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + +/* end head decoding */ +/* data decoding */ + if (cinfo.rec_outbuf_height > 16) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + data = alloca(w * 16 * cinfo.output_components); + if ((prop->rotated) && change_wh) + { + ptr2 = malloc(w * h * sizeof(DATA32)); + ptr_rotate = ptr2; + } + else + ptr2 = pixels; + + if (!ptr2) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + /* We handle first CMYK (4 components) */ + if (cinfo.output_components == 4) + { + // FIXME: handle region + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 4); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + if (cinfo.saw_Adobe_marker) + { + for (x = 0; x < w; x++) + { + /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */ + /* that is C is replaces by 255 - C, etc...*/ + /* See the comment below for the computation of RGB values from CMYK ones. */ + *ptr2 = + (0xff000000) | + ((ptr[0] * ptr[3] / 255) << 16) | + ((ptr[1] * ptr[3] / 255) << 8) | + ((ptr[2] * ptr[3] / 255)); + ptr += 4; + ptr2++; + } + } + else + { + for (x = 0; x < w; x++) + { + /* Conversion from CMYK to RGB is done in 2 steps: */ + /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */ + /* after computation, if C, M, Y and K are between 0 and 1, we have: */ + /* R = (1 - C) * (1 - K) * 255 */ + /* G = (1 - M) * (1 - K) * 255 */ + /* B = (1 - Y) * (1 - K) * 255 */ + /* libjpeg stores CMYK values between 0 and 255, */ + /* so we replace C by C * 255 / 255, etc... and we obtain: */ + /* R = (255 - C) * (255 - K) / 255 */ + /* G = (255 - M) * (255 - K) / 255 */ + /* B = (255 - Y) * (255 - K) / 255 */ + /* with C, M, Y and K between 0 and 255. */ + *ptr2 = + (0xff000000) | + (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) | + (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) | + (((255 - ptr[2]) * (255 - ptr[3]) / 255)); + ptr += 4; + ptr2++; + } + } + } + } + else + { + // if line # > region last line, break + if (l >= (opts->region.y + opts->region.h)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + /*jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_FALSE;*/ + } + // els if scan block intersects region start or later + else if ((l + scans) > + (opts->region.y)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= opts->region.y) && + ((y + l) < (opts->region.y + opts->region.h))) + { + ptr += opts->region.x; + if (cinfo.saw_Adobe_marker) + { + for (x = 0; x < opts->region.w; x++) + { + /* According to libjpeg doc, Photoshop inverse the values of C, M, Y and K, */ + /* that is C is replaces by 255 - C, etc...*/ + /* See the comment below for the computation of RGB values from CMYK ones. */ + *ptr2 = + (0xff000000) | + ((ptr[0] * ptr[3] / 255) << 16) | + ((ptr[1] * ptr[3] / 255) << 8) | + ((ptr[2] * ptr[3] / 255)); + ptr += 4; + ptr2++; + } + } + else + { + for (x = 0; x < opts->region.w; x++) + { + /* Conversion from CMYK to RGB is done in 2 steps: */ + /* CMYK => CMY => RGB (see http://www.easyrgb.com/index.php?X=MATH) */ + /* after computation, if C, M, Y and K are between 0 and 1, we have: */ + /* R = (1 - C) * (1 - K) * 255 */ + /* G = (1 - M) * (1 - K) * 255 */ + /* B = (1 - Y) * (1 - K) * 255 */ + /* libjpeg stores CMYK values between 0 and 255, */ + /* so we replace C by C * 255 / 255, etc... and we obtain: */ + /* R = (255 - C) * (255 - K) / 255 */ + /* G = (255 - M) * (255 - K) / 255 */ + /* B = (255 - Y) * (255 - K) / 255 */ + /* with C, M, Y and K between 0 and 255. */ + *ptr2 = + (0xff000000) | + (((255 - ptr[0]) * (255 - ptr[3]) / 255) << 16) | + (((255 - ptr[1]) * (255 - ptr[3]) / 255) << 8) | + (((255 - ptr[2]) * (255 - ptr[3]) / 255)); + ptr += 4; + ptr2++; + } + } + ptr += (4 * (w - (opts->region.x + opts->region.w))); + } + else + ptr += (4 * w); + } + } + } + } + } + /* We handle then RGB with 3 components */ + else if (cinfo.output_components == 3) + { +/* + double t; + if (region) + { + // debug for now + printf("R| %p %5ix%5i %s: %5i %5i %5ix%5i - ", + ie, + ie->w, ie->h, + ie->file, + opts->region.x, + opts->region.y, + opts->region.w, + opts->region.h); + } + t = get_time(); + */ + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 3); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]); + ptr += 3; + ptr2++; + } + } + } + else + { + // if line # > region last line, break + // but not return immediately for rotation job + if (l >= (opts->region.y + opts->region.h)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + } + // else if scan block intersects region start or later + else if ((l + scans) > + (opts->region.y)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= opts->region.y) && + ((y + l) < (opts->region.y + opts->region.h))) + { + ptr += (3 * opts->region.x); + for (x = 0; x < opts->region.w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[1], ptr[2]); + ptr += 3; + ptr2++; + } + ptr += (3 * (w - (opts->region.x + opts->region.w))); + } + else + ptr += (3 * w); + } + } + } + } +/* + t = get_time() - t; + printf("%3.3f\n", t); + */ + } + /* We finally handle RGB with 1 component */ + else if (cinfo.output_components == 1) + { + for (i = 0; (int)i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + if (!region) + { + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]); + ptr++; + ptr2++; + } + } + } + else + { + // if line # > region last line, break + if (l >= (opts->region.y + opts->region.h)) + { + line_done = EINA_TRUE; + /* if rotation flag is set , we have to rotate image */ + goto done; + /*jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE;*/ + } + // els if scan block intersects region start or later + else if ((l + scans) > + (opts->region.y)) + { + for (y = 0; y < scans; y++) + { + if (((y + l) >= opts->region.y) && + ((y + l) < (opts->region.y + opts->region.h))) + { + ptr += opts->region.x; + for (x = 0; x < opts->region.w; x++) + { + *ptr2 = ARGB_JOIN(0xff, ptr[0], ptr[0], ptr[0]); + ptr++; + ptr2++; + } + ptr += w - (opts->region.x + opts->region.w); + } + else + ptr += w; + } + } + } + } + } + /* if rotation operation need, rotate it */ +done: + + if (prop->rotated) + { + DATA32 *to; + int hw; + + hw = w * h; + to = pixels; + + switch (degree) + { + case 90: + if (prop->flipped) + _rotate_change_wh(to + hw - 1, ptr_rotate, w, h, hw - 1, -h); + else + _rotate_change_wh(to + h - 1, ptr_rotate, w, h, -hw - 1, h); + break; + case 180: + if (prop->flipped) + _flip_vertical(to, w, h); + else + _rotate_180(to, w, h); + break; + case 270: + if (prop->flipped) + _rotate_change_wh(to, ptr_rotate, w, h, -hw + 1, h); + else + _rotate_change_wh(to + hw - h, ptr_rotate, w, h, hw + 1, -h); + break; + default: + if (prop->flipped) + _flip_horizontal(to, w, h); + break; + } + if (ptr_rotate) + { + free(ptr_rotate); + ptr_rotate = NULL; + } + if (region) + { + opts->region.x = load_region_x; + opts->region.y = load_region_y; + opts->region.w = load_region_w; + opts->region.h = load_region_h; + } + } + + if (line_done) + { + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_FALSE; + } + /* end data decoding */ + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + _evas_jpeg_membuf_src_term(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +#if 0 /* not used at the moment */ +static Eina_Bool +evas_image_load_file_data_jpeg_alpha_internal(Image_Entry *ie, FILE *f, int *error) +{ + int w, h; + struct jpeg_decompress_struct cinfo; + struct _JPEG_error_mgr jerr; + DATA8 *ptr, *line[16], *data; + DATA32 *ptr2; + int x, y, l, i, scans, prevy; + + if (!f) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + cinfo.err = jpeg_std_error(&(jerr.pub)); + jerr.pub.error_exit = _JPEGFatalErrorHandler; + jerr.pub.emit_message = _JPEGErrorHandler2; + jerr.pub.output_message = _JPEGErrorHandler; + if (setjmp(jerr.setjmp_buffer)) + { + jpeg_destroy_decompress(&cinfo); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, f); + jpeg_read_header(&cinfo, TRUE); + cinfo.do_fancy_upsampling = FALSE; + cinfo.do_block_smoothing = FALSE; + jpeg_start_decompress(&cinfo); + +/* head decoding */ + ie->w = w = cinfo.output_width; + ie->h = h = cinfo.output_height; +/* end head decoding */ +/* data decoding */ + if (cinfo.rec_outbuf_height > 16) + { + jpeg_destroy_decompress(&cinfo); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + data = alloca(w * 16 * 3); + if (!ie->flags.loaded) + { + jpeg_destroy_decompress(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; + } + ptr2 = evas_cache_image_pixels(ie); + prevy = 0; + if (cinfo.output_components == 3) + { + for (i = 0; i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w * 3); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = + ((*ptr2) & 0x00ffffff) | + (((ptr[0] + ptr[1] + ptr[2]) / 3) << 24); + ptr += 3; + ptr2++; + } + } + } + } + else if (cinfo.output_components == 1) + { + for (i = 0; i < cinfo.rec_outbuf_height; i++) + line[i] = data + (i * w); + for (l = 0; l < h; l += cinfo.rec_outbuf_height) + { + jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); + scans = cinfo.rec_outbuf_height; + if ((h - l) < scans) scans = h - l; + ptr = data; + for (y = 0; y < scans; y++) + { + for (x = 0; x < w; x++) + { + *ptr2 = + ((*ptr2) & 0x00ffffff) | + ((ptr[0]) << 24); + ptr++; + ptr2++; + } + } + } + } +/* end data decoding */ + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} +#endif + +static void * +evas_image_load_file_open_jpeg(Eina_File *f, Eina_Stringshare *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_jpeg(void *loader_data) +{ + free(loader_data); +} + +static Eina_Bool +evas_image_load_file_head_jpeg(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val; + + opts = loader->opts; + f = loader->f; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + val = evas_image_load_file_head_jpeg_internal(&prop->w, &prop->h, + &prop->scale, &prop->rotated, + &prop->flipped, + opts, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + return val; +} + +static Eina_Bool +evas_image_load_file_data_jpeg(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + void *map; + Eina_Bool val = EINA_FALSE; + + f = loader->f; + opts = loader->opts; + + map = eina_file_map_all(f, EINA_FILE_WILLNEED); + if (!map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + goto on_error; + } + + val = evas_image_load_file_data_jpeg_internal(opts, prop, pixels, + map, eina_file_size_get(f), + error); + + eina_file_map_free(f, map); + + on_error: + return val; +} + +static Evas_Image_Load_Func evas_image_load_jpeg_func = +{ + evas_image_load_file_open_jpeg, + evas_image_load_file_close_jpeg, + evas_image_load_file_head_jpeg, + evas_image_load_file_data_jpeg, + NULL, + EINA_TRUE, + EINA_TRUE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_jpeg_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "jpeg", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, jpeg); + +#ifndef EVAS_STATIC_BUILD_JPEG +EVAS_EINA_MODULE_DEFINE(image_loader, jpeg); +#endif + diff --git a/src/modules/evas/image_loaders/pmaps/evas_image_load_pmaps.c b/src/modules/evas/image_loaders/pmaps/evas_image_load_pmaps.c new file mode 100644 index 0000000000..0a15181089 --- /dev/null +++ b/src/modules/evas/image_loaders/pmaps/evas_image_load_pmaps.c @@ -0,0 +1,591 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +#define FILE_BUFFER_SIZE 1024 * 32 +#define FILE_BUFFER_UNREAD_SIZE 16 + +/* The buffer to load pmaps images */ +typedef struct Pmaps_Buffer Pmaps_Buffer; + +struct Pmaps_Buffer +{ + Eina_File *file; + unsigned char *map; + size_t position; + + /* the buffer */ + DATA8 buffer[FILE_BUFFER_SIZE]; + DATA8 unread[FILE_BUFFER_UNREAD_SIZE]; + DATA8 *current; + DATA8 *end; + char type[3]; + unsigned char unread_len:7; + unsigned char last_buffer:1; + + /* image properties */ + int w; + int h; + int max; + + /* interface */ + int (*int_get) (Pmaps_Buffer *b, int *val); + int (*color_get) (Pmaps_Buffer *b, DATA32 *color); +}; + +/* internal used functions */ +static Eina_Bool pmaps_buffer_open(Pmaps_Buffer *b, Eina_File *f, Eina_Bool header, int *error); +static void pmaps_buffer_close(Pmaps_Buffer *b); +static Eina_Bool pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error); +static int pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val); +static int pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color); +static int pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color); +static int pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *color); + +static size_t pmaps_buffer_plain_update(Pmaps_Buffer *b); +static size_t pmaps_buffer_raw_update(Pmaps_Buffer *b); +static int pmaps_buffer_comment_skip(Pmaps_Buffer *b); + +static void * +evas_image_load_file_open_pmaps(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_pmaps(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_pmaps(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + Pmaps_Buffer b; + + if (!pmaps_buffer_open(&b, f, EINA_TRUE, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + if (!pmaps_buffer_header_parse(&b, error)) + { + pmaps_buffer_close(&b); + return EINA_FALSE; + } + + prop->w = b.w; + prop->h = b.h; + + pmaps_buffer_close(&b); + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static Eina_Bool +evas_image_load_file_data_pmaps(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + Pmaps_Buffer b; + int size; + DATA32 *ptr; + Eina_Bool r = EINA_FALSE; + + if (!pmaps_buffer_open(&b, f, EINA_FALSE, error)) + goto on_error; + + if (!pmaps_buffer_header_parse(&b, error)) + goto on_error; + + size = b.w * b.h; + if ((int) prop->w != b.w || + (int) prop->h != b.h) + goto on_error; + + ptr = pixels; + if (b.type[1] != '4') + { + while (size > 0 && b.color_get(&b, ptr)) + { + size--; + ptr++; + } + } + else + { + while (size > 0 + && (b.current != b.end || pmaps_buffer_raw_update(&b))) + { + int i; + + for (i = 7; i >= 0 && size > 0; i--) + { + if (*b.current & (1 << i)) + *ptr = 0xff000000; + else + *ptr = 0xffffffff; + ptr++; + size--; + } + b.current++; + } + } + + /* if there are some pix missing, give them a proper default */ + memset(ptr, 0xff, 4 * size); + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + on_error: + pmaps_buffer_close(&b); + return r; +} + +/* internal used functions */ +static Eina_Bool +pmaps_buffer_open(Pmaps_Buffer *b, Eina_File *f, Eina_Bool header, int *error) +{ + size_t len; + + b->file = f; + b->map = eina_file_map_all(b->file, header ? EINA_FILE_RANDOM : EINA_FILE_SEQUENTIAL); + if (!b->map) + { + *error = EVAS_LOAD_ERROR_DOES_NOT_EXIST; + return EINA_FALSE; + } + + b->position = 0; + *b->buffer = 0; + *b->unread = 0; + b->last_buffer = 0; + b->unread_len = 0; + + len = pmaps_buffer_plain_update(b); + + if (len < 3) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + eina_file_map_free(b->file, b->map); + b->map = NULL; + return EINA_FALSE; + } + + /* copy the type */ + b->type[0] = b->buffer[0]; + b->type[1] = b->buffer[1]; + b->type[2] = 0; + /* skip the PX */ + b->current = b->buffer + 2; + + *error = EVAS_LOAD_ERROR_NONE; + return EINA_TRUE; +} + +static void +pmaps_buffer_close(Pmaps_Buffer *b) +{ + if (b->file) + { + if (b->map) eina_file_map_free(b->file, b->map); + b->map = NULL; + b->file = NULL; + } +} + +static Eina_Bool +pmaps_buffer_header_parse(Pmaps_Buffer *b, int *error) +{ + /* if there is no P at the beginning it is not a file we can parse */ + if (b->type[0] != 'P') + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + /* get the width */ + if (!pmaps_buffer_plain_int_get(b, &(b->w)) || b->w < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + /* get the height */ + if (!pmaps_buffer_plain_int_get(b, &(b->h)) || b->h < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + /* get the maximum value. P1 and P4 don't have a maximum value. */ + if (!(b->type[1] == '1' || b->type[1] == '4') + && (!pmaps_buffer_plain_int_get(b, &(b->max)) || b->max < 1)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + /* set up the color get callback */ + switch (b->type[1]) + { + /* Black and White */ + case '1': + b->color_get = pmaps_buffer_plain_bw_get; + break; + case '4': + /* Binary black and white use another format */ + b->color_get = NULL; + break; + case '2': + case '5': + b->color_get = pmaps_buffer_gray_get; + break; + case '3': + case '6': + b->color_get = pmaps_buffer_rgb_get; + break; + case '7': + /* XXX write me */ + return 0; + break; + default: + return 0; + } + /* set up the int get callback */ + switch (b->type[1]) + { + /* RAW */ + case '5': + case '6': + if (b->max < 256) + b->int_get = pmaps_buffer_1byte_int_get; + else + b->int_get = pmaps_buffer_2byte_int_get; + + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + b->current++; + break; + /* Plain */ + case '2': + case '3': + b->int_get = pmaps_buffer_plain_int_get; + break; + /* Black and White Bitmaps don't use that callback */ + case '1': + case '4': + b->int_get = NULL; + /* we need to skip the next character fpr P4 it + * doesn't hurt if we do it for the P1 as well */ + b->current++; + break; + } + return 1; +} + +static size_t +pmaps_buffer_plain_update(Pmaps_Buffer *b) +{ + size_t r; + size_t max; + + /* if we already are in the last buffer we can not update it */ + if (b->last_buffer) + return 0; + + /* if we have unread bytes we need to put them before the new read + * stuff */ + if (b->unread_len) + memcpy(b->buffer, b->unread, b->unread_len); + + max = FILE_BUFFER_SIZE - b->unread_len - 1; + if (b->position + max > eina_file_size_get(b->file)) + max = eina_file_size_get(b->file) - b->position; + + memcpy(&b->buffer[b->unread_len], b->map + b->position, max); + b->position += max; + r = max + b->unread_len; + + /* we haven't read anything nor have we bytes in the unread buffer */ + if (r == 0) + { + b->buffer[0] = '\0'; + b->end = b->buffer; + b->last_buffer = 1; + return 0; + } + + if (r < FILE_BUFFER_SIZE - 1) + { + /*we reached eof */ ; + b->last_buffer = 1; + } + + b->buffer[r] = 0; + + b->unread[0] = '\0'; + b->unread_len = 0; + + b->buffer[r] = '\0'; + b->current = b->buffer; + b->end = b->buffer + r; + + return r; +} + +static size_t +pmaps_buffer_raw_update(Pmaps_Buffer *b) +{ + size_t r; + size_t max; + + if (b->last_buffer) + return 0; + + if (b->unread_len) + memcpy(b->buffer, b->unread, b->unread_len); + + max = FILE_BUFFER_SIZE - b->unread_len; + if (b->position + max > eina_file_size_get(b->file)) + max = eina_file_size_get(b->file) - b->position; + + memcpy(&b->buffer[b->unread_len], b->map + b->position, max); + b->position += max; + r = max + b->unread_len; + + if (r < FILE_BUFFER_SIZE) + { + /*we reached eof */ + b->last_buffer = 1; + } + + b->end = b->buffer + r; + b->current = b->buffer; + + if (b->unread_len) + { + /* the buffer is now read */ + *b->unread = 0; + b->unread_len = 0; + } + + return r; +} + +static int +pmaps_buffer_plain_int_get(Pmaps_Buffer *b, int *val) +{ + char *start; + DATA8 lastc; + + /* first skip all white space + * Note: we are skipping here actually every character than is not + * a digit */ + while (!isdigit(*b->current)) + { + if (*b->current == '\0') + { + if (!pmaps_buffer_plain_update(b)) + return 0; + + continue; + } + if (*b->current == '#' && !pmaps_buffer_comment_skip(b)) + return 0; + b->current++; + } + + start = (char *)b->current; + /* now find the end of the number */ + while (isdigit(*b->current)) + b->current++; + + lastc = *b->current; + *b->current = '\0'; + *val = atoi(start); + *b->current = lastc; + + return 1; +} + +static int +pmaps_buffer_1byte_int_get(Pmaps_Buffer *b, int *val) +{ + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val = *b->current; + b->current++; + + return 1; +} +static int +pmaps_buffer_2byte_int_get(Pmaps_Buffer *b, int *val) +{ + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val = (int)(*b->current << 8); + b->current++; + + /* are we at the end of the buffer? */ + if (b->current == b->end && !pmaps_buffer_raw_update(b)) + return 0; + + *val |= *b->current; + b->current++; + + return 1; +} + +static int +pmaps_buffer_comment_skip(Pmaps_Buffer *b) +{ + while (*b->current != '\n') + { + if (*b->current == '\0') + { + if (!pmaps_buffer_plain_update(b)) + return 0; + + continue; + } + b->current++; + } + return 1; +} + +static int +pmaps_buffer_rgb_get(Pmaps_Buffer *b, DATA32 *color) +{ + int vr, vg, vb; + + if (!b->int_get(b, &vr) || !b->int_get(b, &vg) || !b->int_get(b, &vb)) + return 0; + + if (b->max != 255) + { + vr = (vr * 255) / b->max; + vg = (vg * 255) / b->max; + vb = (vb * 255) / b->max; + } + if (vr > 255) + vr = 255; + if (vg > 255) + vg = 255; + if (vb > 255) + vb = 255; + + *color = ARGB_JOIN(0xff, vr, vg, vb); + + return 1; +} + +static int +pmaps_buffer_gray_get(Pmaps_Buffer *b, DATA32 *color) +{ + int val; + + if (!b->int_get(b, &val)) + return 0; + + if (b->max != 255) + val = (val * 255) / b->max; + if (val > 255) + val = 255; + *color = ARGB_JOIN(0xff, val, val, val); + + return 1; +} + +static int +pmaps_buffer_plain_bw_get(Pmaps_Buffer *b, DATA32 *val) +{ + /* first skip all white space + * Note: we are skipping here actually every character than is not + * a digit */ + while (!isdigit(*b->current)) + { + if (*b->current == '\0') + { + if (!pmaps_buffer_raw_update(b)) + return 0; + + continue; + } + if (*b->current == '#' && !pmaps_buffer_comment_skip(b)) + return 0; + b->current++; + } + + if (*b->current == '0') + *val = 0xffffffff; + else + *val = 0xff000000; + + b->current++; + + return 1; +} + +/* external functions */ +Evas_Image_Load_Func evas_image_load_pmaps_func = { + evas_image_load_file_open_pmaps, + evas_image_load_file_close_pmaps, + evas_image_load_file_head_pmaps, + evas_image_load_file_data_pmaps, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) + return 0; + em->functions = (void *)(&evas_image_load_pmaps_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = { + EVAS_MODULE_API_VERSION, + "pmaps", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, pmaps); + +#ifndef EVAS_STATIC_BUILD_PMAPS +EVAS_EINA_MODULE_DEFINE(image_loader, pmaps); +#endif diff --git a/src/modules/evas/image_loaders/png/evas_image_load_png.c b/src/modules/evas/image_loaders/png/evas_image_load_png.c new file mode 100644 index 0000000000..a8e9aa3288 --- /dev/null +++ b/src/modules/evas/image_loaders/png/evas_image_load_png.c @@ -0,0 +1,467 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <png.h> +#include <setjmp.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +#define PNG_BYTES_TO_CHECK 4 + +typedef struct _Evas_PNG_Info Evas_PNG_Info; +struct _Evas_PNG_Info +{ + unsigned char *map; + size_t length; + size_t position; +}; + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + Evas_Image_Load_Opts *opts; +}; + +static const Evas_Colorspace cspace_grey[2] = { + EVAS_COLORSPACE_GRY8, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspace_grey_alpha[2] = { + EVAS_COLORSPACE_AGRY88, + EVAS_COLORSPACE_ARGB8888 +}; + +static void +_evas_image_png_read(png_structp png_ptr, png_bytep out, png_size_t count) +{ + Evas_PNG_Info *epi = png_get_io_ptr(png_ptr); + + if (!epi) return; + if (epi->position == epi->length) return; + + if (epi->position + count > epi->length) count = epi->length - epi->position; + memcpy(out, epi->map + epi->position, count); + epi->position += count; +} + +static void * +evas_image_load_file_open_png(Eina_File *f, Eina_Stringshare *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_png(void *loader_data) +{ + free(loader_data); +} + +static Eina_Bool +evas_image_load_file_head_png(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + + Evas_PNG_Info epi; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + png_uint_32 w32, h32; + int bit_depth, color_type, interlace_type; + char hasa; + Eina_Bool r = EINA_FALSE; + + opts = loader->opts; + f = loader->f; + + hasa = 0; + epi.map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!epi.map) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto close_file; + } + epi.length = eina_file_size_get(f); + epi.position = 0; + + if (epi.length < PNG_BYTES_TO_CHECK) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto close_file; + } + + if (png_sig_cmp(epi.map, 0, PNG_BYTES_TO_CHECK)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto close_file; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + png_set_read_fn(png_ptr, &epi, _evas_image_png_read); + + if (setjmp(png_jmpbuf(png_ptr))) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto close_file; + } + + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32), + (png_uint_32 *) (&h32), &bit_depth, &color_type, + &interlace_type, NULL, NULL); + if ((w32 < 1) || (h32 < 1) || (w32 > IMG_MAX_SIZE) || (h32 > IMG_MAX_SIZE) || + IMG_TOO_BIG(w32, h32)) + { + if (IMG_TOO_BIG(w32, h32)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + if (opts->scale_down_by > 1) + { + prop->w = (int) w32 / opts->scale_down_by; + prop->h = (int) h32 / opts->scale_down_by; + if ((prop->w < 1) || (prop->h < 1)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + } + else + { + prop->w = (int) w32; + prop->h = (int) h32; + } + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) hasa = 1; + switch (color_type) + { + case PNG_COLOR_TYPE_RGB_ALPHA: + hasa = 1; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + hasa = 1; + prop->cspaces = cspace_grey_alpha; + break; + case PNG_COLOR_TYPE_GRAY: + if (!hasa) prop->cspaces = cspace_grey; + break; + } + if (hasa) prop->alpha = 1; + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + close_file: + if (png_ptr) png_destroy_read_struct(&png_ptr, + info_ptr ? &info_ptr : NULL, + NULL); + if (epi.map) eina_file_map_free(f, epi. map); + + return r; +} + +static Eina_Bool +evas_image_load_file_data_png(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Evas_Image_Load_Opts *opts; + Eina_File *f; + + unsigned char *surface; + unsigned char *tmp_line; + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; + Evas_PNG_Info epi; + png_uint_32 w32, h32; + unsigned int pack_offset; + int w, h; + int bit_depth, color_type, interlace_type; + char hasa, passes; + int i, j, p, k; + int scale_ratio = 1, image_w = 0, image_h = 0; + Eina_Bool r = EINA_FALSE; + + opts = loader->opts; + f = loader->f; + + hasa = 0; + + epi.map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!epi.map) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto close_file; + } + epi.length = eina_file_size_get(f); + epi.position = 0; + + if (epi.length < PNG_BYTES_TO_CHECK) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto close_file; + } + + /* if we havent read the header before, set the header data */ + if (png_sig_cmp(epi.map, 0, PNG_BYTES_TO_CHECK)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto close_file; + } + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto close_file; + } + + png_set_read_fn(png_ptr, &epi, _evas_image_png_read); + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto close_file; + } + + png_read_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32), + (png_uint_32 *) (&h32), &bit_depth, &color_type, + &interlace_type, NULL, NULL); + image_w = w32; + image_h = h32; + if (opts->scale_down_by > 1) + { + scale_ratio = opts->scale_down_by; + w32 /= scale_ratio; + h32 /= scale_ratio; + } + if (prop->w != w32 || + prop->h != h32) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + + surface = pixels; + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + /* expand transparency entry -> alpha channel if present */ + png_set_tRNS_to_alpha(png_ptr); + hasa = 1; + } + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) hasa = 1; + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) hasa = 1; + if (hasa) prop->alpha = 1; + + /* Prep for transformations... ultimately we want ARGB */ + /* expand palette -> RGB if necessary */ + if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); + /* expand gray (w/reduced bits) -> 8-bit RGB if necessary */ + if ((color_type == PNG_COLOR_TYPE_GRAY) || + (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + { + if (prop->cspace == EVAS_COLORSPACE_ARGB8888) + png_set_gray_to_rgb(png_ptr); + if (bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); + } + /* reduce 16bit color -> 8bit color if necessary */ + if (bit_depth > 8) png_set_strip_16(png_ptr); + /* pack all pixels to byte boundaries */ + png_set_packing(png_ptr); + + w = w32; + h = h32; + + switch (prop->cspace) + { + case EVAS_COLORSPACE_ARGB8888: + /* we want ARGB */ +#ifdef WORDS_BIGENDIAN + png_set_swap_alpha(png_ptr); + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE); +#else + png_set_bgr(png_ptr); + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); +#endif + pack_offset = sizeof(DATA32); + break; + case EVAS_COLORSPACE_AGRY88: + /* we want AGRY */ +#ifdef WORDS_BIGENDIAN + png_set_swap_alpha(png_ptr); + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE); +#else + if (!hasa) png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); +#endif + pack_offset = sizeof(DATA16); + break; + case EVAS_COLORSPACE_GRY8: pack_offset = sizeof(DATA8); break; + default: abort(); + } + + passes = png_set_interlace_handling(png_ptr); + + /* we read image line by line if scale down was set */ + if (scale_ratio == 1) + { + for (p = 0; p < passes; p++) + { + for (i = 0; i < h; i++) + png_read_row(png_ptr, surface + (i * w * pack_offset), NULL); + } + png_read_end(png_ptr, info_ptr); + } + else + { + unsigned char *src_ptr, *dst_ptr; + + dst_ptr = surface; + if (passes == 1) + { + tmp_line = (unsigned char *) alloca(image_w * pack_offset); + for (i = 0; i < h; i++) + { + png_read_row(png_ptr, tmp_line, NULL); + src_ptr = tmp_line; + for (j = 0; j < w; j++) + { + for (k = 0; k < (int)pack_offset; k++) + dst_ptr[k] = src_ptr[k]; + dst_ptr += pack_offset; + src_ptr += scale_ratio * pack_offset; + } + for (j = 0; j < (scale_ratio - 1); j++) + png_read_row(png_ptr, tmp_line, NULL); + } + } + else + { + unsigned char *pixels2 = malloc(image_w * image_h * pack_offset); + + if (pixels2) + { + for (p = 0; p < passes; p++) + { + for (i = 0; i < image_h; i++) + png_read_row(png_ptr, pixels2 + (i * image_w * pack_offset), NULL); + } + + for (i = 0; i < h; i++) + { + src_ptr = pixels2 + (i * scale_ratio * image_w * pack_offset); + for (j = 0; j < w; j++) + { + for (k = 0; k < (int)pack_offset; k++) + dst_ptr[k] = src_ptr[k]; + src_ptr += scale_ratio * pack_offset; + dst_ptr += pack_offset; + } + } + free(pixels2); + } + } + } + + prop->premul = EINA_TRUE; + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + close_file: + if (png_ptr) png_destroy_read_struct(&png_ptr, + info_ptr ? &info_ptr : NULL, + NULL); + if (epi.map) eina_file_map_free(f, epi.map); + return r; +} + +static Evas_Image_Load_Func evas_image_load_png_func = +{ + evas_image_load_file_open_png, + evas_image_load_file_close_png, + evas_image_load_file_head_png, + evas_image_load_file_data_png, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_png_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "png", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, png); + +#ifndef EVAS_STATIC_BUILD_PNG +EVAS_EINA_MODULE_DEFINE(image_loader, png); +#endif diff --git a/src/modules/evas/image_loaders/psd/evas_image_load_psd.c b/src/modules/evas/image_loaders/psd/evas_image_load_psd.c new file mode 100644 index 0000000000..1554e18821 --- /dev/null +++ b/src/modules/evas/image_loaders/psd/evas_image_load_psd.c @@ -0,0 +1,937 @@ +#define _XOPEN_SOURCE 600 + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +typedef struct _PSD_Header PSD_Header; + +typedef enum _PSD_Mode + { + PSD_GREYSCALE = 1, + PSD_INDEXED = 2, + PSD_RGB = 3, + PSD_CMYK = 4 + } PSD_Mode; + +struct _PSD_Header +{ + unsigned char signature[4]; + unsigned short version; + unsigned char reserved[9]; + unsigned short channels; + unsigned int height; + unsigned int width; + unsigned short depth; + + unsigned short channel_num; + + PSD_Mode mode; +}; + +enum { + READ_COMPRESSED_SUCCESS, + READ_COMPRESSED_ERROR_FILE_CORRUPT, + READ_COMPRESSED_ERROR_FILE_READ_ERROR +}; + +static Eina_Bool get_compressed_channels_length(PSD_Header *Head, + const unsigned char *map, size_t length, size_t *position, + unsigned short *rle_table, + unsigned int *chanlen); + +static int +read_ushort(const unsigned char *map, size_t length, size_t *position, unsigned short *ret) +{ + if (*position + 2 > length) return 0; + // FIXME: need to check order + *ret = (map[(*position) + 0] << 8) | map[(*position) + 1]; + *position += 2; + return 1; +} + +static int +read_uint(const unsigned char *map, size_t length, size_t *position, unsigned int *ret) +{ + if (*position + 4 > length) return 0; + // FIXME: need to check order + *ret = ARGB_JOIN(map[(*position) + 0], map[(*position) + 1], map[(*position) + 2], map[(*position) + 3]); + *position += 4; + return 1; +} + +static int +read_block(const unsigned char *map, size_t length, size_t *position, void *target, size_t size) +{ + if (*position + size > length) return 0; + memcpy(target, map + *position, size); + *position += size; + return 1; +} + +// Internal function used to get the Psd header from the current file. +Eina_Bool +psd_get_header(PSD_Header *header, const unsigned char *map, size_t length, size_t *position) +{ + unsigned short tmp; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_block(map, length, position, header->signature, 4)); + CHECK_RET(read_ushort(map, length, position, &header->version)); + CHECK_RET(read_block(map, length, position, header->reserved, 6)); + CHECK_RET(read_ushort(map, length, position, &header->channels)); + CHECK_RET(read_uint(map, length, position, &header->height)); + CHECK_RET(read_uint(map, length, position, &header->width)); + CHECK_RET(read_ushort(map, length, position, &header->depth)); + + CHECK_RET(read_ushort(map, length, position, &tmp)); + header->mode = tmp; + +#undef CHECK_RET + + /* fprintf(stderr, "<<<<<<<<<<<\nsignature : %c%c%c%c\n", */ + /* header->signature[0], */ + /* header->signature[1], */ + /* header->signature[2], */ + /* header->signature[3]); */ + /* fprintf(stderr, "version : %i\n", header->version); */ + /* fprintf(stderr, "channels : %i\n", header->channels); */ + /* fprintf(stderr, "width x height : %dx%d\n", header->width, header->height); */ + /* fprintf(stderr, "depth : %i\n", header->depth); */ + /* fprintf(stderr, "mode : %i\n>>>>>>>>>>>>\n", header->mode); */ + + return EINA_TRUE; +} + + +// Internal function used to check if the HEADER is a valid Psd header. +Eina_Bool +is_psd(PSD_Header *header) +{ + if (strncmp((char*)header->signature, "8BPS", 4)) + return EINA_FALSE; + if (header->version != 1) + return EINA_FALSE; + if (header->channels < 1 || header->channels > 24) + return EINA_FALSE; + if (header->height < 1 || header->width < 1) + return EINA_FALSE; + if (header->depth != 1 && header->depth != 8 && header->depth != 16) + return EINA_FALSE; + + return EINA_TRUE; +} + +static void * +evas_image_load_file_open_psd(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_psd(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_psd(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + void *map; + size_t length; + size_t position; + PSD_Header header; + Eina_Bool correct; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_NONE; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + length = eina_file_size_get(f); + position = 0; + if (!map || length < 1) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + correct = psd_get_header(&header, map, length, &position); + if (!correct || !is_psd(&header)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + prop->w = header.width; + prop->h = header.height; + if (header.channels != 3) + prop->alpha = 1; + + r = EINA_TRUE; + + on_error: + eina_file_map_free(f, map); + return r; +} + +static unsigned int +read_compressed_channel(const unsigned char *map, size_t length, size_t *position, + const unsigned int channel_length EINA_UNUSED, + unsigned int size, + unsigned char* channel) +{ + // FIXME: what does channel_length means, and why is it not used + unsigned int i; + char headbyte, c; + +#define CHECK_RET(Call) \ + if (!Call) return READ_COMPRESSED_ERROR_FILE_READ_ERROR; \ + + for (i = 0; i < size; ) + { + CHECK_RET(read_block(map, length, position, &headbyte, 1)); + + if (headbyte >= 0) + { + if (i + headbyte > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + CHECK_RET(read_block(map, length, position, channel + i, headbyte + 1)); + + i += headbyte + 1; + } + else if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(read_block(map, length, position, &c, 1)); + + run = c; + /* if (run == -1) */ + /* return READ_COMPRESSED_ERROR_FILE_READ_ERROR; */ + + if (i + (-headbyte + 1) > size) + return READ_COMPRESSED_ERROR_FILE_CORRUPT; + + memset(channel + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + +#undef CHECK_RET + + return READ_COMPRESSED_SUCCESS; +} + + +Eina_Bool +psd_get_data(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned char *buffer, Eina_Bool compressed, + int *error) +{ + unsigned int c, x, y, numchan, bps, bpc, bpp; + unsigned int pixels_count; + unsigned char *channel = NULL; + unsigned char *data = NULL; + + // Added 01-07-2009: This is needed to correctly load greyscale and + // paletted images. + switch (head->mode) + { + case PSD_GREYSCALE: + case PSD_INDEXED: + numchan = 1; + break; + default: + numchan = 3; + } + + bpp = head->channels; + bpc = head->depth / 8; + pixels_count = head->width * head->height; + + data = malloc(sizeof (unsigned char) * pixels_count * bpp); + if (!data) return EINA_FALSE; + + channel = malloc(sizeof (unsigned char) * pixels_count * bpc); + if (!channel) + { + free(data); + return EINA_FALSE; + } + + bps = head->width * head->channels * bpc; + // @TODO: Add support for this in, though I have yet to run across a .psd + // file that uses this. + if (compressed && bpc == 2) + { + free(data); + free(channel); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + +#define CHECK_RET(Call) \ + if (!Call) \ + { \ + free(data); \ + free(channel); \ + return EINA_FALSE; \ + } + + if (!compressed) + { + if (bpc == 1) + { + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(read_block(map, length, position, tmp, pixels_count)); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count)); + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + // previous formula was : (old / 255 * new / 255) * 255 + newval = (*tmp) * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + else + { + int bps2; + + bps2 = bps / 2; + + // iCurImage->Bpc == 2 + for (c = 0; c < numchan; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count * 2)); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr++) + { + ((unsigned short*)data)[y + x + c] = *shortptr; + } + } + } + + // Accumulate any remaining channels into a single alpha channel + //@TODO: This needs to be changed for greyscale images. + for (; c < head->channels; c++) + { + unsigned short *shortptr = (unsigned short*) channel; + + CHECK_RET(read_block(map, length, position, channel, pixels_count * 2)); + + for (y = 0; y < head->height * bps2; y += bps2) + { + for (x = 0; x < (unsigned int)bps2; x += bpp, shortptr++) + { + unsigned int newval; + + newval = *shortptr * ((unsigned short*)data)[y + x + 3]; + + ((unsigned short*)data)[y + x + 3] = newval >> 16; + } + } + } + } + } + else + { + unsigned short *rle_table; + unsigned int *chanlen; + + rle_table = alloca(head->height * head->channel_num * sizeof (unsigned short)); + chanlen = alloca(head->channel_num * sizeof (unsigned int)); + if (!get_compressed_channels_length(head, map, length, position, rle_table, chanlen)) + goto file_read_error; + + for (c = 0; c < numchan; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(map, length, position, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + data[y + x + c] = *tmp; + } + } + } + + // Initialize the alpha channel to solid + //@TODO: This needs to be changed for greyscale images. + if (head->channels >= 4) + { + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp) + { + data[y + x + 3] = 255; + } + } + + for (; c < head->channels; c++) + { + unsigned char *tmp = channel; + int err; + + err = read_compressed_channel(map, length, position, + chanlen[c], + pixels_count, + channel); + if (err == READ_COMPRESSED_ERROR_FILE_CORRUPT) + goto file_corrupt; + else if (err == READ_COMPRESSED_ERROR_FILE_READ_ERROR) + goto file_read_error; + + for (y = 0; y < head->height * bps; y += bps) + { + for (x = 0; x < bps; x += bpp, tmp++) + { + unsigned short newval; + + newval = *tmp * data[y + x + 3]; + + data[y + x + 3] = newval >> 8; + } + } + } + } + } + + if (bpp == 3) + { + for (x = 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 3 + 2]; + buffer[x * 4 + 1] = data[x * 3 + 1]; + buffer[x * 4 + 2] = data[x * 3 + 0]; + buffer[x * 4 + 3] = 255; + } + } + else + { + // BRGA to RGBA + for (x= 0; x < pixels_count; x++) + { + buffer[x * 4 + 0] = data[x * 4 + 2]; + buffer[x * 4 + 1] = data[x * 4 + 1]; + buffer[x * 4 + 2] = data[x * 4 + 0]; + buffer[x * 4 + 3] = data[x * 4 + 3]; + } + } + + free(channel); + free(data); + return EINA_TRUE; + +#undef CHECK_RET + + file_corrupt: + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + file_read_error: + free(channel); + free(data); + + return EINA_FALSE; +} + + +Eina_Bool +get_single_channel(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned char *buffer, + Eina_Bool compressed) +{ + unsigned int i, bpc; + char headbyte; + int c; + int pixels_count; + + bpc = (head->depth / 8); + pixels_count = head->width * head->height; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + if (!compressed) + { + if (bpc == 1) + { + CHECK_RET(read_block(map, length, position, buffer, pixels_count)); + } + else + { // Bpc == 2 + CHECK_RET(read_block(map, length, position, buffer, pixels_count * 2)); + } + } + else + { + for (i = 0; i < (unsigned int)pixels_count; ) + { + CHECK_RET(read_block(map, length, position, &headbyte, 1)); + + if (headbyte >= 0) + { // && HeadByte <= 127 + CHECK_RET(read_block(map, length, position, buffer + i, headbyte + 1)); + + i += headbyte + 1; + } + if (headbyte >= -127 && headbyte <= -1) + { + int run; + + CHECK_RET(read_block(map, length, position, &c, 1)); + + run = c; + if (run == -1) return EINA_FALSE; + + memset(buffer + i, run, -headbyte + 1); + i += -headbyte + 1; + } + } + } + +#undef CHECK_RET + + return EINA_TRUE; +} + +Eina_Bool +read_psd_grey(void *pixels, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + CHECK_RET(read_uint(map, length, position, &resource_size)); + // Read the 'image resources section' + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + head->channel_num = head->channels; + // Temporary to read only one channel...some greyscale .psd files have 2. + head->channels = 1; + + switch (head->depth) + { + case 8: + case 16: + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + if (!psd_get_data(head, map, length, position, pixels, compressed, error)) + goto cleanup_error; + + return EINA_TRUE; + +#undef CHECK_RET + + cleanup_error: + return EINA_FALSE; +} + + +Eina_Bool +read_psd_indexed(void *pixels, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!(Call)) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + CHECK_RET(!(color_mode % 3)); + /* + Palette = (unsigned char*)malloc(Colormode); + if (Palette == NULL) + return EINA_FALSE; + if (fread(&Palette, 1, Colormode, file) != Colormode) + goto cleanup_error; + */ + // Skip over the 'color mode data section' + *position += color_mode; + + // Read the 'image resources section' + CHECK_RET(read_uint(map, length, position, &resource_size)); + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + if (head->channels != 1 || head->depth != 8) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + head->channel_num = head->channels; + + if (!psd_get_data(head, map, length, position, pixels, compressed, error)) + return EINA_FALSE; + return EINA_TRUE; + +#undef CHECK_RET +} + +Eina_Bool +read_psd_rgb(void *pixels, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info; + unsigned short compressed; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + // Read the 'image resources section' + CHECK_RET(read_uint(map, length, position, &resource_size)); + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + head->channel_num = head->channels; + + switch (head->depth) + { + case 8: + case 16: + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + if (!psd_get_data(head, map, length, position, pixels, compressed, error)) + return EINA_FALSE; + + return EINA_TRUE; + +#undef CHECK_RET +} + +Eina_Bool +read_psd_cmyk(Evas_Image_Property *prop, void *pixels, PSD_Header *head, const unsigned char *map, size_t length, size_t *position, int *error) +{ + unsigned int color_mode, resource_size, misc_info, size, j, data_size; + unsigned short compressed; + unsigned int format, type; + unsigned char *kchannel = NULL; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + +#define CHECK_RET(Call) \ + if (!Call) return EINA_FALSE; + + CHECK_RET(read_uint(map, length, position, &color_mode)); + // Skip over the 'color mode data section' + *position += color_mode; + + CHECK_RET(read_uint(map, length, position, &resource_size)); + // Read the 'image resources section' + *position += resource_size; + + CHECK_RET(read_uint(map, length, position, &misc_info)); + *position += misc_info; + + CHECK_RET(read_ushort(map, length, position, &compressed)); + + switch (head->channels) + { + case 4: + format = 0x1907; + head->channel_num = 4; + head->channels = 3; + break; + case 5: + format = 0x1908; + head->channel_num = 5; + head->channels = 4; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + switch (head->depth) + { + case 8: + type = 1; + break; + case 16: + type = 2; + break; + default: + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + return EINA_FALSE; + } + + if (!psd_get_data(head, map, length, position, pixels, compressed, error)) + goto cleanup_error; + + size = type * prop->w * prop->h; + kchannel = malloc(size); + if (kchannel == NULL) + goto cleanup_error; + if (!get_single_channel(head, map, length, position, kchannel, compressed)) + goto cleanup_error; + + data_size = head->channels * type * prop->w * prop->h; + if (format == 0x1907) + { + unsigned char *tmp = pixels; + const unsigned char *limit = tmp + data_size; + + for (j = 0; tmp < limit; tmp++, j++) + { + int k; + + for (k = 0; k < 3; k++) + *tmp = (*tmp * kchannel[j]) >> 8; + + // FIXME: tmp[i+3] = 255; + } + } + else + { // RGBA + unsigned char *tmp = pixels; + const unsigned char *limit = tmp + data_size; + + // The KChannel array really holds the alpha channel on this one. + for (j = 0; tmp < limit; tmp += 4, j++) + { + tmp[0] = (tmp[0] * tmp[3]) >> 8; + tmp[1] = (tmp[1] * tmp[3]) >> 8; + tmp[2] = (tmp[2] * tmp[3]) >> 8; + tmp[3] = kchannel[j]; // Swap 'K' with alpha channel. + } + } + + r = EINA_TRUE; + + cleanup_error: + free(kchannel); + return r; +} + +static Eina_Bool +evas_image_load_file_data_psd(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + + void *map; + size_t length; + size_t position; + PSD_Header header; + Eina_Bool bpsd = EINA_FALSE; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + length = eina_file_size_get(f); + position = 0; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (!map || length < 1) + goto on_error; + + *error = EVAS_LOAD_ERROR_GENERIC; + if (!psd_get_header(&header, map, length, &position) || !is_psd(&header)) + goto on_error; + + if (header.width != prop->w || + header.height != prop->h) + goto on_error; + + *error = EVAS_LOAD_ERROR_NONE; + + switch (header.mode) + { + case PSD_GREYSCALE: // Greyscale + bpsd = read_psd_grey(pixels, &header, map, length, &position, error); + break; + case PSD_INDEXED: // Indexed + bpsd = read_psd_indexed(pixels, &header, map, length, &position, error); + break; + case PSD_RGB: // RGB + bpsd = read_psd_rgb(pixels, &header, map, length, &position, error); + prop->premul = EINA_TRUE; + break; + case PSD_CMYK: // CMYK + bpsd = read_psd_cmyk(prop, pixels, &header, map, length, &position, error); + prop->premul = EINA_TRUE; + break; + default : + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + bpsd = EINA_FALSE; + } + + on_error: + if (map) eina_file_map_free(f, map); + + return bpsd; +} + +static Eina_Bool +get_compressed_channels_length(PSD_Header *head, + const unsigned char *map, size_t length, size_t *position, + unsigned short *rle_table, + unsigned int *chanlen) +{ + unsigned int j; + unsigned int c; + + if (!read_block(map, length, position, rle_table, + sizeof (unsigned short) * head->height * head->channel_num)) + return EINA_FALSE; + + memset(chanlen, 0, head->channel_num * sizeof(unsigned int)); + for (c = 0; c < head->channel_num; c++) + { + unsigned int i; + + j = c * head->height; + for (i = 0; i < head->height; i++) + { + chanlen[c] += rle_table[i + j]; + } + } + + return EINA_TRUE; +} + +static const Evas_Image_Load_Func evas_image_load_psd_func = { + evas_image_load_file_open_psd, + evas_image_load_file_close_psd, + evas_image_load_file_head_psd, + evas_image_load_file_data_psd, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_psd_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = + { + EVAS_MODULE_API_VERSION, + "psd", + "none", + { + module_open, + module_close + } + }; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, psd); + + +#ifndef EVAS_STATIC_BUILD_PSD +EVAS_EINA_MODULE_DEFINE(image_loader, psd); +#endif diff --git a/src/modules/evas/image_loaders/tga/evas_image_load_tga.c b/src/modules/evas/image_loaders/tga/evas_image_load_tga.c new file mode 100644 index 0000000000..74d2c620af --- /dev/null +++ b/src/modules/evas/image_loaders/tga/evas_image_load_tga.c @@ -0,0 +1,593 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +/* TGA pixel formats */ +#define TGA_TYPE_MAPPED 1 // handle +#define TGA_TYPE_COLOR 2 +#define TGA_TYPE_GRAY 3 +#define TGA_TYPE_MAPPED_RLE 9 // handle +#define TGA_TYPE_COLOR_RLE 10 +#define TGA_TYPE_GRAY_RLE 11 + +/* TGA header flags */ +#define TGA_DESC_ABITS 0x0f +#define TGA_DESC_HORIZONTAL 0x10 +#define TGA_DESC_VERTICAL 0x20 + +#define TGA_SIGNATURE "TRUEVISION-XFILE" + +typedef struct _tga_header tga_header; +typedef struct _tga_footer tga_footer; + +struct _tga_header +{ + unsigned char idLength; + unsigned char colorMapType; + unsigned char imageType; + unsigned char colorMapIndexLo, colorMapIndexHi; + unsigned char colorMapLengthLo, colorMapLengthHi; + unsigned char colorMapSize; + unsigned char xOriginLo, xOriginHi; + unsigned char yOriginLo, yOriginHi; + unsigned char widthLo, widthHi; + unsigned char heightLo, heightHi; + unsigned char bpp; + unsigned char descriptor; +} __attribute__((packed)); + +struct _tga_footer +{ + unsigned int extensionAreaOffset; + unsigned int developerDirectoryOffset; + char signature[16]; + char dot; + char null; +} __attribute__((packed)); + +static void * +evas_image_load_file_open_tga(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_tga(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_tga(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + unsigned char *seg = NULL, *filedata; + tga_header *header; + tga_footer *footer, tfooter; + char hasa = 0; + int w, h, bpp; + int x, y; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + if (eina_file_size_get(f) < (off_t)(sizeof(tga_header) + sizeof(tga_footer))) + goto close_file; + seg = eina_file_map_all(f, EINA_FILE_RANDOM); + if (seg == NULL) goto close_file; + filedata = seg; + + header = (tga_header *)filedata; + // no unaligned data accessed, so ok + footer = (tga_footer *)(filedata + (eina_file_size_get(f) - sizeof(tga_footer))); + memcpy((unsigned char *)(&tfooter), + (unsigned char *)footer, + sizeof(tga_footer)); + //printf("0\n"); + if (!memcmp(tfooter.signature, TGA_SIGNATURE, sizeof(tfooter.signature))) + { + if ((tfooter.dot == '.') && (tfooter.null == 0)) + { + // footer is there and matches. this is a tga file - any problems now + // are a corrupt file + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + } + } +// else goto close_file; + //printf("1\n"); + + filedata = (unsigned char *)filedata + sizeof(tga_header); + switch (header->imageType) + { + case TGA_TYPE_COLOR_RLE: + case TGA_TYPE_GRAY_RLE: +// rle = 1; + break; + case TGA_TYPE_COLOR: + case TGA_TYPE_GRAY: +// rle = 0; + break; + default: + goto close_file; + } + bpp = header->bpp; + if (!((bpp == 32) || (bpp == 24) || (bpp == 16) || (bpp == 8))) + goto close_file; + if ((bpp == 32) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + if ((bpp == 16) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + // don't handle colormapped images + if ((header->colorMapType) != 0) + goto close_file; + // if colormap size is anything other than legal sizes or 0 - not real tga + if (!((header->colorMapSize == 0) || + (header->colorMapSize == 15) || + (header->colorMapSize == 16) || + (header->colorMapSize == 24) || + (header->colorMapSize == 32))) + goto close_file; + x = (header->xOriginHi << 8) | (header->xOriginLo); + y = (header->yOriginHi << 8) | (header->yOriginLo); + w = (header->widthHi << 8) | header->widthLo; + h = (header->heightHi << 8) | header->heightLo; + // x origin gerater that width, y origin greater than height - wrong file + if ((x >= w) || (y >= h)) + goto close_file; + // if descriptor has either of the top 2 bits set... not tga + if (header->descriptor & 0xc0) + goto close_file; + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + goto close_file; + + prop->w = w; + prop->h = h; + if (hasa) prop->alpha = 1; + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + +close_file: + if (seg != NULL) eina_file_map_free(f, seg); + return r; +} + +static Eina_Bool +evas_image_load_file_data_tga(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + unsigned char *seg = NULL, *filedata; + tga_header *header; + tga_footer *footer, tfooter; + char hasa = 0, footer_present = 0, vinverted = 0, rle = 0; + int w = 0, h = 0, x, y, bpp; + off_t size; + unsigned int *surface, *dataptr; + unsigned int datasize; + unsigned char *bufptr, *bufend; + int abits; + Eina_Bool res = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + if (eina_file_size_get(f) < (off_t)(sizeof(tga_header) + sizeof(tga_footer))) + goto close_file; + seg = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (seg == NULL) goto close_file; + filedata = seg; + size = eina_file_size_get(f); + + header = (tga_header *)filedata; + // no unaligned data accessed, so ok + footer = (tga_footer *)(filedata + (size - sizeof(tga_footer))); + memcpy((unsigned char *)&tfooter, + (unsigned char *)footer, + sizeof(tga_footer)); + if (!memcmp(tfooter.signature, TGA_SIGNATURE, sizeof(tfooter.signature))) + { + if ((tfooter.dot == '.') && (tfooter.null == 0)) + { + // footer is there and matches. this is a tga file - any problems now + // are a corrupt file + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + footer_present = 1; + } + } + + filedata = (unsigned char *)filedata + sizeof(tga_header); + vinverted = !(header->descriptor & TGA_DESC_VERTICAL); + switch (header->imageType) + { + case TGA_TYPE_COLOR_RLE: + case TGA_TYPE_GRAY_RLE: + rle = 1; + break; + case TGA_TYPE_COLOR: + case TGA_TYPE_GRAY: + rle = 0; + break; + default: + goto close_file; + } + bpp = header->bpp; + if (!((bpp == 32) || (bpp == 24) || (bpp == 16) || (bpp == 8))) + goto close_file; + if ((bpp == 32) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + if ((bpp == 16) && (header->descriptor & TGA_DESC_ABITS)) hasa = 1; + abits = header->descriptor & TGA_DESC_ABITS; + // don't handle colormapped images + if ((header->colorMapType) != 0) + goto close_file; + // if colormap size is anything other than legal sizes or 0 - not real tga + if (!((header->colorMapSize == 0) || + (header->colorMapSize == 15) || + (header->colorMapSize == 16) || + (header->colorMapSize == 24) || + (header->colorMapSize == 32))) + goto close_file; + x = (header->xOriginHi << 8) | (header->xOriginLo); + y = (header->yOriginHi << 8) | (header->yOriginLo); + w = (header->widthHi << 8) | header->widthLo; + h = (header->heightHi << 8) | header->heightLo; + // x origin gerater that width, y origin greater than height - wrong file + if ((x >= w) || (y >= h)) + goto close_file; + // if descriptor has either of the top 2 bits set... not tga + if (header->descriptor & 0xc0) + goto close_file; + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + goto close_file; + + if ((w != (int)prop->w) || (h != (int)prop->h)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto close_file; + } + surface = pixels; + + datasize = size - sizeof(tga_header) - header->idLength; + if (footer_present) + datasize -= sizeof(tga_footer); + + bufptr = filedata + header->idLength; + bufend = filedata + datasize; + + if (!rle) + { + for (y = 0; y < h; y++) + { + if (vinverted) + /* some TGA's are stored upside-down! */ + dataptr = surface + ((h - y - 1) * w); + else + dataptr = surface + (y * w); + switch (bpp) + { + case 32: + for (x = 0; (x < w) && ((bufptr + 4) <= bufend); x++) + { + if (hasa) + { + int a = bufptr[3]; + + switch (abits) + { + case 1: + a = (a << 7) | (a << 6) | (a << 5) | (a << 4) | (a << 3) | (a << 2) | (a << 1) | (a); + case 2: + a = (a << 6) | (a << 4) | (a << 2) | (a); + case 3: + a = (a << 5) | (a << 2) | (a >> 1); + case 4: + a = (a << 4) | (a); + case 5: + a = (a << 3) | (a >> 2); + case 6: + a = (a << 2) | (a >> 4); + case 7: + a = (a << 1) | (a >> 6); + default: + break; + } + *dataptr = ARGB_JOIN(a, bufptr[2], bufptr[1], bufptr[0]); + } + else + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 4; + } + break; + case 24: + for (x = 0; (x < w) && ((bufptr + 3) <= bufend); x++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 3; + } + break; + case 16: + for (x = 0; (x < w) && ((bufptr + 3) <= bufend); x++) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + bufptr += 2; + } + break; + case 8: + for (x = 0; (x < w) && ((bufptr + 1) <= bufend); x++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[0], bufptr[0], bufptr[0]); + dataptr++; + bufptr += 1; + } + break; + default: + break; + } + } + } + else + { + int count, i; + unsigned char val; + unsigned int *dataend; + + dataptr = surface; + dataend = dataptr + (w * h); + while ((bufptr < bufend) && (dataptr < dataend)) + { + val = *bufptr; + bufptr++; + count = (val & 0x7f) + 1; + if (val & 0x80) // rel packet + { + switch (bpp) + { + case 32: + if (bufptr < (bufend - 4)) + { + unsigned char r, g, b; + int a = bufptr[3]; + + switch (abits) + { + case 1: + a = (a << 7) | (a << 6) | (a << 5) | (a << 4) | (a << 3) | (a << 2) | (a << 1) | (a); + case 2: + a = (a << 6) | (a << 4) | (a << 2) | (a); + case 3: + a = (a << 5) | (a << 2) | (a >> 1); + case 4: + a = (a << 4) | (a); + case 5: + a = (a << 3) | (a >> 2); + case 6: + a = (a << 2) | (a >> 4); + case 7: + a = (a << 1) | (a >> 6); + default: + break; + } + r = bufptr[2]; + g = bufptr[1]; + b = bufptr[0]; + if (!hasa) a = 0xff; + bufptr += 4; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + } + } + break; + case 24: + if (bufptr < (bufend - 3)) + { + unsigned char r, g, b; + + r = bufptr[2]; + g = bufptr[1]; + b = bufptr[0]; + bufptr += 3; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, r, g, b); + dataptr++; + } + } + break; + case 16: + if (bufptr < (bufend - 2)) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + bufptr += 2; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + } + } + break; + case 8: + if (bufptr < (bufend - 1)) + { + unsigned char g; + + g = bufptr[0]; + bufptr += 1; + for (i = 0; (i < count) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, g, g, g); + dataptr++; + } + } + break; + default: + break; + } + } + else // raw + { + switch (bpp) + { + case 32: + for (i = 0; (i < count) && (bufptr < (bufend - 4)) && (dataptr < dataend); i++) + { + if (hasa) +// *dataptr = ARGB_JOIN(255 - bufptr[3], bufptr[2], bufptr[1], bufptr[0]); + *dataptr = ARGB_JOIN(bufptr[3], bufptr[2], bufptr[1], bufptr[0]); + else + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 4; + } + break; + case 24: + for (i = 0; (i < count) && (bufptr < (bufend - 3)) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[2], bufptr[1], bufptr[0]); + dataptr++; + bufptr += 3; + } + break; + case 16: + for (i = 0; (i < count) && (bufptr < (bufend - 2)) && (dataptr < dataend); i++) + { + unsigned char r, g, b, a; + unsigned short tmp; + + tmp = + (((unsigned short)bufptr[1]) << 8) | + (((unsigned short)bufptr[0])); + r = (tmp >> 7) & 0xf8; r |= r >> 5; + g = (tmp >> 2) & 0xf8; g |= g >> 5; + b = (tmp << 3) & 0xf8; b |= b >> 5; + a = 0xff; + if ((hasa) && (tmp & 0x8000)) a = 0; + *dataptr = ARGB_JOIN(a, r, g, b); + dataptr++; + bufptr += 2; + } + break; + case 8: + for (i = 0; (i < count) && (bufptr < (bufend - 1)) && (dataptr < dataend); i++) + { + *dataptr = ARGB_JOIN(0xff, bufptr[0], bufptr[0], bufptr[0]); + dataptr++; + bufptr += 1; + } + break; + default: + break; + } + } + } + if (vinverted) + { + unsigned int *adv, *adv2, tmp; + + adv = surface; + adv2 = surface + (w * (h - 1)); + for (y = 0; y < (h / 2); y++) + { + for (x = 0; x < w; x++) + { + tmp = adv[x]; + adv[x] = adv2[x]; + adv2[x] = tmp; + } + adv2 -= w; + adv += w; + } + } + } + + prop->premul = EINA_TRUE; + + *error = EVAS_LOAD_ERROR_NONE; + res = EINA_TRUE; + + close_file: + if (seg != NULL) eina_file_map_free(f, seg); + return res; +} + +static Evas_Image_Load_Func evas_image_load_tga_func = +{ + evas_image_load_file_open_tga, + evas_image_load_file_close_tga, + evas_image_load_file_head_tga, + evas_image_load_file_data_tga, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_tga_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "tga", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, tga); + +#ifndef EVAS_STATIC_BUILD_TGA +EVAS_EINA_MODULE_DEFINE(image_loader, tga); +#endif diff --git a/src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c b/src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c new file mode 100644 index 0000000000..b715017831 --- /dev/null +++ b/src/modules/evas/image_loaders/tgv/evas_image_load_tgv.c @@ -0,0 +1,587 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#ifdef _WIN32 +# include <winsock2.h> +#endif /* ifdef _WIN32 */ + +#ifdef ENABLE_LIBLZ4 +# include <lz4.h> +#else +# include "lz4.h" +#endif + +#include "rg_etc1.h" +#include "Evas_Loader.h" + +#ifdef BUILD_NEON +#include <arm_neon.h> +#endif + +#ifndef WORDS_BIGENDIAN +/* x86 */ +#define A_VAL(p) (((uint8_t *)(p))[3]) +#define R_VAL(p) (((uint8_t *)(p))[2]) +#define G_VAL(p) (((uint8_t *)(p))[1]) +#define B_VAL(p) (((uint8_t *)(p))[0]) +#else +/* ppc */ +#define A_VAL(p) (((uint8_t *)(p))[0]) +#define R_VAL(p) (((uint8_t *)(p))[1]) +#define G_VAL(p) (((uint8_t *)(p))[2]) +#define B_VAL(p) (((uint8_t *)(p))[3]) +#endif + +/************************************************************** + * The TGV file format is oriented around compression mecanism + * that hardware are good at decompressing. We do still provide + * a fully software implementation in case your hardware doesn't + * handle it. As OpenGL is pretty bad at handling border of + * texture, we do duplicate the first pixels of every border. + * + * This file format is designed to compress/decompress things + * in block area. Giving opportunity to store really huge file + * and only decompress/compress them as we need. Note that region + * only work with software decompression as we don't have a sane + * way to duplicate border to avoid artifact when scaling texture. + * + * The file format is as follow : + * - char magic[4]: "TGV1" + * - uint8_t block_size (real block size = (4 << bits[0-3], 4 << bits[4-7]) + * - uint8_t algorithm (0 -> ETC1, 1 -> ETC2 RGB, 2 -> ETC2 RGBA, 3 -> ETC1+Alpha) + * - uint8_t options[2] (bitmask: 1 -> lz4, 2 for block-less, 4 -> unpremultiplied) + * - uint32_t width + * - uint32_t height + * - blocks[] + * - 0 length encoded compress size (if length == 64 * block_size => no compression) + * - lzma encoded etc1 block + * + * If the format is ETC1+Alpha (algo = 3), then a second image is encoded + * in ETC1 right after the first one, and it contains grey-scale alpha + * values. + **************************************************************/ + +// FIXME: wondering if we should support mipmap +// TODO: support ETC1+ETC2 images (RGB only) + +typedef struct _Evas_Loader_Internal Evas_Loader_Internal; +struct _Evas_Loader_Internal +{ + Eina_File *f; + + Eina_Rectangle region; + + struct { + unsigned int width; + unsigned int height; + } block; + struct { + unsigned int width; + unsigned int height; + } size; + + Evas_Colorspace cspace; + + Eina_Bool compress : 1; + Eina_Bool blockless : 1; // Special mode used when copying data directly + Eina_Bool unpremul : 1; +}; + +static const Evas_Colorspace cspaces_etc1[2] = { + EVAS_COLORSPACE_ETC1, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_rgb8_etc2[2] = { + EVAS_COLORSPACE_RGB8_ETC2, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_rgba8_etc2_eac[2] = { + EVAS_COLORSPACE_RGBA8_ETC2_EAC, + EVAS_COLORSPACE_ARGB8888 +}; + +static const Evas_Colorspace cspaces_etc1_alpha[2] = { + EVAS_COLORSPACE_ETC1_ALPHA, + EVAS_COLORSPACE_ARGB8888 +}; + +static void * +evas_image_load_file_open_tgv(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts, + Evas_Image_Animated *animated EINA_UNUSED, + int *error) +{ + Evas_Loader_Internal *loader; + + if (eina_file_size_get(f) <= 16) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return NULL; + } + + loader = calloc(1, sizeof (Evas_Loader_Internal)); + if (!loader) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return NULL; + } + + loader->f = eina_file_dup(f); + if (!loader->f) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + free(loader); + return NULL; + } + + if (opts && (opts->region.w > 0) && (opts->region.h > 0)) + { + EINA_RECTANGLE_SET(&loader->region, + opts->region.x, + opts->region.y, + opts->region.w, + opts->region.h); + } + else + { + EINA_RECTANGLE_SET(&loader->region, + 0, 0, + -1, -1); + } + + return loader; +} + + +static void +evas_image_load_file_close_tgv(void *loader_data) +{ + Evas_Loader_Internal *loader = loader_data; + + eina_file_close(loader->f); + free(loader); +} + +static int +roundup(int val, int rup) +{ + if (val >= 0 && rup > 0) + return (val + rup - 1) - ((val + rup - 1) % rup); + return 0; +} + +#define OFFSET_BLOCK_SIZE 4 +#define OFFSET_ALGORITHM 5 +#define OFFSET_OPTIONS 6 +#define OFFSET_WIDTH 8 +#define OFFSET_HEIGHT 12 +#define OFFSET_BLOCKS 16 + +static Eina_Bool +evas_image_load_file_head_tgv(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + Eina_Bool ret = EINA_FALSE; + char *m; + + m = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL); + if (!m) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + if (strncmp(m, "TGV1", 4) != 0) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + switch (m[OFFSET_ALGORITHM] & 0xFF) + { + case 0: + prop->cspaces = cspaces_etc1; + loader->cspace = EVAS_COLORSPACE_ETC1; + prop->alpha = EINA_FALSE; + break; + case 1: + prop->cspaces = cspaces_rgb8_etc2; + loader->cspace = EVAS_COLORSPACE_RGB8_ETC2; + prop->alpha = EINA_FALSE; + break; + case 2: + prop->cspaces = cspaces_rgba8_etc2_eac; + loader->cspace = EVAS_COLORSPACE_RGBA8_ETC2_EAC; + prop->alpha = EINA_TRUE; + break; + case 3: + prop->cspaces = cspaces_etc1_alpha; + loader->cspace = EVAS_COLORSPACE_ETC1_ALPHA; + loader->unpremul = !!(m[OFFSET_OPTIONS] & 0x4); + prop->alpha = EINA_TRUE; + prop->premul = loader->unpremul; + break; + default: + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + loader->compress = m[OFFSET_OPTIONS] & 0x1; + loader->blockless = (m[OFFSET_OPTIONS] & 0x2) != 0; + + loader->size.width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH]))); + loader->size.height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT]))); + + if (loader->blockless) + { + loader->block.width = roundup(loader->size.width + 2, 4); + loader->block.height = roundup(loader->size.height + 2, 4); + } + else + { + loader->block.width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f); + loader->block.height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4); + } + + if (loader->region.w == -1 && loader->region.h == -1) + { + loader->region.w = loader->size.width; + loader->region.h = loader->size.height; + } + else + { + Eina_Rectangle r; + + // ETC1 colorspace doesn't work with region + prop->cspaces = NULL; + + EINA_RECTANGLE_SET(&r, 0, 0, loader->size.width, loader->size.height); + if (!eina_rectangle_intersection(&loader->region, &r)) + { + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error; + } + } + + prop->w = loader->size.width; + prop->h = loader->size.height; + prop->borders.l = 1; + prop->borders.t = 1; + prop->borders.r = roundup(prop->w + 2, 4) - prop->w - 1; + prop->borders.b = roundup(prop->h + 2, 4) - prop->h - 1; + + ret = EINA_TRUE; + +on_error: + eina_file_map_free(loader->f, m); + return ret; +} + +static inline unsigned int +_tgv_length_get(const char *m, unsigned int length, unsigned int *offset) +{ + unsigned int r = 0; + unsigned int shift = 0; + + while (*offset < length && ((*m) & 0x80)) + { + r = r | (((*m) & 0x7F) << shift); + shift += 7; + m++; + (*offset)++; + } + if (*offset < length) + { + r = r | (((*m) & 0x7F) << shift); + (*offset)++; + } + + return r; +} + +Eina_Bool +evas_image_load_file_data_tgv(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Evas_Loader_Internal *loader = loader_data; + const char *m; + unsigned int *p = pixels; + unsigned char *p_etc = pixels; + char *buffer = NULL; + Eina_Rectangle master; + unsigned int block_length; + unsigned int length, offset; + unsigned int x, y; + unsigned int block_count; + unsigned int etc_width = 0; + unsigned int etc_block_size; + Eina_Bool r = EINA_FALSE; + int num_planes = 1, plane, alpha_offset = 0; + + length = eina_file_size_get(loader->f); + offset = OFFSET_BLOCKS; + + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + + m = eina_file_map_all(loader->f, EINA_FILE_WILLNEED); + if (!m) return EINA_FALSE; + + // By definition, prop{.w, .h} == region{.w, .h} + EINA_RECTANGLE_SET(&master, + loader->region.x, loader->region.y, + prop->w, prop->h); + + switch (loader->cspace) + { + case EVAS_COLORSPACE_ETC1: + case EVAS_COLORSPACE_RGB8_ETC2: + etc_block_size = 8; + break; + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: + etc_block_size = 16; + break; + case EVAS_COLORSPACE_ETC1_ALPHA: + etc_block_size = 8; + num_planes = 2; + alpha_offset = ((prop->w + 2 + 3) / 4) * ((prop->h + 2 + 3) / 4) * 8 / sizeof(*p_etc); + break; + default: abort(); + } + etc_width = ((prop->w + 2 + 3) / 4) * etc_block_size; + + switch (prop->cspace) + { + case EVAS_COLORSPACE_ETC1: + case EVAS_COLORSPACE_RGB8_ETC2: + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: + case EVAS_COLORSPACE_ETC1_ALPHA: + if (master.x % 4 || master.y % 4) + abort(); + break; + case EVAS_COLORSPACE_ARGB8888: + // Offset to take duplicated pixels into account + master.x += 1; + master.y += 1; + break; + default: abort(); + } + + if (prop->cspace != EVAS_COLORSPACE_ARGB8888 && prop->cspace != loader->cspace) + { + if (!((prop->cspace == EVAS_COLORSPACE_RGB8_ETC2) && + (loader->cspace == EVAS_COLORSPACE_ETC1))) + goto on_error; + // else: ETC2 is compatible with ETC1 and is preferred + } + + // Allocate space for each ETC block (8 or 16 bytes per 4 * 4 pixels group) + block_count = loader->block.width * loader->block.height / (4 * 4); + if (loader->compress) + buffer = alloca(etc_block_size * block_count); + + for (plane = 0; plane < num_planes; plane++) + for (y = 0; y < loader->size.height + 2; y += loader->block.height) + for (x = 0; x < loader->size.width + 2; x += loader->block.width) + { + Eina_Rectangle current; + const char *data_start; + const char *it; + unsigned int expand_length; + unsigned int i, j; + + block_length = _tgv_length_get(m + offset, length, &offset); + + if (block_length == 0) goto on_error; + + data_start = m + offset; + offset += block_length; + + EINA_RECTANGLE_SET(¤t, x, y, + loader->block.width, loader->block.height); + + if (!eina_rectangle_intersection(¤t, &master)) + continue; + + if (loader->compress) + { + expand_length = LZ4_decompress_fast(data_start, buffer, + block_count * etc_block_size); + // That's an overhead for now, need to be fixed + if (expand_length != block_length) + goto on_error; + } + else + { + buffer = (void*) data_start; + if (block_count * etc_block_size != block_length) + goto on_error; + } + it = buffer; + + for (i = 0; i < loader->block.height; i += 4) + for (j = 0; j < loader->block.width; j += 4, it += etc_block_size) + { + Eina_Rectangle current_etc; + unsigned int temporary[4 * 4]; + unsigned int offset_x, offset_y; + int k, l; + + EINA_RECTANGLE_SET(¤t_etc, x + j, y + i, 4, 4); + + if (!eina_rectangle_intersection(¤t_etc, ¤t)) + continue; + + switch (prop->cspace) + { + case EVAS_COLORSPACE_ARGB8888: + switch (loader->cspace) + { + case EVAS_COLORSPACE_ETC1: + case EVAS_COLORSPACE_ETC1_ALPHA: + if (!rg_etc1_unpack_block(it, temporary, 0)) + { + // TODO: Should we decode as RGB8_ETC2? + fprintf(stderr, "ETC1: Block starting at {%i, %i} is corrupted!\n", x + j, y + i); + continue; + } + break; + case EVAS_COLORSPACE_RGB8_ETC2: + rg_etc2_rgb8_decode_block((uint8_t *) it, temporary); + break; + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: + rg_etc2_rgba8_decode_block((uint8_t *) it, temporary); + break; + default: abort(); + } + + offset_x = current_etc.x - x - j; + offset_y = current_etc.y - y - i; + + if (!plane) + { +#ifdef BUILD_NEON + if (eina_cpu_features_get() & EINA_CPU_NEON) + { + uint32_t *dst = &p[current_etc.x - 1 + (current_etc.y - 1) * master.w]; + uint32_t *src = &temporary[offset_x + offset_y * 4]; + for (k = 0; k < current_etc.h; k++) + { + if (current_etc.w == 4) + vst1q_u32(dst, vld1q_u32(src)); + else if (current_etc.w == 3) + { + vst1_u32(dst, vld1_u32(src)); + *(dst + 2) = *(src + 2); + } + else if (current_etc.w == 2) + vst1_u32(dst, vld1_u32(src)); + else + *dst = *src; + dst += master.w; + src += 4; + } + } + else +#endif + for (k = 0; k < current_etc.h; k++) + { + memcpy(&p[current_etc.x - 1 + (current_etc.y - 1 + k) * master.w], + &temporary[offset_x + (offset_y + k) * 4], + current_etc.w * sizeof (unsigned int)); + } + } + else + { + for (k = 0; k < current_etc.h; k++) + for (l = 0; l < current_etc.w; l++) + { + unsigned int *rgbdata = + &p[current_etc.x - 1 + (current_etc.y - 1 + k) * master.w + l]; + unsigned int *adata = + &temporary[offset_x + (offset_y + k) * 4 + l]; + A_VAL(rgbdata) = G_VAL(adata); + } + } + break; + case EVAS_COLORSPACE_ETC1: + case EVAS_COLORSPACE_RGB8_ETC2: + case EVAS_COLORSPACE_RGBA8_ETC2_EAC: + memcpy(&p_etc[(current_etc.x / 4) * etc_block_size + + (current_etc.y / 4) * etc_width], + it, etc_block_size); + break; + case EVAS_COLORSPACE_ETC1_ALPHA: + memcpy(&p_etc[(current_etc.x / 4) * etc_block_size + + (current_etc.y / 4) * etc_width + + plane * alpha_offset], + it, etc_block_size); + break; + default: + abort(); + } + } // bx,by inside blocks + } // x,y macroblocks + + // TODO: Add support for more unpremultiplied modes (ETC2) + if (prop->cspace == EVAS_COLORSPACE_ARGB8888) + prop->premul = loader->unpremul; // call premul if unpremul data + + r = EINA_TRUE; + *error = EVAS_LOAD_ERROR_NONE; + + on_error: + eina_file_map_free(loader->f, (void*) m); + return r; +} + +Evas_Image_Load_Func evas_image_load_tgv_func = +{ + evas_image_load_file_open_tgv, + evas_image_load_file_close_tgv, + evas_image_load_file_head_tgv, + evas_image_load_file_data_tgv, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_tgv_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "tgv", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, tgv); + +#ifndef EVAS_STATIC_BUILD_TGV +EVAS_EINA_MODULE_DEFINE(image_loader, tgv); +#endif diff --git a/src/modules/evas/image_loaders/tiff/evas_image_load_tiff.c b/src/modules/evas/image_loaders/tiff/evas_image_load_tiff.c new file mode 100644 index 0000000000..943c3e74b2 --- /dev/null +++ b/src/modules/evas/image_loaders/tiff/evas_image_load_tiff.c @@ -0,0 +1,380 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> +#include <tiffio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +static int _evas_loader_tiff_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_tiff_log_dom, __VA_ARGS__) + +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_evas_loader_tiff_log_dom, __VA_ARGS__) + +typedef struct TIFFRGBAImage_Extra TIFFRGBAImage_Extra; +typedef struct TIFFRGBAMap TIFFRGBAMap; + +struct TIFFRGBAImage_Extra { + TIFFRGBAImage rgba; + char pper; + uint32 num_pixels; + uint32 py; +}; + +struct TIFFRGBAMap { + tdata_t mem; + toff_t size; +}; + +static tsize_t +_evas_tiff_RWProc(thandle_t handle EINA_UNUSED, + tdata_t data EINA_UNUSED, + tsize_t size EINA_UNUSED) +{ + return 0; +} + +static toff_t +_evas_tiff_SeekProc(thandle_t handle EINA_UNUSED, + toff_t size EINA_UNUSED, + int origin EINA_UNUSED) +{ + return 0; +} + +static int +_evas_tiff_CloseProc(thandle_t handle EINA_UNUSED) +{ + return 0; +} + +static toff_t +_evas_tiff_SizeProc(thandle_t handle) +{ + TIFFRGBAMap *map = (TIFFRGBAMap*) handle; + + return map->size; +} + +static int +_evas_tiff_MapProc(thandle_t handle, tdata_t *mem, toff_t *size) +{ + TIFFRGBAMap *map = (TIFFRGBAMap*) handle; + + *mem = map->mem; + *size = map->size; + + return 1; +} + +static void +_evas_tiff_UnmapProc(thandle_t handle EINA_UNUSED, tdata_t data EINA_UNUSED, toff_t size EINA_UNUSED) +{ +} + +static void * +evas_image_load_file_open_tiff(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_tiff(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_tiff(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + char txt[1024]; + TIFFRGBAImage tiff_image; + TIFFRGBAMap tiff_map; + TIFF *tif = NULL; + unsigned char *map; + uint16 magic_number; + Eina_Bool r = EINA_FALSE; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map || eina_file_size_get(f) < 3) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + magic_number = *((uint16*) map); + + if ((magic_number != TIFF_BIGENDIAN) /* Checks if actually tiff file */ + && (magic_number != TIFF_LITTLEENDIAN)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + tiff_map.mem = map; + tiff_map.size = eina_file_size_get(f); + + tif = TIFFClientOpen("evas", "rM", &tiff_map, + _evas_tiff_RWProc, _evas_tiff_RWProc, + _evas_tiff_SeekProc, _evas_tiff_CloseProc, + _evas_tiff_SizeProc, + _evas_tiff_MapProc, _evas_tiff_UnmapProc); + if (!tif) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + strcpy(txt, "Evas Tiff loader: cannot be processed by libtiff"); + if (!TIFFRGBAImageOK(tif, txt)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + strcpy(txt, "Evas Tiff loader: cannot begin reading tiff"); + if (!TIFFRGBAImageBegin(& tiff_image, tif, 1, txt)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + if (tiff_image.alpha != EXTRASAMPLE_UNSPECIFIED) + prop->alpha = 1; + if ((tiff_image.width < 1) || (tiff_image.height < 1) || + (tiff_image.width > IMG_MAX_SIZE) || (tiff_image.height > IMG_MAX_SIZE) || + IMG_TOO_BIG(tiff_image.width, tiff_image.height)) + { + if (IMG_TOO_BIG(tiff_image.width, tiff_image.height)) + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + else + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error_end; + } + prop->w = tiff_image.width; + prop->h = tiff_image.height; + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + on_error_end: + TIFFRGBAImageEnd(&tiff_image); + on_error: + if (tif) TIFFClose(tif); + if (map) eina_file_map_free(f, map); + return r; +} + +static Eina_Bool +evas_image_load_file_data_tiff(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + char txt[1024]; + TIFFRGBAImage_Extra rgba_image; + TIFFRGBAMap rgba_map; + TIFF *tif = NULL; + unsigned char *map; + uint32 *rast = NULL; + uint32 num_pixels; + int x, y; + uint16 magic_number; + Eina_Bool res = EINA_FALSE; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map || eina_file_size_get(f) < 3) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + magic_number = *((uint16*) map); + + if ((magic_number != TIFF_BIGENDIAN) /* Checks if actually tiff file */ + && (magic_number != TIFF_LITTLEENDIAN)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + rgba_map.mem = map; + rgba_map.size = eina_file_size_get(f); + + tif = TIFFClientOpen("evas", "rM", &rgba_map, + _evas_tiff_RWProc, _evas_tiff_RWProc, + _evas_tiff_SeekProc, _evas_tiff_CloseProc, + _evas_tiff_SizeProc, + _evas_tiff_MapProc, _evas_tiff_UnmapProc); + if (!tif) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + strcpy(txt, "Evas Tiff loader: cannot be processed by libtiff"); + if (!TIFFRGBAImageOK(tif, txt)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + strcpy(txt, "Evas Tiff loader: cannot begin reading tiff"); + if (!TIFFRGBAImageBegin((TIFFRGBAImage *) & rgba_image, tif, 0, txt)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + + if (rgba_image.rgba.alpha != EXTRASAMPLE_UNSPECIFIED) + prop->alpha = 1; + if ((rgba_image.rgba.width != prop->w) || + (rgba_image.rgba.height != prop->h)) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error_end; + } + + rgba_image.num_pixels = num_pixels = prop->w * prop->h; + + rgba_image.pper = rgba_image.py = 0; + rast = (uint32 *) _TIFFmalloc(sizeof(uint32) * num_pixels); + + if (!rast) + { + ERR("Evas Tiff loader: out of memory"); + + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error_end; + } + if (rgba_image.rgba.bitspersample == 8) + { + if (!TIFFRGBAImageGet((TIFFRGBAImage *) &rgba_image, rast, + rgba_image.rgba.width, rgba_image.rgba.height)) + { + _TIFFfree(rast); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error_end; + } + } + else + { + INF("channel bits == %i", (int)rgba_image.rgba.samplesperpixel); + } + /* process rast -> image rgba. really same as prior code anyway just simpler */ + for (y = 0; y < (int)prop->h; y++) + { + DATA32 *pix, *pd; + uint32 *ps, pixel; + unsigned int a, r, g, b; + unsigned int nas = 0; + + pix = pixels; + pd = pix + ((prop->h - y - 1) * prop->w); + ps = rast + (y * prop->w); + for (x = 0; x < (int)prop->w; x++) + { + pixel = *ps; + a = TIFFGetA(pixel); + r = TIFFGetR(pixel); + g = TIFFGetG(pixel); + b = TIFFGetB(pixel); + if (!prop->alpha) a = 255; + if ((rgba_image.rgba.alpha == EXTRASAMPLE_UNASSALPHA) && + (a < 255)) + { + r = (r * (a + 1)) >> 8; + g = (g * (a + 1)) >> 8; + b = (b * (a + 1)) >> 8; + } + *pd = ARGB_JOIN(a, r, g, b); + + if (a == 0xff) nas++; + ps++; + pd++; + } + + if ((ALPHA_SPARSE_INV_FRACTION * nas) >= (prop->w * prop->h)) + prop->alpha_sparse = EINA_TRUE; + } + + _TIFFfree(rast); + + *error = EVAS_LOAD_ERROR_NONE; + res = EINA_TRUE; + + on_error_end: + TIFFRGBAImageEnd((TIFFRGBAImage *) & rgba_image); + on_error: + if (tif) TIFFClose(tif); + if (map) eina_file_map_free(f, map); + return res; +} + +static Evas_Image_Load_Func evas_image_load_tiff_func = +{ + evas_image_load_file_open_tiff, + evas_image_load_file_close_tiff, + evas_image_load_file_head_tiff, + evas_image_load_file_data_tiff, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + _evas_loader_tiff_log_dom = eina_log_domain_register + ("evas-tiff", EVAS_DEFAULT_LOG_COLOR); + if (_evas_loader_tiff_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + em->functions = (void *)(&evas_image_load_tiff_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + eina_log_domain_unregister(_evas_loader_tiff_log_dom); +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "tiff", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, tiff); + +#ifndef EVAS_STATIC_BUILD_TIFF +EVAS_EINA_MODULE_DEFINE(image_loader, tiff); +#endif diff --git a/src/modules/evas/image_loaders/wbmp/evas_image_load_wbmp.c b/src/modules/evas/image_loaders/wbmp/evas_image_load_wbmp.c new file mode 100644 index 0000000000..70c507824b --- /dev/null +++ b/src/modules/evas/image_loaders/wbmp/evas_image_load_wbmp.c @@ -0,0 +1,208 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +static int +read_mb(unsigned int *data, void *map, size_t length, size_t *position) +{ + int ac = 0, ct; + unsigned char buf; + + for (ct = 0;;) + { + if ((ct++) == 5) return -1; + if (*position > length) return -1; + buf = ((unsigned char *) map)[(*position)++]; + ac = (ac << 7) | (buf & 0x7f); + if ((buf & 0x80) == 0) break; + } + *data = ac; + return 0; +} + +static void * +evas_image_load_file_open_wbmp(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_wbmp(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_wbmp(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + void *map = NULL; + size_t position = 0; + size_t length; + unsigned int type, w, h; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_GENERIC; + length = eina_file_size_get(f); + if (length <= 4) goto bail; + + map = eina_file_map_all(f, EINA_FILE_RANDOM); + if (!map) goto bail; + + if (read_mb(&type, map, length, &position) < 0) goto bail; + + if (type != 0) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto bail; + } + + position++; /* skipping one byte */ + if (read_mb(&w, map, length, &position) < 0) goto bail; + if (read_mb(&h, map, length, &position) < 0) goto bail; + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto bail; + } + + prop->w = w; + prop->h = h; + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + bail: + if (map) eina_file_map_free(f, map); + return r; +} + +static Eina_Bool +evas_image_load_file_data_wbmp(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + void *map = NULL; + size_t position = 0; + size_t length; + unsigned int type, w, h; + unsigned int line_length; + unsigned char *line = NULL; + int cur = 0, x, y; + DATA32 *dst_data; + Eina_Bool r = EINA_FALSE; + + *error = EVAS_LOAD_ERROR_GENERIC; + length = eina_file_size_get(f); + if (length <= 4) goto bail; + + map = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + if (!map) goto bail; + + if (read_mb(&type, map, length, &position) < 0) goto bail; + position++; /* skipping one byte */ + if (read_mb(&w, map, length, &position) < 0) goto bail; + if (read_mb(&h, map, length, &position) < 0) goto bail; + + if (type != 0) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto bail; + } + + if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) || + IMG_TOO_BIG(w, h)) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto bail; + } + + if (prop->w != w || prop->h != h) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto bail; + } + + dst_data = pixels; + + line_length = (prop->w + 7) >> 3; + + for (y = 0; y < (int)prop->h; y++) + { + if (position + line_length > length) goto bail; + line = ((unsigned char*) map) + position; + position += line_length; + for (x = 0; x < (int)prop->w; x++) + { + int idx = x >> 3; + int offset = 1 << (0x07 - (x & 0x07)); + if (line[idx] & offset) dst_data[cur] = 0xffffffff; + else dst_data[cur] = 0xff000000; + cur++; + } + } + + *error = EVAS_LOAD_ERROR_NONE; + r = EINA_TRUE; + + bail: + if (map) eina_file_map_free(f, map); + return r; +} + +static Evas_Image_Load_Func evas_image_load_wbmp_func = +{ + evas_image_load_file_open_wbmp, + evas_image_load_file_close_wbmp, + evas_image_load_file_head_wbmp, + evas_image_load_file_data_wbmp, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_wbmp_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "wbmp", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, wbmp); + +#ifndef EVAS_STATIC_BUILD_WBMP +EVAS_EINA_MODULE_DEFINE(image_loader, wbmp); +#endif diff --git a/src/modules/evas/image_loaders/webp/evas_image_load_webp.c b/src/modules/evas/image_loaders/webp/evas_image_load_webp.c new file mode 100644 index 0000000000..50f52d0e25 --- /dev/null +++ b/src/modules/evas/image_loaders/webp/evas_image_load_webp.c @@ -0,0 +1,156 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <webp/decode.h> + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +static Eina_Bool +evas_image_load_file_check(Eina_File *f, void *map, + unsigned int *w, unsigned int *h, Eina_Bool *alpha, + int *error) +{ + WebPDecoderConfig config; + + if (eina_file_size_get(f) < 30) return EINA_FALSE; + + if (!WebPInitDecoderConfig(&config)) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + if (WebPGetFeatures(map, 30, &config.input) != VP8_STATUS_OK) + { + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + *w = config.input.width; + *h = config.input.height; + *alpha = config.input.has_alpha; + + return EINA_TRUE; +} + +static void * +evas_image_load_file_open_webp(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_webp(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_webp(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + Eina_Bool r; + void *data; + + *error = EVAS_LOAD_ERROR_NONE; + + data = eina_file_map_all(f, EINA_FILE_RANDOM); + + r = evas_image_load_file_check(f, data, + &prop->w, &prop->h, &prop->alpha, + error); + + if (data) eina_file_map_free(f, data); + return r; +} + +static Eina_Bool +evas_image_load_file_data_webp(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + void *data = NULL; + void *decoded = NULL; + void *surface = NULL; + int width, height; + + data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL); + + surface = pixels; + + decoded = WebPDecodeBGRA(data, eina_file_size_get(f), &width, &height); + if (!decoded) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto free_data; + } + *error = EVAS_LOAD_ERROR_NONE; + + if ((int) prop->w != width || + (int) prop->h != height) + goto free_data; + + // XXX: this copy of the surface is inefficient + memcpy(surface, decoded, width * height * 4); + prop->premul = EINA_TRUE; + + free_data: + if (data) eina_file_map_free(f, data); + free(decoded); + + return EINA_TRUE; +} + +static Evas_Image_Load_Func evas_image_load_webp_func = +{ + evas_image_load_file_open_webp, + evas_image_load_file_close_webp, + evas_image_load_file_head_webp, + evas_image_load_file_data_webp, + NULL, + EINA_TRUE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + em->functions = (void *)(&evas_image_load_webp_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "webp", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, webp); + +#ifndef EVAS_STATIC_BUILD_WEBP +EVAS_EINA_MODULE_DEFINE(image_loader, webp); +#endif diff --git a/src/modules/evas/image_loaders/xpm/evas_image_load_xpm.c b/src/modules/evas/image_loaders/xpm/evas_image_load_xpm.c new file mode 100644 index 0000000000..cf9b9df075 --- /dev/null +++ b/src/modules/evas/image_loaders/xpm/evas_image_load_xpm.c @@ -0,0 +1,747 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef HAVE_EVIL +# include <Evil.h> +#endif + +#include "evas_common_private.h" +#include "evas_private.h" + +static int _evas_loader_xpm_log_dom = -1; + +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_evas_loader_xpm_log_dom, __VA_ARGS__) + +static Eina_File *rgb_txt; +static void *rgb_txt_map; + +static int +_xpm_hexa_int(const char *s, int len) +{ + const char *hexa = "0123456789abcdef"; + const char *lookup; + int i, c, r; + + for (r = 0, i = 0; i < len; i++) + { + c = s[i]; + lookup = strchr(hexa, tolower(c)); + r = (r << 4) | (lookup ? lookup - hexa : 0); + } + + return r; +} + +static void +xpm_parse_color(char *color, int *r, int *g, int *b) +{ + char *tmp; + char *max; + char *endline; + char buf[4096]; + + /* is a #ff00ff like color */ + if (color[0] == '#') + { + int len; + + len = strlen(color) - 1; + if (len < 13) + { + + len /= 3; + *r = _xpm_hexa_int(&(color[1 + (0 * len)]), len); + *g = _xpm_hexa_int(&(color[1 + (1 * len)]), len); + *b = _xpm_hexa_int(&(color[1 + (2 * len)]), len); + if (len == 1) + { + *r = (*r << 4) | *r; + *g = (*g << 4) | *g; + *b = (*b << 4) | *b; + } + else if (len > 2) + { + *r >>= (len - 2) * 4; + *g >>= (len - 2) * 4; + *b >>= (len - 2) * 4; + } + } + return; + } + /* look in rgb txt database */ + if (!rgb_txt) return; + tmp = rgb_txt_map; + max = tmp + eina_file_size_get(rgb_txt); + + while (tmp < max) + { + endline = memchr(tmp, '\n', max - tmp); + if (!endline) endline = max; + if ((*tmp != '!') && ((endline - tmp) < (int) (sizeof(buf) - 1))) + { + int rr, gg, bb; + char name[4096]; + + /* FIXME: not really efficient, should be loaded once in memory with a lookup table */ + memcpy(buf, tmp, endline - tmp); + buf[endline - tmp + 1] = '\0'; + + if (sscanf(buf, "%i %i %i %[^\n]", &rr, &gg, &bb, name) == 4) + { + if (!strcasecmp(name, color)) + { + *r = rr; + *g = gg; + *b = bb; + return; + } + } + } + tmp = endline + 1; + } +} + +typedef struct _CMap CMap; +struct _CMap { + EINA_RBTREE; + short r, g, b; + char str[6]; + unsigned char transp; +}; + +Eina_Rbtree_Direction +_cmap_cmp_node_cb(const Eina_Rbtree *left, const Eina_Rbtree *right, void *data EINA_UNUSED) +{ + CMap *lcm; + CMap *rcm; + + lcm = EINA_RBTREE_CONTAINER_GET(left, CMap); + rcm = EINA_RBTREE_CONTAINER_GET(right, CMap); + + if (strcmp(lcm->str, rcm->str) < 0) + return EINA_RBTREE_LEFT; + return EINA_RBTREE_RIGHT; +} + +int +_cmap_cmp_key_cb(const Eina_Rbtree *node, const void *key, int length EINA_UNUSED, void *data EINA_UNUSED) +{ + CMap *root = EINA_RBTREE_CONTAINER_GET(node, CMap); + + return strcmp(root->str, key); +} + +/** FIXME: clean this up and make more efficient **/ +static Eina_Bool +evas_image_load_file_xpm(Eina_File *f, Evas_Image_Property *prop, void *pixels, int load_data, int *error) +{ + DATA32 *ptr, *end, *head = NULL; + const char *map = NULL; + size_t length; + size_t position; + + int pc, c, i, j, k, w, h, ncolors, cpp, comment, transp, + quote, context, len, done, r, g, b, backslash, lu1, lu2; + char *line = NULL; + char s[256], tok[256], col[256], *tl; + int lsz = 256; + CMap *cmap = NULL; + Eina_Rbtree *root = NULL; + + short lookup[128 - 32][128 - 32]; + int count, size; + Eina_Bool res = EINA_FALSE; + + done = 0; +// transp = -1; + transp = 1; + + /* if immediate_load is 1, then dont delay image laoding as below, or */ + /* already data in this image - dont load it again */ + + length = eina_file_size_get(f); + position = 0; + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + if (length < 9) + { +// ERR("XPM ERROR: file size, %zd, is to small", length); + goto on_error; + } + + map = eina_file_map_all(f, load_data ? EINA_FILE_WILLNEED : EINA_FILE_RANDOM); + if (!map) + { + ERR("XPM ERROR: file failed to mmap"); + goto on_error; + } + + if (strncmp("/* XPM */", map, 9)) + { + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + + i = 0; + j = 0; + cmap = NULL; + w = 10; + h = 10; + ptr = NULL; + end = NULL; + c = ' '; + comment = 0; + quote = 0; + context = 0; + size = 0; + count = 0; + line = malloc(lsz); + if (!line) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + backslash = 0; + memset(lookup, 0, sizeof(lookup)); + while (!done) + { + pc = c; + if (position == length) break ; + c = (char) map[position++]; + if (!quote) + { + if ((pc == '/') && (c == '*')) + comment = 1; + else if ((pc == '*') && (c == '/') && (comment)) + comment = 0; + } + if (!comment) + { + if ((!quote) && (c == '"')) + { + quote = 1; + i = 0; + } + else if ((quote) && (c == '"')) + { + line[i] = 0; + quote = 0; + if (context == 0) + { + /* Header */ + if (sscanf(line, "%i %i %i %i", &w, &h, &ncolors, &cpp) != 4) + { +// ERR("XPM ERROR: XPM file malformed header"); + *error = EVAS_LOAD_ERROR_CORRUPT_FILE; + goto on_error; + } + if ((ncolors > 32766) || (ncolors < 1)) + { + ERR("XPM ERROR: XPM files with colors > 32766 or < 1 not supported"); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + if ((cpp > 5) || (cpp < 1)) + { + ERR("XPM ERROR: XPM files with characters per pixel > 5 or < 1not supported"); + *error = EVAS_LOAD_ERROR_UNKNOWN_FORMAT; + goto on_error; + } + if ((w > IMG_MAX_SIZE) || (w < 1)) + { + ERR("XPM ERROR: Image width > IMG_MAX_SIZE or < 1 pixels for file"); + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error; + } + if ((h > IMG_MAX_SIZE) || (h < 1)) + { + ERR("XPM ERROR: Image height > IMG_MAX_SIZE or < 1 pixels for file"); + *error = EVAS_LOAD_ERROR_GENERIC; + goto on_error; + } + if (IMG_TOO_BIG(w, h)) + { + ERR("XPM ERROR: Image just too big to ever allocate"); + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + if (!cmap) + { + cmap = malloc(sizeof(CMap) * ncolors); + if (!cmap) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + } + + if (!load_data) + { + prop->w = w; + prop->h = h; + } + else if ((int) prop->w != w || + (int) prop->h != h) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + + + j = 0; + context++; + } + else if (context == 1) + { + /* Color Table */ + if (j < ncolors) + { + int slen; + int hascolor, iscolor; + + iscolor = 0; + hascolor = 0; + tok[0] = 0; + col[0] = 0; + s[0] = 0; + len = strlen(line); + strncpy(cmap[j].str, line, cpp); + cmap[j].str[cpp] = 0; + if (load_data) root = eina_rbtree_inline_insert(root, EINA_RBTREE_GET(&cmap[j]), _cmap_cmp_node_cb, NULL); + for (slen = 0; slen < cpp; slen++) + { + /* fix the ascii of the color string - if its < 32 - just limit to 32 */ + if (cmap[j].str[slen] < 32) cmap[j].str[slen] = 0; + } + cmap[j].r = -1; + cmap[j].transp = 0; + for (k = cpp; k < len; k++) + { + if (line[k] != ' ') + { + const char *tmp = strchr(&line[k], ' '); + slen = tmp ? tmp - &line[k]: 255; + + strncpy(s, &line[k], slen); + s[slen] = 0; + k += slen; + if (slen == 1 && *s == 'c') iscolor = 1; + if ((slen == 1 && ((s[0] == 'm') || (s[0] == 's') || (s[0] == 'g') || (s[0] == 'c'))) || + (slen == 2 && (s[0] == 'g') && (s[1] == '4')) || + (k >= len)) + { + if (k >= len) + { + if (col[0]) + { + if (strlen(col) < (sizeof(col) - 2)) + strcat(col, " "); + else + done = 1; + } + if ((strlen(col) + strlen(s)) < (sizeof(col) - 1)) + strcat(col, s); + } + if (col[0]) + { + if (!strcasecmp(col, "none")) + { + transp = 1; + cmap[j].transp = 1; + cmap[j].r = 0; + cmap[j].g = 0; + cmap[j].b = 0; + } + else + { + if ((((cmap[j].r < 0) || (!strcmp(tok, "c"))) && (!hascolor))) + { + r = g = b = 0; + xpm_parse_color(col, &r, &g, &b); + cmap[j].r = r; + cmap[j].g = g; + cmap[j].b = b; + if (iscolor) hascolor = 1; + } + } + } + strcpy(tok, s); + col[0] = 0; + } + else + { + if (col[0]) + { + if (strlen(col) < ( sizeof(col) - 2)) + strcat(col, " "); + else + done = 1; + } + if ((strlen(col) + strlen(s)) < (sizeof(col) - 1)) + strcat(col, s); + } + } + } + } + j++; + if (load_data && j >= ncolors) + { + if (cpp == 1) + { + for (i = 0; i < ncolors; i++) + lookup[(int)cmap[i].str[0] - 32][0] = i; + } + if (cpp == 2) + { + for (i = 0; i < ncolors; i++) + lookup[(int)cmap[i].str[0] - 32][(int)cmap[i].str[1] - 32] = i; + } + context++; + } + + if (transp) prop->alpha = 1; + + if (load_data) + { + ptr = pixels; + head = ptr; + if (!ptr) + { + *error = EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + goto on_error; + } + size = w * h; + end = ptr + size; + } + else + { + *error = EVAS_LOAD_ERROR_NONE; + goto on_success; + } + } + else + { + /* Image Data */ + i = 0; + if (cpp == 0) + { + /* Chars per pixel = 0? well u never know */ + } + /* it's xpm - don't care about speed too much. still faster + * that most xpm loaders anyway */ + else if (cpp == 1) + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (cmap[lookup[lu1][0]].transp) + { + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = RGB_JOIN(r, g, b); + ptr++; + count++; + } + else + { + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + if (lu1 < 0) continue; + r = (unsigned char)cmap[lookup[lu1][0]].r; + g = (unsigned char)cmap[lookup[lu1][0]].g; + b = (unsigned char)cmap[lookup[lu1][0]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else if (cpp == 2) + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + i++; + lu2 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (lu2 < 0) continue; + if (cmap[lookup[lu1][lu2]].transp) + { + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = RGB_JOIN(r, g, b); + ptr++; + count++; + } + else + { + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + lu1 = (int)line[i] - 32; + i++; + lu2 = (int)line[i] - 32; + if (lu1 < 0) continue; + if (lu2 < 0) continue; + r = (unsigned char)cmap[lookup[lu1][lu2]].r; + g = (unsigned char)cmap[lookup[lu1][lu2]].g; + b = (unsigned char)cmap[lookup[lu1][lu2]].b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + else + { + if (transp) + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + Eina_Rbtree *l; + + for (j = 0; j < cpp; j++, i++) + { + col[j] = line[i]; + if (col[j] < 32) col[j] = 32; + } + col[j] = 0; + i--; + + l = eina_rbtree_inline_lookup(root, col, j, _cmap_cmp_key_cb, NULL); + if (l) + { + CMap *cm = EINA_RBTREE_CONTAINER_GET(l, CMap); + + r = (unsigned char)cm->r; + g = (unsigned char)cm->g; + b = (unsigned char)cm->b; + if (cm->transp) + { + *ptr = RGB_JOIN(r, g, b); + } + else + { + *ptr = ARGB_JOIN(0xff, r, g, b); + } + + ptr++; + count++; + } + } + } + else + { + for (i = 0; + ((i < 65536) && (ptr < end) && (line[i])); + i++) + { + Eina_Rbtree *l; + + for (j = 0; j < cpp; j++, i++) + { + col[j] = line[i]; + } + col[j] = 0; + i--; + + l = eina_rbtree_inline_lookup(root, col, 0, _cmap_cmp_key_cb, NULL); + if (l) + { + CMap *cm = EINA_RBTREE_CONTAINER_GET(l, CMap); + + r = (unsigned char)cm->r; + g = (unsigned char)cm->g; + b = (unsigned char)cm->b; + *ptr = ARGB_JOIN(0xff, r, g, b); + ptr++; + count++; + } + } + } + } + } + } + } + /* Scan in line from XPM file */ + if ((!comment) && (quote) && (c != '"')) + { + if (c < 32) c = 32; + else if (c > 127) c = 127; + if (c =='\\') + { + if (++backslash < 2) + line[i++] = c; + else + backslash = 0; + } + else + { + backslash = 0; + line[i++] = c; + } + } + if (i >= lsz) + { + lsz += 256; + tl = realloc(line, lsz); + if (!tl) break; + line = tl; + } + if (((ptr) && ((ptr - head) >= (w * h))) || + ((context > 1) && (count >= size))) + break; + } + + on_success: + *error = EVAS_LOAD_ERROR_NONE; + res = EINA_TRUE; + + on_error: + if (map) eina_file_map_free(f, (void*) map); + free(cmap); + free(line); + return res; +} + +static void * +evas_image_load_file_open_xpm(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, + Evas_Image_Load_Opts *opts EINA_UNUSED, + Evas_Image_Animated *animated EINA_UNUSED, + int *error EINA_UNUSED) +{ + return f; +} + +static void +evas_image_load_file_close_xpm(void *loader_data EINA_UNUSED) +{ +} + +static Eina_Bool +evas_image_load_file_head_xpm(void *loader_data, + Evas_Image_Property *prop, + int *error) +{ + Eina_File *f = loader_data; + + return evas_image_load_file_xpm(f, prop, NULL, 0, error); +} + +static Eina_Bool +evas_image_load_file_data_xpm(void *loader_data, + Evas_Image_Property *prop, + void *pixels, + int *error) +{ + Eina_File *f = loader_data; + + return evas_image_load_file_xpm(f, prop, pixels, 1, error); +} + +static Evas_Image_Load_Func evas_image_load_xpm_func = +{ + evas_image_load_file_open_xpm, + evas_image_load_file_close_xpm, + evas_image_load_file_head_xpm, + evas_image_load_file_data_xpm, + NULL, + EINA_FALSE, + EINA_FALSE +}; + +static int +module_open(Evas_Module *em) +{ + if (!em) return 0; + _evas_loader_xpm_log_dom = eina_log_domain_register + ("evas-xpm", EVAS_DEFAULT_LOG_COLOR); + if (_evas_loader_xpm_log_dom < 0) + { + EINA_LOG_ERR("Can not create a module log domain."); + return 0; + } + + /* Shouldn't we make that PATH configurable ? */ + rgb_txt = eina_file_open("/usr/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/X11/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/X11R6/lib/X11/rgb.txt", 0); + if (!rgb_txt) rgb_txt = eina_file_open("/usr/openwin/lib/X11/rgb.txt", 0); + if (rgb_txt) + rgb_txt_map = eina_file_map_all(rgb_txt, EINA_FILE_WILLNEED); + em->functions = (void *)(&evas_image_load_xpm_func); + return 1; +} + +static void +module_close(Evas_Module *em EINA_UNUSED) +{ + if (rgb_txt) + { + eina_file_map_free(rgb_txt, rgb_txt_map); + eina_file_close(rgb_txt); + rgb_txt = NULL; + } + eina_log_domain_unregister(_evas_loader_xpm_log_dom); +} + +static Evas_Module_Api evas_modapi = +{ + EVAS_MODULE_API_VERSION, + "xpm", + "none", + { + module_open, + module_close + } +}; + +EVAS_MODULE_DEFINE(EVAS_MODULE_TYPE_IMAGE_LOADER, image_loader, xpm); + +#ifndef EVAS_STATIC_BUILD_XPM +EVAS_EINA_MODULE_DEFINE(image_loader, xpm); +#endif |