/* * 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 // offsetof #include "evas_common.h" #include "evas_private.h" #include /* 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 :*/