Evas filters: Use box blur by default
authorJean-Philippe Andre <jp.andre@samsung.com>
Tue, 11 Mar 2014 09:21:46 +0000 (18:21 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Wed, 12 Mar 2014 05:08:02 +0000 (14:08 +0900)
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
src/lib/evas/filters/evas_filter_parser.c
src/lib/evas/filters/evas_filter_private.h
src/lib/evas/include/evas_filter.h

index b3f5836..6e899a9 100644 (file)
@@ -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_GAUSSIAN:
+        count = 1;
+        break;
+
       case EVAS_FILTER_BLUR_BOX:
-        if (dx < 0) dx = 0;
-        if (dy < 0) dy = 0;
-        if (!dx && !dy) goto fail;
+        count = MIN(MAX(1, count), 6);
         break;
-      case EVAS_FILTER_BLUR_GAUSSIAN:
-        if (dx < 0) dx = 0;
-        if (dy < 0) dy = 0;
-        if (!dx && !dy) goto fail;
+
+      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;
index 907d7b5..ce7e45d 100644 (file)
@@ -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". <br>
@@ -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 <b>recommended in all situations</b> 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;
 }
index 6255be4..e4b83a8 100644 (file)
@@ -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
index 5445058..052617e 100644 (file)
@@ -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