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 }