diff options
author | Cedric BAIL <cedric@osg.samsung.com> | 2015-01-26 15:49:09 +0100 |
---|---|---|
committer | Cedric BAIL <cedric@osg.samsung.com> | 2015-02-16 14:47:48 +0100 |
commit | d5bd14a81be2b05a2c4b7c8d330a7576e740a41b (patch) | |
tree | 3d89a166e450ca29f85e7818d611c8f02e618814 | |
parent | 7b69926c4cd143b89b2f0e8fef8a9f83ac45b4a8 (diff) | |
download | efl-d5bd14a81be2b05a2c4b7c8d330a7576e740a41b.tar.gz |
emile: initial addition of emile image support.
-rw-r--r-- | src/Makefile_Emile.am | 12 | ||||
-rw-r--r-- | src/lib/emile/Emile.h | 158 | ||||
-rw-r--r-- | src/lib/emile/emile_image.c | 801 |
3 files changed, 968 insertions, 3 deletions
diff --git a/src/Makefile_Emile.am b/src/Makefile_Emile.am index a90e5bda09..87b3906e28 100644 --- a/src/Makefile_Emile.am +++ b/src/Makefile_Emile.am @@ -1,6 +1,5 @@ ### Library - lib_LTLIBRARIES += lib/emile/libemile.la installed_emilemainheadersdir = $(includedir)/emile-@VMAJ@ @@ -9,7 +8,12 @@ dist_installed_emilemainheaders_DATA = lib/emile/Emile.h lib_emile_libemile_la_SOURCES = \ lib/emile/emile_private.h \ lib/emile/emile_main.c \ -lib/emile/emile_compress.c +lib/emile/emile_compress.c \ +lib/emile/emile_image.c \ +static_libs/rg_etc/rg_etc1.c \ +static_libs/rg_etc/rg_etc2.c \ +static_libs/rg_etc/rg_etc1.h \ +static_libs/rg_etc/etc2_encoder.c if ! ENABLE_LIBLZ4 lib_emile_libemile_la_SOURCES += \ @@ -30,6 +34,7 @@ endif endif lib_emile_libemile_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-I$(top_srcdir)/src/static_libs/rg_etc \ -DPACKAGE_BIN_DIR=\"$(bindir)\" \ -DPACKAGE_LIB_DIR=\"$(libdir)\" \ -DPACKAGE_DATA_DIR=\"$(datadir)/emile\" \ @@ -52,7 +57,8 @@ EXTRA_DIST += static_libs/lz4/README \ static_libs/lz4/lz4.c \ static_libs/lz4/lz4.h \ static_libs/lz4/lz4hc.c \ -static_libs/lz4/lz4hc.h +static_libs/lz4/lz4hc.h \ +static_libs/rg_etc/README ### Binary diff --git a/src/lib/emile/Emile.h b/src/lib/emile/Emile.h index 47a9186d3b..ff3ce7e1cf 100644 --- a/src/lib/emile/Emile.h +++ b/src/lib/emile/Emile.h @@ -174,6 +174,164 @@ EAPI Eina_Bool emile_binbuf_expand(const Eina_Binbuf *in, Eina_Binbuf *out, Emile_Compressor_Type t); +/* All the value from below enum should be the same as in Evas_Loader.h */ +typedef enum _Emile_Colorspace +{ + EMILE_COLORSPACE_ARGB8888,/**< ARGB 32 bits per pixel, high-byte is Alpha, accessed 1 32bit word at a time */ + EMILE_COLORSPACE_YCBCR422P601_PL, /**< YCbCr 4:2:2 Planar, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb, then Cr rows */ + EMILE_COLORSPACE_YCBCR422P709_PL, /**< YCbCr 4:2:2 Planar, ITU.BT-709 specifications. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb, then Cr rows */ + EMILE_COLORSPACE_RGB565_A5P, /**< 16bit rgb565 + Alpha plane at end - 5 bits of the 8 being used per alpha byte */ + EMILE_COLORSPACE_GRY8 = 4, + EMILE_COLORSPACE_YCBCR422601_PL, /**< YCbCr 4:2:2, ITU.BT-601 specifications. The data pointed to is just an array of row pointer, pointing to line of Y,Cb,Y,Cr bytes */ + EMILE_COLORSPACE_YCBCR420NV12601_PL, /**< YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of row pointer, pointing to the Y rows, then the Cb,Cr rows. */ + EMILE_COLORSPACE_YCBCR420TM12601_PL, /**< YCbCr 4:2:0, ITU.BT-601 specification. The data pointed to is just an array of tiled row pointer, pointing to the Y rows, then the Cb,Cr rows. */ + EMILE_COLORSPACE_AGRY88 = 8, /**< AY 8bits Alpha and 8bits Grey, accessed 1 16bits at a time */ + EMILE_COLORSPACE_ETC1 = 9, /**< OpenGL ETC1 encoding of RGB texture (4 bit per pixel) @since 1.10 */ + EMILE_COLORSPACE_RGB8_ETC2 = 10, /**< OpenGL GL_COMPRESSED_RGB8_ETC2 texture compression format (4 bit per pixel) @since 1.10 */ + EMILE_COLORSPACE_RGBA8_ETC2_EAC = 11, /**< OpenGL GL_COMPRESSED_RGBA8_ETC2_EAC texture compression format, supports alpha (8 bit per pixel) @since 1.10 */ + EMILE_COLORSPACE_ETC1_ALPHA = 12, /**< ETC1 with alpha support using two planes: ETC1 RGB and ETC1 grey for alpha @since 1.11 */ + EMILE_COLORSPACE_RGB_S3TC_DXT1 = 13, /**< OpenGL COMPRESSED_RGB_S3TC_DXT1_EXT format with RGB only. @since 1.11 */ + EMILE_COLORSPACE_RGBA_S3TC_DXT1 = 14, /**< OpenGL COMPRESSED_RGBA_S3TC_DXT1_EXT format with RGBA punchthrough. @since 1.11 */ + EMILE_COLORSPACE_RGBA_S3TC_DXT2 = 15, /**< DirectDraw DXT2 format with premultiplied RGBA. Not supported by OpenGL itself. @since 1.11 */ + EMILE_COLORSPACE_RGBA_S3TC_DXT3 = 16, /**< OpenGL COMPRESSED_RGBA_S3TC_DXT3_EXT format with RGBA. @since 1.11 */ + EMILE_COLORSPACE_RGBA_S3TC_DXT4 = 17, /**< DirectDraw DXT4 format with premultiplied RGBA. Not supported by OpenGL itself. @since 1.11 */ + EMILE_COLORSPACE_RGBA_S3TC_DXT5 = 18 /**< OpenGL COMPRESSED_RGBA_S3TC_DXT5_EXT format with RGBA. @since 1.11 */ +} Emile_Colorspace; + +typedef enum _Emile_Image_Encoding +{ + EMILE_IMAGE_LOSSLESS = 0, + EMILE_IMAGE_JPEG = 1, + EMILE_IMAGE_ETC1 = 2, + EMILE_IMAGE_ETC2_RGB = 3, + EMILE_IMAGE_ETC2_RGBA = 4, + EMILE_IMAGE_ETC1_ALPHA = 5 +} Emile_Image_Encoding; + +typedef enum _Emile_Image_Scale_Hint +{ + EMILE_IMAGE_SCALE_HINT_NONE = 0, /**< No scale hint at all */ + EMILE_IMAGE_SCALE_HINT_DYNAMIC = 1, /**< Image is being re-scaled over time, thus turning scaling cache @b off for its data */ + EMILE_IMAGE_SCALE_HINT_STATIC = 2 /**< Image is not being re-scaled over time, thus turning scaling cache @b on for its data */ +} Emile_Image_Scale_Hint; + +typedef enum _Emile_Image_Animated_Loop_Hint +{ + EMILE_IMAGE_ANIMATED_HINT_NONE = 0, + EMILE_IMAGE_ANIMATED_HINT_LOOP = 1, + EMILE_IMAGE_ANIMATED_HINT_PINGPONG = 2 +} Emile_Image_Animated_Loop_Hint; + +typedef enum _Emile_Image_Load_Error +{ + EMILE_IMAGE_LOAD_ERROR_NONE = 0, /**< No error on load */ + EMILE_IMAGE_LOAD_ERROR_GENERIC = 1, /**< A non-specific error occurred */ + EMILE_IMAGE_LOAD_ERROR_DOES_NOT_EXIST = 2, /**< File (or file path) does not exist */ + EMILE_IMAGE_LOAD_ERROR_PERMISSION_DENIED = 3, /**< Permission denied to an existing file (or path) */ + EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED = 4, /**< Allocation of resources failure prevented load */ + EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE = 5, /**< File corrupt (but was detected as a known format) */ + EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT = 6 /**< File is not a known format */ +} Emile_Image_Load_Error; /**< Emile image load error codes one can get - see emile_load_error_str() too. */ + +typedef struct _Emile_Image Emile_Image; +typedef struct _Emile_Image_Property Emile_Image_Property; +typedef struct _Emile_Image_Load_Opts Emile_Image_Load_Opts; +typedef struct _Emile_Image_Animated Emile_Image_Animated; + +struct _Emile_Image_Property +{ + struct { + unsigned char l, r, t, b; + } borders; + + const Emile_Colorspace *cspaces; + Emile_Colorspace cspace; + + Emile_Image_Encoding encoding; + + unsigned int w; + unsigned int h; + unsigned int row_stride; + + unsigned char scale; + + Eina_Bool rotated; + Eina_Bool alpha; + Eina_Bool premul; + Eina_Bool alpha_sparse; + + Eina_Bool flipped; + Eina_Bool comp; +}; + +struct _Emile_Image_Animated +{ + Eina_List *frames; + + Emile_Image_Animated_Loop_Hint loop_hint; + + int frame_count; + int loop_count; + int cur_frame; + + Eina_Bool animated; +}; + +struct _Emile_Image_Load_Opts +{ + struct { + int x, y, w, h; + } region; + struct { + int src_x, src_y, src_w, src_h; + int dst_w, dst_h; + int smooth; + Emile_Image_Scale_Hint scale_hint; + } scale_load; + double dpi; + unsigned int w, h; + unsigned int degree; + int scale_down_by; + + Eina_Bool orientation; +}; + +// FIXME: Add enum for error code +// FIXME: should set region at load time, not head time + +EAPI Emile_Image *emile_image_tgv_memory_open(Eina_Binbuf *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error); +EAPI Emile_Image *emile_image_tgv_file_open(Eina_File *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error); + +EAPI Emile_Image *emile_image_jpeg_memory_open(Eina_Binbuf *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error); +EAPI Emile_Image *emile_image_jpeg_file_open(Eina_File *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error); + +EAPI Eina_Bool emile_image_head(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error); +EAPI Eina_Bool emile_image_data(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error); + +EAPI void emile_image_close(Emile_Image *source); + +EAPI const char *emile_load_error_str(Emile_Image *source, + Emile_Image_Load_Error error); + #ifdef __cplusplus } #endif /* ifdef __cplusplus */ diff --git a/src/lib/emile/emile_image.c b/src/lib/emile/emile_image.c new file mode 100644 index 0000000000..9f26a9aa00 --- /dev/null +++ b/src/lib/emile/emile_image.c @@ -0,0 +1,801 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* ifdef HAVE_CONFIG_H */ + +#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 "Emile.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 + +#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 + +struct _Emile_Image +{ + Emile_Image_Load_Opts opts; + + struct { + unsigned int width; + unsigned int height; + } size, block; + + Eina_Rectangle region; + + union { + Eina_Binbuf *bin; + Eina_File *f; + } source; + + const unsigned char *source_data; + + Eina_Bool (*bind)(Emile_Image *image, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error); + Eina_Bool (*head)(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error); + Eina_Bool (*data)(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error); + void (*close)(Emile_Image *image); + + Emile_Colorspace cspace; + + Eina_Bool bin_source : 1; + + /* TGV option */ + Eina_Bool unpremul : 1; + Eina_Bool compress : 1; + Eina_Bool blockless : 1; + Eina_Bool load_opts : 1; +}; + +static const unsigned char * +_emile_image_file_source_map(Emile_Image *image, unsigned int *length) +{ + if (!image) return NULL; + + if (image->bin_source) + { + if (length) *length = eina_binbuf_length_get(image->source.bin); + return eina_binbuf_string_get(image->source.bin); + } + + if (length) *length = eina_file_size_get(image->source.f); + if (!image->source_data) + { + image->source_data = eina_file_map_all(image->source.f, + EINA_FILE_SEQUENTIAL); + } + return image->source_data; +} + +static void +_emile_image_file_source_unmap(Emile_Image *image) +{ + if (!(image && image->source_data)) return ; + eina_file_map_free(image->source.f, + (void*) image->source_data); + image->source_data = NULL; +} + +static int +roundup(int val, int rup) +{ + if (val >= 0 && rup > 0) + return (val + rup - 1) - ((val + rup - 1) % rup); + return 0; +} + +/* TGV Handling */ + +static const Emile_Colorspace cspaces_etc1[2] = { + EMILE_COLORSPACE_ETC1, + EMILE_COLORSPACE_ARGB8888 +}; + +static const Emile_Colorspace cspaces_rgb8_etc2[2] = { + EMILE_COLORSPACE_RGB8_ETC2, + EMILE_COLORSPACE_ARGB8888 +}; + +static const Emile_Colorspace cspaces_rgba8_etc2_eac[2] = { + EMILE_COLORSPACE_RGBA8_ETC2_EAC, + EMILE_COLORSPACE_ARGB8888 +}; + +static const Emile_Colorspace cspaces_etc1_alpha[2] = { + EMILE_COLORSPACE_ETC1_ALPHA, + EMILE_COLORSPACE_ARGB8888 +}; + +/************************************************************** + * 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) + +static Eina_Bool +_emile_tgv_bind(Emile_Image *image, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + const unsigned char *m; + unsigned int length; + + m = _emile_image_file_source_map(image, &length); + if (!m) return EINA_FALSE; + + /* Fast check for file characteristic (useful when trying to guess + a file format without extention). */ + if (length < 16 || + strncmp((const char*) m, "TGV1", 4) != 0) + goto on_error; + + if ((m[OFFSET_ALGORITHM] & 0xFF) > 3) + goto on_error; + + return EINA_TRUE; + + on_error: + *error = EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT; + _emile_image_file_source_unmap(image); + return EINA_FALSE; +} + +static Eina_Bool +_emile_tgv_head(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error) +{ + const unsigned char *m; + unsigned int length; + + m = _emile_image_file_source_map(image, &length); + if (!m) return EINA_FALSE; + + switch (m[OFFSET_ALGORITHM] & 0xFF) + { + case 0: + prop->cspaces = cspaces_etc1; + prop->alpha = EINA_FALSE; + image->cspace = EMILE_COLORSPACE_ETC1; + break; + case 1: + prop->cspaces = cspaces_rgb8_etc2; + prop->alpha = EINA_FALSE; + image->cspace = EMILE_COLORSPACE_RGB8_ETC2; + break; + case 2: + prop->cspaces = cspaces_rgba8_etc2_eac; + prop->alpha = EINA_TRUE; + image->cspace = EMILE_COLORSPACE_RGBA8_ETC2_EAC; + break; + case 3: + prop->cspaces = cspaces_etc1_alpha; + prop->alpha = EINA_TRUE; + prop->premul = !!(m[OFFSET_OPTIONS] & 0x4); + image->unpremul = prop->premul; + break; + default: + *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + image->compress = m[OFFSET_OPTIONS] & 0x1; + image->blockless = (m[OFFSET_OPTIONS] & 0x2) != 0; + + image->size.width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH]))); + image->size.height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT]))); + + if (image->blockless) + { + image->block.width = roundup(image->size.width + 2, 4); + image->block.height = roundup(image->size.height + 2, 4); + } + else + { + image->block.width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f); + image->block.height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4); + } + + EINA_RECTANGLE_SET(&image->region, 0, 0, + image->size.width, image->size.height); + if (image->load_opts && + (image->opts.region.w > 0 && image->opts.region.h > 0)) + { + // ETC colorspace doesn't work with region for now + prop->cspaces = NULL; + + if (!eina_rectangle_intersection(&image->region, + &image->opts.region)) + { + *error = EMILE_IMAGE_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + } + + prop->w = image->size.width; + prop->h = image->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; + + return EINA_TRUE; +} + +static inline unsigned int +_tgv_length_get(const unsigned 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; +} + +static Eina_Bool +_emile_tgv_data(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error) +{ + const unsigned char *m; + unsigned int *p = pixels; + unsigned char *p_etc = pixels; + Eina_Binbuf *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; + int num_planes = 1, plane, alpha_offset = 0; + Eina_Bool r = EINA_FALSE; + + m = _emile_image_file_source_map(image, &length); + if (!m) + { + *error = EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED; + return EINA_FALSE; + } + + offset = OFFSET_BLOCKS; + + *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE; + + // By definition, prop{.w, .h} == region{.w, .h} + EINA_RECTANGLE_SET(&master, + image->region.x, image->region.y, + prop->w, prop->h); + + switch (image->cspace) + { + case EMILE_COLORSPACE_ETC1: + case EMILE_COLORSPACE_RGB8_ETC2: + etc_block_size = 8; + break; + case EMILE_COLORSPACE_RGBA8_ETC2_EAC: + etc_block_size = 16; + break; + case EMILE_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 EMILE_COLORSPACE_ETC1: + case EMILE_COLORSPACE_RGB8_ETC2: + case EMILE_COLORSPACE_RGBA8_ETC2_EAC: + case EMILE_COLORSPACE_ETC1_ALPHA: + if (master.x % 4 || master.y % 4) + // FIXME: Should we really abort here ? Seems like a late check for me + abort(); + break; + case EMILE_COLORSPACE_ARGB8888: + // Offset to take duplicated pixels into account + master.x += 1; + master.y += 1; + break; + default: abort(); + } + + + if (prop->cspace != EMILE_COLORSPACE_ARGB8888 && prop->cspace != image->cspace) + { + if (!((prop->cspace == EMILE_COLORSPACE_RGB8_ETC2) && + (image->cspace == EMILE_COLORSPACE_ETC1))) + { + *error = EMILE_IMAGE_LOAD_ERROR_GENERIC; + return EINA_FALSE; + } + // 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 = image->block.width * image->block.height / (4 * 4); + if (image->compress) + buffer = eina_binbuf_manage_read_only_new_length(alloca(etc_block_size * block_count), etc_block_size * block_count); + + for (plane = 0; plane < num_planes; plane++) + for (y = 0; y < image->size.height + 2; y += image->block.height) + for (x = 0; x < image->size.width + 2; x += image->block.width) + { + Eina_Rectangle current; + Eina_Binbuf *data_start; + const unsigned char *it; + unsigned int expand_length; + unsigned int i, j; + + block_length = _tgv_length_get(m + offset, length, &offset); + + if (block_length == 0) + { + *error = EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE; + return EINA_FALSE; + } + + data_start = eina_binbuf_manage_read_only_new_length(m + offset, block_length); + offset += block_length; + + EINA_RECTANGLE_SET(¤t, x, y, + image->block.width, image->block.height); + + if (!eina_rectangle_intersection(¤t, &master)) + continue; + + if (image->compress) + { + if (!emile_binbuf_expand(data_start, buffer, EMILE_LZ4HC)) + goto on_error; + } + else + { + buffer = data_start; + if (block_count * etc_block_size != block_length) + goto on_error; + } + it = eina_binbuf_string_get(buffer); + + for (i = 0; i < image->block.height; i += 4) + for (j = 0; j < image->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 EMILE_COLORSPACE_ARGB8888: + switch (image->cspace) + { + case EMILE_COLORSPACE_ETC1: + case EMILE_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 EMILE_COLORSPACE_RGB8_ETC2: + rg_etc2_rgb8_decode_block((uint8_t *) it, temporary); + break; + case EMILE_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 EMILE_COLORSPACE_ETC1: + case EMILE_COLORSPACE_RGB8_ETC2: + case EMILE_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 EMILE_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 == EMILE_COLORSPACE_ARGB8888) + prop->premul = image->unpremul; // call premul if unpremul data + + r = EINA_TRUE; + + on_error: + return r; +} + +static void +_emile_tgv_close(Emile_Image *image EINA_UNUSED) +{ + // TGV file loader doesn't keep any data allocated around +} + +/* JPEG Handling */ +static Eina_Bool +_emile_jpeg_bind(Emile_Image *image, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + return EINA_FALSE; +} + +static Eina_Bool +_emile_jpeg_head(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error) +{ + return EINA_FALSE; +} + +static Eina_Bool +_emile_jpeg_data(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error) +{ + return EINA_FALSE; +} + +static void +_emile_jpeg_close(Emile_Image *image) +{ +} + +/* Generic helper to instantiate a new Emile_Image */ + +static Emile_Image * +_emile_image_new(Eina_Bool (*bind) (Emile_Image *image, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error), + Eina_Bool (*head)(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error), + Eina_Bool (*data)(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error), + void (*close)(Emile_Image *image)) +{ + Emile_Image *ei; + + ei = calloc(1, sizeof (Emile_Image)); + if (!ei) return NULL; + + ei->bind = bind; + ei->head = head; + ei->data = data; + ei->close = close; + + return ei; +} + +static void +_emile_image_binbuf_set(Emile_Image *ei, Eina_Binbuf *source) +{ + ei->source.bin = source; + ei->bin_source = EINA_TRUE; +} + +static void +_emile_image_file_set(Emile_Image *ei, Eina_File *source) +{ + ei->source.f = eina_file_dup(source); + ei->bin_source = EINA_FALSE; +} + +static Emile_Image * +_emile_image_bind(Emile_Image *ei, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + if (opts) + { + ei->opts = *opts; + ei->load_opts = 1; + } + + *error = EMILE_IMAGE_LOAD_ERROR_NONE; + if (ei->bind(ei, opts, animated, error)) + return ei; + + /* File is not of that format */ + free(ei); + return NULL; +} + +/* Public API to manipulate Emile_Image */ + +EAPI Emile_Image * +emile_image_tgv_memory_open(Eina_Binbuf *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + Emile_Image *ei; + + ei = _emile_image_new(_emile_tgv_bind, + _emile_tgv_head, + _emile_tgv_data, + _emile_tgv_close); + if (!ei) return NULL; + + _emile_image_binbuf_set(ei, source); + return _emile_image_bind(ei, opts, animated, error); +} + +EAPI Emile_Image * +emile_image_tgv_file_open(Eina_File *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + Emile_Image *ei; + + ei = _emile_image_new(_emile_tgv_bind, + _emile_tgv_head, + _emile_tgv_data, + _emile_tgv_close); + if (!ei) return NULL; + + _emile_image_file_set(ei, source); + return _emile_image_bind(ei, opts, animated, error); +} + +EAPI Emile_Image * +emile_image_jpeg_memory_open(Eina_Binbuf *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + Emile_Image *ei; + + ei = _emile_image_new(_emile_jpeg_bind, + _emile_jpeg_head, + _emile_jpeg_data, + _emile_jpeg_close); + if (!ei) return NULL; + + _emile_image_binbuf_set(ei, source); + return _emile_image_bind(ei, opts, animated, error); +} + +EAPI Emile_Image * +emile_image_jpeg_file_open(Eina_File *source, + Emile_Image_Load_Opts *opts, + Emile_Image_Animated *animated, + Emile_Image_Load_Error *error) +{ + Emile_Image *ei; + + ei = _emile_image_new(_emile_jpeg_bind, + _emile_jpeg_head, + _emile_jpeg_data, + _emile_jpeg_close); + if (!ei) return NULL; + + _emile_image_file_set(ei, source); + return _emile_image_bind(ei, opts, animated, error); +} + +EAPI void +emile_image_close(Emile_Image *image) +{ + if (!image) return ; + + _emile_image_file_source_unmap(image); + if (!image->bin_source) + eina_file_close(image->source.f); + image->close(image); + free(image); +} + +EAPI Eina_Bool +emile_image_head(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + Emile_Image_Load_Error *error) +{ + if (!image) return EINA_FALSE; + + *error = EMILE_IMAGE_LOAD_ERROR_NONE; + return image->head(image, prop, property_size, error); +} + +EAPI Eina_Bool +emile_image_data(Emile_Image *image, + Emile_Image_Property *prop, + unsigned int property_size, + void *pixels, + Emile_Image_Load_Error *error) +{ + if (!image) return EINA_FALSE; + + *error = EMILE_IMAGE_LOAD_ERROR_NONE; + return image->data(image, prop, property_size, pixels, error); +} + +EAPI const char * +emile_load_error_str(Emile_Image *source EINA_UNUSED, + Emile_Image_Load_Error error) +{ + switch (error) + { + case EMILE_IMAGE_LOAD_ERROR_NONE: + return "No error"; + case EMILE_IMAGE_LOAD_ERROR_GENERIC: + return "Generic error encountered while loading image."; + case EMILE_IMAGE_LOAD_ERROR_DOES_NOT_EXIST: + return "File does not exist."; + case EMILE_IMAGE_LOAD_ERROR_PERMISSION_DENIED: + return "Permission to open file denied."; + case EMILE_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED: + return "Not enough memory to open file."; + case EMILE_IMAGE_LOAD_ERROR_CORRUPT_FILE: + return "File is corrupted."; + case EMILE_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT: + return "Unexpected file format."; + } + return NULL; +} |