• 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.hh"
28 #include "hb-shape-plan.hh"
29 #include "hb-shaper.hh"
30 #include "hb-font.hh"
31 #include "hb-buffer.hh"
32 
33 
34 /**
35  * SECTION:hb-shape-plan
36  * @title: hb-shape-plan
37  * @short_description: Object representing a shaping plan
38  * @include: hb.h
39  *
40  * Shape plans are not used for shaping directly, but can be access to query
41  * certain information about how shaping will perform given a set of input
42  * parameters (script, language, direction, features, etc.)
43  * Most client would not need to deal with shape plans directly.
44  **/
45 
46 
47 /*
48  * hb_shape_plan_key_t
49  */
50 
51 bool
init(bool copy,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)52 hb_shape_plan_key_t::init (bool                           copy,
53 			   hb_face_t                     *face,
54 			   const hb_segment_properties_t *props,
55 			   const hb_feature_t            *user_features,
56 			   unsigned int                   num_user_features,
57 			   const int                     *coords,
58 			   unsigned int                   num_coords,
59 			   const char * const            *shaper_list)
60 {
61   hb_feature_t *features = nullptr;
62   if (copy && num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
63     goto bail;
64 
65   this->props = *props;
66   this->num_user_features = num_user_features;
67   this->user_features = copy ? features : user_features;
68   if (copy && num_user_features)
69   {
70     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
71     /* Make start/end uniform to easier catch bugs. */
72     for (unsigned int i = 0; i < num_user_features; i++)
73     {
74       if (features[0].start != HB_FEATURE_GLOBAL_START)
75 	features[0].start = 1;
76       if (features[0].end   != HB_FEATURE_GLOBAL_END)
77 	features[0].end   = 2;
78     }
79   }
80   this->shaper_func = nullptr;
81   this->shaper_name = nullptr;
82 #ifndef HB_NO_OT_SHAPE
83   this->ot.init (face, coords, num_coords);
84 #endif
85 
86   /*
87    * Choose shaper.
88    */
89 
90 #define HB_SHAPER_PLAN(shaper) \
91 	HB_STMT_START { \
92 	  if (face->data.shaper) \
93 	  { \
94 	    this->shaper_func = _hb_##shaper##_shape; \
95 	    this->shaper_name = #shaper; \
96 	    return true; \
97 	  } \
98 	} HB_STMT_END
99 
100   if (unlikely (shaper_list))
101   {
102     for (; *shaper_list; shaper_list++)
103       if (false)
104 	;
105 #define HB_SHAPER_IMPLEMENT(shaper) \
106       else if (0 == strcmp (*shaper_list, #shaper)) \
107 	HB_SHAPER_PLAN (shaper);
108 #include "hb-shaper-list.hh"
109 #undef HB_SHAPER_IMPLEMENT
110   }
111   else
112   {
113     const hb_shaper_entry_t *shapers = _hb_shapers_get ();
114     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
115       if (false)
116 	;
117 #define HB_SHAPER_IMPLEMENT(shaper) \
118       else if (shapers[i].func == _hb_##shaper##_shape) \
119 	HB_SHAPER_PLAN (shaper);
120 #include "hb-shaper-list.hh"
121 #undef HB_SHAPER_IMPLEMENT
122   }
123 #undef HB_SHAPER_PLAN
124 
125 bail:
126   ::free (features);
127   return false;
128 }
129 
130 bool
user_features_match(const hb_shape_plan_key_t * other)131 hb_shape_plan_key_t::user_features_match (const hb_shape_plan_key_t *other)
132 {
133   if (this->num_user_features != other->num_user_features)
134     return false;
135   for (unsigned int i = 0; i < num_user_features; i++)
136   {
137     if (this->user_features[i].tag   != other->user_features[i].tag   ||
138 	this->user_features[i].value != other->user_features[i].value ||
139 	(this->user_features[i].start == HB_FEATURE_GLOBAL_START &&
140 	 this->user_features[i].end   == HB_FEATURE_GLOBAL_END) !=
141 	(other->user_features[i].start == HB_FEATURE_GLOBAL_START &&
142 	 other->user_features[i].end   == HB_FEATURE_GLOBAL_END))
143       return false;
144   }
145   return true;
146 }
147 
148 bool
equal(const hb_shape_plan_key_t * other)149 hb_shape_plan_key_t::equal (const hb_shape_plan_key_t *other)
150 {
151   return hb_segment_properties_equal (&this->props, &other->props) &&
152 	 this->user_features_match (other) &&
153 #ifndef HB_NO_OT_SHAPE
154 	 this->ot.equal (&other->ot) &&
155 #endif
156 	 this->shaper_func == other->shaper_func;
157 }
158 
159 
160 /*
161  * hb_shape_plan_t
162  */
163 
164 
165 /**
166  * hb_shape_plan_create: (Xconstructor)
167  * @face:
168  * @props:
169  * @user_features: (array length=num_user_features):
170  * @num_user_features:
171  * @shaper_list: (array zero-terminated=1):
172  *
173  *
174  *
175  * Return value: (transfer full):
176  *
177  * Since: 0.9.7
178  **/
179 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)180 hb_shape_plan_create (hb_face_t                     *face,
181 		      const hb_segment_properties_t *props,
182 		      const hb_feature_t            *user_features,
183 		      unsigned int                   num_user_features,
184 		      const char * const            *shaper_list)
185 {
186   return hb_shape_plan_create2 (face, props,
187 				user_features, num_user_features,
188 				nullptr, 0,
189 				shaper_list);
190 }
191 
192 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 * coords,unsigned int num_coords,const char * const * shaper_list)193 hb_shape_plan_create2 (hb_face_t                     *face,
194 		       const hb_segment_properties_t *props,
195 		       const hb_feature_t            *user_features,
196 		       unsigned int                   num_user_features,
197 		       const int                     *coords,
198 		       unsigned int                   num_coords,
199 		       const char * const            *shaper_list)
200 {
201   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
202 		  "face=%p num_features=%d num_coords=%d shaper_list=%p",
203 		  face,
204 		  num_user_features,
205 		  num_coords,
206 		  shaper_list);
207 
208   assert (props->direction != HB_DIRECTION_INVALID);
209 
210   hb_shape_plan_t *shape_plan;
211 
212   if (unlikely (!props))
213     goto bail;
214   if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
215     goto bail;
216 
217   if (unlikely (!face))
218     face = hb_face_get_empty ();
219   hb_face_make_immutable (face);
220   shape_plan->face_unsafe = face;
221 
222   if (unlikely (!shape_plan->key.init (true,
223 				       face,
224 				       props,
225 				       user_features,
226 				       num_user_features,
227 				       coords,
228 				       num_coords,
229 				       shaper_list)))
230     goto bail2;
231 #ifndef HB_NO_OT_SHAPE
232   if (unlikely (!shape_plan->ot.init0 (face, &shape_plan->key)))
233     goto bail3;
234 #endif
235 
236   return shape_plan;
237 
238 #ifndef HB_NO_OT_SHAPE
239 bail3:
240 #endif
241   shape_plan->key.free ();
242 bail2:
243   free (shape_plan);
244 bail:
245   return hb_shape_plan_get_empty ();
246 }
247 
248 /**
249  * hb_shape_plan_get_empty:
250  *
251  *
252  *
253  * Return value: (transfer full):
254  *
255  * Since: 0.9.7
256  **/
257 hb_shape_plan_t *
hb_shape_plan_get_empty()258 hb_shape_plan_get_empty ()
259 {
260   return const_cast<hb_shape_plan_t *> (&Null(hb_shape_plan_t));
261 }
262 
263 /**
264  * hb_shape_plan_reference: (skip)
265  * @shape_plan: a shape plan.
266  *
267  *
268  *
269  * Return value: (transfer full):
270  *
271  * Since: 0.9.7
272  **/
273 hb_shape_plan_t *
hb_shape_plan_reference(hb_shape_plan_t * shape_plan)274 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
275 {
276   return hb_object_reference (shape_plan);
277 }
278 
279 /**
280  * hb_shape_plan_destroy: (skip)
281  * @shape_plan: a shape plan.
282  *
283  *
284  *
285  * Since: 0.9.7
286  **/
287 void
hb_shape_plan_destroy(hb_shape_plan_t * shape_plan)288 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
289 {
290   if (!hb_object_destroy (shape_plan)) return;
291 
292 #ifndef HB_NO_OT_SHAPE
293   shape_plan->ot.fini ();
294 #endif
295   shape_plan->key.free ();
296   free (shape_plan);
297 }
298 
299 /**
300  * hb_shape_plan_set_user_data: (skip)
301  * @shape_plan: a shape plan.
302  * @key:
303  * @data:
304  * @destroy:
305  * @replace:
306  *
307  *
308  *
309  * Return value:
310  *
311  * Since: 0.9.7
312  **/
313 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)314 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
315 			     hb_user_data_key_t *key,
316 			     void *              data,
317 			     hb_destroy_func_t   destroy,
318 			     hb_bool_t           replace)
319 {
320   return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
321 }
322 
323 /**
324  * hb_shape_plan_get_user_data: (skip)
325  * @shape_plan: a shape plan.
326  * @key:
327  *
328  *
329  *
330  * Return value: (transfer none):
331  *
332  * Since: 0.9.7
333  **/
334 void *
hb_shape_plan_get_user_data(hb_shape_plan_t * shape_plan,hb_user_data_key_t * key)335 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
336 			     hb_user_data_key_t *key)
337 {
338   return hb_object_get_user_data (shape_plan, key);
339 }
340 
341 /**
342  * hb_shape_plan_get_shaper:
343  * @shape_plan: a shape plan.
344  *
345  *
346  *
347  * Return value: (transfer none):
348  *
349  * Since: 0.9.7
350  **/
351 const char *
hb_shape_plan_get_shaper(hb_shape_plan_t * shape_plan)352 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
353 {
354   return shape_plan->key.shaper_name;
355 }
356 
357 
358 /**
359  * hb_shape_plan_execute:
360  * @shape_plan: a shape plan.
361  * @font: a font.
362  * @buffer: a buffer.
363  * @features: (array length=num_features):
364  * @num_features:
365  *
366  *
367  *
368  * Return value:
369  *
370  * Since: 0.9.7
371  **/
372 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)373 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
374 		       hb_font_t          *font,
375 		       hb_buffer_t        *buffer,
376 		       const hb_feature_t *features,
377 		       unsigned int        num_features)
378 {
379   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
380 		  "num_features=%d shaper_func=%p, shaper_name=%s",
381 		  num_features,
382 		  shape_plan->key.shaper_func,
383 		  shape_plan->key.shaper_name);
384 
385   if (unlikely (!buffer->len))
386     return true;
387 
388   assert (!hb_object_is_immutable (buffer));
389   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
390 
391   if (unlikely (hb_object_is_inert (shape_plan)))
392     return false;
393 
394   assert (shape_plan->face_unsafe == font->face);
395   assert (hb_segment_properties_equal (&shape_plan->key.props, &buffer->props));
396 
397 #define HB_SHAPER_EXECUTE(shaper) \
398 	HB_STMT_START { \
399 	  return font->data.shaper && \
400 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
401 	} HB_STMT_END
402 
403   if (false)
404     ;
405 #define HB_SHAPER_IMPLEMENT(shaper) \
406   else if (shape_plan->key.shaper_func == _hb_##shaper##_shape) \
407     HB_SHAPER_EXECUTE (shaper);
408 #include "hb-shaper-list.hh"
409 #undef HB_SHAPER_IMPLEMENT
410 
411 #undef HB_SHAPER_EXECUTE
412 
413   return false;
414 }
415 
416 
417 /*
418  * Caching
419  */
420 
421 /**
422  * hb_shape_plan_create_cached:
423  * @face:
424  * @props:
425  * @user_features: (array length=num_user_features):
426  * @num_user_features:
427  * @shaper_list: (array zero-terminated=1):
428  *
429  *
430  *
431  * Return value: (transfer full):
432  *
433  * Since: 0.9.7
434  **/
435 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)436 hb_shape_plan_create_cached (hb_face_t                     *face,
437 			     const hb_segment_properties_t *props,
438 			     const hb_feature_t            *user_features,
439 			     unsigned int                   num_user_features,
440 			     const char * const            *shaper_list)
441 {
442   return hb_shape_plan_create_cached2 (face, props,
443 				       user_features, num_user_features,
444 				       nullptr, 0,
445 				       shaper_list);
446 }
447 
448 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)449 hb_shape_plan_create_cached2 (hb_face_t                     *face,
450 			      const hb_segment_properties_t *props,
451 			      const hb_feature_t            *user_features,
452 			      unsigned int                   num_user_features,
453 			      const int                     *coords,
454 			      unsigned int                   num_coords,
455 			      const char * const            *shaper_list)
456 {
457   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
458 		  "face=%p num_features=%d shaper_list=%p",
459 		  face,
460 		  num_user_features,
461 		  shaper_list);
462 
463 retry:
464   hb_face_t::plan_node_t *cached_plan_nodes = face->shape_plans;
465 
466   bool dont_cache = hb_object_is_inert (face);
467 
468   if (likely (!dont_cache))
469   {
470     hb_shape_plan_key_t key;
471     if (!key.init (false,
472 		   face,
473 		   props,
474 		   user_features,
475 		   num_user_features,
476 		   coords,
477 		   num_coords,
478 		   shaper_list))
479       return hb_shape_plan_get_empty ();
480 
481     for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
482       if (node->shape_plan->key.equal (&key))
483       {
484 	DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
485 	return hb_shape_plan_reference (node->shape_plan);
486       }
487   }
488 
489   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
490 						       user_features, num_user_features,
491 						       coords, num_coords,
492 						       shaper_list);
493 
494   if (unlikely (dont_cache))
495     return shape_plan;
496 
497   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
498   if (unlikely (!node))
499     return shape_plan;
500 
501   node->shape_plan = shape_plan;
502   node->next = cached_plan_nodes;
503 
504   if (unlikely (!face->shape_plans.cmpexch (cached_plan_nodes, node)))
505   {
506     hb_shape_plan_destroy (shape_plan);
507     free (node);
508     goto retry;
509   }
510   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
511 
512   return hb_shape_plan_reference (shape_plan);
513 }
514