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.