• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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