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)
38 #define HB_SHAPER_IMPLEMENT(shaper) \
39 HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
40 HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
41 #include "hb-shaper-list.hh"
42 #undef HB_SHAPER_IMPLEMENT
46 hb_shape_plan_plan (hb_shape_plan_t
*shape_plan
,
47 const hb_feature_t
*user_features
,
48 unsigned int num_user_features
,
49 const char * const *shaper_list
)
51 DEBUG_MSG_FUNC (SHAPE_PLAN
, shape_plan
,
52 "num_features=%d shaper_list=%p",
56 const hb_shaper_pair_t
*shapers
= _hb_shapers_get ();
58 #define HB_SHAPER_PLAN(shaper) \
60 if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
61 HB_SHAPER_DATA (shaper, shape_plan) = \
62 HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
63 shape_plan->shaper_func = _hb_##shaper##_shape; \
64 shape_plan->shaper_name = #shaper; \
69 if (likely (!shaper_list
)) {
70 for (unsigned int i
= 0; i
< HB_SHAPERS_COUNT
; i
++)
73 #define HB_SHAPER_IMPLEMENT(shaper) \
74 else if (shapers[i].func == _hb_##shaper##_shape) \
75 HB_SHAPER_PLAN (shaper);
76 #include "hb-shaper-list.hh"
77 #undef HB_SHAPER_IMPLEMENT
79 for (; *shaper_list
; shaper_list
++)
82 #define HB_SHAPER_IMPLEMENT(shaper) \
83 else if (0 == strcmp (*shaper_list, #shaper)) \
84 HB_SHAPER_PLAN (shaper);
85 #include "hb-shaper-list.hh"
86 #undef HB_SHAPER_IMPLEMENT
98 * hb_shape_plan_create: (Xconstructor)
101 * @user_features: (array length=num_user_features):
102 * @num_user_features:
103 * @shaper_list: (array zero-terminated=1):
107 * Return value: (transfer full):
112 hb_shape_plan_create (hb_face_t
*face
,
113 const hb_segment_properties_t
*props
,
114 const hb_feature_t
*user_features
,
115 unsigned int num_user_features
,
116 const char * const *shaper_list
)
118 DEBUG_MSG_FUNC (SHAPE_PLAN
, NULL
,
119 "face=%p num_features=%d shaper_list=%p",
124 hb_shape_plan_t
*shape_plan
;
125 hb_feature_t
*features
= NULL
;
127 if (unlikely (!face
))
128 face
= hb_face_get_empty ();
129 if (unlikely (!props
|| hb_object_is_inert (face
)))
130 return hb_shape_plan_get_empty ();
131 if (num_user_features
&& !(features
= (hb_feature_t
*) malloc (num_user_features
* sizeof (hb_feature_t
))))
132 return hb_shape_plan_get_empty ();
133 if (!(shape_plan
= hb_object_create
<hb_shape_plan_t
> ())) {
135 return hb_shape_plan_get_empty ();
138 assert (props
->direction
!= HB_DIRECTION_INVALID
);
140 hb_face_make_immutable (face
);
141 shape_plan
->default_shaper_list
= shaper_list
== NULL
;
142 shape_plan
->face_unsafe
= face
;
143 shape_plan
->props
= *props
;
144 shape_plan
->num_user_features
= num_user_features
;
145 shape_plan
->user_features
= features
;
146 if (num_user_features
)
147 memcpy (features
, user_features
, num_user_features
* sizeof (hb_feature_t
));
149 hb_shape_plan_plan (shape_plan
, user_features
, num_user_features
, shaper_list
);
155 * hb_shape_plan_get_empty:
159 * Return value: (transfer full):
164 hb_shape_plan_get_empty (void)
166 static const hb_shape_plan_t _hb_shape_plan_nil
= {
167 HB_OBJECT_HEADER_STATIC
,
169 true, /* default_shaper_list */
171 HB_SEGMENT_PROPERTIES_DEFAULT
, /* props */
173 NULL
, /* shaper_func */
174 NULL
, /* shaper_name */
176 NULL
, /* user_features */
177 0, /* num_user_featurs */
180 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
181 #include "hb-shaper-list.hh"
182 #undef HB_SHAPER_IMPLEMENT
186 return const_cast<hb_shape_plan_t
*> (&_hb_shape_plan_nil
);
190 * hb_shape_plan_reference: (skip)
191 * @shape_plan: a shape plan.
195 * Return value: (transfer full):
200 hb_shape_plan_reference (hb_shape_plan_t
*shape_plan
)
202 return hb_object_reference (shape_plan
);
206 * hb_shape_plan_destroy: (skip)
207 * @shape_plan: a shape plan.
214 hb_shape_plan_destroy (hb_shape_plan_t
*shape_plan
)
216 if (!hb_object_destroy (shape_plan
)) return;
218 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
219 #include "hb-shaper-list.hh"
220 #undef HB_SHAPER_IMPLEMENT
222 free (shape_plan
->user_features
);
228 * hb_shape_plan_set_user_data: (skip)
229 * @shape_plan: a shape plan.
242 hb_shape_plan_set_user_data (hb_shape_plan_t
*shape_plan
,
243 hb_user_data_key_t
*key
,
245 hb_destroy_func_t destroy
,
248 return hb_object_set_user_data (shape_plan
, key
, data
, destroy
, replace
);
252 * hb_shape_plan_get_user_data: (skip)
253 * @shape_plan: a shape plan.
258 * Return value: (transfer none):
263 hb_shape_plan_get_user_data (hb_shape_plan_t
*shape_plan
,
264 hb_user_data_key_t
*key
)
266 return hb_object_get_user_data (shape_plan
, key
);
271 * hb_shape_plan_execute:
272 * @shape_plan: a shape plan.
275 * @features: (array length=num_features):
285 hb_shape_plan_execute (hb_shape_plan_t
*shape_plan
,
288 const hb_feature_t
*features
,
289 unsigned int num_features
)
291 DEBUG_MSG_FUNC (SHAPE_PLAN
, shape_plan
,
292 "num_features=%d shaper_func=%p",
294 shape_plan
->shaper_func
);
296 if (unlikely (hb_object_is_inert (shape_plan
) ||
297 hb_object_is_inert (font
) ||
298 hb_object_is_inert (buffer
)))
301 assert (shape_plan
->face_unsafe
== font
->face
);
302 assert (hb_segment_properties_equal (&shape_plan
->props
, &buffer
->props
));
304 #define HB_SHAPER_EXECUTE(shaper) \
306 return HB_SHAPER_DATA (shaper, shape_plan) && \
307 hb_##shaper##_shaper_font_data_ensure (font) && \
308 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
313 #define HB_SHAPER_IMPLEMENT(shaper) \
314 else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
315 HB_SHAPER_EXECUTE (shaper);
316 #include "hb-shaper-list.hh"
317 #undef HB_SHAPER_IMPLEMENT
319 #undef HB_SHAPER_EXECUTE
331 hb_shape_plan_hash (const hb_shape_plan_t
*shape_plan
)
333 return hb_segment_properties_hash (&shape_plan
->props
) +
334 shape_plan
->default_shaper_list
? 0 : (intptr_t) shape_plan
->shaper_func
;
338 /* User-feature caching is currently somewhat dumb:
339 * it only finds matches where the feature array is identical,
340 * not cases where the feature lists would be compatible for plan purposes
341 * but have different ranges, for example.
343 struct hb_shape_plan_proposal_t
345 const hb_segment_properties_t props
;
346 const char * const *shaper_list
;
347 const hb_feature_t
*user_features
;
348 unsigned int num_user_features
;
349 hb_shape_func_t
*shaper_func
;
352 static inline hb_bool_t
353 hb_shape_plan_user_features_match (const hb_shape_plan_t
*shape_plan
,
354 const hb_shape_plan_proposal_t
*proposal
)
356 if (proposal
->num_user_features
!= shape_plan
->num_user_features
) return false;
357 for (unsigned int i
= 0, n
= proposal
->num_user_features
; i
< n
; i
++)
358 if (proposal
->user_features
[i
].tag
!= shape_plan
->user_features
[i
].tag
||
359 proposal
->user_features
[i
].value
!= shape_plan
->user_features
[i
].value
||
360 proposal
->user_features
[i
].start
!= shape_plan
->user_features
[i
].start
||
361 proposal
->user_features
[i
].end
!= shape_plan
->user_features
[i
].end
) return false;
366 hb_shape_plan_matches (const hb_shape_plan_t
*shape_plan
,
367 const hb_shape_plan_proposal_t
*proposal
)
369 return hb_segment_properties_equal (&shape_plan
->props
, &proposal
->props
) &&
370 hb_shape_plan_user_features_match (shape_plan
, proposal
) &&
371 ((shape_plan
->default_shaper_list
&& proposal
->shaper_list
== NULL
) ||
372 (shape_plan
->shaper_func
== proposal
->shaper_func
));
375 static inline hb_bool_t
376 hb_non_global_user_features_present (const hb_feature_t
*user_features
,
377 unsigned int num_user_features
)
379 while (num_user_features
)
380 if (user_features
->start
!= 0 || user_features
->end
!= (unsigned int) -1)
383 num_user_features
--, user_features
++;
388 * hb_shape_plan_create_cached:
391 * @user_features: (array length=num_user_features):
392 * @num_user_features:
393 * @shaper_list: (array zero-terminated=1):
397 * Return value: (transfer full):
402 hb_shape_plan_create_cached (hb_face_t
*face
,
403 const hb_segment_properties_t
*props
,
404 const hb_feature_t
*user_features
,
405 unsigned int num_user_features
,
406 const char * const *shaper_list
)
408 DEBUG_MSG_FUNC (SHAPE_PLAN
, NULL
,
409 "face=%p num_features=%d shaper_list=%p",
414 hb_shape_plan_proposal_t proposal
= {
423 /* Choose shaper. Adapted from hb_shape_plan_plan().
424 * Must choose shaper exactly the same way as that function. */
425 for (const char * const *shaper_item
= shaper_list
; *shaper_item
; shaper_item
++)
428 #define HB_SHAPER_IMPLEMENT(shaper) \
429 else if (0 == strcmp (*shaper_item, #shaper) && \
430 hb_##shaper##_shaper_face_data_ensure (face)) \
432 proposal.shaper_func = _hb_##shaper##_shape; \
435 #include "hb-shaper-list.hh"
436 #undef HB_SHAPER_IMPLEMENT
438 if (unlikely (!proposal
.shaper_func
))
439 return hb_shape_plan_get_empty ();
444 hb_face_t::plan_node_t
*cached_plan_nodes
= (hb_face_t::plan_node_t
*) hb_atomic_ptr_get (&face
->shape_plans
);
445 for (hb_face_t::plan_node_t
*node
= cached_plan_nodes
; node
; node
= node
->next
)
446 if (hb_shape_plan_matches (node
->shape_plan
, &proposal
))
448 DEBUG_MSG_FUNC (SHAPE_PLAN
, node
->shape_plan
, "fulfilled from cache");
449 return hb_shape_plan_reference (node
->shape_plan
);
454 hb_shape_plan_t
*shape_plan
= hb_shape_plan_create (face
, props
, user_features
, num_user_features
, shaper_list
);
456 /* Don't add the plan to the cache if there were user features with non-global ranges */
458 if (hb_non_global_user_features_present (user_features
, num_user_features
))
461 hb_face_t::plan_node_t
*node
= (hb_face_t::plan_node_t
*) calloc (1, sizeof (hb_face_t::plan_node_t
));
462 if (unlikely (!node
))
465 node
->shape_plan
= shape_plan
;
466 node
->next
= cached_plan_nodes
;
468 if (!hb_atomic_ptr_cmpexch (&face
->shape_plans
, cached_plan_nodes
, node
)) {
469 hb_shape_plan_destroy (shape_plan
);
473 DEBUG_MSG_FUNC (SHAPE_PLAN
, shape_plan
, "inserted into cache");
475 return hb_shape_plan_reference (shape_plan
);
479 * hb_shape_plan_get_shaper:
480 * @shape_plan: a shape plan.
484 * Return value: (transfer none):
489 hb_shape_plan_get_shaper (hb_shape_plan_t
*shape_plan
)
491 return shape_plan
->shaper_name
;