David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1 | #include <ft2build.h> |
| 2 | #include FT_INTERNAL_OBJECTS_H |
| 3 | #include FT_INTERNAL_DEBUG_H |
| 4 | #include "pshalgo2.h" |
| 5 | |
| 6 | |
| 7 | #ifdef DEBUG_HINTER |
| 8 | extern PSH2_Hint_Table ps2_debug_hint_table = 0; |
| 9 | extern PSH2_HintFunc ps2_debug_hint_func = 0; |
| 10 | extern PSH2_Glyph ps2_debug_glyph = 0; |
| 11 | #endif |
| 12 | |
| 13 | /************************************************************************/ |
| 14 | /************************************************************************/ |
| 15 | /***** *****/ |
| 16 | /***** BASIC HINTS RECORDINGS *****/ |
| 17 | /***** *****/ |
| 18 | /************************************************************************/ |
| 19 | /************************************************************************/ |
| 20 | |
| 21 | /* return true iff two stem hints overlap */ |
| 22 | static FT_Int |
| 23 | psh2_hint_overlap( PSH2_Hint hint1, |
| 24 | PSH2_Hint hint2 ) |
| 25 | { |
| 26 | return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && |
| 27 | hint2->org_pos + hint2->org_len >= hint1->org_pos ); |
| 28 | } |
| 29 | |
| 30 | |
| 31 | /* destroy hints table */ |
| 32 | static void |
| 33 | psh2_hint_table_done( PSH2_Hint_Table table, |
| 34 | FT_Memory memory ) |
| 35 | { |
| 36 | FREE( table->zones ); |
| 37 | table->num_zones = 0; |
| 38 | table->zone = 0; |
| 39 | |
| 40 | FREE( table->sort ); |
| 41 | FREE( table->hints ); |
| 42 | table->num_hints = 0; |
| 43 | table->max_hints = 0; |
| 44 | table->sort_global = 0; |
| 45 | } |
| 46 | |
| 47 | |
| 48 | /* deactivate all hints in a table */ |
| 49 | static void |
| 50 | psh2_hint_table_deactivate( PSH2_Hint_Table table ) |
| 51 | { |
| 52 | FT_UInt count = table->max_hints; |
| 53 | PSH2_Hint hint = table->hints; |
| 54 | |
| 55 | for ( ; count > 0; count--, hint++ ) |
| 56 | { |
| 57 | psh2_hint_deactivate(hint); |
| 58 | hint->order = -1; |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | |
| 63 | /* internal function used to record a new hint */ |
| 64 | static void |
| 65 | psh2_hint_table_record( PSH2_Hint_Table table, |
| 66 | FT_UInt index ) |
| 67 | { |
| 68 | PSH2_Hint hint = table->hints + index; |
| 69 | |
| 70 | if ( index >= table->max_hints ) |
| 71 | { |
| 72 | FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); |
| 73 | return; |
| 74 | } |
| 75 | |
| 76 | /* ignore active hints */ |
| 77 | if ( psh2_hint_is_active(hint) ) |
| 78 | return; |
| 79 | |
| 80 | psh2_hint_activate(hint); |
| 81 | |
| 82 | /* now scan the current active hint set in order to determine */ |
| 83 | /* if we're overlapping with another segment.. */ |
| 84 | { |
| 85 | PSH2_Hint* sorted = table->sort_global; |
| 86 | FT_UInt count = table->num_hints; |
| 87 | PSH2_Hint hint2; |
| 88 | |
| 89 | hint->parent = 0; |
| 90 | for ( ; count > 0; count--, sorted++ ) |
| 91 | { |
| 92 | hint2 = sorted[0]; |
| 93 | |
| 94 | if ( psh2_hint_overlap( hint, hint2 ) ) |
| 95 | { |
| 96 | hint->parent = hint2; |
| 97 | break; |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | if ( table->num_hints < table->max_hints ) |
| 103 | table->sort_global[ table->num_hints++ ] = hint; |
| 104 | else |
| 105 | { |
| 106 | FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", |
| 107 | "ps.fitter" )); |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | |
| 112 | static void |
| 113 | psh2_hint_table_record_mask( PSH2_Hint_Table table, |
| 114 | PS_Mask hint_mask ) |
| 115 | { |
| 116 | FT_Int mask = 0, val = 0; |
| 117 | FT_Byte* cursor = hint_mask->bytes; |
| 118 | FT_UInt index, limit; |
| 119 | |
| 120 | limit = hint_mask->num_bits; |
| 121 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 122 | for ( index = 0; index < limit; index++ ) |
| 123 | { |
| 124 | if ( mask == 0 ) |
| 125 | { |
| 126 | val = *cursor++; |
| 127 | mask = 0x80; |
| 128 | } |
| 129 | |
| 130 | if ( val & mask ) |
| 131 | psh2_hint_table_record( table, index ); |
| 132 | |
| 133 | mask >>= 1; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | |
| 138 | /* create hints table */ |
| 139 | static FT_Error |
| 140 | psh2_hint_table_init( PSH2_Hint_Table table, |
| 141 | PS_Hint_Table hints, |
| 142 | PS_Mask_Table hint_masks, |
| 143 | PS_Mask_Table counter_masks, |
| 144 | FT_Memory memory ) |
| 145 | { |
| 146 | FT_UInt count = hints->num_hints; |
| 147 | FT_Error error; |
| 148 | |
| 149 | FT_UNUSED(counter_masks); |
| 150 | |
| 151 | /* allocate our tables */ |
| 152 | if ( ALLOC_ARRAY( table->sort, 2*count, PSH2_Hint ) || |
| 153 | ALLOC_ARRAY( table->hints, count, PSH2_HintRec ) || |
| 154 | ALLOC_ARRAY( table->zones, 2*count+1, PSH2_ZoneRec ) ) |
| 155 | goto Exit; |
| 156 | |
| 157 | table->max_hints = count; |
| 158 | table->sort_global = table->sort + count; |
| 159 | table->num_hints = 0; |
| 160 | table->num_zones = 0; |
| 161 | table->zone = 0; |
| 162 | |
| 163 | /* now, initialise the "hints" array */ |
| 164 | { |
| 165 | PSH2_Hint write = table->hints; |
| 166 | PS_Hint read = hints->hints; |
| 167 | |
| 168 | for ( ; count > 0; count--, write++, read++ ) |
| 169 | { |
| 170 | write->org_pos = read->pos; |
| 171 | write->org_len = read->len; |
| 172 | write->flags = read->flags; |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | /* we now need to determine the initial "parent" stems, first */ |
| 177 | /* activate the hints that are given by the initial hint masks */ |
| 178 | if ( hint_masks ) |
| 179 | { |
| 180 | FT_UInt count = hint_masks->num_masks; |
| 181 | PS_Mask mask = hint_masks->masks; |
| 182 | |
| 183 | table->hint_masks = hint_masks; |
| 184 | |
| 185 | for ( ; count > 0; count--, mask++ ) |
| 186 | psh2_hint_table_record_mask( table, mask ); |
| 187 | } |
| 188 | |
| 189 | /* now, do a linear parse in case some hints were left alone */ |
| 190 | if ( table->num_hints != table->max_hints ) |
| 191 | { |
| 192 | FT_UInt index, count; |
| 193 | |
| 194 | FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); |
| 195 | count = table->max_hints; |
| 196 | for ( index = 0; index < count; index++ ) |
| 197 | psh2_hint_table_record( table, index ); |
| 198 | } |
| 199 | |
| 200 | Exit: |
| 201 | return error; |
| 202 | } |
| 203 | |
| 204 | |
| 205 | |
| 206 | static void |
| 207 | psh2_hint_table_activate_mask( PSH2_Hint_Table table, |
| 208 | PS_Mask hint_mask ) |
| 209 | { |
| 210 | FT_Int mask = 0, val = 0; |
| 211 | FT_Byte* cursor = hint_mask->bytes; |
| 212 | FT_UInt index, limit, count; |
| 213 | |
| 214 | limit = hint_mask->num_bits; |
| 215 | count = 0; |
| 216 | |
| 217 | psh2_hint_table_deactivate( table ); |
| 218 | |
| 219 | for ( index = 0; index < limit; index++ ) |
| 220 | { |
| 221 | if ( mask == 0 ) |
| 222 | { |
| 223 | val = *cursor++; |
| 224 | mask = 0x80; |
| 225 | } |
| 226 | |
| 227 | if ( val & mask ) |
| 228 | { |
| 229 | PSH2_Hint hint = &table->hints[index]; |
| 230 | |
| 231 | if ( !psh2_hint_is_active(hint) ) |
| 232 | { |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 233 | FT_UInt count2; |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 234 | |
| 235 | #if 0 |
David Turner | 7e4b52d | 2001-10-19 09:17:49 +0000 | [diff] [blame] | 236 | PSH2_Hint* sort = table->sort; |
David Turner | f373e2d | 2001-10-18 13:20:01 +0000 | [diff] [blame] | 237 | PSH2_Hint hint2; |
| 238 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 239 | for ( count2 = count; count2 > 0; count2--, sort++ ) |
| 240 | { |
| 241 | hint2 = sort[0]; |
| 242 | if ( psh2_hint_overlap( hint, hint2 ) ) |
| 243 | { |
| 244 | FT_ERROR(( "%s.activate_mask: found overlapping hints\n", |
| 245 | "psf.hint" )); |
| 246 | } |
| 247 | } |
| 248 | #else |
| 249 | count2 = 0; |
| 250 | #endif |
| 251 | |
| 252 | if ( count2 == 0 ) |
| 253 | { |
| 254 | psh2_hint_activate( hint ); |
| 255 | if ( count < table->max_hints ) |
| 256 | table->sort[count++] = hint; |
| 257 | else |
| 258 | { |
| 259 | FT_ERROR(( "%s.activate_mask: too many active hints\n", |
| 260 | "psf.hint" )); |
| 261 | } |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | |
| 266 | mask >>= 1; |
| 267 | } |
| 268 | table->num_hints = count; |
| 269 | |
| 270 | /* now, sort the hints, they're guaranteed to not overlap */ |
| 271 | /* so we can compare their "org_pos" field directly.. */ |
| 272 | { |
| 273 | FT_Int i1, i2; |
| 274 | PSH2_Hint hint1, hint2; |
| 275 | PSH2_Hint* sort = table->sort; |
| 276 | |
| 277 | /* a simple bubble sort will do, since in 99% of cases, the hints */ |
| 278 | /* will be already sorted.. and the sort will be linear */ |
| 279 | for ( i1 = 1; i1 < (FT_Int)count; i1++ ) |
| 280 | { |
| 281 | hint1 = sort[i1]; |
| 282 | for ( i2 = i1-1; i2 >= 0; i2-- ) |
| 283 | { |
| 284 | hint2 = sort[i2]; |
| 285 | if ( hint2->org_pos < hint1->org_pos ) |
| 286 | break; |
| 287 | |
| 288 | sort[i1] = hint2; |
| 289 | sort[i2] = hint1; |
| 290 | } |
| 291 | } |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | |
| 296 | |
| 297 | |
| 298 | |
| 299 | |
| 300 | |
| 301 | /************************************************************************/ |
| 302 | /************************************************************************/ |
| 303 | /***** *****/ |
| 304 | /***** HINTS GRID-FITTING AND OPTIMISATION *****/ |
| 305 | /***** *****/ |
| 306 | /************************************************************************/ |
| 307 | /************************************************************************/ |
| 308 | |
| 309 | #ifdef DEBUG_HINTER |
| 310 | static void |
David Turner | 9d7e5e8 | 2001-10-29 17:22:12 +0000 | [diff] [blame] | 311 | ps2_simple_scale( PSH2_Hint_Table table, |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 312 | FT_Fixed scale, |
| 313 | FT_Fixed delta, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 314 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 315 | { |
| 316 | PSH2_Hint hint; |
| 317 | FT_UInt count; |
| 318 | |
| 319 | for ( count = 0; count < table->max_hints; count++ ) |
| 320 | { |
| 321 | hint = table->hints + count; |
| 322 | |
| 323 | hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; |
| 324 | hint->cur_len = FT_MulFix( hint->org_len, scale ); |
| 325 | |
| 326 | if (ps2_debug_hint_func) |
| 327 | ps2_debug_hint_func( hint, vertical ); |
| 328 | } |
| 329 | } |
| 330 | #endif |
| 331 | |
| 332 | |
| 333 | static void |
| 334 | psh2_hint_align( PSH2_Hint hint, |
| 335 | PSH_Globals globals, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 336 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 337 | { |
| 338 | PSH_Dimension dim = &globals->dimension[vertical]; |
| 339 | FT_Fixed scale = dim->scale_mult; |
| 340 | FT_Fixed delta = dim->scale_delta; |
| 341 | |
| 342 | if ( !psh2_hint_is_fitted(hint) ) |
| 343 | { |
| 344 | FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; |
| 345 | FT_Pos len = FT_MulFix( hint->org_len, scale ); |
| 346 | |
| 347 | FT_Pos fit_center; |
| 348 | FT_Pos fit_len; |
| 349 | |
| 350 | PSH_AlignmentRec align; |
| 351 | |
| 352 | /* compute fitted width/height */ |
| 353 | fit_len = 0; |
| 354 | if ( hint->org_len ) |
| 355 | { |
| 356 | fit_len = psh_dimension_snap_width( dim, hint->org_len ); |
| 357 | if ( fit_len < 64 ) |
| 358 | fit_len = 64; |
| 359 | else |
| 360 | fit_len = (fit_len + 32 ) & -64; |
| 361 | } |
| 362 | |
| 363 | hint->cur_len = fit_len; |
| 364 | |
| 365 | /* check blue zones for horizontal stems */ |
| 366 | align.align = 0; |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 367 | align.align_bot = align.align_top = 0; |
| 368 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 369 | if (!vertical) |
| 370 | { |
| 371 | psh_blues_snap_stem( &globals->blues, |
| 372 | hint->org_pos + hint->org_len, |
| 373 | hint->org_pos, |
| 374 | &align ); |
| 375 | } |
| 376 | |
| 377 | switch (align.align) |
| 378 | { |
| 379 | case PSH_BLUE_ALIGN_TOP: |
| 380 | { |
| 381 | /* the top of the stem is aligned against a blue zone */ |
| 382 | hint->cur_pos = align.align_top - fit_len; |
| 383 | break; |
| 384 | } |
| 385 | |
| 386 | case PSH_BLUE_ALIGN_BOT: |
| 387 | { |
| 388 | /* the bottom of the stem is aligned against a blue zone */ |
| 389 | hint->cur_pos = align.align_bot; |
| 390 | break; |
| 391 | } |
| 392 | |
| 393 | case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: |
| 394 | { |
| 395 | /* both edges of the stem are aligned against blue zones */ |
| 396 | hint->cur_pos = align.align_bot; |
| 397 | hint->cur_len = align.align_top - align.align_bot; |
| 398 | break; |
| 399 | } |
| 400 | |
| 401 | default: |
| 402 | { |
| 403 | PSH2_Hint parent = hint->parent; |
| 404 | |
| 405 | if ( parent ) |
| 406 | { |
| 407 | FT_Pos par_org_center, par_cur_center; |
| 408 | FT_Pos cur_org_center, cur_delta; |
| 409 | |
| 410 | /* ensure that parent is already fitted */ |
| 411 | if ( !psh2_hint_is_fitted(parent) ) |
| 412 | psh2_hint_align( parent, globals, vertical ); |
| 413 | |
| 414 | par_org_center = parent->org_pos + (parent->org_len/2); |
| 415 | par_cur_center = parent->cur_pos + (parent->cur_len/2); |
| 416 | cur_org_center = hint->org_pos + (hint->org_len/2); |
| 417 | |
| 418 | cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); |
| 419 | #if 0 |
| 420 | if ( cur_delta >= 0 ) |
| 421 | cur_delta = (cur_delta+16) & -64; |
| 422 | else |
| 423 | cur_delta = -((-cur_delta+16) & -64); |
| 424 | #endif |
| 425 | pos = par_cur_center + cur_delta - (len >> 1); |
| 426 | } |
| 427 | |
| 428 | /* normal processing */ |
| 429 | if ( (fit_len/64) & 1 ) |
| 430 | { |
| 431 | /* odd number of pixels */ |
| 432 | fit_center = ((pos + (len >> 1)) & -64) + 32; |
| 433 | } |
| 434 | else |
| 435 | { |
| 436 | /* even number of pixels */ |
| 437 | fit_center = (pos + (len >> 1) + 32) & -64; |
| 438 | } |
| 439 | |
| 440 | hint->cur_pos = fit_center - (fit_len >> 1); |
| 441 | } |
| 442 | } |
| 443 | |
| 444 | psh2_hint_set_fitted(hint); |
| 445 | |
| 446 | #ifdef DEBUG_HINTER |
| 447 | if (ps2_debug_hint_func) |
| 448 | ps2_debug_hint_func( hint, vertical ); |
| 449 | #endif |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | |
| 454 | static void |
| 455 | psh2_hint_table_align_hints( PSH2_Hint_Table table, |
| 456 | PSH_Globals globals, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 457 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 458 | { |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 459 | PSH2_Hint hint; |
| 460 | FT_UInt count; |
| 461 | |
| 462 | #ifdef DEBUG_HINTER |
David Turner | a11cd4e | 2001-10-26 07:21:38 +0000 | [diff] [blame] | 463 | PSH_Dimension dim = &globals->dimension[vertical]; |
| 464 | FT_Fixed scale = dim->scale_mult; |
| 465 | FT_Fixed delta = dim->scale_delta; |
| 466 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 467 | if ( ps_debug_no_vert_hints && vertical ) |
| 468 | { |
David Turner | 9d7e5e8 | 2001-10-29 17:22:12 +0000 | [diff] [blame] | 469 | ps2_simple_scale( table, scale, delta, vertical ); |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 470 | return; |
| 471 | } |
| 472 | |
| 473 | if ( ps_debug_no_horz_hints && !vertical ) |
| 474 | { |
David Turner | 9d7e5e8 | 2001-10-29 17:22:12 +0000 | [diff] [blame] | 475 | ps2_simple_scale( table, scale, delta, vertical ); |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 476 | return; |
| 477 | } |
| 478 | #endif |
| 479 | |
| 480 | hint = table->hints; |
| 481 | count = table->max_hints; |
| 482 | for ( ; count > 0; count--, hint++ ) |
| 483 | psh2_hint_align( hint, globals, vertical ); |
| 484 | } |
| 485 | |
| 486 | |
| 487 | /************************************************************************/ |
| 488 | /************************************************************************/ |
| 489 | /***** *****/ |
| 490 | /***** POINTS INTERPOLATION ROUTINES *****/ |
| 491 | /***** *****/ |
| 492 | /************************************************************************/ |
| 493 | /************************************************************************/ |
| 494 | |
| 495 | #define PSH2_ZONE_MIN -3200000 |
| 496 | #define PSH2_ZONE_MAX +3200000 |
| 497 | |
| 498 | |
| 499 | #define xxDEBUG_ZONES |
| 500 | |
| 501 | #ifdef DEBUG_ZONES |
| 502 | |
| 503 | #include <stdio.h> |
| 504 | |
| 505 | static void |
| 506 | print_zone( PSH2_Zone zone ) |
| 507 | { |
| 508 | printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", |
| 509 | zone->scale/65536.0, |
| 510 | zone->delta/64.0, |
| 511 | zone->min, |
| 512 | zone->max ); |
| 513 | } |
| 514 | |
| 515 | #else |
| 516 | # define print_zone(x) do { } while (0) |
| 517 | #endif |
| 518 | |
David Turner | 7e4b52d | 2001-10-19 09:17:49 +0000 | [diff] [blame] | 519 | #if 0 |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 520 | /* setup interpolation zones once the hints have been grid-fitted */ |
| 521 | /* by the optimizer.. */ |
| 522 | static void |
| 523 | psh2_hint_table_setup_zones( PSH2_Hint_Table table, |
| 524 | FT_Fixed scale, |
| 525 | FT_Fixed delta ) |
| 526 | { |
| 527 | FT_UInt count; |
| 528 | PSH2_Zone zone; |
| 529 | PSH2_Hint *sort, hint, hint2; |
| 530 | |
| 531 | zone = table->zones; |
| 532 | |
| 533 | /* special case, no hints defined */ |
| 534 | if ( table->num_hints == 0 ) |
| 535 | { |
| 536 | zone->scale = scale; |
| 537 | zone->delta = delta; |
| 538 | zone->min = PSH2_ZONE_MIN; |
| 539 | zone->max = PSH2_ZONE_MAX; |
| 540 | |
| 541 | table->num_zones = 1; |
| 542 | table->zone = zone; |
| 543 | return; |
| 544 | } |
| 545 | |
| 546 | /* the first zone is before the first hint */ |
| 547 | /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ |
| 548 | sort = table->sort; |
| 549 | hint = sort[0]; |
| 550 | |
| 551 | zone->scale = scale; |
| 552 | zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); |
| 553 | zone->min = PSH2_ZONE_MIN; |
| 554 | zone->max = hint->org_pos; |
| 555 | |
| 556 | print_zone( zone ); |
| 557 | |
| 558 | zone++; |
| 559 | |
| 560 | for ( count = table->num_hints; count > 0; count-- ) |
| 561 | { |
| 562 | FT_Fixed scale2; |
| 563 | |
| 564 | if ( hint->org_len > 0 ) |
| 565 | { |
| 566 | /* setup a zone for inner-stem interpolation */ |
| 567 | /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ |
| 568 | /* x' = x*s2 + x0' - x0*s2 */ |
| 569 | |
| 570 | scale2 = FT_DivFix( hint->cur_len, hint->org_len ); |
| 571 | zone->scale = scale2; |
| 572 | zone->min = hint->org_pos; |
| 573 | zone->max = hint->org_pos + hint->org_len; |
| 574 | zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); |
| 575 | |
| 576 | print_zone( zone ); |
| 577 | |
| 578 | zone++; |
| 579 | } |
| 580 | |
| 581 | if ( count == 1 ) |
| 582 | break; |
| 583 | |
| 584 | sort++; |
| 585 | hint2 = sort[0]; |
| 586 | |
| 587 | /* setup zone for inter-stem interpolation */ |
| 588 | /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ |
| 589 | /* x' = x*s3 + x1' - x1*s3 */ |
| 590 | scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), |
| 591 | hint2->org_pos - (hint->org_pos + hint->org_len) ); |
| 592 | zone->scale = scale2; |
| 593 | zone->min = hint->org_pos + hint->org_len; |
| 594 | zone->max = hint2->org_pos; |
| 595 | zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); |
| 596 | |
| 597 | print_zone( zone ); |
| 598 | |
| 599 | zone++; |
| 600 | |
| 601 | hint = hint2; |
| 602 | } |
| 603 | |
| 604 | /* the last zone */ |
| 605 | zone->scale = scale; |
| 606 | zone->min = hint->org_pos + hint->org_len; |
| 607 | zone->max = PSH2_ZONE_MAX; |
| 608 | zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); |
| 609 | |
| 610 | print_zone( zone ); |
| 611 | |
| 612 | zone++; |
| 613 | |
| 614 | table->num_zones = zone - table->zones; |
| 615 | table->zone = table->zones; |
| 616 | } |
David Turner | 7e4b52d | 2001-10-19 09:17:49 +0000 | [diff] [blame] | 617 | #endif |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 618 | |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 619 | #if 0 |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 620 | /* tune a single coordinate with the current interpolation zones */ |
| 621 | static FT_Pos |
| 622 | psh2_hint_table_tune_coord( PSH2_Hint_Table table, |
| 623 | FT_Int coord ) |
| 624 | { |
| 625 | PSH2_Zone zone; |
| 626 | |
| 627 | zone = table->zone; |
| 628 | |
| 629 | if ( coord < zone->min ) |
| 630 | { |
| 631 | do |
| 632 | { |
| 633 | if ( zone == table->zones ) |
| 634 | break; |
| 635 | |
| 636 | zone--; |
| 637 | } |
| 638 | while ( coord < zone->min ); |
| 639 | table->zone = zone; |
| 640 | } |
| 641 | else if ( coord > zone->max ) |
| 642 | { |
| 643 | do |
| 644 | { |
| 645 | if ( zone == table->zones + table->num_zones - 1 ) |
| 646 | break; |
| 647 | |
| 648 | zone++; |
| 649 | } |
| 650 | while ( coord > zone->max ); |
| 651 | table->zone = zone; |
| 652 | } |
| 653 | |
| 654 | return FT_MulFix( coord, zone->scale ) + zone->delta; |
| 655 | } |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 656 | #endif |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 657 | |
| 658 | #if 0 |
| 659 | /* tune a given outline with current interpolation zones */ |
| 660 | /* the function only works in a single dimension.. */ |
| 661 | static void |
| 662 | psh2_hint_table_tune_outline( PSH2_Hint_Table table, |
| 663 | FT_Outline* outline, |
| 664 | PSH_Globals globals, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 665 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 666 | |
| 667 | { |
| 668 | FT_UInt count, first, last; |
| 669 | PS_Mask_Table hint_masks = table->hint_masks; |
| 670 | PS_Mask mask; |
| 671 | PSH_Dimension dim = &globals->dimension[vertical]; |
| 672 | FT_Fixed scale = dim->scale_mult; |
| 673 | FT_Fixed delta = dim->scale_delta; |
| 674 | |
| 675 | if ( hint_masks && hint_masks->num_masks > 0 ) |
| 676 | { |
| 677 | first = 0; |
| 678 | mask = hint_masks->masks; |
| 679 | count = hint_masks->num_masks; |
| 680 | for ( ; count > 0; count--, mask++ ) |
| 681 | { |
| 682 | last = mask->end_point; |
| 683 | |
| 684 | if ( last > first ) |
| 685 | { |
| 686 | FT_Vector* vec; |
| 687 | FT_Int count2; |
| 688 | |
| 689 | psh2_hint_table_activate_mask( table, mask ); |
| 690 | psh2_hint_table_optimize( table, globals, outline, vertical ); |
| 691 | psh2_hint_table_setup_zones( table, scale, delta ); |
| 692 | last = mask->end_point; |
| 693 | |
| 694 | vec = outline->points + first; |
| 695 | count2 = last - first; |
| 696 | for ( ; count2 > 0; count2--, vec++ ) |
| 697 | { |
| 698 | FT_Pos x, *px; |
| 699 | |
| 700 | px = vertical ? &vec->x : &vec->y; |
| 701 | x = *px; |
| 702 | |
| 703 | *px = psh2_hint_table_tune_coord( table, (FT_Int)x ); |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | first = last; |
| 708 | } |
| 709 | } |
| 710 | else /* no hints in this glyph, simply scale the outline */ |
| 711 | { |
| 712 | FT_Vector* vec; |
| 713 | |
| 714 | vec = outline->points; |
| 715 | count = outline->n_points; |
| 716 | |
| 717 | if ( vertical ) |
| 718 | { |
| 719 | for ( ; count > 0; count--, vec++ ) |
| 720 | vec->x = FT_MulFix( vec->x, scale ) + delta; |
| 721 | } |
| 722 | else |
| 723 | { |
| 724 | for ( ; count > 0; count--, vec++ ) |
| 725 | vec->y = FT_MulFix( vec->y, scale ) + delta; |
| 726 | } |
| 727 | } |
| 728 | } |
| 729 | #endif |
| 730 | |
| 731 | /************************************************************************/ |
| 732 | /************************************************************************/ |
| 733 | /***** *****/ |
| 734 | /***** HINTER GLYPH MANAGEMENT *****/ |
| 735 | /***** *****/ |
| 736 | /************************************************************************/ |
| 737 | /************************************************************************/ |
| 738 | |
| 739 | static int |
| 740 | psh2_point_is_extremum( PSH2_Point point ) |
| 741 | { |
| 742 | PSH2_Point before = point; |
| 743 | PSH2_Point after = point; |
| 744 | FT_Pos d_before; |
| 745 | FT_Pos d_after; |
| 746 | |
| 747 | do |
| 748 | { |
| 749 | before = before->prev; |
| 750 | if ( before == point ) |
| 751 | return 0; |
| 752 | |
| 753 | d_before = before->org_u - point->org_u; |
| 754 | } |
| 755 | while ( d_before == 0 ); |
| 756 | |
| 757 | do |
| 758 | { |
| 759 | after = after->next; |
| 760 | if ( after == point ) |
| 761 | return 0; |
| 762 | |
| 763 | d_after = after->org_u - point->org_u; |
| 764 | } |
| 765 | while ( d_after == 0 ); |
| 766 | |
| 767 | return ( ( d_before > 0 && d_after > 0 ) || |
| 768 | ( d_before < 0 && d_after < 0 ) ); |
| 769 | } |
| 770 | |
| 771 | |
| 772 | |
| 773 | static void |
| 774 | psh2_glyph_done( PSH2_Glyph glyph ) |
| 775 | { |
| 776 | FT_Memory memory = glyph->memory; |
| 777 | |
| 778 | psh2_hint_table_done( &glyph->hint_tables[1], memory ); |
| 779 | psh2_hint_table_done( &glyph->hint_tables[0], memory ); |
| 780 | |
| 781 | FREE( glyph->points ); |
| 782 | FREE( glyph->contours ); |
| 783 | |
| 784 | glyph->num_points = 0; |
| 785 | glyph->num_contours = 0; |
| 786 | |
| 787 | glyph->memory = 0; |
| 788 | } |
| 789 | |
| 790 | |
| 791 | static int |
| 792 | psh2_compute_dir( FT_Pos dx, FT_Pos dy ) |
| 793 | { |
| 794 | FT_Pos ax, ay; |
| 795 | int result = PSH2_DIR_NONE; |
| 796 | |
| 797 | ax = ( dx >= 0 ) ? dx : -dx; |
| 798 | ay = ( dy >= 0 ) ? dy : -dy; |
| 799 | |
| 800 | if ( ay*12 < ax ) |
| 801 | { |
| 802 | /* |dy| <<< |dx| means a near-horizontal segment */ |
| 803 | result = ( dx >= 0 ) ? PSH2_DIR_RIGHT : PSH2_DIR_LEFT; |
| 804 | } |
| 805 | else if ( ax*12 < ay ) |
| 806 | { |
| 807 | /* |dx| <<< |dy| means a near-vertical segment */ |
| 808 | result = ( dy >= 0 ) ? PSH2_DIR_UP : PSH2_DIR_DOWN; |
| 809 | } |
| 810 | return result; |
| 811 | } |
| 812 | |
| 813 | |
| 814 | static FT_Error |
| 815 | psh2_glyph_init( PSH2_Glyph glyph, |
| 816 | FT_Outline* outline, |
| 817 | PS_Hints ps_hints, |
| 818 | PSH_Globals globals ) |
| 819 | { |
| 820 | FT_Error error; |
| 821 | FT_Memory memory; |
| 822 | |
| 823 | /* clear all fields */ |
| 824 | memset( glyph, 0, sizeof(*glyph) ); |
| 825 | |
| 826 | memory = globals->memory; |
| 827 | |
| 828 | /* allocate and setup points + contours arrays */ |
| 829 | if ( ALLOC_ARRAY( glyph->points, outline->n_points, PSH2_PointRec ) || |
| 830 | ALLOC_ARRAY( glyph->contours, outline->n_contours, PSH2_ContourRec ) ) |
| 831 | goto Exit; |
| 832 | |
| 833 | glyph->num_points = outline->n_points; |
| 834 | glyph->num_contours = outline->n_contours; |
| 835 | |
| 836 | { |
| 837 | FT_UInt first = 0, next, n; |
| 838 | PSH2_Point points = glyph->points; |
| 839 | PSH2_Contour contour = glyph->contours; |
| 840 | |
| 841 | for ( n = 0; n < glyph->num_contours; n++ ) |
| 842 | { |
| 843 | FT_Int count; |
| 844 | PSH2_Point point; |
| 845 | |
| 846 | next = outline->contours[n] + 1; |
| 847 | count = next - first; |
| 848 | |
| 849 | contour->start = points + first; |
| 850 | contour->count = (FT_UInt)count; |
| 851 | |
| 852 | if ( count > 0 ) |
| 853 | { |
| 854 | point = points + first; |
| 855 | |
| 856 | point->prev = points + next - 1; |
| 857 | point->contour = contour; |
| 858 | for ( ; count > 1; count-- ) |
| 859 | { |
| 860 | point[0].next = point + 1; |
| 861 | point[1].prev = point; |
| 862 | point++; |
| 863 | point->contour = contour; |
| 864 | } |
| 865 | point->next = points + first; |
| 866 | } |
| 867 | |
| 868 | contour++; |
| 869 | first = next; |
| 870 | } |
| 871 | } |
| 872 | |
| 873 | { |
| 874 | PSH2_Point points = glyph->points; |
| 875 | PSH2_Point point = points; |
| 876 | FT_Vector* vec = outline->points; |
| 877 | FT_UInt n; |
| 878 | |
| 879 | for ( n = 0; n < glyph->num_points; n++, point++ ) |
| 880 | { |
| 881 | FT_Int n_prev = point->prev - points; |
| 882 | FT_Int n_next = point->next - points; |
| 883 | FT_Pos dxi, dyi, dxo, dyo; |
| 884 | |
| 885 | if ( !(outline->tags[n] & FT_Curve_Tag_On) ) |
| 886 | point->flags = PSH2_POINT_OFF; |
| 887 | |
| 888 | dxi = vec[n].x - vec[n_prev].x; |
| 889 | dyi = vec[n].y - vec[n_prev].y; |
| 890 | |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 891 | point->dir_in = (FT_Char) psh2_compute_dir( dxi, dyi ); |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 892 | |
| 893 | dxo = vec[n_next].x - vec[n].x; |
| 894 | dyo = vec[n_next].y - vec[n].y; |
| 895 | |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 896 | point->dir_out = (FT_Char) psh2_compute_dir( dxo, dyo ); |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 897 | |
| 898 | /* detect smooth points */ |
| 899 | if ( point->flags & PSH2_POINT_OFF ) |
| 900 | { |
| 901 | point->flags |= PSH2_POINT_SMOOTH; |
| 902 | } |
| 903 | else if ( point->dir_in != PSH2_DIR_NONE || |
| 904 | point->dir_out != PSH2_DIR_NONE ) |
| 905 | { |
| 906 | if ( point->dir_in == point->dir_out ) |
| 907 | point->flags |= PSH2_POINT_SMOOTH; |
| 908 | } |
| 909 | else |
| 910 | { |
| 911 | FT_Angle angle_in, angle_out, diff; |
| 912 | |
| 913 | angle_in = FT_Atan2( dxi, dyi ); |
| 914 | angle_out = FT_Atan2( dxo, dyo ); |
| 915 | |
| 916 | diff = angle_in - angle_out; |
| 917 | if ( diff < 0 ) |
| 918 | diff = -diff; |
| 919 | |
| 920 | if ( diff > FT_ANGLE_PI ) |
| 921 | diff = FT_ANGLE_2PI - diff; |
| 922 | |
| 923 | if ( (diff < FT_ANGLE_PI/16) ) |
| 924 | point->flags |= PSH2_POINT_SMOOTH; |
| 925 | } |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | glyph->memory = memory; |
| 930 | glyph->outline = outline; |
| 931 | glyph->globals = globals; |
| 932 | |
| 933 | /* now deal with hints tables */ |
| 934 | error = psh2_hint_table_init( &glyph->hint_tables [0], |
| 935 | &ps_hints->dimension[0].hints, |
| 936 | &ps_hints->dimension[0].masks, |
| 937 | &ps_hints->dimension[0].counters, |
| 938 | memory ); |
| 939 | if (error) goto Exit; |
| 940 | |
| 941 | error = psh2_hint_table_init( &glyph->hint_tables [1], |
| 942 | &ps_hints->dimension[1].hints, |
| 943 | &ps_hints->dimension[1].masks, |
| 944 | &ps_hints->dimension[1].counters, |
| 945 | memory ); |
| 946 | if (error) goto Exit; |
| 947 | |
| 948 | Exit: |
| 949 | return error; |
| 950 | } |
| 951 | |
| 952 | |
| 953 | /* load outline point coordinates into hinter glyph */ |
| 954 | static void |
| 955 | psh2_glyph_load_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 956 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 957 | { |
| 958 | FT_Vector* vec = glyph->outline->points; |
| 959 | PSH2_Point point = glyph->points; |
| 960 | FT_UInt count = glyph->num_points; |
| 961 | |
| 962 | for ( ; count > 0; count--, point++, vec++ ) |
| 963 | { |
| 964 | point->flags &= PSH2_POINT_OFF | PSH2_POINT_SMOOTH; |
| 965 | point->hint = 0; |
| 966 | if (vertical) |
| 967 | point->org_u = vec->x; |
| 968 | else |
| 969 | point->org_u = vec->y; |
| 970 | |
| 971 | #ifdef DEBUG_HINTER |
| 972 | point->org_x = vec->x; |
| 973 | point->org_y = vec->y; |
| 974 | #endif |
| 975 | } |
| 976 | } |
| 977 | |
| 978 | |
| 979 | /* save hinted point coordinates back to outline */ |
| 980 | static void |
| 981 | psh2_glyph_save_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 982 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 983 | { |
| 984 | FT_UInt n; |
| 985 | PSH2_Point point = glyph->points; |
| 986 | FT_Vector* vec = glyph->outline->points; |
| 987 | char* tags = glyph->outline->tags; |
| 988 | |
| 989 | for ( n = 0; n < glyph->num_points; n++ ) |
| 990 | { |
| 991 | if (vertical) |
| 992 | vec[n].x = point->cur_u; |
| 993 | else |
| 994 | vec[n].y = point->cur_u; |
| 995 | |
| 996 | if ( psh2_point_is_strong(point) ) |
| 997 | tags[n] |= vertical ? 32 : 64; |
| 998 | |
| 999 | #ifdef DEBUG_HINTER |
| 1000 | if (vertical) |
| 1001 | { |
| 1002 | point->cur_x = point->cur_u; |
| 1003 | point->flags_x = point->flags; |
| 1004 | } |
| 1005 | else |
| 1006 | { |
| 1007 | point->cur_y = point->cur_u; |
| 1008 | point->flags_y = point->flags; |
| 1009 | } |
| 1010 | #endif |
| 1011 | point++; |
| 1012 | } |
| 1013 | } |
| 1014 | |
| 1015 | |
| 1016 | static void |
| 1017 | psh2_hint_table_find_strong_point( PSH2_Hint_Table table, |
| 1018 | PSH2_Point point, |
| 1019 | FT_Int major_dir ) |
| 1020 | { |
| 1021 | PSH2_Hint* sort = table->sort; |
| 1022 | FT_UInt num_hints = table->num_hints; |
| 1023 | |
| 1024 | for ( ; num_hints > 0; num_hints--, sort++ ) |
| 1025 | { |
| 1026 | PSH2_Hint hint = sort[0]; |
| 1027 | |
| 1028 | if ( ABS(point->dir_in) == major_dir || |
| 1029 | ABS(point->dir_out) == major_dir ) |
| 1030 | { |
| 1031 | FT_Pos d; |
| 1032 | |
| 1033 | d = point->org_u - hint->org_pos; |
| 1034 | if ( ABS(d) < 3 ) |
| 1035 | { |
| 1036 | Is_Strong: |
| 1037 | psh2_point_set_strong(point); |
| 1038 | point->hint = hint; |
| 1039 | break; |
| 1040 | } |
| 1041 | |
| 1042 | d -= hint->org_len; |
| 1043 | if ( ABS(d) < 3 ) |
| 1044 | goto Is_Strong; |
| 1045 | } |
| 1046 | |
| 1047 | #if 1 |
| 1048 | if ( point->org_u >= hint->org_pos && |
| 1049 | point->org_u <= hint->org_pos + hint->org_len && |
| 1050 | psh2_point_is_extremum( point ) ) |
| 1051 | { |
| 1052 | /* attach to hint, but don't mark as strong */ |
| 1053 | point->hint = hint; |
| 1054 | break; |
| 1055 | } |
| 1056 | #endif |
| 1057 | } |
| 1058 | } |
| 1059 | |
| 1060 | |
| 1061 | |
| 1062 | /* find strong points in a glyph */ |
| 1063 | static void |
| 1064 | psh2_glyph_find_strong_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 1065 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1066 | { |
| 1067 | /* a point is strong if it is located on a stem */ |
| 1068 | /* edge and has an "in" or "out" tangent to the hint's direction */ |
| 1069 | { |
| 1070 | PSH2_Hint_Table table = &glyph->hint_tables[vertical]; |
| 1071 | PS_Mask mask = table->hint_masks->masks; |
| 1072 | FT_UInt num_masks = table->hint_masks->num_masks; |
| 1073 | FT_UInt first = 0; |
| 1074 | FT_Int major_dir = vertical ? PSH2_DIR_UP : PSH2_DIR_RIGHT; |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1075 | |
| 1076 | /* process secondary hints to "selected" points */ |
| 1077 | if ( num_masks > 1 ) |
| 1078 | { |
| 1079 | mask++; |
| 1080 | for ( ; num_masks > 1; num_masks--, mask++ ) |
| 1081 | { |
| 1082 | FT_UInt next; |
| 1083 | FT_Int count; |
| 1084 | |
| 1085 | next = mask->end_point; |
| 1086 | count = next - first; |
| 1087 | if ( count > 0 ) |
| 1088 | { |
| 1089 | PSH2_Point point = glyph->points + first; |
| 1090 | |
| 1091 | psh2_hint_table_activate_mask( table, mask ); |
| 1092 | |
| 1093 | for ( ; count > 0; count--, point++ ) |
| 1094 | psh2_hint_table_find_strong_point( table, point, major_dir ); |
| 1095 | } |
| 1096 | first = next; |
| 1097 | } |
| 1098 | } |
| 1099 | |
| 1100 | /* process primary hints for all points */ |
| 1101 | if ( num_masks == 1 ) |
| 1102 | { |
| 1103 | FT_UInt count = glyph->num_points; |
| 1104 | PSH2_Point point = glyph->points; |
| 1105 | |
| 1106 | psh2_hint_table_activate_mask( table, table->hint_masks->masks ); |
| 1107 | for ( ; count > 0; count--, point++ ) |
| 1108 | { |
| 1109 | if ( !psh2_point_is_strong(point) ) |
| 1110 | psh2_hint_table_find_strong_point( table, point, major_dir ); |
| 1111 | } |
| 1112 | } |
| 1113 | |
| 1114 | /* now, certain points may have been attached to hint and */ |
| 1115 | /* not marked as strong, update their flags then.. */ |
| 1116 | { |
| 1117 | FT_UInt count = glyph->num_points; |
| 1118 | PSH2_Point point = glyph->points; |
| 1119 | |
| 1120 | for ( ; count > 0; count--, point++ ) |
| 1121 | if ( point->hint && !psh2_point_is_strong(point) ) |
| 1122 | psh2_point_set_strong(point); |
| 1123 | } |
| 1124 | } |
| 1125 | } |
| 1126 | |
| 1127 | |
| 1128 | |
| 1129 | /* interpolate strong points with the help of hinted coordinates */ |
| 1130 | static void |
| 1131 | psh2_glyph_interpolate_strong_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 1132 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1133 | { |
| 1134 | PSH_Dimension dim = &glyph->globals->dimension[vertical]; |
| 1135 | FT_Fixed scale = dim->scale_mult; |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1136 | |
| 1137 | { |
| 1138 | FT_UInt count = glyph->num_points; |
| 1139 | PSH2_Point point = glyph->points; |
| 1140 | |
| 1141 | for ( ; count > 0; count--, point++ ) |
| 1142 | { |
| 1143 | PSH2_Hint hint = point->hint; |
| 1144 | |
| 1145 | if ( hint ) |
| 1146 | { |
| 1147 | FT_Pos delta; |
| 1148 | |
| 1149 | delta = point->org_u - hint->org_pos; |
| 1150 | |
| 1151 | if ( delta <= 0 ) |
| 1152 | point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); |
| 1153 | |
| 1154 | else if ( delta >= hint->org_len ) |
| 1155 | point->cur_u = hint->cur_pos + hint->cur_len + |
| 1156 | FT_MulFix( delta - hint->org_len, scale ); |
| 1157 | |
| 1158 | else if ( hint->org_len > 0 ) |
| 1159 | point->cur_u = hint->cur_pos + |
| 1160 | FT_MulDiv( delta, hint->cur_len, hint->org_len ); |
| 1161 | else |
| 1162 | point->cur_u = hint->cur_pos; |
| 1163 | |
| 1164 | psh2_point_set_fitted(point); |
| 1165 | } |
| 1166 | } |
| 1167 | } |
| 1168 | } |
| 1169 | |
| 1170 | |
| 1171 | static void |
| 1172 | psh2_glyph_interpolate_normal_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 1173 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1174 | { |
| 1175 | #if 1 |
| 1176 | PSH_Dimension dim = &glyph->globals->dimension[vertical]; |
| 1177 | FT_Fixed scale = dim->scale_mult; |
| 1178 | |
| 1179 | /* first technique: a point is strong if it is a local extrema */ |
| 1180 | { |
| 1181 | FT_UInt count = glyph->num_points; |
| 1182 | PSH2_Point point = glyph->points; |
| 1183 | |
| 1184 | for ( ; count > 0; count--, point++ ) |
| 1185 | { |
| 1186 | if ( psh2_point_is_strong(point) ) |
| 1187 | continue; |
| 1188 | |
| 1189 | /* sometimes, some local extremas are smooth points */ |
| 1190 | if ( psh2_point_is_smooth(point) ) |
| 1191 | { |
| 1192 | if ( point->dir_in == PSH2_DIR_NONE || |
| 1193 | point->dir_in != point->dir_out ) |
| 1194 | continue; |
| 1195 | |
| 1196 | if ( !psh2_point_is_extremum( point ) ) |
| 1197 | continue; |
| 1198 | |
| 1199 | point->flags &= ~PSH2_POINT_SMOOTH; |
| 1200 | } |
| 1201 | |
| 1202 | /* find best enclosing point coordinates */ |
| 1203 | { |
| 1204 | PSH2_Point before = 0; |
| 1205 | PSH2_Point after = 0; |
| 1206 | |
| 1207 | FT_Pos diff_before = -32000; |
| 1208 | FT_Pos diff_after = 32000; |
| 1209 | FT_Pos u = point->org_u; |
| 1210 | |
| 1211 | FT_Int count2 = glyph->num_points; |
| 1212 | PSH2_Point cur = glyph->points; |
| 1213 | |
| 1214 | for ( ; count2 > 0; count2--, cur++ ) |
| 1215 | { |
| 1216 | if ( psh2_point_is_strong(cur) ) |
| 1217 | { |
| 1218 | FT_Pos diff = cur->org_u - u;; |
| 1219 | |
| 1220 | if ( diff <= 0 ) |
| 1221 | { |
| 1222 | if ( diff > diff_before ) |
| 1223 | { |
| 1224 | diff_before = diff; |
| 1225 | before = cur; |
| 1226 | } |
| 1227 | } |
| 1228 | else if ( diff >= 0 ) |
| 1229 | { |
| 1230 | if ( diff < diff_after ) |
| 1231 | { |
| 1232 | diff_after = diff; |
| 1233 | after = cur; |
| 1234 | } |
| 1235 | } |
| 1236 | } |
| 1237 | } |
| 1238 | |
| 1239 | if ( !before ) |
| 1240 | { |
| 1241 | if ( !after ) |
| 1242 | continue; |
| 1243 | |
| 1244 | /* we're before the first strong point coordinate */ |
| 1245 | /* simply translate the point.. */ |
| 1246 | point->cur_u = after->cur_u + |
| 1247 | FT_MulFix( point->org_u - after->org_u, scale ); |
| 1248 | } |
| 1249 | else if ( !after ) |
| 1250 | { |
| 1251 | /* we're after the last strong point coordinate */ |
| 1252 | /* simply translate the point.. */ |
| 1253 | point->cur_u = before->cur_u + |
| 1254 | FT_MulFix( point->org_u - before->org_u, scale ); |
| 1255 | } |
| 1256 | else |
| 1257 | { |
| 1258 | if ( diff_before == 0 ) |
| 1259 | point->cur_u = before->cur_u; |
| 1260 | |
| 1261 | else if ( diff_after == 0 ) |
| 1262 | point->cur_u = after->cur_u; |
| 1263 | |
| 1264 | else |
| 1265 | point->cur_u = before->cur_u + |
| 1266 | FT_MulDiv( u - before->org_u, |
| 1267 | after->cur_u - before->cur_u, |
| 1268 | after->org_u - before->org_u ); |
| 1269 | } |
| 1270 | |
| 1271 | psh2_point_set_fitted(point); |
| 1272 | } |
| 1273 | } |
| 1274 | } |
| 1275 | #endif |
| 1276 | } |
| 1277 | |
| 1278 | |
| 1279 | |
| 1280 | /* interpolate other points */ |
| 1281 | static void |
| 1282 | psh2_glyph_interpolate_other_points( PSH2_Glyph glyph, |
David Turner | 12d40da | 2001-10-21 15:41:11 +0000 | [diff] [blame] | 1283 | FT_Int vertical ) |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1284 | { |
| 1285 | PSH_Dimension dim = &glyph->globals->dimension[vertical]; |
| 1286 | FT_Fixed scale = dim->scale_mult; |
| 1287 | FT_Fixed delta = dim->scale_delta; |
| 1288 | PSH2_Contour contour = glyph->contours; |
| 1289 | FT_UInt num_contours = glyph->num_contours; |
| 1290 | |
| 1291 | for ( ; num_contours > 0; num_contours--, contour++ ) |
| 1292 | { |
| 1293 | PSH2_Point start = contour->start; |
| 1294 | PSH2_Point first, next, point; |
| 1295 | FT_UInt fit_count; |
| 1296 | |
| 1297 | /* count the number of strong points in this contour */ |
| 1298 | next = start + contour->count; |
| 1299 | fit_count = 0; |
| 1300 | first = 0; |
| 1301 | |
| 1302 | for ( point = start; point < next; point++ ) |
| 1303 | if ( psh2_point_is_fitted(point) ) |
| 1304 | { |
| 1305 | if ( !first ) |
| 1306 | first = point; |
| 1307 | |
| 1308 | fit_count++; |
| 1309 | } |
| 1310 | |
| 1311 | /* if there is less than 2 fitted points in the contour, we'll */ |
| 1312 | /* simply scale and eventually translate the contour points */ |
| 1313 | if ( fit_count < 2 ) |
| 1314 | { |
| 1315 | if ( fit_count == 1 ) |
| 1316 | delta = first->cur_u - FT_MulFix( first->org_u, scale ); |
| 1317 | |
| 1318 | for ( point = start; point < next; point++ ) |
| 1319 | if ( point != first ) |
| 1320 | point->cur_u = FT_MulFix( point->org_u, scale ) + delta; |
| 1321 | |
| 1322 | goto Next_Contour; |
| 1323 | } |
| 1324 | |
| 1325 | /* there are more than 2 strong points in this contour, we'll */ |
| 1326 | /* need to interpolate weak points between them.. */ |
| 1327 | start = first; |
| 1328 | do |
| 1329 | { |
| 1330 | point = first; |
| 1331 | |
| 1332 | /* skip consecutive fitted points */ |
| 1333 | for (;;) |
| 1334 | { |
| 1335 | next = first->next; |
| 1336 | if ( next == start ) |
| 1337 | goto Next_Contour; |
| 1338 | |
| 1339 | if ( !psh2_point_is_fitted(next) ) |
| 1340 | break; |
| 1341 | |
| 1342 | first = next; |
| 1343 | } |
| 1344 | |
| 1345 | /* find next fitted point after unfitted one */ |
| 1346 | for (;;) |
| 1347 | { |
| 1348 | next = next->next; |
| 1349 | if ( psh2_point_is_fitted(next) ) |
| 1350 | break; |
| 1351 | } |
| 1352 | |
| 1353 | /* now interpolate between them */ |
| 1354 | { |
| 1355 | FT_Pos org_a, org_ab, cur_a, cur_ab; |
| 1356 | FT_Pos org_c, org_ac, cur_c; |
| 1357 | FT_Fixed scale_ab; |
| 1358 | |
| 1359 | if ( first->org_u <= next->org_u ) |
| 1360 | { |
| 1361 | org_a = first->org_u; |
| 1362 | cur_a = first->cur_u; |
| 1363 | org_ab = next->org_u - org_a; |
| 1364 | cur_ab = next->cur_u - cur_a; |
| 1365 | } |
| 1366 | else |
| 1367 | { |
| 1368 | org_a = next->org_u; |
| 1369 | cur_a = next->cur_u; |
| 1370 | org_ab = first->org_u - org_a; |
| 1371 | cur_ab = first->cur_u - cur_a; |
| 1372 | } |
| 1373 | |
| 1374 | scale_ab = 0x10000L; |
| 1375 | if ( org_ab > 0 ) |
| 1376 | scale_ab = FT_DivFix( cur_ab, org_ab ); |
| 1377 | |
| 1378 | point = first->next; |
| 1379 | do |
| 1380 | { |
| 1381 | org_c = point->org_u; |
| 1382 | org_ac = org_c - org_a; |
| 1383 | |
| 1384 | if ( org_ac <= 0 ) |
| 1385 | { |
| 1386 | /* on the left of the interpolation zone */ |
| 1387 | cur_c = cur_a + FT_MulFix( org_ac, scale ); |
| 1388 | } |
| 1389 | else if ( org_ac >= org_ab ) |
| 1390 | { |
| 1391 | /* on the right on the interpolation zone */ |
| 1392 | cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); |
| 1393 | } |
| 1394 | else |
| 1395 | { |
| 1396 | /* within the interpolation zone */ |
| 1397 | cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); |
| 1398 | } |
| 1399 | |
| 1400 | point->cur_u = cur_c; |
| 1401 | |
| 1402 | point = point->next; |
| 1403 | } |
| 1404 | while ( point != next ); |
| 1405 | } |
| 1406 | |
| 1407 | /* keep going until all points in the contours have been processed */ |
| 1408 | first = next; |
| 1409 | } |
| 1410 | while ( first != start ); |
| 1411 | |
| 1412 | Next_Contour: |
| 1413 | ; |
| 1414 | } |
| 1415 | } |
| 1416 | |
| 1417 | /************************************************************************/ |
| 1418 | /************************************************************************/ |
| 1419 | /***** *****/ |
| 1420 | /***** HIGH-LEVEL INTERFACE *****/ |
| 1421 | /***** *****/ |
| 1422 | /************************************************************************/ |
| 1423 | /************************************************************************/ |
| 1424 | |
| 1425 | FT_Error |
| 1426 | ps2_hints_apply( PS_Hints ps_hints, |
| 1427 | FT_Outline* outline, |
| 1428 | PSH_Globals globals ) |
| 1429 | { |
| 1430 | PSH2_Glyph glyph; |
| 1431 | PSH2_GlyphRec glyphrec; |
| 1432 | FT_Error error; |
| 1433 | FT_Memory memory; |
| 1434 | FT_Int dimension; |
| 1435 | |
| 1436 | memory = globals->memory; |
| 1437 | |
David Turner | 9d7e5e8 | 2001-10-29 17:22:12 +0000 | [diff] [blame] | 1438 | FT_UNUSED(glyphrec); |
| 1439 | |
| 1440 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1441 | #ifdef DEBUG_HINTER |
| 1442 | if ( ps2_debug_glyph ) |
| 1443 | { |
| 1444 | psh2_glyph_done( ps2_debug_glyph ); |
| 1445 | FREE( ps2_debug_glyph ); |
| 1446 | } |
| 1447 | |
| 1448 | if ( ALLOC( glyph, sizeof(*glyph) ) ) |
| 1449 | return error; |
| 1450 | |
| 1451 | ps2_debug_glyph = glyph; |
| 1452 | #else |
| 1453 | glyph = &glyphrec; |
| 1454 | #endif |
David Turner | 9d7e5e8 | 2001-10-29 17:22:12 +0000 | [diff] [blame] | 1455 | |
David Turner | a83bc08 | 2001-10-18 11:38:43 +0000 | [diff] [blame] | 1456 | error = psh2_glyph_init( glyph, outline, ps_hints, globals ); |
| 1457 | if (error) goto Exit; |
| 1458 | |
| 1459 | for ( dimension = 1; dimension >= 0; dimension-- ) |
| 1460 | { |
| 1461 | /* load outline coordinates into glyph */ |
| 1462 | psh2_glyph_load_points( glyph, dimension ); |
| 1463 | |
| 1464 | /* compute aligned stem/hints positions */ |
| 1465 | psh2_hint_table_align_hints( &glyph->hint_tables[dimension], |
| 1466 | glyph->globals, |
| 1467 | dimension ); |
| 1468 | |
| 1469 | /* find strong points, align them, then interpolate others */ |
| 1470 | psh2_glyph_find_strong_points( glyph, dimension ); |
| 1471 | psh2_glyph_interpolate_strong_points( glyph, dimension ); |
| 1472 | psh2_glyph_interpolate_normal_points( glyph, dimension ); |
| 1473 | psh2_glyph_interpolate_other_points( glyph, dimension ); |
| 1474 | |
| 1475 | /* save hinted coordinates back to outline */ |
| 1476 | psh2_glyph_save_points( glyph, dimension ); |
| 1477 | } |
| 1478 | |
| 1479 | Exit: |
| 1480 | #ifndef DEBUG_HINTER |
| 1481 | psh2_glyph_done( glyph ); |
| 1482 | #endif |
| 1483 | return error; |
| 1484 | } |