Evas filters: Implement curve script API
authorJean-Philippe Andre <jp.andre@samsung.com>
Thu, 2 Jan 2014 06:57:44 +0000 (15:57 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Fri, 7 Feb 2014 08:33:17 +0000 (17:33 +0900)
Currently supports interpolation modes "none" and "linear".
Cubic interpolation is not implemented yet.

src/lib/evas/filters/evas_filter_parser.c
src/lib/evas/filters/evas_filter_private.h
src/lib/evas/filters/evas_filter_utils.c
src/lib/evas/include/evas_filter.h

index 8992f39..b7f7825 100644 (file)
@@ -1,8 +1,7 @@
 #include "evas_filter_private.h"
 #include <stdarg.h>
 
-#define EVAS_FILTER_MODE_BUFFER (EVAS_FILTER_MODE_LAST+1)
-#define EVAS_FILTER_MODE_GROW   (EVAS_FILTER_MODE_LAST+2)
+#define EVAS_FILTER_MODE_GROW   (EVAS_FILTER_MODE_LAST+1)
 
 // Map of the most common HTML color names
 static struct
@@ -83,6 +82,7 @@ typedef struct _Instruction_Param
    Eina_Value *value;
    Eina_Bool set : 1;
    Eina_Bool allow_seq : 1;
+   Eina_Bool allow_any_string : 1;
 } Instruction_Param;
 
 struct _Evas_Filter_Instruction
@@ -514,7 +514,7 @@ _value_parse(Instruction_Param *param, const char *value)
         return EINA_TRUE;
       case VT_STRING:
       case VT_BUFFER:
-        PARSE_CHECK(_is_valid_string(value));
+        if (!param->allow_any_string) PARSE_CHECK(_is_valid_string(value));
         eina_value_set(param->value, value);
         return EINA_TRUE;
       case VT_COLOR:
@@ -819,18 +819,37 @@ _bump_instruction_prepare(Evas_Filter_Instruction *instr)
 static Eina_Bool
 _curve_instruction_prepare(Evas_Filter_Instruction *instr)
 {
+   Instruction_Param *param;
+
    EINA_SAFETY_ON_NULL_RETURN_VAL(instr, EINA_FALSE);
    EINA_SAFETY_ON_NULL_RETURN_VAL(instr->name, EINA_FALSE);
    EINA_SAFETY_ON_FALSE_RETURN_VAL(!strcasecmp(instr->name, "curve"), EINA_FALSE);
 
    /*
-   * curve TODO
-   * This one is a bit trickier: need interpolation functions to describe
-   * the curve.
+   * curve [points=]INTERPSTR [interpolation=]STRING (src=BUFFER) (dst=BUFFER)
+   *
+   * INTERPSTR is a STRING of the following format:
+   * "x1:y1-x2:y2-x3:y3...xn:yn"
+   * Where xk and yk are all numbers in [0-255] and xk are in increasing order
+   *
+   * The interpolation STRING can be one of:
+   * - none:   y = yk in [xk,xk+1] (staircase effect)
+   * - linear: linear interpolation y = ax + b
+   * - cubic:  cubic interpolation y = ax^3 + bx^2 + cx + d
+   * Invalid values default to linear
    */
 
-   //instr->type = EVAS_FILTER_MODE_CURVE;
-   CRI("Not implemented yet");
+   instr->type = EVAS_FILTER_MODE_CURVE;
+
+   _instruction_param_seq_add(instr, "points", VT_STRING, NULL);
+   param = EINA_INLIST_CONTAINER_GET(eina_inlist_last(instr->params), Instruction_Param);
+   param->allow_any_string = EINA_TRUE;
+
+   _instruction_param_seq_add(instr, "interpolation", VT_STRING, "linear");
+   _instruction_param_seq_add(instr, "channel", VT_STRING, "rgb");
+   _instruction_param_name_add(instr, "src", VT_BUFFER, "input");
+   _instruction_param_name_add(instr, "dst", VT_BUFFER, "output");
+
    return EINA_FALSE;
 }
 
@@ -910,7 +929,7 @@ _fill_instruction_prepare(Evas_Filter_Instruction *instr)
    * Works with both Alpha and RGBA.
    */
 
-   instr->type = EVAS_FILTER_MODE_BUFFER;
+   instr->type = EVAS_FILTER_MODE_FILL;
    _instruction_param_seq_add(instr, "dst", VT_BUFFER, NULL);
    _instruction_param_seq_add(instr, "color", VT_COLOR, 0x0);
 
@@ -1463,10 +1482,9 @@ _instr2cmd_fill(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
 
    buf = _buffer_get(pgm, bufname);
 
-   ENFN->context_color_get(ENDT, dc, &R, &G, &B, &A);
-   ENFN->context_color_set(ENDT, dc, R_VAL(&color), G_VAL(&color), B_VAL(&color), A_VAL(&color));
+   SETCOLOR(color);
    cmdid = evas_filter_command_fill_add(ctx, dc, buf->cid);
-   ENFN->context_color_set(ENDT, dc, R, G, B, A);
+   RESETCOLOR();
 
    return cmdid;
 }
