efl/src/lib/evas/canvas/evas_filter.c

1429 lines
36 KiB
C

/*
* Filter implementation for evas
*/
#if 0 // filtering disabled
typedef enum
{
/** Apply any filter effects to this object (Default) */
EVAS_FILTER_MODE_OBJECT,
/** Filter all objects beneath this object on the canvas */
EVAS_FILTER_MODE_BELOW,
} Evas_Filter_Mode;
typedef enum
{
/** No filter: Default */
EVAS_FILTER_NONE,
/** A blur filter. Params are quality (float), and radius (int). */
EVAS_FILTER_BLUR,
/** Negates the colors of an image. Also called solarize */
EVAS_FILTER_INVERT,
EVAS_FILTER_SOLARIZE = EVAS_FILTER_INVERT,
/** Makes a sepia version of the image. */
EVAS_FILTER_SEPIA,
/** Makes a greyscale version of the image. Params are 'red',
* 'green', 'blue' (all floats) which must add to 1. The defaults are
* 0.3, 0.59 and 0.11 which approximates human vision. Setting 'all'
* sets rgb to the same value. */
EVAS_FILTER_GREYSCALE,
EVAS_FILTER_GRAYSCALE = EVAS_FILTER_GREYSCALE,
/** Brighten (or darken) image. Param 'adjust' float (-1.0 to 1.0)
* amount to adjust. */
EVAS_FILTER_BRIGHTNESS,
/** Enhance contrast on image. Param 'adjust' float (-1.0 to 1.0)
* amount to adjust. */
EVAS_FILTER_CONTRAST,
EVAS_FILTER_LAST = EVAS_FILTER_CONTRAST
} Evas_Filter;
/**
* Set the filter mode for an object.
*
* There are two valid filtering modes currently:
* - EVAS_FILTER_MODE_OBJECT: which applies the filter to the object itself
* - EVAS_FILTER_MODE_BELOW: which makes the object invisible and filters
* what is below the object.
*
* The default filter mode is EVAS_FILTER_MODE_OBJECT.
*
* @param o Object to set filter mode on.
* @param mode Mode to set.
* @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
*/
EAPI Eina_Bool evas_object_filter_mode_set (Evas_Object *o, Evas_Filter_Mode mode);
/**
* Get the current filtering mode for an object.
*
* By default all objects are in object filtering mode, even if no filter is
* set.
*
* @param o Object to get filter mode of.
* @return Filter mode (default EVAS_FILTER_MODE_OBJECT)
*/
EAPI Evas_Filter_Mode evas_object_filter_mode_get (Evas_Object *o);
/**
* Set the current filter type.
*
* This sets the filter type, whether a blur, color filter or some other type
* of filter. This is normally the only filter call necessary, although some
* filters require additional parameters.
*
* If the object has a filter already, and existing parameters will be
* cleared.
*
* Setting the blur to EVAS_FILTER_NONE removes any filter.
*
* @param o Object to set the filter on.
* @param filter Filter to set.
* @return @c EINA_TRUE on success.
*/
EAPI Eina_Bool evas_object_filter_set (Evas_Object *o, Evas_Filter filter);
/**
* Get the current filter.
*
* @param o Object to get filter of.
* @return The filter if set, or EVAS_FILTER_NONE.
*/
EAPI Evas_Filter evas_object_filter_get (Evas_Object *o);
/**
* Set an integer parameter of a filter.
*
* This sets an integer parameter of a filter, if such parameter is known to
* the filter. Note that some parameters may actually set multiple fields.
* The individual filters define the specific parameters available.
*
* It should be noted that filter parameters are lost after the filter type
* changes, so set the filter type, then the parameters.
*
* @param o Object to set parameter on.
* @param param Name of parameter to set.
* @param val Value to set.
* @return @c EINA_TRUE if at least one parameter was set, @c EINA_FALSE
* otherwise.
*/
EAPI Eina_Bool evas_object_filter_param_int_set (Evas_Object *o, const char *param, int val);
/**
* Get an integer value parameter from a filter.
*
* Gets the first matching parameter for a filter. Note there is no way to
* later fields if they do not have their own accessor name.
*
* Also note that there is no way to tell the difference between a -1 as a
* value, and the error code. Ask your filter writer to use a different
* range.
*
* @param o The object.
* @Param param Name of the parameter to get.
* @return The value, or -1 on error.
*/
EAPI int evas_object_filter_param_int_get (Evas_Object *o, const char *param);
/**
* Set a string parameter on a filter
*
* Currently unimplemented as no filters use this yet
*/
EAPI Eina_Bool evas_object_filter_param_str_set (Evas_Object *o, const char *param, const char *val);
/**
* Get a string parameter from a filter
*
* Currently unimplemented as no filters use this yet
*/
EAPI const char *evas_object_filter_param_str_get (Evas_Object *o, const char *param);
/**
* Set an object parameter on a filter
*
* Currently unimplemented as no filters use this yet
*/
EAPI Eina_Bool evas_object_filter_param_obj_set (Evas_Object *o, const char *param, Evas_Object *val);
/**
* get an object parameter from a filter
*
* Currently unimplemented as no filters use this yet
*/
EAPI Evas_Object *evas_object_filter_param_obj_get (Evas_Object *o, const char *param);
/**
* Set a float parameter of a filter.
*
* This is the same as evas_object_filter_param_int_set(), but for floating
* point values.
*
* @param o Object to set value on.
* @param param Name of the parameter to set.
* @param @c EINA_TRUE if at least one parameter was set, @c EINA_FALSE
* otherwise.
*/
EAPI Eina_Bool evas_object_filter_param_float_set(Evas_Object *o, const char *param, double val);
/**
* Get a float parameter of a filter.
*
* This is the same as evas_object_filter_param_int_get(), but for floating
* point values.
*
* @param o Object to set value on.
* @param param Name of the parameter to set.
* @return The value, or -1 on error.
*/
EAPI double evas_object_filter_param_float_get(Evas_Object *o, const char *param);
#include <stddef.h> // offsetof
#include "evas_common.h"
#include "evas_private.h"
#include <assert.h>
/* disable neon - even after fixes:
* Error: ARM register expected -- vdup.u32 q14,$0xff000000'
* not going to fix now
#ifdef BUILD_NEON
# define BUILD_NEON0 1
#else
# define BUILD_NEON0 0
#endif
*/
#define BUILD_NEON0 0
typedef struct Evas_Filter_Info_Blur
{
double quality;
int radius;
} Evas_Filter_Info_Blur;
typedef struct Evas_Filter_Info_GreyScale
{
double r,g,b;
} Evas_Filter_Info_GreyScale;
typedef struct Evas_Filter_Info_Brightness
{
double adjust;
} Evas_Filter_Info_Brightness;
typedef struct Evas_Filter_Info_Contrast
{
double adjust;
} Evas_Filter_Info_Contrast;
typedef int (*Filter_Size_FN)(Evas_Filter_Info *,int,int,int*,int*,Eina_Bool);
typedef uint8_t *(*Key_FN)(const Evas_Filter_Info *, uint32_t *);
struct fieldinfo
{
const char *field;
int type;
size_t offset;
};
struct filterinfo
{
Evas_Software_Filter_Fn filter;
const size_t datasize;
Filter_Size_FN sizefn;
Key_FN keyfn;
Eina_Bool alwaysalpha;
};
enum
{
TYPE_INT,
TYPE_FLOAT
};
static int blur_size_get(Evas_Filter_Info*, int, int, int *, int *, Eina_Bool);
static uint8_t *gaussian_key_get(const Evas_Filter_Info *, uint32_t *);
static Eina_Bool gaussian_filter(Evas_Filter_Info *, RGBA_Image*, RGBA_Image*);
static Eina_Bool negation_filter(Evas_Filter_Info *, RGBA_Image*, RGBA_Image*);
static Eina_Bool sepia_filter(Evas_Filter_Info *, RGBA_Image*, RGBA_Image*);
static Eina_Bool greyscale_filter(Evas_Filter_Info*, RGBA_Image*, RGBA_Image*);
static Eina_Bool brightness_filter(Evas_Filter_Info*, RGBA_Image*, RGBA_Image*);
static Eina_Bool contrast_filter(Evas_Filter_Info *, RGBA_Image*, RGBA_Image*);
struct filterinfo filterinfo[] =
{
/* None */
{ NULL, 0, NULL, NULL, EINA_FALSE},
/* Blur */
{ gaussian_filter, sizeof(Evas_Filter_Info_Blur), blur_size_get, gaussian_key_get, EINA_TRUE },
/* Negation */
{ negation_filter, 0, NULL, NULL, EINA_FALSE },
/* Sepia */
{ sepia_filter, 0, NULL, NULL, EINA_FALSE },
/* Greyscale */
{ greyscale_filter, sizeof(Evas_Filter_Info_GreyScale), NULL, NULL, EINA_FALSE },
/* Brightness */
{ brightness_filter, sizeof(Evas_Filter_Info_Brightness), NULL, NULL, EINA_FALSE },
/* Contrast */
{ contrast_filter, sizeof(Evas_Filter_Info_Contrast), NULL, NULL, EINA_FALSE},
};
static struct fieldinfo blurfields[] =
{
{ "quality", TYPE_FLOAT, offsetof(Evas_Filter_Info_Blur, quality) },
{ "radius", TYPE_INT, offsetof(Evas_Filter_Info_Blur, radius) },
{ NULL, 0, 0 },
};
static struct fieldinfo greyfields[] =
{
{ "red", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, r) },
{ "green", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, g) },
{ "blue", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, b) },
{ "all", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, r) },
{ "all", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, g) },
{ "all", TYPE_FLOAT, offsetof(Evas_Filter_Info_GreyScale, b) },
{ NULL, 0, 0 },
};
static struct fieldinfo brightnessfields[] =
{
{ "adjust", TYPE_FLOAT, offsetof(Evas_Filter_Info_Brightness, adjust) },
{ NULL, 0, 0 },
};
static struct fieldinfo contrastfields[] =
{
{ "adjust", TYPE_FLOAT, offsetof(Evas_Filter_Info_Contrast, adjust) },
{ NULL, 0, 0 },
};
static struct fieldinfo *filterfields[] =
{
NULL,
blurfields,
NULL,
NULL,
greyfields,
brightnessfields,
contrastfields,
};
static Evas_Filter_Info *filter_alloc(Evas_Object *o);
EAPI Eina_Bool
evas_object_filter_mode_set(Evas_Object *o, Evas_Filter_Mode mode)
{
Evas_Filter_Info *info;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EINA_FALSE;
MAGIC_CHECK_END();
if ((mode != EVAS_FILTER_MODE_OBJECT) && (mode != EVAS_FILTER_MODE_BELOW))
return EINA_FALSE;
if (!o->filter)
{
filter_alloc(o);
}
if (!o->filter) return EINA_FALSE;
info = o->filter;
if (info->mode == mode) return EINA_TRUE; /* easy case */
info->mode = mode;
info->dirty = 1;
return EINA_TRUE;
}
EAPI Evas_Filter_Mode
evas_object_filter_mode_get(Evas_Object *o)
{
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EVAS_FILTER_MODE_OBJECT;
MAGIC_CHECK_END();
if (!o->filter) return EVAS_FILTER_MODE_OBJECT;
return o->filter->mode;
}
EAPI Eina_Bool
evas_object_filter_set(Evas_Object *o, Evas_Filter filter)
{
Evas_Filter_Info *info;
struct filterinfo *finfo;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EINA_FALSE;
MAGIC_CHECK_END();
/* force filter to be signed: else gcc complains, but enums may always be
* signed */
if (((int)filter < (int)EVAS_FILTER_NONE) || (filter > EVAS_FILTER_LAST))
return EINA_FALSE;
/* Don't alloc on no-op */
if (!o-filter && filter == EVAS_FILTER_NONE) return EINA_TRUE;
if (!o->filter) filter_alloc(o);
if (!o->filter) return EINA_FALSE;
info = o->filter;
if (info->filter == filter) return EINA_TRUE;
finfo = filterinfo + filter;
info->filter = filter;
info->dirty = 1;
if (info->data)
{
if (info->data_free)
info->data_free(info->data);
else
free(info->data);
}
info->datalen = finfo->datasize;
if (finfo->datasize)
{
info->data = calloc(1, finfo->datasize);
if (!info->data)
{
o->filter = EVAS_FILTER_NONE;
return EINA_FALSE;
}
}
else
info->data = NULL;
info->data_free = NULL;
return EINA_TRUE;
}
EAPI Evas_Filter
evas_object_filter_get(Evas_Object *o)
{
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EVAS_FILTER_NONE;
MAGIC_CHECK_END();
if (!o->filter) return EVAS_FILTER_NONE;
return o->filter->filter;
}
EAPI Eina_Bool
evas_object_filter_param_int_set(Evas_Object *o, const char *param, int val)
{
char *data;
const struct fieldinfo *fields;
Eina_Bool found;
int i;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EINA_FALSE;
MAGIC_CHECK_END();
if ((!o->filter) || (!o->filter->data)) return EINA_FALSE;
fields = filterfields[o->filter->filter];
data = o->filter->data;
found = EINA_FALSE;
for (i = 0; fields[i].field; i++)
{
if (!strcmp(fields[i].field, param))
{
if (fields[i].type != TYPE_INT) continue;
*(int *)(data + fields[i].offset) = val;
o->filter->dirty = 1;
evas_object_change(o);
found = EINA_TRUE;
}
}
return found;
}
EAPI int
evas_object_filter_param_int_get(Evas_Object *o, const char *param)
{
char *data;
const struct fieldinfo *fields;
int val;
int i;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return -1;
MAGIC_CHECK_END();
if ((!o->filter) || (!o->filter->data)) return -1;
fields = filterfields[o->filter->filter];
if (!fields) return -1;
data = o->filter->data;
for (i = 0; fields[i].field; i++)
{
if (!strcmp(fields[i].field, param))
{
if (fields[i].type != TYPE_INT) continue;
val = *(int *)(data + fields[i].offset);
return val;
}
}
return -1;
}
EAPI Eina_Bool
evas_object_filter_param_str_set(Evas_Object *o EINA_UNUSED,
const char *param EINA_UNUSED,
const char *val EINA_UNUSED)
{
return EINA_FALSE;
}
EAPI const char *
evas_object_filter_param_str_get(Evas_Object *o EINA_UNUSED,
const char *param EINA_UNUSED)
{
return NULL;
}
EAPI Eina_Bool
evas_object_filter_param_obj_set(Evas_Object *o EINA_UNUSED,
const char *param EINA_UNUSED,
Evas_Object *val EINA_UNUSED)
{
return EINA_FALSE;
}
EAPI Evas_Object *
evas_object_filter_param_obj_get(Evas_Object *o EINA_UNUSED,
const char *param EINA_UNUSED)
{
return NULL;
}
EAPI Eina_Bool
evas_object_filter_param_float_set(Evas_Object *o, const char *param,
double val)
{
char *data;
const struct fieldinfo *fields;
int i;
Eina_Bool rv;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return EINA_FALSE;
MAGIC_CHECK_END();
if ((!o->filter) || (!o->filter->data)) return EINA_FALSE;
rv = EINA_FALSE;
fields = filterfields[o->filter->filter];
if (!fields) return EINA_FALSE;
data = o->filter->data;
for (i = 0; fields[i].field; i++)
{
if (!strcmp(fields[i].field, param))
{
if (fields[i].type != TYPE_FLOAT) continue;
*(double *)(data + fields[i].offset) = val;
o->filter->dirty = 1;
o->changed = 1;
evas_object_change(o);
rv = EINA_TRUE;
}
}
return rv;
}
EAPI double
evas_object_filter_param_float_get(Evas_Object *o, const char *param)
{
char *data;
const struct fieldinfo *fields;
double val;
int i;
MAGIC_CHECK(o, Evas_Object, MAGIC_OBJ);
return -1;
MAGIC_CHECK_END();
if ((!o->filter) || (!o->filter->data)) return -1;
fields = filterfields[o->filter->filter];
if (!fields) return -1;
data = o->filter->data;
for (i = 0; fields[i].field; i++)
{
if (!strcmp(fields[i].field, param))
{
if (fields[i].type != TYPE_FLOAT) continue;
val = *(double *)(data + fields[i].offset);
return val;
}
}
return -1;
}
/*
* Internal call
*/
int
evas_filter_get_size(Evas_Filter_Info *info, int inw, int inh,
int *outw, int *outh, Eina_Bool inv)
{
if (!info) return -1;
if ((!outw) && (!outh)) return 0;
if (filterinfo[info->filter].sizefn)
return filterinfo[info->filter].sizefn(info, inw, inh, outw, outh, inv);
if (outw) *outw = inw;
if (outh) *outh = inh;
return 0;
}
Eina_Bool
evas_filter_always_alpha(Evas_Filter_Info *info)
{
if (!info) return EINA_FALSE;
return filterinfo[info->filter].alwaysalpha;
}
/*
* Another internal call:
* Given a filterinfo, generate a unique key for it
*
* For simple filters, it's just the filter type.
* for more complex filters, it's the type, with it's params.
*
* Note management of the key data is up to the caller, that is it should
* probably be freed after use.
*
* Note the automatic fallback generation places the single byte at the end so
* the memcpy will be aligned. Micro-optimisations FTW!
*
* @param info Filter info to generate from
* @param len Length of the buffer returned.
* @return key Key buffer
*/
uint8_t *
evas_filter_key_get(const Evas_Filter_Info *info, uint32_t *lenp)
{
struct filterinfo *finfo;
uint8_t *key;
int len;
if (!info) return NULL;
finfo = filterinfo + info->filter;
if (finfo->keyfn) return finfo->keyfn(info, lenp);
len = 1 + finfo->datasize;
key = malloc(len);
if (!key) return NULL;
if (finfo->datasize) memcpy(key, info->data, finfo->datasize);
key[finfo->datasize] = info->filter;
if (lenp) *lenp = len;
return key;
}
Evas_Software_Filter_Fn
evas_filter_software_get(Evas_Filter_Info *info)
{
return filterinfo[info->filter].filter;
}
void
evas_filter_free(Evas_Object *o)
{
if (!o->filter) return;
if (o->filter->key) free(o->filter->key);
free(o->filter);
o->filter = NULL;
}
/*
* Private calls
*/
static Evas_Filter_Info *
filter_alloc(Evas_Object *o)
{
Evas_Filter_Info *info;
if (!o) return NULL;
info = calloc(1,sizeof(struct Evas_Filter_Info));
if (!info) return NULL;
info->dirty = 1;
info->filter = EVAS_FILTER_NONE;
info->mode = EVAS_FILTER_MODE_OBJECT;
info->datalen = 0;
o->filter = info;
return info;
}
static int
blur_size_get(Evas_Filter_Info *info, int inw, int inh, int *outw, int *outh,
Eina_Bool inv)
{
Evas_Filter_Info_Blur *blur = info->data;
if (inv)
{
if (outw) *outw = MAX(inw - (blur->radius * 2), 0);
if (outh) *outh = MAX(inh - (blur->radius * 2), 0);
}
else
{
if (outw) *outw = inw + (blur->radius * 2);
if (outh) *outh = inh + (blur->radius * 2);
}
return 0;
}
/*
* Generate a key for the Gaussian generator.
*
* The size is:
* - 1 byte for the type (blur)
* - 1 byte for the quality (0-1 -> 0-255)
* - 2 bytes for radius (max is 508 anyway)
*
* @param info Filter info
* @param len Length of the returned buffer
* @return new buffer
*/
static uint8_t *
gaussian_key_get(const Evas_Filter_Info *info, uint32_t *lenp)
{
struct Evas_Filter_Info_Blur *blur;
uint8_t *key;
if ((!info) || (!info->data)) return NULL;
blur = info->data;
if (lenp) *lenp = 4;
key = malloc(4);
if (!key) return NULL;
key[0] = EVAS_FILTER_BLUR;
key[1] = blur->quality * 255;
key[2] = blur->radius >> 8;
key[3] = blur->radius;
return key;
}
/**
* Software implementations
*/
#define all(OP, A, R, G, B, W, I) \
do { \
A OP A_VAL(I) * W; \
R OP R_VAL(I) * W; \
G OP G_VAL(I) * W; \
B OP B_VAL(I) * W; \
} while (0)
#define wavg(x,n) (((x) / (n)) & 0xff)
#define wavgd(x,n) ((uint32_t)((x) / (n)) & 0xff)
typedef int (*FilterH)(int, uint32_t *, int, uint32_t *);
typedef int (*FilterV)(int, uint32_t *, int, int, uint32_t *);
static int gaussian_filter_h(int rad, uint32_t *in, int w, uint32_t *out);
static int gaussian_filter_h64(int rad, uint32_t *in, int w, uint32_t *out);
static int gaussian_filter_hd(int rad, uint32_t *in, int w, uint32_t *out);
static int gaussian_filter_v(int rad, uint32_t *in, int h, int skip, uint32_t *out);
static int gaussian_filter_v64(int rad, uint32_t *in, int h, int skip, uint32_t *out);
static int gaussian_filter_vd(int rad, uint32_t *in, int h, int skip, uint32_t *out);
static const uint32_t *gaussian_row_get(int row, int *npoints, uint32_t *weight);
static const uint64_t *gaussian_row_get64(int row, int *npoints, uint64_t *weight);
static const double *gaussian_row_getd(int row, int *npoints, double *weight);
static Eina_Bool
gaussian_filter(Evas_Filter_Info *filter, RGBA_Image *src, RGBA_Image *dst)
{
int i;
uint32_t nw, nh;
uint32_t *in, *tmp, *out;
FilterV filter_v = gaussian_filter_v;
FilterH filter_h = gaussian_filter_h;
Evas_Filter_Info_Blur *blur;
int w, h;
blur = filter->data;
/* Use 64 bit version if we are going to overflow */
if (blur->radius > 508) /** too big for doubles: Bail out */
return EINA_FALSE;
else if (blur->radius > 28)
{
filter_v = gaussian_filter_vd;
filter_h = gaussian_filter_hd;
}
else if (blur->radius > 12)
{
filter_v = gaussian_filter_v64;
filter_h = gaussian_filter_h64;
}
w = src->cache_entry.w;
h = src->cache_entry.h;
in = src->image.data;
if (!in) return EINA_FALSE;
nw = w + (2 * blur->radius);
nh = h + (2 * blur->radius);
out = dst->image.data;
if (!out) return EINA_FALSE;
tmp = malloc(nw * h * sizeof(uint32_t));
for (i = 0; i < h; i++)
filter_h(blur->radius,in + (i * w), w, tmp + (i * nw));
for (i = 0; i < (int)nw; i++)
filter_v(blur->radius,tmp + i, h, nw, out + i);
free(tmp);
return EINA_TRUE;
}
/* Blur only horizontally */
static int
gaussian_filter_h(int rad, uint32_t *in, int w, uint32_t *out)
{
const uint32_t *points;
int npoints = 0;
uint32_t weight = 0;
int i = 0, k = 0;
uint32_t r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_get(rad * 2, &npoints, &weight);
for (i = -rad; i < (w + rad); i++)
{
r = g = b = a = 0;
for (k = -rad; k <= rad; k++)
{
if ((k + i) < 0) continue;
if ((k + i) >= w) continue;
all(+=, a, r, g, b, points[k + rad], in + k + i);
}
*(out) = ARGB_JOIN(wavg(a, weight),
wavg(r, weight),
wavg(g, weight),
wavg(b, weight));
out++;
}
return 0;
}
/* Blur only horizontally */
static int
gaussian_filter_hd(int rad, uint32_t *in, int w, uint32_t *out)
{
const double *points;
int npoints = 0;
double weight = 0.0;
int i = 0, k = 0;
double r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_getd(rad * 2, &npoints, &weight);
for (i = -rad; i < (w + rad); i++)
{
r = g = b = a = 0;
for (k = -rad; k <= rad; k++)
{
if ((k + i) < 0) continue;
if ((k + i) >= w) continue;
all(+=, a, r, g, b, points[k + rad], in + k + i);
}
*(out) = ARGB_JOIN(wavgd(a, weight),
wavgd(r, weight),
wavgd(g, weight),
wavgd(b, weight));
out++;
}
return 0;
}
/* Blur only horizontally */
static int
gaussian_filter_h64(int rad, uint32_t *in, int w, uint32_t *out)
{
const uint64_t *points;
int npoints = 0;
uint64_t weight = 0;
int i = 0, k = 0;
uint64_t r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_get64(rad * 2, &npoints, &weight);
for (i = -rad ; i < w + rad; i ++){
r = g = b = a = 0;
for (k = -rad ; k <= rad ; k ++){
if ((k + i) < 0) continue;
if ((k + i) >= w) continue;
all(+=, a, r, g, b, points[k + rad], in + k + i);
}
*(out) = ARGB_JOIN(wavg(a, weight),
wavg(r, weight),
wavg(g, weight),
wavg(b, weight));
out++;
}
return 0;
}
static int
gaussian_filter_v(int rad, uint32_t *in, int h, int skip, uint32_t *out)
{
const uint32_t *points;
int npoints = 0;
uint32_t weight = 0;
int i = 0, k = 0;
uint32_t r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_get(rad * 2, &npoints, &weight);
weight = 0;
for (i = 0; i < npoints; i++) weight += points[i];
for (i = -rad; i < (h + rad); i++)
{
r = g = b = a = 0;
for (k = -rad; k <= rad; k++)
{
if ((k + i) < 0) continue;
if ((k + i) >= h) continue;
all(+=, a, r, g, b, points[k + rad], in + (skip * (k + i)));
}
*(out) = ARGB_JOIN(wavg(a, weight),
wavg(r, weight),
wavg(g, weight),
wavg(b, weight));
out += skip;
}
return 0;
}
static int
gaussian_filter_v64(int rad, uint32_t *in, int h, int skip, uint32_t *out)
{
const uint64_t *points;
int npoints = 0;
uint64_t weight;
int i = 0, k = 0;
uint64_t r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_get64(rad * 2, &npoints, &weight);
weight = 0;
for (i = 0; i < npoints; i++) weight += points[i];
for (i = -rad; i < (h + rad); i++)
{
r = g = b = a = 0;
for (k = -rad ; k <= rad ; k++)
{
if ((k + i) < 0) continue;
if ((k + i) >= h) continue;
all(+=, a, r, g, b, points[k + rad], in + (skip * (k + i)));
}
*(out) = ARGB_JOIN(wavg(a, weight),
wavg(r, weight),
wavg(g, weight),
wavg(b, weight));
out += skip;
}
return 0;
}
static int
gaussian_filter_vd(int rad, uint32_t *in, int h, int skip, uint32_t *out)
{
const double *points;
int npoints = 0;
double weight = 0.0;
int i = 0, k = 0;
double r, g, b, a;
/* Get twice the radius: even rows have 1 element */
points = gaussian_row_getd(rad * 2, &npoints, &weight);
weight = 0;
for (i = 0 ; i < npoints ; i ++) weight += points[i];
for (i = -rad ; i < h + rad; i ++)
{
r = g = b = a = 0;
for (k = -rad ; k <= rad ; k ++)
{
if ((k + i) < 0) continue;
if ((k + i) >= h) continue;
all(+=, a, r, g, b, points[k + rad], in + (skip * (k + i)));
}
*(out) = ARGB_JOIN(wavgd(a, weight),
wavgd(r, weight),
wavgd(g, weight),
wavgd(b, weight));
out += skip;
}
return 0;
}
static const uint32_t *
gaussian_row_get(int row, int *npoints, uint32_t *weight)
{
static uint32_t *points = NULL;
static int last = -1;
static uint32_t lastweight = -1;
int c, k;
if (row < 0) return NULL;
if (npoints) *npoints = row + 1;
if (last == row)
{
if (weight) *weight = lastweight;
return points;
}
if (points) free(points);
points = malloc((row + 1) * sizeof(uint32_t));
if (!points)
{
last = -1;
return NULL;
}
last = row;
c = 1;
for (k = 0; k <= row; k++)
{
points[k] = c;
c = c * (row - k) / (k + 1);
}
for (k = 0, lastweight = 0; k <= row; k++) lastweight += points[k];
if (weight) *weight = lastweight;
return points;
}
static const uint64_t *
gaussian_row_get64(int row, int *npoints, uint64_t *weight)
{
static uint64_t *points = NULL;
static int last = -1;
static uint64_t lastweight = -1;
uint64_t c;
int k;
if (row < 0) return NULL;
if (npoints) *npoints = row + 1;
if (last == row)
{
if (weight) *weight = lastweight;
return points;
}
if (points) free(points);
points = malloc((row + 1) * sizeof(uint64_t));
if (!points)
{
last = -1;
return NULL;
}
last = row;
c = 1;
for (k = 0; k <= row; k++)
{
points[k] = c;
c = c * (row - k) / (k + 1);
}
for (k = 0, lastweight = 0; k <= row; k ++) lastweight += points[k];
if (weight) *weight = lastweight;
return points;
}
static const double *
gaussian_row_getd(int row, int *npoints, double *weight)
{
static double *points = NULL;
static int last = -1;
static double lastweight = -1;
double c;
int k;
if (row < 0) return NULL;
if (last == row)
{
if (weight) *weight = lastweight;
return points;
}
if (points) free(points);
points = malloc((row + 1) * sizeof(double));
if (!points)
{
last = -1;
return NULL;
}
last = row;
if (npoints) *npoints = row + 1;
c = 1;
for (k = 0; k <= row; k++)
{
points[k] = c;
c = c * (row - k) / (k + 1);
}
for (k = 0, lastweight = 0; k <= row; k++) lastweight += points[k];
if (weight) *weight = lastweight;
return points;
}
#if BUILD_NEON0
static Eina_Bool
negation_filter_neon(Evas_Filter_Info *info, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t tmp;
if (src->cache_entry.flags.alpha)
{
// FIXME: not implemented
}
else
{
/* No alpha */
#define AP "NEG_FILTER_NA"
asm volatile (
".fpu neon \n\t"
"vdup.u32 q14, $0xff000000 \n\t"
"vmvn.u32 q15, q1 \n\t"
// fixme: do check for small loops
AP"loopinit: \n\t"
"sub %[tmp], %[e], #31 \n\t"
AP"loop: \n\t"
"vldm %[s]!, {d0,d1,d2,d3} \n\t"
"vand q2, q0, q15 \n\t"
"vand q3, q1, q15 \n\t"
"vand q4, q0, q14 \n\t"
"vand q5, q1, q14 \n\t"
// fixme: can i do this with xor
"cmp %[tmp], %[s] \n\t"
"vmvn q6, q2 \n\t"
"vmvn q7, q3 \n\t"
"vorr q0, q6,q4 \n\t"
"vorr q1, q7,q5 \n\t"
"vstm %[d]!, {d0,d1,d2,d3} \n\t"
"bhi "AP"loop \n\t"
: // no out
: // input
[e] "r" (src->image.data+ src->cache_entry.w*src->cache_entry.h),
[s] "r" (src->image.data),
[tmp] "r" (tmp),
[d] "r" (dst->image.data)
: "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q14", "q15",
"memory"
);
#undef AP
}
return EINA_TRUE;
}
#endif
static Eina_Bool
negation_filter(Evas_Filter_Info *info, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t *in, *out;
int i,j;
int w,h;
uint32_t a;
uint8_t r,g,b;
#if BUILD_NEON0
if (evas_common_cpu_has_feature(CPU_FEATURE_NEON) &&
(!src->cache_entry.flags.alpha))
return negation_filter_neon(info, src, dst);
#endif
in = src->image.data;
out = dst->image.data;
w = src->cache_entry.w;
h = src->cache_entry.h;
if (src->cache_entry.flags.alpha)
{
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
*out = ARGB_JOIN(a, a - r, a - g, a - b);
out++;
in++;
}
}
}
else
{
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
*out = ARGB_JOIN(a, ~r, ~g, ~b);
out++;
in++;
}
}
}
return EINA_TRUE;
info = NULL;
}
static Eina_Bool
sepia_filter(Evas_Filter_Info *info EINA_UNUSED, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t *in, *out;
int i, j;
int w, h;
uint32_t a, r, g, b, nr, ng, nb;
in = src->image.data;
out = dst->image.data;
w = src->cache_entry.w;
h = src->cache_entry.h;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
nr = ((uint32_t)((r * 0.393) + (g * 0.769) + (b * 0.189)));
ng = ((uint32_t)((r * 0.349) + (g * 0.686) + (b * 0.168)));
nb = ((uint32_t)((r * 0.272) + (g * 0.534) + (b * 0.131)));
if (nr > 255) nr = 255;
if (ng > 255) ng = 255;
if (nb > 255) nb = 255;
*out = ARGB_JOIN(a, nr, ng, nb);
out++;
in++;
}
}
return EINA_TRUE;
}
static Eina_Bool
greyscale_filter(Evas_Filter_Info *info EINA_UNUSED, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t *in, *out;
int i, j;
int w, h;
uint32_t cur;
uint32_t a, r, g, b;
in = src->image.data;
out = dst->image.data;
w = src->cache_entry.w;
h = src->cache_entry.h;
if (src->cache_entry.flags.alpha)
{
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
cur = (r * 0.3) + (g * 0.59) + (b * 0.11);
*out = ARGB_JOIN(a, r, g, b);
out++;
in++;
}
}
}
else
{
for (i = 0 ; i < h ; i ++)
{
for (j = 0; j < w ; j ++)
{
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
cur = r * 0.3 + g * 0.59 + b * 0.11;
*out = ARGB_JOIN(255, r, g, b);
out++;
in++;
}
}
}
return EINA_TRUE;
}
static Eina_Bool
brightness_filter(Evas_Filter_Info *info, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t *in, *out;
int i, j;
int w, h;
int a,r,g,b;
int delta;
int adjdelta;
Evas_Filter_Info_Brightness *bness;
in = src->image.data;
out = dst->image.data;
w = src->cache_entry.w;
h = src->cache_entry.h;
bness = info->data;
delta = bness->adjust * 255;
if (delta > 255)
delta = 255;
else if (delta < -255)
delta = -255;
/* Note we could optimise the -255, 0 and 255 cases, but why would people
* be doing that */
if (delta >= 0)
{
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
adjdelta = (a * delta) >> 8;
r = MIN(r + adjdelta, a);
g = MIN(g + adjdelta, a);
b = MIN(b + adjdelta, a);
*out = ARGB_JOIN(a, r ,g, b);
out++;
in++;
}
}
}
else
{
/* Delta negative */
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
a = A_VAL(in);
r = R_VAL(in);
g = G_VAL(in);
b = B_VAL(in);
adjdelta = (a * delta) >> 8;
r = MAX(r + adjdelta, 0);
g = MAX(g + adjdelta, 0);
b = MAX(b + adjdelta, 0);
*out = ARGB_JOIN(a, r ,g, b);
out++;
in++;
}
}
}
return EINA_TRUE;
}
static Eina_Bool
contrast_filter(Evas_Filter_Info *info EINA_UNUSED, RGBA_Image *src, RGBA_Image *dst)
{
uint32_t *in, *out;
int i, j;
int w, h;
in = src->image.data;
out = dst->image.data;
w = src->cache_entry.w;
h = src->cache_entry.h;
for (i = 0; i < h; i++)
{
for (j = 0; j < w; j++)
{
// FIXME: not even implemented
out++;
in++;
}
}
return EINA_TRUE;
}
#endif
/* vim:set ts=8 sw=3 sts=3 expandtab cino=>5n-2f0^-2{2(0W1st0 :*/