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