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 } 586