| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright © 2011,2012 Google, Inc. | 2 * Copyright © 2011,2012 Google, Inc. |
| 3 * | 3 * |
| 4 * This is part of HarfBuzz, a text shaping library. | 4 * This is part of HarfBuzz, a text shaping library. |
| 5 * | 5 * |
| 6 * Permission is hereby granted, without written agreement and without | 6 * Permission is hereby granted, without written agreement and without |
| 7 * license or royalty fees, to use, copy, modify, and distribute this | 7 * license or royalty fees, to use, copy, modify, and distribute this |
| 8 * software and its documentation for any purpose, provided that the | 8 * software and its documentation for any purpose, provided that the |
| 9 * above copyright notice and the following two paragraphs appear in | 9 * above copyright notice and the following two paragraphs appear in |
| 10 * all copies of this software. | 10 * all copies of this software. |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i])) | 185 if (!c->font->get_glyph (decomposed[i], 0, &glyphs[i])) |
| 186 return 0; | 186 return 0; |
| 187 | 187 |
| 188 for (i = 0; i < len; i++) | 188 for (i = 0; i < len; i++) |
| 189 output_char (c->buffer, decomposed[i], glyphs[i]); | 189 output_char (c->buffer, decomposed[i], glyphs[i]); |
| 190 | 190 |
| 191 return len; | 191 return len; |
| 192 } | 192 } |
| 193 | 193 |
| 194 /* Returns true if recomposition may be benefitial. */ | 194 /* Returns true if recomposition may be benefitial. */ |
| 195 static inline bool | 195 static inline void |
| 196 decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor
test) | 196 decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shor
test) |
| 197 { | 197 { |
| 198 hb_buffer_t * const buffer = c->buffer; | 198 hb_buffer_t * const buffer = c->buffer; |
| 199 hb_codepoint_t glyph; | 199 hb_codepoint_t glyph; |
| 200 unsigned int len = 1; | |
| 201 | 200 |
| 202 /* Kind of a cute waterfall here... */ | 201 /* Kind of a cute waterfall here... */ |
| 203 if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) | 202 if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) |
| 204 next_char (buffer, glyph); | 203 next_char (buffer, glyph); |
| 205 else if ((len = decompose (c, shortest, buffer->cur().codepoint))) | 204 else if (decompose (c, shortest, buffer->cur().codepoint)) |
| 206 skip_char (buffer); | 205 skip_char (buffer); |
| 207 else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) | 206 else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph)) |
| 208 next_char (buffer, glyph); | 207 next_char (buffer, glyph); |
| 209 else if ((len = decompose_compatibility (c, buffer->cur().codepoint))) | 208 else if (decompose_compatibility (c, buffer->cur().codepoint)) |
| 210 skip_char (buffer); | 209 skip_char (buffer); |
| 211 else | 210 else |
| 212 next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ | 211 next_char (buffer, glyph); /* glyph is initialized in earlier branches. */ |
| 213 | |
| 214 /* | |
| 215 * A recomposition would only be useful if we decomposed into at least three | |
| 216 * characters... | |
| 217 */ | |
| 218 return len > 2; | |
| 219 } | 212 } |
| 220 | 213 |
| 221 static inline void | 214 static inline void |
| 222 handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, uns
igned int end) | 215 handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, uns
igned int end) |
| 223 { | 216 { |
| 224 hb_buffer_t * const buffer = c->buffer; | 217 hb_buffer_t * const buffer = c->buffer; |
| 225 for (; buffer->idx < end - 1;) { | 218 for (; buffer->idx < end - 1;) { |
| 226 if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepo
int))) { | 219 if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepo
int))) { |
| 227 /* The next two lines are some ugly lines... But work. */ | 220 /* The next two lines are some ugly lines... But work. */ |
| 228 c->font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &b
uffer->cur().glyph_index()); | 221 c->font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &b
uffer->cur().glyph_index()); |
| 229 buffer->replace_glyphs (2, 1, &buffer->cur().codepoint); | 222 buffer->replace_glyphs (2, 1, &buffer->cur().codepoint); |
| 230 } else { | 223 } else { |
| 231 set_glyph (buffer->cur(), c->font); | 224 set_glyph (buffer->cur(), c->font); |
| 232 buffer->next_glyph (); | 225 buffer->next_glyph (); |
| 233 } | 226 } |
| 234 } | 227 } |
| 235 if (likely (buffer->idx < end)) { | 228 if (likely (buffer->idx < end)) { |
| 236 set_glyph (buffer->cur(), c->font); | 229 set_glyph (buffer->cur(), c->font); |
| 237 buffer->next_glyph (); | 230 buffer->next_glyph (); |
| 238 } | 231 } |
| 239 } | 232 } |
| 240 | 233 |
| 241 /* Returns true if recomposition may be benefitial. */ | 234 /* Returns true if recomposition may be benefitial. */ |
| 242 static inline bool | 235 static inline void |
| 243 decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned
int end) | 236 decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned
int end) |
| 244 { | 237 { |
| 245 hb_buffer_t * const buffer = c->buffer; | 238 hb_buffer_t * const buffer = c->buffer; |
| 246 /* TODO Currently if there's a variation-selector we give-up, it's just too ha
rd. */ | 239 /* TODO Currently if there's a variation-selector we give-up, it's just too ha
rd. */ |
| 247 for (unsigned int i = buffer->idx; i < end; i++) | 240 for (unsigned int i = buffer->idx; i < end; i++) |
| 248 if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepo
int))) { | 241 if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepo
int))) { |
| 249 handle_variation_selector_cluster (c, end); | 242 handle_variation_selector_cluster (c, end); |
| 250 return false; | 243 return; |
| 251 } | 244 } |
| 252 | 245 |
| 253 while (buffer->idx < end) | 246 while (buffer->idx < end) |
| 254 decompose_current_character (c, false); | 247 decompose_current_character (c, false); |
| 255 /* We can be smarter here and only return true if there are at least two ccc!=
0 marks. | |
| 256 * But does not matter. */ | |
| 257 return true; | |
| 258 } | 248 } |
| 259 | 249 |
| 260 static inline bool | 250 static inline void |
| 261 decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit,
unsigned int end) | 251 decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit,
unsigned int end) |
| 262 { | 252 { |
| 263 if (likely (c->buffer->idx + 1 == end)) | 253 if (likely (c->buffer->idx + 1 == end)) |
| 264 return decompose_current_character (c, short_circuit); | 254 decompose_current_character (c, short_circuit); |
| 265 else | 255 else |
| 266 return decompose_multi_char_cluster (c, end); | 256 decompose_multi_char_cluster (c, end); |
| 267 } | 257 } |
| 268 | 258 |
| 269 | 259 |
| 270 static int | 260 static int |
| 271 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) | 261 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb) |
| 272 { | 262 { |
| 273 unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); | 263 unsigned int a = _hb_glyph_info_get_modified_combining_class (pa); |
| 274 unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); | 264 unsigned int b = _hb_glyph_info_get_modified_combining_class (pb); |
| 275 | 265 |
| 276 return a < b ? -1 : a == b ? 0 : +1; | 266 return a < b ? -1 : a == b ? 0 : +1; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 289 plan, | 279 plan, |
| 290 buffer, | 280 buffer, |
| 291 font, | 281 font, |
| 292 buffer->unicode, | 282 buffer->unicode, |
| 293 plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, | 283 plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode, |
| 294 plan->shaper->compose ? plan->shaper->compose : compose_unicode | 284 plan->shaper->compose ? plan->shaper->compose : compose_unicode |
| 295 }; | 285 }; |
| 296 | 286 |
| 297 bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && | 287 bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED && |
| 298 mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITIC
S_NO_SHORT_CIRCUIT; | 288 mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITIC
S_NO_SHORT_CIRCUIT; |
| 299 bool can_use_recompose = false; | |
| 300 unsigned int count; | 289 unsigned int count; |
| 301 | 290 |
| 302 /* We do a fairly straightforward yet custom normalization process in three | 291 /* We do a fairly straightforward yet custom normalization process in three |
| 303 * separate rounds: decompose, reorder, recompose (if desired). Currently | 292 * separate rounds: decompose, reorder, recompose (if desired). Currently |
| 304 * this makes two buffer swaps. We can make it faster by moving the last | 293 * this makes two buffer swaps. We can make it faster by moving the last |
| 305 * two rounds into the inner loop for the first round, but it's more readable | 294 * two rounds into the inner loop for the first round, but it's more readable |
| 306 * this way. */ | 295 * this way. */ |
| 307 | 296 |
| 308 | 297 |
| 309 /* First round, decompose */ | 298 /* First round, decompose */ |
| 310 | 299 |
| 311 buffer->clear_output (); | 300 buffer->clear_output (); |
| 312 count = buffer->len; | 301 count = buffer->len; |
| 313 for (buffer->idx = 0; buffer->idx < count;) | 302 for (buffer->idx = 0; buffer->idx < count;) |
| 314 { | 303 { |
| 315 unsigned int end; | 304 unsigned int end; |
| 316 for (end = buffer->idx + 1; end < count; end++) | 305 for (end = buffer->idx + 1; end < count; end++) |
| 317 if (buffer->cur().cluster != buffer->info[end].cluster) | 306 if (buffer->cur().cluster != buffer->info[end].cluster) |
| 318 break; | 307 break; |
| 319 | 308 |
| 320 can_use_recompose = decompose_cluster (&c, short_circuit, end) || can_use_re
compose; | 309 decompose_cluster (&c, short_circuit, end); |
| 321 } | 310 } |
| 322 buffer->swap_buffers (); | 311 buffer->swap_buffers (); |
| 323 | 312 |
| 324 | 313 |
| 325 if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose
) | |
| 326 return; /* Done! */ | |
| 327 | |
| 328 | |
| 329 /* Second round, reorder (inplace) */ | 314 /* Second round, reorder (inplace) */ |
| 330 | 315 |
| 331 count = buffer->len; | 316 count = buffer->len; |
| 332 for (unsigned int i = 0; i < count; i++) | 317 for (unsigned int i = 0; i < count; i++) |
| 333 { | 318 { |
| 334 if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) | 319 if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0) |
| 335 continue; | 320 continue; |
| 336 | 321 |
| 337 unsigned int end; | 322 unsigned int end; |
| 338 for (end = i + 1; end < count; end++) | 323 for (end = i + 1; end < count; end++) |
| (...skipping 23 matching lines...) Expand all Loading... |
| 362 * ccc=0 chars with their previous Starter. */ | 347 * ccc=0 chars with their previous Starter. */ |
| 363 | 348 |
| 364 buffer->clear_output (); | 349 buffer->clear_output (); |
| 365 count = buffer->len; | 350 count = buffer->len; |
| 366 unsigned int starter = 0; | 351 unsigned int starter = 0; |
| 367 buffer->next_glyph (); | 352 buffer->next_glyph (); |
| 368 while (buffer->idx < count) | 353 while (buffer->idx < count) |
| 369 { | 354 { |
| 370 hb_codepoint_t composed, glyph; | 355 hb_codepoint_t composed, glyph; |
| 371 if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't
try to | 356 if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't
try to |
| 372 » * compose a CCC=0 character with it's preceding starter. */ | 357 » * compose a non-mark character with it's preceding starter. This is ju
st an |
| 358 » * optimization to avoid trying to compose every two neighboring glyphs
in most |
| 359 » * scripts. */ |
| 373 (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL || | 360 (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL || |
| 374 » _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) && | 361 » HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_categor
y (&buffer->cur()))) && |
| 375 /* If there's anything between the starter and this char, they should ha
ve CCC | 362 /* If there's anything between the starter and this char, they should ha
ve CCC |
| 376 * smaller than this character's. */ | 363 * smaller than this character's. */ |
| 377 (starter == buffer->out_len - 1 || | 364 (starter == buffer->out_len - 1 || |
| 378 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_gly
ph_info_get_modified_combining_class (&buffer->cur())) && | 365 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_gly
ph_info_get_modified_combining_class (&buffer->cur())) && |
| 379 /* And compose. */ | 366 /* And compose. */ |
| 380 c.compose (&c, | 367 c.compose (&c, |
| 381 buffer->out_info[starter].codepoint, | 368 buffer->out_info[starter].codepoint, |
| 382 buffer->cur().codepoint, | 369 buffer->cur().codepoint, |
| 383 &composed) && | 370 &composed) && |
| 384 /* And the font has glyph for the composite. */ | 371 /* And the font has glyph for the composite. */ |
| (...skipping 14 matching lines...) Expand all Loading... |
| 399 | 386 |
| 400 /* Blocked, or doesn't compose. */ | 387 /* Blocked, or doesn't compose. */ |
| 401 buffer->next_glyph (); | 388 buffer->next_glyph (); |
| 402 | 389 |
| 403 if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) | 390 if (_hb_glyph_info_get_modified_combining_class (&buffer->prev()) == 0) |
| 404 starter = buffer->out_len - 1; | 391 starter = buffer->out_len - 1; |
| 405 } | 392 } |
| 406 buffer->swap_buffers (); | 393 buffer->swap_buffers (); |
| 407 | 394 |
| 408 } | 395 } |
| OLD | NEW |