@@ -1515,10 +1533,89 @@ _instr2cmd_mask(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
    out = _buffer_get(pgm, dst);
    mask = _buffer_get(pgm, msk);
 
-   ENFN->context_color_get(ENDT, dc, &R, &G, &B, &A);
-   ENFN->context_color_set(ENDT, dc, R_VAL(&color), G_VAL(&color), B_VAL(&color), A_VAL(&color));
+   SETCOLOR(color);
    cmdid = evas_filter_command_mask_add(ctx, dc, in->cid, mask->cid, out->cid, fillmode);
-   ENFN->context_color_set(ENDT, dc, R, G, B, A);
+   RESETCOLOR();
+
+   return cmdid;
+}
+
+static int
+_instr2cmd_curve(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
+                 Evas_Filter_Instruction *instr, void *dc)
+{
+   Evas_Filter_Interpolation_Mode mode = EVAS_FILTER_INTERPOLATION_MODE_LINEAR;
+   Evas_Filter_Channel channel = EVAS_FILTER_CHANNEL_RGB;
+   const char *src, *dst, *points_str, *interpolation, *channel_name;
+   DATA8 values[256] = {0}, points[512];
+   int cmdid, point_count = 0;
+   char *token, *copy, *saveptr;
+   Buffer *in, *out;
+   Eina_Bool parse_ok = EINA_FALSE;
+
+   src = _instruction_param_gets(instr, "src", NULL);
+   dst = _instruction_param_gets(instr, "dst", NULL);
+   points_str = _instruction_param_gets(instr, "points", NULL);
+   interpolation = _instruction_param_gets(instr, "interpolation", NULL);
+   channel_name = _instruction_param_gets(instr, "channel", NULL);
+
+   if (channel_name)
+     {
+        if (tolower(*channel_name) == 'r')
+          {
+             if (!strcasecmp(channel_name, "rgb"))
+               channel = EVAS_FILTER_CHANNEL_RGB;
+             else
+               channel = EVAS_FILTER_CHANNEL_RED;
+          }
+        else if (tolower(*channel_name) == 'g')
+          channel = EVAS_FILTER_CHANNEL_GREEN;
+        else if (tolower(*channel_name) == 'b')
+          channel = EVAS_FILTER_CHANNEL_BLUE;
+        else if (tolower(*channel_name) == 'a')
+          channel = EVAS_FILTER_CHANNEL_ALPHA;
+     }
+
+   if (interpolation)
+     {
+        if (!strcasecmp(interpolation, "none"))
+          mode = EVAS_FILTER_INTERPOLATION_MODE_NONE;
+        else if (!strcasecmp(interpolation, "cubic"))
+          mode = EVAS_FILTER_INTERPOLATION_MODE_CUBIC;
+     }
+
+   if (!points_str) goto interpolated;
+   copy = strdup(points_str);
+   token = strtok_r(copy, "-", &saveptr);
+   if (!token) goto interpolated;
+
+   while (token)
+     {
+        int x, y, r, maxx = 0;
+        r = sscanf(token, "%u:%u", &x, &y);
+        if (r != 2) goto interpolated;
+        if (x < maxx || x >= 256) goto interpolated;
+        points[point_count * 2 + 0] = x;
+        points[point_count * 2 + 1] = y;
+        point_count++;
+        token = strtok_r(NULL, "-", &saveptr);
+     }
+
+   parse_ok = evas_filter_interpolate(values, points, point_count, mode);
+
+interpolated:
+   free(copy);
+   if (!parse_ok)
+     {
+        int x;
+        ERR("Failed to parse the interpolation chain");
+        for (x = 0; x < 256; x++)
+          values[x] = x;
+     }
+
+   in = _buffer_get(pgm, src);
+   out = _buffer_get(pgm, dst);
+   cmdid = evas_filter_command_curve_add(ctx, dc, in->cid, out->cid, values, channel);
 
    return cmdid;
 }
