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-private.hh"
  28 #include "hb-debug.hh"
  29 #include "hb-shape-plan-private.hh"
  30 #include "hb-shaper-private.hh"
  31 #include "hb-font-private.hh"
  32 #include "hb-buffer-private.hh"
  33 
  34 
  35 static void
  36 hb_shape_plan_plan (hb_shape_plan_t    *shape_plan,
  37                     const hb_feature_t *user_features,
  38                     unsigned int        num_user_features,
  39                     const int          *coords,
  40                     unsigned int        num_coords,
  41                     const char * const *shaper_list)
  42 {
  43   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
  44                   "num_features=%d num_coords=%d shaper_list=%p",
  45                   num_user_features,
  46                   num_coords,
  47                   shaper_list);
  48 
  49   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
  50 
  51 #define HB_SHAPER_PLAN(shaper) \
  52         HB_STMT_START { \
  53           if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
  54             HB_SHAPER_DATA (shaper, shape_plan) = \
  55               HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, \
  56                                                                user_features, num_user_features, \
  57                                                                coords, num_coords); \
  58             shape_plan->shaper_func = _hb_##shaper##_shape; \
  59             shape_plan->shaper_name = #shaper; \
  60             return; \
  61           } \
  62         } HB_STMT_END
  63 
  64   if (likely (!shaper_list)) {
  65     for (unsigned int i = 0; i < HB_SHAPERS_COUNT; i++)
  66       if (0)
  67         ;
  68 #define HB_SHAPER_IMPLEMENT(shaper) \
  69       else if (shapers[i].func == _hb_##shaper##_shape) \
  70         HB_SHAPER_PLAN (shaper);
  71 #include "hb-shaper-list.hh"
  72 #undef HB_SHAPER_IMPLEMENT
  73   } else {
  74     for (; *shaper_list; shaper_list++)
  75       if (0)
  76         ;
  77 #define HB_SHAPER_IMPLEMENT(shaper) \
  78       else if (0 == strcmp (*shaper_list, #shaper)) \
  79         HB_SHAPER_PLAN (shaper);
  80 #include "hb-shaper-list.hh"
  81 #undef HB_SHAPER_IMPLEMENT
  82   }
  83 
  84 #undef HB_SHAPER_PLAN
  85 }
  86 
  87 
  88 /*
  89  * hb_shape_plan_t
  90  */
  91 
  92 /**
  93  * hb_shape_plan_create: (Xconstructor)
  94  * @face:
  95  * @props:
  96  * @user_features: (array length=num_user_features):
  97  * @num_user_features:
  98  * @shaper_list: (array zero-terminated=1):
  99  *
 100  *
 101  *
 102  * Return value: (transfer full):
 103  *
 104  * Since: 0.9.7
 105  **/
 106 hb_shape_plan_t *
 107 hb_shape_plan_create (hb_face_t                     *face,
 108                       const hb_segment_properties_t *props,
 109                       const hb_feature_t            *user_features,
 110                       unsigned int                   num_user_features,
 111                       const char * const            *shaper_list)
 112 {
 113   return hb_shape_plan_create2 (face, props,
 114                                 user_features, num_user_features,
 115                                 nullptr, 0,
 116                                 shaper_list);
 117 }
 118 
 119 hb_shape_plan_t *
 120 hb_shape_plan_create2 (hb_face_t                     *face,
 121                        const hb_segment_properties_t *props,
 122                        const hb_feature_t            *user_features,
 123                        unsigned int                   num_user_features,
 124                        const int                     *orig_coords,
 125                        unsigned int                   num_coords,
 126                        const char * const            *shaper_list)
 127 {
 128   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
 129                   "face=%p num_features=%d num_coords=%d shaper_list=%p",
 130                   face,
 131                   num_user_features,
 132                   num_coords,
 133                   shaper_list);
 134 
 135   hb_shape_plan_t *shape_plan;
 136   hb_feature_t *features = nullptr;
 137   int *coords = nullptr;
 138 
 139   if (unlikely (!face))
 140     face = hb_face_get_empty ();
 141   if (unlikely (!props))
 142     return hb_shape_plan_get_empty ();
 143   if (num_user_features && !(features = (hb_feature_t *) calloc (num_user_features, sizeof (hb_feature_t))))
 144     return hb_shape_plan_get_empty ();
 145   if (num_coords && !(coords = (int *) calloc (num_coords, sizeof (int))))
 146   {
 147     free (features);
 148     return hb_shape_plan_get_empty ();
 149   }
 150   if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
 151   {
 152     free (coords);
 153     free (features);
 154     return hb_shape_plan_get_empty ();
 155   }
 156 
 157   assert (props->direction != HB_DIRECTION_INVALID);
 158 
 159   hb_face_make_immutable (face);
 160   shape_plan->default_shaper_list = !shaper_list;
 161   shape_plan->face_unsafe = face;
 162   shape_plan->props = *props;
 163   shape_plan->num_user_features = num_user_features;
 164   shape_plan->user_features = features;
 165   if (num_user_features)
 166     memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
 167   shape_plan->num_coords = num_coords;
 168   shape_plan->coords = coords;
 169   if (num_coords)
 170     memcpy (coords, orig_coords, num_coords * sizeof (int));
 171 
 172   hb_shape_plan_plan (shape_plan,
 173                       user_features, num_user_features,
 174                       coords, num_coords,
 175                       shaper_list);
 176 
 177   return shape_plan;
 178 }
 179 
 180 /**
 181  * hb_shape_plan_get_empty:
 182  *
 183  *
 184  *
 185  * Return value: (transfer full):
 186  *
 187  * Since: 0.9.7
 188  **/
 189 hb_shape_plan_t *
 190 hb_shape_plan_get_empty (void)
 191 {
 192   static const hb_shape_plan_t _hb_shape_plan_nil = {
 193     HB_OBJECT_HEADER_STATIC,
 194 
 195     true, /* default_shaper_list */
 196     nullptr, /* face */
 197     HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
 198 
 199     nullptr, /* shaper_func */
 200     nullptr, /* shaper_name */
 201 
 202     nullptr, /* user_features */
 203     0,    /* num_user_featurs */
 204 
 205     nullptr, /* coords */
 206     0,    /* num_coords */
 207 
 208     {
 209 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 210 #include "hb-shaper-list.hh"
 211 #undef HB_SHAPER_IMPLEMENT
 212     }
 213   };
 214 
 215   return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
 216 }
 217 
 218 /**
 219  * hb_shape_plan_reference: (skip)
 220  * @shape_plan: a shape plan.
 221  *
 222  *
 223  *
 224  * Return value: (transfer full):
 225  *
 226  * Since: 0.9.7
 227  **/
 228 hb_shape_plan_t *
 229 hb_shape_plan_reference (hb_shape_plan_t *shape_plan)
 230 {
 231   return hb_object_reference (shape_plan);
 232 }
 233 
 234 /**
 235  * hb_shape_plan_destroy: (skip)
 236  * @shape_plan: a shape plan.
 237  *
 238  *
 239  *
 240  * Since: 0.9.7
 241  **/
 242 void
 243 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
 244 {
 245   if (!hb_object_destroy (shape_plan)) return;
 246 
 247 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
 248 #include "hb-shaper-list.hh"
 249 #undef HB_SHAPER_IMPLEMENT
 250 
 251   free (shape_plan->user_features);
 252   free (shape_plan->coords);
 253 
 254   free (shape_plan);
 255 }
 256 
 257 /**
 258  * hb_shape_plan_set_user_data: (skip)
 259  * @shape_plan: a shape plan.
 260  * @key:
 261  * @data:
 262  * @destroy:
 263  * @replace:
 264  *
 265  *
 266  *
 267  * Return value:
 268  *
 269  * Since: 0.9.7
 270  **/
 271 hb_bool_t
 272 hb_shape_plan_set_user_data (hb_shape_plan_t    *shape_plan,
 273                              hb_user_data_key_t *key,
 274                              void *              data,
 275                              hb_destroy_func_t   destroy,
 276                              hb_bool_t           replace)
 277 {
 278   return hb_object_set_user_data (shape_plan, key, data, destroy, replace);
 279 }
 280 
 281 /**
 282  * hb_shape_plan_get_user_data: (skip)
 283  * @shape_plan: a shape plan.
 284  * @key:
 285  *
 286  *
 287  *
 288  * Return value: (transfer none):
 289  *
 290  * Since: 0.9.7
 291  **/
 292 void *
 293 hb_shape_plan_get_user_data (hb_shape_plan_t    *shape_plan,
 294                              hb_user_data_key_t *key)
 295 {
 296   return hb_object_get_user_data (shape_plan, key);
 297 }
 298 
 299 
 300 /**
 301  * hb_shape_plan_execute:
 302  * @shape_plan: a shape plan.
 303  * @font: a font.
 304  * @buffer: a buffer.
 305  * @features: (array length=num_features):
 306  * @num_features:
 307  *
 308  *
 309  *
 310  * Return value:
 311  *
 312  * Since: 0.9.7
 313  **/
 314 hb_bool_t
 315 hb_shape_plan_execute (hb_shape_plan_t    *shape_plan,
 316                        hb_font_t          *font,
 317                        hb_buffer_t        *buffer,
 318                        const hb_feature_t *features,
 319                        unsigned int        num_features)
 320 {
 321   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan,
 322                   "num_features=%d shaper_func=%p, shaper_name=%s",
 323                   num_features,
 324                   shape_plan->shaper_func,
 325                   shape_plan->shaper_name);
 326 
 327   if (unlikely (!buffer->len))
 328     return true;
 329 
 330   assert (!hb_object_is_inert (buffer));
 331   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
 332 
 333   if (unlikely (hb_object_is_inert (shape_plan)))
 334     return false;
 335 
 336   assert (shape_plan->face_unsafe == font->face);
 337   assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
 338 
 339 #define HB_SHAPER_EXECUTE(shaper) \
 340         HB_STMT_START { \
 341           return HB_SHAPER_DATA (shaper, shape_plan) && \
 342                  hb_##shaper##_shaper_font_data_ensure (font) && \
 343                  _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
 344         } HB_STMT_END
 345 
 346   if (0)
 347     ;
 348 #define HB_SHAPER_IMPLEMENT(shaper) \
 349   else if (shape_plan->shaper_func == _hb_##shaper##_shape) \
 350     HB_SHAPER_EXECUTE (shaper);
 351 #include "hb-shaper-list.hh"
 352 #undef HB_SHAPER_IMPLEMENT
 353 
 354 #undef HB_SHAPER_EXECUTE
 355 
 356   return false;
 357 }
 358 
 359 
 360 /*
 361  * caching
 362  */
 363 
 364 #if 0
 365 static unsigned int
 366 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
 367 {
 368   return hb_segment_properties_hash (&shape_plan->props) +
 369          shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
 370 }
 371 #endif
 372 
 373 /* User-feature caching is currently somewhat dumb:
 374  * it only finds matches where the feature array is identical,
 375  * not cases where the feature lists would be compatible for plan purposes
 376  * but have different ranges, for example.
 377  */
 378 struct hb_shape_plan_proposal_t
 379 {
 380   const hb_segment_properties_t  props;
 381   const char * const            *shaper_list;
 382   const hb_feature_t            *user_features;
 383   unsigned int                   num_user_features;
 384   const int                     *coords;
 385   unsigned int                   num_coords;
 386   hb_shape_func_t               *shaper_func;
 387 };
 388 
 389 static inline hb_bool_t
 390 hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
 391                                    const hb_shape_plan_proposal_t *proposal)
 392 {
 393   if (proposal->num_user_features != shape_plan->num_user_features)
 394     return false;
 395   for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
 396     if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
 397         proposal->user_features[i].value != shape_plan->user_features[i].value ||
 398         proposal->user_features[i].start != shape_plan->user_features[i].start ||
 399         proposal->user_features[i].end   != shape_plan->user_features[i].end)
 400       return false;
 401   return true;
 402 }
 403 
 404 static inline hb_bool_t
 405 hb_shape_plan_coords_match (const hb_shape_plan_t          *shape_plan,
 406                             const hb_shape_plan_proposal_t *proposal)
 407 {
 408   if (proposal->num_coords != shape_plan->num_coords)
 409     return false;
 410   for (unsigned int i = 0, n = proposal->num_coords; i < n; i++)
 411     if (proposal->coords[i] != shape_plan->coords[i])
 412       return false;
 413   return true;
 414 }
 415 
 416 static hb_bool_t
 417 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
 418                        const hb_shape_plan_proposal_t *proposal)
 419 {
 420   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
 421          hb_shape_plan_user_features_match (shape_plan, proposal) &&
 422          hb_shape_plan_coords_match (shape_plan, proposal) &&
 423          ((shape_plan->default_shaper_list && !proposal->shaper_list) ||
 424           (shape_plan->shaper_func == proposal->shaper_func));
 425 }
 426 
 427 static inline hb_bool_t
 428 hb_non_global_user_features_present (const hb_feature_t *user_features,
 429                                      unsigned int        num_user_features)
 430 {
 431   while (num_user_features) {
 432     if (user_features->start != 0 || user_features->end != (unsigned int) -1)
 433       return true;
 434     num_user_features--;
 435     user_features++;
 436   }
 437   return false;
 438 }
 439 
 440 static inline hb_bool_t
 441 hb_coords_present (const int *coords,
 442                    unsigned int num_coords)
 443 {
 444   return num_coords != 0;
 445 }
 446 
 447 /**
 448  * hb_shape_plan_create_cached:
 449  * @face:
 450  * @props:
 451  * @user_features: (array length=num_user_features):
 452  * @num_user_features:
 453  * @shaper_list: (array zero-terminated=1):
 454  *
 455  *
 456  *
 457  * Return value: (transfer full):
 458  *
 459  * Since: 0.9.7
 460  **/
 461 hb_shape_plan_t *
 462 hb_shape_plan_create_cached (hb_face_t                     *face,
 463                              const hb_segment_properties_t *props,
 464                              const hb_feature_t            *user_features,
 465                              unsigned int                   num_user_features,
 466                              const char * const            *shaper_list)
 467 {
 468   return hb_shape_plan_create_cached2 (face, props,
 469                                        user_features, num_user_features,
 470                                        nullptr, 0,
 471                                        shaper_list);
 472 }
 473 
 474 hb_shape_plan_t *
 475 hb_shape_plan_create_cached2 (hb_face_t                     *face,
 476                               const hb_segment_properties_t *props,
 477                               const hb_feature_t            *user_features,
 478                               unsigned int                   num_user_features,
 479                               const int                     *coords,
 480                               unsigned int                   num_coords,
 481                               const char * const            *shaper_list)
 482 {
 483   DEBUG_MSG_FUNC (SHAPE_PLAN, nullptr,
 484                   "face=%p num_features=%d shaper_list=%p",
 485                   face,
 486                   num_user_features,
 487                   shaper_list);
 488 
 489   hb_shape_plan_proposal_t proposal = {
 490     *props,
 491     shaper_list,
 492     user_features,
 493     num_user_features,
 494     nullptr
 495   };
 496 
 497   if (shaper_list) {
 498     /* Choose shaper.  Adapted from hb_shape_plan_plan().
 499      * Must choose shaper exactly the same way as that function. */
 500     for (const char * const *shaper_item = shaper_list; *shaper_item; shaper_item++)
 501       if (0)
 502         ;
 503 #define HB_SHAPER_IMPLEMENT(shaper) \
 504       else if (0 == strcmp (*shaper_item, #shaper) && \
 505                hb_##shaper##_shaper_face_data_ensure (face)) \
 506       { \
 507         proposal.shaper_func = _hb_##shaper##_shape; \
 508         break; \
 509       }
 510 #include "hb-shaper-list.hh"
 511 #undef HB_SHAPER_IMPLEMENT
 512 
 513     if (unlikely (!proposal.shaper_func))
 514       return hb_shape_plan_get_empty ();
 515   }
 516 
 517 
 518 retry:
 519   hb_face_t::plan_node_t *cached_plan_nodes = (hb_face_t::plan_node_t *) hb_atomic_ptr_get (&face->shape_plans);
 520 
 521   /* Don't look for plan in the cache if there were variation coordinates XXX Fix me. */
 522   if (!hb_coords_present (coords, num_coords))
 523     for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
 524       if (hb_shape_plan_matches (node->shape_plan, &proposal))
 525       {
 526         DEBUG_MSG_FUNC (SHAPE_PLAN, node->shape_plan, "fulfilled from cache");
 527         return hb_shape_plan_reference (node->shape_plan);
 528       }
 529 
 530   /* Not found. */
 531   hb_shape_plan_t *shape_plan = hb_shape_plan_create2 (face, props,
 532                                                        user_features, num_user_features,
 533                                                        coords, num_coords,
 534                                                        shaper_list);
 535 
 536   /* Don't add to the cache if face is inert. */
 537   if (unlikely (hb_object_is_inert (face)))
 538     return shape_plan;
 539 
 540   /* Don't add the plan to the cache if there were user features with non-global ranges */
 541   if (hb_non_global_user_features_present (user_features, num_user_features))
 542     return shape_plan;
 543   /* Don't add the plan to the cache if there were variation coordinates XXX Fix me. */
 544   if (hb_coords_present (coords, num_coords))
 545     return shape_plan;
 546 
 547   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
 548   if (unlikely (!node))
 549     return shape_plan;
 550 
 551   node->shape_plan = shape_plan;
 552   node->next = cached_plan_nodes;
 553 
 554   if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
 555     hb_shape_plan_destroy (shape_plan);
 556     free (node);
 557     goto retry;
 558   }
 559   DEBUG_MSG_FUNC (SHAPE_PLAN, shape_plan, "inserted into cache");
 560 
 561   return hb_shape_plan_reference (shape_plan);
 562 }
 563 
 564 /**
 565  * hb_shape_plan_get_shaper:
 566  * @shape_plan: a shape plan.
 567  *
 568  *
 569  *
 570  * Return value: (transfer none):
 571  *
 572  * Since: 0.9.7
 573  **/
 574 const char *
 575 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan)
 576 {
 577   return shape_plan->shaper_name;
 578 }