2 * Copyright © 2012 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Google Author(s): Behdad Esfahbod
27 #include "hb-shape-plan-private.hh"
28 #include "hb-shaper-private.hh"
29 #include "hb-font-private.hh"
30 #include "hb-buffer-private.hh"
33 #ifndef HB_DEBUG_SHAPE_PLAN
34 #define HB_DEBUG_SHAPE_PLAN (HB_DEBUG+0)
39 hb_shape_plan_plan (hb_shape_plan_t *shape_plan,
40 const hb_feature_t *user_features,
41 unsigned int num_user_features,
43 unsigned int num_coords,
44 const char * const *shaper_list)
46 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
47 "num_features=%d num_coords=%d shaper_list=%p",
52 const hb_shaper_pair_t *shapers = _hb_shapers_get ();
54 #define HB_SHAPER_PLAN(shaper) \
56 if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
57 HB_SHAPER_DATA (shaper, shape_plan) = \
58 HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
59 user_features, num_user_features, \
60 coords, num_coords); \
61 shape_plan->shaper_func = _hb_##shaper##_shape; \
62 shape_plan->shaper_name = #shaper; \
67 if (likely (!shaper_list)) {
68 for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
71 #define HB_SHAPER_IMPLEMENT(shaper) \
72 else if (shapers[i].func == _hb_##shaper##_shape) \
73 HB_SHAPER_PLAN (shaper);
74 #include "hb-shaper-list.hh"
75 #undef HB_SHAPER_IMPLEMENT
77 for (; *shaper_list; shaper_list++)
80 #define HB_SHAPER_IMPLEMENT(shaper) \
81 else if (0 == strcmp (*shaper_list, #shaper)) \
82 HB_SHAPER_PLAN (shaper);
83 #include "hb-shaper-list.hh"
84 #undef HB_SHAPER_IMPLEMENT
96 * hb_shape_plan_create: (Xconstructor)
99 * @user_features: (array length=num_user_features):
100 * @num_user_features:
101 * @shaper_list: (array zero-terminated=1):
105 * Return value: (transfer full):
110 hb_shape_plan_create (hb_face_t *face,
111 const hb_segment_properties_t *props,
112 const hb_feature_t *user_features,
113 unsigned int num_user_features,
114 const char * const *shaper_list)
116 return hb_shape_plan_create2 (face, props,
117 user_features, num_user_features,
123 hb_shape_plan_create2 (hb_face_t *face,
124 const hb_segment_properties_t *props,
125 const hb_feature_t *user_features,
126 unsigned int num_user_features,
127 const int *orig_coords,
128 unsigned int num_coords,
129 const char * const *shaper_list)
131 DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
132 "face=%p num_features=%d num_coords=%d shaper_list=%p",
138 hb_shape_plan_t *shape_plan;
139 hb_feature_t *features = NULL;
142 if (unlikely (!face))
143 face = hb_face_get_empty ();
144 if (unlikely (!props))
145 return hb_shape_plan_get_empty ();
146 if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
147 return hb_shape_plan_get_empty ();
148 if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int))))
151 return hb_shape_plan_get_empty ();
153 if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
157 return hb_shape_plan_get_empty ();
160 assert (props->direction != HB_DIRECTION_INVALID);
162 hb_face_make_immutable (face);
163 shape_plan->default_shaper_list = shaper_list == NULL;
164 shape_plan->face_unsafe = face;
165 shape_plan->props = *props;
166 shape_plan->num_user_features = num_user_features;
167 shape_plan->user_features = features;
168 if (num_user_features)
169 memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
170 shape_plan->num_coords = num_coords;
171 shape_plan->coords = coords;
173 memcpy (coords, orig_coords, num_coords * sizeof (int));
175 hb_shape_plan_plan (shape_plan,
176 user_features, num_user_features,
184 * hb_shape_plan_get_empty:
188 * Return value: (transfer full):
193 hb_shape_plan_get_empty (void)
195 static const hb_shape_plan_t _hb_shape_plan_nil = {
196 HB_OBJECT_HEADER_STATIC,
198 true, /* default_shaper_list */
200 HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
202 NULL, /* shaper_func */
203 NULL, /* shaper_name */
205 NULL, /* user_features */
206 0, /* num_user_featurs */
212 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
213 #include "hb-shaper-list.hh"
214 #undef HB_SHAPER_IMPLEMENT
218 return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
222 * hb_shape_plan_reference: (skip)
223 * @shape_plan: a shape plan.
227 * Return value: (transfer full):
232 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
234 return hb_object_reference (shape_plan);
238 * hb_shape_plan_destroy: (skip)
239 * @shape_plan: a shape plan.
246 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
248 if (!hb_object_destroy (shape_plan)) return;
250 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
251 #include "hb-shaper-list.hh"
252 #undef HB_SHAPER_IMPLEMENT
254 free (shape_plan->user_features);
255 free (shape_plan->coords);
261 * hb_shape_plan_set_user_data: (skip)
262 * @shape_plan: a shape plan.
275 hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan,
276 hb_user_data_key_t *key,
278 hb_destroy_func_t destroy,
281 return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
285 * hb_shape_plan_get_user_data: (skip)
286 * @shape_plan: a shape plan.
291 * Return value: (transfer none):
296 hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan,
297 hb_user_data_key_t *key)
299 return hb_object_get_user_data (shape_plan, key);
304 * hb_shape_plan_execute:
305 * @shape_plan: a shape plan.
308 * @features: (array length=num_features):
318 hb_shape_plan_execute (hb_shape_plan_t *shape_plan,
321 const hb_feature_t *features,
322 unsigned int num_features)
324 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
325 "num_features=%d shaper_func=%p, shaper_name=%s",
327 shape_plan->shaper_func,
328 shape_plan->shaper_name);
330 if (unlikely (!buffer->len))
333 assert (!hb_object_is_inert (buffer));
334 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
336 if (unlikely (hb_object_is_inert (shape_plan)))
339 assert (shape_plan->face_unsafe == font->face);
340 assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
342 #define HB_SHAPER_EXECUTE(shaper) \
344 return HB_SHAPER_DATA (shaper, shape_plan) && \
345 hb_##shaper##_shaper_font_data_ensure (font) && \
346 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
351 #define HB_SHAPER_IMPLEMENT(shaper) \
352 else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
353 HB_SHAPER_EXECUTE (shaper);
354 #include "hb-shaper-list.hh"
355 #undef HB_SHAPER_IMPLEMENT
357 #undef HB_SHAPER_EXECUTE
369 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
371 return hb_segment_properties_hash (&shape_plan->props) +
372 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
376 /* User-feature caching is currently somewhat dumb:
377 * it only finds matches where the feature array is identical,
378 * not cases where the feature lists would be compatible for plan purposes
379 * but have different ranges, for example.
381 struct hb_shape_plan_proposal_t
383 const hb_segment_properties_t props;
384 const char * const *shaper_list;
385 const hb_feature_t *user_features;
386 unsigned int num_user_features;
388 unsigned int num_coords;
389 hb_shape_func_t *shaper_func;
392 static inline hb_bool_t
393 hb_shape_plan_user_features_match (const hb_shape_plan_t *shape_plan,
394 const hb_shape_plan_proposal_t *proposal)
396 if (proposal->num_user_features != shape_plan->num_user_features)
398 for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
399 if (proposal->user_features[i].tag != shape_plan->user_features[i].tag ||
400 proposal->user_features[i].value != shape_plan->user_features[i].value ||
401 proposal->user_features[i].start != shape_plan->user_features[i].start ||
402 proposal->user_features[i].end != shape_plan->user_features[i].end)
407 static inline hb_bool_t
408 hb_shape_plan_coords_match (const hb_shape_plan_t *shape_plan,
409 const hb_shape_plan_proposal_t *proposal)
411 if (proposal->num_coords != shape_plan->num_coords)
413 for (unsigned int i = 0, n = proposal->num_coords; i < n; i++)
414 if (proposal->coords[i] != shape_plan->coords[i])
420 hb_shape_plan_matches (const hb_shape_plan_t *shape_plan,
421 const hb_shape_plan_proposal_t *proposal)
423 return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
424 hb_shape_plan_user_features_match (shape_plan, proposal) &&
425 hb_shape_plan_coords_match (shape_plan, proposal) &&
426 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
427 (shape_plan->shaper_func == proposal->shaper_func));
430 static inline hb_bool_t
431 hb_non_global_user_features_present (const hb_feature_t *user_features,
432 unsigned int num_user_features)
434 while (num_user_features) {
435 if (user_features->start != 0 || user_features->end != (unsigned int) -1)
443 static inline hb_bool_t
444 hb_coords_present (const int *coords,
445 unsigned int num_coords)
447 return num_coords != 0;
451 * hb_shape_plan_create_cached:
454 * @user_features: (array length=num_user_features):
455 * @num_user_features:
456 * @shaper_list: (array zero-terminated=1):
460 * Return value: (transfer full):
465 hb_shape_plan_create_cached (hb_face_t *face,
466 const hb_segment_properties_t *props,
467 const hb_feature_t *user_features,
468 unsigned int num_user_features,
469 const char * const *shaper_list)
471 return hb_shape_plan_create_cached2 (face, props,
472 user_features, num_user_features,
478 hb_shape_plan_create_cached2 (hb_face_t *face,
479 const hb_segment_properties_t *props,
480 const hb_feature_t *user_features,
481 unsigned int num_user_features,
483 unsigned int num_coords,
484 const char * const *shaper_list)
486 DEBUG_MSG_FUNC (SHAPE_PLAN, NULL,
487 "face=%p num_features=%d shaper_list=%p",
492 hb_shape_plan_proposal_t proposal = {
501 /* Choose shaper. Adapted from hb_shape_plan_plan().
502 * Must choose shaper exactly the same way as that function. */
503 for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
506 #define HB_SHAPER_IMPLEMENT(shaper) \
507 else if (0 == strcmp (*shaper_item, #shaper) && \
508 hb_##shaper##_shaper_face_data_ensure (face)) \
510 proposal.shaper_func = _hb_##shaper##_shape; \
513 #include "hb-shaper-list.hh"
514 #undef HB_SHAPER_IMPLEMENT
516 if (unlikely (!proposal.shaper_func))
517 return hb_shape_plan_get_empty ();
522 hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
523 for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
524 if (hb_shape_plan_matches (node->shape_plan, &proposal))
526 DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
527 return hb_shape_plan_reference (node->shape_plan);
532 hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
533 user_features, num_user_features,
537 /* Don't add to the cache if face is inert. */
538 if (unlikely (hb_object_is_inert (face)))
541 /* Don't add the plan to the cache if there were user features with non-global ranges */
542 if (hb_non_global_user_features_present (user_features, num_user_features))
544 /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */
545 if (hb_coords_present (coords, num_coords))
548 hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
549 if (unlikely (!node))
552 node->shape_plan = shape_plan;
553 node->next = cached_plan_nodes;
555 if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
556 hb_shape_plan_destroy (shape_plan);
560 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
562 return hb_shape_plan_reference (shape_plan);
566 * hb_shape_plan_get_shaper:
567 * @shape_plan: a shape plan.
571 * Return value: (transfer none):
576 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
578 return shape_plan->shaper_name;