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-private.hh" |
28 | #include "hb-debug.hh" |
29 | #include "hb-shape-plan-private.hh" |
30 | #include "hb-shaper-private.hh" |
31 | #include "hb-font-private.hh" |
32 | #include "hb-buffer-private.hh" |
33 | |
34 | |
35 | static void |
36 | hb_shape_plan_plan (hb_shape_plan_t *shape_plan, |
37 | const hb_feature_t *user_features, |
38 | unsigned int num_user_features, |
39 | const int *coords, |
40 | unsigned int num_coords, |
41 | const char * const *shaper_list) |
42 | { |
43 | DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, |
44 | "num_features=%d num_coords=%d shaper_list=%p" , |
45 | num_user_features, |
46 | num_coords, |
47 | shaper_list); |
48 | |
49 | const hb_shaper_pair_t *shapers = _hb_shapers_get (); |
50 | |
51 | #define HB_SHAPER_PLAN(shaper) \ |
52 | HB_STMT_START { \ |
53 | if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \ |
54 | HB_SHAPER_DATA (shaper, shape_plan) = \ |
55 | HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \ |
56 | user_features, num_user_features, \ |
57 | coords, num_coords); \ |
58 | shape_plan->shaper_func = _hb_##shaper##_shape; \ |
59 | shape_plan->shaper_name = #shaper; \ |
60 | return; \ |
61 | } \ |
62 | } HB_STMT_END |
63 | |
64 | if (likely (!shaper_list)) { |
65 | for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++) |
66 | if (0) |
67 | ; |
68 | #define HB_SHAPER_IMPLEMENT(shaper) \ |
69 | else if (shapers[i].func == _hb_##shaper##_shape) \ |
70 | HB_SHAPER_PLAN (shaper); |
71 | #include "hb-shaper-list.hh" |
72 | #undef HB_SHAPER_IMPLEMENT |
73 | } else { |
74 | for (; *shaper_list; shaper_list++) |
75 | if (0) |
76 | ; |
77 | #define HB_SHAPER_IMPLEMENT(shaper) \ |
78 | else if (0 == strcmp (*shaper_list, #shaper)) \ |
79 | HB_SHAPER_PLAN (shaper); |
80 | #include "hb-shaper-list.hh" |
81 | #undef HB_SHAPER_IMPLEMENT |
82 | } |
83 | |
84 | #undef HB_SHAPER_PLAN |
85 | } |
86 | |
87 | |
88 | /* |
89 | * hb_shape_plan_t |
90 | */ |
91 | |
92 | /** |
93 | * hb_shape_plan_create: (Xconstructor) |
94 | * @face: |
95 | * @props: |
96 | * @user_features: (array length=num_user_features): |
97 | * @num_user_features: |
98 | * @shaper_list: (array zero-terminated=1): |
99 | * |
100 | * |
101 | * |
102 | * Return value: (transfer full): |
103 | * |
104 | * Since: 0.9.7 |
105 | **/ |
106 | hb_shape_plan_t * |
107 | hb_shape_plan_create (hb_face_t *face, |
108 | const hb_segment_properties_t *props, |
109 | const hb_feature_t *user_features, |
110 | unsigned int num_user_features, |
111 | const char * const *shaper_list) |
112 | { |
113 | return hb_shape_plan_create2 (face, props, |
114 | user_features, num_user_features, |
115 | coords: nullptr, num_coords: 0, |
116 | shaper_list); |
117 | } |
118 | |
119 | hb_shape_plan_t * |
120 | hb_shape_plan_create2 (hb_face_t *face, |
121 | const hb_segment_properties_t *props, |
122 | const hb_feature_t *user_features, |
123 | unsigned int num_user_features, |
124 | const int *orig_coords, |
125 | unsigned int num_coords, |
126 | const char * const *shaper_list) |
127 | { |
128 | DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, |
129 | "face=%p num_features=%d num_coords=%d shaper_list=%p" , |
130 | face, |
131 | num_user_features, |
132 | num_coords, |
133 | shaper_list); |
134 | |
135 | hb_shape_plan_t *shape_plan; |
136 | hb_feature_t *features = nullptr; |
137 | int *coords = nullptr; |
138 | |
139 | if (unlikely (!face)) |
140 | face = hb_face_get_empty (); |
141 | if (unlikely (!props)) |
142 | return hb_shape_plan_get_empty (); |
143 | if (num_user_features && !(features = (hb_feature_t *) calloc (nmemb: num_user_features, size: sizeof (hb_feature_t)))) |
144 | return hb_shape_plan_get_empty (); |
145 | if (num_coords && !(coords = (int *) calloc (nmemb: num_coords, size: sizeof (int)))) |
146 | { |
147 | free (ptr: features); |
148 | return hb_shape_plan_get_empty (); |
149 | } |
150 | if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) |
151 | { |
152 | free (ptr: coords); |
153 | free (ptr: features); |
154 | return hb_shape_plan_get_empty (); |
155 | } |
156 | |
157 | assert (props->direction != HB_DIRECTION_INVALID); |
158 | |
159 | hb_face_make_immutable (face); |
160 | shape_plan->default_shaper_list = !shaper_list; |
161 | shape_plan->face_unsafe = face; |
162 | shape_plan->props = *props; |
163 | shape_plan->num_user_features = num_user_features; |
164 | shape_plan->user_features = features; |
165 | if (num_user_features) |
166 | memcpy (dest: features, src: user_features, n: num_user_features * sizeof (hb_feature_t)); |
167 | shape_plan->num_coords = num_coords; |
168 | shape_plan->coords = coords; |
169 | if (num_coords) |
170 | memcpy (dest: coords, src: orig_coords, n: num_coords * sizeof (int)); |
171 | |
172 | hb_shape_plan_plan (shape_plan, |
173 | user_features, num_user_features, |
174 | coords, num_coords, |
175 | shaper_list); |
176 | |
177 | return shape_plan; |
178 | } |
179 | |
180 | /** |
181 | * hb_shape_plan_get_empty: |
182 | * |
183 | * |
184 | * |
185 | * Return value: (transfer full): |
186 | * |
187 | * Since: 0.9.7 |
188 | **/ |
189 | hb_shape_plan_t * |
190 | hb_shape_plan_get_empty (void) |
191 | { |
192 | static const hb_shape_plan_t _hb_shape_plan_nil = { |
193 | HB_OBJECT_HEADER_STATIC, |
194 | |
195 | .default_shaper_list: true, /* default_shaper_list */ |
196 | .face_unsafe: nullptr, /* face */ |
197 | HB_SEGMENT_PROPERTIES_DEFAULT, /* props */ |
198 | |
199 | .shaper_func: nullptr, /* shaper_func */ |
200 | .shaper_name: nullptr, /* shaper_name */ |
201 | |
202 | .user_features: nullptr, /* user_features */ |
203 | .num_user_features: 0, /* num_user_featurs */ |
204 | |
205 | .coords: nullptr, /* coords */ |
206 | .num_coords: 0, /* num_coords */ |
207 | |
208 | .shaper_data: { |
209 | #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID, |
210 | #include "hb-shaper-list.hh" |
211 | #undef HB_SHAPER_IMPLEMENT |
212 | } |
213 | }; |
214 | |
215 | return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil); |
216 | } |
217 | |
218 | /** |
219 | * hb_shape_plan_reference: (skip) |
220 | * @shape_plan: a shape plan. |
221 | * |
222 | * |
223 | * |
224 | * Return value: (transfer full): |
225 | * |
226 | * Since: 0.9.7 |
227 | **/ |
228 | hb_shape_plan_t * |
229 | hb_shape_plan_reference (hb_shape_plan_t *shape_plan) |
230 | { |
231 | return hb_object_reference (obj: shape_plan); |
232 | } |
233 | |
234 | /** |
235 | * hb_shape_plan_destroy: (skip) |
236 | * @shape_plan: a shape plan. |
237 | * |
238 | * |
239 | * |
240 | * Since: 0.9.7 |
241 | **/ |
242 | void |
243 | hb_shape_plan_destroy (hb_shape_plan_t *shape_plan) |
244 | { |
245 | if (!hb_object_destroy (obj: shape_plan)) return; |
246 | |
247 | #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan); |
248 | #include "hb-shaper-list.hh" |
249 | #undef HB_SHAPER_IMPLEMENT |
250 | |
251 | free (ptr: shape_plan->user_features); |
252 | free (ptr: shape_plan->coords); |
253 | |
254 | free (ptr: shape_plan); |
255 | } |
256 | |
257 | /** |
258 | * hb_shape_plan_set_user_data: (skip) |
259 | * @shape_plan: a shape plan. |
260 | * @key: |
261 | * @data: |
262 | * @destroy: |
263 | * @replace: |
264 | * |
265 | * |
266 | * |
267 | * Return value: |
268 | * |
269 | * Since: 0.9.7 |
270 | **/ |
271 | hb_bool_t |
272 | hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan, |
273 | hb_user_data_key_t *key, |
274 | void * data, |
275 | hb_destroy_func_t destroy, |
276 | hb_bool_t replace) |
277 | { |
278 | return hb_object_set_user_data (obj: shape_plan, key, data, destroy, replace); |
279 | } |
280 | |
281 | /** |
282 | * hb_shape_plan_get_user_data: (skip) |
283 | * @shape_plan: a shape plan. |
284 | * @key: |
285 | * |
286 | * |
287 | * |
288 | * Return value: (transfer none): |
289 | * |
290 | * Since: 0.9.7 |
291 | **/ |
292 | void * |
293 | hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan, |
294 | hb_user_data_key_t *key) |
295 | { |
296 | return hb_object_get_user_data (obj: shape_plan, key); |
297 | } |
298 | |
299 | |
300 | /** |
301 | * hb_shape_plan_execute: |
302 | * @shape_plan: a shape plan. |
303 | * @font: a font. |
304 | * @buffer: a buffer. |
305 | * @features: (array length=num_features): |
306 | * @num_features: |
307 | * |
308 | * |
309 | * |
310 | * Return value: |
311 | * |
312 | * Since: 0.9.7 |
313 | **/ |
314 | hb_bool_t |
315 | hb_shape_plan_execute (hb_shape_plan_t *shape_plan, |
316 | hb_font_t *font, |
317 | hb_buffer_t *buffer, |
318 | const hb_feature_t *features, |
319 | unsigned int num_features) |
320 | { |
321 | DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, |
322 | "num_features=%d shaper_func=%p, shaper_name=%s" , |
323 | num_features, |
324 | shape_plan->shaper_func, |
325 | shape_plan->shaper_name); |
326 | |
327 | if (unlikely (!buffer->len)) |
328 | return true; |
329 | |
330 | assert (!hb_object_is_inert (buffer)); |
331 | assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE); |
332 | |
333 | if (unlikely (hb_object_is_inert (shape_plan))) |
334 | return false; |
335 | |
336 | assert (shape_plan->face_unsafe == font->face); |
337 | assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props)); |
338 | |
339 | #define HB_SHAPER_EXECUTE(shaper) \ |
340 | HB_STMT_START { \ |
341 | return HB_SHAPER_DATA (shaper, shape_plan) && \ |
342 | hb_##shaper##_shaper_font_data_ensure (font) && \ |
343 | _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \ |
344 | } HB_STMT_END |
345 | |
346 | if (0) |
347 | ; |
348 | #define HB_SHAPER_IMPLEMENT(shaper) \ |
349 | else if (shape_plan->shaper_func == _hb_##shaper##_shape) \ |
350 | HB_SHAPER_EXECUTE (shaper); |
351 | #include "hb-shaper-list.hh" |
352 | #undef HB_SHAPER_IMPLEMENT |
353 | |
354 | #undef HB_SHAPER_EXECUTE |
355 | |
356 | return false; |
357 | } |
358 | |
359 | |
360 | /* |
361 | * caching |
362 | */ |
363 | |
364 | #if 0 |
365 | static unsigned int |
366 | hb_shape_plan_hash (const hb_shape_plan_t *shape_plan) |
367 | { |
368 | return hb_segment_properties_hash (&shape_plan->props) + |
369 | shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func; |
370 | } |
371 | #endif |
372 | |
373 | /* User-feature caching is currently somewhat dumb: |
374 | * it only finds matches where the feature array is identical, |
375 | * not cases where the feature lists would be compatible for plan purposes |
376 | * but have different ranges, for example. |
377 | */ |
378 | struct hb_shape_plan_proposal_t |
379 | { |
380 | const hb_segment_properties_t props; |
381 | const char * const *shaper_list; |
382 | const hb_feature_t *user_features; |
383 | unsigned int num_user_features; |
384 | const int *coords; |
385 | unsigned int num_coords; |
386 | hb_shape_func_t *shaper_func; |
387 | }; |
388 | |
389 | static inline hb_bool_t |
390 | hb_shape_plan_user_features_match (const hb_shape_plan_t *shape_plan, |
391 | const hb_shape_plan_proposal_t *proposal) |
392 | { |
393 | if (proposal->num_user_features != shape_plan->num_user_features) |
394 | return false; |
395 | for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++) |
396 | if (proposal->user_features[i].tag != shape_plan->user_features[i].tag || |
397 | proposal->user_features[i].value != shape_plan->user_features[i].value || |
398 | proposal->user_features[i].start != shape_plan->user_features[i].start || |
399 | proposal->user_features[i].end != shape_plan->user_features[i].end) |
400 | return false; |
401 | return true; |
402 | } |
403 | |
404 | static inline hb_bool_t |
405 | hb_shape_plan_coords_match (const hb_shape_plan_t *shape_plan, |
406 | const hb_shape_plan_proposal_t *proposal) |
407 | { |
408 | if (proposal->num_coords != shape_plan->num_coords) |
409 | return false; |
410 | for (unsigned int i = 0, n = proposal->num_coords; i < n; i++) |
411 | if (proposal->coords[i] != shape_plan->coords[i]) |
412 | return false; |
413 | return true; |
414 | } |
415 | |
416 | static hb_bool_t |
417 | hb_shape_plan_matches (const hb_shape_plan_t *shape_plan, |
418 | const hb_shape_plan_proposal_t *proposal) |
419 | { |
420 | return hb_segment_properties_equal (a: &shape_plan->props, b: &proposal->props) && |
421 | hb_shape_plan_user_features_match (shape_plan, proposal) && |
422 | hb_shape_plan_coords_match (shape_plan, proposal) && |
423 | ((shape_plan->default_shaper_list && !proposal->shaper_list) || |
424 | (shape_plan->shaper_func == proposal->shaper_func)); |
425 | } |
426 | |
427 | static inline hb_bool_t |
428 | hb_non_global_user_features_present (const hb_feature_t *user_features, |
429 | unsigned int num_user_features) |
430 | { |
431 | while (num_user_features) { |
432 | if (user_features->start != 0 || user_features->end != (unsigned int) -1) |
433 | return true; |
434 | num_user_features--; |
435 | user_features++; |
436 | } |
437 | return false; |
438 | } |
439 | |
440 | static inline hb_bool_t |
441 | hb_coords_present (const int *coords, |
442 | unsigned int num_coords) |
443 | { |
444 | return num_coords != 0; |
445 | } |
446 | |
447 | /** |
448 | * hb_shape_plan_create_cached: |
449 | * @face: |
450 | * @props: |
451 | * @user_features: (array length=num_user_features): |
452 | * @num_user_features: |
453 | * @shaper_list: (array zero-terminated=1): |
454 | * |
455 | * |
456 | * |
457 | * Return value: (transfer full): |
458 | * |
459 | * Since: 0.9.7 |
460 | **/ |
461 | hb_shape_plan_t * |
462 | hb_shape_plan_create_cached (hb_face_t *face, |
463 | const hb_segment_properties_t *props, |
464 | const hb_feature_t *user_features, |
465 | unsigned int num_user_features, |
466 | const char * const *shaper_list) |
467 | { |
468 | return hb_shape_plan_create_cached2 (face, props, |
469 | user_features, num_user_features, |
470 | coords: nullptr, num_coords: 0, |
471 | shaper_list); |
472 | } |
473 | |
474 | hb_shape_plan_t * |
475 | hb_shape_plan_create_cached2 (hb_face_t *face, |
476 | const hb_segment_properties_t *props, |
477 | const hb_feature_t *user_features, |
478 | unsigned int num_user_features, |
479 | const int *coords, |
480 | unsigned int num_coords, |
481 | const char * const *shaper_list) |
482 | { |
483 | DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr, |
484 | "face=%p num_features=%d shaper_list=%p" , |
485 | face, |
486 | num_user_features, |
487 | shaper_list); |
488 | |
489 | hb_shape_plan_proposal_t proposal = { |
490 | .props: *props, |
491 | .shaper_list: shaper_list, |
492 | .user_features: user_features, |
493 | .num_user_features: num_user_features, |
494 | .coords: nullptr |
495 | }; |
496 | |
497 | if (shaper_list) { |
498 | /* Choose shaper. Adapted from hb_shape_plan_plan(). |
499 | * Must choose shaper exactly the same way as that function. */ |
500 | for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++) |
501 | if (0) |
502 | ; |
503 | #define HB_SHAPER_IMPLEMENT(shaper) \ |
504 | else if (0 == strcmp (*shaper_item, #shaper) && \ |
505 | hb_##shaper##_shaper_face_data_ensure (face)) \ |
506 | { \ |
507 | proposal.shaper_func = _hb_##shaper##_shape; \ |
508 | break; \ |
509 | } |
510 | #include "hb-shaper-list.hh" |
511 | #undef HB_SHAPER_IMPLEMENT |
512 | |
513 | if (unlikely (!proposal.shaper_func)) |
514 | return hb_shape_plan_get_empty (); |
515 | } |
516 | |
517 | |
518 | retry: |
519 | hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans); |
520 | |
521 | /* Don't look for plan in the cache if there were variation coordinates XXX Fix me. */ |
522 | if (!hb_coords_present (coords, num_coords)) |
523 | for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next) |
524 | if (hb_shape_plan_matches (shape_plan: node->shape_plan, proposal: &proposal)) |
525 | { |
526 | DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache" ); |
527 | return hb_shape_plan_reference (shape_plan: node->shape_plan); |
528 | } |
529 | |
530 | /* Not found. */ |
531 | hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props, |
532 | user_features, num_user_features, |
533 | orig_coords: coords, num_coords, |
534 | shaper_list); |
535 | |
536 | /* Don't add to the cache if face is inert. */ |
537 | if (unlikely (hb_object_is_inert (face))) |
538 | return shape_plan; |
539 | |
540 | /* Don't add the plan to the cache if there were user features with non-global ranges */ |
541 | if (hb_non_global_user_features_present (user_features, num_user_features)) |
542 | return shape_plan; |
543 | /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */ |
544 | if (hb_coords_present (coords, num_coords)) |
545 | return shape_plan; |
546 | |
547 | hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (nmemb: 1, size: sizeof (hb_face_t::plan_node_t)); |
548 | if (unlikely (!node)) |
549 | return shape_plan; |
550 | |
551 | node->shape_plan = shape_plan; |
552 | node->next = cached_plan_nodes; |
553 | |
554 | if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) { |
555 | hb_shape_plan_destroy (shape_plan); |
556 | free (ptr: node); |
557 | goto retry; |
558 | } |
559 | DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache" ); |
560 | |
561 | return hb_shape_plan_reference (shape_plan); |
562 | } |
563 | |
564 | /** |
565 | * hb_shape_plan_get_shaper: |
566 | * @shape_plan: a shape plan. |
567 | * |
568 | * |
569 | * |
570 | * Return value: (transfer none): |
571 | * |
572 | * Since: 0.9.7 |
573 | **/ |
574 | const char * |
575 | hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) |
576 | { |
577 | return shape_plan->shaper_name; |
578 | } |
579 | |