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 }