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