diff --git a/ChangeLog b/ChangeLog index 31a84ee68..52492cd1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2019-05-09 Werner Lemberg + + [truetype] Increase precision of font variation (#54371). + + This patch make FreeType use font units in 26.6 format internally + instead of integers. + + * src/truetype/ttgxvar.c (FT_fixedToFdot6): New macro. + (TT_Vary_Apply_Glyph_Deltas): Add argument to output unrounded font + coordinates. + * src/truetype/ttgxvar.h: Updated. + + * src/truetype/ttgload.c (TT_Process_Simple_Glyph): Use + `extra_points2' array to temporarily hold unrounded point + coordinates; use them to compute scaled coordinates and linear + advance width and height. + (load_truetype_code): Adjust similarly. + 2019-05-09 Werner Lemberg * src/truetype/ttgload.c (TT_Process_Simple_Glyph): Minor. diff --git a/include/freetype/freetype.h b/include/freetype/freetype.h index f758ea1c4..2f7863c4c 100644 --- a/include/freetype/freetype.h +++ b/include/freetype/freetype.h @@ -1766,6 +1766,13 @@ FT_BEGIN_HEADER * transformed, distorted, emboldened, etc. However, it must not be * freed. * + * [Since 2.10.1] If @FT_LOAD_NO_SCALE is set, outline coordinates of + * OpenType variation fonts for a selected instance are internally + * handled as 26.6 fractional font units but returned as (rounded) + * integers, as expected. To get unrounded font units, don't use + * @FT_LOAD_NO_SCALE but load the glyph with @FT_LOAD_NO_HINTING and + * scale it, using the font's `units_per_EM` value as the ppem. + * * num_subglyphs :: * The number of subglyphs in a composite glyph. This field is only * valid for the composite glyph format that should normally only be diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c index d88f4c4eb..d4a87ef09 100644 --- a/src/truetype/ttgload.c +++ b/src/truetype/ttgload.c @@ -925,6 +925,7 @@ FT_GlyphLoader gloader = loader->gloader; FT_Error error = FT_Err_Ok; FT_Outline* outline; + FT_Vector* unrounded; FT_Int n_points; @@ -950,20 +951,26 @@ if ( FT_IS_NAMED_INSTANCE( FT_FACE( loader->face ) ) || FT_IS_VARIATION( FT_FACE( loader->face ) ) ) { - /* Deltas apply to the unscaled data. */ + /* Deltas apply to the unscaled data. */ + /* We temporarily use `extra_points2' to hold unrounded values. */ + unrounded = gloader->current.extra_points2; error = TT_Vary_Apply_Glyph_Deltas( loader->face, loader->glyph_index, outline, + unrounded, (FT_UInt)n_points ); /* recalculate linear horizontal and vertical advances */ /* if we don't have HVAR and VVAR, respectively */ + + /* XXX: change all FreeType modules to store `linear' and `vadvance' */ + /* in 26.6 format before the `base' module scales them to 16.16 */ if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) - loader->linear = outline->points[n_points - 3].x - - outline->points[n_points - 4].x; + loader->linear = FT_PIX_ROUND( unrounded[n_points - 3].x - + unrounded[n_points - 4].x ) / 64; if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) - loader->vadvance = outline->points[n_points - 1].x - - outline->points[n_points - 2].x; + loader->vadvance = FT_PIX_ROUND( unrounded[n_points - 1].x - + unrounded[n_points - 2].x ) / 64; if ( error ) return error; @@ -1013,10 +1020,17 @@ /* compensate for any scaling by de/emboldening; */ /* the amount was determined via experimentation */ if ( x_scale_factor != 1000 && ppem > 11 ) + { + FT_Vector* orig_points = outline->points; + + + outline->points = unrounded; FT_Outline_EmboldenXY( outline, FT_MulFix( 1280 * ppem, 1000 - x_scale_factor ), 0 ); + outline->points = orig_points; + } do_scale = TRUE; } } @@ -1037,10 +1051,10 @@ if ( do_scale ) { - for ( ; vec < limit; vec++ ) + for ( ; vec < limit; vec++, unrounded++ ) { - vec->x = FT_MulFix( vec->x, x_scale ); - vec->y = FT_MulFix( vec->y, y_scale ); + vec->x = FT_MulDiv( unrounded->x, x_scale, 0x10000L * 64 ); + vec->y = FT_MulDiv( unrounded->y, y_scale, 0x10000L * 64 ); } } @@ -1678,6 +1692,9 @@ short contours[4] = { 0, 1, 2, 3 }; FT_Outline outline; + /* unrounded values */ + FT_Vector unrounded[4]; + points[0].x = loader->pp1.x; points[0].y = loader->pp1.y; @@ -1699,6 +1716,7 @@ error = TT_Vary_Apply_Glyph_Deltas( loader->face, glyph_index, &outline, + unrounded, (FT_UInt)outline.n_points ); if ( error ) goto Exit; @@ -1716,9 +1734,11 @@ /* recalculate linear horizontal and vertical advances */ /* if we don't have HVAR and VVAR, respectively */ if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) - loader->linear = loader->pp2.x - loader->pp1.x; + loader->linear = FT_PIX_ROUND( unrounded[1].x - + unrounded[0].x ) / 64; if ( !( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) - loader->vadvance = loader->pp4.x - loader->pp3.x; + loader->vadvance = FT_PIX_ROUND( unrounded[3].x - + unrounded[2].x ) / 64; } #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ @@ -1858,9 +1878,10 @@ FT_SubGlyph subglyph; FT_Outline outline; - FT_Vector* points = NULL; - char* tags = NULL; - short* contours = NULL; + FT_Vector* points = NULL; + char* tags = NULL; + short* contours = NULL; + FT_Vector* unrounded = NULL; limit = (short)gloader->current.num_subglyphs; @@ -1874,9 +1895,10 @@ outline.tags = NULL; outline.contours = NULL; - if ( FT_NEW_ARRAY( points, outline.n_points ) || - FT_NEW_ARRAY( tags, outline.n_points ) || - FT_NEW_ARRAY( contours, outline.n_points ) ) + if ( FT_NEW_ARRAY( points, outline.n_points ) || + FT_NEW_ARRAY( tags, outline.n_points ) || + FT_NEW_ARRAY( contours, outline.n_points ) || + FT_NEW_ARRAY( unrounded, outline.n_points ) ) goto Exit1; subglyph = gloader->current.subglyphs; @@ -1925,6 +1947,7 @@ face, glyph_index, &outline, + unrounded, (FT_UInt)outline.n_points ) ) ) goto Exit1; @@ -1952,14 +1975,17 @@ /* recalculate linear horizontal and vertical advances */ /* if we don't have HVAR and VVAR, respectively */ if ( !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) - loader->linear = loader->pp2.x - loader->pp1.x; + loader->linear = FT_PIX_ROUND( unrounded[1].x - + unrounded[0].x ) / 64; if ( !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) - loader->vadvance = loader->pp4.x - loader->pp3.x; + loader->vadvance = FT_PIX_ROUND( unrounded[3].x - + unrounded[2].x ) / 64; Exit1: FT_FREE( outline.points ); FT_FREE( outline.tags ); FT_FREE( outline.contours ); + FT_FREE( unrounded ); if ( error ) goto Exit; diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c index 56b5b6d9f..317340acb 100644 --- a/src/truetype/ttgxvar.c +++ b/src/truetype/ttgxvar.c @@ -68,12 +68,14 @@ /* some macros we need */ -#define FT_fdot14ToFixed( x ) \ - ( (FT_Fixed)( (FT_ULong)(x) << 2 ) ) -#define FT_intToFixed( i ) \ - ( (FT_Fixed)( (FT_ULong)(i) << 16 ) ) -#define FT_fixedToInt( x ) \ - ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define FT_fdot14ToFixed( x ) \ + ( (FT_Fixed)( (FT_ULong)(x) << 2 ) ) +#define FT_intToFixed( i ) \ + ( (FT_Fixed)( (FT_ULong)(i) << 16 ) ) +#define FT_fixedToInt( x ) \ + ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) +#define FT_fixedToFdot6( x ) \ + ( (FT_Pos)( ( (FT_UInt32)(x) + 0x20 ) >> 10 ) ) /************************************************************************** @@ -3674,6 +3676,11 @@ * outline :: * The outline to change. * + * @Output: + * unrounded :: + * An array with `n_points' elements that is filled with unrounded + * point coordinates (in 26.6 format). + * * @Return: * FreeType error code. 0 means success. */ @@ -3681,6 +3688,7 @@ TT_Vary_Apply_Glyph_Deltas( TT_Face face, FT_UInt glyph_index, FT_Outline* outline, + FT_Vector* unrounded, FT_UInt n_points ) { FT_Error error; @@ -4068,6 +4076,11 @@ for ( i = 0; i < n_points; i++ ) { + unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x ) + + FT_fixedToFdot6( point_deltas_x[i] ); + unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y ) + + FT_fixedToFdot6( point_deltas_y[i] ); + outline->points[i].x += FT_fixedToInt( point_deltas_x[i] ); outline->points[i].y += FT_fixedToInt( point_deltas_y[i] ); } diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h index 7e8d9768a..07c99b640 100644 --- a/src/truetype/ttgxvar.h +++ b/src/truetype/ttgxvar.h @@ -416,6 +416,7 @@ FT_BEGIN_HEADER TT_Vary_Apply_Glyph_Deltas( TT_Face face, FT_UInt glyph_index, FT_Outline* outline, + FT_Vector* unrounded, FT_UInt n_points ); FT_LOCAL( FT_Error )