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