From 078117d367afff6862b72bec8bbc29909b259fd4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Tue, 22 Mar 2016 16:57:52 +0900 Subject: [PATCH] draw: Import or implement some colorspace conversion routines Those and many more will be required for proper map/unmap support. There will be problems with planar formats: YUV, RGB565_A5P, ETC1_ALPHA The quick solution to this problem is to not support region conversions, only full-image (so we can assume the location of the various planes in memory). --- src/Makefile_Evas.am | 1 + src/static_libs/draw/draw.h | 2 + src/static_libs/draw/draw_convert.c | 527 ++++++++++++++++++++++++++++ src/static_libs/draw/draw_main.c | 3 + src/static_libs/draw/draw_private.h | 27 ++ 5 files changed, 560 insertions(+) create mode 100644 src/static_libs/draw/draw_convert.c diff --git a/src/Makefile_Evas.am b/src/Makefile_Evas.am index a2a4bea0b2..4a47e351fb 100644 --- a/src/Makefile_Evas.am +++ b/src/Makefile_Evas.am @@ -298,6 +298,7 @@ lib/evas/canvas/evas_vg_shape.c # Static draw lib lib_evas_libevas_la_SOURCES += \ static_libs/draw/draw_alpha_main.c \ +static_libs/draw/draw_convert.c \ static_libs/draw/draw_main_neon.c \ static_libs/draw/draw_main.c diff --git a/src/static_libs/draw/draw.h b/src/static_libs/draw/draw.h index 51bf917a30..b63b73d2e0 100644 --- a/src/static_libs/draw/draw.h +++ b/src/static_libs/draw/draw.h @@ -13,6 +13,7 @@ typedef void (*RGBA_Comp_Func_Solid) (uint32_t *dest, int length, uint32_t color typedef void (*RGBA_Comp_Func_Mask) (uint32_t *dest, uint8_t *mask, int length, uint32_t color); typedef void (*Draw_Func_ARGB_Mix3) (uint32_t *dest, uint32_t *src, uint32_t *mul, int len, uint32_t color); typedef void (*Alpha_Gfx_Func) (uint8_t *src, uint8_t *dst, int len); +typedef Eina_Bool (*Cspace_Convert_Func) (void *dst, const void *src, int w, int h, int src_stride, int dst_stride, Eina_Bool has_alpha, Efl_Gfx_Colorspace srccs, Efl_Gfx_Colorspace dstcs); int efl_draw_init(void); @@ -21,6 +22,7 @@ RGBA_Comp_Func_Solid efl_draw_func_solid_span_get (Efl_Gfx_Render_Op op, uint3 RGBA_Comp_Func_Mask efl_draw_func_mask_span_get (Efl_Gfx_Render_Op op, uint32_t color); Draw_Func_ARGB_Mix3 efl_draw_func_argb_mix3_get (Efl_Gfx_Render_Op op, uint32_t color); Alpha_Gfx_Func efl_draw_alpha_func_get (Efl_Gfx_Render_Op op, Eina_Bool has_mask); +Cspace_Convert_Func efl_draw_convert_func_get (Efl_Gfx_Colorspace origcs, Efl_Gfx_Colorspace dstcs, Eina_Bool *region_can); /* common sw draw helpers */ diff --git a/src/static_libs/draw/draw_convert.c b/src/static_libs/draw/draw_convert.c new file mode 100644 index 0000000000..6e1a35b29d --- /dev/null +++ b/src/static_libs/draw/draw_convert.c @@ -0,0 +1,527 @@ +#include "draw.h" +#include "draw_private.h" +#include "../rg_etc/rg_etc1.h" + +#if DIV_USING_BITSHIFT +# define DEFINE_DIVIDER(div) const int pow2 = _pow2_geq((div) << 10); const int numerator = (1 << pow2) / (div); +# define DIVIDE(val) (((val) * numerator) >> pow2) +#else +# define DEFINE_DIVIDER(div) const int divider = (div); +# define DIVIDE(val) ((val) / divider) +#endif + +#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 CONVERT_A5P_TO_A8(s) \ + ((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) + +#define CONVERT_ARGB_8888_TO_A_8(s) ((s) >> 24) + +// finds smallest power of 2 above val +static int +_pow2_geq(int val) +{ + for (int n = 0; n < 32; n++) + if (val <= (1 << n)) + return n; + + return 32; // impossible +} + +static Eina_Bool +_convert_gry8_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint8_t *in = src; + uint32_t *out = dst; + int in_step, out_step, x, y; + + if (!src_stride) src_stride = w; + if (!dst_stride) dst_stride = w * 4; + in_step = src_stride; + out_step = dst_stride / 4; + + if (has_alpha) + { + // transparent white + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = in[x]; + out[x] = DRAW_ARGB_JOIN(c, c, c, c); + } + in += in_step; + out += out_step; + } + } + else + { + // opaque grayscale + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = in[x]; + out[x] = DRAW_ARGB_JOIN(0xFF, c, c, c); + } + in += in_step; + out += out_step; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_agry88_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint16_t *in = src; + uint32_t *out = dst; + int in_step, out_step, x, y; + + if (!src_stride) src_stride = w * 2; + if (!dst_stride) dst_stride = w * 4; + in_step = src_stride / 2; + out_step = dst_stride / 4; + + if (has_alpha) + { + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = in[x] & 0xFF; + int a = in[x] >> 8; + out[x] = DRAW_ARGB_JOIN(a, c, c, c); + } + in += in_step; + out += out_step; + } + } + else + { + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = in[x] & 0xFF; + out[x] = DRAW_ARGB_JOIN(0xFF, c, c, c); + } + in += in_step; + out += out_step; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_argb8888_to_gry8(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint32_t *in = src; + uint8_t *out = dst; + int in_step, out_step, x, y; +#if RGBA2LUM_WEIGHTED + const int WR = 299; + const int WG = 587; + const int WB = 114; +#else + const int WR = 1; + const int WG = 1; + const int WB = 1; +#endif + DEFINE_DIVIDER(WR + WG + WB); + + if (!src_stride) src_stride = w * 4; + if (!dst_stride) dst_stride = w; + in_step = src_stride / 4; + out_step = dst_stride; + + if (has_alpha) + { + // copy only alpha + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + out[x] = A_VAL(in + x); + in += in_step; + out += out_step; + } + } + else + { + // copy only color to grayscale + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + out[x] = DIVIDE((R_VAL(in + x) * WR) + (G_VAL(in + x) * WG) + (B_VAL(in + x) * WB)); + in += in_step; + out += out_step; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_argb8888_to_agry88(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint32_t *in = src; + uint16_t *out = dst; + int in_step, out_step, x, y; +#if RGBA2LUM_WEIGHTED + const int WR = 299; + const int WG = 587; + const int WB = 114; +#else + const int WR = 1; + const int WG = 1; + const int WB = 1; +#endif + DEFINE_DIVIDER(WR + WG + WB); + + if (!src_stride) src_stride = w * 4; + if (!dst_stride) dst_stride = w * 2; + in_step = src_stride / 4; + out_step = dst_stride / 2; + + if (has_alpha) + { + // copy only alpha + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int a = A_VAL(in + x); + int c = DIVIDE((R_VAL(in + x) * WR) + (G_VAL(in + x) * WG) + (B_VAL(in + x) * WB)); + out[x] = (a << 8) | c; + } + in += in_step; + out += out_step; + } + } + else + { + // copy only color to grayscale + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = DIVIDE((R_VAL(in + x) * WR) + (G_VAL(in + x) * WG) + (B_VAL(in + x) * WB)); + out[x] = 0xFF00 | c; + } + in += in_step; + out += out_step; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_rgb565_a5p_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint16_t *in = src; + const uint8_t *in_alpha; + uint32_t *out = dst; + int in_step, out_step, a_step, x, y; + + if (!src_stride) src_stride = w * 2; + if (!dst_stride) dst_stride = w * 4; + in_step = src_stride / 2; + a_step = src_stride; + out_step = dst_stride / 4; + + // no region support (2 planes): basic safety check (can't verify h) + EINA_SAFETY_ON_FALSE_RETURN_VAL((src_stride == (w * 2)) && (dst_stride == (w * 4)), EINA_FALSE); + + if (has_alpha) + { + in_alpha = ((uint8_t *) in) + (src_stride * h); + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int a = CONVERT_A5P_TO_A8(in_alpha[x]); + int c = CONVERT_RGB_565_TO_RGB_888(in[x]); + out[x] = (a << 24) | c; + } + in_alpha += a_step; + in += in_step; + out += out_step; + } + } + else + { + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + int c = CONVERT_RGB_565_TO_RGB_888(in[x]); + out[x] = 0xFF000000 | c; + } + in += in_step; + out += out_step; + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_etc2_rgb8_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint8_t *in = src; + uint32_t *out = dst; + int out_step, x, y, k; + unsigned int bgra[16]; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(!(w & 3) && !(h & 3), EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(!has_alpha, EINA_FALSE); + + // jumps lines 4 by 4 + if (!src_stride) src_stride = w * 8 / 4; + if (!dst_stride) dst_stride = w * 4; + out_step = dst_stride / 4; + + for (y = 0; y < h; y += 4) + { + for (x = 0; x < w; x += 4, in += 8) + { + rg_etc2_rgb8_decode_block(in, bgra); + for (k = 0; k < 4; k++) + memcpy(out + x + k * out_step, bgra + k * 16, 16); + } + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_etc2_rgba8_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint8_t *in = src; + uint32_t *out = dst; + int out_step, x, y, k; + unsigned int bgra[16]; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(!(w & 3) && !(h & 3), EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(!has_alpha, EINA_FALSE); + + // jumps lines 4 by 4 + if (!src_stride) src_stride = w * 16 / 4; + if (!dst_stride) dst_stride = w * 4; + out_step = dst_stride / 4; + + for (y = 0; y < h; y += 4) + { + for (x = 0; x < w; x += 4, in += 16) + { + rg_etc2_rgba8_decode_block(in, bgra); + for (k = 0; k < 4; k++) + memcpy(out + x + k * out_step, bgra + k * 16, 16); + } + out += out_step * 4; + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_etc1_alpha_to_argb8888(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs EINA_UNUSED, + Efl_Gfx_Colorspace dstcs EINA_UNUSED) +{ + const uint8_t *in = src, *in_alpha; + uint32_t *out = dst; + int out_step, x, y, j, k; + unsigned int bgra[16], alpha[16]; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(!(w & 3) && !(h & 3), EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(!has_alpha, EINA_FALSE); + + // jumps lines 4 by 4 + if (!src_stride) src_stride = w * 8 / 4; + if (!dst_stride) dst_stride = w * 4; + out_step = dst_stride / 4; + + in_alpha = in + src_stride * h; + + for (y = 0; y < h; y += 4) + { + for (x = 0; x < w; x += 4, in += 8, in_alpha += 8) + { + rg_etc2_rgba8_decode_block(in, bgra); + rg_etc2_rgba8_decode_block(in_alpha, alpha); + for (k = 0; k < 4; k++) + for (j = 0; j < 4; j++) + { + int a = (alpha[(k * 4) + j] & 0x00FF00) >> 8; + int c = (bgra[(k * 4) + j] & 0x00FFFFFF); + out[(k * out_step) + j] = (a << 24) | c; + } + } + out += out_step; + } + + return EINA_TRUE; +} + +static Eina_Bool +_convert_generic_two_pass(void *dst, const void *src, int w, int h, + int src_stride, int dst_stride, Eina_Bool has_alpha, + Efl_Gfx_Colorspace srccs, Efl_Gfx_Colorspace dstcs) +{ + Cspace_Convert_Func to_argb = efl_draw_convert_func_get(srccs, EFL_GFX_COLORSPACE_ARGB8888, NULL); + Cspace_Convert_Func from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, NULL); + uint32_t *argb; + + EINA_SAFETY_ON_NULL_RETURN_VAL(to_argb, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(from_argb, EINA_FALSE); + + argb = malloc(w * h * sizeof(uint32_t)); + if (!argb) return EINA_FALSE; + + if (!to_argb(argb, src, w, h, src_stride, 0, has_alpha, srccs, EFL_GFX_COLORSPACE_ARGB8888)) + goto fail; + + if (!from_argb(dst, argb, w, h, 0, dst_stride, has_alpha, EFL_GFX_COLORSPACE_ARGB8888, dstcs)) + goto fail; + + free(argb); + return EINA_TRUE; + +fail: + free(argb); + return EINA_FALSE; +} + +Cspace_Convert_Func +efl_draw_convert_func_get(Efl_Gfx_Colorspace srccs, Efl_Gfx_Colorspace dstcs, + Eina_Bool *region_can) +{ + Eina_Bool reg1 = EINA_FALSE, reg2 = EINA_FALSE; + Cspace_Convert_Func to_argb = NULL; + Cspace_Convert_Func from_argb = NULL; + + EINA_SAFETY_ON_FALSE_RETURN_VAL(srccs != dstcs, NULL); + + if (dstcs != EFL_GFX_COLORSPACE_ARGB8888) + to_argb = efl_draw_convert_func_get(srccs, EFL_GFX_COLORSPACE_ARGB8888, ®1); + + if (srccs != EFL_GFX_COLORSPACE_ARGB8888) + from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, ®2); + + if (region_can) *region_can = EINA_TRUE; + + switch (srccs) + { + case EFL_GFX_COLORSPACE_ARGB8888: + if (dstcs == EFL_GFX_COLORSPACE_GRY8) + return _convert_argb8888_to_gry8; + if (dstcs == EFL_GFX_COLORSPACE_AGRY88) + return _convert_argb8888_to_agry88; + break; + + case EFL_GFX_COLORSPACE_GRY8: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + return _convert_gry8_to_argb8888; + break; + + case EFL_GFX_COLORSPACE_AGRY88: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + return _convert_agry88_to_argb8888; + break; + + case EFL_GFX_COLORSPACE_RGB565_A5P: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + { + // we could do regions, but we would need a pointer to the alpha plane. + if (region_can) *region_can = EINA_FALSE; + return _convert_rgb565_a5p_to_argb8888; + } + break; + + case EFL_GFX_COLORSPACE_YCBCR422P601_PL: + case EFL_GFX_COLORSPACE_YCBCR422601_PL: + case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL: + case EFL_GFX_COLORSPACE_YCBCR420TM12601_PL: + case EFL_GFX_COLORSPACE_YCBCR422P709_PL: + /* FIXME */ + + case EFL_GFX_COLORSPACE_ETC1: + case EFL_GFX_COLORSPACE_RGB8_ETC2: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + { + // we could do regions, but only if they are 4x4 aligned. + if (region_can) *region_can = EINA_FALSE; + return _convert_etc2_rgb8_to_argb8888; + } + break; + + case EFL_GFX_COLORSPACE_RGBA8_ETC2_EAC: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + { + // we could do regions, but only if they are 4x4 aligned. + if (region_can) *region_can = EINA_FALSE; + return _convert_etc2_rgba8_to_argb8888; + } + break; + + case EFL_GFX_COLORSPACE_ETC1_ALPHA: + if (dstcs == EFL_GFX_COLORSPACE_ARGB8888) + { + // we could do regions, but only if they are 4x4 aligned. + if (region_can) *region_can = EINA_FALSE; + return _convert_etc1_alpha_to_argb8888; + } + break; + + case EFL_GFX_COLORSPACE_RGB_S3TC_DXT1: + case EFL_GFX_COLORSPACE_RGBA_S3TC_DXT1: + case EFL_GFX_COLORSPACE_RGBA_S3TC_DXT2: + case EFL_GFX_COLORSPACE_RGBA_S3TC_DXT3: + case EFL_GFX_COLORSPACE_RGBA_S3TC_DXT4: + case EFL_GFX_COLORSPACE_RGBA_S3TC_DXT5: + /* FIXME: can convert to ARGB */ + + default: + break; + } + + + // fallback to two-pass + if (to_argb && from_argb) + { + if (region_can) *region_can = (reg1 && reg2); + return _convert_generic_two_pass; + } + + ERR("unsupported colorspace conversion from %d to %d", srccs, dstcs); + return NULL; +} diff --git a/src/static_libs/draw/draw_main.c b/src/static_libs/draw/draw_main.c index 0e423aaa38..3d920639ab 100644 --- a/src/static_libs/draw/draw_main.c +++ b/src/static_libs/draw/draw_main.c @@ -5,6 +5,8 @@ #include #include "draw_private.h" +int _draw_log_dom = -1; + /* s = source pixel d = destination pixel @@ -281,6 +283,7 @@ efl_draw_init() static int i = 0; if (!(i++)) { + _draw_log_dom = eina_log_domain_register("efl_draw", EINA_COLOR_ORANGE); efl_draw_sse2_init(); efl_draw_neon_init(); } diff --git a/src/static_libs/draw/draw_private.h b/src/static_libs/draw/draw_private.h index c07d00a684..329ed113b4 100644 --- a/src/static_libs/draw/draw_private.h +++ b/src/static_libs/draw/draw_private.h @@ -25,6 +25,11 @@ } \ } +// optimization +#define DIV_USING_BITSHIFT 1 +// behaviour setting +#define RGBA2LUM_WEIGHTED 1 + /* 255 - alpha */ static inline int alpha_inverse(uint32_t color) @@ -35,8 +40,30 @@ alpha_inverse(uint32_t color) extern RGBA_Comp_Func_Solid func_for_mode_solid[EFL_GFX_RENDER_OP_LAST]; extern RGBA_Comp_Func func_for_mode[EFL_GFX_RENDER_OP_LAST]; +extern int _draw_log_dom; void efl_draw_sse2_init(void); void efl_draw_neon_init(void); +#ifdef ERR +# undef ERR +#endif +#define ERR(...) EINA_LOG_DOM_ERR(_draw_log_dom, __VA_ARGS__) +#ifdef INF +# undef INF +#endif +#define INF(...) EINA_LOG_DOM_INFO(_draw_log_dom, __VA_ARGS__) +#ifdef WRN +# undef WRN +#endif +#define WRN(...) EINA_LOG_DOM_WARN(_draw_log_dom, __VA_ARGS__) +#ifdef CRI +# undef CRI +#endif +#define CRI(...) EINA_LOG_DOM_CRIT(_draw_log_dom, __VA_ARGS__) +#ifdef DBG +# undef DBG +#endif +#define DBG(...) EINA_LOG_DOM_DBG(_draw_log_dom, __VA_ARGS__) + #endif