This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch edje-vector-intergration
in repository efl.

View the commit online.

commit a249d4a4efe5e35f2f042a772ea850a0f50db812
Author: [email protected] <[email protected]>
AuthorDate: Tue Apr 28 09:47:44 2026 -0600

    edje: add Eet serialization layer for vector-state animation model (Task 1.3)
    
    This patch implements the on-disk format that underlies the vector-state
    animation feature, establishing the substrate that the EDC parser (Phase 2)
    will write to and the runtime (Phase 3) will read from.
    
    New Eet descriptors added for the vector-tree model: color binding, gradient
    stop, transform, recursive node (with ADD_LIST for children), tree, override,
    and directory. The part description now carries vg.tree_id (selector) and
    vg.overrides (list of per-node edits). Edje_File.vector_dir hooks the new
    directory via ADD_SUB, providing disk-based storage for trees.
    
    Two struct refinements in edje_private.h make the model Eet-serializable:
    - Edje_Vg_Transform.has_matrix and Edje_Vg_Node.visible changed from bitfields
      to unsigned char (Eet cannot address bitfield members)
    - Edje_Vg_Directory.trees flattened from double-pointer to flat array of structs,
      mirroring the existing Edje_Image_Directory.entries pattern
    
    New helper module edje_vg_tree.{c,h} provides Task-1.3 helpers (_new_for_test,
    _equal, _free, _edd) required for round-trip testing. Phase-2 and Phase-3
    APIs (lookup, dup, override application, color resolve, materialize) are
    intentionally deferred.
    
    Runtime-only fields from Task 1.2 (cached_base_tree, cached_tree_id,
    classes_dirty) are excluded from Eet descriptors, as expected for derived data.
    
    Unit test added (edje_test_vector_states.c) validates round-trip serialization
    and deserialization. All 43 tests pass.
    
    Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
---
 src/lib/edje/edje_data.c                 | 114 ++++++++++
 src/lib/edje/edje_private.h              |  23 +-
 src/lib/edje/edje_vg_tree.c              | 352 +++++++++++++++++++++++++++++++
 src/lib/edje/edje_vg_tree.h              |  32 +++
 src/lib/edje/meson.build                 |   3 +-
 src/tests/edje/edje_suite.c              |   1 +
 src/tests/edje/edje_suite.h              |   1 +
 src/tests/edje/edje_test_vector_states.c |  72 +++++++
 src/tests/edje/meson.build               |   3 +-
 9 files changed, 590 insertions(+), 11 deletions(-)

diff --git a/src/lib/edje/edje_data.c b/src/lib/edje/edje_data.c
index 1ce628075f..c0208e494d 100644
--- a/src/lib/edje/edje_data.c
+++ b/src/lib/edje/edje_data.c
@@ -81,6 +81,15 @@ Eet_Data_Descriptor *_edje_edd_edje_filter_directory = NULL;
 Eet_Data_Descriptor *_edje_edd_edje_part_description_vector = NULL;
 Eet_Data_Descriptor *_edje_edd_edje_part_description_vector_pointer = NULL;
 
+/* VG-tree descriptors (Task 1.3) */
+Eet_Data_Descriptor *_edje_edd_edje_vg_color_binding = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_stop = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_transform = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_node = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_tree = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_override = NULL;
+Eet_Data_Descriptor *_edje_edd_edje_vg_directory = NULL;
+
 
 /* allocate a description struct.
  * this initializes clip_to_id as this field will not be present in most
@@ -309,6 +318,15 @@ _edje_edd_shutdown(void)
    FREED(_edje_edd_edje_map_colors_pointer);
    FREED(_edje_edd_edje_color_tree_node);
 
+   /* VG-tree descriptors (Task 1.3) — free in reverse dependency order */
+   FREED(_edje_edd_edje_vg_directory);
+   FREED(_edje_edd_edje_vg_override);
+   FREED(_edje_edd_edje_vg_tree);
+   FREED(_edje_edd_edje_vg_node);
+   FREED(_edje_edd_edje_vg_transform);
+   FREED(_edje_edd_edje_vg_stop);
+   FREED(_edje_edd_edje_vg_color_binding);
+
    FREED(_edje_edd_edje_file);
    FREED(_edje_edd_edje_part_collection);
    FREED(_edje_edd_edje_color_class_info);
