Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenpshalgo.c
Go to the documentation of this file.
00001 /***************************************************************************/ 00002 /* */ 00003 /* pshalgo.c */ 00004 /* */ 00005 /* PostScript hinting algorithm (body). */ 00006 /* */ 00007 /* Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 */ 00008 /* by */ 00009 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 00010 /* */ 00011 /* This file is part of the FreeType project, and may only be used */ 00012 /* modified and distributed under the terms of the FreeType project */ 00013 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 00014 /* this file you indicate that you have read the license and */ 00015 /* understand and accept it fully. */ 00016 /* */ 00017 /***************************************************************************/ 00018 00019 00020 #include <ft2build.h> 00021 #include FT_INTERNAL_OBJECTS_H 00022 #include FT_INTERNAL_DEBUG_H 00023 #include FT_INTERNAL_CALC_H 00024 #include "pshalgo.h" 00025 00026 #include "pshnterr.h" 00027 00028 00029 #undef FT_COMPONENT 00030 #define FT_COMPONENT trace_pshalgo2 00031 00032 00033 #ifdef DEBUG_HINTER 00034 PSH_Hint_Table ps_debug_hint_table = 0; 00035 PSH_HintFunc ps_debug_hint_func = 0; 00036 PSH_Glyph ps_debug_glyph = 0; 00037 #endif 00038 00039 00040 #define COMPUTE_INFLEXS /* compute inflection points to optimize `S' */ 00041 /* and similar glyphs */ 00042 #define STRONGER /* slightly increase the contrast of smooth */ 00043 /* hinting */ 00044 00045 00046 /*************************************************************************/ 00047 /*************************************************************************/ 00048 /***** *****/ 00049 /***** BASIC HINTS RECORDINGS *****/ 00050 /***** *****/ 00051 /*************************************************************************/ 00052 /*************************************************************************/ 00053 00054 /* return true if two stem hints overlap */ 00055 static FT_Int 00056 psh_hint_overlap( PSH_Hint hint1, 00057 PSH_Hint hint2 ) 00058 { 00059 return hint1->org_pos + hint1->org_len >= hint2->org_pos && 00060 hint2->org_pos + hint2->org_len >= hint1->org_pos; 00061 } 00062 00063 00064 /* destroy hints table */ 00065 static void 00066 psh_hint_table_done( PSH_Hint_Table table, 00067 FT_Memory memory ) 00068 { 00069 FT_FREE( table->zones ); 00070 table->num_zones = 0; 00071 table->zone = 0; 00072 00073 FT_FREE( table->sort ); 00074 FT_FREE( table->hints ); 00075 table->num_hints = 0; 00076 table->max_hints = 0; 00077 table->sort_global = 0; 00078 } 00079 00080 00081 /* deactivate all hints in a table */ 00082 static void 00083 psh_hint_table_deactivate( PSH_Hint_Table table ) 00084 { 00085 FT_UInt count = table->max_hints; 00086 PSH_Hint hint = table->hints; 00087 00088 00089 for ( ; count > 0; count--, hint++ ) 00090 { 00091 psh_hint_deactivate( hint ); 00092 hint->order = -1; 00093 } 00094 } 00095 00096 00097 /* internal function to record a new hint */ 00098 static void 00099 psh_hint_table_record( PSH_Hint_Table table, 00100 FT_UInt idx ) 00101 { 00102 PSH_Hint hint = table->hints + idx; 00103 00104 00105 if ( idx >= table->max_hints ) 00106 { 00107 FT_TRACE0(( "psh_hint_table_record: invalid hint index %d\n", idx )); 00108 return; 00109 } 00110 00111 /* ignore active hints */ 00112 if ( psh_hint_is_active( hint ) ) 00113 return; 00114 00115 psh_hint_activate( hint ); 00116 00117 /* now scan the current active hint set to check */ 00118 /* whether `hint' overlaps with another hint */ 00119 { 00120 PSH_Hint* sorted = table->sort_global; 00121 FT_UInt count = table->num_hints; 00122 PSH_Hint hint2; 00123 00124 00125 hint->parent = 0; 00126 for ( ; count > 0; count--, sorted++ ) 00127 { 00128 hint2 = sorted[0]; 00129 00130 if ( psh_hint_overlap( hint, hint2 ) ) 00131 { 00132 hint->parent = hint2; 00133 break; 00134 } 00135 } 00136 } 00137 00138 if ( table->num_hints < table->max_hints ) 00139 table->sort_global[table->num_hints++] = hint; 00140 else 00141 FT_TRACE0(( "psh_hint_table_record: too many sorted hints! BUG!\n" )); 00142 } 00143 00144 00145 static void 00146 psh_hint_table_record_mask( PSH_Hint_Table table, 00147 PS_Mask hint_mask ) 00148 { 00149 FT_Int mask = 0, val = 0; 00150 FT_Byte* cursor = hint_mask->bytes; 00151 FT_UInt idx, limit; 00152 00153 00154 limit = hint_mask->num_bits; 00155 00156 for ( idx = 0; idx < limit; idx++ ) 00157 { 00158 if ( mask == 0 ) 00159 { 00160 val = *cursor++; 00161 mask = 0x80; 00162 } 00163 00164 if ( val & mask ) 00165 psh_hint_table_record( table, idx ); 00166 00167 mask >>= 1; 00168 } 00169 } 00170 00171 00172 /* create hints table */ 00173 static FT_Error 00174 psh_hint_table_init( PSH_Hint_Table table, 00175 PS_Hint_Table hints, 00176 PS_Mask_Table hint_masks, 00177 PS_Mask_Table counter_masks, 00178 FT_Memory memory ) 00179 { 00180 FT_UInt count; 00181 FT_Error error; 00182 00183 FT_UNUSED( counter_masks ); 00184 00185 00186 count = hints->num_hints; 00187 00188 /* allocate our tables */ 00189 if ( FT_NEW_ARRAY( table->sort, 2 * count ) || 00190 FT_NEW_ARRAY( table->hints, count ) || 00191 FT_NEW_ARRAY( table->zones, 2 * count + 1 ) ) 00192 goto Exit; 00193 00194 table->max_hints = count; 00195 table->sort_global = table->sort + count; 00196 table->num_hints = 0; 00197 table->num_zones = 0; 00198 table->zone = 0; 00199 00200 /* initialize the `table->hints' array */ 00201 { 00202 PSH_Hint write = table->hints; 00203 PS_Hint read = hints->hints; 00204 00205 00206 for ( ; count > 0; count--, write++, read++ ) 00207 { 00208 write->org_pos = read->pos; 00209 write->org_len = read->len; 00210 write->flags = read->flags; 00211 } 00212 } 00213 00214 /* we now need to determine the initial `parent' stems; first */ 00215 /* activate the hints that are given by the initial hint masks */ 00216 if ( hint_masks ) 00217 { 00218 PS_Mask mask = hint_masks->masks; 00219 00220 00221 count = hint_masks->num_masks; 00222 table->hint_masks = hint_masks; 00223 00224 for ( ; count > 0; count--, mask++ ) 00225 psh_hint_table_record_mask( table, mask ); 00226 } 00227 00228 /* finally, do a linear parse in case some hints were left alone */ 00229 if ( table->num_hints != table->max_hints ) 00230 { 00231 FT_UInt idx; 00232 00233 00234 FT_TRACE0(( "psh_hint_table_init: missing/incorrect hint masks\n" )); 00235 00236 count = table->max_hints; 00237 for ( idx = 0; idx < count; idx++ ) 00238 psh_hint_table_record( table, idx ); 00239 } 00240 00241 Exit: 00242 return error; 00243 } 00244 00245 00246 static void 00247 psh_hint_table_activate_mask( PSH_Hint_Table table, 00248 PS_Mask hint_mask ) 00249 { 00250 FT_Int mask = 0, val = 0; 00251 FT_Byte* cursor = hint_mask->bytes; 00252 FT_UInt idx, limit, count; 00253 00254 00255 limit = hint_mask->num_bits; 00256 count = 0; 00257 00258 psh_hint_table_deactivate( table ); 00259 00260 for ( idx = 0; idx < limit; idx++ ) 00261 { 00262 if ( mask == 0 ) 00263 { 00264 val = *cursor++; 00265 mask = 0x80; 00266 } 00267 00268 if ( val & mask ) 00269 { 00270 PSH_Hint hint = &table->hints[idx]; 00271 00272 00273 if ( !psh_hint_is_active( hint ) ) 00274 { 00275 FT_UInt count2; 00276 00277 #if 0 00278 PSH_Hint* sort = table->sort; 00279 PSH_Hint hint2; 00280 00281 00282 for ( count2 = count; count2 > 0; count2--, sort++ ) 00283 { 00284 hint2 = sort[0]; 00285 if ( psh_hint_overlap( hint, hint2 ) ) 00286 FT_TRACE0(( "psh_hint_table_activate_mask:" 00287 " found overlapping hints\n" )) 00288 } 00289 #else 00290 count2 = 0; 00291 #endif 00292 00293 if ( count2 == 0 ) 00294 { 00295 psh_hint_activate( hint ); 00296 if ( count < table->max_hints ) 00297 table->sort[count++] = hint; 00298 else 00299 FT_TRACE0(( "psh_hint_tableactivate_mask:" 00300 " too many active hints\n" )); 00301 } 00302 } 00303 } 00304 00305 mask >>= 1; 00306 } 00307 table->num_hints = count; 00308 00309 /* now, sort the hints; they are guaranteed to not overlap */ 00310 /* so we can compare their "org_pos" field directly */ 00311 { 00312 FT_Int i1, i2; 00313 PSH_Hint hint1, hint2; 00314 PSH_Hint* sort = table->sort; 00315 00316 00317 /* a simple bubble sort will do, since in 99% of cases, the hints */ 00318 /* will be already sorted -- and the sort will be linear */ 00319 for ( i1 = 1; i1 < (FT_Int)count; i1++ ) 00320 { 00321 hint1 = sort[i1]; 00322 for ( i2 = i1 - 1; i2 >= 0; i2-- ) 00323 { 00324 hint2 = sort[i2]; 00325 00326 if ( hint2->org_pos < hint1->org_pos ) 00327 break; 00328 00329 sort[i2 + 1] = hint2; 00330 sort[i2] = hint1; 00331 } 00332 } 00333 } 00334 } 00335 00336 00337 /*************************************************************************/ 00338 /*************************************************************************/ 00339 /***** *****/ 00340 /***** HINTS GRID-FITTING AND OPTIMIZATION *****/ 00341 /***** *****/ 00342 /*************************************************************************/ 00343 /*************************************************************************/ 00344 00345 #if 1 00346 static FT_Pos 00347 psh_dimension_quantize_len( PSH_Dimension dim, 00348 FT_Pos len, 00349 FT_Bool do_snapping ) 00350 { 00351 if ( len <= 64 ) 00352 len = 64; 00353 else 00354 { 00355 FT_Pos delta = len - dim->stdw.widths[0].cur; 00356 00357 00358 if ( delta < 0 ) 00359 delta = -delta; 00360 00361 if ( delta < 40 ) 00362 { 00363 len = dim->stdw.widths[0].cur; 00364 if ( len < 48 ) 00365 len = 48; 00366 } 00367 00368 if ( len < 3 * 64 ) 00369 { 00370 delta = ( len & 63 ); 00371 len &= -64; 00372 00373 if ( delta < 10 ) 00374 len += delta; 00375 00376 else if ( delta < 32 ) 00377 len += 10; 00378 00379 else if ( delta < 54 ) 00380 len += 54; 00381 00382 else 00383 len += delta; 00384 } 00385 else 00386 len = FT_PIX_ROUND( len ); 00387 } 00388 00389 if ( do_snapping ) 00390 len = FT_PIX_ROUND( len ); 00391 00392 return len; 00393 } 00394 #endif /* 0 */ 00395 00396 00397 #ifdef DEBUG_HINTER 00398 00399 static void 00400 ps_simple_scale( PSH_Hint_Table table, 00401 FT_Fixed scale, 00402 FT_Fixed delta, 00403 FT_Int dimension ) 00404 { 00405 PSH_Hint hint; 00406 FT_UInt count; 00407 00408 00409 for ( count = 0; count < table->max_hints; count++ ) 00410 { 00411 hint = table->hints + count; 00412 00413 hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; 00414 hint->cur_len = FT_MulFix( hint->org_len, scale ); 00415 00416 if ( ps_debug_hint_func ) 00417 ps_debug_hint_func( hint, dimension ); 00418 } 00419 } 00420 00421 #endif /* DEBUG_HINTER */ 00422 00423 00424 static FT_Fixed 00425 psh_hint_snap_stem_side_delta( FT_Fixed pos, 00426 FT_Fixed len ) 00427 { 00428 FT_Fixed delta1 = FT_PIX_ROUND( pos ) - pos; 00429 FT_Fixed delta2 = FT_PIX_ROUND( pos + len ) - pos - len; 00430 00431 00432 if ( FT_ABS( delta1 ) <= FT_ABS( delta2 ) ) 00433 return delta1; 00434 else 00435 return delta2; 00436 } 00437 00438 00439 static void 00440 psh_hint_align( PSH_Hint hint, 00441 PSH_Globals globals, 00442 FT_Int dimension, 00443 PSH_Glyph glyph ) 00444 { 00445 PSH_Dimension dim = &globals->dimension[dimension]; 00446 FT_Fixed scale = dim->scale_mult; 00447 FT_Fixed delta = dim->scale_delta; 00448 00449 00450 if ( !psh_hint_is_fitted( hint ) ) 00451 { 00452 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; 00453 FT_Pos len = FT_MulFix( hint->org_len, scale ); 00454 00455 FT_Int do_snapping; 00456 FT_Pos fit_len; 00457 PSH_AlignmentRec align; 00458 00459 00460 /* ignore stem alignments when requested through the hint flags */ 00461 if ( ( dimension == 0 && !glyph->do_horz_hints ) || 00462 ( dimension == 1 && !glyph->do_vert_hints ) ) 00463 { 00464 hint->cur_pos = pos; 00465 hint->cur_len = len; 00466 00467 psh_hint_set_fitted( hint ); 00468 return; 00469 } 00470 00471 /* perform stem snapping when requested - this is necessary 00472 * for monochrome and LCD hinting modes only 00473 */ 00474 do_snapping = ( dimension == 0 && glyph->do_horz_snapping ) || 00475 ( dimension == 1 && glyph->do_vert_snapping ); 00476 00477 hint->cur_len = fit_len = len; 00478 00479 /* check blue zones for horizontal stems */ 00480 align.align = PSH_BLUE_ALIGN_NONE; 00481 align.align_bot = align.align_top = 0; 00482 00483 if ( dimension == 1 ) 00484 psh_blues_snap_stem( &globals->blues, 00485 hint->org_pos + hint->org_len, 00486 hint->org_pos, 00487 &align ); 00488 00489 switch ( align.align ) 00490 { 00491 case PSH_BLUE_ALIGN_TOP: 00492 /* the top of the stem is aligned against a blue zone */ 00493 hint->cur_pos = align.align_top - fit_len; 00494 break; 00495 00496 case PSH_BLUE_ALIGN_BOT: 00497 /* the bottom of the stem is aligned against a blue zone */ 00498 hint->cur_pos = align.align_bot; 00499 break; 00500 00501 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: 00502 /* both edges of the stem are aligned against blue zones */ 00503 hint->cur_pos = align.align_bot; 00504 hint->cur_len = align.align_top - align.align_bot; 00505 break; 00506 00507 default: 00508 { 00509 PSH_Hint parent = hint->parent; 00510 00511 00512 if ( parent ) 00513 { 00514 FT_Pos par_org_center, par_cur_center; 00515 FT_Pos cur_org_center, cur_delta; 00516 00517 00518 /* ensure that parent is already fitted */ 00519 if ( !psh_hint_is_fitted( parent ) ) 00520 psh_hint_align( parent, globals, dimension, glyph ); 00521 00522 /* keep original relation between hints, this is, use the */ 00523 /* scaled distance between the centers of the hints to */ 00524 /* compute the new position */ 00525 par_org_center = parent->org_pos + ( parent->org_len >> 1 ); 00526 par_cur_center = parent->cur_pos + ( parent->cur_len >> 1 ); 00527 cur_org_center = hint->org_pos + ( hint->org_len >> 1 ); 00528 00529 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); 00530 pos = par_cur_center + cur_delta - ( len >> 1 ); 00531 } 00532 00533 hint->cur_pos = pos; 00534 hint->cur_len = fit_len; 00535 00536 /* Stem adjustment tries to snap stem widths to standard 00537 * ones. This is important to prevent unpleasant rounding 00538 * artefacts. 00539 */ 00540 if ( glyph->do_stem_adjust ) 00541 { 00542 if ( len <= 64 ) 00543 { 00544 /* the stem is less than one pixel; we will center it 00545 * around the nearest pixel center 00546 */ 00547 if ( len >= 32 ) 00548 { 00549 /* This is a special case where we also widen the stem 00550 * and align it to the pixel grid. 00551 * 00552 * stem_center = pos + (len/2) 00553 * nearest_pixel_center = FT_ROUND(stem_center-32)+32 00554 * new_pos = nearest_pixel_center-32 00555 * = FT_ROUND(stem_center-32) 00556 * = FT_FLOOR(stem_center-32+32) 00557 * = FT_FLOOR(stem_center) 00558 * new_len = 64 00559 */ 00560 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ); 00561 len = 64; 00562 } 00563 else if ( len > 0 ) 00564 { 00565 /* This is a very small stem; we simply align it to the 00566 * pixel grid, trying to find the minimal displacement. 00567 * 00568 * left = pos 00569 * right = pos + len 00570 * left_nearest_edge = ROUND(pos) 00571 * right_nearest_edge = ROUND(right) 00572 * 00573 * if ( ABS(left_nearest_edge - left) <= 00574 * ABS(right_nearest_edge - right) ) 00575 * new_pos = left 00576 * else 00577 * new_pos = right 00578 */ 00579 FT_Pos left_nearest = FT_PIX_ROUND( pos ); 00580 FT_Pos right_nearest = FT_PIX_ROUND( pos + len ); 00581 FT_Pos left_disp = left_nearest - pos; 00582 FT_Pos right_disp = right_nearest - ( pos + len ); 00583 00584 00585 if ( left_disp < 0 ) 00586 left_disp = -left_disp; 00587 if ( right_disp < 0 ) 00588 right_disp = -right_disp; 00589 if ( left_disp <= right_disp ) 00590 pos = left_nearest; 00591 else 00592 pos = right_nearest; 00593 } 00594 else 00595 { 00596 /* this is a ghost stem; we simply round it */ 00597 pos = FT_PIX_ROUND( pos ); 00598 } 00599 } 00600 else 00601 { 00602 len = psh_dimension_quantize_len( dim, len, 0 ); 00603 } 00604 } 00605 00606 /* now that we have a good hinted stem width, try to position */ 00607 /* the stem along a pixel grid integer coordinate */ 00608 hint->cur_pos = pos + psh_hint_snap_stem_side_delta( pos, len ); 00609 hint->cur_len = len; 00610 } 00611 } 00612 00613 if ( do_snapping ) 00614 { 00615 pos = hint->cur_pos; 00616 len = hint->cur_len; 00617 00618 if ( len < 64 ) 00619 len = 64; 00620 else 00621 len = FT_PIX_ROUND( len ); 00622 00623 switch ( align.align ) 00624 { 00625 case PSH_BLUE_ALIGN_TOP: 00626 hint->cur_pos = align.align_top - len; 00627 hint->cur_len = len; 00628 break; 00629 00630 case PSH_BLUE_ALIGN_BOT: 00631 hint->cur_len = len; 00632 break; 00633 00634 case PSH_BLUE_ALIGN_BOT | PSH_BLUE_ALIGN_TOP: 00635 /* don't touch */ 00636 break; 00637 00638 00639 default: 00640 hint->cur_len = len; 00641 if ( len & 64 ) 00642 pos = FT_PIX_FLOOR( pos + ( len >> 1 ) ) + 32; 00643 else 00644 pos = FT_PIX_ROUND( pos + ( len >> 1 ) ); 00645 00646 hint->cur_pos = pos - ( len >> 1 ); 00647 hint->cur_len = len; 00648 } 00649 } 00650 00651 psh_hint_set_fitted( hint ); 00652 00653 #ifdef DEBUG_HINTER 00654 if ( ps_debug_hint_func ) 00655 ps_debug_hint_func( hint, dimension ); 00656 #endif 00657 } 00658 } 00659 00660 00661 #if 0 /* not used for now, experimental */ 00662 00663 /* 00664 * A variant to perform "light" hinting (i.e. FT_RENDER_MODE_LIGHT) 00665 * of stems 00666 */ 00667 static void 00668 psh_hint_align_light( PSH_Hint hint, 00669 PSH_Globals globals, 00670 FT_Int dimension, 00671 PSH_Glyph glyph ) 00672 { 00673 PSH_Dimension dim = &globals->dimension[dimension]; 00674 FT_Fixed scale = dim->scale_mult; 00675 FT_Fixed delta = dim->scale_delta; 00676 00677 00678 if ( !psh_hint_is_fitted( hint ) ) 00679 { 00680 FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; 00681 FT_Pos len = FT_MulFix( hint->org_len, scale ); 00682 00683 FT_Pos fit_len; 00684 00685 PSH_AlignmentRec align; 00686 00687 00688 /* ignore stem alignments when requested through the hint flags */ 00689 if ( ( dimension == 0 && !glyph->do_horz_hints ) || 00690 ( dimension == 1 && !glyph->do_vert_hints ) ) 00691 { 00692 hint->cur_pos = pos; 00693 hint->cur_len = len; 00694 00695 psh_hint_set_fitted( hint ); 00696 return; 00697 } 00698 00699 fit_len = len; 00700 00701 hint->cur_len = fit_len; 00702 00703 /* check blue zones for horizontal stems */ 00704 align.align = PSH_BLUE_ALIGN_NONE; 00705 align.align_bot = align.align_top = 0; 00706 00707 if ( dimension == 1 ) 00708 psh_blues_snap_stem( &globals->blues, 00709 hint->org_pos + hint->org_len, 00710 hint->org_pos, 00711 &align ); 00712 00713 switch ( align.align ) 00714 { 00715 case PSH_BLUE_ALIGN_TOP: 00716 /* the top of the stem is aligned against a blue zone */ 00717 hint->cur_pos = align.align_top - fit_len; 00718 break; 00719 00720 case PSH_BLUE_ALIGN_BOT: 00721 /* the bottom of the stem is aligned against a blue zone */ 00722 hint->cur_pos = align.align_bot; 00723 break; 00724 00725 case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: 00726 /* both edges of the stem are aligned against blue zones */ 00727 hint->cur_pos = align.align_bot; 00728 hint->cur_len = align.align_top - align.align_bot; 00729 break; 00730 00731 default: 00732 { 00733 PSH_Hint parent = hint->parent; 00734 00735 00736 if ( parent ) 00737 { 00738 FT_Pos par_org_center, par_cur_center; 00739 FT_Pos cur_org_center, cur_delta; 00740 00741 00742 /* ensure that parent is already fitted */ 00743 if ( !psh_hint_is_fitted( parent ) ) 00744 psh_hint_align_light( parent, globals, dimension, glyph ); 00745 00746 par_org_center = parent->org_pos + ( parent->org_len / 2 ); 00747 par_cur_center = parent->cur_pos + ( parent->cur_len / 2 ); 00748 cur_org_center = hint->org_pos + ( hint->org_len / 2 ); 00749 00750 cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); 00751 pos = par_cur_center + cur_delta - ( len >> 1 ); 00752 } 00753 00754 /* Stems less than one pixel wide are easy -- we want to 00755 * make them as dark as possible, so they must fall within 00756 * one pixel. If the stem is split between two pixels 00757 * then snap the edge that is nearer to the pixel boundary 00758 * to the pixel boundary. 00759 */ 00760 if ( len <= 64 ) 00761 { 00762 if ( ( pos + len + 63 ) / 64 != pos / 64 + 1 ) 00763 pos += psh_hint_snap_stem_side_delta ( pos, len ); 00764 } 00765 00766 /* Position stems other to minimize the amount of mid-grays. 00767 * There are, in general, two positions that do this, 00768 * illustrated as A) and B) below. 00769 * 00770 * + + + + 00771 * 00772 * A) |--------------------------------| 00773 * B) |--------------------------------| 00774 * C) |--------------------------------| 00775 * 00776 * Position A) (split the excess stem equally) should be better 00777 * for stems of width N + f where f < 0.5. 00778 * 00779 * Position B) (split the deficiency equally) should be better 00780 * for stems of width N + f where f > 0.5. 00781 * 00782 * It turns out though that minimizing the total number of lit 00783 * pixels is also important, so position C), with one edge 00784 * aligned with a pixel boundary is actually preferable 00785 * to A). There are also more possibile positions for C) than 00786 * for A) or B), so it involves less distortion of the overall 00787 * character shape. 00788 */ 00789 else /* len > 64 */ 00790 { 00791 FT_Fixed frac_len = len & 63; 00792 FT_Fixed center = pos + ( len >> 1 ); 00793 FT_Fixed delta_a, delta_b; 00794 00795 00796 if ( ( len / 64 ) & 1 ) 00797 { 00798 delta_a = FT_PIX_FLOOR( center ) + 32 - center; 00799 delta_b = FT_PIX_ROUND( center ) - center; 00800 } 00801 else 00802 { 00803 delta_a = FT_PIX_ROUND( center ) - center; 00804 delta_b = FT_PIX_FLOOR( center ) + 32 - center; 00805 } 00806 00807 /* We choose between B) and C) above based on the amount 00808 * of fractinal stem width; for small amounts, choose 00809 * C) always, for large amounts, B) always, and inbetween, 00810 * pick whichever one involves less stem movement. 00811 */ 00812 if ( frac_len < 32 ) 00813 { 00814 pos += psh_hint_snap_stem_side_delta ( pos, len ); 00815 } 00816 else if ( frac_len < 48 ) 00817 { 00818 FT_Fixed side_delta = psh_hint_snap_stem_side_delta ( pos, 00819 len ); 00820 00821 if ( FT_ABS( side_delta ) < FT_ABS( delta_b ) ) 00822 pos += side_delta; 00823 else 00824 pos += delta_b; 00825 } 00826 else 00827 { 00828 pos += delta_b; 00829 } 00830 } 00831 00832 hint->cur_pos = pos; 00833 } 00834 } /* switch */ 00835 00836 psh_hint_set_fitted( hint ); 00837 00838 #ifdef DEBUG_HINTER 00839 if ( ps_debug_hint_func ) 00840 ps_debug_hint_func( hint, dimension ); 00841 #endif 00842 } 00843 } 00844 00845 #endif /* 0 */ 00846 00847 00848 static void 00849 psh_hint_table_align_hints( PSH_Hint_Table table, 00850 PSH_Globals globals, 00851 FT_Int dimension, 00852 PSH_Glyph glyph ) 00853 { 00854 PSH_Hint hint; 00855 FT_UInt count; 00856 00857 #ifdef DEBUG_HINTER 00858 00859 PSH_Dimension dim = &globals->dimension[dimension]; 00860 FT_Fixed scale = dim->scale_mult; 00861 FT_Fixed delta = dim->scale_delta; 00862 00863 00864 if ( ps_debug_no_vert_hints && dimension == 0 ) 00865 { 00866 ps_simple_scale( table, scale, delta, dimension ); 00867 return; 00868 } 00869 00870 if ( ps_debug_no_horz_hints && dimension == 1 ) 00871 { 00872 ps_simple_scale( table, scale, delta, dimension ); 00873 return; 00874 } 00875 00876 #endif /* DEBUG_HINTER*/ 00877 00878 hint = table->hints; 00879 count = table->max_hints; 00880 00881 for ( ; count > 0; count--, hint++ ) 00882 psh_hint_align( hint, globals, dimension, glyph ); 00883 } 00884 00885 00886 /*************************************************************************/ 00887 /*************************************************************************/ 00888 /***** *****/ 00889 /***** POINTS INTERPOLATION ROUTINES *****/ 00890 /***** *****/ 00891 /*************************************************************************/ 00892 /*************************************************************************/ 00893 00894 #define PSH_ZONE_MIN -3200000L 00895 #define PSH_ZONE_MAX +3200000L 00896 00897 #define xxDEBUG_ZONES 00898 00899 00900 #ifdef DEBUG_ZONES 00901 00902 #include FT_CONFIG_STANDARD_LIBRARY_H 00903 00904 static void 00905 psh_print_zone( PSH_Zone zone ) 00906 { 00907 printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", 00908 zone->scale / 65536.0, 00909 zone->delta / 64.0, 00910 zone->min, 00911 zone->max ); 00912 } 00913 00914 #else 00915 00916 #define psh_print_zone( x ) do { } while ( 0 ) 00917 00918 #endif /* DEBUG_ZONES */ 00919 00920 00921 /*************************************************************************/ 00922 /*************************************************************************/ 00923 /***** *****/ 00924 /***** HINTER GLYPH MANAGEMENT *****/ 00925 /***** *****/ 00926 /*************************************************************************/ 00927 /*************************************************************************/ 00928 00929 #if 1 00930 00931 #define psh_corner_is_flat ft_corner_is_flat 00932 #define psh_corner_orientation ft_corner_orientation 00933 00934 #else 00935 00936 FT_LOCAL_DEF( FT_Int ) 00937 psh_corner_is_flat( FT_Pos x_in, 00938 FT_Pos y_in, 00939 FT_Pos x_out, 00940 FT_Pos y_out ) 00941 { 00942 FT_Pos ax = x_in; 00943 FT_Pos ay = y_in; 00944 00945 FT_Pos d_in, d_out, d_corner; 00946 00947 00948 if ( ax < 0 ) 00949 ax = -ax; 00950 if ( ay < 0 ) 00951 ay = -ay; 00952 d_in = ax + ay; 00953 00954 ax = x_out; 00955 if ( ax < 0 ) 00956 ax = -ax; 00957 ay = y_out; 00958 if ( ay < 0 ) 00959 ay = -ay; 00960 d_out = ax + ay; 00961 00962 ax = x_out + x_in; 00963 if ( ax < 0 ) 00964 ax = -ax; 00965 ay = y_out + y_in; 00966 if ( ay < 0 ) 00967 ay = -ay; 00968 d_corner = ax + ay; 00969 00970 return ( d_in + d_out - d_corner ) < ( d_corner >> 4 ); 00971 } 00972 00973 static FT_Int 00974 psh_corner_orientation( FT_Pos in_x, 00975 FT_Pos in_y, 00976 FT_Pos out_x, 00977 FT_Pos out_y ) 00978 { 00979 FT_Int result; 00980 00981 00982 /* deal with the trivial cases quickly */ 00983 if ( in_y == 0 ) 00984 { 00985 if ( in_x >= 0 ) 00986 result = out_y; 00987 else 00988 result = -out_y; 00989 } 00990 else if ( in_x == 0 ) 00991 { 00992 if ( in_y >= 0 ) 00993 result = -out_x; 00994 else 00995 result = out_x; 00996 } 00997 else if ( out_y == 0 ) 00998 { 00999 if ( out_x >= 0 ) 01000 result = in_y; 01001 else 01002 result = -in_y; 01003 } 01004 else if ( out_x == 0 ) 01005 { 01006 if ( out_y >= 0 ) 01007 result = -in_x; 01008 else 01009 result = in_x; 01010 } 01011 else /* general case */ 01012 { 01013 long long delta = (long long)in_x * out_y - (long long)in_y * out_x; 01014 01015 if ( delta == 0 ) 01016 result = 0; 01017 else 01018 result = 1 - 2 * ( delta < 0 ); 01019 } 01020 01021 return result; 01022 } 01023 01024 #endif /* !1 */ 01025 01026 01027 #ifdef COMPUTE_INFLEXS 01028 01029 /* compute all inflex points in a given glyph */ 01030 static void 01031 psh_glyph_compute_inflections( PSH_Glyph glyph ) 01032 { 01033 FT_UInt n; 01034 01035 01036 for ( n = 0; n < glyph->num_contours; n++ ) 01037 { 01038 PSH_Point first, start, end, before, after; 01039 FT_Pos in_x, in_y, out_x, out_y; 01040 FT_Int orient_prev, orient_cur; 01041 FT_Int finished = 0; 01042 01043 01044 /* we need at least 4 points to create an inflection point */ 01045 if ( glyph->contours[n].count < 4 ) 01046 continue; 01047 01048 /* compute first segment in contour */ 01049 first = glyph->contours[n].start; 01050 01051 start = end = first; 01052 do 01053 { 01054 end = end->next; 01055 if ( end == first ) 01056 goto Skip; 01057 01058 in_x = end->org_u - start->org_u; 01059 in_y = end->org_v - start->org_v; 01060 01061 } while ( in_x == 0 && in_y == 0 ); 01062 01063 /* extend the segment start whenever possible */ 01064 before = start; 01065 do 01066 { 01067 do 01068 { 01069 start = before; 01070 before = before->prev; 01071 if ( before == first ) 01072 goto Skip; 01073 01074 out_x = start->org_u - before->org_u; 01075 out_y = start->org_v - before->org_v; 01076 01077 } while ( out_x == 0 && out_y == 0 ); 01078 01079 orient_prev = psh_corner_orientation( in_x, in_y, out_x, out_y ); 01080 01081 } while ( orient_prev == 0 ); 01082 01083 first = start; 01084 in_x = out_x; 01085 in_y = out_y; 01086 01087 /* now, process all segments in the contour */ 01088 do 01089 { 01090 /* first, extend current segment's end whenever possible */ 01091 after = end; 01092 do 01093 { 01094 do 01095 { 01096 end = after; 01097 after = after->next; 01098 if ( after == first ) 01099 finished = 1; 01100 01101 out_x = after->org_u - end->org_u; 01102 out_y = after->org_v - end->org_v; 01103 01104 } while ( out_x == 0 && out_y == 0 ); 01105 01106 orient_cur = psh_corner_orientation( in_x, in_y, out_x, out_y ); 01107 01108 } while ( orient_cur == 0 ); 01109 01110 if ( ( orient_cur ^ orient_prev ) < 0 ) 01111 { 01112 do 01113 { 01114 psh_point_set_inflex( start ); 01115 start = start->next; 01116 } 01117 while ( start != end ); 01118 01119 psh_point_set_inflex( start ); 01120 } 01121 01122 start = end; 01123 end = after; 01124 orient_prev = orient_cur; 01125 in_x = out_x; 01126 in_y = out_y; 01127 01128 } while ( !finished ); 01129 01130 Skip: 01131 ; 01132 } 01133 } 01134 01135 #endif /* COMPUTE_INFLEXS */ 01136 01137 01138 static void 01139 psh_glyph_done( PSH_Glyph glyph ) 01140 { 01141 FT_Memory memory = glyph->memory; 01142 01143 01144 psh_hint_table_done( &glyph->hint_tables[1], memory ); 01145 psh_hint_table_done( &glyph->hint_tables[0], memory ); 01146 01147 FT_FREE( glyph->points ); 01148 FT_FREE( glyph->contours ); 01149 01150 glyph->num_points = 0; 01151 glyph->num_contours = 0; 01152 01153 glyph->memory = 0; 01154 } 01155 01156 01157 static int 01158 psh_compute_dir( FT_Pos dx, 01159 FT_Pos dy ) 01160 { 01161 FT_Pos ax, ay; 01162 int result = PSH_DIR_NONE; 01163 01164 01165 ax = ( dx >= 0 ) ? dx : -dx; 01166 ay = ( dy >= 0 ) ? dy : -dy; 01167 01168 if ( ay * 12 < ax ) 01169 { 01170 /* |dy| <<< |dx| means a near-horizontal segment */ 01171 result = ( dx >= 0 ) ? PSH_DIR_RIGHT : PSH_DIR_LEFT; 01172 } 01173 else if ( ax * 12 < ay ) 01174 { 01175 /* |dx| <<< |dy| means a near-vertical segment */ 01176 result = ( dy >= 0 ) ? PSH_DIR_UP : PSH_DIR_DOWN; 01177 } 01178 01179 return result; 01180 } 01181 01182 01183 /* load outline point coordinates into hinter glyph */ 01184 static void 01185 psh_glyph_load_points( PSH_Glyph glyph, 01186 FT_Int dimension ) 01187 { 01188 FT_Vector* vec = glyph->outline->points; 01189 PSH_Point point = glyph->points; 01190 FT_UInt count = glyph->num_points; 01191 01192 01193 for ( ; count > 0; count--, point++, vec++ ) 01194 { 01195 point->flags2 = 0; 01196 point->hint = NULL; 01197 if ( dimension == 0 ) 01198 { 01199 point->org_u = vec->x; 01200 point->org_v = vec->y; 01201 } 01202 else 01203 { 01204 point->org_u = vec->y; 01205 point->org_v = vec->x; 01206 } 01207 01208 #ifdef DEBUG_HINTER 01209 point->org_x = vec->x; 01210 point->org_y = vec->y; 01211 #endif 01212 01213 } 01214 } 01215 01216 01217 /* save hinted point coordinates back to outline */ 01218 static void 01219 psh_glyph_save_points( PSH_Glyph glyph, 01220 FT_Int dimension ) 01221 { 01222 FT_UInt n; 01223 PSH_Point point = glyph->points; 01224 FT_Vector* vec = glyph->outline->points; 01225 char* tags = glyph->outline->tags; 01226 01227 01228 for ( n = 0; n < glyph->num_points; n++ ) 01229 { 01230 if ( dimension == 0 ) 01231 vec[n].x = point->cur_u; 01232 else 01233 vec[n].y = point->cur_u; 01234 01235 if ( psh_point_is_strong( point ) ) 01236 tags[n] |= (char)( ( dimension == 0 ) ? 32 : 64 ); 01237 01238 #ifdef DEBUG_HINTER 01239 01240 if ( dimension == 0 ) 01241 { 01242 point->cur_x = point->cur_u; 01243 point->flags_x = point->flags2 | point->flags; 01244 } 01245 else 01246 { 01247 point->cur_y = point->cur_u; 01248 point->flags_y = point->flags2 | point->flags; 01249 } 01250 01251 #endif 01252 01253 point++; 01254 } 01255 } 01256 01257 01258 static FT_Error 01259 psh_glyph_init( PSH_Glyph glyph, 01260 FT_Outline* outline, 01261 PS_Hints ps_hints, 01262 PSH_Globals globals ) 01263 { 01264 FT_Error error; 01265 FT_Memory memory; 01266 01267 01268 /* clear all fields */ 01269 FT_MEM_ZERO( glyph, sizeof ( *glyph ) ); 01270 01271 memory = glyph->memory = globals->memory; 01272 01273 /* allocate and setup points + contours arrays */ 01274 if ( FT_NEW_ARRAY( glyph->points, outline->n_points ) || 01275 FT_NEW_ARRAY( glyph->contours, outline->n_contours ) ) 01276 goto Exit; 01277 01278 glyph->num_points = outline->n_points; 01279 glyph->num_contours = outline->n_contours; 01280 01281 { 01282 FT_UInt first = 0, next, n; 01283 PSH_Point points = glyph->points; 01284 PSH_Contour contour = glyph->contours; 01285 01286 01287 for ( n = 0; n < glyph->num_contours; n++ ) 01288 { 01289 FT_Int count; 01290 PSH_Point point; 01291 01292 01293 next = outline->contours[n] + 1; 01294 count = next - first; 01295 01296 contour->start = points + first; 01297 contour->count = (FT_UInt)count; 01298 01299 if ( count > 0 ) 01300 { 01301 point = points + first; 01302 01303 point->prev = points + next - 1; 01304 point->contour = contour; 01305 01306 for ( ; count > 1; count-- ) 01307 { 01308 point[0].next = point + 1; 01309 point[1].prev = point; 01310 point++; 01311 point->contour = contour; 01312 } 01313 point->next = points + first; 01314 } 01315 01316 contour++; 01317 first = next; 01318 } 01319 } 01320 01321 { 01322 PSH_Point points = glyph->points; 01323 PSH_Point point = points; 01324 FT_Vector* vec = outline->points; 01325 FT_UInt n; 01326 01327 01328 for ( n = 0; n < glyph->num_points; n++, point++ ) 01329 { 01330 FT_Int n_prev = (FT_Int)( point->prev - points ); 01331 FT_Int n_next = (FT_Int)( point->next - points ); 01332 FT_Pos dxi, dyi, dxo, dyo; 01333 01334 01335 if ( !( outline->tags[n] & FT_CURVE_TAG_ON ) ) 01336 point->flags = PSH_POINT_OFF; 01337 01338 dxi = vec[n].x - vec[n_prev].x; 01339 dyi = vec[n].y - vec[n_prev].y; 01340 01341 point->dir_in = (FT_Char)psh_compute_dir( dxi, dyi ); 01342 01343 dxo = vec[n_next].x - vec[n].x; 01344 dyo = vec[n_next].y - vec[n].y; 01345 01346 point->dir_out = (FT_Char)psh_compute_dir( dxo, dyo ); 01347 01348 /* detect smooth points */ 01349 if ( point->flags & PSH_POINT_OFF ) 01350 point->flags |= PSH_POINT_SMOOTH; 01351 01352 else if ( point->dir_in == point->dir_out ) 01353 { 01354 if ( point->dir_out != PSH_DIR_NONE || 01355 psh_corner_is_flat( dxi, dyi, dxo, dyo ) ) 01356 point->flags |= PSH_POINT_SMOOTH; 01357 } 01358 } 01359 } 01360 01361 glyph->outline = outline; 01362 glyph->globals = globals; 01363 01364 #ifdef COMPUTE_INFLEXS 01365 psh_glyph_load_points( glyph, 0 ); 01366 psh_glyph_compute_inflections( glyph ); 01367 #endif /* COMPUTE_INFLEXS */ 01368 01369 /* now deal with hints tables */ 01370 error = psh_hint_table_init( &glyph->hint_tables [0], 01371 &ps_hints->dimension[0].hints, 01372 &ps_hints->dimension[0].masks, 01373 &ps_hints->dimension[0].counters, 01374 memory ); 01375 if ( error ) 01376 goto Exit; 01377 01378 error = psh_hint_table_init( &glyph->hint_tables [1], 01379 &ps_hints->dimension[1].hints, 01380 &ps_hints->dimension[1].masks, 01381 &ps_hints->dimension[1].counters, 01382 memory ); 01383 if ( error ) 01384 goto Exit; 01385 01386 Exit: 01387 return error; 01388 } 01389 01390 01391 /* compute all extrema in a glyph for a given dimension */ 01392 static void 01393 psh_glyph_compute_extrema( PSH_Glyph glyph ) 01394 { 01395 FT_UInt n; 01396 01397 01398 /* first of all, compute all local extrema */ 01399 for ( n = 0; n < glyph->num_contours; n++ ) 01400 { 01401 PSH_Point first = glyph->contours[n].start; 01402 PSH_Point point, before, after; 01403 01404 01405 if ( glyph->contours[n].count == 0 ) 01406 continue; 01407 01408 point = first; 01409 before = point; 01410 after = point; 01411 01412 do 01413 { 01414 before = before->prev; 01415 if ( before == first ) 01416 goto Skip; 01417 01418 } while ( before->org_u == point->org_u ); 01419 01420 first = point = before->next; 01421 01422 for (;;) 01423 { 01424 after = point; 01425 do 01426 { 01427 after = after->next; 01428 if ( after == first ) 01429 goto Next; 01430 01431 } while ( after->org_u == point->org_u ); 01432 01433 if ( before->org_u < point->org_u ) 01434 { 01435 if ( after->org_u < point->org_u ) 01436 { 01437 /* local maximum */ 01438 goto Extremum; 01439 } 01440 } 01441 else /* before->org_u > point->org_u */ 01442 { 01443 if ( after->org_u > point->org_u ) 01444 { 01445 /* local minimum */ 01446 Extremum: 01447 do 01448 { 01449 psh_point_set_extremum( point ); 01450 point = point->next; 01451 01452 } while ( point != after ); 01453 } 01454 } 01455 01456 before = after->prev; 01457 point = after; 01458 01459 } /* for */ 01460 01461 Next: 01462 ; 01463 } 01464 01465 /* for each extremum, determine its direction along the */ 01466 /* orthogonal axis */ 01467 for ( n = 0; n < glyph->num_points; n++ ) 01468 { 01469 PSH_Point point, before, after; 01470 01471 01472 point = &glyph->points[n]; 01473 before = point; 01474 after = point; 01475 01476 if ( psh_point_is_extremum( point ) ) 01477 { 01478 do 01479 { 01480 before = before->prev; 01481 if ( before == point ) 01482 goto Skip; 01483 01484 } while ( before->org_v == point->org_v ); 01485 01486 do 01487 { 01488 after = after->next; 01489 if ( after == point ) 01490 goto Skip; 01491 01492 } while ( after->org_v == point->org_v ); 01493 } 01494 01495 if ( before->org_v < point->org_v && 01496 after->org_v > point->org_v ) 01497 { 01498 psh_point_set_positive( point ); 01499 } 01500 else if ( before->org_v > point->org_v && 01501 after->org_v < point->org_v ) 01502 { 01503 psh_point_set_negative( point ); 01504 } 01505 01506 Skip: 01507 ; 01508 } 01509 } 01510 01511 01512 /* major_dir is the direction for points on the bottom/left of the stem; */ 01513 /* Points on the top/right of the stem will have a direction of */ 01514 /* -major_dir. */ 01515 01516 static void 01517 psh_hint_table_find_strong_points( PSH_Hint_Table table, 01518 PSH_Point point, 01519 FT_UInt count, 01520 FT_Int threshold, 01521 FT_Int major_dir ) 01522 { 01523 PSH_Hint* sort = table->sort; 01524 FT_UInt num_hints = table->num_hints; 01525 01526 01527 for ( ; count > 0; count--, point++ ) 01528 { 01529 FT_Int point_dir = 0; 01530 FT_Pos org_u = point->org_u; 01531 01532 01533 if ( psh_point_is_strong( point ) ) 01534 continue; 01535 01536 if ( PSH_DIR_COMPARE( point->dir_in, major_dir ) ) 01537 point_dir = point->dir_in; 01538 01539 else if ( PSH_DIR_COMPARE( point->dir_out, major_dir ) ) 01540 point_dir = point->dir_out; 01541 01542 if ( point_dir ) 01543 { 01544 if ( point_dir == major_dir ) 01545 { 01546 FT_UInt nn; 01547 01548 01549 for ( nn = 0; nn < num_hints; nn++ ) 01550 { 01551 PSH_Hint hint = sort[nn]; 01552 FT_Pos d = org_u - hint->org_pos; 01553 01554 01555 if ( d < threshold && -d < threshold ) 01556 { 01557 psh_point_set_strong( point ); 01558 point->flags2 |= PSH_POINT_EDGE_MIN; 01559 point->hint = hint; 01560 break; 01561 } 01562 } 01563 } 01564 else if ( point_dir == -major_dir ) 01565 { 01566 FT_UInt nn; 01567 01568 01569 for ( nn = 0; nn < num_hints; nn++ ) 01570 { 01571 PSH_Hint hint = sort[nn]; 01572 FT_Pos d = org_u - hint->org_pos - hint->org_len; 01573 01574 01575 if ( d < threshold && -d < threshold ) 01576 { 01577 psh_point_set_strong( point ); 01578 point->flags2 |= PSH_POINT_EDGE_MAX; 01579 point->hint = hint; 01580 break; 01581 } 01582 } 01583 } 01584 } 01585 01586 #if 1 01587 else if ( psh_point_is_extremum( point ) ) 01588 { 01589 /* treat extrema as special cases for stem edge alignment */ 01590 FT_UInt nn, min_flag, max_flag; 01591 01592 01593 if ( major_dir == PSH_DIR_HORIZONTAL ) 01594 { 01595 min_flag = PSH_POINT_POSITIVE; 01596 max_flag = PSH_POINT_NEGATIVE; 01597 } 01598 else 01599 { 01600 min_flag = PSH_POINT_NEGATIVE; 01601 max_flag = PSH_POINT_POSITIVE; 01602 } 01603 01604 if ( point->flags2 & min_flag ) 01605 { 01606 for ( nn = 0; nn < num_hints; nn++ ) 01607 { 01608 PSH_Hint hint = sort[nn]; 01609 FT_Pos d = org_u - hint->org_pos; 01610 01611 01612 if ( d < threshold && -d < threshold ) 01613 { 01614 point->flags2 |= PSH_POINT_EDGE_MIN; 01615 point->hint = hint; 01616 psh_point_set_strong( point ); 01617 break; 01618 } 01619 } 01620 } 01621 else if ( point->flags2 & max_flag ) 01622 { 01623 for ( nn = 0; nn < num_hints; nn++ ) 01624 { 01625 PSH_Hint hint = sort[nn]; 01626 FT_Pos d = org_u - hint->org_pos - hint->org_len; 01627 01628 01629 if ( d < threshold && -d < threshold ) 01630 { 01631 point->flags2 |= PSH_POINT_EDGE_MAX; 01632 point->hint = hint; 01633 psh_point_set_strong( point ); 01634 break; 01635 } 01636 } 01637 } 01638 01639 if ( point->hint == NULL ) 01640 { 01641 for ( nn = 0; nn < num_hints; nn++ ) 01642 { 01643 PSH_Hint hint = sort[nn]; 01644 01645 01646 if ( org_u >= hint->org_pos && 01647 org_u <= hint->org_pos + hint->org_len ) 01648 { 01649 point->hint = hint; 01650 break; 01651 } 01652 } 01653 } 01654 } 01655 01656 #endif /* 1 */ 01657 } 01658 } 01659 01660 01661 /* the accepted shift for strong points in fractional pixels */ 01662 #define PSH_STRONG_THRESHOLD 32 01663 01664 /* the maximum shift value in font units */ 01665 #define PSH_STRONG_THRESHOLD_MAXIMUM 30 01666 01667 01668 /* find strong points in a glyph */ 01669 static void 01670 psh_glyph_find_strong_points( PSH_Glyph glyph, 01671 FT_Int dimension ) 01672 { 01673 /* a point is `strong' if it is located on a stem edge and */ 01674 /* has an `in' or `out' tangent parallel to the hint's direction */ 01675 01676 PSH_Hint_Table table = &glyph->hint_tables[dimension]; 01677 PS_Mask mask = table->hint_masks->masks; 01678 FT_UInt num_masks = table->hint_masks->num_masks; 01679 FT_UInt first = 0; 01680 FT_Int major_dir = dimension == 0 ? PSH_DIR_VERTICAL 01681 : PSH_DIR_HORIZONTAL; 01682 PSH_Dimension dim = &glyph->globals->dimension[dimension]; 01683 FT_Fixed scale = dim->scale_mult; 01684 FT_Int threshold; 01685 01686 01687 threshold = (FT_Int)FT_DivFix( PSH_STRONG_THRESHOLD, scale ); 01688 if ( threshold > PSH_STRONG_THRESHOLD_MAXIMUM ) 01689 threshold = PSH_STRONG_THRESHOLD_MAXIMUM; 01690 01691 /* process secondary hints to `selected' points */ 01692 if ( num_masks > 1 && glyph->num_points > 0 ) 01693 { 01694 /* the `endchar' op can reduce the number of points */ 01695 first = mask->end_point > glyph->num_points 01696 ? glyph->num_points 01697 : mask->end_point; 01698 mask++; 01699 for ( ; num_masks > 1; num_masks--, mask++ ) 01700 { 01701 FT_UInt next; 01702 FT_Int count; 01703 01704 01705 next = mask->end_point > glyph->num_points 01706 ? glyph->num_points 01707 : mask->end_point; 01708 count = next - first; 01709 if ( count > 0 ) 01710 { 01711 PSH_Point point = glyph->points + first; 01712 01713 01714 psh_hint_table_activate_mask( table, mask ); 01715 01716 psh_hint_table_find_strong_points( table, point, count, 01717 threshold, major_dir ); 01718 } 01719 first = next; 01720 } 01721 } 01722 01723 /* process primary hints for all points */ 01724 if ( num_masks == 1 ) 01725 { 01726 FT_UInt count = glyph->num_points; 01727 PSH_Point point = glyph->points; 01728 01729 01730 psh_hint_table_activate_mask( table, table->hint_masks->masks ); 01731 01732 psh_hint_table_find_strong_points( table, point, count, 01733 threshold, major_dir ); 01734 } 01735 01736 /* now, certain points may have been attached to a hint and */ 01737 /* not marked as strong; update their flags then */ 01738 { 01739 FT_UInt count = glyph->num_points; 01740 PSH_Point point = glyph->points; 01741 01742 01743 for ( ; count > 0; count--, point++ ) 01744 if ( point->hint && !psh_point_is_strong( point ) ) 01745 psh_point_set_strong( point ); 01746 } 01747 } 01748 01749 01750 /* find points in a glyph which are in a blue zone and have `in' or */ 01751 /* `out' tangents parallel to the horizontal axis */ 01752 static void 01753 psh_glyph_find_blue_points( PSH_Blues blues, 01754 PSH_Glyph glyph ) 01755 { 01756 PSH_Blue_Table table; 01757 PSH_Blue_Zone zone; 01758 FT_UInt glyph_count = glyph->num_points; 01759 FT_UInt blue_count; 01760 PSH_Point point = glyph->points; 01761 01762 01763 for ( ; glyph_count > 0; glyph_count--, point++ ) 01764 { 01765 FT_Pos y; 01766 01767 01768 /* check tangents */ 01769 if ( !PSH_DIR_COMPARE( point->dir_in, PSH_DIR_HORIZONTAL ) && 01770 !PSH_DIR_COMPARE( point->dir_out, PSH_DIR_HORIZONTAL ) ) 01771 continue; 01772 01773 /* skip strong points */ 01774 if ( psh_point_is_strong( point ) ) 01775 continue; 01776 01777 y = point->org_u; 01778 01779 /* look up top zones */ 01780 table = &blues->normal_top; 01781 blue_count = table->count; 01782 zone = table->zones; 01783 01784 for ( ; blue_count > 0; blue_count--, zone++ ) 01785 { 01786 FT_Pos delta = y - zone->org_bottom; 01787 01788 01789 if ( delta < -blues->blue_fuzz ) 01790 break; 01791 01792 if ( y <= zone->org_top + blues->blue_fuzz ) 01793 if ( blues->no_overshoots || delta <= blues->blue_threshold ) 01794 { 01795 point->cur_u = zone->cur_bottom; 01796 psh_point_set_strong( point ); 01797 psh_point_set_fitted( point ); 01798 } 01799 } 01800 01801 /* look up bottom zones */ 01802 table = &blues->normal_bottom; 01803 blue_count = table->count; 01804 zone = table->zones + blue_count - 1; 01805 01806 for ( ; blue_count > 0; blue_count--, zone-- ) 01807 { 01808 FT_Pos delta = zone->org_top - y; 01809 01810 01811 if ( delta < -blues->blue_fuzz ) 01812 break; 01813 01814 if ( y >= zone->org_bottom - blues->blue_fuzz ) 01815 if ( blues->no_overshoots || delta < blues->blue_threshold ) 01816 { 01817 point->cur_u = zone->cur_top; 01818 psh_point_set_strong( point ); 01819 psh_point_set_fitted( point ); 01820 } 01821 } 01822 } 01823 } 01824 01825 01826 /* interpolate strong points with the help of hinted coordinates */ 01827 static void 01828 psh_glyph_interpolate_strong_points( PSH_Glyph glyph, 01829 FT_Int dimension ) 01830 { 01831 PSH_Dimension dim = &glyph->globals->dimension[dimension]; 01832 FT_Fixed scale = dim->scale_mult; 01833 01834 FT_UInt count = glyph->num_points; 01835 PSH_Point point = glyph->points; 01836 01837 01838 for ( ; count > 0; count--, point++ ) 01839 { 01840 PSH_Hint hint = point->hint; 01841 01842 01843 if ( hint ) 01844 { 01845 FT_Pos delta; 01846 01847 01848 if ( psh_point_is_edge_min( point ) ) 01849 point->cur_u = hint->cur_pos; 01850 01851 else if ( psh_point_is_edge_max( point ) ) 01852 point->cur_u = hint->cur_pos + hint->cur_len; 01853 01854 else 01855 { 01856 delta = point->org_u - hint->org_pos; 01857 01858 if ( delta <= 0 ) 01859 point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); 01860 01861 else if ( delta >= hint->org_len ) 01862 point->cur_u = hint->cur_pos + hint->cur_len + 01863 FT_MulFix( delta - hint->org_len, scale ); 01864 01865 else /* hint->org_len > 0 */ 01866 point->cur_u = hint->cur_pos + 01867 FT_MulDiv( delta, hint->cur_len, 01868 hint->org_len ); 01869 } 01870 psh_point_set_fitted( point ); 01871 } 01872 } 01873 } 01874 01875 01876 #define PSH_MAX_STRONG_INTERNAL 16 01877 01878 static void 01879 psh_glyph_interpolate_normal_points( PSH_Glyph glyph, 01880 FT_Int dimension ) 01881 { 01882 01883 #if 1 01884 /* first technique: a point is strong if it is a local extremum */ 01885 01886 PSH_Dimension dim = &glyph->globals->dimension[dimension]; 01887 FT_Fixed scale = dim->scale_mult; 01888 FT_Memory memory = glyph->memory; 01889 01890 PSH_Point* strongs = NULL; 01891 PSH_Point strongs_0[PSH_MAX_STRONG_INTERNAL]; 01892 FT_UInt num_strongs = 0; 01893 01894 PSH_Point points = glyph->points; 01895 PSH_Point points_end = points + glyph->num_points; 01896 PSH_Point point; 01897 01898 01899 /* first count the number of strong points */ 01900 for ( point = points; point < points_end; point++ ) 01901 { 01902 if ( psh_point_is_strong( point ) ) 01903 num_strongs++; 01904 } 01905 01906 if ( num_strongs == 0 ) /* nothing to do here */ 01907 return; 01908 01909 /* allocate an array to store a list of points, */ 01910 /* stored in increasing org_u order */ 01911 if ( num_strongs <= PSH_MAX_STRONG_INTERNAL ) 01912 strongs = strongs_0; 01913 else 01914 { 01915 FT_Error error; 01916 01917 01918 if ( FT_NEW_ARRAY( strongs, num_strongs ) ) 01919 return; 01920 } 01921 01922 num_strongs = 0; 01923 for ( point = points; point < points_end; point++ ) 01924 { 01925 PSH_Point* insert; 01926 01927 01928 if ( !psh_point_is_strong( point ) ) 01929 continue; 01930 01931 for ( insert = strongs + num_strongs; insert > strongs; insert-- ) 01932 { 01933 if ( insert[-1]->org_u <= point->org_u ) 01934 break; 01935 01936 insert[0] = insert[-1]; 01937 } 01938 insert[0] = point; 01939 num_strongs++; 01940 } 01941 01942 /* now try to interpolate all normal points */ 01943 for ( point = points; point < points_end; point++ ) 01944 { 01945 if ( psh_point_is_strong( point ) ) 01946 continue; 01947 01948 /* sometimes, some local extrema are smooth points */ 01949 if ( psh_point_is_smooth( point ) ) 01950 { 01951 if ( point->dir_in == PSH_DIR_NONE || 01952 point->dir_in != point->dir_out ) 01953 continue; 01954 01955 if ( !psh_point_is_extremum( point ) && 01956 !psh_point_is_inflex( point ) ) 01957 continue; 01958 01959 point->flags &= ~PSH_POINT_SMOOTH; 01960 } 01961 01962 /* find best enclosing point coordinates then interpolate */ 01963 { 01964 PSH_Point before, after; 01965 FT_UInt nn; 01966 01967 01968 for ( nn = 0; nn < num_strongs; nn++ ) 01969 if ( strongs[nn]->org_u > point->org_u ) 01970 break; 01971 01972 if ( nn == 0 ) /* point before the first strong point */ 01973 { 01974 after = strongs[0]; 01975 01976 point->cur_u = after->cur_u + 01977 FT_MulFix( point->org_u - after->org_u, 01978 scale ); 01979 } 01980 else 01981 { 01982 before = strongs[nn - 1]; 01983 01984 for ( nn = num_strongs; nn > 0; nn-- ) 01985 if ( strongs[nn - 1]->org_u < point->org_u ) 01986 break; 01987 01988 if ( nn == num_strongs ) /* point is after last strong point */ 01989 { 01990 before = strongs[nn - 1]; 01991 01992 point->cur_u = before->cur_u + 01993 FT_MulFix( point->org_u - before->org_u, 01994 scale ); 01995 } 01996 else 01997 { 01998 FT_Pos u; 01999 02000 02001 after = strongs[nn]; 02002 02003 /* now interpolate point between before and after */ 02004 u = point->org_u; 02005 02006 if ( u == before->org_u ) 02007 point->cur_u = before->cur_u; 02008 02009 else if ( u == after->org_u ) 02010 point->cur_u = after->cur_u; 02011 02012 else 02013 point->cur_u = before->cur_u + 02014 FT_MulDiv( u - before->org_u, 02015 after->cur_u - before->cur_u, 02016 after->org_u - before->org_u ); 02017 } 02018 } 02019 psh_point_set_fitted( point ); 02020 } 02021 } 02022 02023 if ( strongs != strongs_0 ) 02024 FT_FREE( strongs ); 02025 02026 #endif /* 1 */ 02027 02028 } 02029 02030 02031 /* interpolate other points */ 02032 static void 02033 psh_glyph_interpolate_other_points( PSH_Glyph glyph, 02034 FT_Int dimension ) 02035 { 02036 PSH_Dimension dim = &glyph->globals->dimension[dimension]; 02037 FT_Fixed scale = dim->scale_mult; 02038 FT_Fixed delta = dim->scale_delta; 02039 PSH_Contour contour = glyph->contours; 02040 FT_UInt num_contours = glyph->num_contours; 02041 02042 02043 for ( ; num_contours > 0; num_contours--, contour++ ) 02044 { 02045 PSH_Point start = contour->start; 02046 PSH_Point first, next, point; 02047 FT_UInt fit_count; 02048 02049 02050 /* count the number of strong points in this contour */ 02051 next = start + contour->count; 02052 fit_count = 0; 02053 first = 0; 02054 02055 for ( point = start; point < next; point++ ) 02056 if ( psh_point_is_fitted( point ) ) 02057 { 02058 if ( !first ) 02059 first = point; 02060 02061 fit_count++; 02062 } 02063 02064 /* if there are less than 2 fitted points in the contour, we */ 02065 /* simply scale and eventually translate the contour points */ 02066 if ( fit_count < 2 ) 02067 { 02068 if ( fit_count == 1 ) 02069 delta = first->cur_u - FT_MulFix( first->org_u, scale ); 02070 02071 for ( point = start; point < next; point++ ) 02072 if ( point != first ) 02073 point->cur_u = FT_MulFix( point->org_u, scale ) + delta; 02074 02075 goto Next_Contour; 02076 } 02077 02078 /* there are more than 2 strong points in this contour; we */ 02079 /* need to interpolate weak points between them */ 02080 start = first; 02081 do 02082 { 02083 point = first; 02084 02085 /* skip consecutive fitted points */ 02086 for (;;) 02087 { 02088 next = first->next; 02089 if ( next == start ) 02090 goto Next_Contour; 02091 02092 if ( !psh_point_is_fitted( next ) ) 02093 break; 02094 02095 first = next; 02096 } 02097 02098 /* find next fitted point after unfitted one */ 02099 for (;;) 02100 { 02101 next = next->next; 02102 if ( psh_point_is_fitted( next ) ) 02103 break; 02104 } 02105 02106 /* now interpolate between them */ 02107 { 02108 FT_Pos org_a, org_ab, cur_a, cur_ab; 02109 FT_Pos org_c, org_ac, cur_c; 02110 FT_Fixed scale_ab; 02111 02112 02113 if ( first->org_u <= next->org_u ) 02114 { 02115 org_a = first->org_u; 02116 cur_a = first->cur_u; 02117 org_ab = next->org_u - org_a; 02118 cur_ab = next->cur_u - cur_a; 02119 } 02120 else 02121 { 02122 org_a = next->org_u; 02123 cur_a = next->cur_u; 02124 org_ab = first->org_u - org_a; 02125 cur_ab = first->cur_u - cur_a; 02126 } 02127 02128 scale_ab = 0x10000L; 02129 if ( org_ab > 0 ) 02130 scale_ab = FT_DivFix( cur_ab, org_ab ); 02131 02132 point = first->next; 02133 do 02134 { 02135 org_c = point->org_u; 02136 org_ac = org_c - org_a; 02137 02138 if ( org_ac <= 0 ) 02139 { 02140 /* on the left of the interpolation zone */ 02141 cur_c = cur_a + FT_MulFix( org_ac, scale ); 02142 } 02143 else if ( org_ac >= org_ab ) 02144 { 02145 /* on the right on the interpolation zone */ 02146 cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); 02147 } 02148 else 02149 { 02150 /* within the interpolation zone */ 02151 cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); 02152 } 02153 02154 point->cur_u = cur_c; 02155 02156 point = point->next; 02157 02158 } while ( point != next ); 02159 } 02160 02161 /* keep going until all points in the contours have been processed */ 02162 first = next; 02163 02164 } while ( first != start ); 02165 02166 Next_Contour: 02167 ; 02168 } 02169 } 02170 02171 02172 /*************************************************************************/ 02173 /*************************************************************************/ 02174 /***** *****/ 02175 /***** HIGH-LEVEL INTERFACE *****/ 02176 /***** *****/ 02177 /*************************************************************************/ 02178 /*************************************************************************/ 02179 02180 FT_Error 02181 ps_hints_apply( PS_Hints ps_hints, 02182 FT_Outline* outline, 02183 PSH_Globals globals, 02184 FT_Render_Mode hint_mode ) 02185 { 02186 PSH_GlyphRec glyphrec; 02187 PSH_Glyph glyph = &glyphrec; 02188 FT_Error error; 02189 #ifdef DEBUG_HINTER 02190 FT_Memory memory; 02191 #endif 02192 FT_Int dimension; 02193 02194 02195 /* something to do? */ 02196 if ( outline->n_points == 0 || outline->n_contours == 0 ) 02197 return PSH_Err_Ok; 02198 02199 #ifdef DEBUG_HINTER 02200 02201 memory = globals->memory; 02202 02203 if ( ps_debug_glyph ) 02204 { 02205 psh_glyph_done( ps_debug_glyph ); 02206 FT_FREE( ps_debug_glyph ); 02207 } 02208 02209 if ( FT_NEW( glyph ) ) 02210 return error; 02211 02212 ps_debug_glyph = glyph; 02213 02214 #endif /* DEBUG_HINTER */ 02215 02216 error = psh_glyph_init( glyph, outline, ps_hints, globals ); 02217 if ( error ) 02218 goto Exit; 02219 02220 /* try to optimize the y_scale so that the top of non-capital letters 02221 * is aligned on a pixel boundary whenever possible 02222 */ 02223 { 02224 PSH_Dimension dim_x = &glyph->globals->dimension[0]; 02225 PSH_Dimension dim_y = &glyph->globals->dimension[1]; 02226 02227 FT_Fixed x_scale = dim_x->scale_mult; 02228 FT_Fixed y_scale = dim_y->scale_mult; 02229 02230 FT_Fixed old_x_scale = x_scale; 02231 FT_Fixed old_y_scale = y_scale; 02232 02233 FT_Fixed scaled; 02234 FT_Fixed fitted; 02235 02236 FT_Bool rescale = FALSE; 02237 02238 02239 scaled = FT_MulFix( globals->blues.normal_top.zones->org_ref, y_scale ); 02240 fitted = FT_PIX_ROUND( scaled ); 02241 02242 if ( fitted != 0 && scaled != fitted ) 02243 { 02244 rescale = TRUE; 02245 02246 y_scale = FT_MulDiv( y_scale, fitted, scaled ); 02247 02248 if ( fitted < scaled ) 02249 x_scale -= x_scale / 50; 02250 02251 psh_globals_set_scale( glyph->globals, x_scale, y_scale, 0, 0 ); 02252 } 02253 02254 glyph->do_horz_hints = 1; 02255 glyph->do_vert_hints = 1; 02256 02257 glyph->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || 02258 hint_mode == FT_RENDER_MODE_LCD ); 02259 02260 glyph->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO || 02261 hint_mode == FT_RENDER_MODE_LCD_V ); 02262 02263 glyph->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT ); 02264 02265 for ( dimension = 0; dimension < 2; dimension++ ) 02266 { 02267 /* load outline coordinates into glyph */ 02268 psh_glyph_load_points( glyph, dimension ); 02269 02270 /* compute local extrema */ 02271 psh_glyph_compute_extrema( glyph ); 02272 02273 /* compute aligned stem/hints positions */ 02274 psh_hint_table_align_hints( &glyph->hint_tables[dimension], 02275 glyph->globals, 02276 dimension, 02277 glyph ); 02278 02279 /* find strong points, align them, then interpolate others */ 02280 psh_glyph_find_strong_points( glyph, dimension ); 02281 if ( dimension == 1 ) 02282 psh_glyph_find_blue_points( &globals->blues, glyph ); 02283 psh_glyph_interpolate_strong_points( glyph, dimension ); 02284 psh_glyph_interpolate_normal_points( glyph, dimension ); 02285 psh_glyph_interpolate_other_points( glyph, dimension ); 02286 02287 /* save hinted coordinates back to outline */ 02288 psh_glyph_save_points( glyph, dimension ); 02289 02290 if ( rescale ) 02291 psh_globals_set_scale( glyph->globals, 02292 old_x_scale, old_y_scale, 0, 0 ); 02293 } 02294 } 02295 02296 Exit: 02297 02298 #ifndef DEBUG_HINTER 02299 psh_glyph_done( glyph ); 02300 #endif 02301 02302 return error; 02303 } 02304 02305 02306 /* END */ Generated on Sun May 27 2012 04:34:00 for ReactOS by
1.7.6.1
|