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