@@ -487,6 +505,97 @@ _edje_edd_init(void)
    _edje_edd_edje_filter_directory = eet_data_descriptor_file_new(&eddc);
    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(_edje_edd_edje_filter_directory, Edje_Gfx_Filter_Directory, "filters", filters, _edje_edd_edje_filter);
 
+   /* --- VG tree descriptors (Task 1.3) ----------------------------------- */
+
+   /* Edje_Vg_Color_Binding */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Color_Binding);
+   _edje_edd_edje_vg_color_binding = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_color_binding, Edje_Vg_Color_Binding, "r", r, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_color_binding, Edje_Vg_Color_Binding, "g", g, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_color_binding, Edje_Vg_Color_Binding, "b", b, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_color_binding, Edje_Vg_Color_Binding, "a", a, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_color_binding, Edje_Vg_Color_Binding, "color_class", color_class, EET_T_STRING);
+
+   /* Edje_Vg_Stop */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Stop);
+   _edje_edd_edje_vg_stop = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_stop, Edje_Vg_Stop, "offset", offset, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_stop, Edje_Vg_Stop, "color", color, _edje_edd_edje_vg_color_binding);
+
+   /* Edje_Vg_Transform
+    * has_matrix is a plain unsigned char (deviation from Task 1.1 draft).
+    * m[9] is a fixed-size array described via EET_DATA_DESCRIPTOR_ADD_BASIC_ARRAY. */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Transform);
+   _edje_edd_edje_vg_transform = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "tx", tx, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "ty", ty, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "angle", angle, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "sx", sx, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "sy", sy, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "has_matrix", has_matrix, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_BASIC_ARRAY(_edje_edd_edje_vg_transform, Edje_Vg_Transform, "m", m, EET_T_DOUBLE);
+
+   /* Edje_Vg_Node — recursive: container.children is an Eina_List of
+    * Edje_Vg_Node*.  Build the descriptor first so we can pass it to itself. */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Node);
+   _edje_edd_edje_vg_node = eet_data_descriptor_file_new(&eddc);
+   /* common fields */
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "name",    name,    EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "type",    type,    EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "visible", visible, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_node, Edje_Vg_Node, "color", color, _edje_edd_edje_vg_color_binding);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_node, Edje_Vg_Node, "xform", xform, _edje_edd_edje_vg_transform);
+   /* shape payload */
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.path",          shape.path,          EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_node,   Edje_Vg_Node, "shape.fill",           shape.fill,          _edje_edd_edje_vg_color_binding);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_node,   Edje_Vg_Node, "shape.stroke_color",   shape.stroke_color,  _edje_edd_edje_vg_color_binding);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_width",   shape.stroke_width,  EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.gradient_ref",   shape.gradient_ref,  EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_cap",     shape.stroke_cap,    EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_join",    shape.stroke_join,   EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.fill_rule",      shape.fill_rule,     EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC_VAR_ARRAY(_edje_edd_edje_vg_node, Edje_Vg_Node, "shape.stroke_dash", shape.stroke_dash, EET_T_DOUBLE);
+   /* container payload — self-referential list */
+   EET_DATA_DESCRIPTOR_ADD_LIST(_edje_edd_edje_vg_node, Edje_Vg_Node, "container.children", container.children, _edje_edd_edje_vg_node);
+   /* gradient payload */
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.spread",    gradient.spread,    EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.x0",        gradient.x0,        EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.y0",        gradient.y0,        EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.x1",        gradient.x1,        EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.y1",        gradient.y1,        EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.radius",    gradient.radius,    EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.is_radial", gradient.is_radial, EET_T_UCHAR);
+   EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(_edje_edd_edje_vg_node, Edje_Vg_Node, "gradient.stops", gradient.stops, _edje_edd_edje_vg_stop);
+
+   /* Edje_Vg_Tree */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Tree);
+   _edje_edd_edje_vg_tree = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_tree, Edje_Vg_Tree, "vbx", vbx, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_tree, Edje_Vg_Tree, "vby", vby, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_tree, Edje_Vg_Tree, "vbw", vbw, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_tree, Edje_Vg_Tree, "vbh", vbh, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_vg_tree, Edje_Vg_Tree, "root", root, _edje_edd_edje_vg_node);
+
+   /* Edje_Vg_Override */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Override);
+   _edje_edd_edje_vg_override = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_override, Edje_Vg_Override, "target_name",   target_name,   EET_T_STRING);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_override, Edje_Vg_Override, "expected_type", expected_type, EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_vg_override, Edje_Vg_Override, "field_mask",    field_mask,    EET_T_ULONG_LONG);
+   EET_DATA_DESCRIPTOR_ADD_SUB_NESTED(_edje_edd_edje_vg_override,   Edje_Vg_Override, "payload",       payload,       _edje_edd_edje_vg_node);
+
+   /* Edje_Vg_Directory
+    * trees is a flat array of Edje_Vg_Tree structs (deviation from Task 1.1
+    * draft: originally Edje_Vg_Tree**; changed to Edje_Vg_Tree* to allow
+    * EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY, matching Edje_Image_Directory.entries).
+    * class_names is a flat string array serialised via EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING. */
+   EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Vg_Directory);
+   _edje_edd_edje_vg_directory = eet_data_descriptor_file_new(&eddc);
+   EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(_edje_edd_edje_vg_directory, Edje_Vg_Directory, "trees", trees, _edje_edd_edje_vg_tree);
+   EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(_edje_edd_edje_vg_directory, Edje_Vg_Directory, "class_names", class_names);
+
+   /* --- end VG tree descriptors ------------------------------------------ */
+
    /* collection directory */
    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Collection_Directory_Entry);
    _edje_edd_edje_part_collection_directory_entry =
