From 2a1ba1b9086bdf9d3d474ec7f8a5cfe37fcdd61a Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Tue, 11 Mar 2014 18:21:46 +0900 Subject: [PATCH] Evas filters: Use box blur by default BOX blur is a lot faster (and easier to optimize, too) than GAUSSIAN blur. Repeating 2x or 3x BOX blur will also give similar results to GAUSSIAN blur (very smooth), but in much less time. Add a count parameter to the BOX blur instruction. --- src/lib/evas/filters/evas_filter.c | 83 +++++++++++++++++++--- src/lib/evas/filters/evas_filter_parser.c | 61 +++++++++++++--- src/lib/evas/filters/evas_filter_private.h | 2 + src/lib/evas/include/evas_filter.h | 7 +- 4 files changed, 131 insertions(+), 22 deletions(-) diff --git a/src/lib/evas/filters/evas_filter.c b/src/lib/evas/filters/evas_filter.c index b3f5836025..6e899a901b 100644 --- a/src/lib/evas/filters/evas_filter.c +++ b/src/lib/evas/filters/evas_filter.c @@ -898,7 +898,7 @@ evas_filter_command_fill_add(Evas_Filter_Context *ctx, void *draw_context, int evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx, int inbuf, int outbuf, Evas_Filter_Blur_Type type, - int dx, int dy, int ox, int oy) + int dx, int dy, int ox, int oy, int count) { Evas_Filter_Command *cmd = NULL; Evas_Filter_Buffer *in = NULL, *out = NULL, *tmp = NULL, *in_dy = NULL; @@ -911,18 +911,79 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx, EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, -1); EINA_SAFETY_ON_NULL_RETURN_VAL(drawctx, -1); + if (dx < 0) dx = 0; + if (dy < 0) dy = 0; + if (!dx && !dy) goto fail; + switch (type) { - case EVAS_FILTER_BLUR_BOX: - if (dx < 0) dx = 0; - if (dy < 0) dy = 0; - if (!dx && !dy) goto fail; - break; case EVAS_FILTER_BLUR_GAUSSIAN: - if (dx < 0) dx = 0; - if (dy < 0) dy = 0; - if (!dx && !dy) goto fail; + count = 1; break; + + case EVAS_FILTER_BLUR_BOX: + count = MIN(MAX(1, count), 6); + break; + + case EVAS_FILTER_BLUR_DEFAULT: + count = 1; + + /* In DEFAULT mode we cheat, depending on the size of the kernel: + * For 1px to 2px, use true Gaussian blur. + * For 3px to 6px, use two Box blurs. + * For more than 6px, use three Box blurs. + * This will give both nicer and MUCH faster results than Gaussian. + * + * NOTE: When implementing blur with GL shaders, other tricks will be + * needed, of course! + */ + { + int tmp_out = outbuf; + int tmp_in = inbuf; + int tmp_ox = ox; + int tmp_oy = oy; + + id = -1; + if (dx && dy) + { + tmp = evas_filter_temporary_buffer_get(ctx, 0, 0, EINA_TRUE); + if (!tmp) goto fail; + tmp_in = tmp_out = tmp->id; + tmp_ox = tmp_oy = 0; + } + + if (dx) + { + if (dx <= 2) + type = EVAS_FILTER_BLUR_GAUSSIAN; + else + type = EVAS_FILTER_BLUR_BOX; + + id = evas_filter_command_blur_add(ctx, drawctx, inbuf, tmp_out, + type, dx, 0, tmp_ox, tmp_oy, 0); + if (id < 0) goto fail; + cmd = _evas_filter_command_get(ctx, id); + cmd->blur.auto_count = EINA_TRUE; + } + + if (dy) + { + if (dy <= 2) + type = EVAS_FILTER_BLUR_GAUSSIAN; + else + type = EVAS_FILTER_BLUR_BOX; + + id = evas_filter_command_blur_add(ctx, drawctx, inbuf, tmp_in, + type, 0, dy, ox, oy, 0); + if (id < 0) goto fail; + cmd = _evas_filter_command_get(ctx, id); + cmd->blur.auto_count = EINA_TRUE; + } + + return id; + } + break; + default: CRI("Not implemented yet!"); goto fail; @@ -1031,6 +1092,7 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx, cmd->blur.type = type; cmd->blur.dx = dx; cmd->blur.dy = 0; + cmd->blur.count = count; DRAW_COLOR_SET(R, G, B, A); ret = cmd->id; } @@ -1043,6 +1105,7 @@ evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *drawctx, cmd->blur.type = type; cmd->blur.dx = 0; cmd->blur.dy = dy; + cmd->blur.count = count; DRAW_COLOR_SET(R, G, B, A); if (ret <= 0) ret = cmd->id; } @@ -1157,7 +1220,7 @@ evas_filter_command_grow_add(Evas_Filter_Context *ctx, void *draw_context, blurcmd = evas_filter_command_blur_add(ctx, draw_context, inbuf, growbuf, EVAS_FILTER_BLUR_DEFAULT, - abs(radius), abs(radius), 0, 0); + abs(radius), abs(radius), 0, 0, 0); if (blurcmd < 0) return -1; if (diam > 255) diam = 255; diff --git a/src/lib/evas/filters/evas_filter_parser.c b/src/lib/evas/filters/evas_filter_parser.c index 907d7b51a4..ce7e45d40d 100644 --- a/src/lib/evas/filters/evas_filter_parser.c +++ b/src/lib/evas/filters/evas_filter_parser.c @@ -1018,8 +1018,9 @@ _blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, int *padl, int *padr, int *padt, int *padb) { Eina_Bool yset = EINA_FALSE; - int rx, ry, ox, oy, l, r, t, b; - const char *inbuf, *outbuf; + int rx, ry, ox, oy, l, r, t, b, count; + const char *inbuf, *outbuf, *typestr; + Evas_Filter_Blur_Type type = EVAS_FILTER_BLUR_DEFAULT; Buffer *in, *out; rx = _instruction_param_geti(instr, "rx", NULL); @@ -1028,6 +1029,11 @@ _blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, oy = _instruction_param_geti(instr, "oy", NULL); inbuf = _instruction_param_gets(instr, "src", NULL); outbuf = _instruction_param_gets(instr, "dst", NULL); + count = _instruction_param_geti(instr, "count", NULL); + typestr = _instruction_param_gets(instr, "type", NULL); + + if (typestr && !strcasecmp(typestr, "box")) + type = EVAS_FILTER_BLUR_BOX; in = _buffer_get(pgm, inbuf); out = _buffer_get(pgm, outbuf); @@ -1038,6 +1044,17 @@ _blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, if (rx < 0) rx = 0; if (ry < 0) ry = 0; + if (type == EVAS_FILTER_BLUR_BOX) + { + if (count < 1) count = 1; + if (count > 6) count = 3; + } + else + count = 1; + + rx *= count; + ry *= count; + l = rx + in->pad.l + ((ox < 0) ? (-ox) : 0); r = rx + in->pad.r + ((ox > 0) ? ox : 0); t = ry + in->pad.t + ((oy < 0) ? (-oy) : 0); @@ -1067,7 +1084,7 @@ _blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, @param rx X radius. Specifies the radius of the blurring kernel (X direction). @param ry Y radius. Specifies the radius of the blurring kernel (Y direction). If -1 is used, then @a ry = @a rx. - @param type Blur type to apply. One of @c default, @c box or @c gaussian. @c default is an alias for @c gaussian. + @param type Blur type to apply. One of @c default, @c box or @c gaussian. See below for details about @c default. @param ox X offset. Moves the buffer to the right (@a ox > 0) or to the left (@a ox < 0) by N pixels. @param oy Y offset. Moves the buffer to the bottom (@a oy > 0) or to the top (@a oy < 0) by N pixels. @param color A color to use for alpha to RGBA conversion. See @ref evasfilters_color "colors".
@@ -1075,6 +1092,15 @@ _blur_padding_update(Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, draw the buffer in this color. @param src Source buffer to blur. @param dst Destination buffer for blending. + @param count Number of times to repeat the blur. Only valid with @c box blur. Valid range is: 1 to 6. + + The blur type @c default is recommended in all situations as it will select the smoothest + and fastest operation possible depending on the kernel size. Instead of running a real + gaussian blur, 2 or 3 box blurs may be chained to produce a similar effect at a much + higher speed. The value @a count can be set to a value from 1 to 6 if blur type @c box + has been specified. + + The speedups of @c box over @c gaussian are of orders of 4x to more than 20x faster. If @a src is an alpha buffer and @a dst is an RGBA buffer, then the color option should be set. @@ -1105,6 +1131,7 @@ _blur_instruction_prepare(Evas_Filter_Instruction *instr) _instruction_param_name_add(instr, "color", VT_COLOR, 0xFFFFFFFF); _instruction_param_name_add(instr, "src", VT_BUFFER, "input"); _instruction_param_name_add(instr, "dst", VT_BUFFER, "output"); + _instruction_param_name_add(instr, "count", VT_INT, 0); return EINA_TRUE; } @@ -1947,12 +1974,12 @@ static int _instr2cmd_blur(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, Evas_Filter_Instruction *instr, void *dc) { - Eina_Bool isset = EINA_FALSE, yset = EINA_FALSE; + Eina_Bool colorset = EINA_FALSE, yset = EINA_FALSE, cntset = EINA_FALSE; Evas_Filter_Blur_Type type = EVAS_FILTER_BLUR_DEFAULT; const char *src, *dst, *typestr; DATA32 color; Buffer *in, *out; - int cmdid, ox, oy, rx, ry, A, R, G, B; + int cmdid, ox, oy, rx, ry, A, R, G, B, count; src = _instruction_param_gets(instr, "src", NULL); dst = _instruction_param_gets(instr, "dst", NULL); @@ -1960,8 +1987,9 @@ _instr2cmd_blur(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, oy = _instruction_param_geti(instr, "oy", NULL); rx = _instruction_param_geti(instr, "rx", NULL); ry = _instruction_param_geti(instr, "ry", &yset); - color = _instruction_param_getc(instr, "color", &isset); + color = _instruction_param_getc(instr, "color", &colorset); typestr = _instruction_param_gets(instr, "type", NULL); + count = _instruction_param_geti(instr, "count", &cntset); in = _buffer_get(pgm, src); out = _buffer_get(pgm, dst); @@ -1977,11 +2005,26 @@ _instr2cmd_blur(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm, ERR("Unknown blur type '%s'. Using default blur.", typestr); } + if (type == EVAS_FILTER_BLUR_BOX) + { + if (count < 1) count = 1; + if (count > 6) + { + WRN("Box blur count should be below 6, defaults to 3."); + count = 3; + } + } + else + { + if (cntset) WRN("Blur count can only be used with BOX blur."); + count = 1; + } + if (!yset) ry = rx; - if (isset) SETCOLOR(color); + if (colorset) SETCOLOR(color); cmdid = evas_filter_command_blur_add(ctx, dc, in->cid, out->cid, type, - rx, ry, ox, oy); - if (isset) RESETCOLOR(); + rx, ry, ox, oy, count); + if (colorset) RESETCOLOR(); return cmdid; } diff --git a/src/lib/evas/filters/evas_filter_private.h b/src/lib/evas/filters/evas_filter_private.h index 6255be40d0..e4b83a8291 100644 --- a/src/lib/evas/filters/evas_filter_private.h +++ b/src/lib/evas/filters/evas_filter_private.h @@ -126,7 +126,9 @@ struct _Evas_Filter_Command struct { int dx, dy; + int count; Evas_Filter_Blur_Type type; + Eina_Bool auto_count : 1; // If true, BOX blur will be smooth using } blur; struct diff --git a/src/lib/evas/include/evas_filter.h b/src/lib/evas/include/evas_filter.h index 5445058545..052617e48f 100644 --- a/src/lib/evas/include/evas_filter.h +++ b/src/lib/evas/include/evas_filter.h @@ -42,10 +42,10 @@ enum _Evas_Filter_Mode enum _Evas_Filter_Blur_Type { - EVAS_FILTER_BLUR_GAUSSIAN = 0x0, // Gaussian or sine curve. O(nm) + EVAS_FILTER_BLUR_DEFAULT = 0x0, // Default blur (GAUSSIAN or series of BOX) EVAS_FILTER_BLUR_BOX = 0x1, // Optimizable on CPU. But, UGLY. O(n) + EVAS_FILTER_BLUR_GAUSSIAN = 0x2, // Gaussian blur (using sine curve) EVAS_FILTER_BLUR_LAST, - EVAS_FILTER_BLUR_DEFAULT = EVAS_FILTER_BLUR_GAUSSIAN }; enum _Evas_Filter_Channel @@ -141,9 +141,10 @@ int evas_filter_command_blend_add(Evas_Filter_Context *ctx, * @param dy Y radius of blur. Can be negative ONLY for MOTION blur * @param ox X offset in the destination buffer * @param oy Y offset in the destination buffer + * @param count Number of times to repeat the operation (used for smooth fast blurs with box blur) * @return Filter command ID or -1 in case of error */ -int evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, Evas_Filter_Blur_Type type, int dx, int dy, int ox, int oy); +int evas_filter_command_blur_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, Evas_Filter_Blur_Type type, int dx, int dy, int ox, int oy, int count); /** * @brief Fill a buffer with the current color