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