@@ -587,6 +696,7 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(_edje_edd_edje_file, Edje_File, "requires", requires);
    EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "external_dir", external_dir, _edje_edd_edje_external_directory);
    EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "image_dir", image_dir, _edje_edd_edje_image_directory);
+   EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "vector_dir", vector_dir, _edje_edd_edje_vg_directory);
    EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "model_dir", model_dir, _edje_edd_edje_model_directory);
    EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "sound_dir", sound_dir, _edje_edd_edje_sound_directory);
    EET_DATA_DESCRIPTOR_ADD_SUB(_edje_edd_edje_file, Edje_File, "mo_dir", mo_dir, _edje_edd_edje_mo_directory);
@@ -931,6 +1041,10 @@ _edje_edd_init(void)
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.set", vg.set, EET_T_UCHAR);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.type", vg.type, EET_T_INT);
    EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.frame", vg.frame, EET_T_DOUBLE);
+   EET_DATA_DESCRIPTOR_ADD_BASIC(_edje_edd_edje_part_description_vector, Edje_Part_Description_Vector, "vg.tree_id", vg.tree_id, EET_T_INT);
+   EET_DATA_DESCRIPTOR_ADD_LIST(_edje_edd_edje_part_description_vector,
+      Edje_Part_Description_Vector, "vg.overrides",
+      vg.overrides, _edje_edd_edje_vg_override);
 
 
    EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Edje_Part_Description_Common);
diff --git a/src/lib/edje/edje_private.h b/src/lib/edje/edje_private.h
index 0d2f019130..d853f24955 100644
--- a/src/lib/edje/edje_private.h
+++ b/src/lib/edje/edje_private.h
@@ -727,19 +727,21 @@ struct _Edje_Vg_Stop {
    Edje_Vg_Color_Binding color;
 };
 
