Edje/evas filters: Add filter.source support
authorJean-Philippe Andre <jp.andre@samsung.com>
Tue, 16 Jun 2015 05:50:43 +0000 (14:50 +0900)
committerJean-Philippe Andre <jp.andre@samsung.com>
Thu, 25 Jun 2015 05:36:09 +0000 (14:36 +0900)
This should preserve ABI stability with earlier versions of
edje_cc while still providing more advanced control over
proxy bindings for evas filters from EDC.

Also fix proxy binding for filters.

@feature

src/bin/edje/edje_cc_handlers.c
src/lib/edje/edje_private.h
src/lib/edje/edje_text.c
src/lib/evas/canvas/evas_object_text.c
src/lib/evas/filters/evas_filter_parser.c

index 2268190..53bf91e 100644 (file)
@@ -362,6 +362,7 @@ static void st_collections_group_parts_part_description_text_source(void);
 static void st_collections_group_parts_part_description_text_text_source(void);
 static void st_collections_group_parts_part_description_text_ellipsis(void);
 static void st_collections_group_parts_part_description_text_filter(void);
+static void st_collections_group_parts_part_description_text_filter_source(void);
 static void st_collections_group_parts_part_description_box_layout(void);
 static void st_collections_group_parts_part_description_box_align(void);
 static void st_collections_group_parts_part_description_box_padding(void);
@@ -814,6 +815,8 @@ New_Statement_Handler statement_handlers[] =
      {"collections.group.parts.part.description.text.elipsis", st_collections_group_parts_part_description_text_ellipsis},
      {"collections.group.parts.part.description.text.ellipsis", st_collections_group_parts_part_description_text_ellipsis},
      {"collections.group.parts.part.description.text.filter", st_collections_group_parts_part_description_text_filter},
+     {"collections.group.parts.part.description.text.filter.code", st_collections_group_parts_part_description_text_filter}, /* dup */
+     {"collections.group.parts.part.description.text.filter.source", st_collections_group_parts_part_description_text_filter_source},
      {"collections.group.parts.part.description.box.layout", st_collections_group_parts_part_description_box_layout},
      {"collections.group.parts.part.description.box.align", st_collections_group_parts_part_description_box_align},
      {"collections.group.parts.part.description.box.padding", st_collections_group_parts_part_description_box_padding},
@@ -6696,15 +6699,18 @@ st_collections_group_parts_part_description_inherit(void)
               ted->text.domain = STRDUP(ted->text.domain);
               ted->text.text_class = STRDUP(ted->text.text_class);
               ted->text.font.str = STRDUP(ted->text.font.str);
-              ted->text.filter.code = STRDUP(ted->text.filter.code);
-              {
-                 Eina_List *l;
-                 Eina_Stringshare *name;
-                 static int part_key = 0;
 
-                 EINA_LIST_FOREACH(ted->text.filter.sources, l, name)
-                   data_queue_part_lookup(pc, name, &part_key);
-              }
+              /* Filters stuff */
+              ted->text.filter.code = STRDUP(ted->text.filter.code);
+              if (ted->text.filter.code)
+                {
+                   Eina_List *list, *l;
+                   const char *name;
+                   list = ted->text.filter.sources;
+                   ted->text.filter.sources = NULL;
+                   EINA_LIST_FOREACH(list, l, name)
+                     ted->text.filter.sources = eina_list_append(ted->text.filter.sources, STRDUP(name));
+                }
 
               data_queue_copied_part_nest_lookup(pc, &(tparent->text.id_source), &(ted->text.id_source), &ted->text.id_source_part);
               data_queue_copied_part_nest_lookup(pc, &(tparent->text.id_text_source), &(ted->text.id_text_source), &ted->text.id_text_source_part);
