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
28 #include "hb-shape-plan.hh"
29 #include "hb-shaper.hh"
31 #include "hb-buffer.hh"
35 * SECTION:hb-shape-plan
36 * @title: hb-shape-plan
37 * @short_description: Object representing a shaping plan
40 * Shape plans are not used for shaping directly, but can be access to query
41 * certain information about how shaping will perform given a set of input
42 * parameters (script, language, direction, features, etc.)
43 * Most client would not need to deal with shape plans directly.
52 hb_shape_plan_key_t::init (bool copy,
54 const hb_segment_properties_t *props,
55 const hb_feature_t *user_features,
56 unsigned int num_user_features,
58 unsigned int num_coords,
59 const char * const *shaper_list)
61 hb_feature_t *features = nullptr;
62 if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
66 this->num_user_features = num_user_features;
67 this->user_features = copy ? features : user_features;
68 if (copy && num_user_features)
70 memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
71 /* Make start/end uniform to easier catch bugs. */
72 for (unsigned int i = 0; i < num_user_features; i++)
74 if (features[0].start != HB_FEATURE_GLOBAL_START)
75 features[0].start = 1;
76 if (features[0].end != HB_FEATURE_GLOBAL_END)
80 this->shaper_func = nullptr;
81 this->shaper_name = nullptr;
82 this->ot.init (face, coords, num_coords);
88 #define HB_SHAPER_PLAN(shaper) \
90 if (face->data.shaper) \
92 this->shaper_func = _hb_##shaper##_shape; \
93 this->shaper_name = #shaper; \
98 if (unlikely (shaper_list))
100 for (; *shaper_list; shaper_list++)
103 #define HB_SHAPER_IMPLEMENT(shaper) \
104 else if (0 == strcmp (*shaper_list, #shaper)) \
105 HB_SHAPER_PLAN (shaper);
106 #include "hb-shaper-list.hh"
107 #undef HB_SHAPER_IMPLEMENT
111 const hb_shaper_entry_t *shapers = _hb_shapers_get ();
112 for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
115 #define HB_SHAPER_IMPLEMENT(shaper) \
116 else if (shapers[i].func == _hb_##shaper##_shape) \
117 HB_SHAPER_PLAN (shaper);
118 #include "hb-shaper-list.hh"
119 #undef HB_SHAPER_IMPLEMENT
121 #undef HB_SHAPER_PLAN
129 hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other)
131 if (this->num_user_features != other->num_user_features)
133 for (unsigned int i = 0; i < num_user_features; i++)
135 if (this->user_features[i].tag != other->user_features[i].tag ||
136 this->user_features[i].value != other->user_features[i].value ||
137 (this->user_features[i].start == HB_FEATURE_GLOBAL_START &&
138 this->user_features[i].end == HB_FEATURE_GLOBAL_END) !=
139 (other->user_features[i].start == HB_FEATURE_GLOBAL_START &&
140 other->user_features[i].end == HB_FEATURE_GLOBAL_END))
147 hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
149 return hb_segment_properties_equal (&this->props, &other->props) &&
150 this->user_features_match (other) &&
151 this->ot.equal (&other->ot) &&
152 this->shaper_func == other->shaper_func;
162 * hb_shape_plan_create: (Xconstructor)
165 * @user_features: (array length=num_user_features):
166 * @num_user_features:
167 * @shaper_list: (array zero-terminated=1):
171 * Return value: (transfer full):
176 hb_shape_plan_create (hb_face_t *face,
177 const hb_segment_properties_t *props,
178 const hb_feature_t *user_features,
179 unsigned int num_user_features,
180 const char * const *shaper_list)
182 return hb_shape_plan_create2 (face, props,
183 user_features, num_user_features,
189 hb_shape_plan_create2 (hb_face_t *face,
190 const hb_segment_properties_t *props,
191 const hb_feature_t *user_features,
192 unsigned int num_user_features,
194 unsigned int num_coords,
195 const char * const *shaper_list)
197 DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
198 "face=%p num_features=%d num_coords=%d shaper_list=%p",
204 assert (props->direction != HB_DIRECTION_INVALID);
206 hb_shape_plan_t *shape_plan;
208 if (unlikely (!props))
210 if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
213 if (unlikely (!face))
214 face = hb_face_get_empty ();
215 hb_face_make_immutable (face);
216 shape_plan->face_unsafe = face;
218 if (unlikely (!shape_plan->key.init (true,
227 if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key)))
233 shape_plan->key.free ();
237 return hb_shape_plan_get_empty ();
241 * hb_shape_plan_get_empty:
245 * Return value: (transfer full):
250 hb_shape_plan_get_empty ()
252 return const_cast<hb_shape_plan_t *> (&Null(hb_shape_plan_t));
256 * hb_shape_plan_reference: (skip)
257 * @shape_plan: a shape plan.
261 * Return value: (transfer full):
266 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
268 return hb_object_reference (shape_plan);
272 * hb_shape_plan_destroy: (skip)
273 * @shape_plan: a shape plan.
280 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
282 if (!hb_object_destroy (shape_plan)) return;
284 shape_plan->ot.fini ();
285 shape_plan->key.free ();
290 * hb_shape_plan_set_user_data: (skip)
291 * @shape_plan: a shape plan.
304 hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan,
305 hb_user_data_key_t *key,
307 hb_destroy_func_t destroy,
310 return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
314 * hb_shape_plan_get_user_data: (skip)
315 * @shape_plan: a shape plan.
320 * Return value: (transfer none):
325 hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan,
326 hb_user_data_key_t *key)
328 return hb_object_get_user_data (shape_plan, key);
332 * hb_shape_plan_get_shaper:
333 * @shape_plan: a shape plan.
337 * Return value: (transfer none):
342 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
344 return shape_plan->key.shaper_name;
349 * hb_shape_plan_execute:
350 * @shape_plan: a shape plan.
353 * @features: (array length=num_features):
363 hb_shape_plan_execute (hb_shape_plan_t *shape_plan,
366 const hb_feature_t *features,
367 unsigned int num_features)
369 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
370 "num_features=%d shaper_func=%p, shaper_name=%s",
372 shape_plan->key.shaper_func,
373 shape_plan->key.shaper_name);
375 if (unlikely (!buffer->len))
378 assert (!hb_object_is_immutable (buffer));
379 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
381 if (unlikely (hb_object_is_inert (shape_plan)))
384 assert (shape_plan->face_unsafe == font->face);
385 assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props));
387 #define HB_SHAPER_EXECUTE(shaper) \
389 return font->data.shaper && \
390 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
395 #define HB_SHAPER_IMPLEMENT(shaper) \
396 else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \
397 HB_SHAPER_EXECUTE (shaper);
398 #include "hb-shaper-list.hh"
399 #undef HB_SHAPER_IMPLEMENT
401 #undef HB_SHAPER_EXECUTE
412 * hb_shape_plan_create_cached:
415 * @user_features: (array length=num_user_features):
416 * @num_user_features:
417 * @shaper_list: (array zero-terminated=1):
421 * Return value: (transfer full):
426 hb_shape_plan_create_cached (hb_face_t *face,
427 const hb_segment_properties_t *props,
428 const hb_feature_t *user_features,
429 unsigned int num_user_features,
430 const char * const *shaper_list)
432 return hb_shape_plan_create_cached2 (face, props,
433 user_features, num_user_features,
439 hb_shape_plan_create_cached2 (hb_face_t *face,
440 const hb_segment_properties_t *props,
441 const hb_feature_t *user_features,
442 unsigned int num_user_features,
444 unsigned int num_coords,
445 const char * const *shaper_list)
447 DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
448 "face=%p num_features=%d shaper_list=%p",
454 hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans;
456 bool dont_cache = hb_object_is_inert (face);
458 if (likely (!dont_cache))
460 hb_shape_plan_key_t key;
461 if (!key.init (false,
469 return hb_shape_plan_get_empty ();
471 for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
472 if (node->shape_plan->key.equal (&key))
474 DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
475 return hb_shape_plan_reference (node->shape_plan);
479 hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
480 user_features, num_user_features,
484 if (unlikely (dont_cache))
487 hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
488 if (unlikely (!node))
491 node->shape_plan = shape_plan;
492 node->next = cached_plan_nodes;
494 if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node)))
496 hb_shape_plan_destroy (shape_plan);
500 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
502 return hb_shape_plan_reference (shape_plan);