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).
This commit is contained in:
Jean-Philippe Andre 2016-03-22 16:57:52 +09:00
parent 39a970835b
commit 078117d367
5 changed files with 560 additions and 0 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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, &reg1);
if (srccs != EFL_GFX_COLORSPACE_ARGB8888)
from_argb = efl_draw_convert_func_get(EFL_GFX_COLORSPACE_ARGB8888, dstcs, &reg2);
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;
}

View File

@ -5,6 +5,8 @@
#include <Ector.h>
#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();
}

View File

@ -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