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 #define HB_SHAPER_IMPLEMENT(shaper) \
     33 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
     34 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, font)
     35 #include "hb-shaper-list.hh"
     36 #undef HB_SHAPER_IMPLEMENT
     37 
     38 
     39 static void
     40 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
     41 		    const hb_feature_t *user_features,
     42 		    unsigned int        num_user_features,
     43 		    const char * const *shaper_list)
     44 {
     45   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
     46 
     47 #define HB_SHAPER_PLAN(shaper) \
     48 	HB_STMT_START { \
     49 	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
     50 	    HB_SHAPER_DATA (shaper, shape_plan) = \
     51 	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
     52 	    shape_plan->shaper_func = _hb_##shaper##_shape; \
     53 	    shape_plan->shaper_name = #shaper; \
     54 	    return; \
     55 	  } \
     56 	} HB_STMT_END
     57 
     58   if (likely (!shaper_list)) {
     59     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
     60       if (0)
     61 	;
     62 #define HB_SHAPER_IMPLEMENT(shaper) \
     63       else if (shapers[i].func == _hb_##shaper##_shape) \
     64 	HB_SHAPER_PLAN (shaper);
     65 #include "hb-shaper-list.hh"
     66 #undef HB_SHAPER_IMPLEMENT
     67   } else {
     68     for (; *shaper_list; shaper_list++)
     69       if (0)
     70 	;
     71 #define HB_SHAPER_IMPLEMENT(shaper) \
     72       else if (0 == strcmp (*shaper_list, #shaper)) \
     73 	HB_SHAPER_PLAN (shaper);
     74 #include "hb-shaper-list.hh"
     75 #undef HB_SHAPER_IMPLEMENT
     76   }
     77 
     78 #undef HB_SHAPER_PLAN
     79 }
     80 
     81 
     82 /*
     83  * hb_shape_plan_t
     84  */
     85 
     86 /**
     87  * hb_shape_plan_create: (Xconstructor)
     88  * @face:
     89  * @props:
     90  * @user_features: (array length=num_user_features):
     91  * @num_user_features:
     92  * @shaper_list: (array zero-terminated=1):
     93  *
     94  *
     95  *
     96  * Return value: (transfer full):
     97  *
     98  * Since: 1.0
     99  **/
    100 hb_shape_plan_t *
    101 hb_shape_plan_create (hb_face_t                     *face,
    102 		      const hb_segment_properties_t *props,
    103 		      const hb_feature_t            *user_features,
    104 		      unsigned int                   num_user_features,
    105 		      const char * const            *shaper_list)
    106 {
    107   assert (props->direction != HB_DIRECTION_INVALID);
    108 
    109   hb_shape_plan_t *shape_plan;
    110   hb_feature_t *features = NULL;
    111 
    112   if (unlikely (!face))
    113     face = hb_face_get_empty ();
    114   if (unlikely (!props || hb_object_is_inert (face)))
    115     return hb_shape_plan_get_empty ();
    116   if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t))))
    117     return hb_shape_plan_get_empty ();
    118   if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) {
    119     free (features);
    120     return hb_shape_plan_get_empty ();
    121   }
    122 
    123   hb_face_make_immutable (face);
    124   shape_plan->default_shaper_list = shaper_list == NULL;
    125   shape_plan->face_unsafe = face;
    126   shape_plan->props = *props;
    127   shape_plan->num_user_features = num_user_features;
    128   shape_plan->user_features = features;
    129   if (num_user_features)
    130     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
    131 
    132   hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
    133 
    134   return shape_plan;
    135 }
    136 
    137 /**
    138  * hb_shape_plan_get_empty:
    139  *
    140  *
    141  *
    142  * Return value: (transfer full):
    143  *
    144  * Since: 1.0
    145  **/
    146 hb_shape_plan_t *
    147 hb_shape_plan_get_empty (void)
    148 {
    149   static const hb_shape_plan_t _hb_shape_plan_nil = {
    150     HB_OBJECT_HEADER_STATIC,
    151 
    152     true, /* default_shaper_list */
    153     NULL, /* face */
    154     HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
    155 
    156     NULL, /* shaper_func */
    157     NULL, /* shaper_name */
    158 
    159     NULL, /* user_features */
    160     0,    /* num_user_featurs */
    161 
    162     {
    163 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
    164 #include "hb-shaper-list.hh"
    165 #undef HB_SHAPER_IMPLEMENT
    166     }
    167   };
    168 
    169   return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
    170 }
    171 
    172 /**
    173  * hb_shape_plan_reference: (skip)
    174  * @shape_plan: a shape plan.
    175  *
    176  *
    177  *
    178  * Return value: (transfer full):
    179  *
    180  * Since: 1.0
    181  **/
    182 hb_shape_plan_t *
    183 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
    184 {
    185   return hb_object_reference (shape_plan);
    186 }
    187 
    188 /**
    189  * hb_shape_plan_destroy: (skip)
    190  * @shape_plan: a shape plan.
    191  *
    192  *
    193  *
    194  * Since: 1.0
    195  **/
    196 void
    197 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
    198 {
    199   if (!hb_object_destroy (shape_plan)) return;
    200 
    201 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
    202 #include "hb-shaper-list.hh"
    203 #undef HB_SHAPER_IMPLEMENT
    204 
    205   free (shape_plan->user_features);
    206 
    207   free (shape_plan);
    208 }
    209 
    210 /**
    211  * hb_shape_plan_set_user_data: (skip)
    212  * @shape_plan: a shape plan.
    213  * @key:
    214  * @data:
    215  * @destroy:
    216  * @replace:
    217  *
    218  *
    219  *
    220  * Return value:
    221  *
    222  * Since: 1.0
    223  **/
    224 hb_bool_t
    225 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
    226 			     hb_user_data_key_t *key,
    227 			     void *              data,
    228 			     hb_destroy_func_t   destroy,
    229 			     hb_bool_t           replace)
    230 {
    231   return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
    232 }
    233 
    234 /**
    235  * hb_shape_plan_get_user_data: (skip)
    236  * @shape_plan: a shape plan.
    237  * @key:
    238  *
    239  *
    240  *
    241  * Return value: (transfer none):
    242  *
    243  * Since: 1.0
    244  **/
    245 void *
    246 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
    247 			     hb_user_data_key_t *key)
    248 {
    249   return hb_object_get_user_data (shape_plan, key);
    250 }
    251 
    252 
    253 /**
    254  * hb_shape_plan_execute:
    255  * @shape_plan: a shape plan.
    256  * @font: a font.
    257  * @buffer: a buffer.
    258  * @features: (array length=num_features):
    259  * @num_features:
    260  *
    261  *
    262  *
    263  * Return value:
    264  *
    265  * Since: 1.0
    266  **/
    267 hb_bool_t
    268 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
    269 		       hb_font_t          *font,
    270 		       hb_buffer_t        *buffer,
    271 		       const hb_feature_t *features,
    272 		       unsigned int        num_features)
    273 {
    274   if (unlikely (hb_object_is_inert (shape_plan) ||
    275 		hb_object_is_inert (font) ||
    276 		hb_object_is_inert (buffer)))
    277     return false;
    278 
    279   assert (shape_plan->face_unsafe == font->face);
    280   assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
    281 
    282 #define HB_SHAPER_EXECUTE(shaper) \
    283 	HB_STMT_START { \
    284 	  return HB_SHAPER_DATA (shaper, shape_plan) && \
    285 		 hb_##shaper##_shaper_font_data_ensure (font) && \
    286 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
    287 	} HB_STMT_END
    288 
    289   if (0)
    290     ;
    291 #define HB_SHAPER_IMPLEMENT(shaper) \
    292   else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
    293     HB_SHAPER_EXECUTE (shaper);
    294 #include "hb-shaper-list.hh"
    295 #undef HB_SHAPER_IMPLEMENT
    296 
    297 #undef HB_SHAPER_EXECUTE
    298 
    299   return false;
    300 }
    301 
    302 
    303 /*
    304  * caching
    305  */
    306 
    307 #if 0
    308 static unsigned int
    309 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
    310 {
    311   return hb_segment_properties_hash (&shape_plan->props) +
    312 	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
    313 }
    314 #endif
    315 
    316 /* User-feature caching is currently somewhat dumb:
    317  * it only finds matches where the feature array is identical,
    318  * not cases where the feature lists would be compatible for plan purposes
    319  * but have different ranges, for example.
    320  */
    321 struct hb_shape_plan_proposal_t
    322 {
    323   const hb_segment_properties_t  props;
    324   const char * const            *shaper_list;
    325   const hb_feature_t            *user_features;
    326   unsigned int                   num_user_features;
    327   hb_shape_func_t               *shaper_func;
    328 };
    329 
    330 static inline hb_bool_t
    331 hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
    332 				   const hb_shape_plan_proposal_t *proposal)
    333 {
    334   if (proposal->num_user_features != shape_plan->num_user_features) return false;
    335   for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
    336     if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
    337         proposal->user_features[i].value != shape_plan->user_features[i].value ||
    338         proposal->user_features[i].start != shape_plan->user_features[i].start ||
    339         proposal->user_features[i].end   != shape_plan->user_features[i].end) return false;
    340   return true;
    341 }
    342 
    343 static hb_bool_t
    344 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
    345 		       const hb_shape_plan_proposal_t *proposal)
    346 {
    347   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
    348 	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
    349 	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
    350 	  (shape_plan->shaper_func == proposal->shaper_func));
    351 }
    352 
    353 static inline hb_bool_t
    354 hb_non_global_user_features_present (const hb_feature_t *user_features,
    355 				     unsigned int        num_user_features)
    356 {
    357   while (num_user_features)
    358     if (user_features->start != 0 || user_features->end != (unsigned int) -1)
    359       return true;
    360     else
    361       num_user_features--, user_features++;
    362   return false;
    363 }
    364 
    365 /**
    366  * hb_shape_plan_create_cached:
    367  * @face:
    368  * @props:
    369  * @user_features: (array length=num_user_features):
    370  * @num_user_features:
    371  * @shaper_list: (array zero-terminated=1):
    372  *
    373  *
    374  *
    375  * Return value: (transfer full):
    376  *
    377  * Since: 1.0
    378  **/
    379 hb_shape_plan_t *
    380 hb_shape_plan_create_cached (hb_face_t                     *face,
    381 			     const hb_segment_properties_t *props,
    382 			     const hb_feature_t            *user_features,
    383 			     unsigned int                   num_user_features,
    384 			     const char * const            *shaper_list)
    385 {
    386   hb_shape_plan_proposal_t proposal = {
    387     *props,
    388     shaper_list,
    389     user_features,
    390     num_user_features,
    391     NULL
    392   };
    393 
    394   if (shaper_list) {
    395     /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
    396 #define HB_SHAPER_PLAN(shaper) \
    397 	  HB_STMT_START { \
    398 	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
    399 	      proposal.shaper_func = _hb_##shaper##_shape; \
    400 	  } HB_STMT_END
    401 
    402     for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
    403       if (0)
    404 	;
    405 #define HB_SHAPER_IMPLEMENT(shaper) \
    406       else if (0 == strcmp (*shaper_item, #shaper)) \
    407 	HB_SHAPER_PLAN (shaper);
    408 #include "hb-shaper-list.hh"
    409 #undef HB_SHAPER_IMPLEMENT
    410 
    411 #undef HB_SHAPER_PLAN
    412 
    413     if (unlikely (!proposal.shaper_list))
    414       return hb_shape_plan_get_empty ();
    415   }
    416 
    417 
    418 retry:
    419   hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
    420   for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
    421     if (hb_shape_plan_matches (node->shape_plan, &proposal))
    422       return hb_shape_plan_reference (node->shape_plan);
    423 
    424   /* Not found. */
    425 
    426   hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
    427 
    428   /* Don't add the plan to the cache if there were user features with non-global ranges */
    429 
    430   if (hb_non_global_user_features_present (user_features, num_user_features))
    431     return shape_plan;
    432 
    433   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
    434   if (unlikely (!node))
    435     return shape_plan;
    436 
    437   node->shape_plan = shape_plan;
    438   node->next = cached_plan_nodes;
    439 
    440   if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
    441     hb_shape_plan_destroy (shape_plan);
    442     free (node);
    443     goto retry;
    444   }
    445 
    446   return hb_shape_plan_reference (shape_plan);
    447 }
    448 
    449 /**
    450  * hb_shape_plan_get_shaper:
    451  * @shape_plan: a shape plan.
    452  *
    453  *
    454  *
    455  * Return value: (transfer none):
    456  *
    457  * Since: 1.0
    458  **/
    459 const char *
    460 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
    461 {
    462   return shape_plan->shaper_name;
    463 }
    464