tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-shape-plan.cc (16466B)


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