jpeg pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=c21968bcd76e07ff300c8e102c0062f74fcb704d
commit c21968bcd76e07ff300c8e102c0062f74fcb704d Author: Jean-Philippe Andre <[email protected]> Date: Fri Jun 13 16:56:39 2014 +0900 Eet: Add support for ETC2 encoding and decoding Since we now have full support for ETC2, add the colorspaces to Eet. @feature --- src/Makefile_Eet.am | 2 + src/lib/eet/Eet.h | 8 +- src/lib/eet/eet_image.c | 310 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 237 insertions(+), 83 deletions(-) diff --git a/src/Makefile_Eet.am b/src/Makefile_Eet.am index c5da421..d5a308b 100644 --- a/src/Makefile_Eet.am +++ b/src/Makefile_Eet.am @@ -22,6 +22,8 @@ static_libs/lz4/lz4.h \ static_libs/lz4/lz4hc.c \ static_libs/lz4/lz4hc.h \ static_libs/rg_etc/rg_etc1.c \ +static_libs/rg_etc/rg_etc2.c \ +static_libs/rg_etc/etc2_encoder.c \ static_libs/rg_etc/rg_etc1.h lib_eet_libeet_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ diff --git a/src/lib/eet/Eet.h b/src/lib/eet/Eet.h index 50645a0..95be2e5 100644 --- a/src/lib/eet/Eet.h +++ b/src/lib/eet/Eet.h @@ -477,14 +477,18 @@ typedef enum _Eet_Image_Encoding { EET_IMAGE_LOSSLESS = 0, EET_IMAGE_JPEG = 1, - EET_IMAGE_ETC1 = 2 + EET_IMAGE_ETC1 = 2, + EET_IMAGE_ETC2_RGB = 3, + EET_IMAGE_ETC2_RGBA = 4 } Eet_Image_Encoding; typedef enum _Eet_Colorspace { EET_COLORSPACE_ARGB8888 = 0, /* The number between are reserved to preserve compatibility with evas */ - EET_COLORSPACE_ETC1 = 8 + EET_COLORSPACE_ETC1 = 9, + EET_COLORSPACE_RGB8_ETC2 = 10, + EET_COLORSPACE_RGBA8_ETC2_EAC = 11 } Eet_Colorspace; /** diff --git a/src/lib/eet/eet_image.c b/src/lib/eet/eet_image.c index 4aacee1..f3db7ee 100644 --- a/src/lib/eet/eet_image.c +++ b/src/lib/eet/eet_image.c @@ -31,7 +31,7 @@ #include "rg_etc1.h" #define OFFSET_BLOCK_SIZE 4 -#define OFFSET_ALGORITHN 5 +#define OFFSET_ALGORITHM 5 #define OFFSET_OPTIONS 6 #define OFFSET_WIDTH 8 #define OFFSET_HEIGHT 12 @@ -700,91 +700,149 @@ _tgv_length_get(const char *m, unsigned int length, unsigned int *offset) } static int -eet_data_image_etc1_decode(const void *data, +roundup(int val, int rup) +{ + if (val >= 0 && rup > 0) + return (val + rup - 1) - ((val + rup - 1) % rup); + return 0; +} + +static int +eet_data_image_etc2_decode(const void *data, unsigned int length, unsigned int *p, unsigned int dst_x, unsigned int dst_y, unsigned int dst_w, unsigned int dst_h, - unsigned int cspace) + Eina_Bool alpha, + Eet_Colorspace cspace, + Eet_Image_Encoding lossy) { - Eina_Rectangle master; - Eina_Rectangle current; - const char *m = data; + const char *m = NULL; + unsigned int bwidth, bheight; + unsigned char *p_etc; char *buffer; - unsigned int block_width, block_height; - unsigned int compress; - unsigned int width, height; - unsigned int block_count; - unsigned int etc1_width = 0; + Eina_Rectangle master; + unsigned int block_length; unsigned int offset; - unsigned int x, y; + unsigned int x, y, w, h; + unsigned int block_count; + unsigned int etc_width = 0; + unsigned int etc_block_size; + Eet_Colorspace file_cspace; + Eina_Bool compress, blockless; + Eina_Bool r = EINA_FALSE; - block_width = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f); - block_height = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4); + m = data; - if (m[OFFSET_ALGORITHN] != 0) return 0; + if (strncmp(m, "TGV1", 4) != 0) + return 0; compress = m[OFFSET_OPTIONS] & 0x1; + blockless = (m[OFFSET_OPTIONS] & 0x2) != 0; + w = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH]))); + h = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT]))); - width = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH]))); - height = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT]))); - - EINA_RECTANGLE_SET(&master, 0, 0, width, height); - EINA_RECTANGLE_SET(¤t, dst_x, dst_y, dst_w, dst_h); - - if (!eina_rectangle_intersection(&master, ¤t)) - return 0; + switch (m[OFFSET_ALGORITHM] & 0xFF) + { + case 0: + if (lossy != EET_IMAGE_ETC1) return 0; + file_cspace = EET_COLORSPACE_ETC1; + if (alpha != EINA_FALSE) return 0; + etc_block_size = 8; + etc_width = ((w + 2) / 4 + ((w + 2) % 4 ? 1 : 0)) * 8; + break; + case 1: + if (lossy != EET_IMAGE_ETC2_RGB) return 0; + file_cspace = EET_COLORSPACE_RGB8_ETC2; + if (alpha != EINA_FALSE) return 0; + etc_block_size = 8; + etc_width = ((w + 2) / 4 + ((w + 2) % 4 ? 1 : 0)) * 8; + break; + case 2: + if (lossy != EET_IMAGE_ETC2_RGBA) return 0; + file_cspace = EET_COLORSPACE_RGBA8_ETC2_EAC; + if (alpha != EINA_TRUE) return 0; + etc_block_size = 16; + etc_width = ((w + 2) / 4 + ((w + 2) % 4 ? 1 : 0)) * 16; + break; + default: + return 0; + } - if (cspace == EET_COLORSPACE_ETC1) + if (cspace != EET_COLORSPACE_ARGB8888 && cspace != file_cspace) { - if (master.x % 4 || - master.y % 4) - abort (); + if (!((cspace == EET_COLORSPACE_RGB8_ETC2) && (file_cspace == EET_COLORSPACE_ETC1))) + return 0; + // else: ETC2 is compatible with ETC1 and is preferred + } - etc1_width = ((dst_w + 2) / 4 + ((dst_w + 2) % 4 ? 1 : 0)) * 8; + if (blockless) + { + bwidth = roundup(w + 2, 4); + bheight = roundup(h + 2, 4); } else { - // Account for duplicated border pixels + bwidth = 4 << (m[OFFSET_BLOCK_SIZE] & 0x0f); + bheight = 4 << ((m[OFFSET_BLOCK_SIZE] & 0xf0) >> 4); + } + + EINA_RECTANGLE_SET(&master, dst_x, dst_y, dst_w, dst_h); + + switch (cspace) + { + case EET_COLORSPACE_ETC1: + case EET_COLORSPACE_RGB8_ETC2: + case EET_COLORSPACE_RGBA8_ETC2_EAC: + if (master.x % 4 || master.y % 4) + abort(); + break; + case EET_COLORSPACE_ARGB8888: + // Offset to take duplicated pixels into account master.x += 1; master.y += 1; + break; + default: abort(); } - // Allocate space for each ETC1 block (64bytes per 4 * 4 pixels group) - block_count = block_width * block_height / (4 * 4); + p_etc = (unsigned char*) p; + offset = OFFSET_BLOCKS; + + // Allocate space for each ETC block (8 or 16 bytes per 4 * 4 pixels group) + block_count = bwidth * bheight / (4 * 4); if (compress) - buffer = alloca(8 * block_count); + buffer = alloca(etc_block_size * block_count); else buffer = NULL; - offset = OFFSET_BLOCKS; - - for (y = 0; y < height + 2; y += block_height) - for (x = 0; x < width + 2; x += block_width) + for (y = 0; y < h + 2; y += bheight) + for (x = 0; x < w + 2; x += bwidth) { + Eina_Rectangle current; const char *data_start; const char *it; - unsigned int block_length; unsigned int expand_length; unsigned int i, j; block_length = _tgv_length_get(m + offset, length, &offset); + if (block_length == 0) return 0; data_start = m + offset; offset += block_length; EINA_RECTANGLE_SET(¤t, x, y, - block_width, block_height); + bwidth, bheight); + if (!eina_rectangle_intersection(¤t, &master)) continue ; if (compress) { - expand_length = LZ4_uncompress(data_start, - buffer, block_count * 8); + expand_length = LZ4_uncompress(data_start, buffer, + block_count * etc_block_size); // That's an overhead for now, need to be fixed if (expand_length != block_length) return 0; @@ -792,13 +850,13 @@ eet_data_image_etc1_decode(const void *data, else { buffer = (void*) data_start; - if (block_count * 8 != block_length) + if (block_count * etc_block_size != block_length) return 0; } it = buffer; - for (i = 0; i < block_height; i += 4) - for (j = 0; j < block_width; j += 4, it += 8) + for (i = 0; i < bheight; i += 4) + for (j = 0; j < bwidth; j += 4, it += etc_block_size) { Eina_Rectangle current_etc; unsigned int temporary[4 * 4] = { 0 }; @@ -808,19 +866,33 @@ eet_data_image_etc1_decode(const void *data, EINA_RECTANGLE_SET(¤t_etc, x + j, y + i, 4, 4); if (!eina_rectangle_intersection(¤t_etc, ¤t)) - continue ; + continue; switch (cspace) { case EET_COLORSPACE_ARGB8888: - if (!rg_etc1_unpack_block(it, temporary, 0)) - { - fprintf(stderr, "HOUSTON WE HAVE A PROBLEM ! Block starting at {%i, %i} is corrupted !\n", x + j, y + i); - continue ; - } + switch (file_cspace) + { + case EET_COLORSPACE_ETC1: + 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 EET_COLORSPACE_RGB8_ETC2: + rg_etc2_rgb8_decode_block((uint8_t *) it, temporary); + break; + case EET_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; + // FIXME: Import NEON optimizations from Evas. for (k = 0; k < current_etc.h; k++) { memcpy(&p[current_etc.x - 1 + @@ -830,9 +902,11 @@ eet_data_image_etc1_decode(const void *data, } break; case EET_COLORSPACE_ETC1: - memcpy(&p[current_etc.x + - current_etc.y * etc1_width], - it, 8); + case EET_COLORSPACE_RGB8_ETC2: + case EET_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; default: abort(); @@ -1016,16 +1090,18 @@ eet_data_image_etc1_compressed_convert(int *size, unsigned int w, unsigned int h, int quality, - int compress) + int compress, + Eet_Image_Encoding lossy) { rg_etc1_pack_params param; - char *comp; - char *buffer; + uint8_t *comp = NULL; + uint8_t *buffer; uint32_t *data; uint32_t width, height; uint8_t header[8] = "TGV1"; int block_width, block_height, macro_block_width, macro_block_height; - int block_count, image_stride, image_height; + int block_count, image_stride, image_height, etc_block_size; + Eet_Colorspace cspace; Eina_Binbuf *r; void *result; @@ -1037,6 +1113,7 @@ eet_data_image_etc1_compressed_convert(int *size, data = (uint32_t *) data8; width = htonl(image_stride); height = htonl(image_height); + compress = !!compress; // Disable dithering, as it will deteriorate the quality of flat surfaces param.m_dithering = 0; @@ -1053,11 +1130,29 @@ eet_data_image_etc1_compressed_convert(int *size, block_height = _block_size_get(image_height + 2); header[4] = (block_height << 4) | block_width; - // header[5]: 0 for ETC1 - header[5] = 0; + // header[5]: 0 for ETC1, 1 for RGB8_ETC2, 2 for RGBA8_ETC2_EAC + switch (lossy) + { + case EET_IMAGE_ETC1: + cspace = EET_COLORSPACE_ETC1; + etc_block_size = 8; + header[5] = 0; + break; + case EET_IMAGE_ETC2_RGB: + cspace = EET_COLORSPACE_RGB8_ETC2; + etc_block_size = 8; + header[5] = 1; + break; + case EET_IMAGE_ETC2_RGBA: + cspace = EET_COLORSPACE_RGBA8_ETC2_EAC; + etc_block_size = 16; + header[5] = 2; + break; + default: abort(); + } // header[6]: 0 for raw, 1, for LZ4 compressed - header[6] = (!!compress & 0x1); + header[6] = compress; // header[7]: options (unused) header[7] = 0; @@ -1073,16 +1168,10 @@ eet_data_image_etc1_compressed_convert(int *size, // Number of ETC1 blocks in a compressed block block_count = (macro_block_width * macro_block_height) / (4 * 4); - buffer = alloca(block_count * 8); + buffer = alloca(block_count * etc_block_size); if (compress) - { - comp = alloca(LZ4_compressBound(block_count * 8)); - } - else - { - comp = NULL; - } + comp = alloca(LZ4_compressBound(block_count * etc_block_size)); // Write macro block for (int y = 0; y < image_height + 2; y += macro_block_height) @@ -1097,7 +1186,7 @@ eet_data_image_etc1_compressed_convert(int *size, for (int x = 0; x < image_stride + 2; x += macro_block_width) { - char *offset = buffer; + uint8_t *offset = buffer; int real_x = x; if (x == 0) real_x = 0; @@ -1172,19 +1261,33 @@ eet_data_image_etc1_compressed_convert(int *size, } } - rg_etc1_pack_block(offset, (unsigned int*) todo, ¶m); - offset += 8; + switch (cspace) + { + case EET_COLORSPACE_ETC1: + rg_etc1_pack_block(offset, (uint32_t *) todo, ¶m); + break; + case EET_COLORSPACE_RGB8_ETC2: + etc2_rgb8_block_pack(offset, (uint32_t *) todo, ¶m); + break; + case EET_COLORSPACE_RGBA8_ETC2_EAC: + etc2_rgba8_block_pack(offset, (uint32_t *) todo, ¶m); + break; + default: return 0; + } + + offset += etc_block_size; } } if (compress) { - wlen = LZ4_compressHC(buffer, comp, block_count * 8); + wlen = LZ4_compressHC((char *) buffer, (char *) comp, + block_count * etc_block_size); } else { comp = buffer; - wlen = block_count * 8; + wlen = block_count * etc_block_size; } if (wlen > 0) @@ -1757,9 +1860,12 @@ eet_data_image_encode_cipher(const void *data, w, h, alpha, quality); break; case EET_IMAGE_ETC1: - if (alpha) abort(); + case EET_IMAGE_ETC2_RGB: + if (alpha) abort(); + // fallthrough + case EET_IMAGE_ETC2_RGBA: d = eet_data_image_etc1_compressed_convert(&size, data, w, h, - quality, comp); + quality, comp, lossy); break; default: abort(); @@ -1937,9 +2043,24 @@ eet_data_image_header_decode_cipher(const void *data, if (w) *w = ntohl(*((unsigned int*) &(m[OFFSET_WIDTH]))); if (h) *h = ntohl(*((unsigned int*) &(m[OFFSET_HEIGHT]))); - if (alpha) *alpha = 0; // ETC1 only for now if (comp) *comp = m[OFFSET_OPTIONS] & 0x1; - if (lossy) *lossy = EET_IMAGE_ETC1; + switch (m[OFFSET_ALGORITHM] & 0xFF) + { + case 0: + if (lossy) *lossy = EET_IMAGE_ETC1; + if (alpha) *alpha = EINA_FALSE; + break; + case 1: + if (lossy) *lossy = EET_IMAGE_ETC2_RGB; + if (alpha) *alpha = EINA_FALSE; + break; + case 2: + if (alpha) *alpha = EINA_TRUE; + if (lossy) *lossy = EET_IMAGE_ETC2_RGBA; + break; + default: + return 0; + } if (quality) *quality = 50; return 1; @@ -1983,6 +2104,16 @@ static const Eet_Colorspace _eet_etc1_colorspace[] = { EET_COLORSPACE_ARGB8888 }; +static const Eet_Colorspace _eet_etc2_rgb_colorspace[] = { + EET_COLORSPACE_RGB8_ETC2, + EET_COLORSPACE_ARGB8888 +}; + +static const Eet_Colorspace _eet_etc2_rgba_colorspace[] = { + EET_COLORSPACE_RGBA8_ETC2_EAC, + EET_COLORSPACE_ARGB8888 +}; + EAPI int eet_data_image_colorspace_get(Eet_File *ef, const char *name, @@ -1995,8 +2126,15 @@ eet_data_image_colorspace_get(Eet_File *ef, r = eet_data_image_header_read_cipher(ef, name, cipher_key, NULL, NULL, NULL, NULL, NULL, &lossy); if (!r) return r; - if (lossy == EET_IMAGE_ETC1 && cspaces) - *cspaces = _eet_etc1_colorspace; + if (cspaces) + { + if (lossy == EET_IMAGE_ETC1) + *cspaces = _eet_etc1_colorspace; + else if (lossy == EET_IMAGE_ETC2_RGB) + *cspaces = _eet_etc2_rgb_colorspace; + else if (lossy == EET_IMAGE_ETC2_RGBA) + *cspaces = _eet_etc2_rgba_colorspace; + } return r; } @@ -2184,11 +2322,13 @@ _eet_data_image_decode_inside(const void *data, h, row_stride)) return 0; } - else if (lossy == EET_IMAGE_ETC1) + else if ((lossy == EET_IMAGE_ETC1) || + (lossy == EET_IMAGE_ETC2_RGB) || + (lossy == EET_IMAGE_ETC2_RGBA)) { - return eet_data_image_etc1_decode(data, size, d, + return eet_data_image_etc2_decode(data, size, d, src_x, src_y, src_w, src_h, - cspace); + alpha, cspace, lossy); } else abort(); @@ -2326,6 +2466,14 @@ eet_data_image_decode_to_cspace_surface_cipher(const void *data, ilossy != EET_IMAGE_ETC1) return 0; + if (cspace == EET_COLORSPACE_RGB8_ETC2 && + ilossy != EET_IMAGE_ETC2_RGB) + return 0; + + if (cspace == EET_COLORSPACE_RGBA8_ETC2_EAC && + ilossy != EET_IMAGE_ETC2_RGBA) + return 0; + if (cspace == EET_COLORSPACE_ARGB8888 && w * 4 > row_stride) return 0; --
