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 39 static void 40 hb_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 **/ 100 hb_shape_plan_t * 101 hb_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 assert (props->direction != HB_DIRECTION_INVALID); 108 109 hb_shape_plan_t *shape_plan; 110 hb_feature_t *features = NULL; 111 112 if (unlikely (!face)) 113 face = hb_face_get_empty (); 114 if (unlikely (!props || hb_object_is_inert (face))) 115 return hb_shape_plan_get_empty (); 116 if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t)))) 117 return hb_shape_plan_get_empty (); 118 if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) { 119 free (features); 120 return hb_shape_plan_get_empty (); 121 } 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 **/ 146 hb_shape_plan_t * 147 hb_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 **/ 182 hb_shape_plan_t * 183 hb_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 **/ 196 void 197 hb_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 **/ 224 hb_bool_t 225 hb_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 **/ 245 void * 246 hb_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 **/ 267 hb_bool_t 268 hb_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 308 static unsigned int 309 hb_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 */ 321 struct 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 330 static inline hb_bool_t 331 hb_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 343 static hb_bool_t 344 hb_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 353 static inline hb_bool_t 354 hb_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 **/ 379 hb_shape_plan_t * 380 hb_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 418 retry: 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 **/ 459 const char * 460 hb_shape_plan_get_shaper (hb_shape_plan_t *shape_plan) 461 { 462 return shape_plan->shaper_name; 463 } 464