@@ -1553,15 +1650,9 @@ _command_from_instruction(Evas_Filter_Context *ctx, Evas_Filter_Program *pgm,
       case EVAS_FILTER_MODE_MASK:
         instr2cmd = _instr2cmd_mask;
         break;
-#if 0
       case EVAS_FILTER_MODE_CURVE:
         instr2cmd = _instr2cmd_curve;
         break;
-#endif
-      case EVAS_FILTER_MODE_CURVE:
-        CRI("Not implemented yet");
-        return -1;
-      case EVAS_FILTER_MODE_BUFFER:
       default:
         CRI("Invalid instruction type: %d", instr->type);
         return -1;
index 07dcf20..0a0c85e 100644 (file)
@@ -36,6 +36,8 @@
 #define BUFFERS_LOCK() do { if (cmd->input) cmd->input->locked = 1; if (cmd->output) cmd->output->locked = 1; if (cmd->mask) cmd->mask->locked = 1; } while (0)
 #define BUFFERS_UNLOCK() do { if (cmd->input) cmd->input->locked = 0; if (cmd->output) cmd->output->locked = 0; if (cmd->mask) cmd->mask->locked = 0; } while (0)
 
+typedef enum _Evas_Filter_Interpolation_Mode Evas_Filter_Interpolation_Mode;
+
 struct _Evas_Filter_Context
 {
    Evas_Public_Data *evas;
@@ -126,6 +128,13 @@ struct _Evas_Filter_Buffer
    Eina_Bool locked : 1;      // internal flag
 };
 
+enum _Evas_Filter_Interpolation_Mode
+{
+   EVAS_FILTER_INTERPOLATION_MODE_NONE,
+   EVAS_FILTER_INTERPOLATION_MODE_LINEAR,
+   EVAS_FILTER_INTERPOLATION_MODE_CUBIC
+};
+
 void                     evas_filter_context_clear(Evas_Filter_Context *ctx);
 void                     evas_filter_context_proxy_bind(Evas_Filter_Context *ctx, Evas_Object *eo_proxy, Evas_Object *eo_source, int bufid);
 
@@ -144,5 +153,6 @@ Eina_Bool evas_filter_buffer_alloc(Evas_Filter_Buffer *fb, int w, int h);
 Evas_Filter_Buffer *_filter_buffer_get(Evas_Filter_Context *ctx, int bufid);
 Evas_Filter_Buffer *evas_filter_temporary_buffer_get(Evas_Filter_Context *ctx, int w, int h, Eina_Bool alpha_only);
 Evas_Filter_Buffer *evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx, Evas_Filter_Buffer *src, int w, int h);
+Eina_Bool evas_filter_interpolate(DATA8* output /* 256 values */, DATA8* points /* pairs x + y */, int point_count, Evas_Filter_Interpolation_Mode mode);
 
 #endif // EVAS_FILTER_PRIVATE_H
index f07d69c..340c7d2 100644 (file)
@@ -50,3 +50,70 @@ evas_filter_buffer_scaled_get(Evas_Filter_Context *ctx,
 
    return fb;
 }
+
+static Eina_Bool
+_interpolate_none(DATA8 *output, DATA8 *points, int point_count)
+{
+   int j, k, val, x1, x2;
+   for (j = 0; j < point_count; j++)
+     {
+        x1 = points[j * 2];
+        val = points[j * 2 + 1];
+        if (j < (point_count - 1))
+          x2 = points[(j + 1) * 2];
+        else
+          x2 = 256;
+        if (x2 < x1) return EINA_FALSE;
+        for (k = x1; k < x2; k++)
+          output[k] = val;
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_interpolate_linear(DATA8 *output, DATA8 *points, int point_count)
+{
+   int j, k, val1, val2, x1, x2;
+   for (j = 0; j < point_count; j++)
+     {
+        x1 = points[j * 2];
+        val1 = points[j * 2 + 1];
+        if (j < (point_count - 1))
+          {
+             x2 = points[(j + 1) * 2];
+             val2 = points[(j + 1) * 2 + 1];
+          }
+        else
+          {
+             x2 = 256;
+             val2 = val1;
+          }
+        if (x2 < x1) return EINA_FALSE;
+        for (k = x1; k < x2; k++)
+          output[k] = val1 + ((val2 - val1) * (k - x1)) / (x2 - x1);
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_interpolate_cubic(DATA8 *output, DATA8 *points, int point_count)
+{
+   CRI("Not implemented yet");
+   return EINA_FALSE;
+}
+
+Eina_Bool
+evas_filter_interpolate(DATA8 *output, DATA8 *points, int point_count,
+                        Evas_Filter_Interpolation_Mode mode)
+{
+   switch (mode)
+     {
+      case EVAS_FILTER_INTERPOLATION_MODE_NONE:
+        return _interpolate_none(output, points, point_count);
+      case EVAS_FILTER_INTERPOLATION_MODE_CUBIC:
+        return _interpolate_cubic(output, points, point_count);
+      case EVAS_FILTER_INTERPOLATION_MODE_LINEAR:
+      default:
+        return _interpolate_linear(output, points, point_count);
+     }
+}
index 4fe04f7..59b430b 100644 (file)
@@ -150,6 +150,17 @@ int                      evas_filter_command_blur_add(Evas_Filter_Context *ctx,
  * @note The current draw context's render operation is ignored (always uses COPY mode).
  */
 int                      evas_filter_command_fill_add(Evas_Filter_Context *ctx, void *draw_context, int buf);
+
+/**
+ * @brief evas_filter_command_curve_add
+ * @param ctx            Current filter chain
+ * @param draw_context   Current Evas draw context. Current color is used when buf is RGBA, and clip is used to specify the fill area.
+ * @param inbuf          Input buffer, ALPHA or RGBA.
+ * @param outbuf         Output buffer, must have same colorspace as inbuf.
+ * @param curve          The data points to use, must contain 256 values.
+ * @param channel        Which channel to apply the curve (red, green, blue, alpha or RGB)
+ * @return               Filter command ID or -1 in case of error
+ */
 int                      evas_filter_command_curve_add(Evas_Filter_Context *ctx, void *draw_context, int inbuf, int outbuf, DATA8 *curve /* 256 elements */, Evas_Filter_Channel channel);
 
 /**