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