cf2_blues_init( CF2_Blues blues, CF2_Font font ) { /* pointer to parsed font object */ CFF_Decoder* decoder = font->decoder; CF2_Fixed zoneHeight; CF2_Fixed maxZoneHeight = 0; CF2_Fixed csUnitsPerPixel; size_t numBlueValues; size_t numOtherBlues; size_t numFamilyBlues; size_t numFamilyOtherBlues; FT_Pos* blueValues; FT_Pos* otherBlues; FT_Pos* familyBlues; FT_Pos* familyOtherBlues; size_t i; CF2_Fixed emBoxBottom, emBoxTop; CF2_Int unitsPerEm = font->unitsPerEm; if ( unitsPerEm == 0 ) unitsPerEm = 1000; FT_ZERO( blues ); blues->scale = font->innerTransform.d; cf2_getBlueMetrics( decoder, &blues->blueScale, &blues->blueShift, &blues->blueFuzz ); cf2_getBlueValues( decoder, &numBlueValues, &blueValues ); cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues ); cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues ); cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues ); /* * synthetic em box hint heuristic * * Apply this when ideographic dictionary (LanguageGroup 1) has no * real alignment zones. Adobe tools generate dummy zones at -250 and * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones * should not enable the heuristic. When the heuristic is enabled, * the font's blue zones are ignored. * */ /* get em box from OS/2 typoAscender/Descender */ /* TODO: FreeType does not parse these metrics. Skip them for now. */ #if 0 FCM_getHorizontalLineMetrics( &e, font->font, &ascender, &descender, &linegap ); if ( ascender - descender == unitsPerEm ) { emBoxBottom = cf2_intToFixed( descender ); emBoxTop = cf2_intToFixed( ascender ); } else #endif { emBoxBottom = CF2_ICF_Bottom; emBoxTop = CF2_ICF_Top; } if ( cf2_getLanguageGroup( decoder ) == 1 && ( numBlueValues == 0 || ( numBlueValues == 4 && cf2_blueToFixed( blueValues[0] ) < emBoxBottom && cf2_blueToFixed( blueValues[1] ) < emBoxBottom && cf2_blueToFixed( blueValues[2] ) > emBoxTop && cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) ) { /* * Construct hint edges suitable for synthetic ghost hints at top * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted * features above or below the last hinted edge. This also gives a * net 1 pixel boost to the height of ideographic glyphs. * * Note: Adjust synthetic hints outward by epsilon (0x.0001) to * avoid interference. E.g., some fonts have real hints at * 880 and -120. */ blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON; blues->emBoxBottomEdge.dsCoord = cf2_fixedRound( FT_MulFix( blues->emBoxBottomEdge.csCoord, blues->scale ) ) - CF2_MIN_COUNTER; blues->emBoxBottomEdge.scale = blues->scale; blues->emBoxBottomEdge.flags = CF2_GhostBottom | CF2_Locked | CF2_Synthetic; blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON + 2 * font->darkenY; blues->emBoxTopEdge.dsCoord = cf2_fixedRound( FT_MulFix( blues->emBoxTopEdge.csCoord, blues->scale ) ) + CF2_MIN_COUNTER; blues->emBoxTopEdge.scale = blues->scale; blues->emBoxTopEdge.flags = CF2_GhostTop | CF2_Locked | CF2_Synthetic; blues->doEmBoxHints = TRUE; /* enable the heuristic */ return; } /* copy `BlueValues' and `OtherBlues' to a combined array of top and */ /* bottom zones */ for ( i = 0; i < numBlueValues; i += 2 ) { blues->zone[blues->count].csBottomEdge = cf2_blueToFixed( blueValues[i] ); blues->zone[blues->count].csTopEdge = cf2_blueToFixed( blueValues[i + 1] ); zoneHeight = blues->zone[blues->count].csTopEdge - blues->zone[blues->count].csBottomEdge; if ( zoneHeight < 0 ) { FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); continue; /* reject this zone */ } if ( zoneHeight > maxZoneHeight ) { /* take maximum before darkening adjustment */ /* so overshoot suppression point doesn't change */ maxZoneHeight = zoneHeight; } /* adjust both edges of top zone upward by twice darkening amount */ if ( i != 0 ) { blues->zone[blues->count].csTopEdge += 2 * font->darkenY; blues->zone[blues->count].csBottomEdge += 2 * font->darkenY; } /* first `BlueValue' is bottom zone; others are top */ if ( i == 0 ) { blues->zone[blues->count].bottomZone = TRUE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csTopEdge; } else { blues->zone[blues->count].bottomZone = FALSE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csBottomEdge; } blues->count += 1; } for ( i = 0; i < numOtherBlues; i += 2 ) { blues->zone[blues->count].csBottomEdge = cf2_blueToFixed( otherBlues[i] ); blues->zone[blues->count].csTopEdge = cf2_blueToFixed( otherBlues[i + 1] ); zoneHeight = blues->zone[blues->count].csTopEdge - blues->zone[blues->count].csBottomEdge; if ( zoneHeight < 0 ) { FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); continue; /* reject this zone */ } if ( zoneHeight > maxZoneHeight ) { /* take maximum before darkening adjustment */ /* so overshoot suppression point doesn't change */ maxZoneHeight = zoneHeight; } /* Note: bottom zones are not adjusted for darkening amount */ /* all OtherBlues are bottom zone */ blues->zone[blues->count].bottomZone = TRUE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csTopEdge; blues->count += 1; } /* Adjust for FamilyBlues */ /* Search for the nearest flat edge in `FamilyBlues' or */ /* `FamilyOtherBlues'. According to the Black Book, any matching edge */ /* must be within one device pixel */ csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale ); /* loop on all zones in this font */ for ( i = 0; i < blues->count; i++ ) { size_t j; CF2_Fixed minDiff; CF2_Fixed flatFamilyEdge, diff; /* value for this font */ CF2_Fixed flatEdge = blues->zone[i].csFlatEdge; if ( blues->zone[i].bottomZone ) { /* In a bottom zone, the top edge is the flat edge. */ /* Search `FamilyOtherBlues' for bottom zones; look for closest */ /* Family edge that is within the one pixel threshold. */ minDiff = CF2_FIXED_MAX; for ( j = 0; j < numFamilyOtherBlues; j += 2 ) { /* top edge */ flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] ); diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) { blues->zone[i].csFlatEdge = flatFamilyEdge; minDiff = diff; if ( diff == 0 ) break; } } /* check the first member of FamilyBlues, which is a bottom zone */ if ( numFamilyBlues >= 2 ) { /* top edge */ flatFamilyEdge = cf2_blueToFixed( familyBlues[1] ); diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) blues->zone[i].csFlatEdge = flatFamilyEdge; } } else { /* In a top zone, the bottom edge is the flat edge. */ /* Search `FamilyBlues' for top zones; skip first zone, which is a */ /* bottom zone; look for closest Family edge that is within the */ /* one pixel threshold */ minDiff = CF2_FIXED_MAX; for ( j = 2; j < numFamilyBlues; j += 2 ) { /* bottom edge */ flatFamilyEdge = cf2_blueToFixed( familyBlues[j] ); /* adjust edges of top zone upward by twice darkening amount */ flatFamilyEdge += 2 * font->darkenY; /* bottom edge */ diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) { blues->zone[i].csFlatEdge = flatFamilyEdge; minDiff = diff; if ( diff == 0 ) break; } } } } /* TODO: enforce separation of zones, including BlueFuzz */ /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */ /* `bcsetup.c'. */ if ( maxZoneHeight > 0 ) { if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ), maxZoneHeight ) ) { /* clamp at maximum scale */ blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ), maxZoneHeight ); } /* * TODO: Revisit the bug fix for 613448. The minimum scale * requirement catches a number of library fonts. For * example, with default BlueScale (.039625) and 0.4 minimum, * the test below catches any font with maxZoneHeight < 10.1. * There are library fonts ranging from 2 to 10 that get * caught, including e.g., Eurostile LT Std Medium with * maxZoneHeight of 6. * */ #if 0 if ( blueScale < .4 / maxZoneHeight ) { tetraphilia_assert( 0 ); /* clamp at minimum scale, per bug 0613448 fix */ blueScale = .4 / maxZoneHeight; } #endif } /* * Suppress overshoot and boost blue zones at small sizes. Boost * amount varies linearly from 0.5 pixel near 0 to 0 pixel at * blueScale cutoff. * Note: This boost amount is different from the coretype heuristic. * */ if ( blues->scale < blues->blueScale ) { blues->suppressOvershoot = TRUE; /* Change rounding threshold for `dsFlatEdge'. */ /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */ /* 10ppem Arial */ blues->boost = FT_MulFix( cf2_floatToFixed( .6 ), ( cf2_intToFixed( 1 ) - FT_DivFix( blues->scale, blues->blueScale ) ) ); if ( blues->boost > 0x7FFF ) { /* boost must remain less than 0.5, or baseline could go negative */ blues->boost = 0x7FFF; } } /* boost and darkening have similar effects; don't do both */ if ( font->stemDarkened ) blues->boost = 0; /* set device space alignment for each zone; */ /* apply boost amount before rounding flat edge */ for ( i = 0; i < blues->count; i++ ) { if ( blues->zone[i].bottomZone ) blues->zone[i].dsFlatEdge = cf2_fixedRound( FT_MulFix( blues->zone[i].csFlatEdge, blues->scale ) - blues->boost ); else blues->zone[i].dsFlatEdge = cf2_fixedRound( FT_MulFix( blues->zone[i].csFlatEdge, blues->scale ) + blues->boost ); } }
/* Compute a stem darkening amount in character space. */ static void cf2_computeDarkening( CF2_Fixed emRatio, CF2_Fixed ppem, CF2_Fixed stemWidth, CF2_Fixed* darkenAmount, CF2_Fixed boldenAmount, FT_Bool stemDarkened, FT_Int* darkenParams ) { /* * Total darkening amount is computed in 1000 unit character space * using the modified 5 part curve as Adobe's Avalon rasterizer. * The darkening amount is smaller for thicker stems. * It becomes zero when the stem is thicker than 2.333 pixels. * * By default, we use * * darkenAmount = 0.4 pixels if scaledStem <= 0.5 pixels, * darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels, * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels, * * and piecewise linear in-between: * * * darkening * ^ * | * | (x1,y1) * |--------+ * | \ * | \ * | \ (x3,y3) * | +----------+ * | (x2,y2) \ * | \ * | \ * | +----------------- * | (x4,y4) * +---------------------------------------------> stem * thickness * * * This corresponds to the following values for the * `darkening-parameters' property: * * (x1, y1) = (500, 400) * (x2, y2) = (1000, 275) * (x3, y3) = (1667, 275) * (x4, y4) = (2333, 0) * */ /* Internal calculations are done in units per thousand for */ /* convenience. The x axis is scaled stem width in */ /* thousandths of a pixel. That is, 1000 is 1 pixel. */ /* The y axis is darkening amount in thousandths of a pixel.*/ /* In the code, below, dividing by ppem and */ /* adjusting for emRatio converts darkenAmount to character */ /* space (font units). */ CF2_Fixed stemWidthPer1000, scaledStem; *darkenAmount = 0; if ( boldenAmount == 0 && !stemDarkened ) return; /* protect against range problems and divide by zero */ if ( emRatio < cf2_floatToFixed( .01 ) ) return; if ( stemDarkened ) { FT_Int x1 = darkenParams[0]; FT_Int y1 = darkenParams[1]; FT_Int x2 = darkenParams[2]; FT_Int y2 = darkenParams[3]; FT_Int x3 = darkenParams[4]; FT_Int y3 = darkenParams[5]; FT_Int x4 = darkenParams[6]; FT_Int y4 = darkenParams[7]; /* convert from true character space to 1000 unit character space; */ /* add synthetic emboldening effect */ /* we have to assure that the computation of `scaledStem' */ /* and `stemWidthPer1000' don't overflow */ stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio ); if ( emRatio > CF2_FIXED_ONE && stemWidthPer1000 <= ( stemWidth + boldenAmount ) ) { stemWidthPer1000 = 0; /* to pacify compiler */ scaledStem = cf2_intToFixed( x4 ); } else { scaledStem = FT_MulFix( stemWidthPer1000, ppem ); if ( ppem > CF2_FIXED_ONE && scaledStem <= stemWidthPer1000 ) scaledStem = cf2_intToFixed( x4 ); } /* now apply the darkening parameters */ if ( scaledStem < cf2_intToFixed( x1 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem ); else if ( scaledStem < cf2_intToFixed( x2 ) ) { FT_Int xdelta = x2 - x1; FT_Int ydelta = y2 - y1; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x1 ), ppem ); if ( !xdelta ) goto Try_x3; *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) + FT_DivFix( cf2_intToFixed( y1 ), ppem ); } else if ( scaledStem < cf2_intToFixed( x3 ) ) { Try_x3: { FT_Int xdelta = x3 - x2; FT_Int ydelta = y3 - y2; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x2 ), ppem ); if ( !xdelta ) goto Try_x4; *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) + FT_DivFix( cf2_intToFixed( y2 ), ppem ); } } else if ( scaledStem < cf2_intToFixed( x4 ) ) { Try_x4: { FT_Int xdelta = x4 - x3; FT_Int ydelta = y4 - y3; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x3 ), ppem ); if ( !xdelta ) goto Use_y4; *darkenAmount = FT_MulFix( x, FT_DivFix( ydelta, xdelta ) ) + FT_DivFix( cf2_intToFixed( y3 ), ppem ); } } else { Use_y4: *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem ); } /* use half the amount on each side and convert back to true */ /* character space */ *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio ); } /* add synthetic emboldening effect in character space */ *darkenAmount += boldenAmount / 2; }
/* Compute a stem darkening amount in character space. */ static void cf2_computeDarkening( CF2_Fixed emRatio, CF2_Fixed ppem, CF2_Fixed stemWidth, CF2_Fixed* darkenAmount, CF2_Fixed boldenAmount, FT_Bool stemDarkened ) { /* Internal calculations are done in units per thousand for */ /* convenience. */ CF2_Fixed stemWidthPer1000, scaledStem; *darkenAmount = 0; if ( boldenAmount == 0 && !stemDarkened ) return; /* protect against range problems and divide by zero */ if ( emRatio < cf2_floatToFixed( .01 ) ) return; if ( stemDarkened ) { /* convert from true character space to 1000 unit character space; */ /* add synthetic emboldening effect */ /* we have to assure that the computation of `scaledStem' */ /* and `stemWidthPer1000' don't overflow */ stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio ); if ( emRatio > CF2_FIXED_ONE && stemWidthPer1000 <= ( stemWidth + boldenAmount ) ) { stemWidthPer1000 = 0; /* to pacify compiler */ scaledStem = cf2_intToFixed( 2333 ); } else { scaledStem = FT_MulFix( stemWidthPer1000, ppem ); if ( ppem > CF2_FIXED_ONE && scaledStem <= stemWidthPer1000 ) scaledStem = cf2_intToFixed( 2333 ); } /* * Total darkening amount is computed in 1000 unit character space * using the modified 5 part curve as Avalon rasterizer. * The darkening amount is smaller for thicker stems. * It becomes zero when the stem is thicker than 2.333 pixels. * * In Avalon rasterizer, * * darkenAmount = 0.5 pixels if scaledStem <= 0.5 pixels, * darkenAmount = 0.333 pixels if 1 <= scaledStem <= 1.667 pixels, * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels, * * and piecewise linear in-between. * */ if ( scaledStem < cf2_intToFixed( 500 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( 400 ), ppem ); else if ( scaledStem < cf2_intToFixed( 1000 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( 525 ), ppem ) - FT_MulFix( stemWidthPer1000, cf2_floatToFixed( .25 ) ); else if ( scaledStem < cf2_intToFixed( 1667 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( 275 ), ppem ); else if ( scaledStem < cf2_intToFixed( 2333 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( 963 ), ppem ) - FT_MulFix( stemWidthPer1000, cf2_floatToFixed( .413 ) ); /* use half the amount on each side and convert back to true */ /* character space */ *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio ); } /* add synthetic emboldening effect in character space */ *darkenAmount += boldenAmount / 2; }
/* Compute a stem darkening amount in character space. */ static void cf2_computeDarkening( CF2_Fixed emRatio, CF2_Fixed ppem, CF2_Fixed stemWidth, CF2_Fixed* darkenAmount, CF2_Fixed boldenAmount, FT_Bool stemDarkened, FT_Int* darkenParams ) { /* * Total darkening amount is computed in 1000 unit character space * using the modified 5 part curve as Adobe's Avalon rasterizer. * The darkening amount is smaller for thicker stems. * It becomes zero when the stem is thicker than 2.333 pixels. * * By default, we use * * darkenAmount = 0.4 pixels if scaledStem <= 0.5 pixels, * darkenAmount = 0.275 pixels if 1 <= scaledStem <= 1.667 pixels, * darkenAmount = 0 pixel if scaledStem >= 2.333 pixels, * * and piecewise linear in-between: * * * darkening * ^ * | * | (x1,y1) * |--------+ * | \ * | \ * | \ (x3,y3) * | +----------+ * | (x2,y2) \ * | \ * | \ * | +----------------- * | (x4,y4) * +---------------------------------------------> stem * thickness * * * This corresponds to the following values for the * `darkening-parameters' property: * * (x1, y1) = (500, 400) * (x2, y2) = (1000, 275) * (x3, y3) = (1667, 275) * (x4, y4) = (2333, 0) * */ /* Internal calculations are done in units per thousand for */ /* convenience. The x axis is scaled stem width in */ /* thousandths of a pixel. That is, 1000 is 1 pixel. */ /* The y axis is darkening amount in thousandths of a pixel.*/ /* In the code, below, dividing by ppem and */ /* adjusting for emRatio converts darkenAmount to character */ /* space (font units). */ CF2_Fixed stemWidthPer1000, scaledStem; FT_Int logBase2; *darkenAmount = 0; if ( boldenAmount == 0 && !stemDarkened ) return; /* protect against range problems and divide by zero */ if ( emRatio < cf2_floatToFixed( .01 ) ) return; if ( stemDarkened ) { FT_Int x1 = darkenParams[0]; FT_Int y1 = darkenParams[1]; FT_Int x2 = darkenParams[2]; FT_Int y2 = darkenParams[3]; FT_Int x3 = darkenParams[4]; FT_Int y3 = darkenParams[5]; FT_Int x4 = darkenParams[6]; FT_Int y4 = darkenParams[7]; /* convert from true character space to 1000 unit character space; */ /* add synthetic emboldening effect */ /* `stemWidthPer1000' will not overflow for a legitimate font */ stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio ); /* `scaledStem' can easily overflow, so we must clamp its maximum */ /* value; the test doesn't need to be precise, but must be */ /* conservative. The clamp value (default 2333) where */ /* `darkenAmount' is zero is well below the overflow value of */ /* 32767. */ /* */ /* FT_MSB computes the integer part of the base 2 logarithm. The */ /* number of bits for the product is 1 or 2 more than the sum of */ /* logarithms; remembering that the 16 lowest bits of the fraction */ /* are dropped this is correct to within a factor of almost 4. */ /* For example, 0x80.0000 * 0x80.0000 = 0x4000.0000 is 23+23 and */ /* is flagged as possible overflow because 0xFF.FFFF * 0xFF.FFFF = */ /* 0xFFFF.FE00 is also 23+23. */ logBase2 = FT_MSB( (FT_UInt32)stemWidthPer1000 ) + FT_MSB( (FT_UInt32)ppem ); if ( logBase2 >= 46 ) /* possible overflow */ scaledStem = cf2_intToFixed( x4 ); else scaledStem = FT_MulFix( stemWidthPer1000, ppem ); /* now apply the darkening parameters */ if ( scaledStem < cf2_intToFixed( x1 ) ) *darkenAmount = FT_DivFix( cf2_intToFixed( y1 ), ppem ); else if ( scaledStem < cf2_intToFixed( x2 ) ) { FT_Int xdelta = x2 - x1; FT_Int ydelta = y2 - y1; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x1 ), ppem ); if ( !xdelta ) goto Try_x3; *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( cf2_intToFixed( y1 ), ppem ); } else if ( scaledStem < cf2_intToFixed( x3 ) ) { Try_x3: { FT_Int xdelta = x3 - x2; FT_Int ydelta = y3 - y2; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x2 ), ppem ); if ( !xdelta ) goto Try_x4; *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( cf2_intToFixed( y2 ), ppem ); } } else if ( scaledStem < cf2_intToFixed( x4 ) ) { Try_x4: { FT_Int xdelta = x4 - x3; FT_Int ydelta = y4 - y3; FT_Int x = stemWidthPer1000 - FT_DivFix( cf2_intToFixed( x3 ), ppem ); if ( !xdelta ) goto Use_y4; *darkenAmount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( cf2_intToFixed( y3 ), ppem ); } } else { Use_y4: *darkenAmount = FT_DivFix( cf2_intToFixed( y4 ), ppem ); } /* use half the amount on each side and convert back to true */ /* character space */ *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio ); } /* add synthetic emboldening effect in character space */ *darkenAmount += boldenAmount / 2; }