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 #define HB_SHAPER_IMPLEMENT(shaper) \
33 HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
34 HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
35 #include "hb-shaper-list.hh"
36 #undef HB_SHAPER_IMPLEMENT
37
38
39 static void
hb_shape_plan_plan(hb_shape_plan_t * shape_plan,const hb_feature_t * user_features,unsigned int num_user_features,const char * const * shaper_list)40 hb_shape_plan_plan (hb_shape_plan_t *shape_plan,
41 const hb_feature_t *user_features,
42 unsigned int num_user_features,
43 const char * const *shaper_list)
44 {
45 const hb_shaper_pair_t *shapers = _hb_shapers_get ();
46
47 #define HB_SHAPER_PLAN(shaper) \
48 HB_STMT_START { \
49 if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
50 HB_SHAPER_DATA (shaper, shape_plan) = \
51 HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
52 shape_plan->shaper_func = _hb_##shaper##_shape; \
53 shape_plan->shaper_name = #shaper; \
54 return; \
55 } \
56 } HB_STMT_END
57
58 if (likely (!shaper_list)) {
59 for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
60 if (0)
61 ;
62 #define HB_SHAPER_IMPLEMENT(shaper) \
63 else if (shapers[i].func == _hb_##shaper##_shape) \
64 HB_SHAPER_PLAN (shaper);
65 #include "hb-shaper-list.hh"
66 #undef HB_SHAPER_IMPLEMENT
67 } else {
68 for (; *shaper_list; shaper_list++)
69 if (0)
70 ;
71 #define HB_SHAPER_IMPLEMENT(shaper) \
72 else if (0 == strcmp (*shaper_list, #shaper)) \
73 HB_SHAPER_PLAN (shaper);
74 #include "hb-shaper-list.hh"
75 #undef HB_SHAPER_IMPLEMENT
76 }
77
78 #undef HB_SHAPER_PLAN
79 }
80
81
82 /*
83 * hb_shape_plan_t
84 */
85
86 /**
87 * hb_shape_plan_create: (Xconstructor)
88 * @face:
89 * @props:
90 * @user_features: (array length=num_user_features):
91 * @num_user_features:
92 * @shaper_list: (array zero-terminated=1):
93 *
94 *
95 *
96 * Return value: (transfer full):
97 *
98 * Since: 1.0
99 **/
100 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)101 hb_shape_plan_create (hb_face_t *face,
102 const hb_segment_properties_t *props,
103 const hb_feature_t *user_features,
104 unsigned int num_user_features,
105 const char * const *shaper_list)
106 {
107 hb_shape_plan_t *shape_plan;
108 hb_feature_t *features = NULL;
109
110 if (unlikely (!face))
111 face = hb_face_get_empty ();
112 if (unlikely (!props || hb_object_is_inert (face)))
113 return hb_shape_plan_get_empty ();
114 if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t))))
115 return hb_shape_plan_get_empty ();
116 if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) {
117 free (features);
118 return hb_shape_plan_get_empty ();
119 }
120
121 assert (props->direction != HB_DIRECTION_INVALID);
122
123 hb_face_make_immutable (face);
124 shape_plan->default_shaper_list = shaper_list == NULL;
125 shape_plan->face_unsafe = face;
126 shape_plan->props = *props;
127 shape_plan->num_user_features = num_user_features;
128 shape_plan->user_features = features;
129 if (num_user_features)
130 memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
131
132 hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
133
134 return shape_plan;
135 }
136
137 /**
138 * hb_shape_plan_get_empty:
139 *
140 *
141 *
142 * Return value: (transfer full):
143 *
144 * Since: 1.0
145 **/
146 hb_shape_plan_t *
hb_shape_plan_get_empty(void)147 hb_shape_plan_get_empty (void)
148 {
149 static const hb_shape_plan_t _hb_shape_plan_nil = {
150 HB_OBJECT_HEADER_STATIC,
151
152 true, /* default_shaper_list */
153 NULL, /* face */
154 HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
155
156 NULL, /* shaper_func */
157 NULL, /* shaper_name */
158
159 NULL, /* user_features */
160 0, /* num_user_featurs */
161
162 {
163 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
164 #include "hb-shaper-list.hh"
165 #undef HB_SHAPER_IMPLEMENT
166 }
167 };
168
169 return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
170 }
171
172 /**
173 * hb_shape_plan_reference: (skip)
174 * @shape_plan: a shape plan.
175 *
176 *
177 *
178 * Return value: (transfer full):
179 *
180 * Since: 1.0
181 **/
182 hb_shape_plan_t *
hb_shape_plan_reference(hb_shape_plan_t * shape_plan)183 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
184 {
185 return hb_object_reference (shape_plan);
186 }
187
188 /**
189 * hb_shape_plan_destroy: (skip)
190 * @shape_plan: a shape plan.
191 *
192 *
193 *
194 * Since: 1.0
195 **/
196 void
hb_shape_plan_destroy(hb_shape_plan_t * shape_plan)197 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
198 {
199 if (!hb_object_destroy (shape_plan)) return;
200
201 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
202 #include "hb-shaper-list.hh"
203 #undef HB_SHAPER_IMPLEMENT
204
205 free (shape_plan->user_features);
206
207 free (shape_plan);
208 }
209
210 /**
211 * hb_shape_plan_set_user_data: (skip)
212 * @shape_plan: a shape plan.
213 * @key:
214 * @data:
215 * @destroy:
216 * @replace:
217 *
218 *
219 *
220 * Return value:
221 *
222 * Since: 1.0
223 **/
224 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)225 hb_shape_plan_set_user_data (hb_shape_plan_t *shape_plan,
226 hb_user_data_key_t *key,
227 void * data,
228 hb_destroy_func_t destroy,
229 hb_bool_t replace)
230 {
231 return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
232 }
233
234 /**
235 * hb_shape_plan_get_user_data: (skip)
236 * @shape_plan: a shape plan.
237 * @key:
238 *
239 *
240 *
241 * Return value: (transfer none):
242 *
243 * Since: 1.0
244 **/
245 void *
hb_shape_plan_get_user_data(hb_shape_plan_t * shape_plan,hb_user_data_key_t * key)246 hb_shape_plan_get_user_data (hb_shape_plan_t *shape_plan,
247 hb_user_data_key_t *key)
248 {
249 return hb_object_get_user_data (shape_plan, key);
250 }
251
252
253 /**
254 * hb_shape_plan_execute:
255 * @shape_plan: a shape plan.
256 * @font: a font.
257 * @buffer: a buffer.
258 * @features: (array length=num_features):
259 * @num_features:
260 *
261 *
262 *
263 * Return value:
264 *
265 * Since: 1.0
266 **/
267 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)268 hb_shape_plan_execute (hb_shape_plan_t *shape_plan,
269 hb_font_t *font,
270 hb_buffer_t *buffer,
271 const hb_feature_t *features,
272 unsigned int num_features)
273 {
274 if (unlikely (hb_object_is_inert (shape_plan) ||
275 hb_object_is_inert (font) ||
276 hb_object_is_inert (buffer)))
277 return false;
278
279 assert (shape_plan->face_unsafe == font->face);
280 assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
281
282 #define HB_SHAPER_EXECUTE(shaper) \
283 HB_STMT_START { \
284 return HB_SHAPER_DATA (shaper, shape_plan) && \
285 hb_##shaper##_shaper_font_data_ensure (font) && \
286 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
287 } HB_STMT_END
288
289 if (0)
290 ;
291 #define HB_SHAPER_IMPLEMENT(shaper) \
292 else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
293 HB_SHAPER_EXECUTE (shaper);
294 #include "hb-shaper-list.hh"
295 #undef HB_SHAPER_IMPLEMENT
296
297 #undef HB_SHAPER_EXECUTE
298
299 return false;
300 }
301
302
303 /*
304 * caching
305 */
306
307 #if 0
308 static unsigned int
309 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
310 {
311 return hb_segment_properties_hash (&shape_plan->props) +
312 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
313 }
314 #endif
315
316 /* User-feature caching is currently somewhat dumb:
317 * it only finds matches where the feature array is identical,
318 * not cases where the feature lists would be compatible for plan purposes
319 * but have different ranges, for example.
320 */
321 struct hb_shape_plan_proposal_t
322 {
323 const hb_segment_properties_t props;
324 const char * const *shaper_list;
325 const hb_feature_t *user_features;
326 unsigned int num_user_features;
327 hb_shape_func_t *shaper_func;
328 };
329
330 static inline hb_bool_t
hb_shape_plan_user_features_match(const hb_shape_plan_t * shape_plan,const hb_shape_plan_proposal_t * proposal)331 hb_shape_plan_user_features_match (const hb_shape_plan_t *shape_plan,
332 const hb_shape_plan_proposal_t *proposal)
333 {
334 if (proposal->num_user_features != shape_plan->num_user_features) return false;
335 for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
336 if (proposal->user_features[i].tag != shape_plan->user_features[i].tag ||
337 proposal->user_features[i].value != shape_plan->user_features[i].value ||
338 proposal->user_features[i].start != shape_plan->user_features[i].start ||
339 proposal->user_features[i].end != shape_plan->user_features[i].end) return false;
340 return true;
341 }
342
343 static hb_bool_t
hb_shape_plan_matches(const hb_shape_plan_t * shape_plan,const hb_shape_plan_proposal_t * proposal)344 hb_shape_plan_matches (const hb_shape_plan_t *shape_plan,
345 const hb_shape_plan_proposal_t *proposal)
346 {
347 return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
348 hb_shape_plan_user_features_match (shape_plan, proposal) &&
349 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
350 (shape_plan->shaper_func == proposal->shaper_func));
351 }
352
353 static inline hb_bool_t
hb_non_global_user_features_present(const hb_feature_t * user_features,unsigned int num_user_features)354 hb_non_global_user_features_present (const hb_feature_t *user_features,
355 unsigned int num_user_features)
356 {
357 while (num_user_features)
358 if (user_features->start != 0 || user_features->end != (unsigned int) -1)
359 return true;
360 else
361 num_user_features--, user_features++;
362 return false;
363 }
364
365 /**
366 * hb_shape_plan_create_cached:
367 * @face:
368 * @props:
369 * @user_features: (array length=num_user_features):
370 * @num_user_features:
371 * @shaper_list: (array zero-terminated=1):
372 *
373 *
374 *
375 * Return value: (transfer full):
376 *
377 * Since: 1.0
378 **/
379 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)380 hb_shape_plan_create_cached (hb_face_t *face,
381 const hb_segment_properties_t *props,
382 const hb_feature_t *user_features,
383 unsigned int num_user_features,
384 const char * const *shaper_list)
385 {
386 hb_shape_plan_proposal_t proposal = {
387 *props,
388 shaper_list,
389 user_features,
390 num_user_features,
391 NULL
392 };
393
394 if (shaper_list) {
395 /* Choose shaper. Adapted from hb_shape_plan_plan(). */
396 #define HB_SHAPER_PLAN(shaper) \
397 HB_STMT_START { \
398 if (hb_##shaper##_shaper_face_data_ensure (face)) \
399 proposal.shaper_func = _hb_##shaper##_shape; \
400 } HB_STMT_END
401
402 for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
403 if (0)
404 ;
405 #define HB_SHAPER_IMPLEMENT(shaper) \
406 else if (0 == strcmp (*shaper_item, #shaper)) \
407 HB_SHAPER_PLAN (shaper);
408 #include "hb-shaper-list.hh"
409 #undef HB_SHAPER_IMPLEMENT
410
411 #undef HB_SHAPER_PLAN
412
413 if (unlikely (!proposal.shaper_list))
414 return hb_shape_plan_get_empty ();
415 }
416
417
418 retry:
419 hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
420 for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
421 if (hb_shape_plan_matches (node->shape_plan, &proposal))
422 return hb_shape_plan_reference (node->shape_plan);
423
424 /* Not found. */
425
426 hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
427
428 /* Don't add the plan to the cache if there were user features with non-global ranges */
429
430 if (hb_non_global_user_features_present (user_features, num_user_features))
431 return shape_plan;
432
433 hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
434 if (unlikely (!node))
435 return shape_plan;
436
437 node->shape_plan = shape_plan;
438 node->next = cached_plan_nodes;
439
440 if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
441 hb_shape_plan_destroy (shape_plan);
442 free (node);
443 goto retry;
444 }
445
446 return hb_shape_plan_reference (shape_plan);
447 }
448
449 /**
450 * hb_shape_plan_get_shaper:
451 * @shape_plan: a shape plan.
452 *
453 *
454 *
455 * Return value: (transfer none):
456 *
457 * Since: 1.0
458 **/
459 const char *
hb_shape_plan_get_shaper(hb_shape_plan_t * shape_plan)460 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
461 {
462 return shape_plan->shaper_name;
463 }
464