@@ -8979,31 +8985,88 @@ st_collections_group_parts_part_description_text_ellipsis(void)
 /**
     @page edcref
 
+    @context
+        part {
+            type: TEXT; // or IMAGE
+            description {
+                ..
+                text {
+                    ..
+                    filter {
+                        code: "blend {} -- ..."
+                        source: "part1" "buf";
+                        source: "part2" "otherbuf";
+                        source: "part3";
+                    }
+                    // or as short form:
+                    filter: "blend {} -- ..."
+                }
+                ..
+            }
+        }
     @property
-        filter
+        filter.code
     @parameters
         [filter program as a string]
     @effect
-        Applies a series of filtering operations to the text.
-        EXPERIMENTAL FEATURE. TO BE DOCUMENTED.
+        Applies a series of image filters to a TEXT or IMAGE part. The argument
+        to this field is the source code of a Lua program invoking various
+        filter operations. For more information, please refer to the page
+        "Evas filters reference".
+        @see evasfiltersref
     @endproperty
 */
 static void
 st_collections_group_parts_part_description_text_filter(void)
 {
    Edje_Part_Description_Text *ed;
-   Eina_List *sources = NULL;
-   Eina_Stringshare *name;
-   char *token, *code;
-   Eina_Bool valid = EINA_TRUE;
-   Edje_Part_Collection *pc;
 
-   static int part_key = 0;
+   check_arg_count(1);
+
+   if (current_part->type != EDJE_PART_TYPE_TEXT)
+     {
+        ERR("parse error %s:%i. text attributes in non-TEXT part.",
+            file_in, line - 1);
+        exit(-1);
+     }
+
+   ed = (Edje_Part_Description_Text*) current_desc;
+
+   free((void*) ed->text.filter.code);
+   ed->text.filter.code = parse_str(0);
+}
+
+/**
+    @page edcref
+
+    @property
+        filter.source
+    @parameters
+        [another part's name] [(optional) buffer name for filter program]
+    @effect
+        Binds another part as an image source (like a proxy source) for a
+        text or image filter operation. Optionally, a buffer name may be
+        specified, so the same filter code can be used with different sources.
+        For more information, please refer to the page "Evas filters reference".
+        @see evasfiltersref
+    @endproperty
+*/
+static void
+st_collections_group_parts_part_description_text_filter_source(void)
+{
+   Edje_Part_Description_Text *ed;
+   Edje_Part_Collection *pc;
+   char *name = NULL, *part, *str;
+   size_t sn = 0, sp;
+   int *part_key;
 
    static const char *allowed_name_chars =
          "abcdefghijklmnopqrstuvwxyzABCDEFGHJIKLMNOPQRSTUVWXYZ0123456789_";
 
-   check_arg_count(1);
+   /* 1 or 2 args only */
+   check_min_arg_count(1);
+   if (get_arg_count() > 1)
+     check_arg_count(2);
 
    if (current_part->type != EDJE_PART_TYPE_TEXT)
      {
@@ -9014,72 +9077,55 @@ st_collections_group_parts_part_description_text_filter(void)
 
    ed = (Edje_Part_Description_Text*) current_desc;
    pc = eina_list_data_get(eina_list_last(edje_collections));
-   if (ed->text.filter.code)
+
+   part = parse_str(0);
+   sp = strlen(part);
+
+   if (get_arg_count() > 1)
      {
-        EINA_LIST_FREE(ed->text.filter.sources, name)
+        name = parse_str(1);
+        if (name) sn = strlen(name);
+        if (!name || (strspn(name, allowed_name_chars) != sn))
           {
-             part_lookup_delete(pc, name, &part_key, NULL);
-             eina_stringshare_del(name);
+             ERR("parse error %s:%i. invalid name for a filter buffer: %s",
+                 file_in, line - 1, name);
+             exit(-1);
           }
-        free((void*)ed->text.filter.code);
      }
-   ed->text.filter.sources = NULL;
-
-   ed->text.filter.code = parse_str(0);
-   if (!ed->text.filter.code) return;
 
-   // Parse list of buffers that have a source
-   // note: does not support comments
-   code = strdup(ed->text.filter.code);
-   for (token = strtok(code, ";"); token; token = strtok(NULL, ";"))
+   if (!name && (strspn(part, allowed_name_chars) == sp))
+     str = strdup(part);
+   else
      {
-        size_t len;
-
-        len = strspn(token, " \n\t");
-        token += len;
-
-        if (!strncasecmp("buffer", token, 6))
+        if (!name)
           {
-             // note: a valid string won't necessary compile at runtime
-
-             token = strchr(token, ':');
-             if (!token)
-               {
-                  valid = EINA_FALSE;
-                  break;
-               }
-             token = strchr(token, '(');
-             if (!token)
-               {
-                  valid = EINA_FALSE;
-                  break;
-               }
-             token = strcasestr(token, "src");
-             if (!token) continue;
-             token += 3;
-             len = strspn(token, " =\n\t");
-             if (!len || !token[len])
+             // name = part so we replace all invalid chars by '_'
+             size_t k;
+             name = strdup(part);
+             sn = strlen(name);
+             for (k = 0; k < sn; k++)
                {
-                  valid = EINA_FALSE;
-                  break;
-               }
-             token += len;
-             len = strspn(token, allowed_name_chars);
-             if (!len || !token[len])
-               {
-                  valid = EINA_FALSE;
-                  break;
+                  if (!index(allowed_name_chars, name[k]))
+                    name[k] = '_';
                }
-             token[len] = '\0';
-             name = eina_stringshare_add(token);
-
-             sources = eina_list_append(sources, name);
-             data_queue_part_lookup(pc, name, &part_key);
           }
-     }
-   free(code);
-
-   if (valid) ed->text.filter.sources = sources;
+        sn += sp + 1;
+        str = malloc(sn + 1);
+        if (!str) exit(-1);
+        strncpy(str, name, sn);
+        strncat(str, ":", sn);
+        strncat(str, part, sn);
+        str[sn] = '\0';
+     }
+   ed->text.filter.sources = eina_list_append(ed->text.filter.sources, str);
+
+   // note: this is leaked. not a big deal.
+   part_key = malloc(sizeof(int));
+   *part_key = -1;
+   data_queue_part_lookup(pc, part, part_key);
+
+   free(part);
+   free(name);
 }
 
 
