Index: third_party/harfbuzz-ng/src/hb-shape-plan.cc |
diff --git a/third_party/harfbuzz-ng/src/hb-shape-plan.cc b/third_party/harfbuzz-ng/src/hb-shape-plan.cc |
index 13544056d722410f89b7e14a05d3632082b39fb6..e354f2917634b2edfb15f1ef252a0e858ec3e18e 100644 |
--- a/third_party/harfbuzz-ng/src/hb-shape-plan.cc |
+++ b/third_party/harfbuzz-ng/src/hb-shape-plan.cc |
@@ -107,18 +107,27 @@ hb_shape_plan_create (hb_face_t *face, |
assert (props->direction != HB_DIRECTION_INVALID); |
hb_shape_plan_t *shape_plan; |
+ hb_feature_t *features = NULL; |
if (unlikely (!face)) |
face = hb_face_get_empty (); |
if (unlikely (!props || hb_object_is_inert (face))) |
return hb_shape_plan_get_empty (); |
- if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) |
+ if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t)))) |
return hb_shape_plan_get_empty (); |
+ if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) { |
+ free (features); |
+ return hb_shape_plan_get_empty (); |
+ } |
hb_face_make_immutable (face); |
shape_plan->default_shaper_list = shaper_list == NULL; |
shape_plan->face_unsafe = face; |
shape_plan->props = *props; |
+ shape_plan->num_user_features = num_user_features; |
+ shape_plan->user_features = features; |
+ if (num_user_features) |
+ memcpy (features, user_features, num_user_features * sizeof (hb_feature_t)); |
hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list); |
@@ -147,6 +156,9 @@ hb_shape_plan_get_empty (void) |
NULL, /* shaper_func */ |
NULL, /* shaper_name */ |
+ NULL, /* user_features */ |
+ 0, /* num_user_featurs */ |
+ |
{ |
#define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, |
#include "hb-shaper-list.hh" |
@@ -190,6 +202,8 @@ hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) |
#include "hb-shaper-list.hh" |
#undef HB_SHAPER_IMPLEMENT |
+ free (shape_plan->user_features); |
+ |
free (shape_plan); |
} |
@@ -299,23 +313,55 @@ hb_shape_plan_hash (const hb_shape_plan_t *shape_plan) |
} |
#endif |
-/* TODO no user-feature caching for now. */ |
+/* User-feature caching is currently somewhat dumb: |
+ * it only finds matches where the feature array is identical, |
+ * not cases where the feature lists would be compatible for plan purposes |
+ * but have different ranges, for example. |
+ */ |
struct hb_shape_plan_proposal_t |
{ |
const hb_segment_properties_t props; |
const char * const *shaper_list; |
+ const hb_feature_t *user_features; |
+ unsigned int num_user_features; |
hb_shape_func_t *shaper_func; |
}; |
+static inline hb_bool_t |
+hb_shape_plan_user_features_match (const hb_shape_plan_t *shape_plan, |
+ const hb_shape_plan_proposal_t *proposal) |
+{ |
+ if (proposal->num_user_features != shape_plan->num_user_features) return false; |
+ for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++) |
+ if (proposal->user_features[i].tag != shape_plan->user_features[i].tag || |
+ proposal->user_features[i].value != shape_plan->user_features[i].value || |
+ proposal->user_features[i].start != shape_plan->user_features[i].start || |
+ proposal->user_features[i].end != shape_plan->user_features[i].end) return false; |
+ return true; |
+} |
+ |
static hb_bool_t |
hb_shape_plan_matches (const hb_shape_plan_t *shape_plan, |
const hb_shape_plan_proposal_t *proposal) |
{ |
return hb_segment_properties_equal (&shape_plan->props, &proposal->props) && |
+ hb_shape_plan_user_features_match (shape_plan, proposal) && |
((shape_plan->default_shaper_list && proposal->shaper_list == NULL) || |
(shape_plan->shaper_func == proposal->shaper_func)); |
} |
+static inline hb_bool_t |
+hb_non_global_user_features_present (const hb_feature_t *user_features, |
+ unsigned int num_user_features) |
+{ |
+ while (num_user_features) |
+ if (user_features->start != 0 || user_features->end != (unsigned int) -1) |
+ return true; |
+ else |
+ num_user_features--, user_features++; |
+ return false; |
+} |
+ |
/** |
* hb_shape_plan_create_cached: |
* @face: |
@@ -337,12 +383,11 @@ hb_shape_plan_create_cached (hb_face_t *face, |
unsigned int num_user_features, |
const char * const *shaper_list) |
{ |
- if (num_user_features) |
- return hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list); |
- |
hb_shape_plan_proposal_t proposal = { |
*props, |
shaper_list, |
+ user_features, |
+ num_user_features, |
NULL |
}; |
@@ -380,6 +425,11 @@ retry: |
hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list); |
+ /* Don't add the plan to the cache if there were user features with non-global ranges */ |
+ |
+ if (hb_non_global_user_features_present (user_features, num_user_features)) |
+ return shape_plan; |
+ |
hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t)); |
if (unlikely (!node)) |
return shape_plan; |