/***************************************************************************/ /* */ /* ftsynth.c */ /* */ /* FreeType synthesizing code for emboldening and slanting (body). */ /* */ /* Copyright 2000 by */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ #include #include FT_INTERNAL_OBJECTS_H #include FT_OUTLINE_H #include FT_SYNTHESIS_H #define FT_BOLD_THRESHOLD 0x0100 /*************************************************************************/ /*************************************************************************/ /**** ****/ /**** EXPERIMENTAL OBLIQUING SUPPORT ****/ /**** ****/ /*************************************************************************/ /*************************************************************************/ FT_EXPORT_DEF( FT_Error ) FT_Outline_Oblique( FT_GlyphSlot original, FT_Outline* outline, FT_Pos* advance ) { FT_Matrix transform; FT_UNUSED( original ); /* we don't touch the advance width */ FT_UNUSED( advance ); /* For italic, simply apply a shear transform, with an angle */ /* of about 12 degrees. */ transform.xx = 0x10000L; transform.yx = 0x00000L; transform.xy = 0x06000L; transform.yy = 0x10000L; FT_Outline_Transform( outline, &transform ); return 0; } /*************************************************************************/ /*************************************************************************/ /**** ****/ /**** EXPERIMENTAL EMBOLDENING/OUTLINING SUPPORT ****/ /**** ****/ /*************************************************************************/ /*************************************************************************/ /* Compute the norm of a vector */ #ifdef FT_CONFIG_OPTION_OLD_CALCS #include static FT_Pos ft_norm( FT_Vector* vec ) { FT_Int64 t1, t2; MUL_64( vec->x, vec->x, t1 ); MUL_64( vec->y, vec->y, t2 ); ADD_64( t1, t2, t1 ); return (FT_Pos)SQRT_64( t1 ); } #else /* FT_CONFIG_OPTION_OLD_CALCS */ static FT_Pos ft_norm( FT_Vector* vec ) { FT_F26Dot6 u, v, d; FT_Int shift; FT_ULong H, L, L2, hi, lo, med; u = vec->x; if ( u < 0 ) u = -u; v = vec->y; if ( v < 0 ) v = -v; if ( u < v ) { d = u; u = v; v = d; } /* check that we are not trying to normalize zero! */ if ( u == 0 ) return 0; /* compute (u*u + v*v) on 64 bits with two 32-bit registers [H:L] */ hi = (FT_ULong)u >> 16; lo = (FT_ULong)u & 0xFFFF; med = hi * lo; H = hi * hi + ( med >> 15 ); med <<= 17; L = lo * lo + med; if ( L < med ) H++; hi = (FT_ULong)v >> 16; lo = (FT_ULong)v & 0xFFFF; med = hi * lo; H += hi * hi + ( med >> 15 ); med <<= 17; L2 = lo * lo + med; if ( L2 < med ) H++; L += L2; if ( L < L2 ) H++; /* if the value is smaller than 32 bits */ shift = 0; if ( H == 0 ) { while ( ( L & 0xC0000000UL ) == 0 ) { L <<= 2; shift++; } return ( FT_Sqrt32( L ) >> shift ); } else { while ( H ) { L = ( L >> 2 ) | ( H << 30 ); H >>= 2; shift++; } return ( FT_Sqrt32( L ) << shift ); } } #endif /* FT_CONFIG_OPTION_OLD_CALCS */ static int ft_test_extrema( FT_Outline* outline, int n ) { FT_Vector *prev, *cur, *next; FT_Pos product; FT_Int c, first, last; /* we need to compute the `previous' and `next' point */ /* for these extrema. */ cur = outline->points + n; prev = cur - 1; next = cur + 1; first = 0; for ( c = 0; c < outline->n_contours; c++ ) { last = outline->contours[c]; if ( n == first ) prev = outline->points + last; if ( n == last ) next = outline->points + first; first = last + 1; } product = FT_MulDiv( cur->x - prev->x, /* in.x */ next->y - cur->y, /* out.y */ 0x40 ) - FT_MulDiv( cur->y - prev->y, /* in.y */ next->x - cur->x, /* out.x */ 0x40 ); if ( product ) product = product > 0 ? 1 : -1; return product; } /* Compute the orientation of path filling. It differs between TrueType */ /* and Type1 formats. We could use the `ft_outline_reverse_fill' flag, */ /* but it is better to re-compute it directly (it seems that this flag */ /* isn't correctly set for some weird composite glyphs currently). */ /* */ /* We do this by computing bounding box points, and computing their */ /* curvature. */ /* */ /* The function returns either 1 or -1. */ /* */ static int ft_get_orientation( FT_Outline* outline ) { FT_BBox box; FT_BBox indices; int n, last; indices.xMin = -1; indices.yMin = -1; indices.xMax = -1; indices.yMax = -1; box.xMin = box.yMin = 32767; box.xMax = box.yMax = -32768; /* is it empty ? */ if ( outline->n_contours < 1 ) return 1; last = outline->contours[outline->n_contours - 1]; for ( n = 0; n <= last; n++ ) { FT_Pos x, y; x = outline->points[n].x; if ( x < box.xMin ) { box.xMin = x; indices.xMin = n; } if ( x > box.xMax ) { box.xMax = x; indices.xMax = n; } y = outline->points[n].y; if ( y < box.yMin ) { box.yMin = y; indices.yMin = n; } if ( y > box.yMax ) { box.yMax = y; indices.yMax = n; } } /* test orientation of the xmin */ n = ft_test_extrema( outline, indices.xMin ); if ( n ) goto Exit; n = ft_test_extrema( outline, indices.yMin ); if ( n ) goto Exit; n = ft_test_extrema( outline, indices.xMax ); if ( n ) goto Exit; n = ft_test_extrema( outline, indices.yMax ); if ( !n ) n = 1; Exit: return n; } FT_EXPORT_DEF( FT_Error ) FT_Outline_Embolden( FT_GlyphSlot original, FT_Outline* outline, FT_Pos* advance ) { FT_Vector u, v; FT_Vector* points; FT_Vector cur, prev, next; FT_Pos distance; FT_Face face = FT_SLOT_FACE( original ); int c, n, first, orientation; FT_UNUSED( advance ); /* compute control distance */ distance = FT_MulFix( face->units_per_EM / 60, face->size->metrics.y_scale ); orientation = ft_get_orientation( &original->outline ); points = original->outline.points; first = 0; for ( c = 0; c < outline->n_contours; c++ ) { int last = outline->contours[c]; prev = points[last]; for ( n = first; n <= last; n++ ) { FT_Pos norm, delta, d; FT_Vector in, out; cur = points[n]; if ( n < last ) next = points[n + 1]; else next = points[first]; /* compute the in and out vectors */ in.x = cur.x - prev.x; in.y = cur.y - prev.y; out.x = next.x - cur.x; out.y = next.y - cur.y; /* compute U and V */ norm = ft_norm( &in ); u.x = orientation * FT_DivFix( in.y, norm ); u.y = orientation * -FT_DivFix( in.x, norm ); norm = ft_norm( &out ); v.x = orientation * FT_DivFix( out.y, norm ); v.y = orientation * -FT_DivFix( out.x, norm ); d = distance; if ( ( outline->tags[n] & FT_Curve_Tag_On ) == 0 ) d *= 2; /* Check discriminant for parallel vectors */ delta = FT_MulFix( u.x, v.y ) - FT_MulFix( u.y, v.x ); if ( delta > FT_BOLD_THRESHOLD || delta < -FT_BOLD_THRESHOLD ) { /* Move point -- compute A and B */ FT_Pos x, y, A, B; A = d + FT_MulFix( cur.x, u.x ) + FT_MulFix( cur.y, u.y ); B = d + FT_MulFix( cur.x, v.x ) + FT_MulFix( cur.y, v.y ); x = FT_MulFix( A, v.y ) - FT_MulFix( B, u.y ); y = FT_MulFix( B, u.x ) - FT_MulFix( A, v.x ); outline->points[n].x = distance + FT_DivFix( x, delta ); outline->points[n].y = distance + FT_DivFix( y, delta ); } else { /* Vectors are nearly parallel */ FT_Pos x, y; x = distance + cur.x + FT_MulFix( d, u.x + v.x ) / 2; y = distance + cur.y + FT_MulFix( d, u.y + v.y ) / 2; outline->points[n].x = x; outline->points[n].y = y; } prev = cur; } first = last + 1; } if ( advance ) *advance = ( *advance + distance * 4 ) & -64; return 0; } /* END */