Home | History | Annotate | Download | only in src
      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
     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
    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
    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 *
    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 *
    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 *
    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 *
    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
    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
    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 *
    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 *
    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
    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 *
    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 *
    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