| 1 | /**************************************************************************** | 
|---|
| 2 | * | 
|---|
| 3 | * psblues.c | 
|---|
| 4 | * | 
|---|
| 5 | *   Adobe's code for handling Blue Zones (body). | 
|---|
| 6 | * | 
|---|
| 7 | * Copyright 2009-2014 Adobe Systems Incorporated. | 
|---|
| 8 | * | 
|---|
| 9 | * This software, and all works of authorship, whether in source or | 
|---|
| 10 | * object code form as indicated by the copyright notice(s) included | 
|---|
| 11 | * herein (collectively, the "Work") is made available, and may only be | 
|---|
| 12 | * used, modified, and distributed under the FreeType Project License, | 
|---|
| 13 | * LICENSE.TXT.  Additionally, subject to the terms and conditions of the | 
|---|
| 14 | * FreeType Project License, each contributor to the Work hereby grants | 
|---|
| 15 | * to any individual or legal entity exercising permissions granted by | 
|---|
| 16 | * the FreeType Project License and this section (hereafter, "You" or | 
|---|
| 17 | * "Your") a perpetual, worldwide, non-exclusive, no-charge, | 
|---|
| 18 | * royalty-free, irrevocable (except as stated in this section) patent | 
|---|
| 19 | * license to make, have made, use, offer to sell, sell, import, and | 
|---|
| 20 | * otherwise transfer the Work, where such license applies only to those | 
|---|
| 21 | * patent claims licensable by such contributor that are necessarily | 
|---|
| 22 | * infringed by their contribution(s) alone or by combination of their | 
|---|
| 23 | * contribution(s) with the Work to which such contribution(s) was | 
|---|
| 24 | * submitted.  If You institute patent litigation against any entity | 
|---|
| 25 | * (including a cross-claim or counterclaim in a lawsuit) alleging that | 
|---|
| 26 | * the Work or a contribution incorporated within the Work constitutes | 
|---|
| 27 | * direct or contributory patent infringement, then any patent licenses | 
|---|
| 28 | * granted to You under this License for that Work shall terminate as of | 
|---|
| 29 | * the date such litigation is filed. | 
|---|
| 30 | * | 
|---|
| 31 | * By using, modifying, or distributing the Work you indicate that you | 
|---|
| 32 | * have read and understood the terms and conditions of the | 
|---|
| 33 | * FreeType Project License as well as those provided in this section, | 
|---|
| 34 | * and you accept them fully. | 
|---|
| 35 | * | 
|---|
| 36 | */ | 
|---|
| 37 |  | 
|---|
| 38 |  | 
|---|
| 39 | #include "psft.h" | 
|---|
| 40 | #include FT_INTERNAL_DEBUG_H | 
|---|
| 41 |  | 
|---|
| 42 | #include "psblues.h" | 
|---|
| 43 | #include "pshints.h" | 
|---|
| 44 | #include "psfont.h" | 
|---|
| 45 |  | 
|---|
| 46 |  | 
|---|
| 47 | /************************************************************************** | 
|---|
| 48 | * | 
|---|
| 49 | * The macro FT_COMPONENT is used in trace mode.  It is an implicit | 
|---|
| 50 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log | 
|---|
| 51 | * messages during execution. | 
|---|
| 52 | */ | 
|---|
| 53 | #undef  FT_COMPONENT | 
|---|
| 54 | #define FT_COMPONENT  cf2blues | 
|---|
| 55 |  | 
|---|
| 56 |  | 
|---|
| 57 | /* | 
|---|
| 58 | * For blue values, the FreeType parser produces an array of integers, | 
|---|
| 59 | * while the Adobe CFF engine produces an array of fixed. | 
|---|
| 60 | * Define a macro to convert FreeType to fixed. | 
|---|
| 61 | */ | 
|---|
| 62 | #define cf2_blueToFixed( x )  cf2_intToFixed( x ) | 
|---|
| 63 |  | 
|---|
| 64 |  | 
|---|
| 65 | FT_LOCAL_DEF( void ) | 
|---|
| 66 | cf2_blues_init( CF2_Blues  blues, | 
|---|
| 67 | CF2_Font   font ) | 
|---|
| 68 | { | 
|---|
| 69 | /* pointer to parsed font object */ | 
|---|
| 70 | PS_Decoder*  decoder = font->decoder; | 
|---|
| 71 |  | 
|---|
| 72 | CF2_Fixed  zoneHeight; | 
|---|
| 73 | CF2_Fixed  maxZoneHeight = 0; | 
|---|
| 74 | CF2_Fixed  csUnitsPerPixel; | 
|---|
| 75 |  | 
|---|
| 76 | size_t  numBlueValues; | 
|---|
| 77 | size_t  numOtherBlues; | 
|---|
| 78 | size_t  numFamilyBlues; | 
|---|
| 79 | size_t  numFamilyOtherBlues; | 
|---|
| 80 |  | 
|---|
| 81 | FT_Pos*  blueValues; | 
|---|
| 82 | FT_Pos*  otherBlues; | 
|---|
| 83 | FT_Pos*  familyBlues; | 
|---|
| 84 | FT_Pos*  familyOtherBlues; | 
|---|
| 85 |  | 
|---|
| 86 | size_t     i; | 
|---|
| 87 | CF2_Fixed  emBoxBottom, emBoxTop; | 
|---|
| 88 |  | 
|---|
| 89 | #if 0 | 
|---|
| 90 | CF2_Int  unitsPerEm = font->unitsPerEm; | 
|---|
| 91 |  | 
|---|
| 92 |  | 
|---|
| 93 | if ( unitsPerEm == 0 ) | 
|---|
| 94 | unitsPerEm = 1000; | 
|---|
| 95 | #endif | 
|---|
| 96 |  | 
|---|
| 97 | FT_ZERO( blues ); | 
|---|
| 98 | blues->scale = font->innerTransform.d; | 
|---|
| 99 |  | 
|---|
| 100 | cf2_getBlueMetrics( decoder, | 
|---|
| 101 | &blues->blueScale, | 
|---|
| 102 | &blues->blueShift, | 
|---|
| 103 | &blues->blueFuzz ); | 
|---|
| 104 |  | 
|---|
| 105 | cf2_getBlueValues( decoder, &numBlueValues, &blueValues ); | 
|---|
| 106 | cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues ); | 
|---|
| 107 | cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues ); | 
|---|
| 108 | cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues ); | 
|---|
| 109 |  | 
|---|
| 110 | /* | 
|---|
| 111 | * synthetic em box hint heuristic | 
|---|
| 112 | * | 
|---|
| 113 | * Apply this when ideographic dictionary (LanguageGroup 1) has no | 
|---|
| 114 | * real alignment zones.  Adobe tools generate dummy zones at -250 and | 
|---|
| 115 | * 1100 for a 1000 unit em.  Fonts with ICF-based alignment zones | 
|---|
| 116 | * should not enable the heuristic.  When the heuristic is enabled, | 
|---|
| 117 | * the font's blue zones are ignored. | 
|---|
| 118 | * | 
|---|
| 119 | */ | 
|---|
| 120 |  | 
|---|
| 121 | /* get em box from OS/2 typoAscender/Descender                      */ | 
|---|
| 122 | /* TODO: FreeType does not parse these metrics.  Skip them for now. */ | 
|---|
| 123 | #if 0 | 
|---|
| 124 | FCM_getHorizontalLineMetrics( &e, | 
|---|
| 125 | font->font, | 
|---|
| 126 | &ascender, | 
|---|
| 127 | &descender, | 
|---|
| 128 | &linegap ); | 
|---|
| 129 | if ( ascender - descender == unitsPerEm ) | 
|---|
| 130 | { | 
|---|
| 131 | emBoxBottom = cf2_intToFixed( descender ); | 
|---|
| 132 | emBoxTop    = cf2_intToFixed( ascender ); | 
|---|
| 133 | } | 
|---|
| 134 | else | 
|---|
| 135 | #endif | 
|---|
| 136 | { | 
|---|
| 137 | emBoxBottom = CF2_ICF_Bottom; | 
|---|
| 138 | emBoxTop    = CF2_ICF_Top; | 
|---|
| 139 | } | 
|---|
| 140 |  | 
|---|
| 141 | if ( cf2_getLanguageGroup( decoder ) == 1                   && | 
|---|
| 142 | ( numBlueValues == 0                                 || | 
|---|
| 143 | ( numBlueValues == 4                             && | 
|---|
| 144 | cf2_blueToFixed( blueValues[0] ) < emBoxBottom && | 
|---|
| 145 | cf2_blueToFixed( blueValues[1] ) < emBoxBottom && | 
|---|
| 146 | cf2_blueToFixed( blueValues[2] ) > emBoxTop    && | 
|---|
| 147 | cf2_blueToFixed( blueValues[3] ) > emBoxTop    ) ) ) | 
|---|
| 148 | { | 
|---|
| 149 | /* | 
|---|
| 150 | * Construct hint edges suitable for synthetic ghost hints at top | 
|---|
| 151 | * and bottom of em box.  +-CF2_MIN_COUNTER allows for unhinted | 
|---|
| 152 | * features above or below the last hinted edge.  This also gives a | 
|---|
| 153 | * net 1 pixel boost to the height of ideographic glyphs. | 
|---|
| 154 | * | 
|---|
| 155 | * Note: Adjust synthetic hints outward by epsilon (0x.0001) to | 
|---|
| 156 | *       avoid interference.  E.g., some fonts have real hints at | 
|---|
| 157 | *       880 and -120. | 
|---|
| 158 | */ | 
|---|
| 159 |  | 
|---|
| 160 | blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON; | 
|---|
| 161 | blues->emBoxBottomEdge.dsCoord = cf2_fixedRound( | 
|---|
| 162 | FT_MulFix( | 
|---|
| 163 | blues->emBoxBottomEdge.csCoord, | 
|---|
| 164 | blues->scale ) ) - | 
|---|
| 165 | CF2_MIN_COUNTER; | 
|---|
| 166 | blues->emBoxBottomEdge.scale   = blues->scale; | 
|---|
| 167 | blues->emBoxBottomEdge.flags   = CF2_GhostBottom | | 
|---|
| 168 | CF2_Locked | | 
|---|
| 169 | CF2_Synthetic; | 
|---|
| 170 |  | 
|---|
| 171 | blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON + | 
|---|
| 172 | 2 * font->darkenY; | 
|---|
| 173 | blues->emBoxTopEdge.dsCoord = cf2_fixedRound( | 
|---|
| 174 | FT_MulFix( | 
|---|
| 175 | blues->emBoxTopEdge.csCoord, | 
|---|
| 176 | blues->scale ) ) + | 
|---|
| 177 | CF2_MIN_COUNTER; | 
|---|
| 178 | blues->emBoxTopEdge.scale   = blues->scale; | 
|---|
| 179 | blues->emBoxTopEdge.flags   = CF2_GhostTop | | 
|---|
| 180 | CF2_Locked | | 
|---|
| 181 | CF2_Synthetic; | 
|---|
| 182 |  | 
|---|
| 183 | blues->doEmBoxHints = TRUE;    /* enable the heuristic */ | 
|---|
| 184 |  | 
|---|
| 185 | return; | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | /* copy `BlueValues' and `OtherBlues' to a combined array of top and */ | 
|---|
| 189 | /* bottom zones                                                      */ | 
|---|
| 190 | for ( i = 0; i < numBlueValues; i += 2 ) | 
|---|
| 191 | { | 
|---|
| 192 | blues->zone[blues->count].csBottomEdge = | 
|---|
| 193 | cf2_blueToFixed( blueValues[i] ); | 
|---|
| 194 | blues->zone[blues->count].csTopEdge = | 
|---|
| 195 | cf2_blueToFixed( blueValues[i + 1] ); | 
|---|
| 196 |  | 
|---|
| 197 | zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge, | 
|---|
| 198 | blues->zone[blues->count].csBottomEdge ); | 
|---|
| 199 |  | 
|---|
| 200 | if ( zoneHeight < 0 ) | 
|---|
| 201 | { | 
|---|
| 202 | FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n")); | 
|---|
| 203 | continue;   /* reject this zone */ | 
|---|
| 204 | } | 
|---|
| 205 |  | 
|---|
| 206 | if ( zoneHeight > maxZoneHeight ) | 
|---|
| 207 | { | 
|---|
| 208 | /* take maximum before darkening adjustment      */ | 
|---|
| 209 | /* so overshoot suppression point doesn't change */ | 
|---|
| 210 | maxZoneHeight = zoneHeight; | 
|---|
| 211 | } | 
|---|
| 212 |  | 
|---|
| 213 | /* adjust both edges of top zone upward by twice darkening amount */ | 
|---|
| 214 | if ( i != 0 ) | 
|---|
| 215 | { | 
|---|
| 216 | blues->zone[blues->count].csTopEdge    += 2 * font->darkenY; | 
|---|
| 217 | blues->zone[blues->count].csBottomEdge += 2 * font->darkenY; | 
|---|
| 218 | } | 
|---|
| 219 |  | 
|---|
| 220 | /* first `BlueValue' is bottom zone; others are top */ | 
|---|
| 221 | if ( i == 0 ) | 
|---|
| 222 | { | 
|---|
| 223 | blues->zone[blues->count].bottomZone = | 
|---|
| 224 | TRUE; | 
|---|
| 225 | blues->zone[blues->count].csFlatEdge = | 
|---|
| 226 | blues->zone[blues->count].csTopEdge; | 
|---|
| 227 | } | 
|---|
| 228 | else | 
|---|
| 229 | { | 
|---|
| 230 | blues->zone[blues->count].bottomZone = | 
|---|
| 231 | FALSE; | 
|---|
| 232 | blues->zone[blues->count].csFlatEdge = | 
|---|
| 233 | blues->zone[blues->count].csBottomEdge; | 
|---|
| 234 | } | 
|---|
| 235 |  | 
|---|
| 236 | blues->count += 1; | 
|---|
| 237 | } | 
|---|
| 238 |  | 
|---|
| 239 | for ( i = 0; i < numOtherBlues; i += 2 ) | 
|---|
| 240 | { | 
|---|
| 241 | blues->zone[blues->count].csBottomEdge = | 
|---|
| 242 | cf2_blueToFixed( otherBlues[i] ); | 
|---|
| 243 | blues->zone[blues->count].csTopEdge = | 
|---|
| 244 | cf2_blueToFixed( otherBlues[i + 1] ); | 
|---|
| 245 |  | 
|---|
| 246 | zoneHeight = SUB_INT32( blues->zone[blues->count].csTopEdge, | 
|---|
| 247 | blues->zone[blues->count].csBottomEdge ); | 
|---|
| 248 |  | 
|---|
| 249 | if ( zoneHeight < 0 ) | 
|---|
| 250 | { | 
|---|
| 251 | FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n")); | 
|---|
| 252 | continue;   /* reject this zone */ | 
|---|
| 253 | } | 
|---|
| 254 |  | 
|---|
| 255 | if ( zoneHeight > maxZoneHeight ) | 
|---|
| 256 | { | 
|---|
| 257 | /* take maximum before darkening adjustment      */ | 
|---|
| 258 | /* so overshoot suppression point doesn't change */ | 
|---|
| 259 | maxZoneHeight = zoneHeight; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | /* Note: bottom zones are not adjusted for darkening amount */ | 
|---|
| 263 |  | 
|---|
| 264 | /* all OtherBlues are bottom zone */ | 
|---|
| 265 | blues->zone[blues->count].bottomZone = | 
|---|
| 266 | TRUE; | 
|---|
| 267 | blues->zone[blues->count].csFlatEdge = | 
|---|
| 268 | blues->zone[blues->count].csTopEdge; | 
|---|
| 269 |  | 
|---|
| 270 | blues->count += 1; | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | /* Adjust for FamilyBlues */ | 
|---|
| 274 |  | 
|---|
| 275 | /* Search for the nearest flat edge in `FamilyBlues' or                */ | 
|---|
| 276 | /* `FamilyOtherBlues'.  According to the Black Book, any matching edge */ | 
|---|
| 277 | /* must be within one device pixel                                     */ | 
|---|
| 278 |  | 
|---|
| 279 | csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale ); | 
|---|
| 280 |  | 
|---|
| 281 | /* loop on all zones in this font */ | 
|---|
| 282 | for ( i = 0; i < blues->count; i++ ) | 
|---|
| 283 | { | 
|---|
| 284 | size_t     j; | 
|---|
| 285 | CF2_Fixed  minDiff; | 
|---|
| 286 | CF2_Fixed  flatFamilyEdge, diff; | 
|---|
| 287 | /* value for this font */ | 
|---|
| 288 | CF2_Fixed  flatEdge = blues->zone[i].csFlatEdge; | 
|---|
| 289 |  | 
|---|
| 290 |  | 
|---|
| 291 | if ( blues->zone[i].bottomZone ) | 
|---|
| 292 | { | 
|---|
| 293 | /* In a bottom zone, the top edge is the flat edge.             */ | 
|---|
| 294 | /* Search `FamilyOtherBlues' for bottom zones; look for closest */ | 
|---|
| 295 | /* Family edge that is within the one pixel threshold.          */ | 
|---|
| 296 |  | 
|---|
| 297 | minDiff = CF2_FIXED_MAX; | 
|---|
| 298 |  | 
|---|
| 299 | for ( j = 0; j < numFamilyOtherBlues; j += 2 ) | 
|---|
| 300 | { | 
|---|
| 301 | /* top edge */ | 
|---|
| 302 | flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] ); | 
|---|
| 303 |  | 
|---|
| 304 | diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) ); | 
|---|
| 305 |  | 
|---|
| 306 | if ( diff < minDiff && diff < csUnitsPerPixel ) | 
|---|
| 307 | { | 
|---|
| 308 | blues->zone[i].csFlatEdge = flatFamilyEdge; | 
|---|
| 309 | minDiff                   = diff; | 
|---|
| 310 |  | 
|---|
| 311 | if ( diff == 0 ) | 
|---|
| 312 | break; | 
|---|
| 313 | } | 
|---|
| 314 | } | 
|---|
| 315 |  | 
|---|
| 316 | /* check the first member of FamilyBlues, which is a bottom zone */ | 
|---|
| 317 | if ( numFamilyBlues >= 2 ) | 
|---|
| 318 | { | 
|---|
| 319 | /* top edge */ | 
|---|
| 320 | flatFamilyEdge = cf2_blueToFixed( familyBlues[1] ); | 
|---|
| 321 |  | 
|---|
| 322 | diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) ); | 
|---|
| 323 |  | 
|---|
| 324 | if ( diff < minDiff && diff < csUnitsPerPixel ) | 
|---|
| 325 | blues->zone[i].csFlatEdge = flatFamilyEdge; | 
|---|
| 326 | } | 
|---|
| 327 | } | 
|---|
| 328 | else | 
|---|
| 329 | { | 
|---|
| 330 | /* In a top zone, the bottom edge is the flat edge.                */ | 
|---|
| 331 | /* Search `FamilyBlues' for top zones; skip first zone, which is a */ | 
|---|
| 332 | /* bottom zone; look for closest Family edge that is within the    */ | 
|---|
| 333 | /* one pixel threshold                                             */ | 
|---|
| 334 |  | 
|---|
| 335 | minDiff = CF2_FIXED_MAX; | 
|---|
| 336 |  | 
|---|
| 337 | for ( j = 2; j < numFamilyBlues; j += 2 ) | 
|---|
| 338 | { | 
|---|
| 339 | /* bottom edge */ | 
|---|
| 340 | flatFamilyEdge = cf2_blueToFixed( familyBlues[j] ); | 
|---|
| 341 |  | 
|---|
| 342 | /* adjust edges of top zone upward by twice darkening amount */ | 
|---|
| 343 | flatFamilyEdge += 2 * font->darkenY;      /* bottom edge */ | 
|---|
| 344 |  | 
|---|
| 345 | diff = cf2_fixedAbs( SUB_INT32( flatEdge, flatFamilyEdge ) ); | 
|---|
| 346 |  | 
|---|
| 347 | if ( diff < minDiff && diff < csUnitsPerPixel ) | 
|---|
| 348 | { | 
|---|
| 349 | blues->zone[i].csFlatEdge = flatFamilyEdge; | 
|---|
| 350 | minDiff                   = diff; | 
|---|
| 351 |  | 
|---|
| 352 | if ( diff == 0 ) | 
|---|
| 353 | break; | 
|---|
| 354 | } | 
|---|
| 355 | } | 
|---|
| 356 | } | 
|---|
| 357 | } | 
|---|
| 358 |  | 
|---|
| 359 | /* TODO: enforce separation of zones, including BlueFuzz */ | 
|---|
| 360 |  | 
|---|
| 361 | /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */ | 
|---|
| 362 | /* `bcsetup.c'.                                               */ | 
|---|
| 363 |  | 
|---|
| 364 | if ( maxZoneHeight > 0 ) | 
|---|
| 365 | { | 
|---|
| 366 | if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ), | 
|---|
| 367 | maxZoneHeight ) ) | 
|---|
| 368 | { | 
|---|
| 369 | /* clamp at maximum scale */ | 
|---|
| 370 | blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ), | 
|---|
| 371 | maxZoneHeight ); | 
|---|
| 372 | } | 
|---|
| 373 |  | 
|---|
| 374 | /* | 
|---|
| 375 | * TODO: Revisit the bug fix for 613448.  The minimum scale | 
|---|
| 376 | *       requirement catches a number of library fonts.  For | 
|---|
| 377 | *       example, with default BlueScale (.039625) and 0.4 minimum, | 
|---|
| 378 | *       the test below catches any font with maxZoneHeight < 10.1. | 
|---|
| 379 | *       There are library fonts ranging from 2 to 10 that get | 
|---|
| 380 | *       caught, including e.g., Eurostile LT Std Medium with | 
|---|
| 381 | *       maxZoneHeight of 6. | 
|---|
| 382 | * | 
|---|
| 383 | */ | 
|---|
| 384 | #if 0 | 
|---|
| 385 | if ( blueScale < .4 / maxZoneHeight ) | 
|---|
| 386 | { | 
|---|
| 387 | tetraphilia_assert( 0 ); | 
|---|
| 388 | /* clamp at minimum scale, per bug 0613448 fix */ | 
|---|
| 389 | blueScale = .4 / maxZoneHeight; | 
|---|
| 390 | } | 
|---|
| 391 | #endif | 
|---|
| 392 |  | 
|---|
| 393 | } | 
|---|
| 394 |  | 
|---|
| 395 | /* | 
|---|
| 396 | * Suppress overshoot and boost blue zones at small sizes.  Boost | 
|---|
| 397 | * amount varies linearly from 0.5 pixel near 0 to 0 pixel at | 
|---|
| 398 | * blueScale cutoff. | 
|---|
| 399 | * Note: This boost amount is different from the coretype heuristic. | 
|---|
| 400 | * | 
|---|
| 401 | */ | 
|---|
| 402 |  | 
|---|
| 403 | if ( blues->scale < blues->blueScale ) | 
|---|
| 404 | { | 
|---|
| 405 | blues->suppressOvershoot = TRUE; | 
|---|
| 406 |  | 
|---|
| 407 | /* Change rounding threshold for `dsFlatEdge'.                    */ | 
|---|
| 408 | /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */ | 
|---|
| 409 | /*       10ppem Arial                                             */ | 
|---|
| 410 |  | 
|---|
| 411 | blues->boost = cf2_doubleToFixed( .6 ) - | 
|---|
| 412 | FT_MulDiv( cf2_doubleToFixed ( .6 ), | 
|---|
| 413 | blues->scale, | 
|---|
| 414 | blues->blueScale ); | 
|---|
| 415 | if ( blues->boost > 0x7FFF ) | 
|---|
| 416 | { | 
|---|
| 417 | /* boost must remain less than 0.5, or baseline could go negative */ | 
|---|
| 418 | blues->boost = 0x7FFF; | 
|---|
| 419 | } | 
|---|
| 420 | } | 
|---|
| 421 |  | 
|---|
| 422 | /* boost and darkening have similar effects; don't do both */ | 
|---|
| 423 | if ( font->stemDarkened ) | 
|---|
| 424 | blues->boost = 0; | 
|---|
| 425 |  | 
|---|
| 426 | /* set device space alignment for each zone;    */ | 
|---|
| 427 | /* apply boost amount before rounding flat edge */ | 
|---|
| 428 |  | 
|---|
| 429 | for ( i = 0; i < blues->count; i++ ) | 
|---|
| 430 | { | 
|---|
| 431 | if ( blues->zone[i].bottomZone ) | 
|---|
| 432 | blues->zone[i].dsFlatEdge = cf2_fixedRound( | 
|---|
| 433 | FT_MulFix( | 
|---|
| 434 | blues->zone[i].csFlatEdge, | 
|---|
| 435 | blues->scale ) - | 
|---|
| 436 | blues->boost ); | 
|---|
| 437 | else | 
|---|
| 438 | blues->zone[i].dsFlatEdge = cf2_fixedRound( | 
|---|
| 439 | FT_MulFix( | 
|---|
| 440 | blues->zone[i].csFlatEdge, | 
|---|
| 441 | blues->scale ) + | 
|---|
| 442 | blues->boost ); | 
|---|
| 443 | } | 
|---|
| 444 | } | 
|---|
| 445 |  | 
|---|
| 446 |  | 
|---|
| 447 | /* | 
|---|
| 448 | * Check whether `stemHint' is captured by one of the blue zones. | 
|---|
| 449 | * | 
|---|
| 450 | * Zero, one or both edges may be valid; only valid edges can be | 
|---|
| 451 | * captured.  For compatibility with CoolType, search top and bottom | 
|---|
| 452 | * zones in the same pass (see `BlueLock').  If a hint is captured, | 
|---|
| 453 | * return true and position the edge(s) in one of 3 ways: | 
|---|
| 454 | * | 
|---|
| 455 | * 1) If `BlueScale' suppresses overshoot, position the captured edge | 
|---|
| 456 | *    at the flat edge of the zone. | 
|---|
| 457 | * 2) If overshoot is not suppressed and `BlueShift' requires | 
|---|
| 458 | *    overshoot, position the captured edge a minimum of 1 device pixel | 
|---|
| 459 | *    from the flat edge. | 
|---|
| 460 | * 3) If overshoot is not suppressed or required, position the captured | 
|---|
| 461 | *    edge at the nearest device pixel. | 
|---|
| 462 | * | 
|---|
| 463 | */ | 
|---|
| 464 | FT_LOCAL_DEF( FT_Bool ) | 
|---|
| 465 | cf2_blues_capture( const CF2_Blues  blues, | 
|---|
| 466 | CF2_Hint         bottomHintEdge, | 
|---|
| 467 | CF2_Hint         topHintEdge ) | 
|---|
| 468 | { | 
|---|
| 469 | /* TODO: validate? */ | 
|---|
| 470 | CF2_Fixed  csFuzz = blues->blueFuzz; | 
|---|
| 471 |  | 
|---|
| 472 | /* new position of captured edge */ | 
|---|
| 473 | CF2_Fixed  dsNew; | 
|---|
| 474 |  | 
|---|
| 475 | /* amount that hint is moved when positioned */ | 
|---|
| 476 | CF2_Fixed  dsMove = 0; | 
|---|
| 477 |  | 
|---|
| 478 | FT_Bool   captured = FALSE; | 
|---|
| 479 | CF2_UInt  i; | 
|---|
| 480 |  | 
|---|
| 481 |  | 
|---|
| 482 | /* assert edge flags are consistent */ | 
|---|
| 483 | FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) && | 
|---|
| 484 | !cf2_hint_isBottom( topHintEdge ) ); | 
|---|
| 485 |  | 
|---|
| 486 | /* TODO: search once without blue fuzz for compatibility with coretype? */ | 
|---|
| 487 | for ( i = 0; i < blues->count; i++ ) | 
|---|
| 488 | { | 
|---|
| 489 | if ( blues->zone[i].bottomZone           && | 
|---|
| 490 | cf2_hint_isBottom( bottomHintEdge ) ) | 
|---|
| 491 | { | 
|---|
| 492 | if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <= | 
|---|
| 493 | bottomHintEdge->csCoord                           && | 
|---|
| 494 | bottomHintEdge->csCoord <= | 
|---|
| 495 | ADD_INT32( blues->zone[i].csTopEdge, csFuzz )     ) | 
|---|
| 496 | { | 
|---|
| 497 | /* bottom edge captured by bottom zone */ | 
|---|
| 498 |  | 
|---|
| 499 | if ( blues->suppressOvershoot ) | 
|---|
| 500 | dsNew = blues->zone[i].dsFlatEdge; | 
|---|
| 501 |  | 
|---|
| 502 | else if ( SUB_INT32( blues->zone[i].csTopEdge, | 
|---|
| 503 | bottomHintEdge->csCoord ) >= | 
|---|
| 504 | blues->blueShift ) | 
|---|
| 505 | { | 
|---|
| 506 | /* guarantee minimum of 1 pixel overshoot */ | 
|---|
| 507 | dsNew = FT_MIN( | 
|---|
| 508 | cf2_fixedRound( bottomHintEdge->dsCoord ), | 
|---|
| 509 | blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) ); | 
|---|
| 510 | } | 
|---|
| 511 |  | 
|---|
| 512 | else | 
|---|
| 513 | { | 
|---|
| 514 | /* simply round captured edge */ | 
|---|
| 515 | dsNew = cf2_fixedRound( bottomHintEdge->dsCoord ); | 
|---|
| 516 | } | 
|---|
| 517 |  | 
|---|
| 518 | dsMove   = SUB_INT32( dsNew, bottomHintEdge->dsCoord ); | 
|---|
| 519 | captured = TRUE; | 
|---|
| 520 |  | 
|---|
| 521 | break; | 
|---|
| 522 | } | 
|---|
| 523 | } | 
|---|
| 524 |  | 
|---|
| 525 | if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) ) | 
|---|
| 526 | { | 
|---|
| 527 | if ( SUB_INT32( blues->zone[i].csBottomEdge, csFuzz ) <= | 
|---|
| 528 | topHintEdge->csCoord                              && | 
|---|
| 529 | topHintEdge->csCoord <= | 
|---|
| 530 | ADD_INT32( blues->zone[i].csTopEdge, csFuzz )     ) | 
|---|
| 531 | { | 
|---|
| 532 | /* top edge captured by top zone */ | 
|---|
| 533 |  | 
|---|
| 534 | if ( blues->suppressOvershoot ) | 
|---|
| 535 | dsNew = blues->zone[i].dsFlatEdge; | 
|---|
| 536 |  | 
|---|
| 537 | else if ( SUB_INT32( topHintEdge->csCoord, | 
|---|
| 538 | blues->zone[i].csBottomEdge ) >= | 
|---|
| 539 | blues->blueShift ) | 
|---|
| 540 | { | 
|---|
| 541 | /* guarantee minimum of 1 pixel overshoot */ | 
|---|
| 542 | dsNew = FT_MAX( | 
|---|
| 543 | cf2_fixedRound( topHintEdge->dsCoord ), | 
|---|
| 544 | blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) ); | 
|---|
| 545 | } | 
|---|
| 546 |  | 
|---|
| 547 | else | 
|---|
| 548 | { | 
|---|
| 549 | /* simply round captured edge */ | 
|---|
| 550 | dsNew = cf2_fixedRound( topHintEdge->dsCoord ); | 
|---|
| 551 | } | 
|---|
| 552 |  | 
|---|
| 553 | dsMove   = SUB_INT32( dsNew, topHintEdge->dsCoord ); | 
|---|
| 554 | captured = TRUE; | 
|---|
| 555 |  | 
|---|
| 556 | break; | 
|---|
| 557 | } | 
|---|
| 558 | } | 
|---|
| 559 | } | 
|---|
| 560 |  | 
|---|
| 561 | if ( captured ) | 
|---|
| 562 | { | 
|---|
| 563 | /* move both edges and flag them `locked' */ | 
|---|
| 564 | if ( cf2_hint_isValid( bottomHintEdge ) ) | 
|---|
| 565 | { | 
|---|
| 566 | bottomHintEdge->dsCoord = ADD_INT32( bottomHintEdge->dsCoord, | 
|---|
| 567 | dsMove ); | 
|---|
| 568 | cf2_hint_lock( bottomHintEdge ); | 
|---|
| 569 | } | 
|---|
| 570 |  | 
|---|
| 571 | if ( cf2_hint_isValid( topHintEdge ) ) | 
|---|
| 572 | { | 
|---|
| 573 | topHintEdge->dsCoord = ADD_INT32( topHintEdge->dsCoord, dsMove ); | 
|---|
| 574 | cf2_hint_lock( topHintEdge ); | 
|---|
| 575 | } | 
|---|
| 576 | } | 
|---|
| 577 |  | 
|---|
| 578 | return captured; | 
|---|
| 579 | } | 
|---|
| 580 |  | 
|---|
| 581 |  | 
|---|
| 582 | /* END */ | 
|---|
| 583 |  | 
|---|