Evas filters: Implement displacement maps

Displacement maps are a simple but powerful tool to move pixels
around in a buffer, based on a displacement map (image).

Currently, various modes are implemented:
- X, Y or XY displacement
- Alpha map or RGBA map where R and G only are used

An intensity parameter is given as well.

Some of these might not be useful, we can just strip them off when
the final API is decided.
This commit is contained in:
Jean-Philippe Andre 2013-12-09 16:43:52 +09:00
parent 6458df7a65
commit 40c7f90bdc
1 changed files with 381 additions and 0 deletions

View File

@ -0,0 +1,381 @@
#include "evas_filter.h"
#include "evas_filter_private.h"
static void
_filter_displace_cpu_alpha_do(int w, int h, int map_w, int map_h, int map_step,
int dx, int dy, int intensity,
DATA8 *dst, DATA8 *src, DATA8 *map_start,
Eina_Bool displace_x, Eina_Bool displace_y,
Eina_Bool stretch, Eina_Bool smooth)
{
int x, y, map_x, map_y;
const int map_stride = map_w * map_step;
DATA8 *map;
for (y = 0, map_y = 0; y < h; y++, map_y++)
{
if (map_y >= map_h) map_y = 0;
map = map_start + (map_y * map_stride);
for (x = 0, map_x = 0; x < w; x++, dst++, src++, map_x++)
{
int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
Eina_Bool out = 0;
if (map_x >= map_w)
{
map_x = 0;
map = map_start + (map_y * map_stride);
}
else map += map_step;
if (displace_x)
{
val = ((int) map[dx] - 128) * intensity;
offx = val >> 7;
offx_dec = val & 0x7f;
if ((x + offx) < 0) { offx = -x; out = 1; }
if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }
}
if (displace_y)
{
val = ((int) map[dy] - 128) * intensity;
offy = val >> 7;
offy_dec = val & 0x7f;
if ((y + offy) < 0) { offy = -y; out = 1; }
if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }
}
if (out && !stretch)
*dst = 0;
else
{
if (!smooth)
*dst = src[offx + offy * w];
else if (displace_x && displace_y)
{
val = src[offx + offy * w] * (128 - offx_dec) * (128 - offy_dec);
val += src[offx + 1 + offy * w] * offx_dec * (128 - offy_dec);
val += src[offx + (offy + 1) * w] * (128 - offx_dec) * offy_dec;
val += src[offx + 1 + (offy + 1) * w] * offx_dec * offy_dec;
*dst = val >> 14; // <=> *dst = val / (128 * 128)
}
else
{
if (displace_x)
{
val = (int) src[offx + offy * w] * (128 - offx_dec);
val += (int) src[offx + 1 + offy * w] * offx_dec;
}
else
{
val = (int) src[offx + offy * w] * (128 - offy_dec);
val += (int) src[offx + (offy + 1) * w] * offy_dec;
}
*dst = val >> 7; // <=> *dst = val / 128
}
}
}
}
}
static void
_filter_displace_cpu_rgba_do(int w, int h, int map_w, int map_h, int map_step,
int dx, int dy, int intensity,
DATA8 *map_start, DATA32 *src, DATA32 *dst,
Eina_Bool displace_x, Eina_Bool displace_y,
Eina_Bool stretch, Eina_Bool smooth)
{
int x, y, map_x, map_y;
const int map_stride = map_step * map_w;
DATA8 *map;
for (y = 0, map_y = 0; y < h; y++, map_y++)
{
if (map_y >= map_h) map_y = 0;
map = map_start + (map_y * map_stride);
for (x = 0, map_x = 0; x < w; x++, dst++, src++, map_x++)
{
int offx = 0, offy = 0, offx_dec = 0, offy_dec = 0, val = 0;
Eina_Bool out = 0;
if (map_x >= map_w)
{
map_x = 0;
map = map_start + (map_y * map_stride);
}
else map += map_step;
if (displace_x)
{
val = ((int) map[dx] - 128) * intensity;
offx = val >> 7;
offx_dec = val & 0x7f;
if ((x + offx) < 0) { offx = -x; out = 1; }
if ((x + offx + 1) >= w) { offx = w - x - 2; out = 1; }
}
if (displace_y)
{
val = ((int) map[dy] - 128) * intensity;
offy = val >> 7;
offy_dec = val & 0x7f;
if ((y + offy) < 0) { offy = -y; out = 1; }
if ((y + offy + 1) >= h) { offy = h - y - 2; out = 1; }
}
if (out && !stretch)
*dst = A_VAL(src + offx + offy * w) << (ALPHA * 8);
else if (!smooth)
*dst = src[offx + offy * w];
else if (displace_x && displace_y)
{
int R, G, B, A;
DATA32 s00, s01, s10, s11; // indexes represent x,y
int mul00, mul01, mul10, mul11;
mul00 = (128 - offx_dec) * (128 * offy_dec);
mul01 = (128 - offx_dec) * offy_dec;
mul10 = offx_dec * (128 - offy_dec);
mul11 = offx_dec * offy_dec;
s00 = src[offx + offy * w];
s01 = src[offx + (offy + 1) * w];
s10 = src[offx + 1 + offy * w];
s11 = src[offx + 1 + (offy + 1) * w];
A = (ALPHA_OF(s00) * mul00) + (ALPHA_OF(s10) * mul10)
+ (ALPHA_OF(s01) * mul01) + (ALPHA_OF(s11) * mul11);
R = (RED_OF(s00) * mul00) + (RED_OF(s10) * mul10)
+ (RED_OF(s01) * mul01) + (RED_OF(s11) * mul11);
G = (GREEN_OF(s00) * mul00) + (GREEN_OF(s10) * mul10)
+ (GREEN_OF(s01) * mul01) + (GREEN_OF(s11) * mul11);
B = (BLUE_OF(s00) * mul00) + (BLUE_OF(s10) * mul10)
+ (BLUE_OF(s01) * mul01) + (BLUE_OF(s11) * mul11);
R >>= 14;
G >>= 14;
B >>= 14;
*dst = ARGB_JOIN(A, R, G, B);
}
else if (displace_x)
{
int R, G, B, A;
DATA32 s00, s10;
int mul00, mul10;
mul00 = (128 - offx_dec);
mul10 = offx_dec;
s00 = src[offx + offy * w];
s10 = src[offx + 1 + offy * w];
A = (ALPHA_OF(s00) * mul00) + (ALPHA_OF(s10) * mul10);
R = (RED_OF(s00) * mul00) + (RED_OF(s10) * mul10);
G = (GREEN_OF(s00) * mul00) + (GREEN_OF(s10) * mul10);
B = (BLUE_OF(s00) * mul00) + (BLUE_OF(s10) * mul10);
A >>= 7;
R >>= 7;
G >>= 7;
B >>= 7;
*dst = ARGB_JOIN(A, R, G, B);
}
else
{
int R, G, B, A;
DATA32 s00, s01;
int mul00, mul01;
mul00 = (128 * offy_dec);
mul01 = offy_dec;
s00 = src[offx + offy * w];
s01 = src[offx + (offy + 1)* w];
A = (ALPHA_OF(s00) * mul00) + (ALPHA_OF(s01) * mul01);
R = (RED_OF(s00) * mul00) + (RED_OF(s01) * mul01);
G = (GREEN_OF(s00) * mul00) + (GREEN_OF(s01) * mul01);
B = (BLUE_OF(s00) * mul00) + (BLUE_OF(s01) * mul01);
A >>= 7;
R >>= 7;
G >>= 7;
B >>= 7;
*dst = ARGB_JOIN(A, R, G, B);
}
}
}
}
/**
* Apply distortion map on alpha image
* input: alpha
* output: alpha
* map: alpha or rgba
* direction: X, Y or XY
*/
static Eina_Bool
_filter_displace_cpu_alpha(Evas_Filter_Command *cmd)
{
int w, h, map_w, map_h, intensity, map_step, dx = 0, dy = 0;
DATA8 *dst, *src, *map_start;
Eina_Bool displace_x, displace_y, stretch, smooth;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->mask.data;
map_start = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->mask.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
displace_x = cmd->displacement.flags & EVAS_FILTER_DISPLACE_X;
displace_y = cmd->displacement.flags & EVAS_FILTER_DISPLACE_Y;
stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
map_w = cmd->mask->w;
map_h = cmd->mask->h;
intensity = cmd->displacement.intensity;
if (cmd->mask->alpha_only)
map_step = sizeof(DATA8);
else
{
map_step = sizeof(DATA32);
if (cmd->displacement.flags & EVAS_FILTER_DISPLACE_RG)
{
dx = RED;
dy = GREEN;
}
else dx = dy = ALPHA;
}
_filter_displace_cpu_alpha_do(w, h, map_w, map_h, map_step, dx, dy,
intensity, dst, src, map_start,
displace_x, displace_y, stretch, smooth);
return EINA_TRUE;
}
/**
* Apply distortion map on rgba image
* input: rgba
* output: rgba
* map: alpha or rgba
* direction: X, Y or XY
*/
static Eina_Bool
_filter_displace_cpu_rgba(Evas_Filter_Command *cmd)
{
int w, h, map_w, map_h, intensity, map_step, dx, dy;
DATA32 *dst, *src;
DATA8 *map_start;
Eina_Bool displace_x, displace_y, stretch, smooth;
w = cmd->input->w;
h = cmd->input->h;
EINA_SAFETY_ON_FALSE_RETURN_VAL(w == cmd->output->w, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(h == cmd->output->h, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask->backing, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output->backing, EINA_FALSE);
src = ((RGBA_Image *) cmd->input->backing)->image.data;
map_start = ((RGBA_Image *) cmd->mask->backing)->mask.data;
dst = ((RGBA_Image *) cmd->output->backing)->image.data;
EINA_SAFETY_ON_NULL_RETURN_VAL(src, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(map_start, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(dst, EINA_FALSE);
displace_x = cmd->displacement.flags & EVAS_FILTER_DISPLACE_X;
displace_y = cmd->displacement.flags & EVAS_FILTER_DISPLACE_Y;
stretch = cmd->displacement.flags & EVAS_FILTER_DISPLACE_STRETCH;
smooth = cmd->displacement.flags & EVAS_FILTER_DISPLACE_LINEAR;
map_w = cmd->mask->w;
map_h = cmd->mask->h;
intensity = cmd->displacement.intensity;
if (!displace_x && !displace_y)
{
WRN("Invalid displacement flags! Defaulting to XY displacement.");
displace_x = displace_y = EINA_TRUE;
}
if (cmd->mask->alpha_only)
{
map_step = sizeof(DATA8);
dx = dy = 0;
}
else
{
map_step = sizeof(DATA32);
if (cmd->displacement.flags & EVAS_FILTER_DISPLACE_RG)
{
dx = RED;
dy = GREEN;
}
else dx = dy = ALPHA;
}
_filter_displace_cpu_rgba_do(w, h, map_w, map_h, map_step, dx, dy,
intensity, map_start, src, dst,
displace_x, displace_y, stretch, smooth);
return EINA_TRUE;
}
Evas_Filter_Apply_Func
evas_filter_displace_cpu_func_get(Evas_Filter_Command *cmd)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->input, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->output, NULL);
EINA_SAFETY_ON_NULL_RETURN_VAL(cmd->mask, NULL);
if (cmd->input->alpha_only != cmd->output->alpha_only)
{
CRIT("Invalid color formats");
return NULL;
}
if (cmd->input->alpha_only)
{
if ((cmd->displacement.flags & EVAS_FILTER_DISPLACE_RG)
&& cmd->mask->alpha_only)
{
goto invalid_flags;
}
return _filter_displace_cpu_alpha;
}
else
{
if ((cmd->displacement.flags & EVAS_FILTER_DISPLACE_RG)
&& cmd->mask->alpha_only)
{
goto invalid_flags;
}
return _filter_displace_cpu_rgba;
}
invalid_flags:
ERR("Incompatible flags (0x%02x) and data (input %s, output %s, map %s)",
(cmd->displacement.flags & EVAS_FILTER_DISPLACE_BITMASK),
cmd->input->alpha_only ? "alpha" : "rgba",
cmd->output->alpha_only ? "alpha" : "rgba",
cmd->mask->alpha_only ? "alpha" : "rgba");
return NULL;
}