index 419350c..8aec84a 100644 (file)
@@ -1281,7 +1281,7 @@ struct _Edje_Part_Description_Spec_Border
 struct _Edje_Part_Description_Spec_Filter
 {
    const char    *code;
-   Eina_List     *sources;
+   Eina_List     *sources; /* "part" or "buffer:part" */
 };
 
 struct _Edje_Part_Description_Spec_Image
index cf6c187..8ee579e 100644 (file)
@@ -204,8 +204,8 @@ _edje_text_recalc_apply(Edje *ed, Edje_Real_Part *ep,
    char *font2 = NULL;
    char *sfont = NULL;
    int size;
-   const char *filter, *source_name;
-   Eina_List *filter_sources = NULL, *prev_sources = NULL, *li;
+   const char *filter;
+   Eina_List *filter_sources = NULL, *prev_sources = NULL;
    Evas_Coord tw, th;
    Evas_Coord sw, sh;
    int inlined_font = 0, free_text = 0;
@@ -531,15 +531,60 @@ arrange_text:
    /* filters */
    if (filter)
      {
-        eo_do(ep->object,
-              EINA_LIST_FOREACH(prev_sources, li, source_name)
-                evas_obj_text_filter_source_set(source_name, NULL);
+        const char *src1, *src2, *part;
+        Eina_List *li1, *li2;
 
-              EINA_LIST_FOREACH(filter_sources, li, source_name)
+        eo_do(ep->object,
+              evas_obj_text_filter_program_set(filter);
+              /* update sources. really not optimal. lots of strxxx and loops */
+              if (prev_sources != filter_sources)
                 {
-                  Edje_Real_Part *rp = _edje_real_part_get(ed, source_name);
-                  evas_obj_text_filter_source_set(source_name, rp ? rp->object : NULL);
-                };
+                   /* remove sources that are not there anymore
+                    * this O(n^2) loop assumes a very small number of sources */
+                   EINA_LIST_FOREACH(prev_sources, li1, src1)
+                     {
+                        Eina_Bool found = 0;
+                        EINA_LIST_FOREACH(filter_sources, li2, src2)
+                          {
+                             if (!strcmp(src1, src2))
+                               {
+                                  found = 1;
+                                  break;
+                               }
+                          }
+                        if (!found)
+                          {
+                             part = strchr(src1, ':');
+                             if (!part)
+                               evas_obj_text_filter_source_set(src1, NULL);
+                             else
+                               {
+                                  char *name = strdup(src1);
+                                  name[part - src1] = 0;
+                                  evas_obj_text_filter_source_set(name, NULL);
+                                  free(name);
+                               }
+                          }
+                     }
+                   /* add all sources by part name */
+                   EINA_LIST_FOREACH(filter_sources, li1, src1)
+                     {
+                        Edje_Real_Part *rp;
+                        char *name = NULL;
+                        if ((part = strchr(src1, ':')) != NULL)
+                          {
+                             name = strdup(src1);
+                             name[part - src1] = 0;
+                             part++;
+                          }
+                        else
+                          part = src1;
+                        rp = _edje_real_part_get(ed, part);
+                        evas_obj_text_filter_source_set(name ? name : part, rp ? rp->object : NULL);
+                        free(name);
+                     }
+                 }
+              /* pass edje state for transitions */
               if (ep->param2)
                 {
                    evas_obj_text_filter_state_set(chosen_desc->common.state.name, chosen_desc->common.state.value,
@@ -551,7 +596,7 @@ arrange_text:
                    evas_obj_text_filter_state_set(chosen_desc->common.state.name, chosen_desc->common.state.value,
                                                   NULL, 0.0, state_val);
                 }
-              evas_obj_text_filter_program_set(filter));
+              );
      }
    else
      eo_do(ep->object, evas_obj_text_filter_program_set(NULL));
index f517a97..80a7e75 100644 (file)
@@ -2495,6 +2495,12 @@ _evas_text_filter_source_set(Eo *eo_obj, Evas_Text_Data *o, const char *name, Ev
    if (!source && !o->cur.filter->sources)
      return;
 
+   if (o->cur.filter->sources)
+     {
+        pb_old = eina_hash_find(o->cur.filter->sources, name);
+        if (pb_old && (pb_old->eo_source == eo_source)) return;
+     }
+
    fcow = eina_cow_write(evas_object_filter_cow, (const Eina_Cow_Data**)&o->cur.filter);
 
    if (!fcow->sources)
@@ -2502,15 +2508,8 @@ _evas_text_filter_source_set(Eo *eo_obj, Evas_Text_Data *o, const char *name, Ev
         fcow->sources = eina_hash_string_small_new
               (EINA_FREE_CB(_filter_source_hash_free_cb));
      }
-   else
-     {
-        pb_old = eina_hash_find(fcow->sources, name);
-        if (pb_old)
-          {
-             if (pb_old->eo_source == eo_source) goto update;
-             eina_hash_del(fcow->sources, name, pb_old);
-          }
-     }
+   else if (pb_old)
+     eina_hash_del(fcow->sources, name, pb_old);
 
    if (!source)
      {
@@ -2530,16 +2529,19 @@ _evas_text_filter_source_set(Eo *eo_obj, Evas_Text_Data *o, const char *name, Ev
    pb->eo_source = eo_source;
    pb->name = eina_stringshare_add(name);
 
-   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy,
-                        Evas_Object_Proxy_Data, source_write)
-     if (!eina_list_data_find(source_write->proxies, eo_obj))
-       source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
-   EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
+   if (!eina_list_data_find(source->proxy->proxies, eo_obj))
+     {
+        EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, source->proxy, Evas_Object_Proxy_Data, source_write)
+          source_write->proxies = eina_list_append(source_write->proxies, eo_obj);
+        EINA_COW_WRITE_END(evas_object_proxy_cow, source->proxy, source_write)
+     }
 
-   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy,
-                        Evas_Object_Proxy_Data, proxy_write)
-     proxy_write->is_proxy = EINA_TRUE;
-   EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
+   if (!obj->proxy->is_proxy)
+     {
+        EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, obj->proxy, Evas_Object_Proxy_Data, proxy_write)
+          proxy_write->is_proxy = EINA_TRUE;
+        EINA_COW_WRITE_END(evas_object_proxy_cow, obj->proxy, proxy_write)
+     }
 
    eina_hash_add(fcow->sources, pb->name, pb);
    evas_filter_program_source_set_all(fcow->chain, fcow->sources);
index 7bf812b..f25f82a 100644 (file)
@@ -856,7 +856,7 @@ _lua_buffer_new(lua_State *L)
    instr->type = EVAS_FILTER_MODE_BUFFER;
    instr->parse_run = _buffer_instruction_parse_run;
    _instruction_param_seq_add(instr, "type", VT_STRING, "rgba");
-   _instruction_param_seq_add(instr, "src", VT_BUFFER, NULL);
+   _instruction_param_seq_add(instr, "src", VT_STRING, NULL);
 
    // drop "buffer" metatable
    _lua_implicit_metatable_drop(L, _lua_buffer_meta);