Actually, there is a very nice trick with BOX blur.
Pass BOX blur 3 times and you can approximate a GAUSSIAN
blur with up to 3% accuracy. This is way more than enough
for just a simple graphical effect.
So, despite the crappy quality of BOX blur, we should
optimize it a lot so we can replace large GAUSSIAN blurs
with series of BOX blurs instead.
Source: Wikipedia's page on box blur :)
This commit also moves around some duplicated definitions.
lib/evas/filters/evas_filter_private.h
EXTRA_DIST += \
-lib/evas/filters/blur/blur_gaussian_alpha_.c
+lib/evas/filters/blur/blur_gaussian_alpha_.c \
+lib/evas/filters/blur/blur_gaussian_rgba_.c \
+lib/evas/filters/blur/blur_box_alpha_.c \
+lib/evas/filters/blur/blur_box_rgba_.c
### Engines
--- /dev/null
+/* @file blur_box_alpha_.c
+ * Should define the functions:
+ * - _box_blur_horiz_alpha_step
+ * - _box_blur_vert_alpha_step
+ */
+
+#include "evas_common_private.h"
+#include "../evas_filter_private.h"
+
+#if !defined (FUNCTION_NAME) || !defined (STEP)
+# error Must define FUNCTION_NAME and STEP
+#endif
+
+static inline void
+FUNCTION_NAME(const DATA8* restrict src, DATA8* restrict dst,
+ const int radius, const int len,
+ const int loops, const int loopstep)
+{
+ DEFINE_DIVIDER(2 * radius + 1);
+ const int left = MIN(radius, len);
+ const int right = MIN(radius, (len - radius));
+ int acc = 0, k;
+
+ for (int l = loops; l; --l)
+ {
+ const DATA8* restrict sr = src;
+ const DATA8* restrict sl = src;
+ DATA8* restrict d = dst;
+
+ for (k = left; k; k--)
+ {
+ acc += *sr;
+ sr += STEP;
+ }
+
+ for (k = 0; k < left; k++)
+ {
+ acc += *sr;
+ *d = acc / (k + left + 1);
+ sr += STEP;
+ d += STEP;
+ }
+
+ for (k = len - (2 * radius); k; k--)
+ {
+ acc += *sr;
+ *d = DIVIDE(acc);
+ acc -= *sl;
+ sl += STEP;
+ sr += STEP;
+ d += STEP;
+ }
+
+ for (k = right; k; k--)
+ {
+ *d = acc / (k + right);
+ acc -= *sl;
+ d += STEP;
+ sl += STEP;
+ }
+
+ src += loopstep;
+ dst += loopstep;
+ }
+}
+
+#undef FUNCTION_NAME
+#undef STEP
--- /dev/null
+/* @file blur_box_rgba_.c
+ * Should define the functions:
+ * - _box_blur_horiz_rgba_step
+ * - _box_blur_vert_rgba_step
+ */
+
+#include "evas_common_private.h"
+#include "../evas_filter_private.h"
+
+#if !defined (FUNCTION_NAME) || !defined (STEP)
+# error Must define FUNCTION_NAME and STEP
+#endif
+
+static inline void
+FUNCTION_NAME(const DATA32* restrict src, DATA32* restrict dst,
+ const int radius, const int len,
+ const int loops, const int loopstep)
+{
+ DEFINE_DIVIDER(2 * radius + 1);
+ const int left = MIN(radius, len);
+ const int right = MIN(radius, (len - radius));
+
+ for (int l = loops; l; --l)
+ {
+ int acc[4] = {0};
+ int x, k;
+ int divider;
+
+ const DATA8* restrict sl = (DATA8 *) src;
+ const DATA8* restrict sr = (DATA8 *) src;
+ DATA8* restrict d = (DATA8 *) dst;
+
+ // Read-ahead
+ for (x = left; x; x--)
+ {
+ for (k = 0; k < 4; k++)
+ acc[k] += sr[k];
+ sr += STEP;
+ }
+
+ // Left
+ for (x = 0; x < left; x++)
+ {
+ for (k = 0; k < 4; k++)
+ acc[k] += sr[k];
+ sr += STEP;
+
+ divider = x + left + 1;
+ d[ALPHA] = acc[ALPHA] / divider;
+ d[RED] = acc[RED] / divider;
+ d[GREEN] = acc[GREEN] / divider;
+ d[BLUE] = acc[BLUE] / divider;
+ d += STEP;
+ }
+
+ // Main part
+ for (x = len - (2 * radius); x > 0; x--)
+ {
+ for (k = 0; k < 4; k++)
+ acc[k] += sr[k];
+ sr += STEP;
+
+ d[ALPHA] = DIVIDE(acc[ALPHA]);
+ d[RED] = DIVIDE(acc[RED]);
+ d[GREEN] = DIVIDE(acc[GREEN]);
+ d[BLUE] = DIVIDE(acc[BLUE]);
+ d += STEP;
+
+ for (k = 0; k < 4; k++)
+ acc[k] -= sl[k];
+ sl += STEP;
+ }
+
+ // Right part
+ for (x = right; x; x--)
+ {
+ divider = x + right;
+ d[ALPHA] = acc[ALPHA] / divider;
+ d[RED] = acc[RED] / divider;
+ d[GREEN] = acc[GREEN] / divider;
+ d[BLUE] = acc[BLUE] / divider;
+ d += STEP;
+
+ for (k = 0; k < 4; k++)
+ acc[k] -= sl[k];
+ sl += STEP;
+ }
+
+ src += loopstep;
+ dst += loopstep;
+ }
+}
+
+#undef FUNCTION_NAME
+#undef STEP
//DBG("Allocating temporary buffer of size %ux%u", sw, sh);
fb = evas_filter_buffer_alloc_new(ctx, sw, sh, in->alpha_only);
+ if (!fb) goto alloc_fail;
fb->transient = EINA_TRUE;
}
}
//DBG("Allocating temporary buffer of size %ux%u", sw, sh);
fb = evas_filter_buffer_alloc_new(ctx, sw, sh, in->alpha_only);
+ if (!fb) goto alloc_fail;
fb->transient = EINA_TRUE;
}
//DBG("Allocating buffer of size %ux%u alpha %d", fb->w, fb->h, fb->alpha_only);
im = _rgba_image_alloc(fb, NULL);
- if (!im)
- {
- ERR("Buffer %d allocation failed!", fb->id);
- return EINA_FALSE;
- }
+ if (!im) goto alloc_fail;
fb->backing = im;
fb->allocated = (im != NULL);
}
return EINA_TRUE;
+
+alloc_fail:
+ ERR("Buffer allocation failed! Context size: %dx%d", w, h);
+ return EINA_FALSE;
}
int
}
}
- if (ctx->running && ctx->async)
+ if (ctx->running) // && ctx->async)
{
ERR("Can not create a new buffer from this thread!");
return NULL;
// Use a better formula than R+G+B for rgba to alpha conversion (RGB to YCbCr)
#define RGBA2ALPHA_WEIGHTED 1
-#if DIV_USING_BITSHIFT
-static int
-_smallest_pow2_larger_than(int val)
-{
- int n;
-
- for (n = 0; n < 32; n++)
- if (val <= (1 << n)) return n;
-
- ERR("Value %d is too damn high!", val);
- return 32;
-}
-# define DEFINE_DIVIDER(div) const int pow2 = _smallest_pow2_larger_than((div) << 10); const int numerator = (1 << pow2) / (div);
-# define DIVIDE(val) (((val) * numerator) >> pow2)
-#else
-# define DEFINE_DIAMETER(div) const int divider = (div);
-# define DIVIDE(val) ((val) / divider)
-#endif
-
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);
// Stretch if necessary.
- /* NOTE: As of 2014/02/21, this case is impossible. An alpha buffer will
- * always be of the context buffer size, since only proxy buffers have
- * different sizes... and proxies are all RGBA (never alpha only).
+ /* 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))
#include <math.h>
#include <time.h>
-#if DIV_USING_BITSHIFT
-static int
-_smallest_pow2_larger_than(int val)
-{
- int n;
-
- for (n = 0; n < 32; n++)
- if (val <= (1 << n)) return n;
-
- ERR("Value %d is too damn high!", val);
- return 32;
-}
-
-/* Input:
- * const int pow2 = _smallest_pow2_larger_than(divider * 1024);
- * const int numerator = (1 << pow2) / divider;
- * Result:
- * r = ((val * numerator) >> pow2);
- */
-# define DEFINE_DIAMETER(rad) const int pow2 = _smallest_pow2_larger_than((radius * 2 + 1) << 10); const int numerator = (1 << pow2) / (radius * 2 + 1);
-# define DIVIDE_BY_DIAMETER(val) (((val) * numerator) >> pow2)
-#else
-# define DEFINE_DIAMETER(rad) const int diameter = radius * 2 + 1;
-# define DIVIDE_BY_DIAMETER(val) ((val) / diameter)
-#endif
-
-/* RGBA functions */
-
-static void
-_box_blur_step_rgba(DATA32 *src, DATA32 *dst, int radius, int len, int step)
-{
- DEFINE_DIAMETER(radius);
- int acc[4] = {0};
- DATA8 *d, *sr, *sl;
- int x, k;
- int divider;
- int left = MIN(radius, len);
- int right = MIN(radius, (len - radius));
-
- d = (DATA8 *) dst;
- sl = (DATA8 *) src;
- sr = (DATA8 *) src;
-
- // Read-ahead
- for (x = left; x; x--)
- {
- for (k = 0; k < 4; k++)
- acc[k] += sr[k];
- sr += step;
- }
-
- // Left
- for (x = 0; x < left; x++)
- {
- for (k = 0; k < 4; k++)
- acc[k] += sr[k];
- sr += step;
-
- divider = x + left + 1;
- d[ALPHA] = acc[ALPHA] / divider;
- d[RED] = acc[RED] / divider;
- d[GREEN] = acc[GREEN] / divider;
- d[BLUE] = acc[BLUE] / divider;
- d += step;
- }
-
- // Main part
- for (x = len - (2 * radius); x > 0; x--)
- {
- for (k = 0; k < 4; k++)
- acc[k] += sr[k];
- sr += step;
-
- d[ALPHA] = DIVIDE_BY_DIAMETER(acc[ALPHA]);
- d[RED] = DIVIDE_BY_DIAMETER(acc[RED]);
- d[GREEN] = DIVIDE_BY_DIAMETER(acc[GREEN]);
- d[BLUE] = DIVIDE_BY_DIAMETER(acc[BLUE]);
- d += step;
-
- for (k = 0; k < 4; k++)
- acc[k] -= sl[k];
- sl += step;
- }
-
- // Right part
- for (x = right; x; x--)
- {
- divider = x + right;
- d[ALPHA] = acc[ALPHA] / divider;
- d[RED] = acc[RED] / divider;
- d[GREEN] = acc[GREEN] / divider;
- d[BLUE] = acc[BLUE] / divider;
- d += step;
-
- for (k = 0; k < 4; k++)
- acc[k] -= sl[k];
- sl += step;
- }
-}
+#define FUNCTION_NAME _box_blur_horiz_rgba_step
+#define STEP (sizeof(DATA32))
+#include "./blur/blur_box_rgba_.c"
static void
_box_blur_horiz_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
- int y;
- int step = sizeof(DATA32);
-
DEBUG_TIME_BEGIN();
-
- for (y = 0; y < h; y++)
- {
- _box_blur_step_rgba(src, dst, radius, w, step);
- src += w;
- dst += w;
- }
-
+ _box_blur_horiz_rgba_step(src, dst, radius, w, h, w);
DEBUG_TIME_END();
}
+#define FUNCTION_NAME _box_blur_vert_rgba_step
+#define STEP (loops * sizeof(DATA32))
+#include "./blur/blur_box_rgba_.c"
+
static void
_box_blur_vert_rgba(DATA32 *src, DATA32 *dst, int radius, int w, int h)
{
- int x;
- int step = w * sizeof(DATA32);
-
DEBUG_TIME_BEGIN();
-
- for (x = 0; x < w; x++)
- {
- _box_blur_step_rgba(src, dst, radius, h, step);
- src += 1;
- dst += 1;
- }
-
+ _box_blur_vert_rgba_step(src, dst, radius, h, w, 1);
DEBUG_TIME_END();
}
return EINA_TRUE;
}
-/* Alpha only functions */
-
-/* Box blur */
-
-static void
-_box_blur_step_alpha(DATA8 *src, DATA8 *dst, int radius, int len, int step)
-{
- int k;
- int acc = 0;
- DATA8 *sr = src, *sl = src, *d = dst;
- DEFINE_DIAMETER(radius);
- int left = MIN(radius, len);
- int right = MIN(radius, (len - radius));
-
- for (k = left; k; k--)
- {
- acc += *sr;
- sr += step;
- }
-
- for (k = 0; k < left; k++)
- {
- acc += *sr;
- *d = acc / (k + left + 1);
- sr += step;
- d += step;
- }
-
- for (k = len - (2 * radius); k; k--)
- {
- acc += *sr;
- *d = DIVIDE_BY_DIAMETER(acc);
- acc -= *sl;
- sl += step;
- sr += step;
- d += step;
- }
-
- for (k = right; k; k--)
- {
- *d = acc / (k + right);
- acc -= *sl;
- d += step;
- sl += step;
- }
-}
+#define FUNCTION_NAME _box_blur_horiz_alpha_step
+#define STEP 1
+#include "./blur/blur_box_alpha_.c"
static void
_box_blur_horiz_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
- int k;
-
DEBUG_TIME_BEGIN();
-
- for (k = h; k; k--)
- {
- _box_blur_step_alpha(src, dst, radius, w, 1);
- dst += w;
- src += w;
- }
-
+ _box_blur_horiz_alpha_step(src, dst, radius, w, h, w);
DEBUG_TIME_END();
}
+#define FUNCTION_NAME _box_blur_vert_alpha_step
+#define STEP loops
+#include "./blur/blur_box_alpha_.c"
+
static void
_box_blur_vert_alpha(DATA8 *src, DATA8 *dst, int radius, int w, int h)
{
- int k;
-
DEBUG_TIME_BEGIN();
-
- for (k = w; k; k--)
- {
- _box_blur_step_alpha(src, dst, radius, h, w);
- dst += 1;
- src += 1;
- }
-
+ _box_blur_vert_alpha_step(src, dst, radius, h, w, 1);
DEBUG_TIME_END();
}
# define DEBUG_TIME_END() do {} while(0)
#endif
+#if DIV_USING_BITSHIFT
+# define DEFINE_DIVIDER(div) const int pow2 = evas_filter_smallest_pow2_larger_than((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
+
typedef enum _Evas_Filter_Interpolation_Mode Evas_Filter_Interpolation_Mode;
struct _Evas_Filter_Context
Evas_Filter_Buffer *evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, Evas_Filter_Buffer *src, unsigned w, unsigned h);
Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, DATA8* points /* pairs x + y */, int point_count, Evas_Filter_Interpolation_Mode mode);
Evas_Filter_Command *_evas_filter_command_get(Evas_Filter_Context *ctx, int cmdid);
+int evas_filter_smallest_pow2_larger_than(int val);
#endif // EVAS_FILTER_PRIVATE_H
return _interpolate_linear(output, points, point_count);
}
}
+
+int
+evas_filter_smallest_pow2_larger_than(int val)
+{
+ int n;
+
+ for (n = 0; n < 32; n++)
+ if (val <= (1 << n)) return n;
+
+ ERR("Value %d is too damn high!", val);
+ return 32;
+}