freetype2/src/base/ftglyph.c

807 lines
27 KiB
C

/***************************************************************************/
/* */
/* 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 <freetype/ftglyph.h>
#include <freetype/internal/ftobjs.h>
/*************************************************************************/
/* */
/* 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
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;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* FT_Get_Glyph_Bitmap */
/* */
/* <Description> */
/* A function used to directly return a monochrome bitmap glyph image */
/* from a face. */
/* */
/* <Input> */
/* 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. */
/* */
/* <Output> */
/* abitglyph :: A pointer to the new bitmap glyph. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* 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;
}
/*************************************************************************/
/* */
/* <Function> */
/* FT_Get_Glyph_Outline */
/* */
/* <Description> */
/* 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(). */
/* */
/* <Input> */
/* 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). */
/* */
/* <Output> */
/* vecglyph :: A pointer to the new outline glyph. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
/* <Note> */
/* 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;
}
/*************************************************************************/
/* */
/* <Function> */
/* FT_Done_Glyph */
/* */
/* <Description> */
/* Destroys a given glyph. */
/* */
/* <Input> */
/* 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 );
}
}
/*************************************************************************/
/* */
/* <Function> */
/* FT_Glyph_Get_Box */
/* */
/* <Description> */
/* Returns the glyph image's bounding box in pixels. */
/* */
/* <Input> */
/* glyph :: A handle to the target glyph object. */
/* */
/* <Output> */
/* box :: The glyph bounding box. Coordinates are expressed in */
/* _integer_ pixels, with exclusive maximal bounding values. */
/* */
/* <Note> */
/* 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're 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's better to re-compute it directly (it seems that this flag */
/* isn't correctly set for some weird composite glyphs for now). */
/* */
/* 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 */