forked from enlightenment/efl
505 lines
15 KiB
C
505 lines
15 KiB
C
#include "evas_filter.h"
|
|
#include "evas_filter_private.h"
|
|
#include "evas_blend_private.h"
|
|
|
|
// Use a better formula than R+G+B for rgba to alpha conversion (RGB to YCbCr)
|
|
#define RGBA2ALPHA_WEIGHTED 1
|
|
|
|
typedef Eina_Bool (*image_draw_func) (void *data, void *context, void *surface, void *image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth, Eina_Bool do_async);
|
|
static Eina_Bool _mapped_blend(void *data, void *drawctx, void *in, void *out, Evas_Filter_Fill_Mode fillmode, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, image_draw_func image_draw);
|
|
|
|
struct Filter_Blend_Draw_Context
|
|
{
|
|
int render_op;
|
|
DATA32 color;
|
|
};
|
|
|
|
static Eina_Bool
|
|
_image_draw_cpu_alpha2alpha(void *data EINA_UNUSED, void *context,
|
|
void *surface, void *image,
|
|
int src_x, int src_y, int src_w, int src_h,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
int smooth EINA_UNUSED,
|
|
Eina_Bool do_async EINA_UNUSED)
|
|
{
|
|
struct Filter_Blend_Draw_Context *dc = context;
|
|
RGBA_Image *src = image;
|
|
RGBA_Image *dst = surface;
|
|
DATA8* srcdata = src->image.data8;
|
|
DATA8* dstdata = dst->image.data8;
|
|
Alpha_Gfx_Func func;
|
|
int y, sw, dw;
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);
|
|
|
|
func = evas_common_alpha_func_get(dc->render_op);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
|
|
|
|
sw = src->cache_entry.w;
|
|
dw = dst->cache_entry.w;
|
|
|
|
srcdata += src_y * sw;
|
|
dstdata += dst_y * dw;
|
|
for (y = src_h; y; y--)
|
|
{
|
|
func(srcdata + src_x, dstdata + dst_x, src_w);
|
|
srcdata += sw;
|
|
dstdata += dw;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_image_draw_cpu_alpha2rgba(void *data EINA_UNUSED, void *context,
|
|
void *surface, void *image,
|
|
int src_x, int src_y, int src_w, int src_h,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
int smooth EINA_UNUSED,
|
|
Eina_Bool do_async EINA_UNUSED)
|
|
{
|
|
struct Filter_Blend_Draw_Context *dc = context;
|
|
RGBA_Image *src = image;
|
|
RGBA_Image *dst = surface;
|
|
DATA8* srcdata = src->image.data8;
|
|
DATA32* dstdata = dst->image.data;
|
|
RGBA_Gfx_Func func;
|
|
int y, sw, dw;
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);
|
|
|
|
func = evas_common_gfx_func_composite_mask_color_span_get
|
|
(dc->color, dst->cache_entry.flags.alpha, 1, dc->render_op);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
|
|
|
|
sw = src->cache_entry.w;
|
|
dw = dst->cache_entry.w;
|
|
|
|
srcdata += src_y * sw;
|
|
dstdata += dst_y * dw;
|
|
for (y = src_h; y; y--)
|
|
{
|
|
func(NULL, srcdata + src_x, dc->color, dstdata + dst_x, src_w);
|
|
srcdata += sw;
|
|
dstdata += dw;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_filter_blend_cpu_generic_do(Evas_Filter_Command *cmd,
|
|
image_draw_func image_draw)
|
|
{
|
|
RGBA_Image *in, *out;
|
|
int sw, sh, dx, dy, dw, dh, sx, sy;
|
|
struct Filter_Blend_Draw_Context dc;
|
|
|
|
in = cmd->input->backing;
|
|
out = cmd->output->backing;
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data8, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data8, EINA_FALSE);
|
|
|
|
sx = 0;
|
|
sy = 0;
|
|
sw = in->cache_entry.w;
|
|
sh = in->cache_entry.h;
|
|
|
|
dx = cmd->draw.ox;
|
|
dy = cmd->draw.oy;
|
|
dw = out->cache_entry.w;
|
|
dh = out->cache_entry.h;
|
|
|
|
if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
|
|
return EINA_TRUE;
|
|
|
|
// Stretch if necessary.
|
|
|
|
/* NOTE: As of 2014/03/11, this will happen only with RGBA buffers, since
|
|
* only proxy sources may be scaled. So, we don't need an alpha scaling
|
|
* algorithm just now.
|
|
*/
|
|
|
|
if ((sw != dw || sh != dh) && (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_XY))
|
|
{
|
|
Evas_Filter_Buffer *fb;
|
|
|
|
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
|
|
sw = dw;
|
|
if (cmd->draw.fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
|
|
sh = dh;
|
|
|
|
BUFFERS_LOCK();
|
|
fb = evas_filter_buffer_scaled_get(cmd->ctx, cmd->input, sw, sh);
|
|
BUFFERS_UNLOCK();
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(fb, EINA_FALSE);
|
|
fb->locked = EINA_FALSE;
|
|
in = fb->backing;
|
|
}
|
|
|
|
dc.render_op = cmd->draw.render_op;
|
|
dc.color = ARGB_JOIN(cmd->draw.A, cmd->draw.R, cmd->draw.G, cmd->draw.B);
|
|
return _mapped_blend(cmd->ENDT, &dc, in, out, cmd->draw.fillmode,
|
|
sx, sy, sw, sh, dx, dy, dw, dh, image_draw);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_image_draw_cpu_rgba2alpha(void *data EINA_UNUSED, void *context EINA_UNUSED,
|
|
void *surface, void *image,
|
|
int src_x, int src_y, int src_w, int src_h,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
int smooth EINA_UNUSED,
|
|
Eina_Bool do_async EINA_UNUSED)
|
|
{
|
|
RGBA_Image *src = image;
|
|
RGBA_Image *dst = surface;
|
|
DATA32* srcdata = src->image.data;
|
|
DATA8* dstdata = dst->image.data8;
|
|
int x, y, sw, dw;
|
|
#if RGBA2ALPHA_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);
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);
|
|
|
|
sw = src->cache_entry.w;
|
|
dw = dst->cache_entry.w;
|
|
|
|
srcdata += src_y * sw;
|
|
dstdata += dst_y * dw;
|
|
for (y = src_h; y; y--)
|
|
{
|
|
DATA32 *s = srcdata + src_x;
|
|
DATA8 *d = dstdata + dst_x;
|
|
for (x = src_w; x; x--, d++, s++)
|
|
*d = DIVIDE((R_VAL(s) * WR) + (G_VAL(s) * WG) + (B_VAL(s) * WB));
|
|
srcdata += sw;
|
|
dstdata += dw;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_image_draw_cpu_rgba2rgba(void *data EINA_UNUSED, void *context,
|
|
void *surface, void *image,
|
|
int src_x, int src_y, int src_w, int src_h,
|
|
int dst_x, int dst_y, int dst_w, int dst_h,
|
|
int smooth EINA_UNUSED,
|
|
Eina_Bool do_async EINA_UNUSED)
|
|
{
|
|
struct Filter_Blend_Draw_Context *dc = context;
|
|
RGBA_Image *src = image;
|
|
RGBA_Image *dst = surface;
|
|
DATA32* srcdata = src->image.data;
|
|
DATA32* dstdata = dst->image.data;
|
|
RGBA_Gfx_Func func;
|
|
int y, sw, dw;
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((src_w == dst_w) && (src_h == dst_h), EINA_FALSE);
|
|
|
|
func = evas_common_gfx_func_composite_pixel_span_get(src->cache_entry.flags.alpha, src->cache_entry.flags.alpha_sparse, dst->cache_entry.flags.alpha, 1, dc->render_op);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
|
|
|
|
sw = src->cache_entry.w;
|
|
dw = dst->cache_entry.w;
|
|
|
|
srcdata += src_y * sw;
|
|
dstdata += dst_y * dw;
|
|
for (y = src_h; y; y--)
|
|
{
|
|
func(srcdata + src_x, NULL, dc->color, dstdata + dst_x, src_w);
|
|
srcdata += sw;
|
|
dstdata += dw;
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_filter_blend_cpu_alpha(Evas_Filter_Command *cmd)
|
|
{
|
|
return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_alpha2alpha);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_filter_blend_cpu_mask_rgba(Evas_Filter_Command *cmd)
|
|
{
|
|
return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_alpha2rgba);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_filter_blend_cpu_rgba2alpha(Evas_Filter_Command *cmd)
|
|
{
|
|
return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_rgba2alpha);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_filter_blend_cpu_rgba(Evas_Filter_Command *cmd)
|
|
{
|
|
RGBA_Image *in, *out;
|
|
RGBA_Draw_Context *drawctx;
|
|
int sw, sh, dx, dy, dw, dh, sx, sy;
|
|
Eina_Bool ret;
|
|
|
|
in = cmd->input->backing;
|
|
out = cmd->output->backing;
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(in, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(out, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(in->image.data, EINA_FALSE);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(out->image.data, EINA_FALSE);
|
|
|
|
if (cmd->ctx->gl_engine)
|
|
return _filter_blend_cpu_generic_do(cmd, _image_draw_cpu_rgba2rgba);
|
|
|
|
sx = 0;
|
|
sy = 0;
|
|
sw = in->cache_entry.w;
|
|
sh = in->cache_entry.h;
|
|
|
|
dx = cmd->draw.ox;
|
|
dy = cmd->draw.oy;
|
|
dw = out->cache_entry.w;
|
|
dh = out->cache_entry.h;
|
|
|
|
if ((dw <= 0) || (dh <= 0) || (sw <= 0) || (sh <= 0))
|
|
return EINA_TRUE;
|
|
|
|
drawctx = cmd->ENFN->context_new(cmd->ENDT);
|
|
cmd->ENFN->context_color_set(cmd->ENDT, drawctx, cmd->draw.R, cmd->draw.G,
|
|
cmd->draw.B, cmd->draw.A);
|
|
cmd->ENFN->context_render_op_set(cmd->ENDT, drawctx, cmd->draw.render_op);
|
|
|
|
if (cmd->draw.clip_use)
|
|
{
|
|
cmd->ENFN->context_clip_set(cmd->ENDT, drawctx,
|
|
cmd->draw.clip.x, cmd->draw.clip.y,
|
|
cmd->draw.clip.w, cmd->draw.clip.h);
|
|
cmd->ENFN->context_clip_clip(cmd->ENDT, drawctx, 0, 0,
|
|
out->cache_entry.w, out->cache_entry.h);
|
|
}
|
|
else
|
|
{
|
|
cmd->ENFN->context_clip_set(cmd->ENDT, drawctx, 0, 0,
|
|
out->cache_entry.w, out->cache_entry.h);
|
|
}
|
|
|
|
ret = _mapped_blend(cmd->ENDT, drawctx, in, out, cmd->draw.fillmode,
|
|
sx, sy, sw, sh, dx, dy, dw, dh,
|
|
cmd->ENFN->image_draw);
|
|
|
|
cmd->ENFN->context_free(cmd->ENDT, drawctx);
|
|
return ret;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_mapped_blend(void *data, void *drawctx,
|
|
void *in, void *out,
|
|
Evas_Filter_Fill_Mode fillmode,
|
|
int sx, int sy,
|
|
int sw, int sh,
|
|
int dx, int dy,
|
|
int dw, int dh,
|
|
image_draw_func image_draw)
|
|
{
|
|
int right = 0, bottom = 0, left = 0, top = 0;
|
|
int row, col, rows, cols;
|
|
Eina_Bool ret = EINA_TRUE;
|
|
|
|
EINA_SAFETY_ON_FALSE_RETURN_VAL((sx == 0) && (sy == 0), EINA_FALSE);
|
|
|
|
if (fillmode == EVAS_FILTER_FILL_MODE_NONE)
|
|
{
|
|
_clip_to_target(&sx, &sy, sw, sh, dx, dy, dw, dh, &dx, &dy, &rows, &cols);
|
|
DBG("blend: %d,%d,%d,%d --> %d,%d,%d,%d (from %dx%d to %dx%d +%d,%d)",
|
|
0, 0, sw, sh, dx, dy, cols, rows, sw, sh, dw, dh, dx, dy);
|
|
|
|
image_draw(data, drawctx, out, in,
|
|
sx, sy, cols, rows, // src
|
|
dx, dy, cols, rows, // dst
|
|
EINA_TRUE, // smooth
|
|
EINA_FALSE); // Not async
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_X)
|
|
{
|
|
if (dx > 0) left = dx % sw;
|
|
else if (dx < 0) left = sw + (dx % sw);
|
|
cols = (dw /*- left*/) / sw;
|
|
if (left > 0)
|
|
right = dw - (sw * (cols - 1)) - left;
|
|
else
|
|
right = dw - (sw * cols);
|
|
dx = 0;
|
|
}
|
|
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
|
|
{
|
|
cols = 0;
|
|
dx = 0;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Probably wrong if dx != 0
|
|
cols = 0;
|
|
dw -= dx;
|
|
}
|
|
|
|
if (fillmode & EVAS_FILTER_FILL_MODE_REPEAT_Y)
|
|
{
|
|
if (dy > 0) top = dy % sh;
|
|
else if (dy < 0) top = sh + (dy % sh);
|
|
rows = (dh /*- top*/) / sh;
|
|
if (top > 0)
|
|
bottom = dh - (sh * (rows - 1)) - top;
|
|
else
|
|
bottom = dh - (sh * rows);
|
|
dy = 0;
|
|
}
|
|
else if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
|
|
{
|
|
rows = 0;
|
|
dy = 0;
|
|
}
|
|
else
|
|
{
|
|
// FIXME: Probably wrong if dy != 0
|
|
rows = 0;
|
|
dh -= dy;
|
|
}
|
|
|
|
if (top > 0) row = -1;
|
|
else row = 0;
|
|
for (; row <= rows; row++)
|
|
{
|
|
int src_x, src_y, src_w, src_h;
|
|
int dst_x, dst_y, dst_w, dst_h;
|
|
|
|
if (row == -1 && top > 0)
|
|
{
|
|
// repeat only
|
|
src_h = top;
|
|
src_y = sh - top;
|
|
dst_y = dy;
|
|
dst_h = src_h;
|
|
}
|
|
else if (row == rows && bottom > 0)
|
|
{
|
|
// repeat only
|
|
src_h = bottom;
|
|
src_y = 0;
|
|
dst_y = top + dy + row * sh;
|
|
dst_h = src_h;
|
|
}
|
|
else
|
|
{
|
|
src_y = 0;
|
|
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_Y)
|
|
{
|
|
src_h = sh;
|
|
dst_h = dh;
|
|
dst_y = 0;
|
|
}
|
|
else
|
|
{
|
|
dst_y = top + dy + row * sh;
|
|
src_h = MIN(dh - dst_y, sh);
|
|
dst_h = src_h;
|
|
}
|
|
}
|
|
if (src_h <= 0 || dst_h <= 0) break;
|
|
|
|
if (left > 0) col = -1;
|
|
else col = 0;
|
|
for (; col <= cols; col++)
|
|
{
|
|
if (col == -1 && left > 0)
|
|
{
|
|
// repeat only
|
|
src_w = left;
|
|
src_x = sw - left;
|
|
dst_x = dx;
|
|
dst_w = src_w;
|
|
}
|
|
else if (col == cols && right > 0)
|
|
{
|
|
// repeat only
|
|
src_w = right;
|
|
src_x = 0;
|
|
dst_x = left + dx + col * sw;
|
|
dst_w = src_w;
|
|
}
|
|
else
|
|
{
|
|
src_x = 0;
|
|
if (fillmode & EVAS_FILTER_FILL_MODE_STRETCH_X)
|
|
{
|
|
src_w = sw;
|
|
dst_w = dw;
|
|
dst_x = 0;
|
|
}
|
|
else
|
|
{
|
|
dst_x = left + dx + col * sw;
|
|
src_w = MIN(dw - dst_x, sw);
|
|
dst_w = src_w;
|
|
}
|
|
}
|
|
if (src_w <= 0 || dst_w <= 0) break;
|
|
|
|
DBG("blend: [%d,%d] %d,%d,%dx%d --> %d,%d,%dx%d "
|
|
"(src %dx%d, dst %dx%d)",
|
|
col, row, src_x, src_y, src_w, src_h,
|
|
dst_x, dst_y, dst_w, dst_h,
|
|
sw, sh, dw, dh);
|
|
image_draw(data, drawctx, out, in,
|
|
src_x, src_y, src_w, src_h,
|
|
dst_x, dst_y, dst_w, dst_h,
|
|
EINA_TRUE, EINA_FALSE);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Evas_Filter_Apply_Func
|
|
evas_filter_blend_cpu_func_get(Evas_Filter_Command *cmd)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
|
|
|
|
if (!cmd->ctx->gl_engine || !cmd->output->glimage || cmd->output->backing)
|
|
{
|
|
if (cmd->input->alpha_only)
|
|
{
|
|
if (cmd->output->alpha_only)
|
|
return _filter_blend_cpu_alpha;
|
|
else
|
|
return _filter_blend_cpu_mask_rgba;
|
|
}
|
|
else
|
|
{
|
|
if (cmd->output->alpha_only)
|
|
return _filter_blend_cpu_rgba2alpha;
|
|
else
|
|
return _filter_blend_cpu_rgba;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CRI("Can't render to GL image for the moment!");
|
|
//return _filter_blend_opengl_generic;
|
|
return NULL;
|
|
}
|
|
}
|