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-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
     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 *
    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 *
    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 *
    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 *
    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
    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
    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 *
    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
    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
    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
    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
    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
    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
    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 *
    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 *
    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 *
    582 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
    583 {
    584   return shape_plan->shaper_name;
    585 }
    586