hb-ot-shape.cc revision affc5abac7bdae51df85856a5478d34d96fda4fe
1/* 2 * Copyright (C) 2009,2010 Red Hat, 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 * Red Hat Author(s): Behdad Esfahbod 25 */ 26 27#include "hb-ot-shape-private.h" 28 29#include "hb-open-type-private.hh" 30 31#include "hb-ot-layout.h" 32 33HB_BEGIN_DECLS 34 35 36/* XXX vertical */ 37hb_tag_t default_features[] = { 38 HB_TAG('c','a','l','t'), 39 HB_TAG('c','c','m','p'), 40 HB_TAG('c','l','i','g'), 41 HB_TAG('c','s','w','h'), 42 HB_TAG('c','u','r','s'), 43 HB_TAG('k','e','r','n'), 44 HB_TAG('l','i','g','a'), 45 HB_TAG('l','o','c','l'), 46 HB_TAG('m','a','r','k'), 47 HB_TAG('m','k','m','k'), 48 HB_TAG('r','l','i','g') 49}; 50 51struct lookup_map { 52 unsigned int index; 53 hb_mask_t mask; 54}; 55 56 57static void 58add_feature (hb_face_t *face, 59 hb_tag_t table_tag, 60 unsigned int feature_index, 61 hb_mask_t mask, 62 lookup_map *lookups, 63 unsigned int *num_lookups, 64 unsigned int room_lookups) 65{ 66 unsigned int i = room_lookups - *num_lookups; 67 lookups += *num_lookups; 68 69 unsigned int *lookup_indices = (unsigned int *) lookups; 70 71 hb_ot_layout_feature_get_lookup_indexes (face, table_tag, feature_index, 0, 72 &i, 73 lookup_indices); 74 75 *num_lookups += i; 76 77 while (i--) { 78 lookups[i].mask = mask; 79 lookups[i].index = lookup_indices[i]; 80 } 81} 82 83static int 84cmp_lookups (const void *p1, const void *p2) 85{ 86 const lookup_map *a = (const lookup_map *) p1; 87 const lookup_map *b = (const lookup_map *) p2; 88 89 return a->index - b->index; 90} 91 92 93#define MAX_FEATURES 100 /* FIXME */ 94 95struct hb_mask_allocator_t { 96 97 struct feature_info_t { 98 hb_tag_t tag; 99 unsigned int value; 100 unsigned int seq; 101 bool global; 102 103 static int 104 cmp (const void *p1, const void *p2) 105 { 106 const feature_info_t *a = reinterpret_cast<const feature_info_t *>(p1); 107 const feature_info_t *b = reinterpret_cast<const feature_info_t *>(p2); 108 109 if (a->tag != b->tag) 110 return a->tag < b->tag ? -1 : 1; 111 112 return a->seq < b->seq ? -1 : 1; 113 } 114 }; 115 116 struct feature_map_t { 117 hb_tag_t tag; /* should be first */ 118 unsigned int index; 119 unsigned int shift; 120 hb_mask_t mask; 121 122 static int 123 cmp (const void *p1, const void *p2) 124 { 125 const feature_map_t *a = reinterpret_cast<const feature_map_t *>(p1); 126 const feature_map_t *b = reinterpret_cast<const feature_map_t *>(p2); 127 128 return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; 129 } 130 }; 131 132 hb_mask_allocator_t (void) : count (0) {} 133 134 void add_feature (hb_tag_t tag, 135 unsigned int value, 136 bool global) 137 { 138 feature_info_t *info = &infos[count++]; 139 info->tag = tag; 140 info->value = value; 141 info->seq = count; 142 info->global = global; 143 } 144 145 void compile (hb_face_t *face, 146 hb_tag_t table_tag, 147 unsigned int script_index, 148 unsigned int language_index) 149 { 150 global_mask = 0; 151 unsigned int next_bit = 1; 152 153 if (!count) 154 return; 155 156 qsort (infos, count, sizeof (infos[0]), feature_info_t::cmp); 157 158 unsigned int j = 0; 159 for (unsigned int i = 1; i < count; i++) 160 if (infos[i].tag != infos[j].tag) 161 infos[++j] = infos[i]; 162 else { 163 if (infos[i].global) 164 infos[j] = infos[i]; 165 else { 166 infos[j].global = infos[j].global && (infos[j].value == infos[i].value); 167 infos[j].value = MAX (infos[j].value, infos[i].value); 168 } 169 } 170 count = j + 1; 171 172 /* Allocate bits now */ 173 j = 0; 174 for (unsigned int i = 0; i < count; i++) { 175 const feature_info_t *info = &infos[i]; 176 177 unsigned int bits_needed; 178 179 if (info->global && info->value == 1) 180 /* Uses the global bit */ 181 bits_needed = 0; 182 else 183 bits_needed = _hb_bit_storage (info->value); 184 185 if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t)) 186 continue; /* Feature disabled, or not enough bits. */ 187 188 unsigned int feature_index; 189 if (!hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index, 190 info->tag, &feature_index)) 191 continue; 192 193 feature_map_t *map = &maps[j++]; 194 195 map->tag = info->tag; 196 map->index = feature_index; 197 if (info->global && info->value == 1) { 198 /* Use the global bit */ 199 map->shift = 0; 200 map->mask = 1; 201 } else { 202 map->shift = next_bit; 203 map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit); 204 next_bit += bits_needed; 205 } 206 207 if (info->global && map->mask != 1) 208 global_mask |= map->mask; 209 } 210 count = j; 211 } 212 213 hb_mask_t get_global_mask (void) { return global_mask; } 214 const feature_map_t *find_feature (hb_tag_t tag) const { 215 static const feature_map_t off_map = { HB_TAG_NONE, Index::NOT_FOUND_INDEX, 0, 0 }; 216 const feature_map_t *map = (const feature_map_t *) bsearch (&tag, maps, count, sizeof (maps[0]), feature_map_t::cmp); 217 return map ? map : &off_map; 218 } 219 220 221 private: 222 223 unsigned int count; 224 feature_info_t infos[MAX_FEATURES]; 225 226 feature_map_t maps[MAX_FEATURES]; 227 hb_mask_t global_mask; 228}; 229 230static void 231setup_lookups (hb_ot_shape_context_t *c, 232 lookup_map *lookups, 233 unsigned int *num_lookups) 234{ 235 unsigned int i, j, script_index, language_index, feature_index, room_lookups; 236 237 room_lookups = *num_lookups; 238 *num_lookups = 0; 239 240 hb_ot_layout_table_choose_script (c->face, c->table_tag, 241 hb_ot_tags_from_script (c->buffer->props.script), 242 &script_index); 243 hb_ot_layout_script_find_language (c->face, c->table_tag, script_index, 244 hb_ot_tag_from_language (c->buffer->props.language), 245 &language_index); 246 247 248 hb_mask_allocator_t allocator; 249 250 switch (c->original_direction) { 251 case HB_DIRECTION_LTR: 252 allocator.add_feature (HB_TAG ('l','t','r','a'), 1, true); 253 allocator.add_feature (HB_TAG ('l','t','r','m'), 1, true); 254 break; 255 case HB_DIRECTION_RTL: 256 allocator.add_feature (HB_TAG ('r','t','l','a'), 1, true); 257 allocator.add_feature (HB_TAG ('r','t','l','m'), 1, false); 258 break; 259 case HB_DIRECTION_TTB: 260 case HB_DIRECTION_BTT: 261 default: 262 break; 263 } 264 265 for (i = 0; i < ARRAY_LENGTH (default_features); i++) 266 allocator.add_feature (default_features[i], 1, true); 267 268 /* XXX complex-shaper features go here */ 269 270 for (unsigned int i = 0; i < c->num_features; i++) { 271 const hb_feature_t *feature = &c->features[i]; 272 allocator.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1)); 273 } 274 275 276 /* Compile features */ 277 allocator.compile (c->face, c->table_tag, script_index, language_index); 278 279 280 /* Gather lookup indices for features and set buffer masks at the same time */ 281 282 if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index, 283 &feature_index)) 284 add_feature (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups); 285 286 const hb_mask_allocator_t::feature_map_t *map; 287 288 hb_mask_t global_mask = allocator.get_global_mask (); 289 if (global_mask) 290 c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1); 291 292 switch (c->original_direction) { 293 case HB_DIRECTION_LTR: 294 map = allocator.find_feature (HB_TAG ('l','t','r','a')); 295 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 296 map = allocator.find_feature (HB_TAG ('l','t','r','m')); 297 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 298 break; 299 case HB_DIRECTION_RTL: 300 map = allocator.find_feature (HB_TAG ('r','t','l','a')); 301 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 302 map = allocator.find_feature (HB_TAG ('r','t','l','m')); 303 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 304 break; 305 case HB_DIRECTION_TTB: 306 case HB_DIRECTION_BTT: 307 default: 308 break; 309 } 310 311 for (i = 0; i < ARRAY_LENGTH (default_features); i++) 312 { 313 map = allocator.find_feature (default_features[i]); 314 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 315 } 316 317 for (i = 0; i < c->num_features; i++) 318 { 319 hb_feature_t *feature = &c->features[i]; 320 map = allocator.find_feature (feature->tag); 321 add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups); 322 if (!(feature->start == 0 && feature->end == (unsigned int)-1)) 323 c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end); 324 } 325 326 327 /* Sort lookups and merge duplicates */ 328 329 qsort (lookups, *num_lookups, sizeof (lookups[0]), cmp_lookups); 330 331 if (*num_lookups) 332 { 333 for (i = 1, j = 0; i < *num_lookups; i++) 334 if (lookups[i].index != lookups[j].index) 335 lookups[++j] = lookups[i]; 336 else 337 lookups[j].mask |= lookups[i].mask; 338 j++; 339 *num_lookups = j; 340 } 341} 342 343 344static void 345hb_ot_substitute_complex (hb_ot_shape_context_t *c) 346{ 347 lookup_map lookups[1000]; /* FIXME */ 348 unsigned int num_lookups = ARRAY_LENGTH (lookups); 349 unsigned int i; 350 351 if (!hb_ot_layout_has_substitution (c->face)) 352 return; 353 354 c->table_tag = HB_OT_TAG_GSUB; 355 356 setup_lookups (c, lookups, &num_lookups); 357 358 for (i = 0; i < num_lookups; i++) 359 hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask); 360 361 c->applied_substitute_complex = TRUE; 362 return; 363} 364 365static void 366hb_ot_position_complex (hb_ot_shape_context_t *c) 367{ 368 lookup_map lookups[1000]; /* FIXME */ 369 unsigned int num_lookups = ARRAY_LENGTH (lookups); 370 unsigned int i; 371 372 if (!hb_ot_layout_has_positioning (c->face)) 373 return; 374 375 c->table_tag = HB_OT_TAG_GPOS; 376 377 setup_lookups (c, lookups, &num_lookups); 378 379 for (i = 0; i < num_lookups; i++) 380 hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask); 381 382 hb_ot_layout_position_finish (c->font, c->face, c->buffer); 383 384 c->applied_position_complex = TRUE; 385 return; 386} 387 388 389/* Main shaper */ 390 391/* Prepare */ 392 393static inline hb_bool_t 394is_variation_selector (hb_codepoint_t unicode) 395{ 396 return unlikely ((unicode >= 0x180B && unicode <= 0x180D) || /* MONGOLIAN FREE VARIATION SELECTOR ONE..THREE */ 397 (unicode >= 0xFE00 && unicode <= 0xFE0F) || /* VARIATION SELECTOR-1..16 */ 398 (unicode >= 0xE0100 && unicode <= 0xE01EF)); /* VARIATION SELECTOR-17..256 */ 399} 400 401static void 402hb_form_clusters (hb_buffer_t *buffer) 403{ 404 unsigned int count = buffer->len; 405 for (unsigned int i = 1; i < count; i++) 406 if (buffer->unicode->v.get_general_category (buffer->info[i].codepoint) == HB_CATEGORY_NON_SPACING_MARK) 407 buffer->info[i].cluster = buffer->info[i - 1].cluster; 408} 409 410static void 411hb_ensure_native_direction (hb_buffer_t *buffer) 412{ 413 hb_direction_t direction = buffer->props.direction; 414 415 /* TODO vertical */ 416 if (HB_DIRECTION_IS_HORIZONTAL (direction) && 417 direction != _hb_script_get_horizontal_direction (buffer->props.script)) 418 { 419 hb_buffer_reverse_clusters (buffer); 420 buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction); 421 } 422} 423 424 425/* Substitute */ 426 427static void 428hb_mirror_chars (hb_buffer_t *buffer) 429{ 430 hb_unicode_get_mirroring_func_t get_mirroring = buffer->unicode->v.get_mirroring; 431 432 if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) 433 return; 434 435// map = allocator.find_feature (HB_TAG ('r','t','l','m')); 436 437 unsigned int count = buffer->len; 438 for (unsigned int i = 0; i < count; i++) { 439 hb_codepoint_t codepoint = get_mirroring (buffer->info[i].codepoint); 440 if (likely (codepoint == buffer->info[i].codepoint)) 441;// buffer->info[i].mask |= map->mask; 442 else 443 buffer->info[i].codepoint = codepoint; 444 } 445} 446 447static void 448hb_map_glyphs (hb_font_t *font, 449 hb_face_t *face, 450 hb_buffer_t *buffer) 451{ 452 if (unlikely (!buffer->len)) 453 return; 454 455 buffer->clear_output (); 456 unsigned int count = buffer->len - 1; 457 for (buffer->i = 0; buffer->i < count;) { 458 if (unlikely (is_variation_selector (buffer->info[buffer->i + 1].codepoint))) { 459 buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, buffer->info[buffer->i + 1].codepoint)); 460 buffer->i++; 461 } else { 462 buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0)); 463 } 464 } 465 if (likely (buffer->i < buffer->len)) 466 buffer->add_output_glyph (hb_font_get_glyph (font, face, buffer->info[buffer->i].codepoint, 0)); 467 buffer->swap (); 468} 469 470static void 471hb_substitute_default (hb_ot_shape_context_t *c) 472{ 473 hb_map_glyphs (c->font, c->face, c->buffer); 474} 475 476static void 477hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) 478{ 479 /* TODO Arabic */ 480} 481 482 483/* Position */ 484 485static void 486hb_position_default (hb_ot_shape_context_t *c) 487{ 488 hb_buffer_clear_positions (c->buffer); 489 490 unsigned int count = c->buffer->len; 491 for (unsigned int i = 0; i < count; i++) { 492 hb_glyph_metrics_t metrics; 493 hb_font_get_glyph_metrics (c->font, c->face, c->buffer->info[i].codepoint, &metrics); 494 c->buffer->pos[i].x_advance = metrics.x_advance; 495 c->buffer->pos[i].y_advance = metrics.y_advance; 496 } 497} 498 499static void 500hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED) 501{ 502 /* TODO Mark pos */ 503} 504 505static void 506hb_truetype_kern (hb_ot_shape_context_t *c) 507{ 508 /* TODO Check for kern=0 */ 509 unsigned int count = c->buffer->len; 510 for (unsigned int i = 1; i < count; i++) { 511 hb_position_t kern, kern1, kern2; 512 kern = hb_font_get_kerning (c->font, c->face, c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint); 513 kern1 = kern >> 1; 514 kern2 = kern - kern1; 515 c->buffer->pos[i - 1].x_advance += kern1; 516 c->buffer->pos[i].x_advance += kern2; 517 c->buffer->pos[i].x_offset += kern2; 518 } 519} 520 521static void 522hb_position_complex_fallback_visual (hb_ot_shape_context_t *c) 523{ 524 hb_truetype_kern (c); 525} 526 527 528/* Do it! */ 529 530static void 531hb_ot_shape_internal (hb_ot_shape_context_t *c) 532{ 533 hb_form_clusters (c->buffer); 534 535 /* SUBSTITUTE */ 536 { 537 c->buffer->clear_masks (); 538 539 /* Mirroring needs to see the original direction */ 540 hb_mirror_chars (c->buffer); 541 542 hb_ensure_native_direction (c->buffer); 543 544 hb_substitute_default (c); 545 546 hb_ot_substitute_complex (c); 547 548 if (!c->applied_substitute_complex) 549 hb_substitute_complex_fallback (c); 550 } 551 552 /* POSITION */ 553 { 554 c->buffer->clear_masks (); 555 556 hb_position_default (c); 557 558 hb_ot_position_complex (c); 559 560 hb_bool_t position_fallback = !c->applied_position_complex; 561 if (position_fallback) 562 hb_position_complex_fallback (c); 563 564 if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction)) 565 hb_buffer_reverse (c->buffer); 566 567 if (position_fallback) 568 hb_position_complex_fallback_visual (c); 569 } 570 571 c->buffer->props.direction = c->original_direction; 572} 573 574void 575hb_ot_shape (hb_font_t *font, 576 hb_face_t *face, 577 hb_buffer_t *buffer, 578 hb_feature_t *features, 579 unsigned int num_features) 580{ 581 hb_ot_shape_context_t c = {font, face, buffer, features, num_features}; 582 583 /* Setup transient context members */ 584 c.original_direction = buffer->props.direction; 585 586 hb_ot_shape_internal (&c); 587} 588 589 590HB_END_DECLS 591