/***************************************************************************/ /* */ /* ftglyph.c */ /* */ /* FreeType convenience functions to handle glyphs (body). */ /* */ /* Copyright 1996-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. */ /* */ /***************************************************************************/ /*************************************************************************/ /* */ /* This file contains the definition of several convenience functions */ /* that can be used by client applications to easily retrieve glyph */ /* bitmaps and outlines from a given face. */ /* */ /* These functions should be optional if you are writing a font server */ /* or text layout engine on top of FreeType. However, they are pretty */ /* handy for many other simple uses of the library. */ /* */ /*************************************************************************/ #include #include /*************************************************************************/ /* */ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ /* messages during execution. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_glyph /* a helper function to avoid duplication of code */ static void ft_prepare_glyph( FT_Glyph glyph, FT_Face face, FT_Bool vertical ) { FT_Glyph_Metrics* metrics = &face->glyph->metrics; glyph->memory = face->memory; glyph->width = metrics->width; glyph->height = metrics->height; if ( vertical ) { glyph->bearingX = metrics->vertBearingX; glyph->bearingY = metrics->vertBearingY; glyph->advance = metrics->vertAdvance; } else { glyph->bearingX = metrics->horiBearingX; glyph->bearingY = metrics->horiBearingY; glyph->advance = metrics->horiAdvance; } } /*************************************************************************/ /* */ /* */ /* FT_Get_Glyph_Bitmap */ /* */ /* */ /* A function used to directly return a monochrome bitmap glyph image */ /* from a face. */ /* */ /* */ /* face :: A handle to source face object. */ /* glyph_index :: A glyph index into the face. */ /* load_flags :: Load flags (see FT_LOAD_FLAG_XXXX constants). */ /* grays :: The number of gray levels for anti-aliased bitmaps. */ /* Set it to 0 if you want to render a monochrome */ /* bitmap. */ /* origin :: A pointer to the origin's position. Set it to 0 */ /* if the current transform is the identity. */ /* */ /* */ /* abitglyph :: A pointer to the new bitmap glyph. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* If the font contains glyph outlines, these will be automatically */ /* converted to a bitmap according to the value of `grays'. */ /* */ /* If `grays' is set to 0, the result is a 1-bit monochrome bitmap */ /* otherwise, it is an 8-bit gray-level bitmap. */ /* */ /* The number of gray levels in the result anti-aliased bitmap might */ /* not be `grays', depending on the current scan-converter */ /* implementation. */ /* */ /* Note that it is not possible to generate 8-bit monochrome bitmaps */ /* with this function. Rather, use FT_Get_Glyph_Outline(), then */ /* FT_Glyph_Render_Outline(), and provide your own span callbacks. */ /* */ /* If the face doesn't contain scalable outlines, this function will */ /* fail if the current transformation is not the identity, or if the */ /* glyph origin's phase to the pixel grid is not 0 in both */ /* directions! */ /* */ FT_EXPORT_FUNC( FT_Error ) FT_Get_Glyph_Bitmap( FT_Face face, FT_UInt glyph_index, FT_UInt load_flags, FT_Int grays, FT_Vector* origin, FT_BitmapGlyph* abitglyph ) { FT_Error error; FT_Memory memory; FT_BitmapGlyph bitglyph; FT_Glyph glyph; FT_Pos origin_x = 0; FT_Pos origin_y = 0; if ( !face ) return FT_Err_Invalid_Face_Handle; if ( !abitglyph ) return FT_Err_Invalid_Argument; *abitglyph = 0; if ( origin ) { origin_x = origin->x & 63; origin_y = origin->y & 63; } /* check arguments whether the face's format is not scalable */ if ( !( face->face_flags & FT_FACE_FLAG_SCALABLE ) && face->transform_flags ) { /* we can't transform bitmaps, so return an error */ error = FT_Err_Unimplemented_Feature; goto Exit; } /* check that NO_SCALE and NO_RECURSE are not set */ if ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE ) ) { error = FT_Err_Invalid_Argument; goto Exit; } /* disable embedded bitmaps for transformed images */ if ( face->face_flags & FT_FACE_FLAG_SCALABLE && face->transform_flags ) load_flags |= FT_LOAD_NO_BITMAP; error = FT_Load_Glyph( face, glyph_index, load_flags ); if ( error ) goto Exit; /* now, handle bitmap and outline glyph images */ memory = face->memory; switch ( face->glyph->format ) { case ft_glyph_format_bitmap: { FT_Long size; FT_Bitmap* source; if ( ALLOC( bitglyph, sizeof ( *bitglyph ) ) ) goto Exit; glyph = (FT_Glyph)bitglyph; glyph->glyph_type = ft_glyph_type_bitmap; ft_prepare_glyph( glyph, face, 0 ); source = &face->glyph->bitmap; size = source->rows * source->pitch; if ( size < 0 ) size = -size; bitglyph->bitmap = *source; if ( ALLOC( bitglyph->bitmap.buffer, size ) ) goto Fail; /* copy the content of the source glyph */ MEM_Copy( bitglyph->bitmap.buffer, source->buffer, size ); } break; case ft_glyph_format_outline: { FT_BBox cbox; FT_Int width, height, pitch; FT_Long size; /* transform the outline -- note that the original metrics are NOT */ /* transformed by this, only the outline points themselves... */ FT_Outline_Translate( &face->glyph->outline, origin_x, origin_y ); /* compute the size in pixels of the outline */ FT_Outline_Get_CBox( &face->glyph->outline, &cbox ); cbox.xMin &= -64; cbox.yMin &= -64; cbox.xMax = ( cbox.xMax + 63 ) & -64; cbox.yMax = ( cbox.yMax + 63 ) & -64; width = ( cbox.xMax - cbox.xMin ) >> 6; height = ( cbox.yMax - cbox.yMin ) >> 6; /* allocate the pixel buffer for the glyph bitmap */ if ( grays ) /* some raster implementation need this */ pitch = ( width + 3 ) & -4; else pitch = ( width + 7 ) >> 3; size = pitch * height; if ( ALLOC( bitglyph, sizeof ( *bitglyph ) ) ) goto Exit; glyph = (FT_Glyph)bitglyph; glyph->glyph_type = ft_glyph_type_bitmap; ft_prepare_glyph( glyph, face, 0 ); if ( ALLOC( bitglyph->bitmap.buffer, size ) ) goto Fail; bitglyph->bitmap.width = width; bitglyph->bitmap.rows = height; bitglyph->bitmap.pitch = pitch; bitglyph->bitmap.pixel_mode = grays ? ft_pixel_mode_grays : ft_pixel_mode_mono; bitglyph->bitmap.num_grays = (short)grays; bitglyph->left = cbox.xMin >> 6; bitglyph->top = cbox.yMax >> 6; /* render the monochrome outline into the target buffer */ FT_Outline_Translate( &face->glyph->outline, -cbox.xMin, -cbox.yMin ); error = FT_Outline_Get_Bitmap( face->driver->root.library, &face->glyph->outline, &bitglyph->bitmap ); if ( error ) { FREE( bitglyph->bitmap.buffer ); goto Fail; } } break; default: error = FT_Err_Invalid_Glyph_Index; goto Exit; } *abitglyph = bitglyph; Exit: return error; Fail: FREE( glyph ); goto Exit; } /*************************************************************************/ /* */ /* */ /* FT_Get_Glyph_Outline */ /* */ /* */ /* A function used to directly return a bitmap glyph image from a */ /* face. This is faster than calling FT_Load_Glyph() + */ /* FT_Get_Outline_Bitmap(). */ /* */ /* */ /* face :: A handle to the source face object. */ /* glyph_index :: A glyph index into face */ /* load_flags :: Load flags (see FT_LOAD_FLAG_XXXX constants). */ /* */ /* */ /* vecglyph :: A pointer to the new outline glyph. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* This function will fail if the load flags FT_LOAD_NO_OUTLINE and */ /* FT_LOAD_NO_RECURSE are set. */ /* */ FT_EXPORT_FUNC( FT_Error ) FT_Get_Glyph_Outline( FT_Face face, FT_UInt glyph_index, FT_UInt load_flags, FT_OutlineGlyph* vecglyph ) { FT_Error error; FT_Memory memory; FT_OutlineGlyph glyph; /* test for valid `face' delayed to FT_Load_Glyph() */ if ( !vecglyph ) return FT_Err_Invalid_Argument; *vecglyph = 0; /* check that RENDER and NO_RECURSE are not set */ if ( load_flags & ( FT_LOAD_RENDER | FT_LOAD_NO_RECURSE ) ) { error = FT_Err_Invalid_Argument; goto Exit; } /* disable the loading of embedded bitmaps */ load_flags |= FT_LOAD_NO_BITMAP; error = FT_Load_Glyph( face, glyph_index, load_flags ); if ( error ) goto Exit; /* check that we really loaded an outline */ if ( face->glyph->format != ft_glyph_format_outline ) { error = FT_Err_Invalid_Glyph_Index; goto Exit; } /* now, create a new outline glyph and copy everything */ memory = face->memory; if ( ALLOC( glyph, sizeof ( *glyph ) ) ) goto Exit; ft_prepare_glyph( (FT_Glyph)glyph, face, 0 ); glyph->metrics.glyph_type = ft_glyph_type_outline; error = FT_Outline_New( face->driver->root.library, face->glyph->outline.n_points, face->glyph->outline.n_contours, &glyph->outline ); if ( !error ) error = FT_Outline_Copy( &face->glyph->outline, &glyph->outline ); if ( error ) goto Fail; *vecglyph = glyph; Exit: return error; Fail: FREE( glyph ); goto Exit; } /*************************************************************************/ /* */ /* */ /* FT_Done_Glyph */ /* */ /* */ /* Destroys a given glyph. */ /* */ /* */ /* glyph :: A handle to the target glyph object. */ /* */ FT_EXPORT_FUNC( void ) FT_Done_Glyph( FT_Glyph glyph ) { if ( glyph ) { FT_Memory memory = glyph->memory; if ( glyph->glyph_type == ft_glyph_type_bitmap ) { FT_BitmapGlyph bit = (FT_BitmapGlyph)glyph; FREE( bit->bitmap.buffer ); } else if ( glyph->glyph_type == ft_glyph_type_outline ) { FT_OutlineGlyph out = (FT_OutlineGlyph)glyph; if ( out->outline.flags & ft_outline_owner ) { FREE( out->outline.points ); FREE( out->outline.contours ); FREE( out->outline.tags ); } } FREE( glyph ); } } /*************************************************************************/ /* */ /* */ /* FT_Glyph_Get_Box */ /* */ /* */ /* Returns the glyph image's bounding box in pixels. */ /* */ /* */ /* glyph :: A handle to the target glyph object. */ /* */ /* */ /* box :: The glyph bounding box. Coordinates are expressed in */ /* _integer_ pixels, with exclusive maximal bounding values. */ /* */ /* */ /* Coordinates are relative to the glyph origin, using the Y-upwards */ /* convention. */ /* */ /* The width of the box in pixels is `box.xMax-box.xMin'; the height */ /* is `box.yMax-box.yMin'. */ /* */ FT_EXPORT_FUNC( void ) FT_Glyph_Get_Box( FT_Glyph glyph, FT_BBox* box ) { if ( !box ) return; box->xMin = box->xMax = 0; box->yMin = box->yMax = 0; if ( glyph ) switch ( glyph->glyph_type ) { case ft_glyph_type_bitmap: { FT_BitmapGlyph bit = (FT_BitmapGlyph)glyph; box->xMin = bit->left; box->xMax = box->xMin + bit->bitmap.width; box->yMax = bit->top; box->yMin = box->yMax - bit->bitmap.rows; } break; case ft_glyph_type_outline: { FT_OutlineGlyph out = (FT_OutlineGlyph)glyph; FT_Outline_Get_CBox( &out->outline, box ); box->xMin >>= 6; box->yMin >>= 6; box->xMax = ( box->xMax + 63 ) >> 6; box->yMax = ( box->yMax + 63 ) >> 6; } break; default: ; } } /*************************************************************************/ /*************************************************************************/ /**** ****/ /**** EXPERIMENTAL EMBOLDENING/OUTLINING SUPPORT ****/ /**** ****/ /*************************************************************************/ /*************************************************************************/ #if 0 /* Compute the norm of a vector */ #ifdef FT_CONFIG_OPTION_OLD_CALCS 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 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 */ return ft_test_extrema( outline, indices.xMin ) || ft_test_extrema( outline, indices.yMin ) || ft_test_extrema( outline, indices.xMax ) || ft_test_extrema( outline, indices.yMax ) || 1; /* this is an empty glyph? */ } static FT_Error ft_embolden( FT_Face original, FT_Outline* outline, FT_Pos* advance ) { FT_Vector u, v; FT_Vector* points; FT_Vector cur, prev, next; FT_Pos distance; int c, n, first, orientation; UNUSED( advance ); /* compute control distance */ distance = FT_MulFix( original->em_size / 60, original->size->metrics.y_scale ); orientation = ft_get_orientation( &original->glyph->outline ); points = original->glyph->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->flags[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; } #endif /* 0 -- EXPERIMENTAL STUFF! */ /* END */