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