From 18e969f6441682a48760868b1fb5ab305b5e1058 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Thu, 19 Jun 2014 15:55:28 +0900 Subject: [PATCH] Evas DDS: Implement DXT1 to RGBA decoding --- src/Makefile_Evas.am | 15 +- .../evas/loaders/dds/evas_image_load_dds.c | 141 ++++++++++++------ src/modules/evas/loaders/dds/s3tc.h | 10 +- src/modules/evas/loaders/dds/s3tc_decoder.c | 61 ++++++++ 4 files changed, 174 insertions(+), 53 deletions(-) diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index bc1a65f679..f6b6b68efd 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -1374,18 +1374,27 @@ endif if BUILD_LOADER_DDS if EVAS_STATIC_BUILD_DDS -lib_evas_libevas_la_SOURCES += modules/evas/loaders/dds/evas_image_load_dds.c +lib_evas_libevas_la_SOURCES += \ +modules/evas/loaders/dds/evas_image_load_dds.c \ +modules/evas/loaders/dds/s3tc_decoder.c \ +modules/evas/loaders/dds/s3tc.h lib_evas_libevas_la_CPPFLAGS += @evas_image_loader_dds_cflags@ lib_evas_libevas_la_LIBADD += @evas_image_loader_dds_libs@ if EVAS_CSERVE2 -bin_evas_evas_cserve2_slave_SOURCES += modules/evas/loaders/dds/evas_image_load_dds.c +bin_evas_evas_cserve2_slave_SOURCES += \ +modules/evas/loaders/dds/evas_image_load_dds.c \ +modules/evas/loaders/dds/s3tc_decoder.c \ +modules/evas/loaders/dds/s3tc.h bin_evas_evas_cserve2_slave_CPPFLAGS += @evas_image_loader_dds_cflags@ bin_evas_evas_cserve2_slave_LDADD += @evas_image_loader_dds_libs@ endif else loaderddspkgdir = $(libdir)/evas/modules/loaders/dds/$(MODULE_ARCH) loaderddspkg_LTLIBRARIES = modules/evas/loaders/dds/module.la -modules_evas_loaders_dds_module_la_SOURCES = modules/evas/loaders/dds/evas_image_load_dds.c +modules_evas_loaders_dds_module_la_SOURCES = \ +modules/evas/loaders/dds/evas_image_load_dds.c \ +modules/evas/loaders/dds/s3tc_decoder.c \ +modules/evas/loaders/dds/s3tc.h modules_evas_loaders_dds_module_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -I$(top_srcdir)/src/lib/evas/include \ @EVAS_CFLAGS@ \ diff --git a/src/modules/evas/loaders/dds/evas_image_load_dds.c b/src/modules/evas/loaders/dds/evas_image_load_dds.c index 836751d651..db2b9cc294 100644 --- a/src/modules/evas/loaders/dds/evas_image_load_dds.c +++ b/src/modules/evas/loaders/dds/evas_image_load_dds.c @@ -16,22 +16,24 @@ # include #endif +#define DDS_HEADER_SIZE 128 + typedef struct _Evas_Loader_Internal Evas_Loader_Internal; struct _Evas_Loader_Internal { Eina_File *f; - DxtFormat format; - size_t data_size; + Evas_Colorspace format; + unsigned int stride, block_size, data_size; struct { - uint32_t flags; - uint32_t fourcc; - uint32_t rgb_bitcount; - uint32_t r_mask; - uint32_t g_mask; - uint32_t b_mask; - uint32_t a_mask; + 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; Eina_Bool has_alpha : 1; // TODO: check mipmaps to load faster a small image :) @@ -88,17 +90,17 @@ static const Evas_Colorspace cspaces_s3tc_dxt1_rgba[] = { }; static const Evas_Colorspace cspaces_s3tc_dxt2[] = { - //EVAS_COLORSPACE_RGBA_S3TC_DXT2, // Not in OpenGL + //EVAS_COLORSPACE_RGBA_S3TC_DXT2, EVAS_COLORSPACE_ARGB8888 }; static const Evas_Colorspace cspaces_s3tc_dxt3[] = { - //EVAS_COLORSPACE_RGB_S3TC_DXT3, + //EVAS_COLORSPACE_RGBA_S3TC_DXT3, EVAS_COLORSPACE_ARGB8888 }; static const Evas_Colorspace cspaces_s3tc_dxt4[] = { - //EVAS_COLORSPACE_RGBA_S3TC_DXT4, // Not in OpenGL + //EVAS_COLORSPACE_RGBA_S3TC_DXT4, EVAS_COLORSPACE_ARGB8888 }; @@ -115,7 +117,7 @@ evas_image_load_file_open_dds(Eina_File *f, Eina_Stringshare *key EINA_UNUSED, { Evas_Loader_Internal *loader; - if (eina_file_size_get(f) <= 128) + if (eina_file_size_get(f) <= DDS_HEADER_SIZE) { *error = EVAS_LOAD_ERROR_CORRUPT_FILE; return NULL; @@ -149,37 +151,40 @@ evas_image_load_file_close_dds(void *loader_data) free(loader); } -static inline uint32_t +static inline unsigned int _dword_read(const char **m) { - uint32_t val = *((uint32_t *) *m); + unsigned int val = *((unsigned int *) *m); *m += 4; return val; } -#define FAIL() do { fprintf(stderr, "DDS: ERROR at %s:%d", __FUNCTION__, __LINE__); goto on_error; } while (0) +#define FAIL() do { fprintf(stderr, "DDS: ERROR at %s:%d", \ + __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 uint32_t base_flags = /* 0x1007 */ + static const unsigned int base_flags = /* 0x1007 */ DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; Evas_Loader_Internal *loader = loader_data; - uint32_t flags, height, width, pitchOrLinearSize, block_size = 16, stride, - caps, caps2; + unsigned int flags, height, width, pitchOrLinearSize, caps, caps2; Eina_Bool has_linearsize, has_mipmapcount; const char *m; + char *map; - m = eina_file_map_all(loader->f, EINA_FILE_SEQUENTIAL); - if (!m) + 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 @@ -215,7 +220,7 @@ evas_image_load_file_head_dds(void *loader_data, FAIL(); // Skip depth & mipmap count + reserved[11] - m += 13 * sizeof(uint32_t); + m += 13 * sizeof(unsigned int); // Entering DDS_PIXELFORMAT ddspf if (_dword_read(&m) != 32) FAIL(); @@ -224,41 +229,43 @@ evas_image_load_file_head_dds(void *loader_data, FAIL(); // Unsupported (uncompressed formats may not have a FOURCC) loader->pf.fourcc = _dword_read(&m); loader->pf.has_alpha = EINA_TRUE; + loader->block_size = 16; switch (loader->pf.fourcc) { case FOURCC('D', 'X', 'T', '1'): - loader->format = DXT1; + loader->block_size = 8; if ((loader->pf.flags & DDPF_ALPHAPIXELS) == 0) { prop->alpha = EINA_FALSE; prop->cspaces = cspaces_s3tc_dxt1_rgb; loader->pf.has_alpha = EINA_FALSE; + 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; } - block_size = 8; break; #if 0 case FOURCC('D', 'X', 'T', '2'): - loader->format = DXT2; + 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 = DXT3; + 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 = DXT4; + 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 = DXT5; + loader->format = EVAS_COLORSPACE_RGBA_S3TC_DXT5; prop->alpha = EINA_TRUE; prop->cspaces = cspaces_s3tc_dxt5; break; @@ -291,13 +298,13 @@ evas_image_load_file_head_dds(void *loader_data, } // Since the rest is unused, just ignore it. - stride = ((width + 3) >> 2) * block_size; - loader->data_size = stride * ((height + 3) >> 2); + 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) < (128 + loader->data_size)) + if (eina_file_size_get(loader->f) < (DDS_HEADER_SIZE + loader->data_size)) FAIL(); prop->h = height; @@ -305,7 +312,7 @@ evas_image_load_file_head_dds(void *loader_data, *error = EVAS_LOAD_ERROR_NONE; on_error: - eina_file_map_free(loader->f, (void *) m); + eina_file_map_free(loader->f, map); return (*error == EVAS_LOAD_ERROR_NONE); } @@ -315,24 +322,74 @@ evas_image_load_file_data_dds(void *loader_data, void *pixels, int *error) { + void (*func) (unsigned int *bgra, const unsigned char *s3tc) = NULL; Evas_Loader_Internal *loader = loader_data; - const char *m; - - Eina_Bool r = EINA_FALSE; + unsigned int *pix = pixels; + unsigned char *map = NULL; + const unsigned char *src; *error = EVAS_LOAD_ERROR_CORRUPT_FILE; - m = eina_file_map_all(loader->f, EINA_FILE_WILLNEED); - if (!m) return EINA_FALSE; + map = eina_file_map_all(loader->f, EINA_FILE_WILLNEED); + if (!map) + return EINA_FALSE; - // TODO + src = map + DDS_HEADER_SIZE; + if (eina_file_size_get(loader->f) < (DDS_HEADER_SIZE + loader->data_size)) + FAIL(); - *error = EVAS_LOAD_ERROR_GENERIC; - r = EINA_FALSE; // FIXME + if (prop->cspace != EVAS_COLORSPACE_ARGB8888) + { + if (loader->format != prop->cspace) + FAIL(); + memcpy(pixels, src, loader->data_size); + *error = EVAS_LOAD_ERROR_NONE; + eina_file_map_free(loader->f, (void *) src); + return EINA_TRUE; + } + + // Decode to BGRA + switch (loader->format) + { + case EVAS_COLORSPACE_RGB_S3TC_DXT1: + func = s3tc_decode_dxt1_rgb; + break; + case EVAS_COLORSPACE_RGBA_S3TC_DXT1: + func = s3tc_decode_dxt1_rgba; + 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, m); - return r; + eina_file_map_free(loader->f, (void *) src); + return (*error == EVAS_LOAD_ERROR_NONE); } Evas_Image_Load_Func evas_image_load_dds_func = diff --git a/src/modules/evas/loaders/dds/s3tc.h b/src/modules/evas/loaders/dds/s3tc.h index 8380c3a6a8..a2efdeb47b 100644 --- a/src/modules/evas/loaders/dds/s3tc.h +++ b/src/modules/evas/loaders/dds/s3tc.h @@ -1,13 +1,7 @@ #ifndef EFL_S3TC_H #define EFL_S3TC_H -typedef enum { - DXT1, - DXT2, - DXT3, - DXT4, - DXT5, - DX10 -} DxtFormat; +void s3tc_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc); +void s3tc_decode_dxt1_rgba(unsigned int *bgra, const unsigned char *s3tc); #endif // EFL_S3TC_H diff --git a/src/modules/evas/loaders/dds/s3tc_decoder.c b/src/modules/evas/loaders/dds/s3tc_decoder.c index 44492dc517..30cd8ad795 100644 --- a/src/modules/evas/loaders/dds/s3tc_decoder.c +++ b/src/modules/evas/loaders/dds/s3tc_decoder.c @@ -1,2 +1,63 @@ #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))) + +static inline unsigned int +_rgb565_to_rgba8888(unsigned short s) +{ + return 0xFF000000 | CONVERT_RGB_565_TO_RGB_888(s); +} + +static void +_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc, + 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] = _rgb565_to_rgba8888(color0); + colors[1] = _rgb565_to_rgba8888(color1); + if (color0 > color1) + { + // This is what's not supported by S2TC. + colors[2] = 0xFF000000 | INTERP_RGB_256((2*256)/3, colors[0], colors[1]); + colors[3] = 0xFF000000 | INTERP_RGB_256((1*256)/3, colors[0], colors[1]); + } + else + { + colors[2] = 0xFF000000 | INTERP_RGB_256(128, colors[0], colors[1]); + colors[3] = (alpha ? 0x00000000 : 0xFF000000); + } + + 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; + bits >>= 2; + + bgra[(j * 4) + i] = colors[idx]; + } +} + +void s3tc_decode_dxt1_rgb(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc, EINA_FALSE); +} + +void s3tc_decode_dxt1_rgba(unsigned int *bgra, const unsigned char *s3tc) +{ + _decode_dxt1_rgb(bgra, s3tc, EINA_TRUE); +}