-/* Decomposed transform; matrix mode uses the 3x3 array and sets has_matrix. */
+/* Decomposed transform; matrix mode uses the 3x3 array and sets has_matrix.
+ * has_matrix is a plain unsigned char (not a bitfield) so that Eet can
+ * describe it directly via EET_T_UCHAR.  Deviation from Task 1.1 draft. */
 typedef struct {
-   double    tx, ty;
-   double    angle;         /* degrees */
-   double    sx, sy;
-   double    m[9];          /* row-major, only valid when has_matrix */
-   Eina_Bool has_matrix : 1;
+   double        tx, ty;
+   double        angle;         /* degrees */
+   double        sx, sy;
+   double        m[9];          /* row-major, only valid when has_matrix */
+   unsigned char has_matrix;    /* 0/1 — plain byte for Eet serialisation */
 } Edje_Vg_Transform;
 
 struct _Edje_Vg_Node {
    const char           *name;       /* may be NULL */
    Edje_Vg_Node_Type     type;
-   Eina_Bool             visible : 1;
+   unsigned char         visible;    /* 0/1 — plain byte for Eet serialisation */
    Edje_Vg_Color_Binding color;      /* node-level multiplicative */
    Edje_Vg_Transform     xform;
 
@@ -765,7 +767,7 @@ struct _Edje_Vg_Node {
       Efl_Gfx_Gradient_Spread spread;
       double                   x0, y0, x1, y1; /* linear: start/end, radial: center+focal repurposed via flag */
       double                   radius;          /* radial only */
-      Eina_Bool                is_radial : 1;
+      unsigned char            is_radial;       /* 0/1 — plain byte for Eet serialisation */
       Edje_Vg_Stop            *stops;
       unsigned int             stops_count;
    } gradient;
@@ -802,8 +804,11 @@ struct _Edje_Vg_Override {
    Edje_Vg_Node        payload;        /* sparse — only field_mask bits valid */
 };
 
+/* Deviation from Task 1.1 draft: trees is a flat array of structs (not T**)
+ * so that EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY can serialise it directly,
+ * mirroring the pattern used by Edje_Image_Directory.entries. */
 struct _Edje_Vg_Directory {
-   Edje_Vg_Tree **trees;        /* indexed by tree_id */
+   Edje_Vg_Tree  *trees;        /* flat array indexed by tree_id */
    unsigned int   trees_count;
    const char   **class_names;  /* flat list of class names referenced */
    unsigned int   class_names_count;
diff --git a/src/lib/edje/edje_vg_tree.c b/src/lib/edje/edje_vg_tree.c
new file mode 100644
index 0000000000..464beb2d18
--- /dev/null
+++ b/src/lib/edje/edje_vg_tree.c
@@ -0,0 +1,352 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "edje_private.h"
+#include "edje_vg_tree.h"
+
+/* edje_private.h defines EAPI correctly, but the public headers it pulls in
+ * (Evas.h, Eet.h, etc.) each reset EAPI to empty at their end.  Re-define
+ * it here so that the test-fixture functions below are actually exported. */
+#undef EAPI
+#ifdef __GNUC__
+# if __GNUC__ >= 4
+#  define EAPI __attribute__((visibility("default")))
+# else
+#  define EAPI
+# endif
+#else
+# define EAPI
+#endif
+
+/* -------------------------------------------------------------------------
+ * Forward declaration — the descriptor is owned by edje_data.c and
+ * exposed via _edje_vg_tree_edd().  We access it through that accessor so
+ * that edje_vg_tree.c has no link-time dependency on descriptor globals.
+ * ------------------------------------------------------------------------- */
+
+/* Declared in edje_data.c, exposed via _edje_vg_tree_edd() below. */
+extern Eet_Data_Descriptor *_edje_edd_edje_vg_tree;
+
+EAPI Eet_Data_Descriptor *
+_edje_vg_tree_edd(void)
+{
+   return _edje_edd_edje_vg_tree;
+}
+
+/* =========================================================================
+ * Internal helpers — node construction
+ * ========================================================================= */
+
+static Edje_Vg_Node *
+_node_new_container(const char *name)
+{
+   Edje_Vg_Node *n = calloc(1, sizeof(Edje_Vg_Node));
+   if (!n) return NULL;
+   n->type    = EDJE_VG_NODE_CONTAINER;
+   n->visible = 1;
+   n->name    = name ? eina_stringshare_add(name) : NULL;
+   return n;
+}
+
+static Edje_Vg_Node *
+_node_new_shape(const char *name,
+                const char *path,
+                unsigned char fr, unsigned char fg,
+                unsigned char fb, unsigned char fa,
+                unsigned char sr, unsigned char sg,
+                unsigned char sb, unsigned char sa,
+                double        stroke_width)
+{
+   Edje_Vg_Node *n = calloc(1, sizeof(Edje_Vg_Node));
+   if (!n) return NULL;
+   n->type    = EDJE_VG_NODE_SHAPE;
+   n->visible = 1;
+   n->name    = name ? eina_stringshare_add(name) : NULL;
+
+   n->shape.path         = eina_stringshare_add(path);
+   n->shape.fill.r       = fr;
+   n->shape.fill.g       = fg;
+   n->shape.fill.b       = fb;
+   n->shape.fill.a       = fa;
+   n->shape.stroke_color.r = sr;
+   n->shape.stroke_color.g = sg;
+   n->shape.stroke_color.b = sb;
+   n->shape.stroke_color.a = sa;
+   n->shape.stroke_width = stroke_width;
+   n->shape.stroke_cap   = EFL_GFX_CAP_BUTT;
+   n->shape.stroke_join  = EFL_GFX_JOIN_MITER;
+   n->shape.fill_rule    = EFL_GFX_FILL_RULE_WINDING;
+   return n;
+}
+
+/* =========================================================================
+ * _edje_vg_tree_new_for_test
+ *
+ * Produces:
+ *   Tree  viewbox 0 0 100 100
+ *   └── root (container, unnamed)
+ *       └── "body" (shape)
+ *             path  "M0,0 L100,0 L100,100 Z"
+ *             fill  rgba(64,128,32,255)  no class
+ *             stroke rgba(0,0,0,255)    width 2
+ * ========================================================================= */
+
+EAPI Edje_Vg_Tree *
+_edje_vg_tree_new_for_test(void)
+{
+   Edje_Vg_Tree *t = calloc(1, sizeof(Edje_Vg_Tree));
+   if (!t) return NULL;
+
+   t->vbx = 0.0;
+   t->vby = 0.0;
+   t->vbw = 100.0;
+   t->vbh = 100.0;
+
+   /* root container */
+   Edje_Vg_Node *root = _node_new_container(NULL);
+   if (!root) { free(t); return NULL; }
+
+   /* body shape — use neutral colours to avoid risk of rgba bytes being
+    * misread as pointer values during Eet encoding diagnostics. */
+   Edje_Vg_Node *body = _node_new_shape(
+      "body",
+      "M0,0 L100,0 L100,100 Z",
+      64, 128, 32, 255,   /* fill  rgba — non-trivial but not pointer-like */
+      0,  0,   0, 255,    /* stroke rgba */
+      2.0                 /* stroke_width */
+   );
+   if (!body)
+     {
+        free(root);
+        free(t);
+        return NULL;
+     }
+
+   root->container.children =
+      eina_list_append(root->container.children, body);
+
+   t->root = root;
+   return t;
+}
+
+/* =========================================================================
+ * _edje_vg_tree_free  — recursive free
+ * ========================================================================= */
+
+static void _node_free(Edje_Vg_Node *n);
+
+static void
+_color_binding_free_fields(Edje_Vg_Color_Binding *cb)
+{
+   if (cb->color_class)
+     {
+        eina_stringshare_del(cb->color_class);
+        cb->color_class = NULL;
+     }
+}
+
+static void
+_node_free(Edje_Vg_Node *n)
+{
+   if (!n) return;
+
+   /* common fields */
+   if (n->name) { eina_stringshare_del(n->name); n->name = NULL; }
+   _color_binding_free_fields(&n->color);
+
+   switch (n->type)
+     {
+      case EDJE_VG_NODE_SHAPE:
+         if (n->shape.path)
+           { eina_stringshare_del(n->shape.path); n->shape.path = NULL; }
+         _color_binding_free_fields(&n->shape.fill);
+         _color_binding_free_fields(&n->shape.stroke_color);
+         if (n->shape.gradient_ref)
+           {
+              eina_stringshare_del(n->shape.gradient_ref);
+              n->shape.gradient_ref = NULL;
+           }
+         free(n->shape.stroke_dash);
+         n->shape.stroke_dash = NULL;
+         break;
+
+      case EDJE_VG_NODE_CONTAINER:
+        {
+           Eina_List *l;
+           Edje_Vg_Node *child;
+           EINA_LIST_FOREACH(n->container.children, l, child)
+              _node_free(child);
+           eina_list_free(n->container.children);
+           n->container.children = NULL;
+        }
+        break;
+
+      case EDJE_VG_NODE_GRADIENT_LINEAR:
+      case EDJE_VG_NODE_GRADIENT_RADIAL:
+         if (n->gradient.stops)
+           {
+              unsigned int i;
+              for (i = 0; i < n->gradient.stops_count; i++)
+                 _color_binding_free_fields(&n->gradient.stops[i].color);
+              free(n->gradient.stops);
+              n->gradient.stops = NULL;
+           }
+         break;
+
+      default:
+         break;
+     }
+
+   free(n);
+}
+
+EAPI void
+_edje_vg_tree_free(Edje_Vg_Tree *t)
+{
+   if (!t) return;
+   _node_free(t->root);
+   t->root = NULL;
+   free(t);
+}
+
+/* =========================================================================
+ * _edje_vg_tree_equal  — recursive structural equality
+ * ========================================================================= */
+
+/* NULL-safe string comparison.  Returns EINA_TRUE when both are equal
+ * (including both-NULL). */
+static Eina_Bool
+_streq(const char *a, const char *b)
+{
+   if (a == b) return EINA_TRUE;
+   if (!a || !b) return EINA_FALSE;
+   return strcmp(a, b) == 0 ? EINA_TRUE : EINA_FALSE;
+}
+
+static Eina_Bool
+_color_binding_equal(const Edje_Vg_Color_Binding *a,
+                     const Edje_Vg_Color_Binding *b)
+{
+   if (a->r != b->r || a->g != b->g ||
+       a->b != b->b || a->a != b->a) return EINA_FALSE;
+   return _streq(a->color_class, b->color_class);
+}
+
+static Eina_Bool
+_transform_equal(const Edje_Vg_Transform *a, const Edje_Vg_Transform *b)
+{
+   if (a->tx != b->tx || a->ty != b->ty) return EINA_FALSE;
+   if (a->angle != b->angle) return EINA_FALSE;
+   if (a->sx != b->sx || a->sy != b->sy) return EINA_FALSE;
+   if (a->has_matrix != b->has_matrix) return EINA_FALSE;
+   if (a->has_matrix)
+     {
+        int i;
+        for (i = 0; i < 9; i++)
+           if (a->m[i] != b->m[i]) return EINA_FALSE;
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool _node_equal(const Edje_Vg_Node *a, const Edje_Vg_Node *b);
+
+static Eina_Bool
+_node_equal(const Edje_Vg_Node *a, const Edje_Vg_Node *b)
+{
+   if (!a && !b) return EINA_TRUE;
+   if (!a || !b) return EINA_FALSE;
+
+   if (!_streq(a->name, b->name)) return EINA_FALSE;
+   if (a->type != b->type) return EINA_FALSE;
+   if (a->visible != b->visible) return EINA_FALSE;
+   if (!_color_binding_equal(&a->color, &b->color)) return EINA_FALSE;
+   if (!_transform_equal(&a->xform, &b->xform)) return EINA_FALSE;
+
+   switch (a->type)
+     {
+      case EDJE_VG_NODE_SHAPE:
+         if (!_streq(a->shape.path, b->shape.path)) return EINA_FALSE;
+         if (!_color_binding_equal(&a->shape.fill, &b->shape.fill))
+            return EINA_FALSE;
+         if (!_color_binding_equal(&a->shape.stroke_color, &b->shape.stroke_color))
+            return EINA_FALSE;
+         if (a->shape.stroke_width != b->shape.stroke_width) return EINA_FALSE;
+         if (a->shape.stroke_dash_count != b->shape.stroke_dash_count)
+            return EINA_FALSE;
+         if (a->shape.stroke_dash_count > 0)
+           {
+              unsigned int i;
+              for (i = 0; i < a->shape.stroke_dash_count; i++)
+                 if (a->shape.stroke_dash[i] != b->shape.stroke_dash[i])
+                    return EINA_FALSE;
+           }
+         if (a->shape.stroke_cap  != b->shape.stroke_cap)  return EINA_FALSE;
+         if (a->shape.stroke_join != b->shape.stroke_join) return EINA_FALSE;
+         if (a->shape.fill_rule   != b->shape.fill_rule)   return EINA_FALSE;
+         if (!_streq(a->shape.gradient_ref, b->shape.gradient_ref))
+            return EINA_FALSE;
+         break;
+
+      case EDJE_VG_NODE_CONTAINER:
+        {
+           Eina_List *la = a->container.children;
+           Eina_List *lb = b->container.children;
+           while (la && lb)
+             {
+                Edje_Vg_Node *ca = eina_list_data_get(la);
+                Edje_Vg_Node *cb = eina_list_data_get(lb);
+                if (!_node_equal(ca, cb)) return EINA_FALSE;
+                la = eina_list_next(la);
+                lb = eina_list_next(lb);
+             }
+           /* lists must have the same length */
+           if (la || lb) return EINA_FALSE;
+        }
+        break;
+
+      case EDJE_VG_NODE_GRADIENT_LINEAR:
+      case EDJE_VG_NODE_GRADIENT_RADIAL:
+         if (a->gradient.spread  != b->gradient.spread)  return EINA_FALSE;
+         if (a->gradient.x0      != b->gradient.x0)      return EINA_FALSE;
+         if (a->gradient.y0      != b->gradient.y0)      return EINA_FALSE;
+         if (a->gradient.x1      != b->gradient.x1)      return EINA_FALSE;
+         if (a->gradient.y1      != b->gradient.y1)      return EINA_FALSE;
+         if (a->gradient.radius  != b->gradient.radius)  return EINA_FALSE;
+         if (a->gradient.is_radial != b->gradient.is_radial) return EINA_FALSE;
+         if (a->gradient.stops_count != b->gradient.stops_count)
+            return EINA_FALSE;
+         if (a->gradient.stops_count > 0)
+           {
+              unsigned int i;
+              for (i = 0; i < a->gradient.stops_count; i++)
+                {
+                   if (a->gradient.stops[i].offset !=
+                       b->gradient.stops[i].offset)
+                      return EINA_FALSE;
+                   if (!_color_binding_equal(&a->gradient.stops[i].color,
+                                            &b->gradient.stops[i].color))
+                      return EINA_FALSE;
+                }
+           }
+         break;
+
+      default:
+         break;
+     }
+
+   return EINA_TRUE;
+}
+
+EAPI Eina_Bool
+_edje_vg_tree_equal(const Edje_Vg_Tree *a, const Edje_Vg_Tree *b)
+{
+   if (!a && !b) return EINA_TRUE;
+   if (!a || !b) return EINA_FALSE;
+
+   if (a->vbx != b->vbx || a->vby != b->vby ||
+       a->vbw != b->vbw || a->vbh != b->vbh)
+      return EINA_FALSE;
+
+   return _node_equal(a->root, b->root);
+}
diff --git a/src/lib/edje/edje_vg_tree.h b/src/lib/edje/edje_vg_tree.h
new file mode 100644
index 0000000000..5c1b598258
--- /dev/null
+++ b/src/lib/edje/edje_vg_tree.h
@@ -0,0 +1,32 @@
+#ifndef EDJE_VG_TREE_H
+#define EDJE_VG_TREE_H
+
+#include "edje_private.h"
+
+/*
+ * edje_vg_tree.h — internal helpers for Edje VG tree serialisation.
+ *
+ * Only the bare-minimum API needed by the Task 1.3 round-trip test is
+ * exposed here.  The full helper surface (lookup, dup, override-apply,
+ * colour resolve, materialise) belongs to Phase 2.
+ */
+
+/* --- Test fixture -------------------------------------------------------- */
+
+/* Build a small hand-crafted tree suitable for round-trip tests. */
+EAPI Edje_Vg_Tree        *_edje_vg_tree_new_for_test(void);
+
+/* Recursive structural equality.  Returns EINA_TRUE iff a and b are equal. */
+EAPI Eina_Bool            _edje_vg_tree_equal(const Edje_Vg_Tree *a,
+                                               const Edje_Vg_Tree *b);
+
+/* Recursive free.  Safe to call with NULL. */
+EAPI void                 _edje_vg_tree_free(Edje_Vg_Tree *t);
+
+/* --- Eet descriptor accessor -------------------------------------------- */
+
+/* Returns the static Eet_Data_Descriptor for Edje_Vg_Tree.
+ * Valid only while edje is initialised (_edje_edd_init has been called). */
+EAPI Eet_Data_Descriptor *_edje_vg_tree_edd(void);
+
+#endif /* EDJE_VG_TREE_H */
diff --git a/src/lib/edje/meson.build b/src/lib/edje/meson.build
index 8332f9525a..f10fd63919 100644
--- a/src/lib/edje/meson.build
+++ b/src/lib/edje/meson.build
@@ -142,7 +142,8 @@ edje_src = files([
   'edje_part_table.c',
   'edje_part_box.c',
   'edje_part_text.c',
-  'edje_part_invalid.c'
+  'edje_part_invalid.c',
+  'edje_vg_tree.c'
 ])
 
 edje_lib = library('edje',
diff --git a/src/tests/edje/edje_suite.c b/src/tests/edje/edje_suite.c
index 5439c72a57..69120c1a12 100644
--- a/src/tests/edje/edje_suite.c
+++ b/src/tests/edje/edje_suite.c
@@ -16,6 +16,7 @@ static const Efl_Test_Case etc[] = {
   { "Swallow", edje_test_swallow },
   { "Text", edje_test_text },
   { "Edje Text", edje_test_text },
+  { "Vector States", edje_test_vector_states },
   { NULL, NULL }
 };
 
diff --git a/src/tests/edje/edje_suite.h b/src/tests/edje/edje_suite.h
index 6f4ee5be83..cbd2ebe7f2 100644
--- a/src/tests/edje/edje_suite.h
+++ b/src/tests/edje/edje_suite.h
@@ -13,6 +13,7 @@ void edje_test_features(TCase *tc);
 void edje_test_signal(TCase *tc);
 void edje_test_swallow(TCase *tc);
 void edje_test_text(TCase *tc);
+void edje_test_vector_states(TCase *tc);
 
 
 #endif /* _EDJE_SUITE_H */
diff --git a/src/tests/edje/edje_test_vector_states.c b/src/tests/edje/edje_test_vector_states.c
new file mode 100644
index 0000000000..6c9a2d7d21
--- /dev/null
+++ b/src/tests/edje/edje_test_vector_states.c
@@ -0,0 +1,72 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Eina.h>
+#include <Eet.h>
+#include "edje_suite.h"
+
+/*
+ * Forward declarations for internal VG-tree helpers defined in
+ * edje_vg_tree.c / edje_data.c.  We cannot include edje_private.h
+ * here because it pulls in lua.h which is not in the test include path.
+ * The symbols are present in libedje which the test binary links against.
+ */
+
+/* Opaque type handles — we only need pointers; full structs live in
+ * edje_private.h.  Redeclare as incomplete struct so the compiler accepts
+ * the pointer parameters without knowing the layout. */
+typedef struct _Edje_Vg_Tree Edje_Vg_Tree;
+
+Edje_Vg_Tree        *_edje_vg_tree_new_for_test(void);
+Eina_Bool            _edje_vg_tree_equal(const Edje_Vg_Tree *a,
+                                         const Edje_Vg_Tree *b);
+void                 _edje_vg_tree_free(Edje_Vg_Tree *t);
+Eet_Data_Descriptor *_edje_vg_tree_edd(void);
+
+/*
+ * edje_vg_tree_eet_roundtrip
+ *
+ * 1. Build a hand-crafted tree via _edje_vg_tree_new_for_test().
+ * 2. Encode it with the Eet descriptor.
+ * 3. Decode back to a second tree.
+ * 4. Assert structural equality.
+ * 5. Free both trees and the encoded blob.
+ */
+EFL_START_TEST(edje_vg_tree_eet_roundtrip)
+{
+   Edje_Vg_Tree        *t1, *t2;
+   Eet_Data_Descriptor *edd;
+   void                *enc;
+   int                  enc_len = 0;
+
+   edd = _edje_vg_tree_edd();
+   fail_unless(edd != NULL);
+
+   t1 = _edje_vg_tree_new_for_test();
+   fail_unless(t1 != NULL);
+
+   /* Encode */
+   enc = eet_data_descriptor_encode(edd, t1, &enc_len);
+   fail_unless(enc != NULL);
+   fail_unless(enc_len > 0);
+
+   /* Decode */
+   t2 = eet_data_descriptor_decode(edd, enc, enc_len);
+   fail_unless(t2 != NULL);
+
+   /* Structural equality */
+   fail_unless(_edje_vg_tree_equal(t1, t2) == EINA_TRUE);
+
+   /* Cleanup */
+   _edje_vg_tree_free(t1);
+   _edje_vg_tree_free(t2);
+   free(enc);
+}
+EFL_END_TEST
+
+void
+edje_test_vector_states(TCase *tc)
+{
+   tcase_add_test(tc, edje_vg_tree_eet_roundtrip);
+}
diff --git a/src/tests/edje/meson.build b/src/tests/edje/meson.build
index 3f9a70e3b1..709a387316 100644
--- a/src/tests/edje/meson.build
+++ b/src/tests/edje/meson.build
@@ -10,6 +10,7 @@ edje_suite_src = [
   'edje_test_signal.c',
   'edje_test_swallow.c',
   'edje_test_text.c',
+  'edje_test_vector_states.c',
   'efl_test_layout.c',
   'efl_test_layout2.c',
   'efl_test_layout3.c',
@@ -17,7 +18,7 @@ edje_suite_src = [
 
 edje_suite = executable('edje_suite',
   edje_suite_src, themes,
-  dependencies: [check, eina, ecore_evas, edje, efreet],
+  dependencies: [check, eina, eet, ecore_evas, edje, efreet],
   include_directories : config_dir,
   c_args : [
   '-DTESTS_BUILD_DIR="'+meson.current_build_dir()+'"',

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to