1 /*
2 * Copyright © 2012 Google, Inc.
3 *
4 * This is part of HarfBuzz, a text shaping library.
5 *
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.
11 *
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
16 * DAMAGE.
17 *
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.
23 *
24 * Google Author(s): Behdad Esfahbod
25 */
26
27 #include "hb.hh"
28 #include "hb-shape-plan.hh"
29 #include "hb-shaper.hh"
30 #include "hb-font.hh"
31 #include "hb-buffer.hh"
32
33
34 /**
35 * SECTION:hb-shape-plan
36 * @title: hb-shape-plan
37 * @short_description: Object representing a shaping plan
38 * @include: hb.h
39 *
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.
44 **/
45
46
47 /*
48 * hb_shape_plan_key_t
49 */
50
51 bool
init(bool copy,hb_face_t * face,const hb_segment_properties_t * props,const hb_feature_t * user_features,unsigned int num_user_features,const int * coords,unsigned int num_coords,const char * const * shaper_list)52 hb_shape_plan_key_t::init (bool copy,
53 hb_face_t *face,
54 const hb_segment_properties_t *props,
55 const hb_feature_t *user_features,
56 unsigned int num_user_features,
57 const int *coords,
58 unsigned int num_coords,
59 const char * const *shaper_list)
60 {
61 hb_feature_t *features = nullptr;
62 if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
63 goto bail;
64
65 this->props = *props;
66 this->num_user_features = num_user_features;
67 this->user_features = copy ? features : user_features;
68 if (copy && num_user_features)
69 {
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++)
73 {
74 if (features[0].start != HB_FEATURE_GLOBAL_START)
75 features[0].start = 1;
76 if (features[0].end != HB_FEATURE_GLOBAL_END)
77 features[0].end = 2;
78 }
79 }
80 this->shaper_func = nullptr;
81 this->shaper_name = nullptr;
82 #ifndef HB_NO_OT_SHAPE
83 this->ot.init (face, coords, num_coords);
84 #endif
85
86 /*
87 * Choose shaper.
88 */
89
90 #define HB_SHAPER_PLAN(shaper) \
91 HB_STMT_START { \
92 if (face->data.shaper) \
93 { \
94 this->shaper_func = _hb_##shaper##_shape; \
95 this->shaper_name = #shaper; \
96 return true; \
97 } \
98 } HB_STMT_END
99
100 if (unlikely (shaper_list))
101 {
102 for (; *shaper_list; shaper_list++)
103 if (false)
104 ;
105 #define HB_SHAPER_IMPLEMENT(shaper) \
106 else if (0 == strcmp (*shaper_list, #shaper)) \
107 HB_SHAPER_PLAN (shaper);
108 #include "hb-shaper-list.hh"
109 #undef HB_SHAPER_IMPLEMENT
110 }
111 else
112 {
113 const hb_shaper_entry_t *shapers = _hb_shapers_get ();
114 for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
115 if (false)
116 ;
117 #define HB_SHAPER_IMPLEMENT(shaper) \
118 else if (shapers[i].func == _hb_##shaper##_shape) \
119 HB_SHAPER_PLAN (shaper);
120 #include "hb-shaper-list.hh"
121 #undef HB_SHAPER_IMPLEMENT
122 }
123 #undef HB_SHAPER_PLAN
124
125 bail:
126 ::free (features);
127 return false;
128 }
129
130 bool
user_features_match(const hb_shape_plan_key_t * other)131 hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other)
132 {
133 if (this->num_user_features != other->num_user_features)
134 return false;
135 for (unsigned int i = 0; i < num_user_features; i++)
136 {
137 if (this->user_features[i].tag != other->user_features[i].tag ||
138 this->user_features[i].value != other->user_features[i].value ||
139 (this->user_features[i].start == HB_FEATURE_GLOBAL_START &&
140 this->user_features[i].end == HB_FEATURE_GLOBAL_END) !=
141 (other->user_features[i].start == HB_FEATURE_GLOBAL_START &&
142 other->user_features[i].end == HB_FEATURE_GLOBAL_END))
143 return false;
144 }
145 return true;
146 }
147
148 bool
equal(const hb_shape_plan_key_t * other)149 hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
150 {
151 return hb_segment_properties_equal (&this->props, &other->props) &&
152 this->user_features_match (other) &&
153 #ifndef HB_NO_OT_SHAPE
154 this->ot.equal (&other->ot) &&
155 #endif
156 this->shaper_func == other->shaper_func;
157 }
158
159
160 /*
161 * hb_shape_plan_t
162 */
163
164
165 /**
166 * hb_shape_plan_create: (Xconstructor)
167 * @face:
168 * @props:
169 * @user_features: (array length=num_user_features):
170 * @num_user_features:
171 * @shaper_list: (array zero-terminated=1):
172 *
173 *
174 *
175 * Return value: (transfer full):
176 *
177 * Since: 0.9.7
178 **/
179 hb_shape_plan_t *
hb_shape_plan_create(hb_face_t * face,const hb_segment_properties_t * props,const hb_feature_t * user_features,unsigned int num_user_features,const char * const * shaper_list)180 hb_shape_plan_create (hb_face_t *face,
181 const hb_segment_properties_t *props,
182 const hb_feature_t *user_features,
183 unsigned int num_user_features,
184 const char * const *shaper_list)
185 {
186 return hb_shape_plan_create2 (face, props,
187 user_features, num_user_features,
188 nullptr, 0,
189 shaper_list);
190 }
191
192 hb_shape_plan_t *
hb_shape_plan_create2(hb_face_t * face,const hb_segment_properties_t * props,const hb_feature_t * user_features,unsigned int num_user_features,const int * coords,unsigned int num_coords,const char * const * shaper_list)193 hb_shape_plan_create2 (hb_face_t *face,
194 const hb_segment_properties_t *props,
195 const hb_feature_t *user_features,
196 unsigned int num_user_features,
197 const int *coords,
198 unsigned int num_coords,
199 const char * const *shaper_list)
200 {
201 DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
202 "face=%p num_features=%d num_coords=%d shaper_list=%p",
203 face,
204 num_user_features,
205 num_coords,
206 shaper_list);
207
208 assert (props->direction != HB_DIRECTION_INVALID);
209
210 hb_shape_plan_t *shape_plan;
211
212 if (unlikely (!props))
213 goto bail;
214 if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
215 goto bail;
216
217 if (unlikely (!face))
218 face = hb_face_get_empty ();
219 hb_face_make_immutable (face);
220 shape_plan->face_unsafe = face;
221
222 if (unlikely (!shape_plan->key.init (true,
223 face,
224 props,
225 user_features,
226 num_user_features,
227 coords,
228 num_coords,
229 shaper_list)))
230 goto bail2;
231 #ifndef HB_NO_OT_SHAPE
232 if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key)))
233 goto bail3;
234 #endif
235
236 return shape_plan;
237
238 #ifndef HB_NO_OT_SHAPE
239 bail3:
240 #endif
241 shape_plan->key.free ();
242 bail2:
243 free (shape_plan);
244 bail:
245 return hb_shape_plan_get_empty ();
246 }
247
248 /**
249 * hb_shape_plan_get_empty:
250 *
251 *
252 *
253 * Return value: (transfer full):
254 *
255 * Since: 0.9.7
256 **/
257 hb_shape_plan_t *
hb_shape_plan_get_empty()258 hb_shape_plan_get_empty ()
259 {
260 return const_cast<hb_shape_plan_t *> (&Null(hb_shape_plan_t));
261 }
262
263 /**
264 * hb_shape_plan_reference: (skip)
265 * @shape_plan: a shape plan.
266 *
267 *
268 *
269 * Return value: (transfer full):
270 *
271 * Since: 0.9.7
272 **/
273 hb_shape_plan_t *
hb_shape_plan_reference(hb_shape_plan_t * shape_plan)274 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
275 {
276 return hb_object_reference (shape_plan);
277 }
278
279 /**
280 * hb_shape_plan_destroy: (skip)
281 * @shape_plan: a shape plan.
282 *
283 *
284 *
285 * Since: 0.9.7
286 **/
287 void
hb_shape_plan_destroy(hb_shape_plan_t * shape_plan)288 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
289 {
290 if (!hb_object_destroy (shape_plan)) return;
291
292 #ifndef HB_NO_OT_SHAPE
293 shape_plan->ot.fini ();
294 #endif
295 shape_plan->key.free ();
296 free (shape_plan);
297 }
298
299 /**
300 * hb_shape_plan_set_user_data: (skip)
301 * @shape_plan: a shape plan.
302 * @key:
303 * @data:
304 * @destroy:
305 * @replace:
306 *
307 *
308 *
309 * Return value:
310 *
311 * Since: 0.9.7
312 **/
313 hb_bool_t
hb_shape_plan_set_user_data(hb_shape_plan_t * shape_plan,hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)314 hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan,
315 hb_user_data_key_t *key,
316 void * data,
317 hb_destroy_func_t destroy,
318 hb_bool_t replace)
319 {
320 return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
321 }
322
323 /**
324 * hb_shape_plan_get_user_data: (skip)
325 * @shape_plan: a shape plan.
326 * @key:
327 *
328 *
329 *
330 * Return value: (transfer none):
331 *
332 * Since: 0.9.7
333 **/
334 void *
hb_shape_plan_get_user_data(hb_shape_plan_t * shape_plan,hb_user_data_key_t * key)335 hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan,
336 hb_user_data_key_t *key)
337 {
338 return hb_object_get_user_data (shape_plan, key);
339 }
340
341 /**
342 * hb_shape_plan_get_shaper:
343 * @shape_plan: a shape plan.
344 *
345 *
346 *
347 * Return value: (transfer none):
348 *
349 * Since: 0.9.7
350 **/
351 const char *
hb_shape_plan_get_shaper(hb_shape_plan_t * shape_plan)352 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
353 {
354 return shape_plan->key.shaper_name;
355 }
356
357
358 /**
359 * hb_shape_plan_execute:
360 * @shape_plan: a shape plan.
361 * @font: a font.
362 * @buffer: a buffer.
363 * @features: (array length=num_features):
364 * @num_features:
365 *
366 *
367 *
368 * Return value:
369 *
370 * Since: 0.9.7
371 **/
372 hb_bool_t
hb_shape_plan_execute(hb_shape_plan_t * shape_plan,hb_font_t * font,hb_buffer_t * buffer,const hb_feature_t * features,unsigned int num_features)373 hb_shape_plan_execute (hb_shape_plan_t *shape_plan,
374 hb_font_t *font,
375 hb_buffer_t *buffer,
376 const hb_feature_t *features,
377 unsigned int num_features)
378 {
379 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
380 "num_features=%d shaper_func=%p, shaper_name=%s",
381 num_features,
382 shape_plan->key.shaper_func,
383 shape_plan->key.shaper_name);
384
385 if (unlikely (!buffer->len))
386 return true;
387
388 assert (!hb_object_is_immutable (buffer));
389 assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
390
391 if (unlikely (hb_object_is_inert (shape_plan)))
392 return false;
393
394 assert (shape_plan->face_unsafe == font->face);
395 assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props));
396
397 #define HB_SHAPER_EXECUTE(shaper) \
398 HB_STMT_START { \
399 return font->data.shaper && \
400 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
401 } HB_STMT_END
402
403 if (false)
404 ;
405 #define HB_SHAPER_IMPLEMENT(shaper) \
406 else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \
407 HB_SHAPER_EXECUTE (shaper);
408 #include "hb-shaper-list.hh"
409 #undef HB_SHAPER_IMPLEMENT
410
411 #undef HB_SHAPER_EXECUTE
412
413 return false;
414 }
415
416
417 /*
418 * Caching
419 */
420
421 /**
422 * hb_shape_plan_create_cached:
423 * @face:
424 * @props:
425 * @user_features: (array length=num_user_features):
426 * @num_user_features:
427 * @shaper_list: (array zero-terminated=1):
428 *
429 *
430 *
431 * Return value: (transfer full):
432 *
433 * Since: 0.9.7
434 **/
435 hb_shape_plan_t *
hb_shape_plan_create_cached(hb_face_t * face,const hb_segment_properties_t * props,const hb_feature_t * user_features,unsigned int num_user_features,const char * const * shaper_list)436 hb_shape_plan_create_cached (hb_face_t *face,
437 const hb_segment_properties_t *props,
438 const hb_feature_t *user_features,
439 unsigned int num_user_features,
440 const char * const *shaper_list)
441 {
442 return hb_shape_plan_create_cached2 (face, props,
443 user_features, num_user_features,
444 nullptr, 0,
445 shaper_list);
446 }
447
448 hb_shape_plan_t *
hb_shape_plan_create_cached2(hb_face_t * face,const hb_segment_properties_t * props,const hb_feature_t * user_features,unsigned int num_user_features,const int * coords,unsigned int num_coords,const char * const * shaper_list)449 hb_shape_plan_create_cached2 (hb_face_t *face,
450 const hb_segment_properties_t *props,
451 const hb_feature_t *user_features,
452 unsigned int num_user_features,
453 const int *coords,
454 unsigned int num_coords,
455 const char * const *shaper_list)
456 {
457 DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
458 "face=%p num_features=%d shaper_list=%p",
459 face,
460 num_user_features,
461 shaper_list);
462
463 retry:
464 hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans;
465
466 bool dont_cache = hb_object_is_inert (face);
467
468 if (likely (!dont_cache))
469 {
470 hb_shape_plan_key_t key;
471 if (!key.init (false,
472 face,
473 props,
474 user_features,
475 num_user_features,
476 coords,
477 num_coords,
478 shaper_list))
479 return hb_shape_plan_get_empty ();
480
481 for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
482 if (node->shape_plan->key.equal (&key))
483 {
484 DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
485 return hb_shape_plan_reference (node->shape_plan);
486 }
487 }
488
489 hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
490 user_features, num_user_features,
491 coords, num_coords,
492 shaper_list);
493
494 if (unlikely (dont_cache))
495 return shape_plan;
496
497 hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
498 if (unlikely (!node))
499 return shape_plan;
500
501 node->shape_plan = shape_plan;
502 node->next = cached_plan_nodes;
503
504 if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node)))
505 {
506 hb_shape_plan_destroy (shape_plan);
507 free (node);
508 goto retry;
509 }
510 DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
511
512 return hb_shape_plan_reference (shape_plan);
513 }
514