freetype2_raster_san_cov_fuzzer Coverage Report
Touched Files
Not Touched Files
include/freetype/internal/ftcalc.h |
src/autofit/afangles.c |
src/autofit/afcjk.c |
src/autofit/afdummy.c |
src/autofit/afglobal.c |
src/autofit/afhints.c |
src/autofit/afindic.c |
src/autofit/aflatin.c |
src/autofit/afloader.c |
src/autofit/afshaper.c |
src/autofit/afwarp.c |
src/base/ftadvanc.c |
src/base/ftbitmap.c |
src/base/ftgloadr.c |
src/base/fthash.c |
src/base/ftrfork.c |
src/base/ftsnames.c |
src/base/ftstream.c |
src/base/fttrigon.c |
src/bdf/bdfdrivr.c |
src/bdf/bdflib.c |
src/bzip2/ftbzip2.c |
src/cff/cf2arrst.c |
src/cff/cf2blues.c |
src/cff/cf2error.c |
src/cff/cf2font.c |
src/cff/cf2ft.c |
src/cff/cf2hints.c |
src/cff/cf2intrp.c |
src/cff/cf2read.c |
src/cff/cf2stack.c |
src/cff/cffcmap.c |
src/cff/cffdrivr.c |
src/cff/cffgload.c |
src/cff/cffload.c |
src/cff/cffparse.c |
src/cid/cidgload.c |
src/cid/cidload.c |
src/cid/cidparse.c |
src/cid/cidriver.c |
src/gzip/ftgzip.c |
src/lzw/ftlzw.c |
src/lzw/ftzopen.c |
src/pcf/pcfdrivr.c |
src/pcf/pcfread.c |
src/pcf/pcfutil.c |
src/pfr/pfrcmap.c |
src/pfr/pfrdrivr.c |
src/pfr/pfrgload.c |
src/pfr/pfrload.c |
src/pfr/pfrobjs.c |
src/pfr/pfrsbit.c |
src/psaux/afmparse.c |
src/psaux/psconv.c |
src/psaux/psobjs.c |
src/psaux/t1cmap.c |
src/psaux/t1decode.c |
src/pshinter/pshalgo.c |
src/pshinter/pshglob.c |
src/psnames/psmodule.c |
src/psnames/pstables.h |
src/sfnt/pngshim.c |
src/sfnt/sfdriver.c |
src/sfnt/sfobjs.c |
src/sfnt/ttbdf.c |
src/sfnt/ttcmap.c |
src/sfnt/ttkern.c |
src/sfnt/ttload.c |
src/sfnt/ttmtx.c |
src/sfnt/ttpost.c |
src/sfnt/ttsbit.c |
src/truetype/ttdriver.c |
src/truetype/ttgload.c |
src/truetype/ttgxvar.c |
src/truetype/ttinterp.c |
src/truetype/ttpload.c |
src/type1/t1afm.c |
src/type1/t1driver.c |
src/type1/t1gload.c |
src/type1/t1load.c |
src/type1/t1parse.c |
src/type42/t42drivr.c |
src/type42/t42parse.c |
src/winfonts/winfnt.c |
src/autofit/afmodule.c
Not Covered Functions
/***************************************************************************/
/* */
/* afmodule.c */
/* */
/* Auto-fitter module implementation (body). */
/* */
/* Copyright 2003-2016 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 "afglobal.h"
#include "afmodule.h"
#include "afloader.h"
#include "aferrors.h"
#include "afpic.h"
#ifdef FT_DEBUG_AUTOFIT
#ifndef FT_MAKE_OPTION_SINGLE_OBJECT
#ifdef __cplusplus
extern "C" {
#endif
extern void
af_glyph_hints_dump_segments( AF_GlyphHints hints,
FT_Bool to_stdout );
extern void
af_glyph_hints_dump_points( AF_GlyphHints hints,
FT_Bool to_stdout );
extern void
af_glyph_hints_dump_edges( AF_GlyphHints hints,
FT_Bool to_stdout );
#ifdef __cplusplus
}
#endif
#endif
int _af_debug_disable_horz_hints;
int _af_debug_disable_vert_hints;
int _af_debug_disable_blue_hints;
/* we use a global object instead of a local one for debugging */
AF_GlyphHintsRec _af_debug_hints_rec[1];
void* _af_debug_hints = _af_debug_hints_rec;
#endif
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_AUTOHINTER_H
#include FT_SERVICE_PROPERTIES_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_afmodule
static FT_Error
af_property_get_face_globals( FT_Face face,
AF_FaceGlobals* aglobals,
AF_Module module )
{
FT_Error error = FT_Err_Ok;
AF_FaceGlobals globals;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
globals = (AF_FaceGlobals)face->autohint.data;
if ( !globals )
{
/* trigger computation of the global style data */
/* in case it hasn't been done yet */
error = af_face_globals_new( face, &globals, module );
if ( !error )
{
face->autohint.data =
(FT_Pointer)globals;
face->autohint.finalizer =
(FT_Generic_Finalizer)af_face_globals_free;
}
}
if ( !error )
*aglobals = globals;
return error;
}
static FT_Error
af_property_set( FT_Module ft_module,
const char* property_name,
const void* value )
{
FT_Error error = FT_Err_Ok;
AF_Module module = (AF_Module)ft_module;
if ( !ft_strcmp( property_name, "fallback-script" ) )
{
FT_UInt* fallback_script = (FT_UInt*)value;
FT_UInt ss;
/* We translate the fallback script to a fallback style that uses */
/* `fallback-script' as its script and `AF_COVERAGE_NONE' as its */
/* coverage value. */
for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ )
{
AF_StyleClass style_class = AF_STYLE_CLASSES_GET[ss];
if ( (FT_UInt)style_class->script == *fallback_script &&
style_class->coverage == AF_COVERAGE_DEFAULT )
{
module->fallback_style = ss;
break;
}
}
if ( !AF_STYLE_CLASSES_GET[ss] )
{
FT_TRACE0(( "af_property_set: Invalid value %d for property `%s'\n",
fallback_script, property_name ));
return FT_THROW( Invalid_Argument );
}
return error;
}
else if ( !ft_strcmp( property_name, "default-script" ) )
{
FT_UInt* default_script = (FT_UInt*)value;
module->default_script = *default_script;
return error;
}
else if ( !ft_strcmp( property_name, "increase-x-height" ) )
{
FT_Prop_IncreaseXHeight* prop = (FT_Prop_IncreaseXHeight*)value;
AF_FaceGlobals globals;
error = af_property_get_face_globals( prop->face, &globals, module );
if ( !error )
globals->increase_x_height = prop->limit;
return error;
}
#ifdef AF_CONFIG_OPTION_USE_WARPER
else if ( !ft_strcmp( property_name, "warping" ) )
{
FT_Bool* warping = (FT_Bool*)value;
module->warping = *warping;
return error;
}
#endif /* AF_CONFIG_OPTION_USE_WARPER */
else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
{
FT_Int* darken_params = (FT_Int*)value;
FT_Int x1 = darken_params[0];
FT_Int y1 = darken_params[1];
FT_Int x2 = darken_params[2];
FT_Int y2 = darken_params[3];
FT_Int x3 = darken_params[4];
FT_Int y3 = darken_params[5];
FT_Int x4 = darken_params[6];
FT_Int y4 = darken_params[7];
if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 ||
y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 ||
x1 > x2 || x2 > x3 || x3 > x4 ||
y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
return FT_THROW( Invalid_Argument );
module->darken_params[0] = x1;
module->darken_params[1] = y1;
module->darken_params[2] = x2;
module->darken_params[3] = y2;
module->darken_params[4] = x3;
module->darken_params[5] = y3;
module->darken_params[6] = x4;
module->darken_params[7] = y4;
return error;
}
else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
{
FT_Bool* no_stem_darkening = (FT_Bool*)value;
module->no_stem_darkening = *no_stem_darkening;
return error;
}
FT_TRACE0(( "af_property_set: missing property `%s'\n",
property_name ));
return FT_THROW( Missing_Property );
}
static FT_Error
af_property_get( FT_Module ft_module,
const char* property_name,
void* value )
{
FT_Error error = FT_Err_Ok;
AF_Module module = (AF_Module)ft_module;
FT_UInt fallback_style = module->fallback_style;
FT_UInt default_script = module->default_script;
#ifdef AF_CONFIG_OPTION_USE_WARPER
FT_Bool warping = module->warping;
#endif
if ( !ft_strcmp( property_name, "glyph-to-script-map" ) )
{
FT_Prop_GlyphToScriptMap* prop = (FT_Prop_GlyphToScriptMap*)value;
AF_FaceGlobals globals;
error = af_property_get_face_globals( prop->face, &globals, module );
if ( !error )
prop->map = globals->glyph_styles;
return error;
}
else if ( !ft_strcmp( property_name, "fallback-script" ) )
{
FT_UInt* val = (FT_UInt*)value;
AF_StyleClass style_class = AF_STYLE_CLASSES_GET[fallback_style];
*val = style_class->script;
return error;
}
else if ( !ft_strcmp( property_name, "default-script" ) )
{
FT_UInt* val = (FT_UInt*)value;
*val = default_script;
return error;
}
else if ( !ft_strcmp( property_name, "increase-x-height" ) )
{
FT_Prop_IncreaseXHeight* prop = (FT_Prop_IncreaseXHeight*)value;
AF_FaceGlobals globals;
error = af_property_get_face_globals( prop->face, &globals, module );
if ( !error )
prop->limit = globals->increase_x_height;
return error;
}
#ifdef AF_CONFIG_OPTION_USE_WARPER
else if ( !ft_strcmp( property_name, "warping" ) )
{
FT_Bool* val = (FT_Bool*)value;
*val = warping;
return error;
}
#endif /* AF_CONFIG_OPTION_USE_WARPER */
else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
{
FT_Int* darken_params = module->darken_params;
FT_Int* val = (FT_Int*)value;
val[0] = darken_params[0];
val[1] = darken_params[1];
val[2] = darken_params[2];
val[3] = darken_params[3];
val[4] = darken_params[4];
val[5] = darken_params[5];
val[6] = darken_params[6];
val[7] = darken_params[7];
return error;
}
else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
{
FT_Bool no_stem_darkening = module->no_stem_darkening;
FT_Bool* val = (FT_Bool*)value;
*val = no_stem_darkening;
return error;
}
FT_TRACE0(( "af_property_get: missing property `%s'\n",
property_name ));
return FT_THROW( Missing_Property );
}
FT_DEFINE_SERVICE_PROPERTIESREC(
af_service_properties,
(FT_Properties_SetFunc)af_property_set, /* set_property */
(FT_Properties_GetFunc)af_property_get ) /* get_property */
FT_DEFINE_SERVICEDESCREC1(
af_services,
FT_SERVICE_ID_PROPERTIES, &AF_SERVICE_PROPERTIES_GET )
FT_CALLBACK_DEF( FT_Module_Interface )
af_get_interface( FT_Module module,
const char* module_interface )
{
/* AF_SERVICES_GET dereferences `library' in PIC mode */
#ifdef FT_CONFIG_OPTION_PIC
FT_Library library;
if ( !module )
return NULL;
library = module->library;
if ( !library )
return NULL;
#else
FT_UNUSED( module );
#endif
return ft_service_list_lookup( AF_SERVICES_GET, module_interface );
}
FT_CALLBACK_DEF( FT_Error )
af_autofitter_init( FT_Module ft_module ) /* AF_Module */
{
AF_Module module = (AF_Module)ft_module;
module->fallback_style = AF_STYLE_FALLBACK;
module->default_script = AF_SCRIPT_DEFAULT;
#ifdef AF_CONFIG_OPTION_USE_WARPER
module->warping = 0;
#endif
module->no_stem_darkening = TRUE;
module->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
module->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
module->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
module->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
module->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
module->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
return FT_Err_Ok;
}
FT_CALLBACK_DEF( void )
af_autofitter_done( FT_Module ft_module ) /* AF_Module */
{
FT_UNUSED( ft_module );
#ifdef FT_DEBUG_AUTOFIT
if ( _af_debug_hints_rec->memory )
af_glyph_hints_done( _af_debug_hints_rec );
#endif
}
FT_CALLBACK_DEF( FT_Error )
af_autofitter_load_glyph( AF_Module module,
FT_GlyphSlot slot,
FT_Size size,
FT_UInt glyph_index,
FT_Int32 load_flags )
{
FT_Error error = FT_Err_Ok;
FT_Memory memory = module->root.library->memory;
#ifdef FT_DEBUG_AUTOFIT
/* in debug mode, we use a global object that survives this routine */
AF_GlyphHints hints = _af_debug_hints_rec;
AF_LoaderRec loader[1];
FT_UNUSED( size );
if ( hints->memory )
af_glyph_hints_done( hints );
af_glyph_hints_init( hints, memory );
af_loader_init( loader, hints );
error = af_loader_load_glyph( loader, module, slot->face,
glyph_index, load_flags );
af_glyph_hints_dump_points( hints, 0 );
af_glyph_hints_dump_segments( hints, 0 );
af_glyph_hints_dump_edges( hints, 0 );
af_loader_done( loader );
return error;
#else /* !FT_DEBUG_AUTOFIT */
AF_GlyphHintsRec hints[1];
AF_LoaderRec loader[1];
FT_UNUSED( size );
af_glyph_hints_init( hints, memory );
af_loader_init( loader, hints );
error = af_loader_load_glyph( loader, module, slot->face,
glyph_index, load_flags );
af_loader_done( loader );
af_glyph_hints_done( hints );
return error;
#endif /* !FT_DEBUG_AUTOFIT */
}
FT_DEFINE_AUTOHINTER_INTERFACE(
af_autofitter_interface,
NULL, /* reset_face */
NULL, /* get_global_hints */
NULL, /* done_global_hints */
(FT_AutoHinter_GlyphLoadFunc)af_autofitter_load_glyph ) /* load_glyph */
FT_DEFINE_MODULE(
autofit_module_class,
FT_MODULE_HINTER,
sizeof ( AF_ModuleRec ),
"autofitter",
0x10000L, /* version 1.0 of the autofitter */
0x20000L, /* requires FreeType 2.0 or above */
(const void*)&AF_INTERFACE_GET,
(FT_Module_Constructor)af_autofitter_init,
(FT_Module_Destructor) af_autofitter_done,
(FT_Module_Requester) af_get_interface )
/* END */
src/base/ftcalc.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftcalc.c */
/* */
/* Arithmetic computations (body). */
/* */
/* Copyright 1996-2016 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. */
/* */
/***************************************************************************/
/*************************************************************************/
/* */
/* Support for 1-complement arithmetic has been totally dropped in this */
/* release. You can still write your own code if you need it. */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* Implementing basic computation routines. */
/* */
/* FT_MulDiv(), FT_MulFix(), FT_DivFix(), FT_RoundFix(), FT_CeilFix(), */
/* and FT_FloorFix() are declared in freetype.h. */
/* */
/*************************************************************************/
#include <ft2build.h>
#include FT_GLYPH_H
#include FT_TRIGONOMETRY_H
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_OBJECTS_H
#ifdef FT_MULFIX_ASSEMBLER
#undef FT_MulFix
#endif
/* we need to emulate a 64-bit data type if a real one isn't available */
#ifndef FT_LONG64
typedef struct FT_Int64_
{
FT_UInt32 lo;
FT_UInt32 hi;
} FT_Int64;
#endif /* !FT_LONG64 */
/*************************************************************************/
/* */
/* 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_calc
/* transfer sign leaving a positive number */
#define FT_MOVE_SIGN( x, s ) \
FT_BEGIN_STMNT \
if ( x < 0 ) \
{ \
x = -x; \
s = -s; \
} \
FT_END_STMNT
/* The following three functions are available regardless of whether */
/* FT_LONG64 is defined. */
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Fixed )
FT_RoundFix( FT_Fixed a )
{
return ( a + 0x8000L - ( a < 0 ) ) & ~0xFFFFL;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Fixed )
FT_CeilFix( FT_Fixed a )
{
return ( a + 0xFFFFL ) & ~0xFFFFL;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Fixed )
FT_FloorFix( FT_Fixed a )
{
return a & ~0xFFFFL;
}
#ifndef FT_MSB
FT_BASE_DEF ( FT_Int )
FT_MSB( FT_UInt32 z )
{
FT_Int shift = 0;
/* determine msb bit index in `shift' */
if ( z & 0xFFFF0000UL )
{
z >>= 16;
shift += 16;
}
if ( z & 0x0000FF00UL )
{
z >>= 8;
shift += 8;
}
if ( z & 0x000000F0UL )
{
z >>= 4;
shift += 4;
}
if ( z & 0x0000000CUL )
{
z >>= 2;
shift += 2;
}
if ( z & 0x00000002UL )
{
/* z >>= 1; */
shift += 1;
}
return shift;
}
#endif /* !FT_MSB */
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_Fixed )
FT_Hypot( FT_Fixed x,
FT_Fixed y )
{
FT_Vector v;
v.x = x;
v.y = y;
return FT_Vector_Length( &v );
}
#ifdef FT_LONG64
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_MulDiv( FT_Long a_,
FT_Long b_,
FT_Long c_ )
{
FT_Int s = 1;
FT_UInt64 a, b, c, d;
FT_Long d_;
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
FT_MOVE_SIGN( c_, s );
a = (FT_UInt64)a_;
b = (FT_UInt64)b_;
c = (FT_UInt64)c_;
d = c > 0 ? ( a * b + ( c >> 1 ) ) / c
: 0x7FFFFFFFUL;
d_ = (FT_Long)d;
return s < 0 ? -d_ : d_;
}
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_Long )
FT_MulDiv_No_Round( FT_Long a_,
FT_Long b_,
FT_Long c_ )
{
FT_Int s = 1;
FT_UInt64 a, b, c, d;
FT_Long d_;
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
FT_MOVE_SIGN( c_, s );
a = (FT_UInt64)a_;
b = (FT_UInt64)b_;
c = (FT_UInt64)c_;
d = c > 0 ? a * b / c
: 0x7FFFFFFFUL;
d_ = (FT_Long)d;
return s < 0 ? -d_ : d_;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_MulFix( FT_Long a_,
FT_Long b_ )
{
#ifdef FT_MULFIX_ASSEMBLER
return FT_MULFIX_ASSEMBLER( (FT_Int32)a_, (FT_Int32)b_ );
#else
FT_Int64 ab = (FT_Int64)a_ * (FT_Int64)b_;
/* this requires arithmetic right shift of signed numbers */
return (FT_Long)( ( ab + 0x8000L - ( ab < 0 ) ) >> 16 );
#endif /* FT_MULFIX_ASSEMBLER */
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_DivFix( FT_Long a_,
FT_Long b_ )
{
FT_Int s = 1;
FT_UInt64 a, b, q;
FT_Long q_;
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
a = (FT_UInt64)a_;
b = (FT_UInt64)b_;
q = b > 0 ? ( ( a << 16 ) + ( b >> 1 ) ) / b
: 0x7FFFFFFFUL;
q_ = (FT_Long)q;
return s < 0 ? -q_ : q_;
}
#else /* !FT_LONG64 */
static void
ft_multo64( FT_UInt32 x,
FT_UInt32 y,
FT_Int64 *z )
{
FT_UInt32 lo1, hi1, lo2, hi2, lo, hi, i1, i2;
lo1 = x & 0x0000FFFFU; hi1 = x >> 16;
lo2 = y & 0x0000FFFFU; hi2 = y >> 16;
lo = lo1 * lo2;
i1 = lo1 * hi2;
i2 = lo2 * hi1;
hi = hi1 * hi2;
/* Check carry overflow of i1 + i2 */
i1 += i2;
hi += (FT_UInt32)( i1 < i2 ) << 16;
hi += i1 >> 16;
i1 = i1 << 16;
/* Check carry overflow of i1 + lo */
lo += i1;
hi += ( lo < i1 );
z->lo = lo;
z->hi = hi;
}
static FT_UInt32
ft_div64by32( FT_UInt32 hi,
FT_UInt32 lo,
FT_UInt32 y )
{
FT_UInt32 r, q;
FT_Int i;
if ( hi >= y )
return (FT_UInt32)0x7FFFFFFFL;
/* We shift as many bits as we can into the high register, perform */
/* 32-bit division with modulo there, then work through the remaining */
/* bits with long division. This optimization is especially noticeable */
/* for smaller dividends that barely use the high register. */
i = 31 - FT_MSB( hi );
r = ( hi << i ) | ( lo >> ( 32 - i ) ); lo <<= i; /* left 64-bit shift */
q = r / y;
r -= q * y; /* remainder */
i = 32 - i; /* bits remaining in low register */
do
{
q <<= 1;
r = ( r << 1 ) | ( lo >> 31 ); lo <<= 1;
if ( r >= y )
{
r -= y;
q |= 1;
}
} while ( --i );
return q;
}
static void
FT_Add64( FT_Int64* x,
FT_Int64* y,
FT_Int64 *z )
{
FT_UInt32 lo, hi;
lo = x->lo + y->lo;
hi = x->hi + y->hi + ( lo < x->lo );
z->lo = lo;
z->hi = hi;
}
/* The FT_MulDiv function has been optimized thanks to ideas from */
/* Graham Asher and Alexei Podtelezhnikov. The trick is to optimize */
/* a rather common case when everything fits within 32-bits. */
/* */
/* We compute 'a*b+c/2', then divide it by 'c' (all positive values). */
/* */
/* The product of two positive numbers never exceeds the square of */
/* its mean values. Therefore, we always avoid the overflow by */
/* imposing */
/* */
/* (a + b) / 2 <= sqrt(X - c/2) , */
/* */
/* where X = 2^32 - 1, the maximum unsigned 32-bit value, and using */
/* unsigned arithmetic. Now we replace `sqrt' with a linear function */
/* that is smaller or equal for all values of c in the interval */
/* [0;X/2]; it should be equal to sqrt(X) and sqrt(3X/4) at the */
/* endpoints. Substituting the linear solution and explicit numbers */
/* we get */
/* */
/* a + b <= 131071.99 - c / 122291.84 . */
/* */
/* In practice, we should use a faster and even stronger inequality */
/* */
/* a + b <= 131071 - (c >> 16) */
/* */
/* or, alternatively, */
/* */
/* a + b <= 129894 - (c >> 17) . */
/* */
/* FT_MulFix, on the other hand, is optimized for a small value of */
/* the first argument, when the second argument can be much larger. */
/* This can be achieved by scaling the second argument and the limit */
/* in the above inequalities. For example, */
/* */
/* a + (b >> 8) <= (131071 >> 4) */
/* */
/* covers the practical range of use. The actual test below is a bit */
/* tighter to avoid the border case overflows. */
/* */
/* In the case of FT_DivFix, the exact overflow check */
/* */
/* a << 16 <= X - c/2 */
/* */
/* is scaled down by 2^16 and we use */
/* */
/* a <= 65535 - (c >> 17) . */
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_MulDiv( FT_Long a_,
FT_Long b_,
FT_Long c_ )
{
FT_Int s = 1;
FT_UInt32 a, b, c;
/* XXX: this function does not allow 64-bit arguments */
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
FT_MOVE_SIGN( c_, s );
a = (FT_UInt32)a_;
b = (FT_UInt32)b_;
c = (FT_UInt32)c_;
if ( c == 0 )
a = 0x7FFFFFFFUL;
else if ( a + b <= 129894UL - ( c >> 17 ) )
a = ( a * b + ( c >> 1 ) ) / c;
else
{
FT_Int64 temp, temp2;
ft_multo64( a, b, &temp );
temp2.hi = 0;
temp2.lo = c >> 1;
FT_Add64( &temp, &temp2, &temp );
/* last attempt to ditch long division */
a = temp.hi == 0 ? temp.lo / c
: ft_div64by32( temp.hi, temp.lo, c );
}
a_ = (FT_Long)a;
return s < 0 ? -a_ : a_;
}
FT_BASE_DEF( FT_Long )
FT_MulDiv_No_Round( FT_Long a_,
FT_Long b_,
FT_Long c_ )
{
FT_Int s = 1;
FT_UInt32 a, b, c;
/* XXX: this function does not allow 64-bit arguments */
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
FT_MOVE_SIGN( c_, s );
a = (FT_UInt32)a_;
b = (FT_UInt32)b_;
c = (FT_UInt32)c_;
if ( c == 0 )
a = 0x7FFFFFFFUL;
else if ( a + b <= 131071UL )
a = a * b / c;
else
{
FT_Int64 temp;
ft_multo64( a, b, &temp );
/* last attempt to ditch long division */
a = temp.hi == 0 ? temp.lo / c
: ft_div64by32( temp.hi, temp.lo, c );
}
a_ = (FT_Long)a;
return s < 0 ? -a_ : a_;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_MulFix( FT_Long a_,
FT_Long b_ )
{
#ifdef FT_MULFIX_ASSEMBLER
return FT_MULFIX_ASSEMBLER( a_, b_ );
#elif 0
/*
* This code is nonportable. See comment below.
*
* However, on a platform where right-shift of a signed quantity fills
* the leftmost bits by copying the sign bit, it might be faster.
*/
FT_Long sa, sb;
FT_UInt32 a, b;
/*
* This is a clever way of converting a signed number `a' into its
* absolute value (stored back into `a') and its sign. The sign is
* stored in `sa'; 0 means `a' was positive or zero, and -1 means `a'
* was negative. (Similarly for `b' and `sb').
*
* Unfortunately, it doesn't work (at least not portably).
*
* It makes the assumption that right-shift on a negative signed value
* fills the leftmost bits by copying the sign bit. This is wrong.
* According to K&R 2nd ed, section `A7.8 Shift Operators' on page 206,
* the result of right-shift of a negative signed value is
* implementation-defined. At least one implementation fills the
* leftmost bits with 0s (i.e., it is exactly the same as an unsigned
* right shift). This means that when `a' is negative, `sa' ends up
* with the value 1 rather than -1. After that, everything else goes
* wrong.
*/
sa = ( a_ >> ( sizeof ( a_ ) * 8 - 1 ) );
a = ( a_ ^ sa ) - sa;
sb = ( b_ >> ( sizeof ( b_ ) * 8 - 1 ) );
b = ( b_ ^ sb ) - sb;
a = (FT_UInt32)a_;
b = (FT_UInt32)b_;
if ( a + ( b >> 8 ) <= 8190UL )
a = ( a * b + 0x8000U ) >> 16;
else
{
FT_UInt32 al = a & 0xFFFFUL;
a = ( a >> 16 ) * b + al * ( b >> 16 ) +
( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 );
}
sa ^= sb;
a = ( a ^ sa ) - sa;
return (FT_Long)a;
#else /* 0 */
FT_Int s = 1;
FT_UInt32 a, b;
/* XXX: this function does not allow 64-bit arguments */
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
a = (FT_UInt32)a_;
b = (FT_UInt32)b_;
if ( a + ( b >> 8 ) <= 8190UL )
a = ( a * b + 0x8000UL ) >> 16;
else
{
FT_UInt32 al = a & 0xFFFFUL;
a = ( a >> 16 ) * b + al * ( b >> 16 ) +
( ( al * ( b & 0xFFFFUL ) + 0x8000UL ) >> 16 );
}
a_ = (FT_Long)a;
return s < 0 ? -a_ : a_;
#endif /* 0 */
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Long )
FT_DivFix( FT_Long a_,
FT_Long b_ )
{
FT_Int s = 1;
FT_UInt32 a, b, q;
FT_Long q_;
/* XXX: this function does not allow 64-bit arguments */
FT_MOVE_SIGN( a_, s );
FT_MOVE_SIGN( b_, s );
a = (FT_UInt32)a_;
b = (FT_UInt32)b_;
if ( b == 0 )
{
/* check for division by 0 */
q = 0x7FFFFFFFUL;
}
else if ( a <= 65535UL - ( b >> 17 ) )
{
/* compute result directly */
q = ( ( a << 16 ) + ( b >> 1 ) ) / b;
}
else
{
/* we need more bits; we have to do it by hand */
FT_Int64 temp, temp2;
temp.hi = a >> 16;
temp.lo = a << 16;
temp2.hi = 0;
temp2.lo = b >> 1;
FT_Add64( &temp, &temp2, &temp );
q = ft_div64by32( temp.hi, temp.lo, b );
}
q_ = (FT_Long)q;
return s < 0 ? -q_ : q_;
}
#endif /* !FT_LONG64 */
/* documentation is in ftglyph.h */
FT_EXPORT_DEF( void )
FT_Matrix_Multiply( const FT_Matrix* a,
FT_Matrix *b )
{
FT_Fixed xx, xy, yx, yy;
if ( !a || !b )
return;
xx = FT_MulFix( a->xx, b->xx ) + FT_MulFix( a->xy, b->yx );
xy = FT_MulFix( a->xx, b->xy ) + FT_MulFix( a->xy, b->yy );
yx = FT_MulFix( a->yx, b->xx ) + FT_MulFix( a->yy, b->yx );
yy = FT_MulFix( a->yx, b->xy ) + FT_MulFix( a->yy, b->yy );
b->xx = xx; b->xy = xy;
b->yx = yx; b->yy = yy;
}
/* documentation is in ftglyph.h */
FT_EXPORT_DEF( FT_Error )
FT_Matrix_Invert( FT_Matrix* matrix )
{
FT_Pos delta, xx, yy;
if ( !matrix )
return FT_THROW( Invalid_Argument );
/* compute discriminant */
delta = FT_MulFix( matrix->xx, matrix->yy ) -
FT_MulFix( matrix->xy, matrix->yx );
if ( !delta )
return FT_THROW( Invalid_Argument ); /* matrix can't be inverted */
matrix->xy = - FT_DivFix( matrix->xy, delta );
matrix->yx = - FT_DivFix( matrix->yx, delta );
xx = matrix->xx;
yy = matrix->yy;
matrix->xx = FT_DivFix( yy, delta );
matrix->yy = FT_DivFix( xx, delta );
return FT_Err_Ok;
}
/* documentation is in ftcalc.h */
FT_BASE_DEF( void )
FT_Matrix_Multiply_Scaled( const FT_Matrix* a,
FT_Matrix *b,
FT_Long scaling )
{
FT_Fixed xx, xy, yx, yy;
FT_Long val = 0x10000L * scaling;
if ( !a || !b )
return;
xx = FT_MulDiv( a->xx, b->xx, val ) + FT_MulDiv( a->xy, b->yx, val );
xy = FT_MulDiv( a->xx, b->xy, val ) + FT_MulDiv( a->xy, b->yy, val );
yx = FT_MulDiv( a->yx, b->xx, val ) + FT_MulDiv( a->yy, b->yx, val );
yy = FT_MulDiv( a->yx, b->xy, val ) + FT_MulDiv( a->yy, b->yy, val );
b->xx = xx; b->xy = xy;
b->yx = yx; b->yy = yy;
}
/* documentation is in ftcalc.h */
FT_BASE_DEF( void )
FT_Vector_Transform_Scaled( FT_Vector* vector,
const FT_Matrix* matrix,
FT_Long scaling )
{
FT_Pos xz, yz;
FT_Long val = 0x10000L * scaling;
if ( !vector || !matrix )
return;
xz = FT_MulDiv( vector->x, matrix->xx, val ) +
FT_MulDiv( vector->y, matrix->xy, val );
yz = FT_MulDiv( vector->x, matrix->yx, val ) +
FT_MulDiv( vector->y, matrix->yy, val );
vector->x = xz;
vector->y = yz;
}
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_UInt32 )
FT_Vector_NormLen( FT_Vector* vector )
{
FT_Int32 x_ = vector->x;
FT_Int32 y_ = vector->y;
FT_Int32 b, z;
FT_UInt32 x, y, u, v, l;
FT_Int sx = 1, sy = 1, shift;
FT_MOVE_SIGN( x_, sx );
FT_MOVE_SIGN( y_, sy );
x = (FT_UInt32)x_;
y = (FT_UInt32)y_;
/* trivial cases */
if ( x == 0 )
{
if ( y > 0 )
vector->y = sy * 0x10000;
return y;
}
else if ( y == 0 )
{
if ( x > 0 )
vector->x = sx * 0x10000;
return x;
}
/* Estimate length and prenormalize by shifting so that */
/* the new approximate length is between 2/3 and 4/3. */
/* The magic constant 0xAAAAAAAAUL (2/3 of 2^32) helps */
/* achieve this in 16.16 fixed-point representation. */
l = x > y ? x + ( y >> 1 )
: y + ( x >> 1 );
shift = 31 - FT_MSB( l );
shift -= 15 + ( l >= ( 0xAAAAAAAAUL >> shift ) );
if ( shift > 0 )
{
x <<= shift;
y <<= shift;
/* re-estimate length for tiny vectors */
l = x > y ? x + ( y >> 1 )
: y + ( x >> 1 );
}
else
{
x >>= -shift;
y >>= -shift;
l >>= -shift;
}
/* lower linear approximation for reciprocal length minus one */
b = 0x10000 - (FT_Int32)l;
x_ = (FT_Int32)x;
y_ = (FT_Int32)y;
/* Newton's iterations */
do
{
u = (FT_UInt32)( x_ + ( x_ * b >> 16 ) );
v = (FT_UInt32)( y_ + ( y_ * b >> 16 ) );
/* Normalized squared length in the parentheses approaches 2^32. */
/* On two's complement systems, converting to signed gives the */
/* difference with 2^32 even if the expression wraps around. */
z = -(FT_Int32)( u * u + v * v ) / 0x200;
z = z * ( ( 0x10000 + b ) >> 8 ) / 0x10000;
b += z;
} while ( z > 0 );
vector->x = sx < 0 ? -(FT_Pos)u : (FT_Pos)u;
vector->y = sy < 0 ? -(FT_Pos)v : (FT_Pos)v;
/* Conversion to signed helps to recover from likely wrap around */
/* in calculating the prenormalized length, because it gives the */
/* correct difference with 2^32 on two's complement systems. */
l = (FT_UInt32)( 0x10000 + (FT_Int32)( u * x + v * y ) / 0x10000 );
if ( shift > 0 )
l = ( l + ( 1 << ( shift - 1 ) ) ) >> shift;
else
l <<= -shift;
return l;
}
#if 0
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_Int32 )
FT_SqrtFixed( FT_Int32 x )
{
FT_UInt32 root, rem_hi, rem_lo, test_div;
FT_Int count;
root = 0;
if ( x > 0 )
{
rem_hi = 0;
rem_lo = (FT_UInt32)x;
count = 24;
do
{
rem_hi = ( rem_hi << 2 ) | ( rem_lo >> 30 );
rem_lo <<= 2;
root <<= 1;
test_div = ( root << 1 ) + 1;
if ( rem_hi >= test_div )
{
rem_hi -= test_div;
root += 1;
}
} while ( --count );
}
return (FT_Int32)root;
}
#endif /* 0 */
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_Int )
ft_corner_orientation( FT_Pos in_x,
FT_Pos in_y,
FT_Pos out_x,
FT_Pos out_y )
{
#ifdef FT_LONG64
FT_Int64 delta = (FT_Int64)in_x * out_y - (FT_Int64)in_y * out_x;
return ( delta > 0 ) - ( delta < 0 );
#else
FT_Int result;
if ( (FT_ULong)FT_ABS( in_x ) + (FT_ULong)FT_ABS( out_y ) <= 131071UL &&
(FT_ULong)FT_ABS( in_y ) + (FT_ULong)FT_ABS( out_x ) <= 131071UL )
{
FT_Long z1 = in_x * out_y;
FT_Long z2 = in_y * out_x;
if ( z1 > z2 )
result = +1;
else if ( z1 < z2 )
result = -1;
else
result = 0;
}
else /* products might overflow 32 bits */
{
FT_Int64 z1, z2;
/* XXX: this function does not allow 64-bit arguments */
ft_multo64( (FT_UInt32)in_x, (FT_UInt32)out_y, &z1 );
ft_multo64( (FT_UInt32)in_y, (FT_UInt32)out_x, &z2 );
if ( z1.hi > z2.hi )
result = +1;
else if ( z1.hi < z2.hi )
result = -1;
else if ( z1.lo > z2.lo )
result = +1;
else if ( z1.lo < z2.lo )
result = -1;
else
result = 0;
}
/* XXX: only the sign of return value, +1/0/-1 must be used */
return result;
#endif
}
/* documentation is in ftcalc.h */
FT_BASE_DEF( FT_Int )
ft_corner_is_flat( FT_Pos in_x,
FT_Pos in_y,
FT_Pos out_x,
FT_Pos out_y )
{
FT_Pos ax = in_x + out_x;
FT_Pos ay = in_y + out_y;
FT_Pos d_in, d_out, d_hypot;
/* The idea of this function is to compare the length of the */
/* hypotenuse with the `in' and `out' length. The `corner' */
/* represented by `in' and `out' is flat if the hypotenuse's */
/* length isn't too large. */
/* */
/* This approach has the advantage that the angle between */
/* `in' and `out' is not checked. In case one of the two */
/* vectors is `dominant', this is, much larger than the */
/* other vector, we thus always have a flat corner. */
/* */
/* hypotenuse */
/* x---------------------------x */
/* \ / */
/* \ / */
/* in \ / out */
/* \ / */
/* o */
/* Point */
d_in = FT_HYPOT( in_x, in_y );
d_out = FT_HYPOT( out_x, out_y );
d_hypot = FT_HYPOT( ax, ay );
/* now do a simple length comparison: */
/* */
/* d_in + d_out < 17/16 d_hypot */
return ( d_in + d_out - d_hypot ) < ( d_hypot >> 4 );
}
/* END */
src/base/ftinit.c
/***************************************************************************/
/* */
/* ftinit.c */
/* */
/* FreeType initialization layer (body). */
/* */
/* Copyright 1996-2016 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. */
/* */
/***************************************************************************/
/*************************************************************************/
/* */
/* The purpose of this file is to implement the following two */
/* functions: */
/* */
/* FT_Add_Default_Modules(): */
/* This function is used to add the set of default modules to a */
/* fresh new library object. The set is taken from the header file */
/* `freetype/config/ftmodule.h'. See the document `FreeType 2.0 */
/* Build System' for more information. */
/* */
/* FT_Init_FreeType(): */
/* This function creates a system object for the current platform, */
/* builds a library out of it, then calls FT_Default_Drivers(). */
/* */
/* Note that even if FT_Init_FreeType() uses the implementation of the */
/* system object defined at build time, client applications are still */
/* able to provide their own `ftsystem.c'. */
/* */
/*************************************************************************/
#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_MODULE_H
#include "basepic.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_init
#ifndef FT_CONFIG_OPTION_PIC
#undef FT_USE_MODULE
#ifdef __cplusplus
#define FT_USE_MODULE( type, x ) extern "C" const type x;
#else
#define FT_USE_MODULE( type, x ) extern const type x;
#endif
#include FT_CONFIG_MODULES_H
#undef FT_USE_MODULE
#define FT_USE_MODULE( type, x ) (const FT_Module_Class*)&(x),
static
const FT_Module_Class* const ft_default_modules[] =
{
#include FT_CONFIG_MODULES_H
0
};
#else /* FT_CONFIG_OPTION_PIC */
#ifdef __cplusplus
#define FT_EXTERNC extern "C"
#else
#define FT_EXTERNC extern
#endif
/* declare the module's class creation/destruction functions */
#undef FT_USE_MODULE
#define FT_USE_MODULE( type, x ) \
FT_EXTERNC FT_Error \
FT_Create_Class_ ## x( FT_Library library, \
FT_Module_Class* *output_class ); \
FT_EXTERNC void \
FT_Destroy_Class_ ## x( FT_Library library, \
FT_Module_Class* clazz );
#include FT_CONFIG_MODULES_H
/* count all module classes */
#undef FT_USE_MODULE
#define FT_USE_MODULE( type, x ) MODULE_CLASS_ ## x,
enum
{
#include FT_CONFIG_MODULES_H
FT_NUM_MODULE_CLASSES
};
/* destroy all module classes */
#undef FT_USE_MODULE
#define FT_USE_MODULE( type, x ) \
if ( classes[i] ) \
{ \
FT_Destroy_Class_ ## x( library, classes[i] ); \
} \
i++;
FT_BASE_DEF( void )
ft_destroy_default_module_classes( FT_Library library )
{
FT_Module_Class* *classes;
FT_Memory memory;
FT_UInt i;
BasePIC* pic_container = (BasePIC*)library->pic_container.base;
if ( !pic_container->default_module_classes )
return;
memory = library->memory;
classes = pic_container->default_module_classes;
i = 0;
#include FT_CONFIG_MODULES_H
FT_FREE( classes );
pic_container->default_module_classes = NULL;
}
/* initialize all module classes and the pointer table */
#undef FT_USE_MODULE
#define FT_USE_MODULE( type, x ) \
error = FT_Create_Class_ ## x( library, &clazz ); \
if ( error ) \
goto Exit; \
classes[i++] = clazz;
FT_BASE_DEF( FT_Error )
ft_create_default_module_classes( FT_Library library )
{
FT_Error error;
FT_Memory memory;
FT_Module_Class* *classes = NULL;
FT_Module_Class* clazz;
FT_UInt i;
BasePIC* pic_container = (BasePIC*)library->pic_container.base;
memory = library->memory;
pic_container->default_module_classes = NULL;
if ( FT_ALLOC( classes, sizeof ( FT_Module_Class* ) *
( FT_NUM_MODULE_CLASSES + 1 ) ) )
return error;
/* initialize all pointers to 0, especially the last one */
for ( i = 0; i < FT_NUM_MODULE_CLASSES; i++ )
classes[i] = NULL;
classes[FT_NUM_MODULE_CLASSES] = NULL;
i = 0;
#include FT_CONFIG_MODULES_H
Exit:
if ( error )
ft_destroy_default_module_classes( library );
else
pic_container->default_module_classes = classes;
return error;
}
#endif /* FT_CONFIG_OPTION_PIC */
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( void )
FT_Add_Default_Modules( FT_Library library )
{
FT_Error error;
const FT_Module_Class* const* cur;
/* FT_DEFAULT_MODULES_GET dereferences `library' in PIC mode */
#ifdef FT_CONFIG_OPTION_PIC
if ( !library )
return;
#endif
/* GCC 4.6 warns the type difference:
* FT_Module_Class** != const FT_Module_Class* const*
*/
cur = (const FT_Module_Class* const*)FT_DEFAULT_MODULES_GET;
/* test for valid `library' delayed to FT_Add_Module() */
while ( *cur )
{
error = FT_Add_Module( library, *cur );
/* notify errors, but don't stop */
if ( error )
FT_TRACE0(( "FT_Add_Default_Module:"
" Cannot install `%s', error = 0x%x\n",
(*cur)->module_name, error ));
cur++;
}
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Init_FreeType( FT_Library *alibrary )
{
FT_Error error;
FT_Memory memory;
/* check of `alibrary' delayed to `FT_New_Library' */
/* First of all, allocate a new system object -- this function is part */
/* of the system-specific component, i.e. `ftsystem.c'. */
memory = FT_New_Memory();
if ( !memory )
{
FT_ERROR(( "FT_Init_FreeType: cannot find memory manager\n" ));
return FT_THROW( Unimplemented_Feature );
}
/* build a library out of it, then fill it with the set of */
/* default drivers. */
error = FT_New_Library( memory, alibrary );
if ( error )
FT_Done_Memory( memory );
else
FT_Add_Default_Modules( *alibrary );
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Done_FreeType( FT_Library library )
{
FT_Memory memory;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
memory = library->memory;
/* Discard the library object */
FT_Done_Library( library );
/* discard memory manager */
FT_Done_Memory( memory );
return FT_Err_Ok;
}
/* END */
src/base/ftobjs.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftobjs.c */
/* */
/* The FreeType private base classes (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_LIST_H
#include FT_OUTLINE_H
#include FT_INTERNAL_VALIDATE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_RFORK_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_SFNT_H /* for SFNT_Load_Table_Func */
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_TAGS_H
#include FT_TRUETYPE_IDS_H
#include FT_SERVICE_PROPERTIES_H
#include FT_SERVICE_SFNT_H
#include FT_SERVICE_POSTSCRIPT_NAME_H
#include FT_SERVICE_GLYPH_DICT_H
#include FT_SERVICE_TT_CMAP_H
#include FT_SERVICE_KERNING_H
#include FT_SERVICE_TRUETYPE_ENGINE_H
#ifdef FT_CONFIG_OPTION_MAC_FONTS
#include "ftbase.h"
#endif
#ifdef FT_DEBUG_LEVEL_TRACE
#include FT_BITMAP_H
#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */
/* We disable the warning `conversion from XXX to YYY, */
/* possible loss of data' in order to compile cleanly with */
/* the maximum level of warnings: `md5.c' is non-FreeType */
/* code, and it gets used during development builds only. */
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif /* _MSC_VER */
/* It's easiest to include `md5.c' directly. However, since OpenSSL */
/* also provides the same functions, there might be conflicts if */
/* both FreeType and OpenSSL are built as static libraries. For */
/* this reason, we put the MD5 stuff into the `FT_' namespace. */
#define MD5_u32plus FT_MD5_u32plus
#define MD5_CTX FT_MD5_CTX
#define MD5_Init FT_MD5_Init
#define MD5_Update FT_MD5_Update
#define MD5_Final FT_MD5_Final
#undef HAVE_OPENSSL
#include "md5.c"
#if defined( _MSC_VER )
#pragma warning( pop )
#endif
#endif /* FT_DEBUG_LEVEL_TRACE */
#define GRID_FIT_METRICS
FT_BASE_DEF( FT_Pointer )
ft_service_list_lookup( FT_ServiceDesc service_descriptors,
const char* service_id )
{
FT_Pointer result = NULL;
FT_ServiceDesc desc = service_descriptors;
if ( desc && service_id )
{
for ( ; desc->serv_id != NULL; desc++ )
{
if ( ft_strcmp( desc->serv_id, service_id ) == 0 )
{
result = (FT_Pointer)desc->serv_data;
break;
}
}
}
return result;
}
FT_BASE_DEF( void )
ft_validator_init( FT_Validator valid,
const FT_Byte* base,
const FT_Byte* limit,
FT_ValidationLevel level )
{
valid->base = base;
valid->limit = limit;
valid->level = level;
valid->error = FT_Err_Ok;
}
FT_BASE_DEF( FT_Int )
ft_validator_run( FT_Validator valid )
{
/* This function doesn't work! None should call it. */
FT_UNUSED( valid );
return -1;
}
FT_BASE_DEF( void )
ft_validator_error( FT_Validator valid,
FT_Error error )
{
/* since the cast below also disables the compiler's */
/* type check, we introduce a dummy variable, which */
/* will be optimized away */
volatile ft_jmp_buf* jump_buffer = &valid->jump_buffer;
valid->error = error;
/* throw away volatileness; use `jump_buffer' or the */
/* compiler may warn about an unused local variable */
ft_longjmp( *(ft_jmp_buf*) jump_buffer, 1 );
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** S T R E A M ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* create a new input stream from an FT_Open_Args structure */
/* */
FT_BASE_DEF( FT_Error )
FT_Stream_New( FT_Library library,
const FT_Open_Args* args,
FT_Stream *astream )
{
FT_Error error;
FT_Memory memory;
FT_Stream stream = NULL;
*astream = NULL;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( !args )
return FT_THROW( Invalid_Argument );
memory = library->memory;
if ( FT_NEW( stream ) )
goto Exit;
stream->memory = memory;
if ( args->flags & FT_OPEN_MEMORY )
{
/* create a memory-based stream */
FT_Stream_OpenMemory( stream,
(const FT_Byte*)args->memory_base,
(FT_ULong)args->memory_size );
}
#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
else if ( args->flags & FT_OPEN_PATHNAME )
{
/* create a normal system stream */
error = FT_Stream_Open( stream, args->pathname );
stream->pathname.pointer = args->pathname;
}
else if ( ( args->flags & FT_OPEN_STREAM ) && args->stream )
{
/* use an existing, user-provided stream */
/* in this case, we do not need to allocate a new stream object */
/* since the caller is responsible for closing it himself */
FT_FREE( stream );
stream = args->stream;
}
#endif
else
error = FT_THROW( Invalid_Argument );
if ( error )
FT_FREE( stream );
else
stream->memory = memory; /* just to be certain */
*astream = stream;
Exit:
return error;
}
FT_BASE_DEF( void )
FT_Stream_Free( FT_Stream stream,
FT_Int external )
{
if ( stream )
{
FT_Memory memory = stream->memory;
FT_Stream_Close( stream );
if ( !external )
FT_FREE( stream );
}
}
/*************************************************************************/
/* */
/* 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_objs
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** FACE, SIZE & GLYPH SLOT OBJECTS ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
static FT_Error
ft_glyphslot_init( FT_GlyphSlot slot )
{
FT_Driver driver = slot->face->driver;
FT_Driver_Class clazz = driver->clazz;
FT_Memory memory = driver->root.memory;
FT_Error error = FT_Err_Ok;
FT_Slot_Internal internal = NULL;
slot->library = driver->root.library;
if ( FT_NEW( internal ) )
goto Exit;
slot->internal = internal;
if ( FT_DRIVER_USES_OUTLINES( driver ) )
error = FT_GlyphLoader_New( memory, &internal->loader );
if ( !error && clazz->init_slot )
error = clazz->init_slot( slot );
Exit:
return error;
}
FT_BASE_DEF( void )
ft_glyphslot_free_bitmap( FT_GlyphSlot slot )
{
if ( slot->internal && ( slot->internal->flags & FT_GLYPH_OWN_BITMAP ) )
{
FT_Memory memory = FT_FACE_MEMORY( slot->face );
FT_FREE( slot->bitmap.buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
else
{
/* assume that the bitmap buffer was stolen or not */
/* allocated from the heap */
slot->bitmap.buffer = NULL;
}
}
FT_BASE_DEF( void )
ft_glyphslot_set_bitmap( FT_GlyphSlot slot,
FT_Byte* buffer )
{
ft_glyphslot_free_bitmap( slot );
slot->bitmap.buffer = buffer;
FT_ASSERT( (slot->internal->flags & FT_GLYPH_OWN_BITMAP) == 0 );
}
FT_BASE_DEF( FT_Error )
ft_glyphslot_alloc_bitmap( FT_GlyphSlot slot,
FT_ULong size )
{
FT_Memory memory = FT_FACE_MEMORY( slot->face );
FT_Error error;
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
FT_FREE( slot->bitmap.buffer );
else
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
(void)FT_ALLOC( slot->bitmap.buffer, size );
return error;
}
static void
ft_glyphslot_clear( FT_GlyphSlot slot )
{
/* free bitmap if needed */
ft_glyphslot_free_bitmap( slot );
/* clear all public fields in the glyph slot */
FT_ZERO( &slot->metrics );
FT_ZERO( &slot->outline );
slot->bitmap.width = 0;
slot->bitmap.rows = 0;
slot->bitmap.pitch = 0;
slot->bitmap.pixel_mode = 0;
/* `slot->bitmap.buffer' has been handled by ft_glyphslot_free_bitmap */
slot->bitmap_left = 0;
slot->bitmap_top = 0;
slot->num_subglyphs = 0;
slot->subglyphs = NULL;
slot->control_data = NULL;
slot->control_len = 0;
slot->other = NULL;
slot->format = FT_GLYPH_FORMAT_NONE;
slot->linearHoriAdvance = 0;
slot->linearVertAdvance = 0;
slot->lsb_delta = 0;
slot->rsb_delta = 0;
}
static void
ft_glyphslot_done( FT_GlyphSlot slot )
{
FT_Driver driver = slot->face->driver;
FT_Driver_Class clazz = driver->clazz;
FT_Memory memory = driver->root.memory;
if ( clazz->done_slot )
clazz->done_slot( slot );
/* free bitmap buffer if needed */
ft_glyphslot_free_bitmap( slot );
/* slot->internal might be NULL in out-of-memory situations */
if ( slot->internal )
{
/* free glyph loader */
if ( FT_DRIVER_USES_OUTLINES( driver ) )
{
FT_GlyphLoader_Done( slot->internal->loader );
slot->internal->loader = NULL;
}
FT_FREE( slot->internal );
}
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( FT_Error )
FT_New_GlyphSlot( FT_Face face,
FT_GlyphSlot *aslot )
{
FT_Error error;
FT_Driver driver;
FT_Driver_Class clazz;
FT_Memory memory;
FT_GlyphSlot slot = NULL;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !face->driver )
return FT_THROW( Invalid_Argument );
driver = face->driver;
clazz = driver->clazz;
memory = driver->root.memory;
FT_TRACE4(( "FT_New_GlyphSlot: Creating new slot object\n" ));
if ( !FT_ALLOC( slot, clazz->slot_object_size ) )
{
slot->face = face;
error = ft_glyphslot_init( slot );
if ( error )
{
ft_glyphslot_done( slot );
FT_FREE( slot );
goto Exit;
}
slot->next = face->glyph;
face->glyph = slot;
if ( aslot )
*aslot = slot;
}
else if ( aslot )
*aslot = NULL;
Exit:
FT_TRACE4(( "FT_New_GlyphSlot: Return %d\n", error ));
return error;
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( void )
FT_Done_GlyphSlot( FT_GlyphSlot slot )
{
if ( slot )
{
FT_Driver driver = slot->face->driver;
FT_Memory memory = driver->root.memory;
FT_GlyphSlot prev;
FT_GlyphSlot cur;
/* Remove slot from its parent face's list */
prev = NULL;
cur = slot->face->glyph;
while ( cur )
{
if ( cur == slot )
{
if ( !prev )
slot->face->glyph = cur->next;
else
prev->next = cur->next;
/* finalize client-specific data */
if ( slot->generic.finalizer )
slot->generic.finalizer( slot );
ft_glyphslot_done( slot );
FT_FREE( slot );
break;
}
prev = cur;
cur = cur->next;
}
}
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( void )
FT_Set_Transform( FT_Face face,
FT_Matrix* matrix,
FT_Vector* delta )
{
FT_Face_Internal internal;
if ( !face )
return;
internal = face->internal;
internal->transform_flags = 0;
if ( !matrix )
{
internal->transform_matrix.xx = 0x10000L;
internal->transform_matrix.xy = 0;
internal->transform_matrix.yx = 0;
internal->transform_matrix.yy = 0x10000L;
matrix = &internal->transform_matrix;
}
else
internal->transform_matrix = *matrix;
/* set transform_flags bit flag 0 if `matrix' isn't the identity */
if ( ( matrix->xy | matrix->yx ) ||
matrix->xx != 0x10000L ||
matrix->yy != 0x10000L )
internal->transform_flags |= 1;
if ( !delta )
{
internal->transform_delta.x = 0;
internal->transform_delta.y = 0;
delta = &internal->transform_delta;
}
else
internal->transform_delta = *delta;
/* set transform_flags bit flag 1 if `delta' isn't the null vector */
if ( delta->x | delta->y )
internal->transform_flags |= 2;
}
static FT_Renderer
ft_lookup_glyph_renderer( FT_GlyphSlot slot );
#ifdef GRID_FIT_METRICS
static void
ft_glyphslot_grid_fit_metrics( FT_GlyphSlot slot,
FT_Bool vertical )
{
FT_Glyph_Metrics* metrics = &slot->metrics;
FT_Pos right, bottom;
if ( vertical )
{
metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY );
right = FT_PIX_CEIL( metrics->vertBearingX + metrics->width );
bottom = FT_PIX_CEIL( metrics->vertBearingY + metrics->height );
metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
metrics->width = right - metrics->vertBearingX;
metrics->height = bottom - metrics->vertBearingY;
}
else
{
metrics->vertBearingX = FT_PIX_FLOOR( metrics->vertBearingX );
metrics->vertBearingY = FT_PIX_FLOOR( metrics->vertBearingY );
right = FT_PIX_CEIL ( metrics->horiBearingX + metrics->width );
bottom = FT_PIX_FLOOR( metrics->horiBearingY - metrics->height );
metrics->horiBearingX = FT_PIX_FLOOR( metrics->horiBearingX );
metrics->horiBearingY = FT_PIX_CEIL ( metrics->horiBearingY );
metrics->width = right - metrics->horiBearingX;
metrics->height = metrics->horiBearingY - bottom;
}
metrics->horiAdvance = FT_PIX_ROUND( metrics->horiAdvance );
metrics->vertAdvance = FT_PIX_ROUND( metrics->vertAdvance );
}
#endif /* GRID_FIT_METRICS */
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Load_Glyph( FT_Face face,
FT_UInt glyph_index,
FT_Int32 load_flags )
{
FT_Error error;
FT_Driver driver;
FT_GlyphSlot slot;
FT_Library library;
FT_Bool autohint = FALSE;
FT_Module hinter;
TT_Face ttface = (TT_Face)face;
if ( !face || !face->size || !face->glyph )
return FT_THROW( Invalid_Face_Handle );
/* The validity test for `glyph_index' is performed by the */
/* font drivers. */
slot = face->glyph;
ft_glyphslot_clear( slot );
driver = face->driver;
library = driver->root.library;
hinter = library->auto_hinter;
/* resolve load flags dependencies */
if ( load_flags & FT_LOAD_NO_RECURSE )
load_flags |= FT_LOAD_NO_SCALE |
FT_LOAD_IGNORE_TRANSFORM;
if ( load_flags & FT_LOAD_NO_SCALE )
{
load_flags |= FT_LOAD_NO_HINTING |
FT_LOAD_NO_BITMAP;
load_flags &= ~FT_LOAD_RENDER;
}
/*
* Determine whether we need to auto-hint or not.
* The general rules are:
*
* - Do only auto-hinting if we have a hinter module, a scalable font
* format dealing with outlines, and no transforms except simple
* slants and/or rotations by integer multiples of 90 degrees.
*
* - Then, auto-hint if FT_LOAD_FORCE_AUTOHINT is set or if we don't
* have a native font hinter.
*
* - Otherwise, auto-hint for LIGHT hinting mode or if there isn't
* any hinting bytecode in the TrueType/OpenType font.
*
* - Exception: The font is `tricky' and requires the native hinter to
* load properly.
*/
if ( hinter &&
!( load_flags & FT_LOAD_NO_HINTING ) &&
!( load_flags & FT_LOAD_NO_AUTOHINT ) &&
FT_DRIVER_IS_SCALABLE( driver ) &&
FT_DRIVER_USES_OUTLINES( driver ) &&
!FT_IS_TRICKY( face ) &&
( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) ||
( face->internal->transform_matrix.yx == 0 &&
face->internal->transform_matrix.xx != 0 ) ||
( face->internal->transform_matrix.xx == 0 &&
face->internal->transform_matrix.yx != 0 ) ) )
{
if ( ( load_flags & FT_LOAD_FORCE_AUTOHINT ) ||
!FT_DRIVER_HAS_HINTER( driver ) )
autohint = TRUE;
else
{
FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
/* the check for `num_locations' assures that we actually */
/* test for instructions in a TTF and not in a CFF-based OTF */
/* */
/* since `maxSizeOfInstructions' might be unreliable, we */
/* check the size of the `fpgm' and `prep' tables, too -- */
/* the assumption is that there don't exist real TTFs where */
/* both `fpgm' and `prep' tables are missing */
if ( ( mode == FT_RENDER_MODE_LIGHT &&
!FT_DRIVER_HINTS_LIGHTLY( driver ) ) ||
( FT_IS_SFNT( face ) &&
ttface->num_locations &&
ttface->max_profile.maxSizeOfInstructions == 0 &&
ttface->font_program_size == 0 &&
ttface->cvt_program_size == 0 ) )
autohint = TRUE;
}
}
if ( autohint )
{
FT_AutoHinter_Interface hinting;
/* try to load embedded bitmaps first if available */
/* */
/* XXX: This is really a temporary hack that should disappear */
/* promptly with FreeType 2.1! */
/* */
if ( FT_HAS_FIXED_SIZES( face ) &&
( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
{
error = driver->clazz->load_glyph( slot, face->size,
glyph_index,
load_flags | FT_LOAD_SBITS_ONLY );
if ( !error && slot->format == FT_GLYPH_FORMAT_BITMAP )
goto Load_Ok;
}
{
FT_Face_Internal internal = face->internal;
FT_Int transform_flags = internal->transform_flags;
/* since the auto-hinter calls FT_Load_Glyph by itself, */
/* make sure that glyphs aren't transformed */
internal->transform_flags = 0;
/* load auto-hinted outline */
hinting = (FT_AutoHinter_Interface)hinter->clazz->module_interface;
error = hinting->load_glyph( (FT_AutoHinter)hinter,
slot, face->size,
glyph_index, load_flags );
internal->transform_flags = transform_flags;
}
}
else
{
error = driver->clazz->load_glyph( slot,
face->size,
glyph_index,
load_flags );
if ( error )
goto Exit;
if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
{
/* check that the loaded outline is correct */
error = FT_Outline_Check( &slot->outline );
if ( error )
goto Exit;
#ifdef GRID_FIT_METRICS
if ( !( load_flags & FT_LOAD_NO_HINTING ) )
ft_glyphslot_grid_fit_metrics( slot,
FT_BOOL( load_flags & FT_LOAD_VERTICAL_LAYOUT ) );
#endif
}
}
Load_Ok:
/* compute the advance */
if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
{
slot->advance.x = 0;
slot->advance.y = slot->metrics.vertAdvance;
}
else
{
slot->advance.x = slot->metrics.horiAdvance;
slot->advance.y = 0;
}
/* compute the linear advance in 16.16 pixels */
if ( ( load_flags & FT_LOAD_LINEAR_DESIGN ) == 0 &&
( FT_IS_SCALABLE( face ) ) )
{
FT_Size_Metrics* metrics = &face->size->metrics;
/* it's tricky! */
slot->linearHoriAdvance = FT_MulDiv( slot->linearHoriAdvance,
metrics->x_scale, 64 );
slot->linearVertAdvance = FT_MulDiv( slot->linearVertAdvance,
metrics->y_scale, 64 );
}
if ( ( load_flags & FT_LOAD_IGNORE_TRANSFORM ) == 0 )
{
FT_Face_Internal internal = face->internal;
/* now, transform the glyph image if needed */
if ( internal->transform_flags )
{
/* get renderer */
FT_Renderer renderer = ft_lookup_glyph_renderer( slot );
if ( renderer )
error = renderer->clazz->transform_glyph(
renderer, slot,
&internal->transform_matrix,
&internal->transform_delta );
else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
{
/* apply `standard' transformation if no renderer is available */
if ( internal->transform_flags & 1 )
FT_Outline_Transform( &slot->outline,
&internal->transform_matrix );
if ( internal->transform_flags & 2 )
FT_Outline_Translate( &slot->outline,
internal->transform_delta.x,
internal->transform_delta.y );
}
/* transform advance */
FT_Vector_Transform( &slot->advance, &internal->transform_matrix );
}
}
FT_TRACE5(( " x advance: %d\n" , slot->advance.x ));
FT_TRACE5(( " y advance: %d\n" , slot->advance.y ));
FT_TRACE5(( " linear x advance: %d\n" , slot->linearHoriAdvance ));
FT_TRACE5(( " linear y advance: %d\n" , slot->linearVertAdvance ));
/* do we need to render the image now? */
if ( !error &&
slot->format != FT_GLYPH_FORMAT_BITMAP &&
slot->format != FT_GLYPH_FORMAT_COMPOSITE &&
load_flags & FT_LOAD_RENDER )
{
FT_Render_Mode mode = FT_LOAD_TARGET_MODE( load_flags );
if ( mode == FT_RENDER_MODE_NORMAL &&
(load_flags & FT_LOAD_MONOCHROME ) )
mode = FT_RENDER_MODE_MONO;
error = FT_Render_Glyph( slot, mode );
}
Exit:
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Load_Char( FT_Face face,
FT_ULong char_code,
FT_Int32 load_flags )
{
FT_UInt glyph_index;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
glyph_index = (FT_UInt)char_code;
if ( face->charmap )
glyph_index = FT_Get_Char_Index( face, char_code );
return FT_Load_Glyph( face, glyph_index, load_flags );
}
/* destructor for sizes list */
static void
destroy_size( FT_Memory memory,
FT_Size size,
FT_Driver driver )
{
/* finalize client-specific data */
if ( size->generic.finalizer )
size->generic.finalizer( size );
/* finalize format-specific stuff */
if ( driver->clazz->done_size )
driver->clazz->done_size( size );
FT_FREE( size->internal );
FT_FREE( size );
}
static void
ft_cmap_done_internal( FT_CMap cmap );
static void
destroy_charmaps( FT_Face face,
FT_Memory memory )
{
FT_Int n;
if ( !face )
return;
for ( n = 0; n < face->num_charmaps; n++ )
{
FT_CMap cmap = FT_CMAP( face->charmaps[n] );
ft_cmap_done_internal( cmap );
face->charmaps[n] = NULL;
}
FT_FREE( face->charmaps );
face->num_charmaps = 0;
}
/* destructor for faces list */
static void
destroy_face( FT_Memory memory,
FT_Face face,
FT_Driver driver )
{
FT_Driver_Class clazz = driver->clazz;
/* discard auto-hinting data */
if ( face->autohint.finalizer )
face->autohint.finalizer( face->autohint.data );
/* Discard glyph slots for this face. */
/* Beware! FT_Done_GlyphSlot() changes the field `face->glyph' */
while ( face->glyph )
FT_Done_GlyphSlot( face->glyph );
/* discard all sizes for this face */
FT_List_Finalize( &face->sizes_list,
(FT_List_Destructor)destroy_size,
memory,
driver );
face->size = NULL;
/* now discard client data */
if ( face->generic.finalizer )
face->generic.finalizer( face );
/* discard charmaps */
destroy_charmaps( face, memory );
/* finalize format-specific stuff */
if ( clazz->done_face )
clazz->done_face( face );
/* close the stream for this face if needed */
FT_Stream_Free(
face->stream,
( face->face_flags & FT_FACE_FLAG_EXTERNAL_STREAM ) != 0 );
face->stream = NULL;
/* get rid of it */
if ( face->internal )
{
FT_FREE( face->internal );
}
FT_FREE( face );
}
static void
Destroy_Driver( FT_Driver driver )
{
FT_List_Finalize( &driver->faces_list,
(FT_List_Destructor)destroy_face,
driver->root.memory,
driver );
}
/*************************************************************************/
/* */
/* <Function> */
/* find_unicode_charmap */
/* */
/* <Description> */
/* This function finds a Unicode charmap, if there is one. */
/* And if there is more than one, it tries to favour the more */
/* extensive one, i.e., one that supports UCS-4 against those which */
/* are limited to the BMP (said UCS-2 encoding.) */
/* */
/* This function is called from open_face() (just below), and also */
/* from FT_Select_Charmap( ..., FT_ENCODING_UNICODE ). */
/* */
static FT_Error
find_unicode_charmap( FT_Face face )
{
FT_CharMap* first;
FT_CharMap* cur;
/* caller should have already checked that `face' is valid */
FT_ASSERT( face );
first = face->charmaps;
if ( !first )
return FT_THROW( Invalid_CharMap_Handle );
/*
* The original TrueType specification(s) only specified charmap
* formats that are capable of mapping 8 or 16 bit character codes to
* glyph indices.
*
* However, recent updates to the Apple and OpenType specifications
* introduced new formats that are capable of mapping 32-bit character
* codes as well. And these are already used on some fonts, mainly to
* map non-BMP Asian ideographs as defined in Unicode.
*
* For compatibility purposes, these fonts generally come with
* *several* Unicode charmaps:
*
* - One of them in the "old" 16-bit format, that cannot access
* all glyphs in the font.
*
* - Another one in the "new" 32-bit format, that can access all
* the glyphs.
*
* This function has been written to always favor a 32-bit charmap
* when found. Otherwise, a 16-bit one is returned when found.
*/
/* Since the `interesting' table, with IDs (3,10), is normally the */
/* last one, we loop backwards. This loses with type1 fonts with */
/* non-BMP characters (<.0001%), this wins with .ttf with non-BMP */
/* chars (.01% ?), and this is the same about 99.99% of the time! */
cur = first + face->num_charmaps; /* points after the last one */
for ( ; --cur >= first; )
{
if ( cur[0]->encoding == FT_ENCODING_UNICODE )
{
/* XXX If some new encodings to represent UCS-4 are added, */
/* they should be added here. */
if ( ( cur[0]->platform_id == TT_PLATFORM_MICROSOFT &&
cur[0]->encoding_id == TT_MS_ID_UCS_4 ) ||
( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
cur[0]->encoding_id == TT_APPLE_ID_UNICODE_32 ) )
{
face->charmap = cur[0];
return FT_Err_Ok;
}
}
}
/* We do not have any UCS-4 charmap. */
/* Do the loop again and search for UCS-2 charmaps. */
cur = first + face->num_charmaps;
for ( ; --cur >= first; )
{
if ( cur[0]->encoding == FT_ENCODING_UNICODE )
{
face->charmap = cur[0];
return FT_Err_Ok;
}
}
return FT_THROW( Invalid_CharMap_Handle );
}
/*************************************************************************/
/* */
/* <Function> */
/* find_variant_selector_charmap */
/* */
/* <Description> */
/* This function finds the variant selector charmap, if there is one. */
/* There can only be one (platform=0, specific=5, format=14). */
/* */
static FT_CharMap
find_variant_selector_charmap( FT_Face face )
{
FT_CharMap* first;
FT_CharMap* end;
FT_CharMap* cur;
/* caller should have already checked that `face' is valid */
FT_ASSERT( face );
first = face->charmaps;
if ( !first )
return NULL;
end = first + face->num_charmaps; /* points after the last one */
for ( cur = first; cur < end; ++cur )
{
if ( cur[0]->platform_id == TT_PLATFORM_APPLE_UNICODE &&
cur[0]->encoding_id == TT_APPLE_ID_VARIANT_SELECTOR &&
FT_Get_CMap_Format( cur[0] ) == 14 )
return cur[0];
}
return NULL;
}
/*************************************************************************/
/* */
/* <Function> */
/* open_face */
/* */
/* <Description> */
/* This function does some work for FT_Open_Face(). */
/* */
static FT_Error
open_face( FT_Driver driver,
FT_Stream *astream,
FT_Bool external_stream,
FT_Long face_index,
FT_Int num_params,
FT_Parameter* params,
FT_Face *aface )
{
FT_Memory memory;
FT_Driver_Class clazz;
FT_Face face = NULL;
FT_Face_Internal internal = NULL;
FT_Error error, error2;
clazz = driver->clazz;
memory = driver->root.memory;
/* allocate the face object and perform basic initialization */
if ( FT_ALLOC( face, clazz->face_object_size ) )
goto Fail;
face->driver = driver;
face->memory = memory;
face->stream = *astream;
/* set the FT_FACE_FLAG_EXTERNAL_STREAM bit for FT_Done_Face */
if ( external_stream )
face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM;
if ( FT_NEW( internal ) )
goto Fail;
face->internal = internal;
#ifdef FT_CONFIG_OPTION_INCREMENTAL
{
int i;
face->internal->incremental_interface = NULL;
for ( i = 0; i < num_params && !face->internal->incremental_interface;
i++ )
if ( params[i].tag == FT_PARAM_TAG_INCREMENTAL )
face->internal->incremental_interface =
(FT_Incremental_Interface)params[i].data;
}
#endif
if ( clazz->init_face )
error = clazz->init_face( *astream,
face,
(FT_Int)face_index,
num_params,
params );
*astream = face->stream; /* Stream may have been changed. */
if ( error )
goto Fail;
/* select Unicode charmap by default */
error2 = find_unicode_charmap( face );
/* if no Unicode charmap can be found, FT_Err_Invalid_CharMap_Handle */
/* is returned. */
/* no error should happen, but we want to play safe */
if ( error2 && FT_ERR_NEQ( error2, Invalid_CharMap_Handle ) )
{
error = error2;
goto Fail;
}
*aface = face;
Fail:
if ( error )
{
destroy_charmaps( face, memory );
if ( clazz->done_face )
clazz->done_face( face );
FT_FREE( internal );
FT_FREE( face );
*aface = NULL;
}
return error;
}
/* there's a Mac-specific extended implementation of FT_New_Face() */
/* in src/base/ftmac.c */
#ifndef FT_MACINTOSH
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_New_Face( FT_Library library,
const char* pathname,
FT_Long face_index,
FT_Face *aface )
{
FT_Open_Args args;
/* test for valid `library' and `aface' delayed to `FT_Open_Face' */
if ( !pathname )
return FT_THROW( Invalid_Argument );
args.flags = FT_OPEN_PATHNAME;
args.pathname = (char*)pathname;
args.stream = NULL;
return FT_Open_Face( library, &args, face_index, aface );
}
#endif
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_New_Memory_Face( FT_Library library,
const FT_Byte* file_base,
FT_Long file_size,
FT_Long face_index,
FT_Face *aface )
{
FT_Open_Args args;
/* test for valid `library' and `face' delayed to `FT_Open_Face' */
if ( !file_base )
return FT_THROW( Invalid_Argument );
args.flags = FT_OPEN_MEMORY;
args.memory_base = file_base;
args.memory_size = file_size;
args.stream = NULL;
return FT_Open_Face( library, &args, face_index, aface );
}
#ifdef FT_CONFIG_OPTION_MAC_FONTS
/* The behavior here is very similar to that in base/ftmac.c, but it */
/* is designed to work on non-mac systems, so no mac specific calls. */
/* */
/* We look at the file and determine if it is a mac dfont file or a mac */
/* resource file, or a macbinary file containing a mac resource file. */
/* */
/* Unlike ftmac I'm not going to look at a `FOND'. I don't really see */
/* the point, especially since there may be multiple `FOND' resources. */
/* Instead I'll just look for `sfnt' and `POST' resources, ordered as */
/* they occur in the file. */
/* */
/* Note that multiple `POST' resources do not mean multiple postscript */
/* fonts; they all get jammed together to make what is essentially a */
/* pfb file. */
/* */
/* We aren't interested in `NFNT' or `FONT' bitmap resources. */
/* */
/* As soon as we get an `sfnt' load it into memory and pass it off to */
/* FT_Open_Face. */
/* */
/* If we have a (set of) `POST' resources, massage them into a (memory) */
/* pfb file and pass that to FT_Open_Face. (As with ftmac.c I'm not */
/* going to try to save the kerning info. After all that lives in the */
/* `FOND' which isn't in the file containing the `POST' resources so */
/* we don't really have access to it. */
/* Finalizer for a memory stream; gets called by FT_Done_Face(). */
/* It frees the memory it uses. */
/* From ftmac.c. */
static void
memory_stream_close( FT_Stream stream )
{
FT_Memory memory = stream->memory;
FT_FREE( stream->base );
stream->size = 0;
stream->base = NULL;
stream->close = NULL;
}
/* Create a new memory stream from a buffer and a size. */
/* From ftmac.c. */
static FT_Error
new_memory_stream( FT_Library library,
FT_Byte* base,
FT_ULong size,
FT_Stream_CloseFunc close,
FT_Stream *astream )
{
FT_Error error;
FT_Memory memory;
FT_Stream stream = NULL;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( !base )
return FT_THROW( Invalid_Argument );
*astream = NULL;
memory = library->memory;
if ( FT_NEW( stream ) )
goto Exit;
FT_Stream_OpenMemory( stream, base, size );
stream->close = close;
*astream = stream;
Exit:
return error;
}
/* Create a new FT_Face given a buffer and a driver name. */
/* from ftmac.c */
FT_LOCAL_DEF( FT_Error )
open_face_from_buffer( FT_Library library,
FT_Byte* base,
FT_ULong size,
FT_Long face_index,
const char* driver_name,
FT_Face *aface )
{
FT_Open_Args args;
FT_Error error;
FT_Stream stream = NULL;
FT_Memory memory = library->memory;
error = new_memory_stream( library,
base,
size,
memory_stream_close,
&stream );
if ( error )
{
FT_FREE( base );
return error;
}
args.flags = FT_OPEN_STREAM;
args.stream = stream;
if ( driver_name )
{
args.flags = args.flags | FT_OPEN_DRIVER;
args.driver = FT_Get_Module( library, driver_name );
}
#ifdef FT_MACINTOSH
/* At this point, the face index has served its purpose; */
/* whoever calls this function has already used it to */
/* locate the correct font data. We should not propagate */
/* this index to FT_Open_Face() (unless it is negative). */
if ( face_index > 0 )
face_index &= 0x7FFF0000L; /* retain GX data */
#endif
error = FT_Open_Face( library, &args, face_index, aface );
if ( error == FT_Err_Ok )
(*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
else
#ifdef FT_MACINTOSH
FT_Stream_Free( stream, 0 );
#else
{
FT_Stream_Close( stream );
FT_FREE( stream );
}
#endif
return error;
}
/* Look up `TYP1' or `CID ' table from sfnt table directory. */
/* `offset' and `length' must exclude the binary header in tables. */
/* Type 1 and CID-keyed font drivers should recognize sfnt-wrapped */
/* format too. Here, since we can't expect that the TrueType font */
/* driver is loaded unconditially, we must parse the font by */
/* ourselves. We are only interested in the name of the table and */
/* the offset. */
static FT_Error
ft_lookup_PS_in_sfnt_stream( FT_Stream stream,
FT_Long face_index,
FT_ULong* offset,
FT_ULong* length,
FT_Bool* is_sfnt_cid )
{
FT_Error error;
FT_UShort numTables;
FT_Long pstable_index;
FT_ULong tag;
int i;
*offset = 0;
*length = 0;
*is_sfnt_cid = FALSE;
/* TODO: support for sfnt-wrapped PS/CID in TTC format */
/* version check for 'typ1' (should be ignored?) */
if ( FT_READ_ULONG( tag ) )
return error;
if ( tag != TTAG_typ1 )
return FT_THROW( Unknown_File_Format );
if ( FT_READ_USHORT( numTables ) )
return error;
if ( FT_STREAM_SKIP( 2 * 3 ) ) /* skip binary search header */
return error;
pstable_index = -1;
*is_sfnt_cid = FALSE;
for ( i = 0; i < numTables; i++ )
{
if ( FT_READ_ULONG( tag ) || FT_STREAM_SKIP( 4 ) ||
FT_READ_ULONG( *offset ) || FT_READ_ULONG( *length ) )
return error;
if ( tag == TTAG_CID )
{
pstable_index++;
*offset += 22;
*length -= 22;
*is_sfnt_cid = TRUE;
if ( face_index < 0 )
return FT_Err_Ok;
}
else if ( tag == TTAG_TYP1 )
{
pstable_index++;
*offset += 24;
*length -= 24;
*is_sfnt_cid = FALSE;
if ( face_index < 0 )
return FT_Err_Ok;
}
if ( face_index >= 0 && pstable_index == face_index )
return FT_Err_Ok;
}
return FT_THROW( Table_Missing );
}
FT_LOCAL_DEF( FT_Error )
open_face_PS_from_sfnt_stream( FT_Library library,
FT_Stream stream,
FT_Long face_index,
FT_Int num_params,
FT_Parameter *params,
FT_Face *aface )
{
FT_Error error;
FT_Memory memory = library->memory;
FT_ULong offset, length;
FT_ULong pos;
FT_Bool is_sfnt_cid;
FT_Byte* sfnt_ps = NULL;
FT_UNUSED( num_params );
FT_UNUSED( params );
/* ignore GX stuff */
if ( face_index > 0 )
face_index &= 0xFFFFL;
pos = FT_STREAM_POS();
error = ft_lookup_PS_in_sfnt_stream( stream,
face_index,
&offset,
&length,
&is_sfnt_cid );
if ( error )
goto Exit;
error = FT_Stream_Seek( stream, pos + offset );
if ( error )
goto Exit;
if ( FT_ALLOC( sfnt_ps, (FT_Long)length ) )
goto Exit;
error = FT_Stream_Read( stream, (FT_Byte *)sfnt_ps, length );
if ( error ) {
FT_FREE( sfnt_ps );
goto Exit;
}
error = open_face_from_buffer( library,
sfnt_ps,
length,
FT_MIN( face_index, 0 ),
is_sfnt_cid ? "cid" : "type1",
aface );
Exit:
{
FT_Error error1;
if ( FT_ERR_EQ( error, Unknown_File_Format ) )
{
error1 = FT_Stream_Seek( stream, pos );
if ( error1 )
return error1;
}
return error;
}
}
#ifndef FT_MACINTOSH
/* The resource header says we've got resource_cnt `POST' (type1) */
/* resources in this file. They all need to be coalesced into */
/* one lump which gets passed on to the type1 driver. */
/* Here can be only one PostScript font in a file so face_index */
/* must be 0 (or -1). */
/* */
static FT_Error
Mac_Read_POST_Resource( FT_Library library,
FT_Stream stream,
FT_Long *offsets,
FT_Long resource_cnt,
FT_Long face_index,
FT_Face *aface )
{
FT_Error error = FT_ERR( Cannot_Open_Resource );
FT_Memory memory = library->memory;
FT_Byte* pfb_data = NULL;
int i, type, flags;
FT_ULong len;
FT_ULong pfb_len, pfb_pos, pfb_lenpos;
FT_ULong rlen, temp;
if ( face_index == -1 )
face_index = 0;
if ( face_index != 0 )
return error;
/* Find the length of all the POST resources, concatenated. Assume */
/* worst case (each resource in its own section). */
pfb_len = 0;
for ( i = 0; i < resource_cnt; ++i )
{
error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] );
if ( error )
goto Exit;
if ( FT_READ_ULONG( temp ) )
goto Exit;
/* FT2 allocator takes signed long buffer length,
* too large value causing overflow should be checked
*/
FT_TRACE4(( " POST fragment #%d: length=0x%08x"
" total pfb_len=0x%08x\n",
i, temp, pfb_len + temp + 6));
if ( FT_MAC_RFORK_MAX_LEN < temp ||
FT_MAC_RFORK_MAX_LEN - temp < pfb_len + 6 )
{
FT_TRACE2(( " MacOS resource length cannot exceed"
" 0x%08x\n", FT_MAC_RFORK_MAX_LEN ));
error = FT_THROW( Invalid_Offset );
goto Exit;
}
pfb_len += temp + 6;
}
FT_TRACE2(( " total buffer size to concatenate %d"
" POST fragments: 0x%08x\n",
resource_cnt, pfb_len + 2));
if ( pfb_len + 2 < 6 ) {
FT_TRACE2(( " too long fragment length makes"
" pfb_len confused: pfb_len=0x%08x\n", pfb_len ));
error = FT_THROW( Array_Too_Large );
goto Exit;
}
if ( FT_ALLOC( pfb_data, (FT_Long)pfb_len + 2 ) )
goto Exit;
pfb_data[0] = 0x80;
pfb_data[1] = 1; /* Ascii section */
pfb_data[2] = 0; /* 4-byte length, fill in later */
pfb_data[3] = 0;
pfb_data[4] = 0;
pfb_data[5] = 0;
pfb_pos = 6;
pfb_lenpos = 2;
len = 0;
type = 1;
for ( i = 0; i < resource_cnt; ++i )
{
error = FT_Stream_Seek( stream, (FT_ULong)offsets[i] );
if ( error )
goto Exit2;
if ( FT_READ_ULONG( rlen ) )
goto Exit2;
/* FT2 allocator takes signed long buffer length,
* too large fragment length causing overflow should be checked
*/
if ( 0x7FFFFFFFUL < rlen )
{
error = FT_THROW( Invalid_Offset );
goto Exit2;
}
if ( FT_READ_USHORT( flags ) )
goto Exit2;
FT_TRACE3(( "POST fragment[%d]: offsets=0x%08x, rlen=0x%08x, flags=0x%04x\n",
i, offsets[i], rlen, flags ));
error = FT_ERR( Array_Too_Large );
/* postpone the check of rlen longer than buffer until FT_Stream_Read() */
if ( ( flags >> 8 ) == 0 ) /* Comment, should not be loaded */
{
FT_TRACE3(( " Skip POST fragment #%d because it is a comment\n", i ));
continue;
}
/* the flags are part of the resource, so rlen >= 2. */
/* but some fonts declare rlen = 0 for empty fragment */
if ( rlen > 2 )
rlen -= 2;
else
rlen = 0;
if ( ( flags >> 8 ) == type )
len += rlen;
else
{
FT_TRACE3(( " Write POST fragment #%d header (4-byte) to buffer"
" %p + 0x%08x\n", i, pfb_data, pfb_lenpos ));
if ( pfb_lenpos + 3 > pfb_len + 2 )
goto Exit2;
pfb_data[pfb_lenpos ] = (FT_Byte)( len );
pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
if ( ( flags >> 8 ) == 5 ) /* End of font mark */
break;
FT_TRACE3(( " Write POST fragment #%d header (6-byte) to buffer"
" %p + 0x%08x\n", i, pfb_data, pfb_pos ));
if ( pfb_pos + 6 > pfb_len + 2 )
goto Exit2;
pfb_data[pfb_pos++] = 0x80;
type = flags >> 8;
len = rlen;
pfb_data[pfb_pos++] = (FT_Byte)type;
pfb_lenpos = pfb_pos;
pfb_data[pfb_pos++] = 0; /* 4-byte length, fill in later */
pfb_data[pfb_pos++] = 0;
pfb_data[pfb_pos++] = 0;
pfb_data[pfb_pos++] = 0;
}
if ( pfb_pos > pfb_len || pfb_pos + rlen > pfb_len )
goto Exit2;
FT_TRACE3(( " Load POST fragment #%d (%d byte) to buffer"
" %p + 0x%08x\n", i, rlen, pfb_data, pfb_pos ));
error = FT_Stream_Read( stream, (FT_Byte *)pfb_data + pfb_pos, rlen );
if ( error )
goto Exit2;
pfb_pos += rlen;
}
error = FT_ERR( Array_Too_Large );
if ( pfb_pos + 2 > pfb_len + 2 )
goto Exit2;
pfb_data[pfb_pos++] = 0x80;
pfb_data[pfb_pos++] = 3;
if ( pfb_lenpos + 3 > pfb_len + 2 )
goto Exit2;
pfb_data[pfb_lenpos ] = (FT_Byte)( len );
pfb_data[pfb_lenpos + 1] = (FT_Byte)( len >> 8 );
pfb_data[pfb_lenpos + 2] = (FT_Byte)( len >> 16 );
pfb_data[pfb_lenpos + 3] = (FT_Byte)( len >> 24 );
return open_face_from_buffer( library,
pfb_data,
pfb_pos,
face_index,
"type1",
aface );
Exit2:
if ( error == FT_ERR( Array_Too_Large ) )
FT_TRACE2(( " Abort due to too-short buffer to store"
" all POST fragments\n" ));
else if ( error == FT_ERR( Invalid_Offset ) )
FT_TRACE2(( " Abort due to invalid offset in a POST fragment\n" ));
if ( error )
error = FT_ERR( Cannot_Open_Resource );
FT_FREE( pfb_data );
Exit:
return error;
}
/* The resource header says we've got resource_cnt `sfnt' */
/* (TrueType/OpenType) resources in this file. Look through */
/* them for the one indicated by face_index, load it into mem, */
/* pass it on to the truetype driver, and return it. */
/* */
static FT_Error
Mac_Read_sfnt_Resource( FT_Library library,
FT_Stream stream,
FT_Long *offsets,
FT_Long resource_cnt,
FT_Long face_index,
FT_Face *aface )
{
FT_Memory memory = library->memory;
FT_Byte* sfnt_data = NULL;
FT_Error error;
FT_ULong flag_offset;
FT_Long rlen;
int is_cff;
FT_Long face_index_in_resource = 0;
if ( face_index == -1 )
face_index = 0;
if ( face_index >= resource_cnt )
return FT_THROW( Cannot_Open_Resource );
flag_offset = (FT_ULong)offsets[face_index];
error = FT_Stream_Seek( stream, flag_offset );
if ( error )
goto Exit;
if ( FT_READ_LONG( rlen ) )
goto Exit;
if ( rlen == -1 )
return FT_THROW( Cannot_Open_Resource );
if ( (FT_ULong)rlen > FT_MAC_RFORK_MAX_LEN )
return FT_THROW( Invalid_Offset );
error = open_face_PS_from_sfnt_stream( library,
stream,
face_index,
0, NULL,
aface );
if ( !error )
goto Exit;
/* rewind sfnt stream before open_face_PS_from_sfnt_stream() */
error = FT_Stream_Seek( stream, flag_offset + 4 );
if ( error )
goto Exit;
if ( FT_ALLOC( sfnt_data, rlen ) )
return error;
error = FT_Stream_Read( stream, (FT_Byte *)sfnt_data, (FT_ULong)rlen );
if ( error ) {
FT_FREE( sfnt_data );
goto Exit;
}
is_cff = rlen > 4 && !ft_memcmp( sfnt_data, "OTTO", 4 );
error = open_face_from_buffer( library,
sfnt_data,
(FT_ULong)rlen,
face_index_in_resource,
is_cff ? "cff" : "truetype",
aface );
Exit:
return error;
}
/* Check for a valid resource fork header, or a valid dfont */
/* header. In a resource fork the first 16 bytes are repeated */
/* at the location specified by bytes 4-7. In a dfont bytes */
/* 4-7 point to 16 bytes of zeroes instead. */
/* */
static FT_Error
IsMacResource( FT_Library library,
FT_Stream stream,
FT_Long resource_offset,
FT_Long face_index,
FT_Face *aface )
{
FT_Memory memory = library->memory;
FT_Error error;
FT_Long map_offset, rdara_pos;
FT_Long *data_offsets;
FT_Long count;
error = FT_Raccess_Get_HeaderInfo( library, stream, resource_offset,
&map_offset, &rdara_pos );
if ( error )
return error;
/* POST resources must be sorted to concatenate properly */
error = FT_Raccess_Get_DataOffsets( library, stream,
map_offset, rdara_pos,
TTAG_POST, TRUE,
&data_offsets, &count );
if ( !error )
{
error = Mac_Read_POST_Resource( library, stream, data_offsets, count,
face_index, aface );
FT_FREE( data_offsets );
/* POST exists in an LWFN providing a single face */
if ( !error )
(*aface)->num_faces = 1;
return error;
}
/* sfnt resources should not be sorted to preserve the face order by
QuickDraw API */
error = FT_Raccess_Get_DataOffsets( library, stream,
map_offset, rdara_pos,
TTAG_sfnt, FALSE,
&data_offsets, &count );
if ( !error )
{
FT_Long face_index_internal = face_index % count;
error = Mac_Read_sfnt_Resource( library, stream, data_offsets, count,
face_index_internal, aface );
FT_FREE( data_offsets );
if ( !error )
(*aface)->num_faces = count;
}
return error;
}
/* Check for a valid macbinary header, and if we find one */
/* check that the (flattened) resource fork in it is valid. */
/* */
static FT_Error
IsMacBinary( FT_Library library,
FT_Stream stream,
FT_Long face_index,
FT_Face *aface )
{
unsigned char header[128];
FT_Error error;
FT_Long dlen, offset;
if ( NULL == stream )
return FT_THROW( Invalid_Stream_Operation );
error = FT_Stream_Seek( stream, 0 );
if ( error )
goto Exit;
error = FT_Stream_Read( stream, (FT_Byte*)header, 128 );
if ( error )
goto Exit;
if ( header[ 0] != 0 ||
header[74] != 0 ||
header[82] != 0 ||
header[ 1] == 0 ||
header[ 1] > 33 ||
header[63] != 0 ||
header[2 + header[1]] != 0 ||
header[0x53] > 0x7F )
return FT_THROW( Unknown_File_Format );
dlen = ( header[0x53] << 24 ) |
( header[0x54] << 16 ) |
( header[0x55] << 8 ) |
header[0x56];
#if 0
rlen = ( header[0x57] << 24 ) |
( header[0x58] << 16 ) |
( header[0x59] << 8 ) |
header[0x5A];
#endif /* 0 */
offset = 128 + ( ( dlen + 127 ) & ~127 );
return IsMacResource( library, stream, offset, face_index, aface );
Exit:
return error;
}
static FT_Error
load_face_in_embedded_rfork( FT_Library library,
FT_Stream stream,
FT_Long face_index,
FT_Face *aface,
const FT_Open_Args *args )
{
#undef FT_COMPONENT
#define FT_COMPONENT trace_raccess
FT_Memory memory = library->memory;
FT_Error error = FT_ERR( Unknown_File_Format );
FT_UInt i;
char * file_names[FT_RACCESS_N_RULES];
FT_Long offsets[FT_RACCESS_N_RULES];
FT_Error errors[FT_RACCESS_N_RULES];
FT_Bool is_darwin_vfs, vfs_rfork_has_no_font = FALSE; /* not tested */
FT_Open_Args args2;
FT_Stream stream2 = NULL;
FT_Raccess_Guess( library, stream,
args->pathname, file_names, offsets, errors );
for ( i = 0; i < FT_RACCESS_N_RULES; i++ )
{
is_darwin_vfs = ft_raccess_rule_by_darwin_vfs( library, i );
if ( is_darwin_vfs && vfs_rfork_has_no_font )
{
FT_TRACE3(( "Skip rule %d: darwin vfs resource fork"
" is already checked and"
" no font is found\n", i ));
continue;
}
if ( errors[i] )
{
FT_TRACE3(( "Error[%d] has occurred in rule %d\n", errors[i], i ));
continue;
}
args2.flags = FT_OPEN_PATHNAME;
args2.pathname = file_names[i] ? file_names[i] : args->pathname;
FT_TRACE3(( "Try rule %d: %s (offset=%d) ...",
i, args2.pathname, offsets[i] ));
error = FT_Stream_New( library, &args2, &stream2 );
if ( is_darwin_vfs && FT_ERR_EQ( error, Cannot_Open_Stream ) )
vfs_rfork_has_no_font = TRUE;
if ( error )
{
FT_TRACE3(( "failed\n" ));
continue;
}
error = IsMacResource( library, stream2, offsets[i],
face_index, aface );
FT_Stream_Free( stream2, 0 );
FT_TRACE3(( "%s\n", error ? "failed": "successful" ));
if ( !error )
break;
else if ( is_darwin_vfs )
vfs_rfork_has_no_font = TRUE;
}
for (i = 0; i < FT_RACCESS_N_RULES; i++)
{
if ( file_names[i] )
FT_FREE( file_names[i] );
}
/* Caller (load_mac_face) requires FT_Err_Unknown_File_Format. */
if ( error )
error = FT_ERR( Unknown_File_Format );
return error;
#undef FT_COMPONENT
#define FT_COMPONENT trace_objs
}
/* Check for some macintosh formats without Carbon framework. */
/* Is this a macbinary file? If so look at the resource fork. */
/* Is this a mac dfont file? */
/* Is this an old style resource fork? (in data) */
/* Else call load_face_in_embedded_rfork to try extra rules */
/* (defined in `ftrfork.c'). */
/* */
static FT_Error
load_mac_face( FT_Library library,
FT_Stream stream,
FT_Long face_index,
FT_Face *aface,
const FT_Open_Args *args )
{
FT_Error error;
FT_UNUSED( args );
error = IsMacBinary( library, stream, face_index, aface );
if ( FT_ERR_EQ( error, Unknown_File_Format ) )
{
#undef FT_COMPONENT
#define FT_COMPONENT trace_raccess
#ifdef FT_DEBUG_LEVEL_TRACE
FT_TRACE3(( "Try as dfont: " ));
if ( !( args->flags & FT_OPEN_MEMORY ) )
FT_TRACE3(( "%s ...", args->pathname ));
#endif
error = IsMacResource( library, stream, 0, face_index, aface );
FT_TRACE3(( "%s\n", error ? "failed" : "successful" ));
#undef FT_COMPONENT
#define FT_COMPONENT trace_objs
}
if ( ( FT_ERR_EQ( error, Unknown_File_Format ) ||
FT_ERR_EQ( error, Invalid_Stream_Operation ) ) &&
( args->flags & FT_OPEN_PATHNAME ) )
error = load_face_in_embedded_rfork( library, stream,
face_index, aface, args );
return error;
}
#endif
#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Open_Face( FT_Library library,
const FT_Open_Args* args,
FT_Long face_index,
FT_Face *aface )
{
FT_Error error;
FT_Driver driver = NULL;
FT_Memory memory = NULL;
FT_Stream stream = NULL;
FT_Face face = NULL;
FT_ListNode node = NULL;
FT_Bool external_stream;
FT_Module* cur;
FT_Module* limit;
/* test for valid `library' delayed to `FT_Stream_New' */
if ( ( !aface && face_index >= 0 ) || !args )
return FT_THROW( Invalid_Argument );
external_stream = FT_BOOL( ( args->flags & FT_OPEN_STREAM ) &&
args->stream );
/* create input stream */
error = FT_Stream_New( library, args, &stream );
if ( error )
goto Fail3;
memory = library->memory;
/* If the font driver is specified in the `args' structure, use */
/* it. Otherwise, we scan the list of registered drivers. */
if ( ( args->flags & FT_OPEN_DRIVER ) && args->driver )
{
driver = FT_DRIVER( args->driver );
/* not all modules are drivers, so check... */
if ( FT_MODULE_IS_DRIVER( driver ) )
{
FT_Int num_params = 0;
FT_Parameter* params = NULL;
if ( args->flags & FT_OPEN_PARAMS )
{
num_params = args->num_params;
params = args->params;
}
error = open_face( driver, &stream, external_stream, face_index,
num_params, params, &face );
if ( !error )
goto Success;
}
else
error = FT_THROW( Invalid_Handle );
FT_Stream_Free( stream, external_stream );
goto Fail;
}
else
{
error = FT_ERR( Missing_Module );
/* check each font driver for an appropriate format */
cur = library->modules;
limit = cur + library->num_modules;
for ( ; cur < limit; cur++ )
{
/* not all modules are font drivers, so check... */
if ( FT_MODULE_IS_DRIVER( cur[0] ) )
{
FT_Int num_params = 0;
FT_Parameter* params = NULL;
driver = FT_DRIVER( cur[0] );
if ( args->flags & FT_OPEN_PARAMS )
{
num_params = args->num_params;
params = args->params;
}
error = open_face( driver, &stream, external_stream, face_index,
num_params, params, &face );
if ( !error )
goto Success;
#ifdef FT_CONFIG_OPTION_MAC_FONTS
if ( ft_strcmp( cur[0]->clazz->module_name, "truetype" ) == 0 &&
FT_ERR_EQ( error, Table_Missing ) )
{
/* TrueType but essential tables are missing */
error = FT_Stream_Seek( stream, 0 );
if ( error )
break;
error = open_face_PS_from_sfnt_stream( library,
stream,
face_index,
num_params,
params,
aface );
if ( !error )
{
FT_Stream_Free( stream, external_stream );
return error;
}
}
#endif
if ( FT_ERR_NEQ( error, Unknown_File_Format ) )
goto Fail3;
}
}
Fail3:
/* If we are on the mac, and we get an */
/* FT_Err_Invalid_Stream_Operation it may be because we have an */
/* empty data fork, so we need to check the resource fork. */
if ( FT_ERR_NEQ( error, Cannot_Open_Stream ) &&
FT_ERR_NEQ( error, Unknown_File_Format ) &&
FT_ERR_NEQ( error, Invalid_Stream_Operation ) )
goto Fail2;
#if !defined( FT_MACINTOSH ) && defined( FT_CONFIG_OPTION_MAC_FONTS )
error = load_mac_face( library, stream, face_index, aface, args );
if ( !error )
{
/* We don't want to go to Success here. We've already done that. */
/* On the other hand, if we succeeded we still need to close this */
/* stream (we opened a different stream which extracted the */
/* interesting information out of this stream here. That stream */
/* will still be open and the face will point to it). */
FT_Stream_Free( stream, external_stream );
return error;
}
if ( FT_ERR_NEQ( error, Unknown_File_Format ) )
goto Fail2;
#endif /* !FT_MACINTOSH && FT_CONFIG_OPTION_MAC_FONTS */
/* no driver is able to handle this format */
error = FT_THROW( Unknown_File_Format );
Fail2:
FT_Stream_Free( stream, external_stream );
goto Fail;
}
Success:
FT_TRACE4(( "FT_Open_Face: New face object, adding to list\n" ));
/* add the face object to its driver's list */
if ( FT_NEW( node ) )
goto Fail;
node->data = face;
/* don't assume driver is the same as face->driver, so use */
/* face->driver instead. */
FT_List_Add( &face->driver->faces_list, node );
/* now allocate a glyph slot object for the face */
FT_TRACE4(( "FT_Open_Face: Creating glyph slot\n" ));
if ( face_index >= 0 )
{
error = FT_New_GlyphSlot( face, NULL );
if ( error )
goto Fail;
/* finally, allocate a size object for the face */
{
FT_Size size;
FT_TRACE4(( "FT_Open_Face: Creating size object\n" ));
error = FT_New_Size( face, &size );
if ( error )
goto Fail;
face->size = size;
}
}
/* some checks */
if ( FT_IS_SCALABLE( face ) )
{
if ( face->height < 0 )
face->height = (FT_Short)-face->height;
if ( !FT_HAS_VERTICAL( face ) )
face->max_advance_height = (FT_Short)face->height;
}
if ( FT_HAS_FIXED_SIZES( face ) )
{
FT_Int i;
for ( i = 0; i < face->num_fixed_sizes; i++ )
{
FT_Bitmap_Size* bsize = face->available_sizes + i;
if ( bsize->height < 0 )
bsize->height = (FT_Short)-bsize->height;
if ( bsize->x_ppem < 0 )
bsize->x_ppem = (FT_Short)-bsize->x_ppem;
if ( bsize->y_ppem < 0 )
bsize->y_ppem = -bsize->y_ppem;
}
}
/* initialize internal face data */
{
FT_Face_Internal internal = face->internal;
internal->transform_matrix.xx = 0x10000L;
internal->transform_matrix.xy = 0;
internal->transform_matrix.yx = 0;
internal->transform_matrix.yy = 0x10000L;
internal->transform_delta.x = 0;
internal->transform_delta.y = 0;
internal->refcount = 1;
}
if ( aface )
*aface = face;
else
FT_Done_Face( face );
goto Exit;
Fail:
if ( node )
FT_Done_Face( face ); /* face must be in the driver's list */
else if ( face )
destroy_face( memory, face, driver );
Exit:
FT_TRACE4(( "FT_Open_Face: Return %d\n", error ));
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Attach_File( FT_Face face,
const char* filepathname )
{
FT_Open_Args open;
/* test for valid `face' delayed to `FT_Attach_Stream' */
if ( !filepathname )
return FT_THROW( Invalid_Argument );
open.stream = NULL;
open.flags = FT_OPEN_PATHNAME;
open.pathname = (char*)filepathname;
return FT_Attach_Stream( face, &open );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Attach_Stream( FT_Face face,
FT_Open_Args* parameters )
{
FT_Stream stream;
FT_Error error;
FT_Driver driver;
FT_Driver_Class clazz;
/* test for valid `parameters' delayed to `FT_Stream_New' */
if ( !face )
return FT_THROW( Invalid_Face_Handle );
driver = face->driver;
if ( !driver )
return FT_THROW( Invalid_Driver_Handle );
error = FT_Stream_New( driver->root.library, parameters, &stream );
if ( error )
goto Exit;
/* we implement FT_Attach_Stream in each driver through the */
/* `attach_file' interface */
error = FT_ERR( Unimplemented_Feature );
clazz = driver->clazz;
if ( clazz->attach_file )
error = clazz->attach_file( face, stream );
/* close the attached stream */
FT_Stream_Free( stream,
(FT_Bool)( parameters->stream &&
( parameters->flags & FT_OPEN_STREAM ) ) );
Exit:
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Reference_Face( FT_Face face )
{
if ( !face )
return FT_THROW( Invalid_Face_Handle );
face->internal->refcount++;
return FT_Err_Ok;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Done_Face( FT_Face face )
{
FT_Error error;
FT_Driver driver;
FT_Memory memory;
FT_ListNode node;
error = FT_ERR( Invalid_Face_Handle );
if ( face && face->driver )
{
face->internal->refcount--;
if ( face->internal->refcount > 0 )
error = FT_Err_Ok;
else
{
driver = face->driver;
memory = driver->root.memory;
/* find face in driver's list */
node = FT_List_Find( &driver->faces_list, face );
if ( node )
{
/* remove face object from the driver's list */
FT_List_Remove( &driver->faces_list, node );
FT_FREE( node );
/* now destroy the object proper */
destroy_face( memory, face, driver );
error = FT_Err_Ok;
}
}
}
return error;
}
/* documentation is in ftobjs.h */
FT_EXPORT_DEF( FT_Error )
FT_New_Size( FT_Face face,
FT_Size *asize )
{
FT_Error error;
FT_Memory memory;
FT_Driver driver;
FT_Driver_Class clazz;
FT_Size size = NULL;
FT_ListNode node = NULL;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !asize )
return FT_THROW( Invalid_Argument );
if ( !face->driver )
return FT_THROW( Invalid_Driver_Handle );
*asize = NULL;
driver = face->driver;
clazz = driver->clazz;
memory = face->memory;
/* Allocate new size object and perform basic initialisation */
if ( FT_ALLOC( size, clazz->size_object_size ) || FT_NEW( node ) )
goto Exit;
size->face = face;
/* for now, do not use any internal fields in size objects */
size->internal = NULL;
if ( clazz->init_size )
error = clazz->init_size( size );
/* in case of success, add to the face's list */
if ( !error )
{
*asize = size;
node->data = size;
FT_List_Add( &face->sizes_list, node );
}
Exit:
if ( error )
{
FT_FREE( node );
FT_FREE( size );
}
return error;
}
/* documentation is in ftobjs.h */
FT_EXPORT_DEF( FT_Error )
FT_Done_Size( FT_Size size )
{
FT_Error error;
FT_Driver driver;
FT_Memory memory;
FT_Face face;
FT_ListNode node;
if ( !size )
return FT_THROW( Invalid_Size_Handle );
face = size->face;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
driver = face->driver;
if ( !driver )
return FT_THROW( Invalid_Driver_Handle );
memory = driver->root.memory;
error = FT_Err_Ok;
node = FT_List_Find( &face->sizes_list, size );
if ( node )
{
FT_List_Remove( &face->sizes_list, node );
FT_FREE( node );
if ( face->size == size )
{
face->size = NULL;
if ( face->sizes_list.head )
face->size = (FT_Size)(face->sizes_list.head->data);
}
destroy_size( memory, size, driver );
}
else
error = FT_THROW( Invalid_Size_Handle );
return error;
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( FT_Error )
FT_Match_Size( FT_Face face,
FT_Size_Request req,
FT_Bool ignore_width,
FT_ULong* size_index )
{
FT_Int i;
FT_Long w, h;
if ( !FT_HAS_FIXED_SIZES( face ) )
return FT_THROW( Invalid_Face_Handle );
/* FT_Bitmap_Size doesn't provide enough info... */
if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
return FT_THROW( Unimplemented_Feature );
w = FT_REQUEST_WIDTH ( req );
h = FT_REQUEST_HEIGHT( req );
if ( req->width && !req->height )
h = w;
else if ( !req->width && req->height )
w = h;
w = FT_PIX_ROUND( w );
h = FT_PIX_ROUND( h );
for ( i = 0; i < face->num_fixed_sizes; i++ )
{
FT_Bitmap_Size* bsize = face->available_sizes + i;
if ( h != FT_PIX_ROUND( bsize->y_ppem ) )
continue;
if ( w == FT_PIX_ROUND( bsize->x_ppem ) || ignore_width )
{
FT_TRACE3(( "FT_Match_Size: bitmap strike %d matches\n", i ));
if ( size_index )
*size_index = (FT_ULong)i;
return FT_Err_Ok;
}
}
return FT_THROW( Invalid_Pixel_Size );
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( void )
ft_synthesize_vertical_metrics( FT_Glyph_Metrics* metrics,
FT_Pos advance )
{
FT_Pos height = metrics->height;
/* compensate for glyph with bbox above/below the baseline */
if ( metrics->horiBearingY < 0 )
{
if ( height < metrics->horiBearingY )
height = metrics->horiBearingY;
}
else if ( metrics->horiBearingY > 0 )
height -= metrics->horiBearingY;
/* the factor 1.2 is a heuristical value */
if ( !advance )
advance = height * 12 / 10;
metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2;
metrics->vertBearingY = ( advance - height ) / 2;
metrics->vertAdvance = advance;
}
static void
ft_recompute_scaled_metrics( FT_Face face,
FT_Size_Metrics* metrics )
{
/* Compute root ascender, descender, test height, and max_advance */
#ifdef GRID_FIT_METRICS
metrics->ascender = FT_PIX_CEIL( FT_MulFix( face->ascender,
metrics->y_scale ) );
metrics->descender = FT_PIX_FLOOR( FT_MulFix( face->descender,
metrics->y_scale ) );
metrics->height = FT_PIX_ROUND( FT_MulFix( face->height,
metrics->y_scale ) );
metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->max_advance_width,
metrics->x_scale ) );
#else /* !GRID_FIT_METRICS */
metrics->ascender = FT_MulFix( face->ascender,
metrics->y_scale );
metrics->descender = FT_MulFix( face->descender,
metrics->y_scale );
metrics->height = FT_MulFix( face->height,
metrics->y_scale );
metrics->max_advance = FT_MulFix( face->max_advance_width,
metrics->x_scale );
#endif /* !GRID_FIT_METRICS */
}
FT_BASE_DEF( void )
FT_Select_Metrics( FT_Face face,
FT_ULong strike_index )
{
FT_Size_Metrics* metrics;
FT_Bitmap_Size* bsize;
metrics = &face->size->metrics;
bsize = face->available_sizes + strike_index;
metrics->x_ppem = (FT_UShort)( ( bsize->x_ppem + 32 ) >> 6 );
metrics->y_ppem = (FT_UShort)( ( bsize->y_ppem + 32 ) >> 6 );
if ( FT_IS_SCALABLE( face ) )
{
metrics->x_scale = FT_DivFix( bsize->x_ppem,
face->units_per_EM );
metrics->y_scale = FT_DivFix( bsize->y_ppem,
face->units_per_EM );
ft_recompute_scaled_metrics( face, metrics );
}
else
{
metrics->x_scale = 1L << 16;
metrics->y_scale = 1L << 16;
metrics->ascender = bsize->y_ppem;
metrics->descender = 0;
metrics->height = bsize->height << 6;
metrics->max_advance = bsize->x_ppem;
}
FT_TRACE5(( "FT_Select_Metrics:\n" ));
FT_TRACE5(( " x scale: %d (%f)\n",
metrics->x_scale, metrics->x_scale / 65536.0 ));
FT_TRACE5(( " y scale: %d (%f)\n",
metrics->y_scale, metrics->y_scale / 65536.0 ));
FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
FT_BASE_DEF( void )
FT_Request_Metrics( FT_Face face,
FT_Size_Request req )
{
FT_Size_Metrics* metrics;
metrics = &face->size->metrics;
if ( FT_IS_SCALABLE( face ) )
{
FT_Long w = 0, h = 0, scaled_w = 0, scaled_h = 0;
switch ( req->type )
{
case FT_SIZE_REQUEST_TYPE_NOMINAL:
w = h = face->units_per_EM;
break;
case FT_SIZE_REQUEST_TYPE_REAL_DIM:
w = h = face->ascender - face->descender;
break;
case FT_SIZE_REQUEST_TYPE_BBOX:
w = face->bbox.xMax - face->bbox.xMin;
h = face->bbox.yMax - face->bbox.yMin;
break;
case FT_SIZE_REQUEST_TYPE_CELL:
w = face->max_advance_width;
h = face->ascender - face->descender;
break;
case FT_SIZE_REQUEST_TYPE_SCALES:
metrics->x_scale = (FT_Fixed)req->width;
metrics->y_scale = (FT_Fixed)req->height;
if ( !metrics->x_scale )
metrics->x_scale = metrics->y_scale;
else if ( !metrics->y_scale )
metrics->y_scale = metrics->x_scale;
goto Calculate_Ppem;
case FT_SIZE_REQUEST_TYPE_MAX:
break;
}
/* to be on the safe side */
if ( w < 0 )
w = -w;
if ( h < 0 )
h = -h;
scaled_w = FT_REQUEST_WIDTH ( req );
scaled_h = FT_REQUEST_HEIGHT( req );
/* determine scales */
if ( req->width )
{
metrics->x_scale = FT_DivFix( scaled_w, w );
if ( req->height )
{
metrics->y_scale = FT_DivFix( scaled_h, h );
if ( req->type == FT_SIZE_REQUEST_TYPE_CELL )
{
if ( metrics->y_scale > metrics->x_scale )
metrics->y_scale = metrics->x_scale;
else
metrics->x_scale = metrics->y_scale;
}
}
else
{
metrics->y_scale = metrics->x_scale;
scaled_h = FT_MulDiv( scaled_w, h, w );
}
}
else
{
metrics->x_scale = metrics->y_scale = FT_DivFix( scaled_h, h );
scaled_w = FT_MulDiv( scaled_h, w, h );
}
Calculate_Ppem:
/* calculate the ppems */
if ( req->type != FT_SIZE_REQUEST_TYPE_NOMINAL )
{
scaled_w = FT_MulFix( face->units_per_EM, metrics->x_scale );
scaled_h = FT_MulFix( face->units_per_EM, metrics->y_scale );
}
metrics->x_ppem = (FT_UShort)( ( scaled_w + 32 ) >> 6 );
metrics->y_ppem = (FT_UShort)( ( scaled_h + 32 ) >> 6 );
ft_recompute_scaled_metrics( face, metrics );
}
else
{
FT_ZERO( metrics );
metrics->x_scale = 1L << 16;
metrics->y_scale = 1L << 16;
}
FT_TRACE5(( "FT_Request_Metrics:\n" ));
FT_TRACE5(( " x scale: %d (%f)\n",
metrics->x_scale, metrics->x_scale / 65536.0 ));
FT_TRACE5(( " y scale: %d (%f)\n",
metrics->y_scale, metrics->y_scale / 65536.0 ));
FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Select_Size( FT_Face face,
FT_Int strike_index )
{
FT_Driver_Class clazz;
if ( !face || !FT_HAS_FIXED_SIZES( face ) )
return FT_THROW( Invalid_Face_Handle );
if ( strike_index < 0 || strike_index >= face->num_fixed_sizes )
return FT_THROW( Invalid_Argument );
clazz = face->driver->clazz;
if ( clazz->select_size )
{
FT_Error error;
error = clazz->select_size( face->size, (FT_ULong)strike_index );
#ifdef FT_DEBUG_LEVEL_TRACE
{
FT_Size_Metrics* metrics = &face->size->metrics;
FT_TRACE5(( "FT_Select_Size (font driver's `select_size'):\n" ));
FT_TRACE5(( " x scale: %d (%f)\n",
metrics->x_scale, metrics->x_scale / 65536.0 ));
FT_TRACE5(( " y scale: %d (%f)\n",
metrics->y_scale, metrics->y_scale / 65536.0 ));
FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
#endif
return error;
}
FT_Select_Metrics( face, (FT_ULong)strike_index );
return FT_Err_Ok;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Request_Size( FT_Face face,
FT_Size_Request req )
{
FT_Driver_Class clazz;
FT_ULong strike_index;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !req || req->width < 0 || req->height < 0 ||
req->type >= FT_SIZE_REQUEST_TYPE_MAX )
return FT_THROW( Invalid_Argument );
clazz = face->driver->clazz;
if ( clazz->request_size )
{
FT_Error error;
error = clazz->request_size( face->size, req );
#ifdef FT_DEBUG_LEVEL_TRACE
{
FT_Size_Metrics* metrics = &face->size->metrics;
FT_TRACE5(( "FT_Request_Size (font driver's `request_size'):\n" ));
FT_TRACE5(( " x scale: %d (%f)\n",
metrics->x_scale, metrics->x_scale / 65536.0 ));
FT_TRACE5(( " y scale: %d (%f)\n",
metrics->y_scale, metrics->y_scale / 65536.0 ));
FT_TRACE5(( " ascender: %f\n", metrics->ascender / 64.0 ));
FT_TRACE5(( " descender: %f\n", metrics->descender / 64.0 ));
FT_TRACE5(( " height: %f\n", metrics->height / 64.0 ));
FT_TRACE5(( " max advance: %f\n", metrics->max_advance / 64.0 ));
FT_TRACE5(( " x ppem: %d\n", metrics->x_ppem ));
FT_TRACE5(( " y ppem: %d\n", metrics->y_ppem ));
}
#endif
return error;
}
/*
* The reason that a driver doesn't have `request_size' defined is
* either that the scaling here suffices or that the supported formats
* are bitmap-only and size matching is not implemented.
*
* In the latter case, a simple size matching is done.
*/
if ( !FT_IS_SCALABLE( face ) && FT_HAS_FIXED_SIZES( face ) )
{
FT_Error error;
error = FT_Match_Size( face, req, 0, &strike_index );
if ( error )
return error;
return FT_Select_Size( face, (FT_Int)strike_index );
}
FT_Request_Metrics( face, req );
return FT_Err_Ok;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Set_Char_Size( FT_Face face,
FT_F26Dot6 char_width,
FT_F26Dot6 char_height,
FT_UInt horz_resolution,
FT_UInt vert_resolution )
{
FT_Size_RequestRec req;
/* check of `face' delayed to `FT_Request_Size' */
if ( !char_width )
char_width = char_height;
else if ( !char_height )
char_height = char_width;
if ( !horz_resolution )
horz_resolution = vert_resolution;
else if ( !vert_resolution )
vert_resolution = horz_resolution;
if ( char_width < 1 * 64 )
char_width = 1 * 64;
if ( char_height < 1 * 64 )
char_height = 1 * 64;
if ( !horz_resolution )
horz_resolution = vert_resolution = 72;
req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
req.width = char_width;
req.height = char_height;
req.horiResolution = horz_resolution;
req.vertResolution = vert_resolution;
return FT_Request_Size( face, &req );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Set_Pixel_Sizes( FT_Face face,
FT_UInt pixel_width,
FT_UInt pixel_height )
{
FT_Size_RequestRec req;
/* check of `face' delayed to `FT_Request_Size' */
if ( pixel_width == 0 )
pixel_width = pixel_height;
else if ( pixel_height == 0 )
pixel_height = pixel_width;
if ( pixel_width < 1 )
pixel_width = 1;
if ( pixel_height < 1 )
pixel_height = 1;
/* use `>=' to avoid potential compiler warning on 16bit platforms */
if ( pixel_width >= 0xFFFFU )
pixel_width = 0xFFFFU;
if ( pixel_height >= 0xFFFFU )
pixel_height = 0xFFFFU;
req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
req.width = (FT_Long)( pixel_width << 6 );
req.height = (FT_Long)( pixel_height << 6 );
req.horiResolution = 0;
req.vertResolution = 0;
return FT_Request_Size( face, &req );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Get_Kerning( FT_Face face,
FT_UInt left_glyph,
FT_UInt right_glyph,
FT_UInt kern_mode,
FT_Vector *akerning )
{
FT_Error error = FT_Err_Ok;
FT_Driver driver;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !akerning )
return FT_THROW( Invalid_Argument );
driver = face->driver;
akerning->x = 0;
akerning->y = 0;
if ( driver->clazz->get_kerning )
{
error = driver->clazz->get_kerning( face,
left_glyph,
right_glyph,
akerning );
if ( !error )
{
if ( kern_mode != FT_KERNING_UNSCALED )
{
akerning->x = FT_MulFix( akerning->x, face->size->metrics.x_scale );
akerning->y = FT_MulFix( akerning->y, face->size->metrics.y_scale );
if ( kern_mode != FT_KERNING_UNFITTED )
{
FT_Pos orig_x = akerning->x;
FT_Pos orig_y = akerning->y;
/* we scale down kerning values for small ppem values */
/* to avoid that rounding makes them too big. */
/* `25' has been determined heuristically. */
if ( face->size->metrics.x_ppem < 25 )
akerning->x = FT_MulDiv( orig_x,
face->size->metrics.x_ppem, 25 );
if ( face->size->metrics.y_ppem < 25 )
akerning->y = FT_MulDiv( orig_y,
face->size->metrics.y_ppem, 25 );
akerning->x = FT_PIX_ROUND( akerning->x );
akerning->y = FT_PIX_ROUND( akerning->y );
#ifdef FT_DEBUG_LEVEL_TRACE
{
FT_Pos orig_x_rounded = FT_PIX_ROUND( orig_x );
FT_Pos orig_y_rounded = FT_PIX_ROUND( orig_y );
if ( akerning->x != orig_x_rounded ||
akerning->y != orig_y_rounded )
FT_TRACE5(( "FT_Get_Kerning: horizontal kerning"
" (%d, %d) scaled down to (%d, %d) pixels\n",
orig_x_rounded / 64, orig_y_rounded / 64,
akerning->x / 64, akerning->y / 64 ));
}
#endif
}
}
}
}
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Get_Track_Kerning( FT_Face face,
FT_Fixed point_size,
FT_Int degree,
FT_Fixed* akerning )
{
FT_Service_Kerning service;
FT_Error error = FT_Err_Ok;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !akerning )
return FT_THROW( Invalid_Argument );
FT_FACE_FIND_SERVICE( face, service, KERNING );
if ( !service )
return FT_THROW( Unimplemented_Feature );
error = service->get_track( face,
point_size,
degree,
akerning );
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Select_Charmap( FT_Face face,
FT_Encoding encoding )
{
FT_CharMap* cur;
FT_CharMap* limit;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( encoding == FT_ENCODING_NONE )
return FT_THROW( Invalid_Argument );
/* FT_ENCODING_UNICODE is special. We try to find the `best' Unicode */
/* charmap available, i.e., one with UCS-4 characters, if possible. */
/* */
/* This is done by find_unicode_charmap() above, to share code. */
if ( encoding == FT_ENCODING_UNICODE )
return find_unicode_charmap( face );
cur = face->charmaps;
if ( !cur )
return FT_THROW( Invalid_CharMap_Handle );
limit = cur + face->num_charmaps;
for ( ; cur < limit; cur++ )
{
if ( cur[0]->encoding == encoding )
{
face->charmap = cur[0];
return 0;
}
}
return FT_THROW( Invalid_Argument );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Set_Charmap( FT_Face face,
FT_CharMap charmap )
{
FT_CharMap* cur;
FT_CharMap* limit;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
cur = face->charmaps;
if ( !cur || !charmap )
return FT_THROW( Invalid_CharMap_Handle );
if ( FT_Get_CMap_Format( charmap ) == 14 )
return FT_THROW( Invalid_Argument );
limit = cur + face->num_charmaps;
for ( ; cur < limit; cur++ )
{
if ( cur[0] == charmap )
{
face->charmap = cur[0];
return FT_Err_Ok;
}
}
return FT_THROW( Invalid_Argument );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Int )
FT_Get_Charmap_Index( FT_CharMap charmap )
{
FT_Int i;
if ( !charmap || !charmap->face )
return -1;
for ( i = 0; i < charmap->face->num_charmaps; i++ )
if ( charmap->face->charmaps[i] == charmap )
break;
FT_ASSERT( i < charmap->face->num_charmaps );
return i;
}
static void
ft_cmap_done_internal( FT_CMap cmap )
{
FT_CMap_Class clazz = cmap->clazz;
FT_Face face = cmap->charmap.face;
FT_Memory memory = FT_FACE_MEMORY( face );
if ( clazz->done )
clazz->done( cmap );
FT_FREE( cmap );
}
FT_BASE_DEF( void )
FT_CMap_Done( FT_CMap cmap )
{
if ( cmap )
{
FT_Face face = cmap->charmap.face;
FT_Memory memory = FT_FACE_MEMORY( face );
FT_Error error;
FT_Int i, j;
for ( i = 0; i < face->num_charmaps; i++ )
{
if ( (FT_CMap)face->charmaps[i] == cmap )
{
FT_CharMap last_charmap = face->charmaps[face->num_charmaps - 1];
if ( FT_RENEW_ARRAY( face->charmaps,
face->num_charmaps,
face->num_charmaps - 1 ) )
return;
/* remove it from our list of charmaps */
for ( j = i + 1; j < face->num_charmaps; j++ )
{
if ( j == face->num_charmaps - 1 )
face->charmaps[j - 1] = last_charmap;
else
face->charmaps[j - 1] = face->charmaps[j];
}
face->num_charmaps--;
if ( (FT_CMap)face->charmap == cmap )
face->charmap = NULL;
ft_cmap_done_internal( cmap );
break;
}
}
}
}
FT_BASE_DEF( FT_Error )
FT_CMap_New( FT_CMap_Class clazz,
FT_Pointer init_data,
FT_CharMap charmap,
FT_CMap *acmap )
{
FT_Error error = FT_Err_Ok;
FT_Face face;
FT_Memory memory;
FT_CMap cmap = NULL;
if ( clazz == NULL || charmap == NULL || charmap->face == NULL )
return FT_THROW( Invalid_Argument );
face = charmap->face;
memory = FT_FACE_MEMORY( face );
if ( !FT_ALLOC( cmap, clazz->size ) )
{
cmap->charmap = *charmap;
cmap->clazz = clazz;
if ( clazz->init )
{
error = clazz->init( cmap, init_data );
if ( error )
goto Fail;
}
/* add it to our list of charmaps */
if ( FT_RENEW_ARRAY( face->charmaps,
face->num_charmaps,
face->num_charmaps + 1 ) )
goto Fail;
face->charmaps[face->num_charmaps++] = (FT_CharMap)cmap;
}
Exit:
if ( acmap )
*acmap = cmap;
return error;
Fail:
ft_cmap_done_internal( cmap );
cmap = NULL;
goto Exit;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt )
FT_Get_Char_Index( FT_Face face,
FT_ULong charcode )
{
FT_UInt result = 0;
if ( face && face->charmap )
{
FT_CMap cmap = FT_CMAP( face->charmap );
if ( charcode > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large charcode" ));
FT_TRACE1(( " 0x%x is truncated\n", charcode ));
}
result = cmap->clazz->char_index( cmap, (FT_UInt32)charcode );
if ( result >= (FT_UInt)face->num_glyphs )
result = 0;
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_ULong )
FT_Get_First_Char( FT_Face face,
FT_UInt *agindex )
{
FT_ULong result = 0;
FT_UInt gindex = 0;
/* only do something if we have a charmap, and we have glyphs at all */
if ( face && face->charmap && face->num_glyphs )
{
gindex = FT_Get_Char_Index( face, 0 );
if ( gindex == 0 )
result = FT_Get_Next_Char( face, 0, &gindex );
}
if ( agindex )
*agindex = gindex;
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_ULong )
FT_Get_Next_Char( FT_Face face,
FT_ULong charcode,
FT_UInt *agindex )
{
FT_ULong result = 0;
FT_UInt gindex = 0;
if ( face && face->charmap && face->num_glyphs )
{
FT_UInt32 code = (FT_UInt32)charcode;
FT_CMap cmap = FT_CMAP( face->charmap );
do
{
gindex = cmap->clazz->char_next( cmap, &code );
} while ( gindex >= (FT_UInt)face->num_glyphs );
result = ( gindex == 0 ) ? 0 : code;
}
if ( agindex )
*agindex = gindex;
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt )
FT_Face_GetCharVariantIndex( FT_Face face,
FT_ULong charcode,
FT_ULong variantSelector )
{
FT_UInt result = 0;
if ( face &&
face->charmap &&
face->charmap->encoding == FT_ENCODING_UNICODE )
{
FT_CharMap charmap = find_variant_selector_charmap( face );
FT_CMap ucmap = FT_CMAP( face->charmap );
if ( charmap != NULL )
{
FT_CMap vcmap = FT_CMAP( charmap );
if ( charcode > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large charcode" ));
FT_TRACE1(( " 0x%x is truncated\n", charcode ));
}
if ( variantSelector > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" ));
FT_TRACE1(( " 0x%x is truncated\n", variantSelector ));
}
result = vcmap->clazz->char_var_index( vcmap, ucmap,
(FT_UInt32)charcode,
(FT_UInt32)variantSelector );
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Int )
FT_Face_GetCharVariantIsDefault( FT_Face face,
FT_ULong charcode,
FT_ULong variantSelector )
{
FT_Int result = -1;
if ( face )
{
FT_CharMap charmap = find_variant_selector_charmap( face );
if ( charmap != NULL )
{
FT_CMap vcmap = FT_CMAP( charmap );
if ( charcode > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large charcode" ));
FT_TRACE1(( " 0x%x is truncated\n", charcode ));
}
if ( variantSelector > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" ));
FT_TRACE1(( " 0x%x is truncated\n", variantSelector ));
}
result = vcmap->clazz->char_var_default( vcmap,
(FT_UInt32)charcode,
(FT_UInt32)variantSelector );
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt32* )
FT_Face_GetVariantSelectors( FT_Face face )
{
FT_UInt32 *result = NULL;
if ( face )
{
FT_CharMap charmap = find_variant_selector_charmap( face );
if ( charmap != NULL )
{
FT_CMap vcmap = FT_CMAP( charmap );
FT_Memory memory = FT_FACE_MEMORY( face );
result = vcmap->clazz->variant_list( vcmap, memory );
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt32* )
FT_Face_GetVariantsOfChar( FT_Face face,
FT_ULong charcode )
{
FT_UInt32 *result = NULL;
if ( face )
{
FT_CharMap charmap = find_variant_selector_charmap( face );
if ( charmap != NULL )
{
FT_CMap vcmap = FT_CMAP( charmap );
FT_Memory memory = FT_FACE_MEMORY( face );
if ( charcode > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large charcode" ));
FT_TRACE1(( " 0x%x is truncated\n", charcode ));
}
result = vcmap->clazz->charvariant_list( vcmap, memory,
(FT_UInt32)charcode );
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt32* )
FT_Face_GetCharsOfVariant( FT_Face face,
FT_ULong variantSelector )
{
FT_UInt32 *result = NULL;
if ( face )
{
FT_CharMap charmap = find_variant_selector_charmap( face );
if ( charmap != NULL )
{
FT_CMap vcmap = FT_CMAP( charmap );
FT_Memory memory = FT_FACE_MEMORY( face );
if ( variantSelector > 0xFFFFFFFFUL )
{
FT_TRACE1(( "FT_Get_Char_Index: too large variantSelector" ));
FT_TRACE1(( " 0x%x is truncated\n", variantSelector ));
}
result = vcmap->clazz->variantchar_list( vcmap, memory,
(FT_UInt32)variantSelector );
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_UInt )
FT_Get_Name_Index( FT_Face face,
FT_String* glyph_name )
{
FT_UInt result = 0;
if ( face &&
FT_HAS_GLYPH_NAMES( face ) &&
glyph_name )
{
FT_Service_GlyphDict service;
FT_FACE_LOOKUP_SERVICE( face,
service,
GLYPH_DICT );
if ( service && service->name_index )
result = service->name_index( face, glyph_name );
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Get_Glyph_Name( FT_Face face,
FT_UInt glyph_index,
FT_Pointer buffer,
FT_UInt buffer_max )
{
FT_Error error;
FT_Service_GlyphDict service;
if ( !face )
return FT_THROW( Invalid_Face_Handle );
if ( !buffer || buffer_max == 0 )
return FT_THROW( Invalid_Argument );
/* clean up buffer */
((FT_Byte*)buffer)[0] = '\0';
if ( (FT_Long)glyph_index >= face->num_glyphs )
return FT_THROW( Invalid_Glyph_Index );
if ( !FT_HAS_GLYPH_NAMES( face ) )
return FT_THROW( Invalid_Argument );
FT_FACE_LOOKUP_SERVICE( face, service, GLYPH_DICT );
if ( service && service->get_name )
error = service->get_name( face, glyph_index, buffer, buffer_max );
else
error = FT_THROW( Invalid_Argument );
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( const char* )
FT_Get_Postscript_Name( FT_Face face )
{
const char* result = NULL;
if ( !face )
goto Exit;
if ( !result )
{
FT_Service_PsFontName service;
FT_FACE_LOOKUP_SERVICE( face,
service,
POSTSCRIPT_FONT_NAME );
if ( service && service->get_ps_font_name )
result = service->get_ps_font_name( face );
}
Exit:
return result;
}
/* documentation is in tttables.h */
FT_EXPORT_DEF( void* )
FT_Get_Sfnt_Table( FT_Face face,
FT_Sfnt_Tag tag )
{
void* table = NULL;
FT_Service_SFNT_Table service;
if ( face && FT_IS_SFNT( face ) )
{
FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
if ( service != NULL )
table = service->get_table( face, tag );
}
return table;
}
/* documentation is in tttables.h */
FT_EXPORT_DEF( FT_Error )
FT_Load_Sfnt_Table( FT_Face face,
FT_ULong tag,
FT_Long offset,
FT_Byte* buffer,
FT_ULong* length )
{
FT_Service_SFNT_Table service;
if ( !face || !FT_IS_SFNT( face ) )
return FT_THROW( Invalid_Face_Handle );
FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
if ( service == NULL )
return FT_THROW( Unimplemented_Feature );
return service->load_table( face, tag, offset, buffer, length );
}
/* documentation is in tttables.h */
FT_EXPORT_DEF( FT_Error )
FT_Sfnt_Table_Info( FT_Face face,
FT_UInt table_index,
FT_ULong *tag,
FT_ULong *length )
{
FT_Service_SFNT_Table service;
FT_ULong offset;
/* test for valid `length' delayed to `service->table_info' */
if ( !face || !FT_IS_SFNT( face ) )
return FT_THROW( Invalid_Face_Handle );
FT_FACE_FIND_SERVICE( face, service, SFNT_TABLE );
if ( service == NULL )
return FT_THROW( Unimplemented_Feature );
return service->table_info( face, table_index, tag, &offset, length );
}
/* documentation is in tttables.h */
FT_EXPORT_DEF( FT_ULong )
FT_Get_CMap_Language_ID( FT_CharMap charmap )
{
FT_Service_TTCMaps service;
FT_Face face;
TT_CMapInfo cmap_info;
if ( !charmap || !charmap->face )
return 0;
face = charmap->face;
FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
if ( service == NULL )
return 0;
if ( service->get_cmap_info( charmap, &cmap_info ))
return 0;
return cmap_info.language;
}
/* documentation is in tttables.h */
FT_EXPORT_DEF( FT_Long )
FT_Get_CMap_Format( FT_CharMap charmap )
{
FT_Service_TTCMaps service;
FT_Face face;
TT_CMapInfo cmap_info;
if ( !charmap || !charmap->face )
return -1;
face = charmap->face;
FT_FACE_FIND_SERVICE( face, service, TT_CMAP );
if ( service == NULL )
return -1;
if ( service->get_cmap_info( charmap, &cmap_info ))
return -1;
return cmap_info.format;
}
/* documentation is in ftsizes.h */
FT_EXPORT_DEF( FT_Error )
FT_Activate_Size( FT_Size size )
{
FT_Face face;
if ( !size )
return FT_THROW( Invalid_Size_Handle );
face = size->face;
if ( !face || !face->driver )
return FT_THROW( Invalid_Face_Handle );
/* we don't need anything more complex than that; all size objects */
/* are already listed by the face */
face->size = size;
return FT_Err_Ok;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** R E N D E R E R S ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* lookup a renderer by glyph format in the library's list */
FT_BASE_DEF( FT_Renderer )
FT_Lookup_Renderer( FT_Library library,
FT_Glyph_Format format,
FT_ListNode* node )
{
FT_ListNode cur;
FT_Renderer result = NULL;
if ( !library )
goto Exit;
cur = library->renderers.head;
if ( node )
{
if ( *node )
cur = (*node)->next;
*node = NULL;
}
while ( cur )
{
FT_Renderer renderer = FT_RENDERER( cur->data );
if ( renderer->glyph_format == format )
{
if ( node )
*node = cur;
result = renderer;
break;
}
cur = cur->next;
}
Exit:
return result;
}
static FT_Renderer
ft_lookup_glyph_renderer( FT_GlyphSlot slot )
{
FT_Face face = slot->face;
FT_Library library = FT_FACE_LIBRARY( face );
FT_Renderer result = library->cur_renderer;
if ( !result || result->glyph_format != slot->format )
result = FT_Lookup_Renderer( library, slot->format, 0 );
return result;
}
static void
ft_set_current_renderer( FT_Library library )
{
FT_Renderer renderer;
renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE, 0 );
library->cur_renderer = renderer;
}
static FT_Error
ft_add_renderer( FT_Module module )
{
FT_Library library = module->library;
FT_Memory memory = library->memory;
FT_Error error;
FT_ListNode node = NULL;
if ( FT_NEW( node ) )
goto Exit;
{
FT_Renderer render = FT_RENDERER( module );
FT_Renderer_Class* clazz = (FT_Renderer_Class*)module->clazz;
render->clazz = clazz;
render->glyph_format = clazz->glyph_format;
/* allocate raster object if needed */
if ( clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE &&
clazz->raster_class->raster_new )
{
error = clazz->raster_class->raster_new( memory, &render->raster );
if ( error )
goto Fail;
render->raster_render = clazz->raster_class->raster_render;
render->render = clazz->render_glyph;
}
/* add to list */
node->data = module;
FT_List_Add( &library->renderers, node );
ft_set_current_renderer( library );
}
Fail:
if ( error )
FT_FREE( node );
Exit:
return error;
}
static void
ft_remove_renderer( FT_Module module )
{
FT_Library library;
FT_Memory memory;
FT_ListNode node;
library = module->library;
if ( !library )
return;
memory = library->memory;
node = FT_List_Find( &library->renderers, module );
if ( node )
{
FT_Renderer render = FT_RENDERER( module );
/* release raster object, if any */
if ( render->clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE &&
render->raster )
render->clazz->raster_class->raster_done( render->raster );
/* remove from list */
FT_List_Remove( &library->renderers, node );
FT_FREE( node );
ft_set_current_renderer( library );
}
}
/* documentation is in ftrender.h */
FT_EXPORT_DEF( FT_Renderer )
FT_Get_Renderer( FT_Library library,
FT_Glyph_Format format )
{
/* test for valid `library' delayed to `FT_Lookup_Renderer' */
return FT_Lookup_Renderer( library, format, 0 );
}
/* documentation is in ftrender.h */
FT_EXPORT_DEF( FT_Error )
FT_Set_Renderer( FT_Library library,
FT_Renderer renderer,
FT_UInt num_params,
FT_Parameter* parameters )
{
FT_ListNode node;
FT_Error error = FT_Err_Ok;
FT_Renderer_SetModeFunc set_mode;
if ( !library )
{
error = FT_THROW( Invalid_Library_Handle );
goto Exit;
}
if ( !renderer )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
if ( num_params > 0 && !parameters )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
node = FT_List_Find( &library->renderers, renderer );
if ( !node )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
FT_List_Up( &library->renderers, node );
if ( renderer->glyph_format == FT_GLYPH_FORMAT_OUTLINE )
library->cur_renderer = renderer;
set_mode = renderer->clazz->set_mode;
for ( ; num_params > 0; num_params-- )
{
error = set_mode( renderer, parameters->tag, parameters->data );
if ( error )
break;
parameters++;
}
Exit:
return error;
}
FT_BASE_DEF( FT_Error )
FT_Render_Glyph_Internal( FT_Library library,
FT_GlyphSlot slot,
FT_Render_Mode render_mode )
{
FT_Error error = FT_Err_Ok;
FT_Renderer renderer;
/* if it is already a bitmap, no need to do anything */
switch ( slot->format )
{
case FT_GLYPH_FORMAT_BITMAP: /* already a bitmap, don't do anything */
break;
default:
{
FT_ListNode node = NULL;
/* small shortcut for the very common case */
if ( slot->format == FT_GLYPH_FORMAT_OUTLINE )
{
renderer = library->cur_renderer;
node = library->renderers.head;
}
else
renderer = FT_Lookup_Renderer( library, slot->format, &node );
error = FT_ERR( Unimplemented_Feature );
while ( renderer )
{
error = renderer->render( renderer, slot, render_mode, NULL );
if ( !error ||
FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
break;
/* FT_Err_Cannot_Render_Glyph is returned if the render mode */
/* is unsupported by the current renderer for this glyph image */
/* format. */
/* now, look for another renderer that supports the same */
/* format. */
renderer = FT_Lookup_Renderer( library, slot->format, &node );
}
}
}
#ifdef FT_DEBUG_LEVEL_TRACE
#undef FT_COMPONENT
#define FT_COMPONENT trace_bitmap
/*
* Computing the MD5 checksum is expensive, unnecessarily distorting a
* possible profiling of FreeType if compiled with tracing support. For
* this reason, we execute the following code only if explicitly
* requested.
*/
/* we use FT_TRACE3 in this block */
if ( ft_trace_levels[trace_bitmap] >= 3 )
{
/* we convert to a single bitmap format for computing the checksum */
if ( !error )
{
FT_Bitmap bitmap;
FT_Error err;
FT_Bitmap_Init( &bitmap );
/* this also converts the bitmap flow to `down' (i.e., pitch > 0) */
err = FT_Bitmap_Convert( library, &slot->bitmap, &bitmap, 1 );
if ( !err )
{
MD5_CTX ctx;
unsigned char md5[16];
int i;
unsigned int rows = bitmap.rows;
unsigned int pitch = (unsigned int)bitmap.pitch;
MD5_Init( &ctx );
if ( bitmap.buffer )
MD5_Update( &ctx, bitmap.buffer, rows * pitch );
MD5_Final( md5, &ctx );
FT_TRACE3(( "MD5 checksum for %dx%d bitmap:\n"
" ",
rows, pitch ));
for ( i = 0; i < 16; i++ )
FT_TRACE3(( "%02X", md5[i] ));
FT_TRACE3(( "\n" ));
}
FT_Bitmap_Done( library, &bitmap );
}
}
#undef FT_COMPONENT
#define FT_COMPONENT trace_objs
#endif /* FT_DEBUG_LEVEL_TRACE */
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Render_Glyph( FT_GlyphSlot slot,
FT_Render_Mode render_mode )
{
FT_Library library;
if ( !slot || !slot->face )
return FT_THROW( Invalid_Argument );
library = FT_FACE_LIBRARY( slot->face );
return FT_Render_Glyph_Internal( library, slot, render_mode );
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** M O D U L E S ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* Destroy_Module */
/* */
/* <Description> */
/* Destroys a given module object. For drivers, this also destroys */
/* all child faces. */
/* */
/* <InOut> */
/* module :: A handle to the target driver object. */
/* */
/* <Note> */
/* The driver _must_ be LOCKED! */
/* */
static void
Destroy_Module( FT_Module module )
{
FT_Memory memory = module->memory;
FT_Module_Class* clazz = module->clazz;
FT_Library library = module->library;
if ( library && library->auto_hinter == module )
library->auto_hinter = NULL;
/* if the module is a renderer */
if ( FT_MODULE_IS_RENDERER( module ) )
ft_remove_renderer( module );
/* if the module is a font driver, add some steps */
if ( FT_MODULE_IS_DRIVER( module ) )
Destroy_Driver( FT_DRIVER( module ) );
/* finalize the module object */
if ( clazz->module_done )
clazz->module_done( module );
/* discard it */
FT_FREE( module );
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Add_Module( FT_Library library,
const FT_Module_Class* clazz )
{
FT_Error error;
FT_Memory memory;
FT_Module module;
FT_UInt nn;
#define FREETYPE_VER_FIXED ( ( (FT_Long)FREETYPE_MAJOR << 16 ) | \
FREETYPE_MINOR )
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( !clazz )
return FT_THROW( Invalid_Argument );
/* check freetype version */
if ( clazz->module_requires > FREETYPE_VER_FIXED )
return FT_THROW( Invalid_Version );
/* look for a module with the same name in the library's table */
for ( nn = 0; nn < library->num_modules; nn++ )
{
module = library->modules[nn];
if ( ft_strcmp( module->clazz->module_name, clazz->module_name ) == 0 )
{
/* this installed module has the same name, compare their versions */
if ( clazz->module_version <= module->clazz->module_version )
return FT_THROW( Lower_Module_Version );
/* remove the module from our list, then exit the loop to replace */
/* it by our new version.. */
FT_Remove_Module( library, module );
break;
}
}
memory = library->memory;
error = FT_Err_Ok;
if ( library->num_modules >= FT_MAX_MODULES )
{
error = FT_THROW( Too_Many_Drivers );
goto Exit;
}
/* allocate module object */
if ( FT_ALLOC( module, clazz->module_size ) )
goto Exit;
/* base initialization */
module->library = library;
module->memory = memory;
module->clazz = (FT_Module_Class*)clazz;
/* check whether the module is a renderer - this must be performed */
/* before the normal module initialization */
if ( FT_MODULE_IS_RENDERER( module ) )
{
/* add to the renderers list */
error = ft_add_renderer( module );
if ( error )
goto Fail;
}
/* is the module a auto-hinter? */
if ( FT_MODULE_IS_HINTER( module ) )
library->auto_hinter = module;
/* if the module is a font driver */
if ( FT_MODULE_IS_DRIVER( module ) )
{
FT_Driver driver = FT_DRIVER( module );
driver->clazz = (FT_Driver_Class)module->clazz;
}
if ( clazz->module_init )
{
error = clazz->module_init( module );
if ( error )
goto Fail;
}
/* add module to the library's table */
library->modules[library->num_modules++] = module;
Exit:
return error;
Fail:
if ( FT_MODULE_IS_RENDERER( module ) )
{
FT_Renderer renderer = FT_RENDERER( module );
if ( renderer->clazz &&
renderer->clazz->glyph_format == FT_GLYPH_FORMAT_OUTLINE &&
renderer->raster )
renderer->clazz->raster_class->raster_done( renderer->raster );
}
FT_FREE( module );
goto Exit;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Module )
FT_Get_Module( FT_Library library,
const char* module_name )
{
FT_Module result = NULL;
FT_Module* cur;
FT_Module* limit;
if ( !library || !module_name )
return result;
cur = library->modules;
limit = cur + library->num_modules;
for ( ; cur < limit; cur++ )
if ( ft_strcmp( cur[0]->clazz->module_name, module_name ) == 0 )
{
result = cur[0];
break;
}
return result;
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( const void* )
FT_Get_Module_Interface( FT_Library library,
const char* mod_name )
{
FT_Module module;
/* test for valid `library' delayed to FT_Get_Module() */
module = FT_Get_Module( library, mod_name );
return module ? module->clazz->module_interface : 0;
}
FT_BASE_DEF( FT_Pointer )
ft_module_get_service( FT_Module module,
const char* service_id )
{
FT_Pointer result = NULL;
if ( module )
{
FT_ASSERT( module->clazz && module->clazz->get_interface );
/* first, look for the service in the module */
if ( module->clazz->get_interface )
result = module->clazz->get_interface( module, service_id );
if ( result == NULL )
{
/* we didn't find it, look in all other modules then */
FT_Library library = module->library;
FT_Module* cur = library->modules;
FT_Module* limit = cur + library->num_modules;
for ( ; cur < limit; cur++ )
{
if ( cur[0] != module )
{
FT_ASSERT( cur[0]->clazz );
if ( cur[0]->clazz->get_interface )
{
result = cur[0]->clazz->get_interface( cur[0], service_id );
if ( result != NULL )
break;
}
}
}
}
}
return result;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Remove_Module( FT_Library library,
FT_Module module )
{
/* try to find the module from the table, then remove it from there */
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( module )
{
FT_Module* cur = library->modules;
FT_Module* limit = cur + library->num_modules;
for ( ; cur < limit; cur++ )
{
if ( cur[0] == module )
{
/* remove it from the table */
library->num_modules--;
limit--;
while ( cur < limit )
{
cur[0] = cur[1];
cur++;
}
limit[0] = NULL;
/* destroy the module */
Destroy_Module( module );
return FT_Err_Ok;
}
}
}
return FT_THROW( Invalid_Driver_Handle );
}
static FT_Error
ft_property_do( FT_Library library,
const FT_String* module_name,
const FT_String* property_name,
void* value,
FT_Bool set )
{
FT_Module* cur;
FT_Module* limit;
FT_Module_Interface interface;
FT_Service_Properties service;
#ifdef FT_DEBUG_LEVEL_ERROR
const FT_String* set_name = "FT_Property_Set";
const FT_String* get_name = "FT_Property_Get";
const FT_String* func_name = set ? set_name : get_name;
#endif
FT_Bool missing_func;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( !module_name || !property_name || !value )
return FT_THROW( Invalid_Argument );
cur = library->modules;
limit = cur + library->num_modules;
/* search module */
for ( ; cur < limit; cur++ )
if ( !ft_strcmp( cur[0]->clazz->module_name, module_name ) )
break;
if ( cur == limit )
{
FT_ERROR(( "%s: can't find module `%s'\n",
func_name, module_name ));
return FT_THROW( Missing_Module );
}
/* check whether we have a service interface */
if ( !cur[0]->clazz->get_interface )
{
FT_ERROR(( "%s: module `%s' doesn't support properties\n",
func_name, module_name ));
return FT_THROW( Unimplemented_Feature );
}
/* search property service */
interface = cur[0]->clazz->get_interface( cur[0],
FT_SERVICE_ID_PROPERTIES );
if ( !interface )
{
FT_ERROR(( "%s: module `%s' doesn't support properties\n",
func_name, module_name ));
return FT_THROW( Unimplemented_Feature );
}
service = (FT_Service_Properties)interface;
if ( set )
missing_func = (FT_Bool)( !service->set_property );
else
missing_func = (FT_Bool)( !service->get_property );
if ( missing_func )
{
FT_ERROR(( "%s: property service of module `%s' is broken\n",
func_name, module_name ));
return FT_THROW( Unimplemented_Feature );
}
return set ? service->set_property( cur[0], property_name, value )
: service->get_property( cur[0], property_name, value );
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Property_Set( FT_Library library,
const FT_String* module_name,
const FT_String* property_name,
const void* value )
{
return ft_property_do( library,
module_name,
property_name,
(void*)value,
TRUE );
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Property_Get( FT_Library library,
const FT_String* module_name,
const FT_String* property_name,
void* value )
{
return ft_property_do( library,
module_name,
property_name,
value,
FALSE );
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/**** ****/
/**** ****/
/**** L I B R A R Y ****/
/**** ****/
/**** ****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Reference_Library( FT_Library library )
{
if ( !library )
return FT_THROW( Invalid_Library_Handle );
library->refcount++;
return FT_Err_Ok;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_New_Library( FT_Memory memory,
FT_Library *alibrary )
{
FT_Library library = NULL;
FT_Error error;
if ( !memory || !alibrary )
return FT_THROW( Invalid_Argument );
#ifdef FT_DEBUG_LEVEL_ERROR
/* init debugging support */
ft_debug_init();
#endif
/* first of all, allocate the library object */
if ( FT_NEW( library ) )
return error;
library->memory = memory;
#ifdef FT_CONFIG_OPTION_PIC
/* initialize position independent code containers */
error = ft_pic_container_init( library );
if ( error )
goto Fail;
#endif
/* we don't use raster_pool anymore. */
library->raster_pool_size = 0;
library->raster_pool = NULL;
library->version_major = FREETYPE_MAJOR;
library->version_minor = FREETYPE_MINOR;
library->version_patch = FREETYPE_PATCH;
library->refcount = 1;
/* That's ok now */
*alibrary = library;
return FT_Err_Ok;
#ifdef FT_CONFIG_OPTION_PIC
Fail:
ft_pic_container_destroy( library );
#endif
FT_FREE( library );
return error;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( void )
FT_Library_Version( FT_Library library,
FT_Int *amajor,
FT_Int *aminor,
FT_Int *apatch )
{
FT_Int major = 0;
FT_Int minor = 0;
FT_Int patch = 0;
if ( library )
{
major = library->version_major;
minor = library->version_minor;
patch = library->version_patch;
}
if ( amajor )
*amajor = major;
if ( aminor )
*aminor = minor;
if ( apatch )
*apatch = patch;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_Error )
FT_Done_Library( FT_Library library )
{
FT_Memory memory;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
library->refcount--;
if ( library->refcount > 0 )
goto Exit;
memory = library->memory;
/*
* Close all faces in the library. If we don't do this, we can have
* some subtle memory leaks.
*
* Example:
*
* - the cff font driver uses the pshinter module in cff_size_done
* - if the pshinter module is destroyed before the cff font driver,
* opened FT_Face objects managed by the driver are not properly
* destroyed, resulting in a memory leak
*
* Some faces are dependent on other faces, like Type42 faces that
* depend on TrueType faces synthesized internally.
*
* The order of drivers should be specified in driver_name[].
*/
{
FT_UInt m, n;
const char* driver_name[] = { "type42", NULL };
for ( m = 0;
m < sizeof ( driver_name ) / sizeof ( driver_name[0] );
m++ )
{
for ( n = 0; n < library->num_modules; n++ )
{
FT_Module module = library->modules[n];
const char* module_name = module->clazz->module_name;
FT_List faces;
if ( driver_name[m] &&
ft_strcmp( module_name, driver_name[m] ) != 0 )
continue;
if ( ( module->clazz->module_flags & FT_MODULE_FONT_DRIVER ) == 0 )
continue;
FT_TRACE7(( "FT_Done_Library: close faces for %s\n", module_name ));
faces = &FT_DRIVER( module )->faces_list;
while ( faces->head )
{
FT_Done_Face( FT_FACE( faces->head->data ) );
if ( faces->head )
FT_TRACE0(( "FT_Done_Library: failed to free some faces\n" ));
}
}
}
}
/* Close all other modules in the library */
#if 1
/* XXX Modules are removed in the reversed order so that */
/* type42 module is removed before truetype module. This */
/* avoids double free in some occasions. It is a hack. */
while ( library->num_modules > 0 )
FT_Remove_Module( library,
library->modules[library->num_modules - 1] );
#else
{
FT_UInt n;
for ( n = 0; n < library->num_modules; n++ )
{
FT_Module module = library->modules[n];
if ( module )
{
Destroy_Module( module );
library->modules[n] = NULL;
}
}
}
#endif
#ifdef FT_CONFIG_OPTION_PIC
/* Destroy pic container contents */
ft_pic_container_destroy( library );
#endif
FT_FREE( library );
Exit:
return FT_Err_Ok;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( void )
FT_Set_Debug_Hook( FT_Library library,
FT_UInt hook_index,
FT_DebugHook_Func debug_hook )
{
if ( library && debug_hook &&
hook_index <
( sizeof ( library->debug_hooks ) / sizeof ( void* ) ) )
library->debug_hooks[hook_index] = debug_hook;
}
/* documentation is in ftmodapi.h */
FT_EXPORT_DEF( FT_TrueTypeEngineType )
FT_Get_TrueType_Engine_Type( FT_Library library )
{
FT_TrueTypeEngineType result = FT_TRUETYPE_ENGINE_TYPE_NONE;
if ( library )
{
FT_Module module = FT_Get_Module( library, "truetype" );
if ( module )
{
FT_Service_TrueTypeEngine service;
service = (FT_Service_TrueTypeEngine)
ft_module_get_service( module,
FT_SERVICE_ID_TRUETYPE_ENGINE );
if ( service )
result = service->engine_type;
}
}
return result;
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( FT_Error )
FT_Get_SubGlyph_Info( FT_GlyphSlot glyph,
FT_UInt sub_index,
FT_Int *p_index,
FT_UInt *p_flags,
FT_Int *p_arg1,
FT_Int *p_arg2,
FT_Matrix *p_transform )
{
FT_Error error = FT_ERR( Invalid_Argument );
if ( glyph &&
glyph->subglyphs &&
glyph->format == FT_GLYPH_FORMAT_COMPOSITE &&
sub_index < glyph->num_subglyphs )
{
FT_SubGlyph subg = glyph->subglyphs + sub_index;
*p_index = subg->index;
*p_flags = subg->flags;
*p_arg1 = subg->arg1;
*p_arg2 = subg->arg2;
*p_transform = subg->transform;
error = FT_Err_Ok;
}
return error;
}
/* END */
src/base/ftoutln.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftoutln.c */
/* */
/* FreeType outline management (body). */
/* */
/* Copyright 1996-2016 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. */
/* */
/***************************************************************************/
/*************************************************************************/
/* */
/* All functions are declared in freetype.h. */
/* */
/*************************************************************************/
#include <ft2build.h>
#include FT_OUTLINE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_DEBUG_H
#include FT_TRIGONOMETRY_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_outline
static
const FT_Outline null_outline = { 0, 0, 0, 0, 0, 0 };
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Decompose( FT_Outline* outline,
const FT_Outline_Funcs* func_interface,
void* user )
{
#undef SCALED
#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << shift ) \
: ( (x) << shift ) ) - delta )
FT_Vector v_last;
FT_Vector v_control;
FT_Vector v_start;
FT_Vector* point;
FT_Vector* limit;
char* tags;
FT_Error error;
FT_Int n; /* index of contour in outline */
FT_UInt first; /* index of first point in contour */
FT_Int tag; /* current point's state */
FT_Int shift;
FT_Pos delta;
if ( !outline )
return FT_THROW( Invalid_Outline );
if ( !func_interface )
return FT_THROW( Invalid_Argument );
shift = func_interface->shift;
delta = func_interface->delta;
first = 0;
for ( n = 0; n < outline->n_contours; n++ )
{
FT_Int last; /* index of last point in contour */
FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
last = outline->contours[n];
if ( last < 0 )
goto Invalid_Outline;
limit = outline->points + last;
v_start = outline->points[first];
v_start.x = SCALED( v_start.x );
v_start.y = SCALED( v_start.y );
v_last = outline->points[last];
v_last.x = SCALED( v_last.x );
v_last.y = SCALED( v_last.y );
v_control = v_start;
point = outline->points + first;
tags = outline->tags + first;
tag = FT_CURVE_TAG( tags[0] );
/* A contour cannot start with a cubic control point! */
if ( tag == FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
/* check first point to determine origin */
if ( tag == FT_CURVE_TAG_CONIC )
{
/* first point is conic control. Yes, this happens. */
if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
{
/* start at last point if it is on the curve */
v_start = v_last;
limit--;
}
else
{
/* if both first and last points are conic, */
/* start at their middle and record its position */
/* for closure */
v_start.x = ( v_start.x + v_last.x ) / 2;
v_start.y = ( v_start.y + v_last.y ) / 2;
/* v_last = v_start; */
}
point--;
tags--;
}
FT_TRACE5(( " move to (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0 ));
error = func_interface->move_to( &v_start, user );
if ( error )
goto Exit;
while ( point < limit )
{
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
switch ( tag )
{
case FT_CURVE_TAG_ON: /* emit a single line_to */
{
FT_Vector vec;
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
FT_TRACE5(( " line to (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0 ));
error = func_interface->line_to( &vec, user );
if ( error )
goto Exit;
continue;
}
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
v_control.x = SCALED( point->x );
v_control.y = SCALED( point->y );
Do_Conic:
if ( point < limit )
{
FT_Vector vec;
FT_Vector v_middle;
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
if ( tag == FT_CURVE_TAG_ON )
{
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &vec, user );
if ( error )
goto Exit;
continue;
}
if ( tag != FT_CURVE_TAG_CONIC )
goto Invalid_Outline;
v_middle.x = ( v_control.x + vec.x ) / 2;
v_middle.y = ( v_control.y + vec.y ) / 2;
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
v_middle.x / 64.0, v_middle.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &v_middle, user );
if ( error )
goto Exit;
v_control = vec;
goto Do_Conic;
}
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &v_start, user );
goto Close;
default: /* FT_CURVE_TAG_CUBIC */
{
FT_Vector vec1, vec2;
if ( point + 1 > limit ||
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
point += 2;
tags += 2;
vec1.x = SCALED( point[-2].x );
vec1.y = SCALED( point[-2].y );
vec2.x = SCALED( point[-1].x );
vec2.y = SCALED( point[-1].y );
if ( point <= limit )
{
FT_Vector vec;
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
FT_TRACE5(( " cubic to (%.2f, %.2f)"
" with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0,
vec1.x / 64.0, vec1.y / 64.0,
vec2.x / 64.0, vec2.y / 64.0 ));
error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
if ( error )
goto Exit;
continue;
}
FT_TRACE5(( " cubic to (%.2f, %.2f)"
" with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0,
vec1.x / 64.0, vec1.y / 64.0,
vec2.x / 64.0, vec2.y / 64.0 ));
error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
goto Close;
}
}
}
/* close the contour with a line segment */
FT_TRACE5(( " line to (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0 ));
error = func_interface->line_to( &v_start, user );
Close:
if ( error )
goto Exit;
first = (FT_UInt)last + 1;
}
FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
return FT_Err_Ok;
Exit:
FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error ));
return error;
Invalid_Outline:
return FT_THROW( Invalid_Outline );
}
FT_EXPORT_DEF( FT_Error )
FT_Outline_New_Internal( FT_Memory memory,
FT_UInt numPoints,
FT_Int numContours,
FT_Outline *anoutline )
{
FT_Error error;
if ( !anoutline || !memory )
return FT_THROW( Invalid_Argument );
*anoutline = null_outline;
if ( numContours < 0 ||
(FT_UInt)numContours > numPoints )
return FT_THROW( Invalid_Argument );
if ( numPoints > FT_OUTLINE_POINTS_MAX )
return FT_THROW( Array_Too_Large );
if ( FT_NEW_ARRAY( anoutline->points, numPoints ) ||
FT_NEW_ARRAY( anoutline->tags, numPoints ) ||
FT_NEW_ARRAY( anoutline->contours, numContours ) )
goto Fail;
anoutline->n_points = (FT_Short)numPoints;
anoutline->n_contours = (FT_Short)numContours;
anoutline->flags |= FT_OUTLINE_OWNER;
return FT_Err_Ok;
Fail:
anoutline->flags |= FT_OUTLINE_OWNER;
FT_Outline_Done_Internal( memory, anoutline );
return error;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_New( FT_Library library,
FT_UInt numPoints,
FT_Int numContours,
FT_Outline *anoutline )
{
if ( !library )
return FT_THROW( Invalid_Library_Handle );
return FT_Outline_New_Internal( library->memory, numPoints,
numContours, anoutline );
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Check( FT_Outline* outline )
{
if ( outline )
{
FT_Int n_points = outline->n_points;
FT_Int n_contours = outline->n_contours;
FT_Int end0, end;
FT_Int n;
/* empty glyph? */
if ( n_points == 0 && n_contours == 0 )
return FT_Err_Ok;
/* check point and contour counts */
if ( n_points <= 0 || n_contours <= 0 )
goto Bad;
end0 = end = -1;
for ( n = 0; n < n_contours; n++ )
{
end = outline->contours[n];
/* note that we don't accept empty contours */
if ( end <= end0 || end >= n_points )
goto Bad;
end0 = end;
}
if ( end != n_points - 1 )
goto Bad;
/* XXX: check the tags array */
return FT_Err_Ok;
}
Bad:
return FT_THROW( Invalid_Argument );
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Copy( const FT_Outline* source,
FT_Outline *target )
{
FT_Int is_owner;
if ( !source || !target )
return FT_THROW( Invalid_Outline );
if ( source->n_points != target->n_points ||
source->n_contours != target->n_contours )
return FT_THROW( Invalid_Argument );
if ( source == target )
return FT_Err_Ok;
if ( source->n_points )
{
FT_ARRAY_COPY( target->points, source->points, source->n_points );
FT_ARRAY_COPY( target->tags, source->tags, source->n_points );
}
if ( source->n_contours )
FT_ARRAY_COPY( target->contours, source->contours, source->n_contours );
/* copy all flags, except the `FT_OUTLINE_OWNER' one */
is_owner = target->flags & FT_OUTLINE_OWNER;
target->flags = source->flags;
target->flags &= ~FT_OUTLINE_OWNER;
target->flags |= is_owner;
return FT_Err_Ok;
}
FT_EXPORT_DEF( FT_Error )
FT_Outline_Done_Internal( FT_Memory memory,
FT_Outline* outline )
{
if ( !outline )
return FT_THROW( Invalid_Outline );
if ( !memory )
return FT_THROW( Invalid_Argument );
if ( outline->flags & FT_OUTLINE_OWNER )
{
FT_FREE( outline->points );
FT_FREE( outline->tags );
FT_FREE( outline->contours );
}
*outline = null_outline;
return FT_Err_Ok;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Done( FT_Library library,
FT_Outline* outline )
{
/* check for valid `outline' in FT_Outline_Done_Internal() */
if ( !library )
return FT_THROW( Invalid_Library_Handle );
return FT_Outline_Done_Internal( library->memory, outline );
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( void )
FT_Outline_Get_CBox( const FT_Outline* outline,
FT_BBox *acbox )
{
FT_Pos xMin, yMin, xMax, yMax;
if ( outline && acbox )
{
if ( outline->n_points == 0 )
{
xMin = 0;
yMin = 0;
xMax = 0;
yMax = 0;
}
else
{
FT_Vector* vec = outline->points;
FT_Vector* limit = vec + outline->n_points;
xMin = xMax = vec->x;
yMin = yMax = vec->y;
vec++;
for ( ; vec < limit; vec++ )
{
FT_Pos x, y;
x = vec->x;
if ( x < xMin ) xMin = x;
if ( x > xMax ) xMax = x;
y = vec->y;
if ( y < yMin ) yMin = y;
if ( y > yMax ) yMax = y;
}
}
acbox->xMin = xMin;
acbox->xMax = xMax;
acbox->yMin = yMin;
acbox->yMax = yMax;
}
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( void )
FT_Outline_Translate( const FT_Outline* outline,
FT_Pos xOffset,
FT_Pos yOffset )
{
FT_UShort n;
FT_Vector* vec;
if ( !outline )
return;
vec = outline->points;
for ( n = 0; n < outline->n_points; n++ )
{
vec->x += xOffset;
vec->y += yOffset;
vec++;
}
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( void )
FT_Outline_Reverse( FT_Outline* outline )
{
FT_UShort n;
FT_Int first, last;
if ( !outline )
return;
first = 0;
for ( n = 0; n < outline->n_contours; n++ )
{
last = outline->contours[n];
/* reverse point table */
{
FT_Vector* p = outline->points + first;
FT_Vector* q = outline->points + last;
FT_Vector swap;
while ( p < q )
{
swap = *p;
*p = *q;
*q = swap;
p++;
q--;
}
}
/* reverse tags table */
{
char* p = outline->tags + first;
char* q = outline->tags + last;
while ( p < q )
{
char swap;
swap = *p;
*p = *q;
*q = swap;
p++;
q--;
}
}
first = last + 1;
}
outline->flags ^= FT_OUTLINE_REVERSE_FILL;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Render( FT_Library library,
FT_Outline* outline,
FT_Raster_Params* params )
{
FT_Error error;
FT_Renderer renderer;
FT_ListNode node;
FT_BBox cbox;
if ( !library )
return FT_THROW( Invalid_Library_Handle );
if ( !outline )
return FT_THROW( Invalid_Outline );
if ( !params )
return FT_THROW( Invalid_Argument );
FT_Outline_Get_CBox( outline, &cbox );
if ( cbox.xMin < -0x1000000L || cbox.yMin < -0x1000000L ||
cbox.xMax > 0x1000000L || cbox.yMax > 0x1000000L )
return FT_THROW( Invalid_Outline );
renderer = library->cur_renderer;
node = library->renderers.head;
params->source = (void*)outline;
error = FT_ERR( Cannot_Render_Glyph );
while ( renderer )
{
error = renderer->raster_render( renderer->raster, params );
if ( !error || FT_ERR_NEQ( error, Cannot_Render_Glyph ) )
break;
/* FT_Err_Cannot_Render_Glyph is returned if the render mode */
/* is unsupported by the current renderer for this glyph image */
/* format */
/* now, look for another renderer that supports the same */
/* format */
renderer = FT_Lookup_Renderer( library, FT_GLYPH_FORMAT_OUTLINE,
&node );
}
return error;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Get_Bitmap( FT_Library library,
FT_Outline* outline,
const FT_Bitmap *abitmap )
{
FT_Raster_Params params;
if ( !abitmap )
return FT_THROW( Invalid_Argument );
/* other checks are delayed to `FT_Outline_Render' */
params.target = abitmap;
params.flags = 0;
if ( abitmap->pixel_mode == FT_PIXEL_MODE_GRAY ||
abitmap->pixel_mode == FT_PIXEL_MODE_LCD ||
abitmap->pixel_mode == FT_PIXEL_MODE_LCD_V )
params.flags |= FT_RASTER_FLAG_AA;
return FT_Outline_Render( library, outline, ¶ms );
}
/* documentation is in freetype.h */
FT_EXPORT_DEF( void )
FT_Vector_Transform( FT_Vector* vector,
const FT_Matrix* matrix )
{
FT_Pos xz, yz;
if ( !vector || !matrix )
return;
xz = FT_MulFix( vector->x, matrix->xx ) +
FT_MulFix( vector->y, matrix->xy );
yz = FT_MulFix( vector->x, matrix->yx ) +
FT_MulFix( vector->y, matrix->yy );
vector->x = xz;
vector->y = yz;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( void )
FT_Outline_Transform( const FT_Outline* outline,
const FT_Matrix* matrix )
{
FT_Vector* vec;
FT_Vector* limit;
if ( !outline || !matrix )
return;
vec = outline->points;
limit = vec + outline->n_points;
for ( ; vec < limit; vec++ )
FT_Vector_Transform( vec, matrix );
}
#if 0
#define FT_OUTLINE_GET_CONTOUR( outline, c, first, last ) \
do \
{ \
(first) = ( c > 0 ) ? (outline)->points + \
(outline)->contours[c - 1] + 1 \
: (outline)->points; \
(last) = (outline)->points + (outline)->contours[c]; \
} while ( 0 )
/* Is a point in some contour? */
/* */
/* We treat every point of the contour as if it */
/* it were ON. That is, we allow false positives, */
/* but disallow false negatives. (XXX really?) */
static FT_Bool
ft_contour_has( FT_Outline* outline,
FT_Short c,
FT_Vector* point )
{
FT_Vector* first;
FT_Vector* last;
FT_Vector* a;
FT_Vector* b;
FT_UInt n = 0;
FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
for ( a = first; a <= last; a++ )
{
FT_Pos x;
FT_Int intersect;
b = ( a == last ) ? first : a + 1;
intersect = ( a->y - point->y ) ^ ( b->y - point->y );
/* a and b are on the same side */
if ( intersect >= 0 )
{
if ( intersect == 0 && a->y == point->y )
{
if ( ( a->x <= point->x && b->x >= point->x ) ||
( a->x >= point->x && b->x <= point->x ) )
return 1;
}
continue;
}
x = a->x + ( b->x - a->x ) * (point->y - a->y ) / ( b->y - a->y );
if ( x < point->x )
n++;
else if ( x == point->x )
return 1;
}
return n & 1;
}
static FT_Bool
ft_contour_enclosed( FT_Outline* outline,
FT_UShort c )
{
FT_Vector* first;
FT_Vector* last;
FT_Short i;
FT_OUTLINE_GET_CONTOUR( outline, c, first, last );
for ( i = 0; i < outline->n_contours; i++ )
{
if ( i != c && ft_contour_has( outline, i, first ) )
{
FT_Vector* pt;
for ( pt = first + 1; pt <= last; pt++ )
if ( !ft_contour_has( outline, i, pt ) )
return 0;
return 1;
}
}
return 0;
}
/* This version differs from the public one in that each */
/* part (contour not enclosed in another contour) of the */
/* outline is checked for orientation. This is */
/* necessary for some buggy CJK fonts. */
static FT_Orientation
ft_outline_get_orientation( FT_Outline* outline )
{
FT_Short i;
FT_Vector* first;
FT_Vector* last;
FT_Orientation orient = FT_ORIENTATION_NONE;
first = outline->points;
for ( i = 0; i < outline->n_contours; i++, first = last + 1 )
{
FT_Vector* point;
FT_Vector* xmin_point;
FT_Pos xmin;
last = outline->points + outline->contours[i];
/* skip degenerate contours */
if ( last < first + 2 )
continue;
if ( ft_contour_enclosed( outline, i ) )
continue;
xmin = first->x;
xmin_point = first;
for ( point = first + 1; point <= last; point++ )
{
if ( point->x < xmin )
{
xmin = point->x;
xmin_point = point;
}
}
/* check the orientation of the contour */
{
FT_Vector* prev;
FT_Vector* next;
FT_Orientation o;
prev = ( xmin_point == first ) ? last : xmin_point - 1;
next = ( xmin_point == last ) ? first : xmin_point + 1;
if ( FT_Atan2( prev->x - xmin_point->x, prev->y - xmin_point->y ) >
FT_Atan2( next->x - xmin_point->x, next->y - xmin_point->y ) )
o = FT_ORIENTATION_POSTSCRIPT;
else
o = FT_ORIENTATION_TRUETYPE;
if ( orient == FT_ORIENTATION_NONE )
orient = o;
else if ( orient != o )
return FT_ORIENTATION_NONE;
}
}
return orient;
}
#endif /* 0 */
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_Embolden( FT_Outline* outline,
FT_Pos strength )
{
return FT_Outline_EmboldenXY( outline, strength, strength );
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Error )
FT_Outline_EmboldenXY( FT_Outline* outline,
FT_Pos xstrength,
FT_Pos ystrength )
{
FT_Vector* points;
FT_Int c, first, last;
FT_Int orientation;
if ( !outline )
return FT_THROW( Invalid_Outline );
xstrength /= 2;
ystrength /= 2;
if ( xstrength == 0 && ystrength == 0 )
return FT_Err_Ok;
orientation = FT_Outline_Get_Orientation( outline );
if ( orientation == FT_ORIENTATION_NONE )
{
if ( outline->n_contours )
return FT_THROW( Invalid_Argument );
else
return FT_Err_Ok;
}
points = outline->points;
first = 0;
for ( c = 0; c < outline->n_contours; c++ )
{
FT_Vector in, out, anchor, shift;
FT_Fixed l_in, l_out, l_anchor = 0, l, q, d;
FT_Int i, j, k;
l_in = 0;
last = outline->contours[c];
/* pacify compiler */
in.x = in.y = anchor.x = anchor.y = 0;
/* Counter j cycles though the points; counter i advances only */
/* when points are moved; anchor k marks the first moved point. */
for ( i = last, j = first, k = -1;
j != i && i != k;
j = j < last ? j + 1 : first )
{
if ( j != k )
{
out.x = points[j].x - points[i].x;
out.y = points[j].y - points[i].y;
l_out = (FT_Fixed)FT_Vector_NormLen( &out );
if ( l_out == 0 )
continue;
}
else
{
out = anchor;
l_out = l_anchor;
}
if ( l_in != 0 )
{
if ( k < 0 )
{
k = i;
anchor = in;
l_anchor = l_in;
}
d = FT_MulFix( in.x, out.x ) + FT_MulFix( in.y, out.y );
/* shift only if turn is less than ~160 degrees */
if ( d > -0xF000L )
{
d = d + 0x10000L;
/* shift components along lateral bisector in proper orientation */
shift.x = in.y + out.y;
shift.y = in.x + out.x;
if ( orientation == FT_ORIENTATION_TRUETYPE )
shift.x = -shift.x;
else
shift.y = -shift.y;
/* restrict shift magnitude to better handle collapsing segments */
q = FT_MulFix( out.x, in.y ) - FT_MulFix( out.y, in.x );
if ( orientation == FT_ORIENTATION_TRUETYPE )
q = -q;
l = FT_MIN( l_in, l_out );
/* non-strict inequalities avoid divide-by-zero when q == l == 0 */
if ( FT_MulFix( xstrength, q ) <= FT_MulFix( l, d ) )
shift.x = FT_MulDiv( shift.x, xstrength, d );
else
shift.x = FT_MulDiv( shift.x, l, q );
if ( FT_MulFix( ystrength, q ) <= FT_MulFix( l, d ) )
shift.y = FT_MulDiv( shift.y, ystrength, d );
else
shift.y = FT_MulDiv( shift.y, l, q );
}
else
shift.x = shift.y = 0;
for ( ;
i != j;
i = i < last ? i + 1 : first )
{
points[i].x += xstrength + shift.x;
points[i].y += ystrength + shift.y;
}
}
else
i = j;
in = out;
l_in = l_out;
}
first = last + 1;
}
return FT_Err_Ok;
}
/* documentation is in ftoutln.h */
FT_EXPORT_DEF( FT_Orientation )
FT_Outline_Get_Orientation( FT_Outline* outline )
{
FT_BBox cbox;
FT_Int xshift, yshift;
FT_Vector* points;
FT_Vector v_prev, v_cur;
FT_Int c, n, first;
FT_Pos area = 0;
if ( !outline || outline->n_points <= 0 )
return FT_ORIENTATION_TRUETYPE;
/* We use the nonzero winding rule to find the orientation. */
/* Since glyph outlines behave much more `regular' than arbitrary */
/* cubic or quadratic curves, this test deals with the polygon */
/* only that is spanned up by the control points. */
FT_Outline_Get_CBox( outline, &cbox );
/* Handle collapsed outlines to avoid undefined FT_MSB. */
if ( cbox.xMin == cbox.xMax || cbox.yMin == cbox.yMax )
return FT_ORIENTATION_NONE;
xshift = FT_MSB( (FT_UInt32)( FT_ABS( cbox.xMax ) |
FT_ABS( cbox.xMin ) ) ) - 14;
xshift = FT_MAX( xshift, 0 );
yshift = FT_MSB( (FT_UInt32)( cbox.yMax - cbox.yMin ) ) - 14;
yshift = FT_MAX( yshift, 0 );
points = outline->points;
first = 0;
for ( c = 0; c < outline->n_contours; c++ )
{
FT_Int last = outline->contours[c];
v_prev.x = points[last].x >> xshift;
v_prev.y = points[last].y >> yshift;
for ( n = first; n <= last; n++ )
{
v_cur.x = points[n].x >> xshift;
v_cur.y = points[n].y >> yshift;
area += ( v_cur.y - v_prev.y ) * ( v_cur.x + v_prev.x );
v_prev = v_cur;
}
first = last + 1;
}
if ( area > 0 )
return FT_ORIENTATION_POSTSCRIPT;
else if ( area < 0 )
return FT_ORIENTATION_TRUETYPE;
else
return FT_ORIENTATION_NONE;
}
/* END */
src/base/ftsystem.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftsystem.c */
/* */
/* ANSI-specific FreeType low-level system interface (body). */
/* */
/* Copyright 1996-2016 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 default interface used by FreeType to access */
/* low-level, i.e. memory management, i/o access as well as thread */
/* synchronisation. It can be replaced by user-specific routines if */
/* necessary. */
/* */
/*************************************************************************/
#include <ft2build.h>
#include FT_CONFIG_CONFIG_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_SYSTEM_H
#include FT_ERRORS_H
#include FT_TYPES_H
/*************************************************************************/
/* */
/* MEMORY MANAGEMENT INTERFACE */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* It is not necessary to do any error checking for the */
/* allocation-related functions. This will be done by the higher level */
/* routines like ft_mem_alloc() or ft_mem_realloc(). */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* ft_alloc */
/* */
/* <Description> */
/* The memory allocation function. */
/* */
/* <Input> */
/* memory :: A pointer to the memory object. */
/* */
/* size :: The requested size in bytes. */
/* */
/* <Return> */
/* The address of newly allocated block. */
/* */
FT_CALLBACK_DEF( void* )
ft_alloc( FT_Memory memory,
long size )
{
FT_UNUSED( memory );
return ft_smalloc( (size_t)size );
}
/*************************************************************************/
/* */
/* <Function> */
/* ft_realloc */
/* */
/* <Description> */
/* The memory reallocation function. */
/* */
/* <Input> */
/* memory :: A pointer to the memory object. */
/* */
/* cur_size :: The current size of the allocated memory block. */
/* */
/* new_size :: The newly requested size in bytes. */
/* */
/* block :: The current address of the block in memory. */
/* */
/* <Return> */
/* The address of the reallocated memory block. */
/* */
FT_CALLBACK_DEF( void* )
ft_realloc( FT_Memory memory,
long cur_size,
long new_size,
void* block )
{
FT_UNUSED( memory );
FT_UNUSED( cur_size );
return ft_srealloc( block, (size_t)new_size );
}
/*************************************************************************/
/* */
/* <Function> */
/* ft_free */
/* */
/* <Description> */
/* The memory release function. */
/* */
/* <Input> */
/* memory :: A pointer to the memory object. */
/* */
/* block :: The address of block in memory to be freed. */
/* */
FT_CALLBACK_DEF( void )
ft_free( FT_Memory memory,
void* block )
{
FT_UNUSED( memory );
ft_sfree( block );
}
/*************************************************************************/
/* */
/* RESOURCE MANAGEMENT INTERFACE */
/* */
/*************************************************************************/
#ifndef FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT
/*************************************************************************/
/* */
/* 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_io
/* We use the macro STREAM_FILE for convenience to extract the */
/* system-specific stream handle from a given FreeType stream object */
#define STREAM_FILE( stream ) ( (FT_FILE*)stream->descriptor.pointer )
/*************************************************************************/
/* */
/* <Function> */
/* ft_ansi_stream_close */
/* */
/* <Description> */
/* The function to close a stream. */
/* */
/* <Input> */
/* stream :: A pointer to the stream object. */
/* */
FT_CALLBACK_DEF( void )
ft_ansi_stream_close( FT_Stream stream )
{
ft_fclose( STREAM_FILE( stream ) );
stream->descriptor.pointer = NULL;
stream->size = 0;
stream->base = NULL;
}
/*************************************************************************/
/* */
/* <Function> */
/* ft_ansi_stream_io */
/* */
/* <Description> */
/* The function to open a stream. */
/* */
/* <Input> */
/* stream :: A pointer to the stream object. */
/* */
/* offset :: The position in the data stream to start reading. */
/* */
/* buffer :: The address of buffer to store the read data. */
/* */
/* count :: The number of bytes to read from the stream. */
/* */
/* <Return> */
/* The number of bytes actually read. If `count' is zero (this is, */
/* the function is used for seeking), a non-zero return value */
/* indicates an error. */
/* */
FT_CALLBACK_DEF( unsigned long )
ft_ansi_stream_io( FT_Stream stream,
unsigned long offset,
unsigned char* buffer,
unsigned long count )
{
FT_FILE* file;
if ( !count && offset > stream->size )
return 1;
file = STREAM_FILE( stream );
if ( stream->pos != offset )
ft_fseek( file, (long)offset, SEEK_SET );
return (unsigned long)ft_fread( buffer, 1, count, file );
}
/* documentation is in ftstream.h */
FT_BASE_DEF( FT_Error )
FT_Stream_Open( FT_Stream stream,
const char* filepathname )
{
FT_FILE* file;
if ( !stream )
return FT_THROW( Invalid_Stream_Handle );
stream->descriptor.pointer = NULL;
stream->pathname.pointer = (char*)filepathname;
stream->base = NULL;
stream->pos = 0;
stream->read = NULL;
stream->close = NULL;
file = ft_fopen( filepathname, "rb" );
if ( !file )
{
FT_ERROR(( "FT_Stream_Open:"
" could not open `%s'\n", filepathname ));
return FT_THROW( Cannot_Open_Resource );
}
ft_fseek( file, 0, SEEK_END );
stream->size = (unsigned long)ft_ftell( file );
if ( !stream->size )
{
FT_ERROR(( "FT_Stream_Open:" ));
FT_ERROR(( " opened `%s' but zero-sized\n", filepathname ));
ft_fclose( file );
return FT_THROW( Cannot_Open_Stream );
}
ft_fseek( file, 0, SEEK_SET );
stream->descriptor.pointer = file;
stream->read = ft_ansi_stream_io;
stream->close = ft_ansi_stream_close;
FT_TRACE1(( "FT_Stream_Open:" ));
FT_TRACE1(( " opened `%s' (%d bytes) successfully\n",
filepathname, stream->size ));
return FT_Err_Ok;
}
#endif /* !FT_CONFIG_OPTION_DISABLE_STREAM_SUPPORT */
#ifdef FT_DEBUG_MEMORY
extern FT_Int
ft_mem_debug_init( FT_Memory memory );
extern void
ft_mem_debug_done( FT_Memory memory );
#endif
/* documentation is in ftobjs.h */
FT_BASE_DEF( FT_Memory )
FT_New_Memory( void )
{
FT_Memory memory;
memory = (FT_Memory)ft_smalloc( sizeof ( *memory ) );
if ( memory )
{
memory->user = NULL;
memory->alloc = ft_alloc;
memory->realloc = ft_realloc;
memory->free = ft_free;
#ifdef FT_DEBUG_MEMORY
ft_mem_debug_init( memory );
#endif
}
return memory;
}
/* documentation is in ftobjs.h */
FT_BASE_DEF( void )
FT_Done_Memory( FT_Memory memory )
{
#ifdef FT_DEBUG_MEMORY
ft_mem_debug_done( memory );
#endif
ft_sfree( memory );
}
/* END */
src/base/ftutil.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftutil.c */
/* */
/* FreeType utility file for memory and list management (body). */
/* */
/* Copyright 2002-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_MEMORY_H
#include FT_INTERNAL_OBJECTS_H
#include FT_LIST_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_memory
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** *****/
/***** M E M O R Y M A N A G E M E N T *****/
/***** *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
FT_BASE_DEF( FT_Pointer )
ft_mem_alloc( FT_Memory memory,
FT_Long size,
FT_Error *p_error )
{
FT_Error error;
FT_Pointer block = ft_mem_qalloc( memory, size, &error );
if ( !error && size > 0 )
FT_MEM_ZERO( block, size );
*p_error = error;
return block;
}
FT_BASE_DEF( FT_Pointer )
ft_mem_qalloc( FT_Memory memory,
FT_Long size,
FT_Error *p_error )
{
FT_Error error = FT_Err_Ok;
FT_Pointer block = NULL;
if ( size > 0 )
{
block = memory->alloc( memory, size );
if ( block == NULL )
error = FT_THROW( Out_Of_Memory );
}
else if ( size < 0 )
{
/* may help catch/prevent security issues */
error = FT_THROW( Invalid_Argument );
}
*p_error = error;
return block;
}
FT_BASE_DEF( FT_Pointer )
ft_mem_realloc( FT_Memory memory,
FT_Long item_size,
FT_Long cur_count,
FT_Long new_count,
void* block,
FT_Error *p_error )
{
FT_Error error = FT_Err_Ok;
block = ft_mem_qrealloc( memory, item_size,
cur_count, new_count, block, &error );
if ( !error && new_count > cur_count )
FT_MEM_ZERO( (char*)block + cur_count * item_size,
( new_count - cur_count ) * item_size );
*p_error = error;
return block;
}
FT_BASE_DEF( FT_Pointer )
ft_mem_qrealloc( FT_Memory memory,
FT_Long item_size,
FT_Long cur_count,
FT_Long new_count,
void* block,
FT_Error *p_error )
{
FT_Error error = FT_Err_Ok;
/* Note that we now accept `item_size == 0' as a valid parameter, in
* order to cover very weird cases where an ALLOC_MULT macro would be
* called.
*/
if ( cur_count < 0 || new_count < 0 || item_size < 0 )
{
/* may help catch/prevent nasty security issues */
error = FT_THROW( Invalid_Argument );
}
else if ( new_count == 0 || item_size == 0 )
{
ft_mem_free( memory, block );
block = NULL;
}
else if ( new_count > FT_INT_MAX/item_size )
{
error = FT_THROW( Array_Too_Large );
}
else if ( cur_count == 0 )
{
FT_ASSERT( block == NULL );
block = ft_mem_alloc( memory, new_count*item_size, &error );
}
else
{
FT_Pointer block2;
FT_Long cur_size = cur_count*item_size;
FT_Long new_size = new_count*item_size;
block2 = memory->realloc( memory, cur_size, new_size, block );
if ( block2 == NULL )
error = FT_THROW( Out_Of_Memory );
else
block = block2;
}
*p_error = error;
return block;
}
FT_BASE_DEF( void )
ft_mem_free( FT_Memory memory,
const void *P )
{
if ( P )
memory->free( memory, (void*)P );
}
FT_BASE_DEF( FT_Pointer )
ft_mem_dup( FT_Memory memory,
const void* address,
FT_ULong size,
FT_Error *p_error )
{
FT_Error error;
FT_Pointer p = ft_mem_qalloc( memory, (FT_Long)size, &error );
if ( !error && address )
ft_memcpy( p, address, size );
*p_error = error;
return p;
}
FT_BASE_DEF( FT_Pointer )
ft_mem_strdup( FT_Memory memory,
const char* str,
FT_Error *p_error )
{
FT_ULong len = str ? (FT_ULong)ft_strlen( str ) + 1
: 0;
return ft_mem_dup( memory, str, len, p_error );
}
FT_BASE_DEF( FT_Int )
ft_mem_strcpyn( char* dst,
const char* src,
FT_ULong size )
{
while ( size > 1 && *src != 0 )
{
*dst++ = *src++;
size--;
}
*dst = 0; /* always zero-terminate */
return *src != 0;
}
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** *****/
/***** D O U B L Y L I N K E D L I S T S *****/
/***** *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
#undef FT_COMPONENT
#define FT_COMPONENT trace_list
/* documentation is in ftlist.h */
FT_EXPORT_DEF( FT_ListNode )
FT_List_Find( FT_List list,
void* data )
{
FT_ListNode cur;
if ( !list )
return NULL;
cur = list->head;
while ( cur )
{
if ( cur->data == data )
return cur;
cur = cur->next;
}
return NULL;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( void )
FT_List_Add( FT_List list,
FT_ListNode node )
{
FT_ListNode before;
if ( !list || !node )
return;
before = list->tail;
node->next = NULL;
node->prev = before;
if ( before )
before->next = node;
else
list->head = node;
list->tail = node;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( void )
FT_List_Insert( FT_List list,
FT_ListNode node )
{
FT_ListNode after;
if ( !list || !node )
return;
after = list->head;
node->next = after;
node->prev = NULL;
if ( !after )
list->tail = node;
else
after->prev = node;
list->head = node;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( void )
FT_List_Remove( FT_List list,
FT_ListNode node )
{
FT_ListNode before, after;
if ( !list || !node )
return;
before = node->prev;
after = node->next;
if ( before )
before->next = after;
else
list->head = after;
if ( after )
after->prev = before;
else
list->tail = before;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( void )
FT_List_Up( FT_List list,
FT_ListNode node )
{
FT_ListNode before, after;
if ( !list || !node )
return;
before = node->prev;
after = node->next;
/* check whether we are already on top of the list */
if ( !before )
return;
before->next = after;
if ( after )
after->prev = before;
else
list->tail = before;
node->prev = NULL;
node->next = list->head;
list->head->prev = node;
list->head = node;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( FT_Error )
FT_List_Iterate( FT_List list,
FT_List_Iterator iterator,
void* user )
{
FT_ListNode cur;
FT_Error error = FT_Err_Ok;
if ( !list || !iterator )
return FT_THROW( Invalid_Argument );
cur = list->head;
while ( cur )
{
FT_ListNode next = cur->next;
error = iterator( cur, user );
if ( error )
break;
cur = next;
}
return error;
}
/* documentation is in ftlist.h */
FT_EXPORT_DEF( void )
FT_List_Finalize( FT_List list,
FT_List_Destructor destroy,
FT_Memory memory,
void* user )
{
FT_ListNode cur;
if ( !list || !memory )
return;
cur = list->head;
while ( cur )
{
FT_ListNode next = cur->next;
void* data = cur->data;
if ( destroy )
destroy( memory, data, user );
FT_FREE( cur );
cur = next;
}
list->head = NULL;
list->tail = NULL;
}
/* END */
src/cff/cffobjs.c
Not Covered Functions
/***************************************************************************/
/* */
/* cffobjs.c */
/* */
/* OpenType objects manager (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_STREAM_H
#include FT_ERRORS_H
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_SFNT_H
#include FT_CFF_DRIVER_H
#include "cffobjs.h"
#include "cffload.h"
#include "cffcmap.h"
#include "cffpic.h"
#include "cfferrs.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_cffobjs
/*************************************************************************/
/* */
/* SIZE FUNCTIONS */
/* */
/* Note that we store the global hints in the size's `internal' root */
/* field. */
/* */
/*************************************************************************/
static PSH_Globals_Funcs
cff_size_get_globals_funcs( CFF_Size size )
{
CFF_Face face = (CFF_Face)size->root.face;
CFF_Font font = (CFF_Font)face->extra.data;
PSHinter_Service pshinter = font->pshinter;
FT_Module module;
module = FT_Get_Module( size->root.face->driver->root.library,
"pshinter" );
return ( module && pshinter && pshinter->get_globals_funcs )
? pshinter->get_globals_funcs( module )
: 0;
}
FT_LOCAL_DEF( void )
cff_size_done( FT_Size cffsize ) /* CFF_Size */
{
CFF_Size size = (CFF_Size)cffsize;
CFF_Face face = (CFF_Face)size->root.face;
CFF_Font font = (CFF_Font)face->extra.data;
CFF_Internal internal = (CFF_Internal)cffsize->internal;
if ( internal )
{
PSH_Globals_Funcs funcs;
funcs = cff_size_get_globals_funcs( size );
if ( funcs )
{
FT_UInt i;
funcs->destroy( internal->topfont );
for ( i = font->num_subfonts; i > 0; i-- )
funcs->destroy( internal->subfonts[i - 1] );
}
/* `internal' is freed by destroy_size (in ftobjs.c) */
}
}
/* CFF and Type 1 private dictionaries have slightly different */
/* structures; we need to synthesize a Type 1 dictionary on the fly */
static void
cff_make_private_dict( CFF_SubFont subfont,
PS_Private priv )
{
CFF_Private cpriv = &subfont->private_dict;
FT_UInt n, count;
FT_MEM_ZERO( priv, sizeof ( *priv ) );
count = priv->num_blue_values = cpriv->num_blue_values;
for ( n = 0; n < count; n++ )
priv->blue_values[n] = (FT_Short)cpriv->blue_values[n];
count = priv->num_other_blues = cpriv->num_other_blues;
for ( n = 0; n < count; n++ )
priv->other_blues[n] = (FT_Short)cpriv->other_blues[n];
count = priv->num_family_blues = cpriv->num_family_blues;
for ( n = 0; n < count; n++ )
priv->family_blues[n] = (FT_Short)cpriv->family_blues[n];
count = priv->num_family_other_blues = cpriv->num_family_other_blues;
for ( n = 0; n < count; n++ )
priv->family_other_blues[n] = (FT_Short)cpriv->family_other_blues[n];
priv->blue_scale = cpriv->blue_scale;
priv->blue_shift = (FT_Int)cpriv->blue_shift;
priv->blue_fuzz = (FT_Int)cpriv->blue_fuzz;
priv->standard_width[0] = (FT_UShort)cpriv->standard_width;
priv->standard_height[0] = (FT_UShort)cpriv->standard_height;
count = priv->num_snap_widths = cpriv->num_snap_widths;
for ( n = 0; n < count; n++ )
priv->snap_widths[n] = (FT_Short)cpriv->snap_widths[n];
count = priv->num_snap_heights = cpriv->num_snap_heights;
for ( n = 0; n < count; n++ )
priv->snap_heights[n] = (FT_Short)cpriv->snap_heights[n];
priv->force_bold = cpriv->force_bold;
priv->language_group = cpriv->language_group;
priv->lenIV = cpriv->lenIV;
}
FT_LOCAL_DEF( FT_Error )
cff_size_init( FT_Size cffsize ) /* CFF_Size */
{
CFF_Size size = (CFF_Size)cffsize;
FT_Error error = FT_Err_Ok;
PSH_Globals_Funcs funcs = cff_size_get_globals_funcs( size );
if ( funcs )
{
CFF_Face face = (CFF_Face)cffsize->face;
CFF_Font font = (CFF_Font)face->extra.data;
CFF_Internal internal = NULL;
PS_PrivateRec priv;
FT_Memory memory = cffsize->face->memory;
FT_UInt i;
if ( FT_NEW( internal ) )
goto Exit;
cff_make_private_dict( &font->top_font, &priv );
error = funcs->create( cffsize->face->memory, &priv,
&internal->topfont );
if ( error )
goto Exit;
for ( i = font->num_subfonts; i > 0; i-- )
{
CFF_SubFont sub = font->subfonts[i - 1];
cff_make_private_dict( sub, &priv );
error = funcs->create( cffsize->face->memory, &priv,
&internal->subfonts[i - 1] );
if ( error )
goto Exit;
}
cffsize->internal = (FT_Size_Internal)(void*)internal;
}
size->strike_index = 0xFFFFFFFFUL;
Exit:
return error;
}
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
FT_LOCAL_DEF( FT_Error )
cff_size_select( FT_Size size,
FT_ULong strike_index )
{
CFF_Size cffsize = (CFF_Size)size;
PSH_Globals_Funcs funcs;
cffsize->strike_index = strike_index;
FT_Select_Metrics( size->face, strike_index );
funcs = cff_size_get_globals_funcs( cffsize );
if ( funcs )
{
CFF_Face face = (CFF_Face)size->face;
CFF_Font font = (CFF_Font)face->extra.data;
CFF_Internal internal = (CFF_Internal)size->internal;
FT_Long top_upm = (FT_Long)font->top_font.font_dict.units_per_em;
FT_UInt i;
funcs->set_scale( internal->topfont,
size->metrics.x_scale, size->metrics.y_scale,
0, 0 );
for ( i = font->num_subfonts; i > 0; i-- )
{
CFF_SubFont sub = font->subfonts[i - 1];
FT_Long sub_upm = (FT_Long)sub->font_dict.units_per_em;
FT_Pos x_scale, y_scale;
if ( top_upm != sub_upm )
{
x_scale = FT_MulDiv( size->metrics.x_scale, top_upm, sub_upm );
y_scale = FT_MulDiv( size->metrics.y_scale, top_upm, sub_upm );
}
else
{
x_scale = size->metrics.x_scale;
y_scale = size->metrics.y_scale;
}
funcs->set_scale( internal->subfonts[i - 1],
x_scale, y_scale, 0, 0 );
}
}
return FT_Err_Ok;
}
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
FT_LOCAL_DEF( FT_Error )
cff_size_request( FT_Size size,
FT_Size_Request req )
{
CFF_Size cffsize = (CFF_Size)size;
PSH_Globals_Funcs funcs;
#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
if ( FT_HAS_FIXED_SIZES( size->face ) )
{
CFF_Face cffface = (CFF_Face)size->face;
SFNT_Service sfnt = (SFNT_Service)cffface->sfnt;
FT_ULong strike_index;
if ( sfnt->set_sbit_strike( cffface, req, &strike_index ) )
cffsize->strike_index = 0xFFFFFFFFUL;
else
return cff_size_select( size, strike_index );
}
#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
FT_Request_Metrics( size->face, req );
funcs = cff_size_get_globals_funcs( cffsize );
if ( funcs )
{
CFF_Face cffface = (CFF_Face)size->face;
CFF_Font font = (CFF_Font)cffface->extra.data;
CFF_Internal internal = (CFF_Internal)size->internal;
FT_Long top_upm = (FT_Long)font->top_font.font_dict.units_per_em;
FT_UInt i;
funcs->set_scale( internal->topfont,
size->metrics.x_scale, size->metrics.y_scale,
0, 0 );
for ( i = font->num_subfonts; i > 0; i-- )
{
CFF_SubFont sub = font->subfonts[i - 1];
FT_Long sub_upm = (FT_Long)sub->font_dict.units_per_em;
FT_Pos x_scale, y_scale;
if ( top_upm != sub_upm )
{
x_scale = FT_MulDiv( size->metrics.x_scale, top_upm, sub_upm );
y_scale = FT_MulDiv( size->metrics.y_scale, top_upm, sub_upm );
}
else
{
x_scale = size->metrics.x_scale;
y_scale = size->metrics.y_scale;
}
funcs->set_scale( internal->subfonts[i - 1],
x_scale, y_scale, 0, 0 );
}
}
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* SLOT FUNCTIONS */
/* */
/*************************************************************************/
FT_LOCAL_DEF( void )
cff_slot_done( FT_GlyphSlot slot )
{
slot->internal->glyph_hints = NULL;
}
FT_LOCAL_DEF( FT_Error )
cff_slot_init( FT_GlyphSlot slot )
{
CFF_Face face = (CFF_Face)slot->face;
CFF_Font font = (CFF_Font)face->extra.data;
PSHinter_Service pshinter = font->pshinter;
if ( pshinter )
{
FT_Module module;
module = FT_Get_Module( slot->face->driver->root.library,
"pshinter" );
if ( module )
{
T2_Hints_Funcs funcs;
funcs = pshinter->get_t2_funcs( module );
slot->internal->glyph_hints = (void*)funcs;
}
}
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* FACE FUNCTIONS */
/* */
/*************************************************************************/
static FT_String*
cff_strcpy( FT_Memory memory,
const FT_String* source )
{
FT_Error error;
FT_String* result;
(void)FT_STRDUP( result, source );
FT_UNUSED( error );
return result;
}
/* Strip all subset prefixes of the form `ABCDEF+'. Usually, there */
/* is only one, but font names like `APCOOG+JFABTD+FuturaBQ-Bold' */
/* have been seen in the wild. */
static void
remove_subset_prefix( FT_String* name )
{
FT_Int32 idx = 0;
FT_Int32 length = (FT_Int32)strlen( name ) + 1;
FT_Bool continue_search = 1;
while ( continue_search )
{
if ( length >= 7 && name[6] == '+' )
{
for ( idx = 0; idx < 6; idx++ )
{
/* ASCII uppercase letters */
if ( !( 'A' <= name[idx] && name[idx] <= 'Z' ) )
continue_search = 0;
}
if ( continue_search )
{
for ( idx = 7; idx < length; idx++ )
name[idx - 7] = name[idx];
length -= 7;
}
}
else
continue_search = 0;
}
}
/* Remove the style part from the family name (if present). */
static void
remove_style( FT_String* family_name,
const FT_String* style_name )
{
FT_Int32 family_name_length, style_name_length;
family_name_length = (FT_Int32)strlen( family_name );
style_name_length = (FT_Int32)strlen( style_name );
if ( family_name_length > style_name_length )
{
FT_Int idx;
for ( idx = 1; idx <= style_name_length; ++idx )
{
if ( family_name[family_name_length - idx] !=
style_name[style_name_length - idx] )
break;
}
if ( idx > style_name_length )
{
/* family_name ends with style_name; remove it */
idx = family_name_length - style_name_length - 1;
/* also remove special characters */
/* between real family name and style */
while ( idx > 0 &&
( family_name[idx] == '-' ||
family_name[idx] == ' ' ||
family_name[idx] == '_' ||
family_name[idx] == '+' ) )
--idx;
if ( idx > 0 )
family_name[idx + 1] = '\0';
}
}
}
FT_LOCAL_DEF( FT_Error )
cff_face_init( FT_Stream stream,
FT_Face cffface, /* CFF_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
CFF_Face face = (CFF_Face)cffface;
FT_Error error;
SFNT_Service sfnt;
FT_Service_PsCMaps psnames;
PSHinter_Service pshinter;
FT_Bool pure_cff = 1;
FT_Bool sfnt_format = 0;
FT_Library library = cffface->driver->root.library;
sfnt = (SFNT_Service)FT_Get_Module_Interface(
library, "sfnt" );
if ( !sfnt )
{
FT_ERROR(( "cff_face_init: cannot access `sfnt' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
pshinter = (PSHinter_Service)FT_Get_Module_Interface(
library, "pshinter" );
FT_TRACE2(( "CFF driver\n" ));
/* create input stream from resource */
if ( FT_STREAM_SEEK( 0 ) )
goto Exit;
/* check whether we have a valid OpenType file */
error = sfnt->init_face( stream, face, face_index, num_params, params );
if ( !error )
{
if ( face->format_tag != TTAG_OTTO ) /* `OTTO'; OpenType/CFF font */
{
FT_TRACE2(( " not an OpenType/CFF font\n" ));
error = FT_THROW( Unknown_File_Format );
goto Exit;
}
/* if we are performing a simple font format check, exit immediately */
if ( face_index < 0 )
return FT_Err_Ok;
sfnt_format = 1;
/* now, the font can be either an OpenType/CFF font, or an SVG CEF */
/* font; in the latter case it doesn't have a `head' table */
error = face->goto_table( face, TTAG_head, stream, 0 );
if ( !error )
{
pure_cff = 0;
/* load font directory */
error = sfnt->load_face( stream, face, face_index,
num_params, params );
if ( error )
goto Exit;
}
else
{
/* load the `cmap' table explicitly */
error = sfnt->load_cmap( face, stream );
if ( error )
goto Exit;
}
/* now load the CFF part of the file */
error = face->goto_table( face, TTAG_CFF, stream, 0 );
if ( error )
goto Exit;
}
else
{
/* rewind to start of file; we are going to load a pure-CFF font */
if ( FT_STREAM_SEEK( 0 ) )
goto Exit;
error = FT_Err_Ok;
}
/* now load and parse the CFF table in the file */
{
CFF_Font cff = NULL;
CFF_FontRecDict dict;
FT_Memory memory = cffface->memory;
FT_Int32 flags;
FT_UInt i;
if ( FT_NEW( cff ) )
goto Exit;
face->extra.data = cff;
error = cff_font_load( library, stream, face_index, cff, pure_cff );
if ( error )
goto Exit;
/* if we are performing a simple font format check, exit immediately */
/* (this is here for pure CFF) */
if ( face_index < 0 )
{
cffface->num_faces = (FT_Long)cff->num_faces;
return FT_Err_Ok;
}
cff->pshinter = pshinter;
cff->psnames = psnames;
cffface->face_index = face_index & 0xFFFF;
/* Complement the root flags with some interesting information. */
/* Note that this is only necessary for pure CFF and CEF fonts; */
/* SFNT based fonts use the `name' table instead. */
cffface->num_glyphs = (FT_Long)cff->num_glyphs;
dict = &cff->top_font.font_dict;
/* we need the `PSNames' module for CFF and CEF formats */
/* which aren't CID-keyed */
if ( dict->cid_registry == 0xFFFFU && !psnames )
{
FT_ERROR(( "cff_face_init:"
" cannot open CFF & CEF fonts\n"
" "
" without the `PSNames' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
#ifdef FT_DEBUG_LEVEL_TRACE
{
FT_UInt idx;
FT_String* s;
FT_TRACE4(( "SIDs\n" ));
/* dump string index, including default strings for convenience */
for ( idx = 0; idx <= 390; idx++ )
{
s = cff_index_get_sid_string( cff, idx );
if ( s )
FT_TRACE4(( " %5d %s\n", idx, s ));
}
/* In Multiple Master CFFs, two SIDs hold the Normalize Design */
/* Vector (NDV) and Convert Design Vector (CDV) charstrings, */
/* which may contain NULL bytes in the middle of the data, too. */
/* We thus access `cff->strings' directly. */
for ( idx = 1; idx < cff->num_strings; idx++ )
{
FT_Byte* s1 = cff->strings[idx - 1];
FT_Byte* s2 = cff->strings[idx];
FT_PtrDist s1len = s2 - s1 - 1; /* without the final NULL byte */
FT_PtrDist l;
FT_TRACE4(( " %5d ", idx + 390 ));
for ( l = 0; l < s1len; l++ )
FT_TRACE4(( "%c", s1[l] ));
FT_TRACE4(( "\n" ));
}
/* print last element */
if ( cff->num_strings )
{
FT_Byte* s1 = cff->strings[cff->num_strings - 1];
FT_Byte* s2 = cff->string_pool + cff->string_pool_size;
FT_PtrDist s1len = s2 - s1 - 1;
FT_PtrDist l;
FT_TRACE4(( " %5d ", cff->num_strings + 390 ));
for ( l = 0; l < s1len; l++ )
FT_TRACE4(( "%c", s1[l] ));
FT_TRACE4(( "\n" ));
}
}
#endif /* FT_DEBUG_LEVEL_TRACE */
if ( !dict->has_font_matrix )
dict->units_per_em = pure_cff ? 1000 : face->root.units_per_EM;
/* Normalize the font matrix so that `matrix->yy' is 1; the */
/* scaling is done with `units_per_em' then (at this point, */
/* it already contains the scaling factor, but without */
/* normalization of the matrix). */
/* */
/* Note that the offsets must be expressed in integer font */
/* units. */
{
FT_Matrix* matrix = &dict->font_matrix;
FT_Vector* offset = &dict->font_offset;
FT_ULong* upm = &dict->units_per_em;
FT_Fixed temp = FT_ABS( matrix->yy );
if ( temp != 0x10000L )
{
*upm = (FT_ULong)FT_DivFix( (FT_Long)*upm, temp );
matrix->xx = FT_DivFix( matrix->xx, temp );
matrix->yx = FT_DivFix( matrix->yx, temp );
matrix->xy = FT_DivFix( matrix->xy, temp );
matrix->yy = FT_DivFix( matrix->yy, temp );
offset->x = FT_DivFix( offset->x, temp );
offset->y = FT_DivFix( offset->y, temp );
}
offset->x >>= 16;
offset->y >>= 16;
}
for ( i = cff->num_subfonts; i > 0; i-- )
{
CFF_FontRecDict sub = &cff->subfonts[i - 1]->font_dict;
CFF_FontRecDict top = &cff->top_font.font_dict;
FT_Matrix* matrix;
FT_Vector* offset;
FT_ULong* upm;
FT_Fixed temp;
if ( sub->has_font_matrix )
{
FT_Long scaling;
/* if we have a top-level matrix, */
/* concatenate the subfont matrix */
if ( top->has_font_matrix )
{
if ( top->units_per_em > 1 && sub->units_per_em > 1 )
scaling = (FT_Long)FT_MIN( top->units_per_em,
sub->units_per_em );
else
scaling = 1;
FT_Matrix_Multiply_Scaled( &top->font_matrix,
&sub->font_matrix,
scaling );
FT_Vector_Transform_Scaled( &sub->font_offset,
&top->font_matrix,
scaling );
sub->units_per_em = (FT_ULong)
FT_MulDiv( (FT_Long)sub->units_per_em,
(FT_Long)top->units_per_em,
scaling );
}
}
else
{
sub->font_matrix = top->font_matrix;
sub->font_offset = top->font_offset;
sub->units_per_em = top->units_per_em;
}
matrix = &sub->font_matrix;
offset = &sub->font_offset;
upm = &sub->units_per_em;
temp = FT_ABS( matrix->yy );
if ( temp != 0x10000L )
{
*upm = (FT_ULong)FT_DivFix( (FT_Long)*upm, temp );
matrix->xx = FT_DivFix( matrix->xx, temp );
matrix->yx = FT_DivFix( matrix->yx, temp );
matrix->xy = FT_DivFix( matrix->xy, temp );
matrix->yy = FT_DivFix( matrix->yy, temp );
offset->x = FT_DivFix( offset->x, temp );
offset->y = FT_DivFix( offset->y, temp );
}
offset->x >>= 16;
offset->y >>= 16;
}
if ( pure_cff )
{
char* style_name = NULL;
/* set up num_faces */
cffface->num_faces = (FT_Long)cff->num_faces;
/* compute number of glyphs */
if ( dict->cid_registry != 0xFFFFU )
cffface->num_glyphs = (FT_Long)( cff->charset.max_cid + 1 );
else
cffface->num_glyphs = (FT_Long)cff->charstrings_index.count;
/* set global bbox, as well as EM size */
cffface->bbox.xMin = dict->font_bbox.xMin >> 16;
cffface->bbox.yMin = dict->font_bbox.yMin >> 16;
/* no `U' suffix here to 0xFFFF! */
cffface->bbox.xMax = ( dict->font_bbox.xMax + 0xFFFF ) >> 16;
cffface->bbox.yMax = ( dict->font_bbox.yMax + 0xFFFF ) >> 16;
cffface->units_per_EM = (FT_UShort)( dict->units_per_em );
cffface->ascender = (FT_Short)( cffface->bbox.yMax );
cffface->descender = (FT_Short)( cffface->bbox.yMin );
cffface->height = (FT_Short)( ( cffface->units_per_EM * 12 ) / 10 );
if ( cffface->height < cffface->ascender - cffface->descender )
cffface->height = (FT_Short)( cffface->ascender - cffface->descender );
cffface->underline_position =
(FT_Short)( dict->underline_position >> 16 );
cffface->underline_thickness =
(FT_Short)( dict->underline_thickness >> 16 );
/* retrieve font family & style name */
cffface->family_name = cff_index_get_name(
cff,
(FT_UInt)( face_index & 0xFFFF ) );
if ( cffface->family_name )
{
char* full = cff_index_get_sid_string( cff,
dict->full_name );
char* fullp = full;
char* family = cffface->family_name;
char* family_name = NULL;
remove_subset_prefix( cffface->family_name );
if ( dict->family_name )
{
family_name = cff_index_get_sid_string( cff,
dict->family_name );
if ( family_name )
family = family_name;
}
/* We try to extract the style name from the full name. */
/* We need to ignore spaces and dashes during the search. */
if ( full && family )
{
while ( *fullp )
{
/* skip common characters at the start of both strings */
if ( *fullp == *family )
{
family++;
fullp++;
continue;
}
/* ignore spaces and dashes in full name during comparison */
if ( *fullp == ' ' || *fullp == '-' )
{
fullp++;
continue;
}
/* ignore spaces and dashes in family name during comparison */
if ( *family == ' ' || *family == '-' )
{
family++;
continue;
}
if ( !*family && *fullp )
{
/* The full name begins with the same characters as the */
/* family name, with spaces and dashes removed. In this */
/* case, the remaining string in `fullp' will be used as */
/* the style name. */
style_name = cff_strcpy( memory, fullp );
/* remove the style part from the family name (if present) */
remove_style( cffface->family_name, style_name );
}
break;
}
}
}
else
{
char *cid_font_name =
cff_index_get_sid_string( cff,
dict->cid_font_name );
/* do we have a `/FontName' for a CID-keyed font? */
if ( cid_font_name )
cffface->family_name = cff_strcpy( memory, cid_font_name );
}
if ( style_name )
cffface->style_name = style_name;
else
/* assume "Regular" style if we don't know better */
cffface->style_name = cff_strcpy( memory, (char *)"Regular" );
/*******************************************************************/
/* */
/* Compute face flags. */
/* */
flags = FT_FACE_FLAG_SCALABLE | /* scalable outlines */
FT_FACE_FLAG_HORIZONTAL | /* horizontal data */
FT_FACE_FLAG_HINTER; /* has native hinter */
if ( sfnt_format )
flags |= FT_FACE_FLAG_SFNT;
/* fixed width font? */
if ( dict->is_fixed_pitch )
flags |= FT_FACE_FLAG_FIXED_WIDTH;
/* XXX: WE DO NOT SUPPORT KERNING METRICS IN THE GPOS TABLE FOR NOW */
#if 0
/* kerning available? */
if ( face->kern_pairs )
flags |= FT_FACE_FLAG_KERNING;
#endif
cffface->face_flags |= flags;
/*******************************************************************/
/* */
/* Compute style flags. */
/* */
flags = 0;
if ( dict->italic_angle )
flags |= FT_STYLE_FLAG_ITALIC;
{
char *weight = cff_index_get_sid_string( cff,
dict->weight );
if ( weight )
if ( !ft_strcmp( weight, "Bold" ) ||
!ft_strcmp( weight, "Black" ) )
flags |= FT_STYLE_FLAG_BOLD;
}
/* double check */
if ( !(flags & FT_STYLE_FLAG_BOLD) && cffface->style_name )
if ( !ft_strncmp( cffface->style_name, "Bold", 4 ) ||
!ft_strncmp( cffface->style_name, "Black", 5 ) )
flags |= FT_STYLE_FLAG_BOLD;
cffface->style_flags = flags;
}
#ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES
/* CID-keyed CFF fonts don't have glyph names -- the SFNT loader */
/* has unset this flag because of the 3.0 `post' table. */
if ( dict->cid_registry == 0xFFFFU )
cffface->face_flags |= FT_FACE_FLAG_GLYPH_NAMES;
#endif
if ( dict->cid_registry != 0xFFFFU && pure_cff )
cffface->face_flags |= FT_FACE_FLAG_CID_KEYED;
/*******************************************************************/
/* */
/* Compute char maps. */
/* */
/* Try to synthesize a Unicode charmap if there is none available */
/* already. If an OpenType font contains a Unicode "cmap", we */
/* will use it, whatever be in the CFF part of the file. */
{
FT_CharMapRec cmaprec;
FT_CharMap cmap;
FT_UInt nn;
CFF_Encoding encoding = &cff->encoding;
for ( nn = 0; nn < (FT_UInt)cffface->num_charmaps; nn++ )
{
cmap = cffface->charmaps[nn];
/* Windows Unicode? */
if ( cmap->platform_id == TT_PLATFORM_MICROSOFT &&
cmap->encoding_id == TT_MS_ID_UNICODE_CS )
goto Skip_Unicode;
/* Apple Unicode platform id? */
if ( cmap->platform_id == TT_PLATFORM_APPLE_UNICODE )
goto Skip_Unicode; /* Apple Unicode */
}
/* since CID-keyed fonts don't contain glyph names, we can't */
/* construct a cmap */
if ( pure_cff && cff->top_font.font_dict.cid_registry != 0xFFFFU )
goto Exit;
/* we didn't find a Unicode charmap -- synthesize one */
cmaprec.face = cffface;
cmaprec.platform_id = TT_PLATFORM_MICROSOFT;
cmaprec.encoding_id = TT_MS_ID_UNICODE_CS;
cmaprec.encoding = FT_ENCODING_UNICODE;
nn = (FT_UInt)cffface->num_charmaps;
error = FT_CMap_New( &CFF_CMAP_UNICODE_CLASS_REC_GET, NULL,
&cmaprec, NULL );
if ( error &&
FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) )
goto Exit;
error = FT_Err_Ok;
/* if no Unicode charmap was previously selected, select this one */
if ( cffface->charmap == NULL && nn != (FT_UInt)cffface->num_charmaps )
cffface->charmap = cffface->charmaps[nn];
Skip_Unicode:
if ( encoding->count > 0 )
{
FT_CMap_Class clazz;
cmaprec.face = cffface;
cmaprec.platform_id = TT_PLATFORM_ADOBE; /* Adobe platform id */
if ( encoding->offset == 0 )
{
cmaprec.encoding_id = TT_ADOBE_ID_STANDARD;
cmaprec.encoding = FT_ENCODING_ADOBE_STANDARD;
clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET;
}
else if ( encoding->offset == 1 )
{
cmaprec.encoding_id = TT_ADOBE_ID_EXPERT;
cmaprec.encoding = FT_ENCODING_ADOBE_EXPERT;
clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET;
}
else
{
cmaprec.encoding_id = TT_ADOBE_ID_CUSTOM;
cmaprec.encoding = FT_ENCODING_ADOBE_CUSTOM;
clazz = &CFF_CMAP_ENCODING_CLASS_REC_GET;
}
error = FT_CMap_New( clazz, NULL, &cmaprec, NULL );
}
}
}
Exit:
return error;
}
FT_LOCAL_DEF( void )
cff_face_done( FT_Face cffface ) /* CFF_Face */
{
CFF_Face face = (CFF_Face)cffface;
FT_Memory memory;
SFNT_Service sfnt;
if ( !face )
return;
memory = cffface->memory;
sfnt = (SFNT_Service)face->sfnt;
if ( sfnt )
sfnt->done_face( face );
{
CFF_Font cff = (CFF_Font)face->extra.data;
if ( cff )
{
cff_font_done( cff );
FT_FREE( face->extra.data );
}
}
}
FT_LOCAL_DEF( FT_Error )
cff_driver_init( FT_Module module ) /* CFF_Driver */
{
CFF_Driver driver = (CFF_Driver)module;
/* set default property values, cf. `ftcffdrv.h' */
#ifdef CFF_CONFIG_OPTION_OLD_ENGINE
driver->hinting_engine = FT_CFF_HINTING_FREETYPE;
#else
driver->hinting_engine = FT_CFF_HINTING_ADOBE;
#endif
driver->no_stem_darkening = TRUE;
driver->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
driver->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
driver->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
driver->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
driver->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
driver->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
driver->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
driver->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
return FT_Err_Ok;
}
FT_LOCAL_DEF( void )
cff_driver_done( FT_Module module ) /* CFF_Driver */
{
FT_UNUSED( module );
}
/* END */
src/cid/cidobjs.c
Not Covered Functions
/***************************************************************************/
/* */
/* cidobjs.c */
/* */
/* CID objects manager (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include "cidgload.h"
#include "cidload.h"
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
#include FT_INTERNAL_POSTSCRIPT_AUX_H
#include FT_INTERNAL_POSTSCRIPT_HINTS_H
#include "ciderrs.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_cidobjs
/*************************************************************************/
/* */
/* SLOT FUNCTIONS */
/* */
/*************************************************************************/
FT_LOCAL_DEF( void )
cid_slot_done( FT_GlyphSlot slot )
{
slot->internal->glyph_hints = NULL;
}
FT_LOCAL_DEF( FT_Error )
cid_slot_init( FT_GlyphSlot slot )
{
CID_Face face;
PSHinter_Service pshinter;
face = (CID_Face)slot->face;
pshinter = (PSHinter_Service)face->pshinter;
if ( pshinter )
{
FT_Module module;
module = FT_Get_Module( slot->face->driver->root.library,
"pshinter" );
if ( module )
{
T1_Hints_Funcs funcs;
funcs = pshinter->get_t1_funcs( module );
slot->internal->glyph_hints = (void*)funcs;
}
}
return 0;
}
/*************************************************************************/
/* */
/* SIZE FUNCTIONS */
/* */
/*************************************************************************/
static PSH_Globals_Funcs
cid_size_get_globals_funcs( CID_Size size )
{
CID_Face face = (CID_Face)size->root.face;
PSHinter_Service pshinter = (PSHinter_Service)face->pshinter;
FT_Module module;
module = FT_Get_Module( size->root.face->driver->root.library,
"pshinter" );
return ( module && pshinter && pshinter->get_globals_funcs )
? pshinter->get_globals_funcs( module )
: 0;
}
FT_LOCAL_DEF( void )
cid_size_done( FT_Size cidsize ) /* CID_Size */
{
CID_Size size = (CID_Size)cidsize;
if ( cidsize->internal )
{
PSH_Globals_Funcs funcs;
funcs = cid_size_get_globals_funcs( size );
if ( funcs )
funcs->destroy( (PSH_Globals)cidsize->internal );
cidsize->internal = NULL;
}
}
FT_LOCAL_DEF( FT_Error )
cid_size_init( FT_Size cidsize ) /* CID_Size */
{
CID_Size size = (CID_Size)cidsize;
FT_Error error = FT_Err_Ok;
PSH_Globals_Funcs funcs = cid_size_get_globals_funcs( size );
if ( funcs )
{
PSH_Globals globals;
CID_Face face = (CID_Face)cidsize->face;
CID_FaceDict dict = face->cid.font_dicts + face->root.face_index;
PS_Private priv = &dict->private_dict;
error = funcs->create( cidsize->face->memory, priv, &globals );
if ( !error )
cidsize->internal = (FT_Size_Internal)(void*)globals;
}
return error;
}
FT_LOCAL( FT_Error )
cid_size_request( FT_Size size,
FT_Size_Request req )
{
PSH_Globals_Funcs funcs;
FT_Request_Metrics( size->face, req );
funcs = cid_size_get_globals_funcs( (CID_Size)size );
if ( funcs )
funcs->set_scale( (PSH_Globals)size->internal,
size->metrics.x_scale,
size->metrics.y_scale,
0, 0 );
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* FACE FUNCTIONS */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* cid_face_done */
/* */
/* <Description> */
/* Finalizes a given face object. */
/* */
/* <Input> */
/* face :: A pointer to the face object to destroy. */
/* */
FT_LOCAL_DEF( void )
cid_face_done( FT_Face cidface ) /* CID_Face */
{
CID_Face face = (CID_Face)cidface;
FT_Memory memory;
CID_FaceInfo cid;
PS_FontInfo info;
if ( !face )
return;
cid = &face->cid;
info = &cid->font_info;
memory = cidface->memory;
/* release subrs */
if ( face->subrs )
{
FT_Int n;
for ( n = 0; n < cid->num_dicts; n++ )
{
CID_Subrs subr = face->subrs + n;
if ( subr->code )
{
FT_FREE( subr->code[0] );
FT_FREE( subr->code );
}
}
FT_FREE( face->subrs );
}
/* release FontInfo strings */
FT_FREE( info->version );
FT_FREE( info->notice );
FT_FREE( info->full_name );
FT_FREE( info->family_name );
FT_FREE( info->weight );
/* release font dictionaries */
FT_FREE( cid->font_dicts );
cid->num_dicts = 0;
/* release other strings */
FT_FREE( cid->cid_font_name );
FT_FREE( cid->registry );
FT_FREE( cid->ordering );
cidface->family_name = NULL;
cidface->style_name = NULL;
FT_FREE( face->binary_data );
FT_FREE( face->cid_stream );
}
/*************************************************************************/
/* */
/* <Function> */
/* cid_face_init */
/* */
/* <Description> */
/* Initializes a given CID face object. */
/* */
/* <Input> */
/* stream :: The source font stream. */
/* */
/* face_index :: The index of the font face in the resource. */
/* */
/* num_params :: Number of additional generic parameters. Ignored. */
/* */
/* params :: Additional generic parameters. Ignored. */
/* */
/* <InOut> */
/* face :: The newly built face object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
cid_face_init( FT_Stream stream,
FT_Face cidface, /* CID_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
CID_Face face = (CID_Face)cidface;
FT_Error error;
PSAux_Service psaux;
PSHinter_Service pshinter;
FT_UNUSED( num_params );
FT_UNUSED( params );
FT_UNUSED( stream );
cidface->num_faces = 1;
psaux = (PSAux_Service)face->psaux;
if ( !psaux )
{
psaux = (PSAux_Service)FT_Get_Module_Interface(
FT_FACE_LIBRARY( face ), "psaux" );
if ( !psaux )
{
FT_ERROR(( "cid_face_init: cannot access `psaux' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
face->psaux = psaux;
}
pshinter = (PSHinter_Service)face->pshinter;
if ( !pshinter )
{
pshinter = (PSHinter_Service)FT_Get_Module_Interface(
FT_FACE_LIBRARY( face ), "pshinter" );
face->pshinter = pshinter;
}
FT_TRACE2(( "CID driver\n" ));
/* open the tokenizer; this will also check the font format */
if ( FT_STREAM_SEEK( 0 ) )
goto Exit;
error = cid_face_open( face, face_index );
if ( error )
goto Exit;
/* if we just wanted to check the format, leave successfully now */
if ( face_index < 0 )
goto Exit;
/* check the face index */
/* XXX: handle CID fonts with more than a single face */
if ( ( face_index & 0xFFFF ) != 0 )
{
FT_ERROR(( "cid_face_init: invalid face index\n" ));
error = FT_THROW( Invalid_Argument );
goto Exit;
}
/* now load the font program into the face object */
/* initialize the face object fields */
/* set up root face fields */
{
CID_FaceInfo cid = &face->cid;
PS_FontInfo info = &cid->font_info;
cidface->num_glyphs = (FT_Long)cid->cid_count;
cidface->num_charmaps = 0;
cidface->face_index = face_index & 0xFFFF;
cidface->face_flags |= FT_FACE_FLAG_SCALABLE | /* scalable outlines */
FT_FACE_FLAG_HORIZONTAL | /* horizontal data */
FT_FACE_FLAG_HINTER; /* has native hinter */
if ( info->is_fixed_pitch )
cidface->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
/* XXX: TODO: add kerning with .afm support */
/* get style name -- be careful, some broken fonts only */
/* have a /FontName dictionary entry! */
cidface->family_name = info->family_name;
/* assume "Regular" style if we don't know better */
cidface->style_name = (char *)"Regular";
if ( cidface->family_name )
{
char* full = info->full_name;
char* family = cidface->family_name;
if ( full )
{
while ( *full )
{
if ( *full == *family )
{
family++;
full++;
}
else
{
if ( *full == ' ' || *full == '-' )
full++;
else if ( *family == ' ' || *family == '-' )
family++;
else
{
if ( !*family )
cidface->style_name = full;
break;
}
}
}
}
}
else
{
/* do we have a `/FontName'? */
if ( cid->cid_font_name )
cidface->family_name = cid->cid_font_name;
}
/* compute style flags */
cidface->style_flags = 0;
if ( info->italic_angle )
cidface->style_flags |= FT_STYLE_FLAG_ITALIC;
if ( info->weight )
{
if ( !ft_strcmp( info->weight, "Bold" ) ||
!ft_strcmp( info->weight, "Black" ) )
cidface->style_flags |= FT_STYLE_FLAG_BOLD;
}
/* no embedded bitmap support */
cidface->num_fixed_sizes = 0;
cidface->available_sizes = NULL;
cidface->bbox.xMin = cid->font_bbox.xMin >> 16;
cidface->bbox.yMin = cid->font_bbox.yMin >> 16;
/* no `U' suffix here to 0xFFFF! */
cidface->bbox.xMax = ( cid->font_bbox.xMax + 0xFFFF ) >> 16;
cidface->bbox.yMax = ( cid->font_bbox.yMax + 0xFFFF ) >> 16;
if ( !cidface->units_per_EM )
cidface->units_per_EM = 1000;
cidface->ascender = (FT_Short)( cidface->bbox.yMax );
cidface->descender = (FT_Short)( cidface->bbox.yMin );
cidface->height = (FT_Short)( ( cidface->units_per_EM * 12 ) / 10 );
if ( cidface->height < cidface->ascender - cidface->descender )
cidface->height = (FT_Short)( cidface->ascender - cidface->descender );
cidface->underline_position = (FT_Short)info->underline_position;
cidface->underline_thickness = (FT_Short)info->underline_thickness;
}
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* cid_driver_init */
/* */
/* <Description> */
/* Initializes a given CID driver object. */
/* */
/* <Input> */
/* driver :: A handle to the target driver object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
cid_driver_init( FT_Module driver )
{
FT_UNUSED( driver );
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* cid_driver_done */
/* */
/* <Description> */
/* Finalizes a given CID driver. */
/* */
/* <Input> */
/* driver :: A handle to the target CID driver. */
/* */
FT_LOCAL_DEF( void )
cid_driver_done( FT_Module driver )
{
FT_UNUSED( driver );
}
/* END */
src/pshinter/pshmod.c
Not Covered Functions
/***************************************************************************/
/* */
/* pshmod.c */
/* */
/* FreeType PostScript hinter module implementation (body). */
/* */
/* Copyright 2001-2016 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 <ft2build.h>
#include FT_INTERNAL_OBJECTS_H
#include "pshrec.h"
#include "pshalgo.h"
#include "pshpic.h"
/* the Postscript Hinter module structure */
typedef struct PS_Hinter_Module_Rec_
{
FT_ModuleRec root;
PS_HintsRec ps_hints;
PSH_Globals_FuncsRec globals_funcs;
T1_Hints_FuncsRec t1_funcs;
T2_Hints_FuncsRec t2_funcs;
} PS_Hinter_ModuleRec, *PS_Hinter_Module;
/* finalize module */
FT_CALLBACK_DEF( void )
ps_hinter_done( PS_Hinter_Module module )
{
module->t1_funcs.hints = NULL;
module->t2_funcs.hints = NULL;
ps_hints_done( &module->ps_hints );
}
/* initialize module, create hints recorder and the interface */
FT_CALLBACK_DEF( FT_Error )
ps_hinter_init( PS_Hinter_Module module )
{
FT_Memory memory = module->root.memory;
void* ph = &module->ps_hints;
ps_hints_init( &module->ps_hints, memory );
psh_globals_funcs_init( &module->globals_funcs );
t1_hints_funcs_init( &module->t1_funcs );
module->t1_funcs.hints = (T1_Hints)ph;
t2_hints_funcs_init( &module->t2_funcs );
module->t2_funcs.hints = (T2_Hints)ph;
return 0;
}
/* returns global hints interface */
FT_CALLBACK_DEF( PSH_Globals_Funcs )
pshinter_get_globals_funcs( FT_Module module )
{
return &((PS_Hinter_Module)module)->globals_funcs;
}
/* return Type 1 hints interface */
FT_CALLBACK_DEF( T1_Hints_Funcs )
pshinter_get_t1_funcs( FT_Module module )
{
return &((PS_Hinter_Module)module)->t1_funcs;
}
/* return Type 2 hints interface */
FT_CALLBACK_DEF( T2_Hints_Funcs )
pshinter_get_t2_funcs( FT_Module module )
{
return &((PS_Hinter_Module)module)->t2_funcs;
}
FT_DEFINE_PSHINTER_INTERFACE(
pshinter_interface,
pshinter_get_globals_funcs,
pshinter_get_t1_funcs,
pshinter_get_t2_funcs )
FT_DEFINE_MODULE(
pshinter_module_class,
0,
sizeof ( PS_Hinter_ModuleRec ),
"pshinter",
0x10000L,
0x20000L,
&PSHINTER_INTERFACE_GET, /* module-specific interface */
(FT_Module_Constructor)ps_hinter_init,
(FT_Module_Destructor) ps_hinter_done,
(FT_Module_Requester) NULL ) /* no additional interface for now */
/* END */
src/pshinter/pshrec.c
Not Covered Functions
/***************************************************************************/
/* */
/* pshrec.c */
/* */
/* FreeType PostScript hints recorder (body). */
/* */
/* Copyright 2001-2016 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 <ft2build.h>
#include FT_FREETYPE_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_CALC_H
#include "pshrec.h"
#include "pshalgo.h"
#include "pshnterr.h"
#undef FT_COMPONENT
#define FT_COMPONENT trace_pshrec
#ifdef DEBUG_HINTER
PS_Hints ps_debug_hints = NULL;
int ps_debug_no_horz_hints = 0;
int ps_debug_no_vert_hints = 0;
#endif
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** PS_HINT MANAGEMENT *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* destroy hints table */
static void
ps_hint_table_done( PS_Hint_Table table,
FT_Memory memory )
{
FT_FREE( table->hints );
table->num_hints = 0;
table->max_hints = 0;
}
/* ensure that a table can contain "count" elements */
static FT_Error
ps_hint_table_ensure( PS_Hint_Table table,
FT_UInt count,
FT_Memory memory )
{
FT_UInt old_max = table->max_hints;
FT_UInt new_max = count;
FT_Error error = FT_Err_Ok;
if ( new_max > old_max )
{
/* try to grow the table */
new_max = FT_PAD_CEIL( new_max, 8 );
if ( !FT_RENEW_ARRAY( table->hints, old_max, new_max ) )
table->max_hints = new_max;
}
return error;
}
static FT_Error
ps_hint_table_alloc( PS_Hint_Table table,
FT_Memory memory,
PS_Hint *ahint )
{
FT_Error error = FT_Err_Ok;
FT_UInt count;
PS_Hint hint = NULL;
count = table->num_hints;
count++;
if ( count >= table->max_hints )
{
error = ps_hint_table_ensure( table, count, memory );
if ( error )
goto Exit;
}
hint = table->hints + count - 1;
hint->pos = 0;
hint->len = 0;
hint->flags = 0;
table->num_hints = count;
Exit:
*ahint = hint;
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** PS_MASK MANAGEMENT *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* destroy mask */
static void
ps_mask_done( PS_Mask mask,
FT_Memory memory )
{
FT_FREE( mask->bytes );
mask->num_bits = 0;
mask->max_bits = 0;
mask->end_point = 0;
}
/* ensure that a mask can contain "count" bits */
static FT_Error
ps_mask_ensure( PS_Mask mask,
FT_UInt count,
FT_Memory memory )
{
FT_UInt old_max = ( mask->max_bits + 7 ) >> 3;
FT_UInt new_max = ( count + 7 ) >> 3;
FT_Error error = FT_Err_Ok;
if ( new_max > old_max )
{
new_max = FT_PAD_CEIL( new_max, 8 );
if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) )
mask->max_bits = new_max * 8;
}
return error;
}
/* test a bit value in a given mask */
static FT_Int
ps_mask_test_bit( PS_Mask mask,
FT_Int idx )
{
if ( (FT_UInt)idx >= mask->num_bits )
return 0;
return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) );
}
/* clear a given bit */
static void
ps_mask_clear_bit( PS_Mask mask,
FT_UInt idx )
{
FT_Byte* p;
if ( idx >= mask->num_bits )
return;
p = mask->bytes + ( idx >> 3 );
p[0] = (FT_Byte)( p[0] & ~( 0x80 >> ( idx & 7 ) ) );
}
/* set a given bit, possibly grow the mask */
static FT_Error
ps_mask_set_bit( PS_Mask mask,
FT_UInt idx,
FT_Memory memory )
{
FT_Error error = FT_Err_Ok;
FT_Byte* p;
if ( idx >= mask->num_bits )
{
error = ps_mask_ensure( mask, idx + 1, memory );
if ( error )
goto Exit;
mask->num_bits = idx + 1;
}
p = mask->bytes + ( idx >> 3 );
p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) );
Exit:
return error;
}
/* destroy mask table */
static void
ps_mask_table_done( PS_Mask_Table table,
FT_Memory memory )
{
FT_UInt count = table->max_masks;
PS_Mask mask = table->masks;
for ( ; count > 0; count--, mask++ )
ps_mask_done( mask, memory );
FT_FREE( table->masks );
table->num_masks = 0;
table->max_masks = 0;
}
/* ensure that a mask table can contain "count" masks */
static FT_Error
ps_mask_table_ensure( PS_Mask_Table table,
FT_UInt count,
FT_Memory memory )
{
FT_UInt old_max = table->max_masks;
FT_UInt new_max = count;
FT_Error error = FT_Err_Ok;
if ( new_max > old_max )
{
new_max = FT_PAD_CEIL( new_max, 8 );
if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) )
table->max_masks = new_max;
}
return error;
}
/* allocate a new mask in a table */
static FT_Error
ps_mask_table_alloc( PS_Mask_Table table,
FT_Memory memory,
PS_Mask *amask )
{
FT_UInt count;
FT_Error error = FT_Err_Ok;
PS_Mask mask = NULL;
count = table->num_masks;
count++;
if ( count > table->max_masks )
{
error = ps_mask_table_ensure( table, count, memory );
if ( error )
goto Exit;
}
mask = table->masks + count - 1;
mask->num_bits = 0;
mask->end_point = 0;
table->num_masks = count;
Exit:
*amask = mask;
return error;
}
/* return last hint mask in a table, create one if the table is empty */
static FT_Error
ps_mask_table_last( PS_Mask_Table table,
FT_Memory memory,
PS_Mask *amask )
{
FT_Error error = FT_Err_Ok;
FT_UInt count;
PS_Mask mask;
count = table->num_masks;
if ( count == 0 )
{
error = ps_mask_table_alloc( table, memory, &mask );
if ( error )
goto Exit;
}
else
mask = table->masks + count - 1;
Exit:
*amask = mask;
return error;
}
/* set a new mask to a given bit range */
static FT_Error
ps_mask_table_set_bits( PS_Mask_Table table,
const FT_Byte* source,
FT_UInt bit_pos,
FT_UInt bit_count,
FT_Memory memory )
{
FT_Error error;
PS_Mask mask;
error = ps_mask_table_last( table, memory, &mask );
if ( error )
goto Exit;
error = ps_mask_ensure( mask, bit_count, memory );
if ( error )
goto Exit;
mask->num_bits = bit_count;
/* now, copy bits */
{
FT_Byte* read = (FT_Byte*)source + ( bit_pos >> 3 );
FT_Int rmask = 0x80 >> ( bit_pos & 7 );
FT_Byte* write = mask->bytes;
FT_Int wmask = 0x80;
FT_Int val;
for ( ; bit_count > 0; bit_count-- )
{
val = write[0] & ~wmask;
if ( read[0] & rmask )
val |= wmask;
write[0] = (FT_Byte)val;
rmask >>= 1;
if ( rmask == 0 )
{
read++;
rmask = 0x80;
}
wmask >>= 1;
if ( wmask == 0 )
{
write++;
wmask = 0x80;
}
}
}
Exit:
return error;
}
/* test whether two masks in a table intersect */
static FT_Int
ps_mask_table_test_intersect( PS_Mask_Table table,
FT_UInt index1,
FT_UInt index2 )
{
PS_Mask mask1 = table->masks + index1;
PS_Mask mask2 = table->masks + index2;
FT_Byte* p1 = mask1->bytes;
FT_Byte* p2 = mask2->bytes;
FT_UInt count1 = mask1->num_bits;
FT_UInt count2 = mask2->num_bits;
FT_UInt count;
count = FT_MIN( count1, count2 );
for ( ; count >= 8; count -= 8 )
{
if ( p1[0] & p2[0] )
return 1;
p1++;
p2++;
}
if ( count == 0 )
return 0;
return ( p1[0] & p2[0] ) & ~( 0xFF >> count );
}
/* merge two masks, used by ps_mask_table_merge_all */
static FT_Error
ps_mask_table_merge( PS_Mask_Table table,
FT_UInt index1,
FT_UInt index2,
FT_Memory memory )
{
FT_Error error = FT_Err_Ok;
/* swap index1 and index2 so that index1 < index2 */
if ( index1 > index2 )
{
FT_UInt temp;
temp = index1;
index1 = index2;
index2 = temp;
}
if ( index1 < index2 && index2 < table->num_masks )
{
/* we need to merge the bitsets of index1 and index2 with a */
/* simple union */
PS_Mask mask1 = table->masks + index1;
PS_Mask mask2 = table->masks + index2;
FT_UInt count1 = mask1->num_bits;
FT_UInt count2 = mask2->num_bits;
FT_Int delta;
if ( count2 > 0 )
{
FT_UInt pos;
FT_Byte* read;
FT_Byte* write;
/* if "count2" is greater than "count1", we need to grow the */
/* first bitset, and clear the highest bits */
if ( count2 > count1 )
{
error = ps_mask_ensure( mask1, count2, memory );
if ( error )
goto Exit;
for ( pos = count1; pos < count2; pos++ )
ps_mask_clear_bit( mask1, pos );
}
/* merge (unite) the bitsets */
read = mask2->bytes;
write = mask1->bytes;
pos = ( count2 + 7 ) >> 3;
for ( ; pos > 0; pos-- )
{
write[0] = (FT_Byte)( write[0] | read[0] );
write++;
read++;
}
}
/* Now, remove "mask2" from the list. We need to keep the masks */
/* sorted in order of importance, so move table elements. */
mask2->num_bits = 0;
mask2->end_point = 0;
/* number of masks to move */
delta = (FT_Int)( table->num_masks - 1 - index2 );
if ( delta > 0 )
{
/* move to end of table for reuse */
PS_MaskRec dummy = *mask2;
ft_memmove( mask2,
mask2 + 1,
(FT_UInt)delta * sizeof ( PS_MaskRec ) );
mask2[delta] = dummy;
}
table->num_masks--;
}
else
FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n",
index1, index2 ));
Exit:
return error;
}
/* Try to merge all masks in a given table. This is used to merge */
/* all counter masks into independent counter "paths". */
/* */
static FT_Error
ps_mask_table_merge_all( PS_Mask_Table table,
FT_Memory memory )
{
FT_Int index1, index2;
FT_Error error = FT_Err_Ok;
/* both loops go down to 0, thus FT_Int for index1 and index2 */
for ( index1 = (FT_Int)table->num_masks - 1; index1 > 0; index1-- )
{
for ( index2 = index1 - 1; index2 >= 0; index2-- )
{
if ( ps_mask_table_test_intersect( table,
(FT_UInt)index1,
(FT_UInt)index2 ) )
{
error = ps_mask_table_merge( table,
(FT_UInt)index2,
(FT_UInt)index1,
memory );
if ( error )
goto Exit;
break;
}
}
}
Exit:
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** PS_DIMENSION MANAGEMENT *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* finalize a given dimension */
static void
ps_dimension_done( PS_Dimension dimension,
FT_Memory memory )
{
ps_mask_table_done( &dimension->counters, memory );
ps_mask_table_done( &dimension->masks, memory );
ps_hint_table_done( &dimension->hints, memory );
}
/* initialize a given dimension */
static void
ps_dimension_init( PS_Dimension dimension )
{
dimension->hints.num_hints = 0;
dimension->masks.num_masks = 0;
dimension->counters.num_masks = 0;
}
#if 0
/* set a bit at a given index in the current hint mask */
static FT_Error
ps_dimension_set_mask_bit( PS_Dimension dim,
FT_UInt idx,
FT_Memory memory )
{
PS_Mask mask;
FT_Error error = FT_Err_Ok;
/* get last hint mask */
error = ps_mask_table_last( &dim->masks, memory, &mask );
if ( error )
goto Exit;
error = ps_mask_set_bit( mask, idx, memory );
Exit:
return error;
}
#endif
/* set the end point in a mask, called from "End" & "Reset" methods */
static void
ps_dimension_end_mask( PS_Dimension dim,
FT_UInt end_point )
{
FT_UInt count = dim->masks.num_masks;
if ( count > 0 )
{
PS_Mask mask = dim->masks.masks + count - 1;
mask->end_point = end_point;
}
}
/* set the end point in the current mask, then create a new empty one */
/* (called by "Reset" method) */
static FT_Error
ps_dimension_reset_mask( PS_Dimension dim,
FT_UInt end_point,
FT_Memory memory )
{
PS_Mask mask;
/* end current mask */
ps_dimension_end_mask( dim, end_point );
/* allocate new one */
return ps_mask_table_alloc( &dim->masks, memory, &mask );
}
/* set a new mask, called from the "T2Stem" method */
static FT_Error
ps_dimension_set_mask_bits( PS_Dimension dim,
const FT_Byte* source,
FT_UInt source_pos,
FT_UInt source_bits,
FT_UInt end_point,
FT_Memory memory )
{
FT_Error error;
/* reset current mask, if any */
error = ps_dimension_reset_mask( dim, end_point, memory );
if ( error )
goto Exit;
/* set bits in new mask */
error = ps_mask_table_set_bits( &dim->masks, source,
source_pos, source_bits, memory );
Exit:
return error;
}
/* add a new single stem (called from "T1Stem" method) */
static FT_Error
ps_dimension_add_t1stem( PS_Dimension dim,
FT_Int pos,
FT_Int len,
FT_Memory memory,
FT_Int *aindex )
{
FT_Error error = FT_Err_Ok;
FT_UInt flags = 0;
/* detect ghost stem */
if ( len < 0 )
{
flags |= PS_HINT_FLAG_GHOST;
if ( len == -21 )
{
flags |= PS_HINT_FLAG_BOTTOM;
pos += len;
}
len = 0;
}
if ( aindex )
*aindex = -1;
/* now, lookup stem in the current hints table */
{
PS_Mask mask;
FT_UInt idx;
FT_UInt max = dim->hints.num_hints;
PS_Hint hint = dim->hints.hints;
for ( idx = 0; idx < max; idx++, hint++ )
{
if ( hint->pos == pos && hint->len == len )
break;
}
/* we need to create a new hint in the table */
if ( idx >= max )
{
error = ps_hint_table_alloc( &dim->hints, memory, &hint );
if ( error )
goto Exit;
hint->pos = pos;
hint->len = len;
hint->flags = flags;
}
/* now, store the hint in the current mask */
error = ps_mask_table_last( &dim->masks, memory, &mask );
if ( error )
goto Exit;
error = ps_mask_set_bit( mask, idx, memory );
if ( error )
goto Exit;
if ( aindex )
*aindex = (FT_Int)idx;
}
Exit:
return error;
}
/* add a "hstem3/vstem3" counter to our dimension table */
static FT_Error
ps_dimension_add_counter( PS_Dimension dim,
FT_Int hint1,
FT_Int hint2,
FT_Int hint3,
FT_Memory memory )
{
FT_Error error = FT_Err_Ok;
FT_UInt count = dim->counters.num_masks;
PS_Mask counter = dim->counters.masks;
/* try to find an existing counter mask that already uses */
/* one of these stems here */
for ( ; count > 0; count--, counter++ )
{
if ( ps_mask_test_bit( counter, hint1 ) ||
ps_mask_test_bit( counter, hint2 ) ||
ps_mask_test_bit( counter, hint3 ) )
break;
}
/* create a new counter when needed */
if ( count == 0 )
{
error = ps_mask_table_alloc( &dim->counters, memory, &counter );
if ( error )
goto Exit;
}
/* now, set the bits for our hints in the counter mask */
if ( hint1 >= 0 )
{
error = ps_mask_set_bit( counter, (FT_UInt)hint1, memory );
if ( error )
goto Exit;
}
if ( hint2 >= 0 )
{
error = ps_mask_set_bit( counter, (FT_UInt)hint2, memory );
if ( error )
goto Exit;
}
if ( hint3 >= 0 )
{
error = ps_mask_set_bit( counter, (FT_UInt)hint3, memory );
if ( error )
goto Exit;
}
Exit:
return error;
}
/* end of recording session for a given dimension */
static FT_Error
ps_dimension_end( PS_Dimension dim,
FT_UInt end_point,
FT_Memory memory )
{
/* end hint mask table */
ps_dimension_end_mask( dim, end_point );
/* merge all counter masks into independent "paths" */
return ps_mask_table_merge_all( &dim->counters, memory );
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** PS_RECORDER MANAGEMENT *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
/* destroy hints */
FT_LOCAL( void )
ps_hints_done( PS_Hints hints )
{
FT_Memory memory = hints->memory;
ps_dimension_done( &hints->dimension[0], memory );
ps_dimension_done( &hints->dimension[1], memory );
hints->error = FT_Err_Ok;
hints->memory = NULL;
}
FT_LOCAL( void )
ps_hints_init( PS_Hints hints,
FT_Memory memory )
{
FT_MEM_ZERO( hints, sizeof ( *hints ) );
hints->memory = memory;
}
/* initialize a hints for a new session */
static void
ps_hints_open( PS_Hints hints,
PS_Hint_Type hint_type )
{
hints->error = FT_Err_Ok;
hints->hint_type = hint_type;
ps_dimension_init( &hints->dimension[0] );
ps_dimension_init( &hints->dimension[1] );
}
/* add one or more stems to the current hints table */
static void
ps_hints_stem( PS_Hints hints,
FT_UInt dimension,
FT_Int count,
FT_Long* stems )
{
PS_Dimension dim;
if ( hints->error )
return;
/* limit "dimension" to 0..1 */
if ( dimension > 1 )
{
FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n",
dimension ));
dimension = ( dimension != 0 );
}
/* record the stems in the current hints/masks table */
/* (Type 1 & 2's `hstem' or `vstem' operators) */
dim = &hints->dimension[dimension];
for ( ; count > 0; count--, stems += 2 )
{
FT_Error error;
FT_Memory memory = hints->memory;
error = ps_dimension_add_t1stem( dim,
(FT_Int)stems[0],
(FT_Int)stems[1],
memory,
NULL );
if ( error )
{
FT_ERROR(( "ps_hints_stem: could not add stem"
" (%d,%d) to hints table\n", stems[0], stems[1] ));
hints->error = error;
return;
}
}
}
/* add one Type1 counter stem to the current hints table */
static void
ps_hints_t1stem3( PS_Hints hints,
FT_UInt dimension,
FT_Fixed* stems )
{
FT_Error error = FT_Err_Ok;
if ( !hints->error )
{
PS_Dimension dim;
FT_Memory memory = hints->memory;
FT_Int count;
FT_Int idx[3];
/* limit "dimension" to 0..1 */
if ( dimension > 1 )
{
FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n",
dimension ));
dimension = ( dimension != 0 );
}
dim = &hints->dimension[dimension];
/* there must be 6 elements in the 'stem' array */
if ( hints->hint_type == PS_HINT_TYPE_1 )
{
/* add the three stems to our hints/masks table */
for ( count = 0; count < 3; count++, stems += 2 )
{
error = ps_dimension_add_t1stem( dim,
(FT_Int)FIXED_TO_INT( stems[0] ),
(FT_Int)FIXED_TO_INT( stems[1] ),
memory, &idx[count] );
if ( error )
goto Fail;
}
/* now, add the hints to the counters table */
error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2],
memory );
if ( error )
goto Fail;
}
else
{
FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type\n" ));
error = FT_THROW( Invalid_Argument );
goto Fail;
}
}
return;
Fail:
FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" ));
hints->error = error;
}
/* reset hints (only with Type 1 hints) */
static void
ps_hints_t1reset( PS_Hints hints,
FT_UInt end_point )
{
FT_Error error = FT_Err_Ok;
if ( !hints->error )
{
FT_Memory memory = hints->memory;
if ( hints->hint_type == PS_HINT_TYPE_1 )
{
error = ps_dimension_reset_mask( &hints->dimension[0],
end_point, memory );
if ( error )
goto Fail;
error = ps_dimension_reset_mask( &hints->dimension[1],
end_point, memory );
if ( error )
goto Fail;
}
else
{
/* invalid hint type */
error = FT_THROW( Invalid_Argument );
goto Fail;
}
}
return;
Fail:
hints->error = error;
}
/* Type2 "hintmask" operator, add a new hintmask to each direction */
static void
ps_hints_t2mask( PS_Hints hints,
FT_UInt end_point,
FT_UInt bit_count,
const FT_Byte* bytes )
{
FT_Error error;
if ( !hints->error )
{
PS_Dimension dim = hints->dimension;
FT_Memory memory = hints->memory;
FT_UInt count1 = dim[0].hints.num_hints;
FT_UInt count2 = dim[1].hints.num_hints;
/* check bit count; must be equal to current total hint count */
if ( bit_count != count1 + count2 )
{
FT_TRACE0(( "ps_hints_t2mask:"
" called with invalid bitcount %d (instead of %d)\n",
bit_count, count1 + count2 ));
/* simply ignore the operator */
return;
}
/* set-up new horizontal and vertical hint mask now */
error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1,
end_point, memory );
if ( error )
goto Fail;
error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2,
end_point, memory );
if ( error )
goto Fail;
}
return;
Fail:
hints->error = error;
}
static void
ps_hints_t2counter( PS_Hints hints,
FT_UInt bit_count,
const FT_Byte* bytes )
{
FT_Error error;
if ( !hints->error )
{
PS_Dimension dim = hints->dimension;
FT_Memory memory = hints->memory;
FT_UInt count1 = dim[0].hints.num_hints;
FT_UInt count2 = dim[1].hints.num_hints;
/* check bit count, must be equal to current total hint count */
if ( bit_count != count1 + count2 )
{
FT_TRACE0(( "ps_hints_t2counter:"
" called with invalid bitcount %d (instead of %d)\n",
bit_count, count1 + count2 ));
/* simply ignore the operator */
return;
}
/* set-up new horizontal and vertical hint mask now */
error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1,
0, memory );
if ( error )
goto Fail;
error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2,
0, memory );
if ( error )
goto Fail;
}
return;
Fail:
hints->error = error;
}
/* end recording session */
static FT_Error
ps_hints_close( PS_Hints hints,
FT_UInt end_point )
{
FT_Error error;
error = hints->error;
if ( !error )
{
FT_Memory memory = hints->memory;
PS_Dimension dim = hints->dimension;
error = ps_dimension_end( &dim[0], end_point, memory );
if ( !error )
{
error = ps_dimension_end( &dim[1], end_point, memory );
}
}
#ifdef DEBUG_HINTER
if ( !error )
ps_debug_hints = hints;
#endif
return error;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** TYPE 1 HINTS RECORDING INTERFACE *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static void
t1_hints_open( T1_Hints hints )
{
ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 );
}
static void
t1_hints_stem( T1_Hints hints,
FT_UInt dimension,
FT_Fixed* coords )
{
FT_Pos stems[2];
stems[0] = FIXED_TO_INT( coords[0] );
stems[1] = FIXED_TO_INT( coords[1] );
ps_hints_stem( (PS_Hints)hints, dimension, 1, stems );
}
FT_LOCAL_DEF( void )
t1_hints_funcs_init( T1_Hints_FuncsRec* funcs )
{
FT_MEM_ZERO( (char*)funcs, sizeof ( *funcs ) );
funcs->open = (T1_Hints_OpenFunc) t1_hints_open;
funcs->close = (T1_Hints_CloseFunc) ps_hints_close;
funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem;
funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3;
funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset;
funcs->apply = (T1_Hints_ApplyFunc) ps_hints_apply;
}
/*************************************************************************/
/*************************************************************************/
/***** *****/
/***** TYPE 2 HINTS RECORDING INTERFACE *****/
/***** *****/
/*************************************************************************/
/*************************************************************************/
static void
t2_hints_open( T2_Hints hints )
{
ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 );
}
static void
t2_hints_stems( T2_Hints hints,
FT_UInt dimension,
FT_Int count,
FT_Fixed* coords )
{
FT_Pos stems[32], y;
FT_Int total = count, n;
y = 0;
while ( total > 0 )
{
/* determine number of stems to write */
count = total;
if ( count > 16 )
count = 16;
/* compute integer stem positions in font units */
for ( n = 0; n < count * 2; n++ )
{
y += coords[n];
stems[n] = FIXED_TO_INT( y );
}
/* compute lengths */
for ( n = 0; n < count * 2; n += 2 )
stems[n + 1] = stems[n + 1] - stems[n];
/* add them to the current dimension */
ps_hints_stem( (PS_Hints)hints, dimension, count, stems );
total -= count;
}
}
FT_LOCAL_DEF( void )
t2_hints_funcs_init( T2_Hints_FuncsRec* funcs )
{
FT_MEM_ZERO( funcs, sizeof ( *funcs ) );
funcs->open = (T2_Hints_OpenFunc) t2_hints_open;
funcs->close = (T2_Hints_CloseFunc) ps_hints_close;
funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems;
funcs->hintmask= (T2_Hints_MaskFunc) ps_hints_t2mask;
funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter;
funcs->apply = (T2_Hints_ApplyFunc) ps_hints_apply;
}
/* END */
src/raster/ftraster.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftraster.c */
/* */
/* The FreeType glyph rasterizer (body). */
/* */
/* Copyright 1996-2016 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 can be compiled without the rest of the FreeType engine, by */
/* defining the STANDALONE_ macro when compiling it. You also need to */
/* put the files `ftimage.h' and `ftmisc.h' into the $(incdir) */
/* directory. Typically, you should do something like */
/* */
/* - copy `src/raster/ftraster.c' (this file) to your current directory */
/* */
/* - copy `include/freetype/ftimage.h' and `src/raster/ftmisc.h' to your */
/* current directory */
/* */
/* - compile `ftraster' with the STANDALONE_ macro defined, as in */
/* */
/* cc -c -DSTANDALONE_ ftraster.c */
/* */
/* The renderer can be initialized with a call to */
/* `ft_standard_raster.raster_new'; a bitmap can be generated */
/* with a call to `ft_standard_raster.raster_render'. */
/* */
/* See the comments and documentation in the file `ftimage.h' for more */
/* details on how the raster works. */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* This is a rewrite of the FreeType 1.x scan-line converter */
/* */
/*************************************************************************/
#ifdef STANDALONE_
/* The size in bytes of the render pool used by the scan-line converter */
/* to do all of its work. */
#define FT_RENDER_POOL_SIZE 16384L
#define FT_CONFIG_STANDARD_LIBRARY_H <stdlib.h>
#include <string.h> /* for memset */
#include "ftmisc.h"
#include "ftimage.h"
#else /* !STANDALONE_ */
#include <ft2build.h>
#include "ftraster.h"
#include FT_INTERNAL_CALC_H /* for FT_MulDiv and FT_MulDiv_No_Round */
#include "rastpic.h"
#endif /* !STANDALONE_ */
/*************************************************************************/
/* */
/* A simple technical note on how the raster works */
/* ----------------------------------------------- */
/* */
/* Converting an outline into a bitmap is achieved in several steps: */
/* */
/* 1 - Decomposing the outline into successive `profiles'. Each */
/* profile is simply an array of scanline intersections on a given */
/* dimension. A profile's main attributes are */
/* */
/* o its scanline position boundaries, i.e. `Ymin' and `Ymax' */
/* */
/* o an array of intersection coordinates for each scanline */
/* between `Ymin' and `Ymax' */
/* */
/* o a direction, indicating whether it was built going `up' or */
/* `down', as this is very important for filling rules */
/* */
/* o its drop-out mode */
/* */
/* 2 - Sweeping the target map's scanlines in order to compute segment */
/* `spans' which are then filled. Additionally, this pass */
/* performs drop-out control. */
/* */
/* The outline data is parsed during step 1 only. The profiles are */
/* built from the bottom of the render pool, used as a stack. The */
/* following graphics shows the profile list under construction: */
/* */
/* __________________________________________________________ _ _ */
/* | | | | | */
/* | profile | coordinates for | profile | coordinates for |--> */
/* | 1 | profile 1 | 2 | profile 2 |--> */
/* |_________|_________________|_________|_________________|__ _ _ */
/* */
/* ^ ^ */
/* | | */
/* start of render pool top */
/* */
/* The top of the profile stack is kept in the `top' variable. */
/* */
/* As you can see, a profile record is pushed on top of the render */
/* pool, which is then followed by its coordinates/intersections. If */
/* a change of direction is detected in the outline, a new profile is */
/* generated until the end of the outline. */
/* */
/* Note that when all profiles have been generated, the function */
/* Finalize_Profile_Table() is used to record, for each profile, its */
/* bottom-most scanline as well as the scanline above its upmost */
/* boundary. These positions are called `y-turns' because they (sort */
/* of) correspond to local extrema. They are stored in a sorted list */
/* built from the top of the render pool as a downwards stack: */
/* */
/* _ _ _______________________________________ */
/* | | */
/* <--| sorted list of | */
/* <--| extrema scanlines | */
/* _ _ __________________|____________________| */
/* */
/* ^ ^ */
/* | | */
/* maxBuff sizeBuff = end of pool */
/* */
/* This list is later used during the sweep phase in order to */
/* optimize performance (see technical note on the sweep below). */
/* */
/* Of course, the raster detects whether the two stacks collide and */
/* handles the situation properly. */
/* */
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/** **/
/** CONFIGURATION MACROS **/
/** **/
/*************************************************************************/
/*************************************************************************/
/* define DEBUG_RASTER if you want to compile a debugging version */
/* #define DEBUG_RASTER */
/*************************************************************************/
/*************************************************************************/
/** **/
/** OTHER MACROS (do not change) **/
/** **/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* */
/* 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_raster
#ifdef STANDALONE_
/* Auxiliary macros for token concatenation. */
#define FT_ERR_XCAT( x, y ) x ## y
#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y )
#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) )
/* This macro is used to indicate that a function parameter is unused. */
/* Its purpose is simply to reduce compiler warnings. Note also that */
/* simply defining it as `(void)x' doesn't avoid warnings with certain */
/* ANSI compilers (e.g. LCC). */
#define FT_UNUSED( x ) (x) = (x)
/* Disable the tracing mechanism for simplicity -- developers can */
/* activate it easily by redefining these macros. */
#ifndef FT_ERROR
#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
#endif
#ifndef FT_TRACE
#define FT_TRACE( x ) do { } while ( 0 ) /* nothing */
#define FT_TRACE1( x ) do { } while ( 0 ) /* nothing */
#define FT_TRACE6( x ) do { } while ( 0 ) /* nothing */
#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */
#endif
#ifndef FT_THROW
#define FT_THROW( e ) FT_ERR_CAT( Raster_Err_, e )
#endif
#define Raster_Err_None 0
#define Raster_Err_Not_Ini -1
#define Raster_Err_Overflow -2
#define Raster_Err_Neg_Height -3
#define Raster_Err_Invalid -4
#define Raster_Err_Unsupported -5
#define ft_memset memset
#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, raster_new_, \
raster_reset_, raster_set_mode_, \
raster_render_, raster_done_ ) \
const FT_Raster_Funcs class_ = \
{ \
glyph_format_, \
raster_new_, \
raster_reset_, \
raster_set_mode_, \
raster_render_, \
raster_done_ \
};
#else /* !STANDALONE_ */
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H /* for FT_TRACE, FT_ERROR, and FT_THROW */
#include "rasterrs.h"
#define Raster_Err_None FT_Err_Ok
#define Raster_Err_Not_Ini Raster_Err_Raster_Uninitialized
#define Raster_Err_Overflow Raster_Err_Raster_Overflow
#define Raster_Err_Neg_Height Raster_Err_Raster_Negative_Height
#define Raster_Err_Invalid Raster_Err_Invalid_Outline
#define Raster_Err_Unsupported Raster_Err_Cannot_Render_Glyph
#endif /* !STANDALONE_ */
#ifndef FT_MEM_SET
#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
#endif
#ifndef FT_MEM_ZERO
#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
#endif
/* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
/* typically a small value and the result of a*b is known to fit into */
/* 32 bits. */
#define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
/* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
/* for clipping computations. It simply uses the FT_MulDiv() function */
/* defined in `ftcalc.h'. */
#define SMulDiv FT_MulDiv
#define SMulDiv_No_Round FT_MulDiv_No_Round
/* The rasterizer is a very general purpose component; please leave */
/* the following redefinitions there (you never know your target */
/* environment). */
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef NULL
#define NULL (void*)0
#endif
#ifndef SUCCESS
#define SUCCESS 0
#endif
#ifndef FAILURE
#define FAILURE 1
#endif
#define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
/* Setting this constant to more than 32 is a */
/* pure waste of space. */
#define Pixel_Bits 6 /* fractional bits of *input* coordinates */
/*************************************************************************/
/*************************************************************************/
/** **/
/** SIMPLE TYPE DECLARATIONS **/
/** **/
/*************************************************************************/
/*************************************************************************/
typedef int Int;
typedef unsigned int UInt;
typedef short Short;
typedef unsigned short UShort, *PUShort;
typedef long Long, *PLong;
typedef unsigned long ULong;
typedef unsigned char Byte, *PByte;
typedef char Bool;
typedef union Alignment_
{
Long l;
void* p;
void (*f)(void);
} Alignment, *PAlignment;
typedef struct TPoint_
{
Long x;
Long y;
} TPoint;
/* values for the `flags' bit field */
#define Flow_Up 0x08U
#define Overshoot_Top 0x10U
#define Overshoot_Bottom 0x20U
/* States of each line, arc, and profile */
typedef enum TStates_
{
Unknown_State,
Ascending_State,
Descending_State,
Flat_State
} TStates;
typedef struct TProfile_ TProfile;
typedef TProfile* PProfile;
struct TProfile_
{
FT_F26Dot6 X; /* current coordinate during sweep */
PProfile link; /* link to next profile (various purposes) */
PLong offset; /* start of profile's data in render pool */
UShort flags; /* Bit 0-2: drop-out mode */
/* Bit 3: profile orientation (up/down) */
/* Bit 4: is top profile? */
/* Bit 5: is bottom profile? */
Long height; /* profile's height in scanlines */
Long start; /* profile's starting scanline */
Int countL; /* number of lines to step before this */
/* profile becomes drawable */
PProfile next; /* next profile in same contour, used */
/* during drop-out control */
};
typedef PProfile TProfileList;
typedef PProfile* PProfileList;
/* Simple record used to implement a stack of bands, required */
/* by the sub-banding mechanism */
typedef struct black_TBand_
{
Short y_min; /* band's minimum */
Short y_max; /* band's maximum */
} black_TBand;
#define AlignProfileSize \
( ( sizeof ( TProfile ) + sizeof ( Alignment ) - 1 ) / sizeof ( Long ) )
#undef RAS_ARG
#undef RAS_ARGS
#undef RAS_VAR
#undef RAS_VARS
#ifdef FT_STATIC_RASTER
#define RAS_ARGS /* void */
#define RAS_ARG /* void */
#define RAS_VARS /* void */
#define RAS_VAR /* void */
#define FT_UNUSED_RASTER do { } while ( 0 )
#else /* !FT_STATIC_RASTER */
#define RAS_ARGS black_PWorker worker,
#define RAS_ARG black_PWorker worker
#define RAS_VARS worker,
#define RAS_VAR worker
#define FT_UNUSED_RASTER FT_UNUSED( worker )
#endif /* !FT_STATIC_RASTER */
typedef struct black_TWorker_ black_TWorker, *black_PWorker;
/* prototypes used for sweep function dispatch */
typedef void
Function_Sweep_Init( RAS_ARGS Short* min,
Short* max );
typedef void
Function_Sweep_Span( RAS_ARGS Short y,
FT_F26Dot6 x1,
FT_F26Dot6 x2,
PProfile left,
PProfile right );
typedef void
Function_Sweep_Step( RAS_ARG );
/* NOTE: These operations are only valid on 2's complement processors */
#undef FLOOR
#undef CEILING
#undef TRUNC
#undef SCALED
#define FLOOR( x ) ( (x) & -ras.precision )
#define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
#define TRUNC( x ) ( (Long)(x) >> ras.precision_bits )
#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
#define SCALED( x ) ( ( (x) < 0 ? -( -(x) << ras.scale_shift ) \
: ( (x) << ras.scale_shift ) ) \
- ras.precision_half )
#define IS_BOTTOM_OVERSHOOT( x ) \
(Bool)( CEILING( x ) - x >= ras.precision_half )
#define IS_TOP_OVERSHOOT( x ) \
(Bool)( x - FLOOR( x ) >= ras.precision_half )
/* The most used variables are positioned at the top of the structure. */
/* Thus, their offset can be coded with less opcodes, resulting in a */
/* smaller executable. */
struct black_TWorker_
{
Int precision_bits; /* precision related variables */
Int precision;
Int precision_half;
Int precision_shift;
Int precision_step;
Int precision_jitter;
Int scale_shift; /* == precision_shift for bitmaps */
/* == precision_shift+1 for pixmaps */
PLong buff; /* The profiles buffer */
PLong sizeBuff; /* Render pool size */
PLong maxBuff; /* Profiles buffer size */
PLong top; /* Current cursor in buffer */
FT_Error error;
Int numTurns; /* number of Y-turns in outline */
TPoint* arc; /* current Bezier arc pointer */
UShort bWidth; /* target bitmap width */
PByte bTarget; /* target bitmap buffer */
PByte gTarget; /* target pixmap buffer */
Long lastX, lastY;
Long minY, maxY;
UShort num_Profs; /* current number of profiles */
Bool fresh; /* signals a fresh new profile which */
/* `start' field must be completed */
Bool joint; /* signals that the last arc ended */
/* exactly on a scanline. Allows */
/* removal of doublets */
PProfile cProfile; /* current profile */
PProfile fProfile; /* head of linked list of profiles */
PProfile gProfile; /* contour's first profile in case */
/* of impact */
TStates state; /* rendering state */
FT_Bitmap target; /* description of target bit/pixmap */
FT_Outline outline;
Long traceOfs; /* current offset in target bitmap */
Long traceG; /* current offset in target pixmap */
Short traceIncr; /* sweep's increment in target bitmap */
/* dispatch variables */
Function_Sweep_Init* Proc_Sweep_Init;
Function_Sweep_Span* Proc_Sweep_Span;
Function_Sweep_Span* Proc_Sweep_Drop;
Function_Sweep_Step* Proc_Sweep_Step;
Byte dropOutControl; /* current drop_out control method */
Bool second_pass; /* indicates whether a horizontal pass */
/* should be performed to control */
/* drop-out accurately when calling */
/* Render_Glyph. */
TPoint arcs[3 * MaxBezier + 1]; /* The Bezier stack */
black_TBand band_stack[16]; /* band stack used for sub-banding */
Int band_top; /* band stack top */
};
typedef struct black_TRaster_
{
void* memory;
} black_TRaster, *black_PRaster;
#ifdef FT_STATIC_RASTER
static black_TWorker cur_ras;
#define ras cur_ras
#else /* !FT_STATIC_RASTER */
#define ras (*worker)
#endif /* !FT_STATIC_RASTER */
/*************************************************************************/
/*************************************************************************/
/** **/
/** PROFILES COMPUTATION **/
/** **/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* Set_High_Precision */
/* */
/* <Description> */
/* Set precision variables according to param flag. */
/* */
/* <Input> */
/* High :: Set to True for high precision (typically for ppem < 24), */
/* false otherwise. */
/* */
static void
Set_High_Precision( RAS_ARGS Int High )
{
/*
* `precision_step' is used in `Bezier_Up' to decide when to split a
* given y-monotonous Bezier arc that crosses a scanline before
* approximating it as a straight segment. The default value of 32 (for
* low accuracy) corresponds to
*
* 32 / 64 == 0.5 pixels,
*
* while for the high accuracy case we have
*
* 256 / (1 << 12) = 0.0625 pixels.
*
* `precision_jitter' is an epsilon threshold used in
* `Vertical_Sweep_Span' to deal with small imperfections in the Bezier
* decomposition (after all, we are working with approximations only);
* it avoids switching on additional pixels which would cause artifacts
* otherwise.
*
* The value of `precision_jitter' has been determined heuristically.
*
*/
if ( High )
{
ras.precision_bits = 12;
ras.precision_step = 256;
ras.precision_jitter = 30;
}
else
{
ras.precision_bits = 6;
ras.precision_step = 32;
ras.precision_jitter = 2;
}
FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
ras.precision = 1 << ras.precision_bits;
ras.precision_half = ras.precision / 2;
ras.precision_shift = ras.precision_bits - Pixel_Bits;
}
/*************************************************************************/
/* */
/* <Function> */
/* New_Profile */
/* */
/* <Description> */
/* Create a new profile in the render pool. */
/* */
/* <Input> */
/* aState :: The state/orientation of the new profile. */
/* */
/* overshoot :: Whether the profile's unrounded start position */
/* differs by at least a half pixel. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow or of incoherent */
/* profile. */
/* */
static Bool
New_Profile( RAS_ARGS TStates aState,
Bool overshoot )
{
if ( !ras.fProfile )
{
ras.cProfile = (PProfile)ras.top;
ras.fProfile = ras.cProfile;
ras.top += AlignProfileSize;
}
if ( ras.top >= ras.maxBuff )
{
ras.error = FT_THROW( Overflow );
return FAILURE;
}
ras.cProfile->flags = 0;
ras.cProfile->start = 0;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
ras.cProfile->link = (PProfile)0;
ras.cProfile->next = (PProfile)0;
ras.cProfile->flags = ras.dropOutControl;
switch ( aState )
{
case Ascending_State:
ras.cProfile->flags |= Flow_Up;
if ( overshoot )
ras.cProfile->flags |= Overshoot_Bottom;
FT_TRACE6(( " new ascending profile = %p\n", ras.cProfile ));
break;
case Descending_State:
if ( overshoot )
ras.cProfile->flags |= Overshoot_Top;
FT_TRACE6(( " new descending profile = %p\n", ras.cProfile ));
break;
default:
FT_ERROR(( "New_Profile: invalid profile direction\n" ));
ras.error = FT_THROW( Invalid );
return FAILURE;
}
if ( !ras.gProfile )
ras.gProfile = ras.cProfile;
ras.state = aState;
ras.fresh = TRUE;
ras.joint = FALSE;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* End_Profile */
/* */
/* <Description> */
/* Finalize the current profile. */
/* */
/* <Input> */
/* overshoot :: Whether the profile's unrounded end position differs */
/* by at least a half pixel. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow or incoherency. */
/* */
static Bool
End_Profile( RAS_ARGS Bool overshoot )
{
Long h;
h = (Long)( ras.top - ras.cProfile->offset );
if ( h < 0 )
{
FT_ERROR(( "End_Profile: negative height encountered\n" ));
ras.error = FT_THROW( Neg_Height );
return FAILURE;
}
if ( h > 0 )
{
PProfile oldProfile;
FT_TRACE6(( " ending profile %p, start = %ld, height = %ld\n",
ras.cProfile, ras.cProfile->start, h ));
ras.cProfile->height = h;
if ( overshoot )
{
if ( ras.cProfile->flags & Flow_Up )
ras.cProfile->flags |= Overshoot_Top;
else
ras.cProfile->flags |= Overshoot_Bottom;
}
oldProfile = ras.cProfile;
ras.cProfile = (PProfile)ras.top;
ras.top += AlignProfileSize;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
oldProfile->next = ras.cProfile;
ras.num_Profs++;
}
if ( ras.top >= ras.maxBuff )
{
FT_TRACE1(( "overflow in End_Profile\n" ));
ras.error = FT_THROW( Overflow );
return FAILURE;
}
ras.joint = FALSE;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Insert_Y_Turn */
/* */
/* <Description> */
/* Insert a salient into the sorted list placed on top of the render */
/* pool. */
/* */
/* <Input> */
/* New y scanline position. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow. */
/* */
static Bool
Insert_Y_Turn( RAS_ARGS Int y )
{
PLong y_turns;
Int n;
n = ras.numTurns - 1;
y_turns = ras.sizeBuff - ras.numTurns;
/* look for first y value that is <= */
while ( n >= 0 && y < y_turns[n] )
n--;
/* if it is <, simply insert it, ignore if == */
if ( n >= 0 && y > y_turns[n] )
do
{
Int y2 = (Int)y_turns[n];
y_turns[n] = y;
y = y2;
} while ( --n >= 0 );
if ( n < 0 )
{
ras.maxBuff--;
if ( ras.maxBuff <= ras.top )
{
ras.error = FT_THROW( Overflow );
return FAILURE;
}
ras.numTurns++;
ras.sizeBuff[-ras.numTurns] = y;
}
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Finalize_Profile_Table */
/* */
/* <Description> */
/* Adjust all links in the profiles list. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow. */
/* */
static Bool
Finalize_Profile_Table( RAS_ARG )
{
UShort n;
PProfile p;
n = ras.num_Profs;
p = ras.fProfile;
if ( n > 1 && p )
{
do
{
Int bottom, top;
if ( n > 1 )
p->link = (PProfile)( p->offset + p->height );
else
p->link = NULL;
if ( p->flags & Flow_Up )
{
bottom = (Int)p->start;
top = (Int)( p->start + p->height - 1 );
}
else
{
bottom = (Int)( p->start - p->height + 1 );
top = (Int)p->start;
p->start = bottom;
p->offset += p->height - 1;
}
if ( Insert_Y_Turn( RAS_VARS bottom ) ||
Insert_Y_Turn( RAS_VARS top + 1 ) )
return FAILURE;
p = p->link;
} while ( --n );
}
else
ras.fProfile = NULL;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Split_Conic */
/* */
/* <Description> */
/* Subdivide one conic Bezier into two joint sub-arcs in the Bezier */
/* stack. */
/* */
/* <Input> */
/* None (subdivided Bezier is taken from the top of the stack). */
/* */
/* <Note> */
/* This routine is the `beef' of this component. It is _the_ inner */
/* loop that should be optimized to hell to get the best performance. */
/* */
static void
Split_Conic( TPoint* base )
{
Long a, b;
base[4].x = base[2].x;
b = base[1].x;
a = base[3].x = ( base[2].x + b ) / 2;
b = base[1].x = ( base[0].x + b ) / 2;
base[2].x = ( a + b ) / 2;
base[4].y = base[2].y;
b = base[1].y;
a = base[3].y = ( base[2].y + b ) / 2;
b = base[1].y = ( base[0].y + b ) / 2;
base[2].y = ( a + b ) / 2;
/* hand optimized. gcc doesn't seem to be too good at common */
/* expression substitution and instruction scheduling ;-) */
}
/*************************************************************************/
/* */
/* <Function> */
/* Split_Cubic */
/* */
/* <Description> */
/* Subdivide a third-order Bezier arc into two joint sub-arcs in the */
/* Bezier stack. */
/* */
/* <Note> */
/* This routine is the `beef' of the component. It is one of _the_ */
/* inner loops that should be optimized like hell to get the best */
/* performance. */
/* */
static void
Split_Cubic( TPoint* base )
{
Long a, b, c, d;
base[6].x = base[3].x;
c = base[1].x;
d = base[2].x;
base[1].x = a = ( base[0].x + c + 1 ) >> 1;
base[5].x = b = ( base[3].x + d + 1 ) >> 1;
c = ( c + d + 1 ) >> 1;
base[2].x = a = ( a + c + 1 ) >> 1;
base[4].x = b = ( b + c + 1 ) >> 1;
base[3].x = ( a + b + 1 ) >> 1;
base[6].y = base[3].y;
c = base[1].y;
d = base[2].y;
base[1].y = a = ( base[0].y + c + 1 ) >> 1;
base[5].y = b = ( base[3].y + d + 1 ) >> 1;
c = ( c + d + 1 ) >> 1;
base[2].y = a = ( a + c + 1 ) >> 1;
base[4].y = b = ( b + c + 1 ) >> 1;
base[3].y = ( a + b + 1 ) >> 1;
}
/*************************************************************************/
/* */
/* <Function> */
/* Line_Up */
/* */
/* <Description> */
/* Compute the x-coordinates of an ascending line segment and store */
/* them in the render pool. */
/* */
/* <Input> */
/* x1 :: The x-coordinate of the segment's start point. */
/* */
/* y1 :: The y-coordinate of the segment's start point. */
/* */
/* x2 :: The x-coordinate of the segment's end point. */
/* */
/* y2 :: The y-coordinate of the segment's end point. */
/* */
/* miny :: A lower vertical clipping bound value. */
/* */
/* maxy :: An upper vertical clipping bound value. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow. */
/* */
static Bool
Line_Up( RAS_ARGS Long x1,
Long y1,
Long x2,
Long y2,
Long miny,
Long maxy )
{
Long Dx, Dy;
Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
Long Ix, Rx, Ax;
PLong top;
Dx = x2 - x1;
Dy = y2 - y1;
if ( Dy <= 0 || y2 < miny || y1 > maxy )
return SUCCESS;
if ( y1 < miny )
{
/* Take care: miny-y1 can be a very large value; we use */
/* a slow MulDiv function to avoid clipping bugs */
x1 += SMulDiv( Dx, miny - y1, Dy );
e1 = (Int)TRUNC( miny );
f1 = 0;
}
else
{
e1 = (Int)TRUNC( y1 );
f1 = (Int)FRAC( y1 );
}
if ( y2 > maxy )
{
/* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
e2 = (Int)TRUNC( maxy );
f2 = 0;
}
else
{
e2 = (Int)TRUNC( y2 );
f2 = (Int)FRAC( y2 );
}
if ( f1 > 0 )
{
if ( e1 == e2 )
return SUCCESS;
else
{
x1 += SMulDiv( Dx, ras.precision - f1, Dy );
e1 += 1;
}
}
else
if ( ras.joint )
{
ras.top--;
ras.joint = FALSE;
}
ras.joint = (char)( f2 == 0 );
if ( ras.fresh )
{
ras.cProfile->start = e1;
ras.fresh = FALSE;
}
size = e2 - e1 + 1;
if ( ras.top + size >= ras.maxBuff )
{
ras.error = FT_THROW( Overflow );
return FAILURE;
}
if ( Dx > 0 )
{
Ix = SMulDiv_No_Round( ras.precision, Dx, Dy );
Rx = ( ras.precision * Dx ) % Dy;
Dx = 1;
}
else
{
Ix = -SMulDiv_No_Round( ras.precision, -Dx, Dy );
Rx = ( ras.precision * -Dx ) % Dy;
Dx = -1;
}
Ax = -Dy;
top = ras.top;
while ( size > 0 )
{
*top++ = x1;
x1 += Ix;
Ax += Rx;
if ( Ax >= 0 )
{
Ax -= Dy;
x1 += Dx;
}
size--;
}
ras.top = top;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Line_Down */
/* */
/* <Description> */
/* Compute the x-coordinates of an descending line segment and store */
/* them in the render pool. */
/* */
/* <Input> */
/* x1 :: The x-coordinate of the segment's start point. */
/* */
/* y1 :: The y-coordinate of the segment's start point. */
/* */
/* x2 :: The x-coordinate of the segment's end point. */
/* */
/* y2 :: The y-coordinate of the segment's end point. */
/* */
/* miny :: A lower vertical clipping bound value. */
/* */
/* maxy :: An upper vertical clipping bound value. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow. */
/* */
static Bool
Line_Down( RAS_ARGS Long x1,
Long y1,
Long x2,
Long y2,
Long miny,
Long maxy )
{
Bool result, fresh;
fresh = ras.fresh;
result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
if ( fresh && !ras.fresh )
ras.cProfile->start = -ras.cProfile->start;
return result;
}
/* A function type describing the functions used to split Bezier arcs */
typedef void (*TSplitter)( TPoint* base );
/*************************************************************************/
/* */
/* <Function> */
/* Bezier_Up */
/* */
/* <Description> */
/* Compute the x-coordinates of an ascending Bezier arc and store */
/* them in the render pool. */
/* */
/* <Input> */
/* degree :: The degree of the Bezier arc (either 2 or 3). */
/* */
/* splitter :: The function to split Bezier arcs. */
/* */
/* miny :: A lower vertical clipping bound value. */
/* */
/* maxy :: An upper vertical clipping bound value. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow. */
/* */
static Bool
Bezier_Up( RAS_ARGS Int degree,
TSplitter splitter,
Long miny,
Long maxy )
{
Long y1, y2, e, e2, e0;
Short f1;
TPoint* arc;
TPoint* start_arc;
PLong top;
arc = ras.arc;
y1 = arc[degree].y;
y2 = arc[0].y;
top = ras.top;
if ( y2 < miny || y1 > maxy )
goto Fin;
e2 = FLOOR( y2 );
if ( e2 > maxy )
e2 = maxy;
e0 = miny;
if ( y1 < miny )
e = miny;
else
{
e = CEILING( y1 );
f1 = (Short)( FRAC( y1 ) );
e0 = e;
if ( f1 == 0 )
{
if ( ras.joint )
{
top--;
ras.joint = FALSE;
}
*top++ = arc[degree].x;
e += ras.precision;
}
}
if ( ras.fresh )
{
ras.cProfile->start = TRUNC( e0 );
ras.fresh = FALSE;
}
if ( e2 < e )
goto Fin;
if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
{
ras.top = top;
ras.error = FT_THROW( Overflow );
return FAILURE;
}
start_arc = arc;
do
{
ras.joint = FALSE;
y2 = arc[0].y;
if ( y2 > e )
{
y1 = arc[degree].y;
if ( y2 - y1 >= ras.precision_step )
{
splitter( arc );
arc += degree;
}
else
{
*top++ = arc[degree].x + FMulDiv( arc[0].x - arc[degree].x,
e - y1, y2 - y1 );
arc -= degree;
e += ras.precision;
}
}
else
{
if ( y2 == e )
{
ras.joint = TRUE;
*top++ = arc[0].x;
e += ras.precision;
}
arc -= degree;
}
} while ( arc >= start_arc && e <= e2 );
Fin:
ras.top = top;
ras.arc -= degree;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Bezier_Down */
/* */
/* <Description> */
/* Compute the x-coordinates of an descending Bezier arc and store */
/* them in the render pool. */
/* */
/* <Input> */
/* degree :: The degree of the Bezier arc (either 2 or 3). */
/* */
/* splitter :: The function to split Bezier arcs. */
/* */
/* miny :: A lower vertical clipping bound value. */
/* */
/* maxy :: An upper vertical clipping bound value. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow. */
/* */
static Bool
Bezier_Down( RAS_ARGS Int degree,
TSplitter splitter,
Long miny,
Long maxy )
{
TPoint* arc = ras.arc;
Bool result, fresh;
arc[0].y = -arc[0].y;
arc[1].y = -arc[1].y;
arc[2].y = -arc[2].y;
if ( degree > 2 )
arc[3].y = -arc[3].y;
fresh = ras.fresh;
result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
if ( fresh && !ras.fresh )
ras.cProfile->start = -ras.cProfile->start;
arc[0].y = -arc[0].y;
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* Line_To */
/* */
/* <Description> */
/* Inject a new line segment and adjust the Profiles list. */
/* */
/* <Input> */
/* x :: The x-coordinate of the segment's end point (its start point */
/* is stored in `lastX'). */
/* */
/* y :: The y-coordinate of the segment's end point (its start point */
/* is stored in `lastY'). */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
/* profile. */
/* */
static Bool
Line_To( RAS_ARGS Long x,
Long y )
{
/* First, detect a change of direction */
switch ( ras.state )
{
case Unknown_State:
if ( y > ras.lastY )
{
if ( New_Profile( RAS_VARS Ascending_State,
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
else
{
if ( y < ras.lastY )
if ( New_Profile( RAS_VARS Descending_State,
IS_TOP_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
case Ascending_State:
if ( y < ras.lastY )
{
if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
New_Profile( RAS_VARS Descending_State,
IS_TOP_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
case Descending_State:
if ( y > ras.lastY )
{
if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
New_Profile( RAS_VARS Ascending_State,
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
default:
;
}
/* Then compute the lines */
switch ( ras.state )
{
case Ascending_State:
if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
case Descending_State:
if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
default:
;
}
ras.lastX = x;
ras.lastY = y;
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Conic_To */
/* */
/* <Description> */
/* Inject a new conic arc and adjust the profile list. */
/* */
/* <Input> */
/* cx :: The x-coordinate of the arc's new control point. */
/* */
/* cy :: The y-coordinate of the arc's new control point. */
/* */
/* x :: The x-coordinate of the arc's end point (its start point is */
/* stored in `lastX'). */
/* */
/* y :: The y-coordinate of the arc's end point (its start point is */
/* stored in `lastY'). */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
/* profile. */
/* */
static Bool
Conic_To( RAS_ARGS Long cx,
Long cy,
Long x,
Long y )
{
Long y1, y2, y3, x3, ymin, ymax;
TStates state_bez;
ras.arc = ras.arcs;
ras.arc[2].x = ras.lastX;
ras.arc[2].y = ras.lastY;
ras.arc[1].x = cx;
ras.arc[1].y = cy;
ras.arc[0].x = x;
ras.arc[0].y = y;
do
{
y1 = ras.arc[2].y;
y2 = ras.arc[1].y;
y3 = ras.arc[0].y;
x3 = ras.arc[0].x;
/* first, categorize the Bezier arc */
if ( y1 <= y3 )
{
ymin = y1;
ymax = y3;
}
else
{
ymin = y3;
ymax = y1;
}
if ( y2 < ymin || y2 > ymax )
{
/* this arc has no given direction, split it! */
Split_Conic( ras.arc );
ras.arc += 2;
}
else if ( y1 == y3 )
{
/* this arc is flat, ignore it and pop it from the Bezier stack */
ras.arc -= 2;
}
else
{
/* the arc is y-monotonous, either ascending or descending */
/* detect a change of direction */
state_bez = y1 < y3 ? Ascending_State : Descending_State;
if ( ras.state != state_bez )
{
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
: IS_TOP_OVERSHOOT( y1 );
/* finalize current profile if any */
if ( ras.state != Unknown_State &&
End_Profile( RAS_VARS o ) )
goto Fail;
/* create a new profile */
if ( New_Profile( RAS_VARS state_bez, o ) )
goto Fail;
}
/* now call the appropriate routine */
if ( state_bez == Ascending_State )
{
if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
goto Fail;
}
else
if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
goto Fail;
}
} while ( ras.arc >= ras.arcs );
ras.lastX = x3;
ras.lastY = y3;
return SUCCESS;
Fail:
return FAILURE;
}
/*************************************************************************/
/* */
/* <Function> */
/* Cubic_To */
/* */
/* <Description> */
/* Inject a new cubic arc and adjust the profile list. */
/* */
/* <Input> */
/* cx1 :: The x-coordinate of the arc's first new control point. */
/* */
/* cy1 :: The y-coordinate of the arc's first new control point. */
/* */
/* cx2 :: The x-coordinate of the arc's second new control point. */
/* */
/* cy2 :: The y-coordinate of the arc's second new control point. */
/* */
/* x :: The x-coordinate of the arc's end point (its start point is */
/* stored in `lastX'). */
/* */
/* y :: The y-coordinate of the arc's end point (its start point is */
/* stored in `lastY'). */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on render pool overflow or incorrect */
/* profile. */
/* */
static Bool
Cubic_To( RAS_ARGS Long cx1,
Long cy1,
Long cx2,
Long cy2,
Long x,
Long y )
{
Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
TStates state_bez;
ras.arc = ras.arcs;
ras.arc[3].x = ras.lastX;
ras.arc[3].y = ras.lastY;
ras.arc[2].x = cx1;
ras.arc[2].y = cy1;
ras.arc[1].x = cx2;
ras.arc[1].y = cy2;
ras.arc[0].x = x;
ras.arc[0].y = y;
do
{
y1 = ras.arc[3].y;
y2 = ras.arc[2].y;
y3 = ras.arc[1].y;
y4 = ras.arc[0].y;
x4 = ras.arc[0].x;
/* first, categorize the Bezier arc */
if ( y1 <= y4 )
{
ymin1 = y1;
ymax1 = y4;
}
else
{
ymin1 = y4;
ymax1 = y1;
}
if ( y2 <= y3 )
{
ymin2 = y2;
ymax2 = y3;
}
else
{
ymin2 = y3;
ymax2 = y2;
}
if ( ymin2 < ymin1 || ymax2 > ymax1 )
{
/* this arc has no given direction, split it! */
Split_Cubic( ras.arc );
ras.arc += 3;
}
else if ( y1 == y4 )
{
/* this arc is flat, ignore it and pop it from the Bezier stack */
ras.arc -= 3;
}
else
{
state_bez = ( y1 <= y4 ) ? Ascending_State : Descending_State;
/* detect a change of direction */
if ( ras.state != state_bez )
{
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
: IS_TOP_OVERSHOOT( y1 );
/* finalize current profile if any */
if ( ras.state != Unknown_State &&
End_Profile( RAS_VARS o ) )
goto Fail;
if ( New_Profile( RAS_VARS state_bez, o ) )
goto Fail;
}
/* compute intersections */
if ( state_bez == Ascending_State )
{
if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
goto Fail;
}
else
if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
goto Fail;
}
} while ( ras.arc >= ras.arcs );
ras.lastX = x4;
ras.lastY = y4;
return SUCCESS;
Fail:
return FAILURE;
}
#undef SWAP_
#define SWAP_( x, y ) do \
{ \
Long swap = x; \
\
\
x = y; \
y = swap; \
} while ( 0 )
/*************************************************************************/
/* */
/* <Function> */
/* Decompose_Curve */
/* */
/* <Description> */
/* Scan the outline arrays in order to emit individual segments and */
/* Beziers by calling Line_To() and Bezier_To(). It handles all */
/* weird cases, like when the first point is off the curve, or when */
/* there are simply no `on' points in the contour! */
/* */
/* <Input> */
/* first :: The index of the first point in the contour. */
/* */
/* last :: The index of the last point in the contour. */
/* */
/* flipped :: If set, flip the direction of the curve. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE on error. */
/* */
static Bool
Decompose_Curve( RAS_ARGS UShort first,
UShort last,
Int flipped )
{
FT_Vector v_last;
FT_Vector v_control;
FT_Vector v_start;
FT_Vector* points;
FT_Vector* point;
FT_Vector* limit;
char* tags;
UInt tag; /* current point's state */
points = ras.outline.points;
limit = points + last;
v_start.x = SCALED( points[first].x );
v_start.y = SCALED( points[first].y );
v_last.x = SCALED( points[last].x );
v_last.y = SCALED( points[last].y );
if ( flipped )
{
SWAP_( v_start.x, v_start.y );
SWAP_( v_last.x, v_last.y );
}
v_control = v_start;
point = points + first;
tags = ras.outline.tags + first;
/* set scan mode if necessary */
if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
ras.dropOutControl = (Byte)tags[0] >> 5;
tag = FT_CURVE_TAG( tags[0] );
/* A contour cannot start with a cubic control point! */
if ( tag == FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
/* check first point to determine origin */
if ( tag == FT_CURVE_TAG_CONIC )
{
/* first point is conic control. Yes, this happens. */
if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_CURVE_TAG_ON )
{
/* start at last point if it is on the curve */
v_start = v_last;
limit--;
}
else
{
/* if both first and last points are conic, */
/* start at their middle and record its position */
/* for closure */
v_start.x = ( v_start.x + v_last.x ) / 2;
v_start.y = ( v_start.y + v_last.y ) / 2;
/* v_last = v_start; */
}
point--;
tags--;
}
ras.lastX = v_start.x;
ras.lastY = v_start.y;
while ( point < limit )
{
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
switch ( tag )
{
case FT_CURVE_TAG_ON: /* emit a single line_to */
{
Long x, y;
x = SCALED( point->x );
y = SCALED( point->y );
if ( flipped )
SWAP_( x, y );
if ( Line_To( RAS_VARS x, y ) )
goto Fail;
continue;
}
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
v_control.x = SCALED( point[0].x );
v_control.y = SCALED( point[0].y );
if ( flipped )
SWAP_( v_control.x, v_control.y );
Do_Conic:
if ( point < limit )
{
FT_Vector v_middle;
Long x, y;
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
x = SCALED( point[0].x );
y = SCALED( point[0].y );
if ( flipped )
SWAP_( x, y );
if ( tag == FT_CURVE_TAG_ON )
{
if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
goto Fail;
continue;
}
if ( tag != FT_CURVE_TAG_CONIC )
goto Invalid_Outline;
v_middle.x = ( v_control.x + x ) / 2;
v_middle.y = ( v_control.y + y ) / 2;
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
v_middle.x, v_middle.y ) )
goto Fail;
v_control.x = x;
v_control.y = y;
goto Do_Conic;
}
if ( Conic_To( RAS_VARS v_control.x, v_control.y,
v_start.x, v_start.y ) )
goto Fail;
goto Close;
default: /* FT_CURVE_TAG_CUBIC */
{
Long x1, y1, x2, y2, x3, y3;
if ( point + 1 > limit ||
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
point += 2;
tags += 2;
x1 = SCALED( point[-2].x );
y1 = SCALED( point[-2].y );
x2 = SCALED( point[-1].x );
y2 = SCALED( point[-1].y );
if ( flipped )
{
SWAP_( x1, y1 );
SWAP_( x2, y2 );
}
if ( point <= limit )
{
x3 = SCALED( point[0].x );
y3 = SCALED( point[0].y );
if ( flipped )
SWAP_( x3, y3 );
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
goto Fail;
continue;
}
if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
goto Fail;
goto Close;
}
}
}
/* close the contour with a line segment */
if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
goto Fail;
Close:
return SUCCESS;
Invalid_Outline:
ras.error = FT_THROW( Invalid );
Fail:
return FAILURE;
}
/*************************************************************************/
/* */
/* <Function> */
/* Convert_Glyph */
/* */
/* <Description> */
/* Convert a glyph into a series of segments and arcs and make a */
/* profiles list with them. */
/* */
/* <Input> */
/* flipped :: If set, flip the direction of curve. */
/* */
/* <Return> */
/* SUCCESS on success, FAILURE if any error was encountered during */
/* rendering. */
/* */
static Bool
Convert_Glyph( RAS_ARGS Int flipped )
{
Int i;
UInt start;
ras.fProfile = NULL;
ras.joint = FALSE;
ras.fresh = FALSE;
ras.maxBuff = ras.sizeBuff - AlignProfileSize;
ras.numTurns = 0;
ras.cProfile = (PProfile)ras.top;
ras.cProfile->offset = ras.top;
ras.num_Profs = 0;
start = 0;
for ( i = 0; i < ras.outline.n_contours; i++ )
{
PProfile lastProfile;
Bool o;
ras.state = Unknown_State;
ras.gProfile = NULL;
if ( Decompose_Curve( RAS_VARS (UShort)start,
(UShort)ras.outline.contours[i],
flipped ) )
return FAILURE;
start = (UShort)ras.outline.contours[i] + 1;
/* we must now check whether the extreme arcs join or not */
if ( FRAC( ras.lastY ) == 0 &&
ras.lastY >= ras.minY &&
ras.lastY <= ras.maxY )
if ( ras.gProfile &&
( ras.gProfile->flags & Flow_Up ) ==
( ras.cProfile->flags & Flow_Up ) )
ras.top--;
/* Note that ras.gProfile can be nil if the contour was too small */
/* to be drawn. */
lastProfile = ras.cProfile;
if ( ras.top != ras.cProfile->offset &&
( ras.cProfile->flags & Flow_Up ) )
o = IS_TOP_OVERSHOOT( ras.lastY );
else
o = IS_BOTTOM_OVERSHOOT( ras.lastY );
if ( End_Profile( RAS_VARS o ) )
return FAILURE;
/* close the `next profile in contour' linked list */
if ( ras.gProfile )
lastProfile->next = ras.gProfile;
}
if ( Finalize_Profile_Table( RAS_VAR ) )
return FAILURE;
return (Bool)( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
}
/*************************************************************************/
/*************************************************************************/
/** **/
/** SCAN-LINE SWEEPS AND DRAWING **/
/** **/
/*************************************************************************/
/*************************************************************************/
/*************************************************************************/
/* */
/* Init_Linked */
/* */
/* Initializes an empty linked list. */
/* */
static void
Init_Linked( TProfileList* l )
{
*l = NULL;
}
/*************************************************************************/
/* */
/* InsNew */
/* */
/* Inserts a new profile in a linked list. */
/* */
static void
InsNew( PProfileList list,
PProfile profile )
{
PProfile *old, current;
Long x;
old = list;
current = *old;
x = profile->X;
while ( current )
{
if ( x < current->X )
break;
old = ¤t->link;
current = *old;
}
profile->link = current;
*old = profile;
}
/*************************************************************************/
/* */
/* DelOld */
/* */
/* Removes an old profile from a linked list. */
/* */
static void
DelOld( PProfileList list,
PProfile profile )
{
PProfile *old, current;
old = list;
current = *old;
while ( current )
{
if ( current == profile )
{
*old = current->link;
return;
}
old = ¤t->link;
current = *old;
}
/* we should never get there, unless the profile was not part of */
/* the list. */
}
/*************************************************************************/
/* */
/* Sort */
/* */
/* Sorts a trace list. In 95%, the list is already sorted. We need */
/* an algorithm which is fast in this case. Bubble sort is enough */
/* and simple. */
/* */
static void
Sort( PProfileList list )
{
PProfile *old, current, next;
/* First, set the new X coordinate of each profile */
current = *list;
while ( current )
{
current->X = *current->offset;
current->offset += ( current->flags & Flow_Up ) ? 1 : -1;
current->height--;
current = current->link;
}
/* Then sort them */
old = list;
current = *old;
if ( !current )
return;
next = current->link;
while ( next )
{
if ( current->X <= next->X )
{
old = ¤t->link;
current = *old;
if ( !current )
return;
}
else
{
*old = next;
current->link = next->link;
next->link = current;
old = list;
current = *old;
}
next = current->link;
}
}
/*************************************************************************/
/* */
/* Vertical Sweep Procedure Set */
/* */
/* These four routines are used during the vertical black/white sweep */
/* phase by the generic Draw_Sweep() function. */
/* */
/*************************************************************************/
static void
Vertical_Sweep_Init( RAS_ARGS Short* min,
Short* max )
{
Long pitch = ras.target.pitch;
FT_UNUSED( max );
ras.traceIncr = (Short)-pitch;
ras.traceOfs = -*min * pitch;
if ( pitch > 0 )
ras.traceOfs += (Long)( ras.target.rows - 1 ) * pitch;
}
static void
Vertical_Sweep_Span( RAS_ARGS Short y,
FT_F26Dot6 x1,
FT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2;
Byte* target;
Int dropOutControl = left->flags & 7;
FT_UNUSED( y );
FT_UNUSED( left );
FT_UNUSED( right );
/* in high-precision mode, we need 12 digits after the comma to */
/* represent multiples of 1/(1<<12) = 1/4096 */
FT_TRACE7(( " y=%d x=[%.12f;%.12f], drop-out=%d",
y,
x1 / (double)ras.precision,
x2 / (double)ras.precision,
dropOutControl ));
/* Drop-out control */
e1 = TRUNC( CEILING( x1 ) );
if ( dropOutControl != 2 &&
x2 - x1 - ras.precision <= ras.precision_jitter )
e2 = e1;
else
e2 = TRUNC( FLOOR( x2 ) );
if ( e2 >= 0 && e1 < ras.bWidth )
{
Int c1, c2;
Byte f1, f2;
if ( e1 < 0 )
e1 = 0;
if ( e2 >= ras.bWidth )
e2 = ras.bWidth - 1;
FT_TRACE7(( " -> x=[%d;%d]", e1, e2 ));
c1 = (Short)( e1 >> 3 );
c2 = (Short)( e2 >> 3 );
f1 = (Byte) ( 0xFF >> ( e1 & 7 ) );
f2 = (Byte) ~( 0x7F >> ( e2 & 7 ) );
target = ras.bTarget + ras.traceOfs + c1;
c2 -= c1;
if ( c2 > 0 )
{
target[0] |= f1;
/* memset() is slower than the following code on many platforms. */
/* This is due to the fact that, in the vast majority of cases, */
/* the span length in bytes is relatively small. */
c2--;
while ( c2 > 0 )
{
*(++target) = 0xFF;
c2--;
}
target[1] |= f2;
}
else
*target |= ( f1 & f2 );
}
FT_TRACE7(( "\n" ));
}
static void
Vertical_Sweep_Drop( RAS_ARGS Short y,
FT_F26Dot6 x1,
FT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2, pxl;
Short c1, f1;
FT_TRACE7(( " y=%d x=[%.12f;%.12f]",
y,
x1 / (double)ras.precision,
x2 / (double)ras.precision ));
/* Drop-out control */
/* e2 x2 x1 e1 */
/* */
/* ^ | */
/* | | */
/* +-------------+---------------------+------------+ */
/* | | */
/* | v */
/* */
/* pixel contour contour pixel */
/* center center */
/* drop-out mode scan conversion rules (as defined in OpenType) */
/* --------------------------------------------------------------- */
/* 0 1, 2, 3 */
/* 1 1, 2, 4 */
/* 2 1, 2 */
/* 3 same as mode 2 */
/* 4 1, 2, 5 */
/* 5 1, 2, 6 */
/* 6, 7 same as mode 2 */
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
pxl = e1;
if ( e1 > e2 )
{
Int dropOutControl = left->flags & 7;
FT_TRACE7(( ", drop-out=%d", dropOutControl ));
if ( e1 == e2 + ras.precision )
{
switch ( dropOutControl )
{
case 0: /* simple drop-outs including stubs */
pxl = e2;
break;
case 4: /* smart drop-outs including stubs */
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
break;
case 1: /* simple drop-outs excluding stubs */
case 5: /* smart drop-outs excluding stubs */
/* Drop-out Control Rules #4 and #6 */
/* The specification neither provides an exact definition */
/* of a `stub' nor gives exact rules to exclude them. */
/* */
/* Here the constraints we use to recognize a stub. */
/* */
/* upper stub: */
/* */
/* - P_Left and P_Right are in the same contour */
/* - P_Right is the successor of P_Left in that contour */
/* - y is the top of P_Left and P_Right */
/* */
/* lower stub: */
/* */
/* - P_Left and P_Right are in the same contour */
/* - P_Left is the successor of P_Right in that contour */
/* - y is the bottom of P_Left */
/* */
/* We draw a stub if the following constraints are met. */
/* */
/* - for an upper or lower stub, there is top or bottom */
/* overshoot, respectively */
/* - the covered interval is greater or equal to a half */
/* pixel */
/* upper stub test */
if ( left->next == right &&
left->height <= 0 &&
!( left->flags & Overshoot_Top &&
x2 - x1 >= ras.precision_half ) )
goto Exit;
/* lower stub test */
if ( right->next == left &&
left->start == y &&
!( left->flags & Overshoot_Bottom &&
x2 - x1 >= ras.precision_half ) )
goto Exit;
if ( dropOutControl == 1 )
pxl = e2;
else
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
break;
default: /* modes 2, 3, 6, 7 */
goto Exit; /* no drop-out control */
}
/* undocumented but confirmed: If the drop-out would result in a */
/* pixel outside of the bounding box, use the pixel inside of the */
/* bounding box instead */
if ( pxl < 0 )
pxl = e1;
else if ( TRUNC( pxl ) >= ras.bWidth )
pxl = e2;
/* check that the other pixel isn't set */
e1 = pxl == e1 ? e2 : e1;
e1 = TRUNC( e1 );
c1 = (Short)( e1 >> 3 );
f1 = (Short)( e1 & 7 );
if ( e1 >= 0 && e1 < ras.bWidth &&
ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
goto Exit;
}
else
goto Exit;
}
e1 = TRUNC( pxl );
if ( e1 >= 0 && e1 < ras.bWidth )
{
FT_TRACE7(( " -> x=%d (drop-out)", e1 ));
c1 = (Short)( e1 >> 3 );
f1 = (Short)( e1 & 7 );
ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
}
Exit:
FT_TRACE7(( "\n" ));
}
static void
Vertical_Sweep_Step( RAS_ARG )
{
ras.traceOfs += ras.traceIncr;
}
/***********************************************************************/
/* */
/* Horizontal Sweep Procedure Set */
/* */
/* These four routines are used during the horizontal black/white */
/* sweep phase by the generic Draw_Sweep() function. */
/* */
/***********************************************************************/
static void
Horizontal_Sweep_Init( RAS_ARGS Short* min,
Short* max )
{
/* nothing, really */
FT_UNUSED_RASTER;
FT_UNUSED( min );
FT_UNUSED( max );
}
static void
Horizontal_Sweep_Span( RAS_ARGS Short y,
FT_F26Dot6 x1,
FT_F26Dot6 x2,
PProfile left,
PProfile right )
{
FT_UNUSED( left );
FT_UNUSED( right );
if ( x2 - x1 < ras.precision )
{
Long e1, e2;
FT_TRACE7(( " x=%d y=[%.12f;%.12f]",
y,
x1 / (double)ras.precision,
x2 / (double)ras.precision ));
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
if ( e1 == e2 )
{
e1 = TRUNC( e1 );
if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
{
Byte f1;
PByte bits;
PByte p;
FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
bits = ras.bTarget + ( y >> 3 );
f1 = (Byte)( 0x80 >> ( y & 7 ) );
p = bits - e1 * ras.target.pitch;
if ( ras.target.pitch > 0 )
p += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
p[0] |= f1;
}
}
FT_TRACE7(( "\n" ));
}
}
static void
Horizontal_Sweep_Drop( RAS_ARGS Short y,
FT_F26Dot6 x1,
FT_F26Dot6 x2,
PProfile left,
PProfile right )
{
Long e1, e2, pxl;
PByte bits;
Byte f1;
FT_TRACE7(( " x=%d y=[%.12f;%.12f]",
y,
x1 / (double)ras.precision,
x2 / (double)ras.precision ));
/* During the horizontal sweep, we only take care of drop-outs */
/* e1 + <-- pixel center */
/* | */
/* x1 ---+--> <-- contour */
/* | */
/* | */
/* x2 <--+--- <-- contour */
/* | */
/* | */
/* e2 + <-- pixel center */
e1 = CEILING( x1 );
e2 = FLOOR ( x2 );
pxl = e1;
if ( e1 > e2 )
{
Int dropOutControl = left->flags & 7;
FT_TRACE7(( ", dropout=%d", dropOutControl ));
if ( e1 == e2 + ras.precision )
{
switch ( dropOutControl )
{
case 0: /* simple drop-outs including stubs */
pxl = e2;
break;
case 4: /* smart drop-outs including stubs */
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
break;
case 1: /* simple drop-outs excluding stubs */
case 5: /* smart drop-outs excluding stubs */
/* see Vertical_Sweep_Drop for details */
/* rightmost stub test */
if ( left->next == right &&
left->height <= 0 &&
!( left->flags & Overshoot_Top &&
x2 - x1 >= ras.precision_half ) )
goto Exit;
/* leftmost stub test */
if ( right->next == left &&
left->start == y &&
!( left->flags & Overshoot_Bottom &&
x2 - x1 >= ras.precision_half ) )
goto Exit;
if ( dropOutControl == 1 )
pxl = e2;
else
pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
break;
default: /* modes 2, 3, 6, 7 */
goto Exit; /* no drop-out control */
}
/* undocumented but confirmed: If the drop-out would result in a */
/* pixel outside of the bounding box, use the pixel inside of the */
/* bounding box instead */
if ( pxl < 0 )
pxl = e1;
else if ( (ULong)( TRUNC( pxl ) ) >= ras.target.rows )
pxl = e2;
/* check that the other pixel isn't set */
e1 = pxl == e1 ? e2 : e1;
e1 = TRUNC( e1 );
bits = ras.bTarget + ( y >> 3 );
f1 = (Byte)( 0x80 >> ( y & 7 ) );
bits -= e1 * ras.target.pitch;
if ( ras.target.pitch > 0 )
bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
if ( e1 >= 0 &&
(ULong)e1 < ras.target.rows &&
*bits & f1 )
goto Exit;
}
else
goto Exit;
}
e1 = TRUNC( pxl );
if ( e1 >= 0 && (ULong)e1 < ras.target.rows )
{
FT_TRACE7(( " -> y=%d (drop-out)", e1 ));
bits = ras.bTarget + ( y >> 3 );
f1 = (Byte)( 0x80 >> ( y & 7 ) );
bits -= e1 * ras.target.pitch;
if ( ras.target.pitch > 0 )
bits += (Long)( ras.target.rows - 1 ) * ras.target.pitch;
bits[0] |= f1;
}
Exit:
FT_TRACE7(( "\n" ));
}
static void
Horizontal_Sweep_Step( RAS_ARG )
{
/* Nothing, really */
FT_UNUSED_RASTER;
}
/*************************************************************************/
/* */
/* Generic Sweep Drawing routine */
/* */
/*************************************************************************/
static Bool
Draw_Sweep( RAS_ARG )
{
Short y, y_change, y_height;
PProfile P, Q, P_Left, P_Right;
Short min_Y, max_Y, top, bottom, dropouts;
Long x1, x2, xs, e1, e2;
TProfileList waiting;
TProfileList draw_left, draw_right;
/* initialize empty linked lists */
Init_Linked( &waiting );
Init_Linked( &draw_left );
Init_Linked( &draw_right );
/* first, compute min and max Y */
P = ras.fProfile;
max_Y = (Short)TRUNC( ras.minY );
min_Y = (Short)TRUNC( ras.maxY );
while ( P )
{
Q = P->link;
bottom = (Short)P->start;
top = (Short)( P->start + P->height - 1 );
if ( min_Y > bottom )
min_Y = bottom;
if ( max_Y < top )
max_Y = top;
P->X = 0;
InsNew( &waiting, P );
P = Q;
}
/* check the Y-turns */
if ( ras.numTurns == 0 )
{
ras.error = FT_THROW( Invalid );
return FAILURE;
}
/* now initialize the sweep */
ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
/* then compute the distance of each profile from min_Y */
P = waiting;
while ( P )
{
P->countL = P->start - min_Y;
P = P->link;
}
/* let's go */
y = min_Y;
y_height = 0;
if ( ras.numTurns > 0 &&
ras.sizeBuff[-ras.numTurns] == min_Y )
ras.numTurns--;
while ( ras.numTurns > 0 )
{
/* check waiting list for new activations */
P = waiting;
while ( P )
{
Q = P->link;
P->countL -= y_height;
if ( P->countL == 0 )
{
DelOld( &waiting, P );
if ( P->flags & Flow_Up )
InsNew( &draw_left, P );
else
InsNew( &draw_right, P );
}
P = Q;
}
/* sort the drawing lists */
Sort( &draw_left );
Sort( &draw_right );
y_change = (Short)ras.sizeBuff[-ras.numTurns--];
y_height = (Short)( y_change - y );
while ( y < y_change )
{
/* let's trace */
dropouts = 0;
P_Left = draw_left;
P_Right = draw_right;
while ( P_Left )
{
x1 = P_Left ->X;
x2 = P_Right->X;
if ( x1 > x2 )
{
xs = x1;
x1 = x2;
x2 = xs;
}
e1 = FLOOR( x1 );
e2 = CEILING( x2 );
if ( x2 - x1 <= ras.precision &&
e1 != x1 && e2 != x2 )
{
if ( e1 > e2 || e2 == e1 + ras.precision )
{
Int dropOutControl = P_Left->flags & 7;
if ( dropOutControl != 2 )
{
/* a drop-out was detected */
P_Left ->X = x1;
P_Right->X = x2;
/* mark profile for drop-out processing */
P_Left->countL = 1;
dropouts++;
}
goto Skip_To_Next;
}
}
ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
Skip_To_Next:
P_Left = P_Left->link;
P_Right = P_Right->link;
}
/* handle drop-outs _after_ the span drawing -- */
/* drop-out processing has been moved out of the loop */
/* for performance tuning */
if ( dropouts > 0 )
goto Scan_DropOuts;
Next_Line:
ras.Proc_Sweep_Step( RAS_VAR );
y++;
if ( y < y_change )
{
Sort( &draw_left );
Sort( &draw_right );
}
}
/* now finalize the profiles that need it */
P = draw_left;
while ( P )
{
Q = P->link;
if ( P->height == 0 )
DelOld( &draw_left, P );
P = Q;
}
P = draw_right;
while ( P )
{
Q = P->link;
if ( P->height == 0 )
DelOld( &draw_right, P );
P = Q;
}
}
/* for gray-scaling, flush the bitmap scanline cache */
while ( y <= max_Y )
{
ras.Proc_Sweep_Step( RAS_VAR );
y++;
}
return SUCCESS;
Scan_DropOuts:
P_Left = draw_left;
P_Right = draw_right;
while ( P_Left )
{
if ( P_Left->countL )
{
P_Left->countL = 0;
#if 0
dropouts--; /* -- this is useful when debugging only */
#endif
ras.Proc_Sweep_Drop( RAS_VARS y,
P_Left->X,
P_Right->X,
P_Left,
P_Right );
}
P_Left = P_Left->link;
P_Right = P_Right->link;
}
goto Next_Line;
}
/*************************************************************************/
/* */
/* <Function> */
/* Render_Single_Pass */
/* */
/* <Description> */
/* Perform one sweep with sub-banding. */
/* */
/* <Input> */
/* flipped :: If set, flip the direction of the outline. */
/* */
/* <Return> */
/* Renderer error code. */
/* */
static int
Render_Single_Pass( RAS_ARGS Bool flipped )
{
Short i, j, k;
while ( ras.band_top >= 0 )
{
ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
ras.top = ras.buff;
ras.error = Raster_Err_None;
if ( Convert_Glyph( RAS_VARS flipped ) )
{
if ( ras.error != Raster_Err_Overflow )
return FAILURE;
ras.error = Raster_Err_None;
/* sub-banding */
#ifdef DEBUG_RASTER
ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
#endif
i = ras.band_stack[ras.band_top].y_min;
j = ras.band_stack[ras.band_top].y_max;
k = (Short)( ( i + j ) / 2 );
if ( ras.band_top >= 7 || k < i )
{
ras.band_top = 0;
ras.error = FT_THROW( Invalid );
return ras.error;
}
ras.band_stack[ras.band_top + 1].y_min = k;
ras.band_stack[ras.band_top + 1].y_max = j;
ras.band_stack[ras.band_top].y_max = (Short)( k - 1 );
ras.band_top++;
}
else
{
if ( ras.fProfile )
if ( Draw_Sweep( RAS_VAR ) )
return ras.error;
ras.band_top--;
}
}
return SUCCESS;
}
/*************************************************************************/
/* */
/* <Function> */
/* Render_Glyph */
/* */
/* <Description> */
/* Render a glyph in a bitmap. Sub-banding if needed. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
static FT_Error
Render_Glyph( RAS_ARG )
{
FT_Error error;
Set_High_Precision( RAS_VARS ras.outline.flags &
FT_OUTLINE_HIGH_PRECISION );
ras.scale_shift = ras.precision_shift;
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
ras.dropOutControl = 2;
else
{
if ( ras.outline.flags & FT_OUTLINE_SMART_DROPOUTS )
ras.dropOutControl = 4;
else
ras.dropOutControl = 0;
if ( !( ras.outline.flags & FT_OUTLINE_INCLUDE_STUBS ) )
ras.dropOutControl += 1;
}
ras.second_pass = (Bool)( !( ras.outline.flags &
FT_OUTLINE_SINGLE_PASS ) );
/* Vertical Sweep */
FT_TRACE7(( "Vertical pass (ftraster)\n" ));
ras.Proc_Sweep_Init = Vertical_Sweep_Init;
ras.Proc_Sweep_Span = Vertical_Sweep_Span;
ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
ras.Proc_Sweep_Step = Vertical_Sweep_Step;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = (Short)( ras.target.rows - 1 );
ras.bWidth = (UShort)ras.target.width;
ras.bTarget = (Byte*)ras.target.buffer;
if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
return error;
/* Horizontal Sweep */
if ( ras.second_pass && ras.dropOutControl != 2 )
{
FT_TRACE7(( "Horizontal pass (ftraster)\n" ));
ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
ras.band_top = 0;
ras.band_stack[0].y_min = 0;
ras.band_stack[0].y_max = (Short)( ras.target.width - 1 );
if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
return error;
}
return Raster_Err_None;
}
static void
ft_black_init( black_PRaster raster )
{
FT_UNUSED( raster );
}
/**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
/**** a static object. *****/
#ifdef STANDALONE_
static int
ft_black_new( void* memory,
FT_Raster *araster )
{
static black_TRaster the_raster;
FT_UNUSED( memory );
*araster = (FT_Raster)&the_raster;
FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
ft_black_init( &the_raster );
return 0;
}
static void
ft_black_done( FT_Raster raster )
{
/* nothing */
FT_UNUSED( raster );
}
#else /* !STANDALONE_ */
static int
ft_black_new( FT_Memory memory,
black_PRaster *araster )
{
FT_Error error;
black_PRaster raster = NULL;
*araster = 0;
if ( !FT_NEW( raster ) )
{
raster->memory = memory;
ft_black_init( raster );
*araster = raster;
}
return error;
}
static void
ft_black_done( black_PRaster raster )
{
FT_Memory memory = (FT_Memory)raster->memory;
FT_FREE( raster );
}
#endif /* !STANDALONE_ */
static void
ft_black_reset( black_PRaster raster,
char* pool_base,
Long pool_size )
{
FT_UNUSED( raster );
FT_UNUSED( pool_base );
FT_UNUSED( pool_size );
}
static int
ft_black_set_mode( black_PRaster raster,
ULong mode,
const char* palette )
{
FT_UNUSED( raster );
FT_UNUSED( mode );
FT_UNUSED( palette );
return 0;
}
static int
ft_black_render( black_PRaster raster,
const FT_Raster_Params* params )
{
const FT_Outline* outline = (const FT_Outline*)params->source;
const FT_Bitmap* target_map = params->target;
black_TWorker worker[1];
Long buffer[FT_MAX( FT_RENDER_POOL_SIZE, 2048 ) / sizeof ( Long )];
if ( !raster )
return FT_THROW( Not_Ini );
if ( !outline )
return FT_THROW( Invalid );
/* return immediately if the outline is empty */
if ( outline->n_points == 0 || outline->n_contours <= 0 )
return Raster_Err_None;
if ( !outline->contours || !outline->points )
return FT_THROW( Invalid );
if ( outline->n_points !=
outline->contours[outline->n_contours - 1] + 1 )
return FT_THROW( Invalid );
/* this version of the raster does not support direct rendering, sorry */
if ( params->flags & FT_RASTER_FLAG_DIRECT )
return FT_THROW( Unsupported );
if ( params->flags & FT_RASTER_FLAG_AA )
return FT_THROW( Unsupported );
if ( !target_map )
return FT_THROW( Invalid );
/* nothing to do */
if ( !target_map->width || !target_map->rows )
return Raster_Err_None;
if ( !target_map->buffer )
return FT_THROW( Invalid );
ras.outline = *outline;
ras.target = *target_map;
worker->buff = buffer;
worker->sizeBuff = (&buffer)[1]; /* Points to right after buffer. */
return Render_Glyph( RAS_VAR );
}
FT_DEFINE_RASTER_FUNCS(
ft_standard_raster,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Raster_New_Func) ft_black_new,
(FT_Raster_Reset_Func) ft_black_reset,
(FT_Raster_Set_Mode_Func)ft_black_set_mode,
(FT_Raster_Render_Func) ft_black_render,
(FT_Raster_Done_Func) ft_black_done )
/* END */
src/raster/ftrend1.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftrend1.c */
/* */
/* The FreeType glyph rasterizer interface (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_OBJECTS_H
#include FT_OUTLINE_H
#include "ftrend1.h"
#include "ftraster.h"
#include "rastpic.h"
#include "rasterrs.h"
/* initialize renderer -- init its raster */
static FT_Error
ft_raster1_init( FT_Renderer render )
{
FT_Library library = FT_MODULE_LIBRARY( render );
render->clazz->raster_class->raster_reset( render->raster,
library->raster_pool,
library->raster_pool_size );
return FT_Err_Ok;
}
/* set render-specific mode */
static FT_Error
ft_raster1_set_mode( FT_Renderer render,
FT_ULong mode_tag,
FT_Pointer data )
{
/* we simply pass it to the raster */
return render->clazz->raster_class->raster_set_mode( render->raster,
mode_tag,
data );
}
/* transform a given glyph image */
static FT_Error
ft_raster1_transform( FT_Renderer render,
FT_GlyphSlot slot,
const FT_Matrix* matrix,
const FT_Vector* delta )
{
FT_Error error = FT_Err_Ok;
if ( slot->format != render->glyph_format )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
if ( matrix )
FT_Outline_Transform( &slot->outline, matrix );
if ( delta )
FT_Outline_Translate( &slot->outline, delta->x, delta->y );
Exit:
return error;
}
/* return the glyph's control box */
static void
ft_raster1_get_cbox( FT_Renderer render,
FT_GlyphSlot slot,
FT_BBox* cbox )
{
FT_MEM_ZERO( cbox, sizeof ( *cbox ) );
if ( slot->format == render->glyph_format )
FT_Outline_Get_CBox( &slot->outline, cbox );
}
/* convert a slot's glyph image into a bitmap */
static FT_Error
ft_raster1_render( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin )
{
FT_Error error;
FT_Outline* outline;
FT_BBox cbox, cbox0;
FT_UInt width, height, pitch;
FT_Bitmap* bitmap;
FT_Memory memory;
FT_Raster_Params params;
/* check glyph image format */
if ( slot->format != render->glyph_format )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
/* check rendering mode */
if ( mode != FT_RENDER_MODE_MONO )
{
/* raster1 is only capable of producing monochrome bitmaps */
return FT_THROW( Cannot_Render_Glyph );
}
outline = &slot->outline;
/* translate the outline to the new origin if needed */
if ( origin )
FT_Outline_Translate( outline, origin->x, origin->y );
/* compute the control box, and grid fit it */
FT_Outline_Get_CBox( outline, &cbox0 );
/* undocumented but confirmed: bbox values get rounded */
#if 1
cbox.xMin = FT_PIX_ROUND( cbox0.xMin );
cbox.yMin = FT_PIX_ROUND( cbox0.yMin );
cbox.xMax = FT_PIX_ROUND( cbox0.xMax );
cbox.yMax = FT_PIX_ROUND( cbox0.yMax );
#else
cbox.xMin = FT_PIX_FLOOR( cbox.xMin );
cbox.yMin = FT_PIX_FLOOR( cbox.yMin );
cbox.xMax = FT_PIX_CEIL( cbox.xMax );
cbox.yMax = FT_PIX_CEIL( cbox.yMax );
#endif
/* If either `width' or `height' round to 0, try */
/* explicitly rounding up/down. In the case of */
/* glyphs containing only one very narrow feature, */
/* this gives the drop-out compensation in the scan */
/* conversion code a chance to do its stuff. */
width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
if ( width == 0 )
{
cbox.xMin = FT_PIX_FLOOR( cbox0.xMin );
cbox.xMax = FT_PIX_CEIL( cbox0.xMax );
width = (FT_UInt)( ( cbox.xMax - cbox.xMin ) >> 6 );
}
height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
if ( height == 0 )
{
cbox.yMin = FT_PIX_FLOOR( cbox0.yMin );
cbox.yMax = FT_PIX_CEIL( cbox0.yMax );
height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
}
if ( width > FT_USHORT_MAX || height > FT_USHORT_MAX )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
bitmap = &slot->bitmap;
memory = render->root.memory;
/* release old bitmap buffer */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
pitch = ( ( width + 15 ) >> 4 ) << 1;
bitmap->pixel_mode = FT_PIXEL_MODE_MONO;
bitmap->width = width;
bitmap->rows = height;
bitmap->pitch = (int)pitch;
if ( FT_ALLOC_MULT( bitmap->buffer, pitch, height ) )
goto Exit;
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
/* translate outline to render it into the bitmap */
FT_Outline_Translate( outline, -cbox.xMin, -cbox.yMin );
/* set up parameters */
params.target = bitmap;
params.source = outline;
params.flags = 0;
/* render outline into the bitmap */
error = render->raster_render( render->raster, ¶ms );
FT_Outline_Translate( outline, cbox.xMin, cbox.yMin );
if ( error )
goto Exit;
slot->format = FT_GLYPH_FORMAT_BITMAP;
slot->bitmap_left = (FT_Int)( cbox.xMin >> 6 );
slot->bitmap_top = (FT_Int)( cbox.yMax >> 6 );
Exit:
return error;
}
FT_DEFINE_RENDERER( ft_raster1_renderer_class,
FT_MODULE_RENDERER,
sizeof ( FT_RendererRec ),
"raster1",
0x10000L,
0x20000L,
0, /* module specific interface */
(FT_Module_Constructor)ft_raster1_init,
(FT_Module_Destructor) 0,
(FT_Module_Requester) 0
,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Renderer_RenderFunc) ft_raster1_render,
(FT_Renderer_TransformFunc)ft_raster1_transform,
(FT_Renderer_GetCBoxFunc) ft_raster1_get_cbox,
(FT_Renderer_SetModeFunc) ft_raster1_set_mode,
(FT_Raster_Funcs*) &FT_STANDARD_RASTER_GET
)
/* END */
src/smooth/ftgrays.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftgrays.c */
/* */
/* A new `perfect' anti-aliasing renderer (body). */
/* */
/* Copyright 2000-2016 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 can be compiled without the rest of the FreeType engine, by */
/* defining the STANDALONE_ macro when compiling it. You also need to */
/* put the files `ftgrays.h' and `ftimage.h' into the current */
/* compilation directory. Typically, you could do something like */
/* */
/* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
/* */
/* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
/* same directory */
/* */
/* - compile `ftgrays' with the STANDALONE_ macro defined, as in */
/* */
/* cc -c -DSTANDALONE_ ftgrays.c */
/* */
/* The renderer can be initialized with a call to */
/* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
/* with a call to `ft_gray_raster.raster_render'. */
/* */
/* See the comments and documentation in the file `ftimage.h' for more */
/* details on how the raster works. */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* This is a new anti-aliasing scan-converter for FreeType 2. The */
/* algorithm used here is _very_ different from the one in the standard */
/* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
/* coverage of the outline on each pixel cell. */
/* */
/* It is based on ideas that I initially found in Raph Levien's */
/* excellent LibArt graphics library (see http://www.levien.com/libart */
/* for more information, though the web pages do not tell anything */
/* about the renderer; you'll have to dive into the source code to */
/* understand how it works). */
/* */
/* Note, however, that this is a _very_ different implementation */
/* compared to Raph's. Coverage information is stored in a very */
/* different way, and I don't use sorted vector paths. Also, it doesn't */
/* use floating point values. */
/* */
/* This renderer has the following advantages: */
/* */
/* - It doesn't need an intermediate bitmap. Instead, one can supply a */
/* callback function that will be called by the renderer to draw gray */
/* spans on any target surface. You can thus do direct composition on */
/* any kind of bitmap, provided that you give the renderer the right */
/* callback. */
/* */
/* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
/* each pixel cell. */
/* */
/* - It performs a single pass on the outline (the `standard' FT2 */
/* renderer makes two passes). */
/* */
/* - It can easily be modified to render to _any_ number of gray levels */
/* cheaply. */
/* */
/* - For small (< 20) pixel sizes, it is faster than the standard */
/* renderer. */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* 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_smooth
#ifdef STANDALONE_
/* The size in bytes of the render pool used by the scan-line converter */
/* to do all of its work. */
#define FT_RENDER_POOL_SIZE 16384L
/* Auxiliary macros for token concatenation. */
#define FT_ERR_XCAT( x, y ) x ## y
#define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y )
#define FT_BEGIN_STMNT do {
#define FT_END_STMNT } while ( 0 )
#define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) )
#define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) )
/*
* Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
* algorithm. We use alpha = 1, beta = 3/8, giving us results with a
* largest error less than 7% compared to the exact value.
*/
#define FT_HYPOT( x, y ) \
( x = FT_ABS( x ), \
y = FT_ABS( y ), \
x > y ? x + ( 3 * y >> 3 ) \
: y + ( 3 * x >> 3 ) )
/* define this to dump debugging information */
/* #define FT_DEBUG_LEVEL_TRACE */
#ifdef FT_DEBUG_LEVEL_TRACE
#include <stdio.h>
#include <stdarg.h>
#endif
#include <stddef.h>
#include <string.h>
#include <setjmp.h>
#include <limits.h>
#define FT_CHAR_BIT CHAR_BIT
#define FT_UINT_MAX UINT_MAX
#define FT_INT_MAX INT_MAX
#define FT_ULONG_MAX ULONG_MAX
#define ft_memset memset
#define ft_setjmp setjmp
#define ft_longjmp longjmp
#define ft_jmp_buf jmp_buf
typedef ptrdiff_t FT_PtrDist;
#define ErrRaster_Invalid_Mode -2
#define ErrRaster_Invalid_Outline -1
#define ErrRaster_Invalid_Argument -3
#define ErrRaster_Memory_Overflow -4
#define FT_BEGIN_HEADER
#define FT_END_HEADER
#include "ftimage.h"
#include "ftgrays.h"
/* This macro is used to indicate that a function parameter is unused. */
/* Its purpose is simply to reduce compiler warnings. Note also that */
/* simply defining it as `(void)x' doesn't avoid warnings with certain */
/* ANSI compilers (e.g. LCC). */
#define FT_UNUSED( x ) (x) = (x)
/* we only use level 5 & 7 tracing messages; cf. ftdebug.h */
#ifdef FT_DEBUG_LEVEL_TRACE
void
FT_Message( const char* fmt,
... )
{
va_list ap;
va_start( ap, fmt );
vfprintf( stderr, fmt, ap );
va_end( ap );
}
/* empty function useful for setting a breakpoint to catch errors */
int
FT_Throw( int error,
int line,
const char* file )
{
FT_UNUSED( error );
FT_UNUSED( line );
FT_UNUSED( file );
return 0;
}
/* we don't handle tracing levels in stand-alone mode; */
#ifndef FT_TRACE5
#define FT_TRACE5( varformat ) FT_Message varformat
#endif
#ifndef FT_TRACE7
#define FT_TRACE7( varformat ) FT_Message varformat
#endif
#ifndef FT_ERROR
#define FT_ERROR( varformat ) FT_Message varformat
#endif
#define FT_THROW( e ) \
( FT_Throw( FT_ERR_CAT( ErrRaster, e ), \
__LINE__, \
__FILE__ ) | \
FT_ERR_CAT( ErrRaster, e ) )
#else /* !FT_DEBUG_LEVEL_TRACE */
#define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */
#define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */
#define FT_ERROR( x ) do { } while ( 0 ) /* nothing */
#define FT_THROW( e ) FT_ERR_CAT( ErrRaster_, e )
#endif /* !FT_DEBUG_LEVEL_TRACE */
#define FT_DEFINE_OUTLINE_FUNCS( class_, \
move_to_, line_to_, \
conic_to_, cubic_to_, \
shift_, delta_ ) \
static const FT_Outline_Funcs class_ = \
{ \
move_to_, \
line_to_, \
conic_to_, \
cubic_to_, \
shift_, \
delta_ \
};
#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, \
raster_new_, raster_reset_, \
raster_set_mode_, raster_render_, \
raster_done_ ) \
const FT_Raster_Funcs class_ = \
{ \
glyph_format_, \
raster_new_, \
raster_reset_, \
raster_set_mode_, \
raster_render_, \
raster_done_ \
};
#else /* !STANDALONE_ */
#include <ft2build.h>
#include "ftgrays.h"
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_OUTLINE_H
#include "ftsmerrs.h"
#include "ftspic.h"
#define Smooth_Err_Invalid_Mode Smooth_Err_Cannot_Render_Glyph
#define Smooth_Err_Memory_Overflow Smooth_Err_Out_Of_Memory
#define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory
#endif /* !STANDALONE_ */
#ifndef FT_MEM_SET
#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
#endif
#ifndef FT_MEM_ZERO
#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
#endif
/* as usual, for the speed hungry :-) */
#undef RAS_ARG
#undef RAS_ARG_
#undef RAS_VAR
#undef RAS_VAR_
#ifndef FT_STATIC_RASTER
#define RAS_ARG gray_PWorker worker
#define RAS_ARG_ gray_PWorker worker,
#define RAS_VAR worker
#define RAS_VAR_ worker,
#else /* FT_STATIC_RASTER */
#define RAS_ARG /* empty */
#define RAS_ARG_ /* empty */
#define RAS_VAR /* empty */
#define RAS_VAR_ /* empty */
#endif /* FT_STATIC_RASTER */
/* must be at least 6 bits! */
#define PIXEL_BITS 8
#undef FLOOR
#undef CEILING
#undef TRUNC
#undef SCALED
#define ONE_PIXEL ( 1L << PIXEL_BITS )
#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )
#define SUBPIXELS( x ) ( (TPos)(x) * ONE_PIXEL )
#define FLOOR( x ) ( (x) & -ONE_PIXEL )
#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
#if PIXEL_BITS >= 6
#define UPSCALE( x ) ( (x) * ( ONE_PIXEL >> 6 ) )
#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
#else
#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
#define DOWNSCALE( x ) ( (x) * ( 64 >> PIXEL_BITS ) )
#endif
/* Compute `dividend / divisor' and return both its quotient and */
/* remainder, cast to a specific type. This macro also ensures that */
/* the remainder is always positive. */
#define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \
FT_BEGIN_STMNT \
(quotient) = (type)( (dividend) / (divisor) ); \
(remainder) = (type)( (dividend) % (divisor) ); \
if ( (remainder) < 0 ) \
{ \
(quotient)--; \
(remainder) += (type)(divisor); \
} \
FT_END_STMNT
#ifdef __arm__
/* Work around a bug specific to GCC which make the compiler fail to */
/* optimize a division and modulo operation on the same parameters */
/* into a single call to `__aeabi_idivmod'. See */
/* */
/* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43721 */
#undef FT_DIV_MOD
#define FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) \
FT_BEGIN_STMNT \
(quotient) = (type)( (dividend) / (divisor) ); \
(remainder) = (type)( (dividend) - (quotient) * (divisor) ); \
if ( (remainder) < 0 ) \
{ \
(quotient)--; \
(remainder) += (type)(divisor); \
} \
FT_END_STMNT
#endif /* __arm__ */
/* These macros speed up repetitive divisions by replacing them */
/* with multiplications and right shifts. */
#define FT_UDIVPREP( b ) \
long b ## _r = (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b )
#define FT_UDIV( a, b ) \
( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \
( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) )
/*************************************************************************/
/* */
/* TYPE DEFINITIONS */
/* */
/* don't change the following types to FT_Int or FT_Pos, since we might */
/* need to define them to "float" or "double" when experimenting with */
/* new algorithms */
typedef long TCoord; /* integer scanline/pixel coordinate */
typedef long TPos; /* sub-pixel coordinate */
typedef long TArea; /* cell areas, coordinate products */
/* maximum number of gray spans in a call to the span callback */
#define FT_MAX_GRAY_SPANS 32
typedef struct TCell_* PCell;
typedef struct TCell_
{
TPos x; /* same with gray_TWorker.ex */
TCoord cover; /* same with gray_TWorker.cover */
TArea area;
PCell next;
} TCell;
#if defined( _MSC_VER ) /* Visual C++ (and Intel C++) */
/* We disable the warning `structure was padded due to */
/* __declspec(align())' in order to compile cleanly with */
/* the maximum level of warnings. */
#pragma warning( push )
#pragma warning( disable : 4324 )
#endif /* _MSC_VER */
typedef struct gray_TWorker_
{
ft_jmp_buf jump_buffer;
TCoord ex, ey;
TPos min_ex, max_ex;
TPos min_ey, max_ey;
TPos count_ex, count_ey;
TArea area;
TCoord cover;
int invalid;
PCell cells;
FT_PtrDist max_cells;
FT_PtrDist num_cells;
TPos x, y;
FT_Vector bez_stack[32 * 3 + 1];
int lev_stack[32];
FT_Outline outline;
FT_Bitmap target;
FT_BBox clip_box;
FT_Span gray_spans[FT_MAX_GRAY_SPANS];
int num_gray_spans;
FT_Raster_Span_Func render_span;
void* render_span_data;
int span_y;
int band_size;
int band_shoot;
void* buffer;
long buffer_size;
PCell* ycells;
TPos ycount;
} gray_TWorker, *gray_PWorker;
#if defined( _MSC_VER )
#pragma warning( pop )
#endif
#ifndef FT_STATIC_RASTER
#define ras (*worker)
#else
static gray_TWorker ras;
#endif
typedef struct gray_TRaster_
{
void* memory;
} gray_TRaster, *gray_PRaster;
/*************************************************************************/
/* */
/* Initialize the cells table. */
/* */
static void
gray_init_cells( RAS_ARG_ void* buffer,
long byte_size )
{
ras.buffer = buffer;
ras.buffer_size = byte_size;
ras.ycells = (PCell*) buffer;
ras.cells = NULL;
ras.max_cells = 0;
ras.num_cells = 0;
ras.area = 0;
ras.cover = 0;
ras.invalid = 1;
}
/*************************************************************************/
/* */
/* Compute the outline bounding box. */
/* */
static void
gray_compute_cbox( RAS_ARG )
{
FT_Outline* outline = &ras.outline;
FT_Vector* vec = outline->points;
FT_Vector* limit = vec + outline->n_points;
if ( outline->n_points <= 0 )
{
ras.min_ex = ras.max_ex = 0;
ras.min_ey = ras.max_ey = 0;
return;
}
ras.min_ex = ras.max_ex = vec->x;
ras.min_ey = ras.max_ey = vec->y;
vec++;
for ( ; vec < limit; vec++ )
{
TPos x = vec->x;
TPos y = vec->y;
if ( x < ras.min_ex ) ras.min_ex = x;
if ( x > ras.max_ex ) ras.max_ex = x;
if ( y < ras.min_ey ) ras.min_ey = y;
if ( y > ras.max_ey ) ras.max_ey = y;
}
/* truncate the bounding box to integer pixels */
ras.min_ex = ras.min_ex >> 6;
ras.min_ey = ras.min_ey >> 6;
ras.max_ex = ( ras.max_ex + 63 ) >> 6;
ras.max_ey = ( ras.max_ey + 63 ) >> 6;
}
/*************************************************************************/
/* */
/* Record the current cell in the table. */
/* */
static PCell
gray_find_cell( RAS_ARG )
{
PCell *pcell, cell;
TPos x = ras.ex;
if ( x > ras.count_ex )
x = ras.count_ex;
pcell = &ras.ycells[ras.ey];
for (;;)
{
cell = *pcell;
if ( cell == NULL || cell->x > x )
break;
if ( cell->x == x )
goto Exit;
pcell = &cell->next;
}
if ( ras.num_cells >= ras.max_cells )
ft_longjmp( ras.jump_buffer, 1 );
cell = ras.cells + ras.num_cells++;
cell->x = x;
cell->area = 0;
cell->cover = 0;
cell->next = *pcell;
*pcell = cell;
Exit:
return cell;
}
static void
gray_record_cell( RAS_ARG )
{
if ( ras.area | ras.cover )
{
PCell cell = gray_find_cell( RAS_VAR );
cell->area += ras.area;
cell->cover += ras.cover;
}
}
/*************************************************************************/
/* */
/* Set the current cell to a new position. */
/* */
static void
gray_set_cell( RAS_ARG_ TCoord ex,
TCoord ey )
{
/* Move the cell pointer to a new position. We set the `invalid' */
/* flag to indicate that the cell isn't part of those we're interested */
/* in during the render phase. This means that: */
/* */
/* . the new vertical position must be within min_ey..max_ey-1. */
/* . the new horizontal position must be strictly less than max_ex */
/* */
/* Note that if a cell is to the left of the clipping region, it is */
/* actually set to the (min_ex-1) horizontal position. */
/* All cells that are on the left of the clipping region go to the */
/* min_ex - 1 horizontal position. */
ey -= ras.min_ey;
if ( ex > ras.max_ex )
ex = ras.max_ex;
ex -= ras.min_ex;
if ( ex < 0 )
ex = -1;
/* are we moving to a different cell ? */
if ( ex != ras.ex || ey != ras.ey )
{
/* record the current one if it is valid */
if ( !ras.invalid )
gray_record_cell( RAS_VAR );
ras.area = 0;
ras.cover = 0;
ras.ex = ex;
ras.ey = ey;
}
ras.invalid = ( (unsigned int)ey >= (unsigned int)ras.count_ey ||
ex >= ras.count_ex );
}
/*************************************************************************/
/* */
/* Start a new contour at a given cell. */
/* */
static void
gray_start_cell( RAS_ARG_ TCoord ex,
TCoord ey )
{
if ( ex > ras.max_ex )
ex = (TCoord)( ras.max_ex );
if ( ex < ras.min_ex )
ex = (TCoord)( ras.min_ex - 1 );
ras.area = 0;
ras.cover = 0;
ras.ex = ex - ras.min_ex;
ras.ey = ey - ras.min_ey;
ras.invalid = 0;
gray_set_cell( RAS_VAR_ ex, ey );
}
#if 0
/*************************************************************************/
/* */
/* Render a scanline as one or more cells. */
/* */
static void
gray_render_scanline( RAS_ARG_ TCoord ey,
TPos x1,
TCoord y1,
TPos x2,
TCoord y2 )
{
TCoord ex1, ex2, fx1, fx2, delta, mod;
long p, first, dx;
int incr;
dx = x2 - x1;
ex1 = TRUNC( x1 );
ex2 = TRUNC( x2 );
fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
/* trivial case. Happens often */
if ( y1 == y2 )
{
gray_set_cell( RAS_VAR_ ex2, ey );
return;
}
/* everything is located in a single cell. That is easy! */
/* */
if ( ex1 == ex2 )
{
delta = y2 - y1;
ras.area += (TArea)(( fx1 + fx2 ) * delta);
ras.cover += delta;
return;
}
/* ok, we'll have to render a run of adjacent cells on the same */
/* scanline... */
/* */
p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
first = ONE_PIXEL;
incr = 1;
if ( dx < 0 )
{
p = fx1 * ( y2 - y1 );
first = 0;
incr = -1;
dx = -dx;
}
FT_DIV_MOD( TCoord, p, dx, delta, mod );
ras.area += (TArea)(( fx1 + first ) * delta);
ras.cover += delta;
ex1 += incr;
gray_set_cell( RAS_VAR_ ex1, ey );
y1 += delta;
if ( ex1 != ex2 )
{
TCoord lift, rem;
p = ONE_PIXEL * ( y2 - y1 + delta );
FT_DIV_MOD( TCoord, p, dx, lift, rem );
mod -= (int)dx;
do
{
delta = lift;
mod += rem;
if ( mod >= 0 )
{
mod -= (TCoord)dx;
delta++;
}
ras.area += (TArea)(ONE_PIXEL * delta);
ras.cover += delta;
y1 += delta;
ex1 += incr;
gray_set_cell( RAS_VAR_ ex1, ey );
} while ( ex1 != ex2 );
}
delta = y2 - y1;
ras.area += (TArea)(( fx2 + ONE_PIXEL - first ) * delta);
ras.cover += delta;
}
/*************************************************************************/
/* */
/* Render a given line as a series of scanlines. */
/* */
static void
gray_render_line( RAS_ARG_ TPos to_x,
TPos to_y )
{
TCoord ey1, ey2, fy1, fy2, mod;
TPos dx, dy, x, x2;
long p, first;
int delta, rem, lift, incr;
ey1 = TRUNC( ras.y );
ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
fy1 = (TCoord)( ras.y - SUBPIXELS( ey1 ) );
fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
dx = to_x - ras.x;
dy = to_y - ras.y;
/* perform vertical clipping */
if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
( ey1 < ras.min_ey && ey2 < ras.min_ey ) )
goto End;
/* everything is on a single scanline */
if ( ey1 == ey2 )
{
gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
goto End;
}
/* vertical line - avoid calling gray_render_scanline */
incr = 1;
if ( dx == 0 )
{
TCoord ex = TRUNC( ras.x );
TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );
TArea area;
first = ONE_PIXEL;
if ( dy < 0 )
{
first = 0;
incr = -1;
}
delta = (int)( first - fy1 );
ras.area += (TArea)two_fx * delta;
ras.cover += delta;
ey1 += incr;
gray_set_cell( RAS_VAR_ ex, ey1 );
delta = (int)( first + first - ONE_PIXEL );
area = (TArea)two_fx * delta;
while ( ey1 != ey2 )
{
ras.area += area;
ras.cover += delta;
ey1 += incr;
gray_set_cell( RAS_VAR_ ex, ey1 );
}
delta = (int)( fy2 - ONE_PIXEL + first );
ras.area += (TArea)two_fx * delta;
ras.cover += delta;
goto End;
}
/* ok, we have to render several scanlines */
p = ( ONE_PIXEL - fy1 ) * dx;
first = ONE_PIXEL;
incr = 1;
if ( dy < 0 )
{
p = fy1 * dx;
first = 0;
incr = -1;
dy = -dy;
}
FT_DIV_MOD( int, p, dy, delta, mod );
x = ras.x + delta;
gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );
ey1 += incr;
gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
if ( ey1 != ey2 )
{
p = ONE_PIXEL * dx;
FT_DIV_MOD( int, p, dy, lift, rem );
mod -= (int)dy;
do
{
delta = lift;
mod += rem;
if ( mod >= 0 )
{
mod -= (int)dy;
delta++;
}
x2 = x + delta;
gray_render_scanline( RAS_VAR_ ey1, x,
(TCoord)( ONE_PIXEL - first ), x2,
(TCoord)first );
x = x2;
ey1 += incr;
gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
} while ( ey1 != ey2 );
}
gray_render_scanline( RAS_VAR_ ey1, x,
(TCoord)( ONE_PIXEL - first ), to_x,
fy2 );
End:
ras.x = to_x;
ras.y = to_y;
}
#else
/*************************************************************************/
/* */
/* Render a straight line across multiple cells in any direction. */
/* */
static void
gray_render_line( RAS_ARG_ TPos to_x,
TPos to_y )
{
TPos dx, dy, fx1, fy1, fx2, fy2;
TCoord ex1, ex2, ey1, ey2;
ex1 = TRUNC( ras.x );
ex2 = TRUNC( to_x );
ey1 = TRUNC( ras.y );
ey2 = TRUNC( to_y );
/* perform vertical clipping */
if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
( ey1 < ras.min_ey && ey2 < ras.min_ey ) )
goto End;
dx = to_x - ras.x;
dy = to_y - ras.y;
fx1 = ras.x - SUBPIXELS( ex1 );
fy1 = ras.y - SUBPIXELS( ey1 );
if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */
;
else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */
{
ex1 = ex2;
gray_set_cell( RAS_VAR_ ex1, ey1 );
}
else if ( dx == 0 )
{
if ( dy > 0 ) /* vertical line up */
do
{
fy2 = ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * fx1 * 2;
fy1 = 0;
ey1++;
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ey1 != ey2 );
else /* vertical line down */
do
{
fy2 = 0;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * fx1 * 2;
fy1 = ONE_PIXEL;
ey1--;
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ey1 != ey2 );
}
else /* any other line */
{
TArea prod = dx * fy1 - dy * fx1;
FT_UDIVPREP( dx );
FT_UDIVPREP( dy );
/* The fundamental value `prod' determines which side and the */
/* exact coordinate where the line exits current cell. It is */
/* also easily updated when moving from one cell to the next. */
do
{
if ( prod <= 0 &&
prod - dx * ONE_PIXEL > 0 ) /* left */
{
fx2 = 0;
fy2 = (TPos)FT_UDIV( -prod, -dx );
prod -= dy * ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = ONE_PIXEL;
fy1 = fy2;
ex1--;
}
else if ( prod - dx * ONE_PIXEL <= 0 &&
prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */
{
prod -= dx * ONE_PIXEL;
fx2 = (TPos)FT_UDIV( -prod, dy );
fy2 = ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = fx2;
fy1 = 0;
ey1++;
}
else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 &&
prod + dy * ONE_PIXEL >= 0 ) /* right */
{
prod += dy * ONE_PIXEL;
fx2 = ONE_PIXEL;
fy2 = (TPos)FT_UDIV( prod, dx );
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = 0;
fy1 = fy2;
ex1++;
}
else /* ( prod + dy * ONE_PIXEL < 0 &&
prod > 0 ) down */
{
fx2 = (TPos)FT_UDIV( prod, -dy );
fy2 = 0;
prod += dx * ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = fx2;
fy1 = ONE_PIXEL;
ey1--;
}
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ex1 != ex2 || ey1 != ey2 );
}
fx2 = to_x - SUBPIXELS( ex2 );
fy2 = to_y - SUBPIXELS( ey2 );
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
End:
ras.x = to_x;
ras.y = to_y;
}
#endif
static void
gray_split_conic( FT_Vector* base )
{
TPos a, b;
base[4].x = base[2].x;
b = base[1].x;
a = base[3].x = ( base[2].x + b ) / 2;
b = base[1].x = ( base[0].x + b ) / 2;
base[2].x = ( a + b ) / 2;
base[4].y = base[2].y;
b = base[1].y;
a = base[3].y = ( base[2].y + b ) / 2;
b = base[1].y = ( base[0].y + b ) / 2;
base[2].y = ( a + b ) / 2;
}
static void
gray_render_conic( RAS_ARG_ const FT_Vector* control,
const FT_Vector* to )
{
TPos dx, dy;
TPos min, max, y;
int top, level;
int* levels;
FT_Vector* arc;
levels = ras.lev_stack;
arc = ras.bez_stack;
arc[0].x = UPSCALE( to->x );
arc[0].y = UPSCALE( to->y );
arc[1].x = UPSCALE( control->x );
arc[1].y = UPSCALE( control->y );
arc[2].x = ras.x;
arc[2].y = ras.y;
top = 0;
dx = FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x );
dy = FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y );
if ( dx < dy )
dx = dy;
if ( dx < ONE_PIXEL / 4 )
goto Draw;
/* short-cut the arc that crosses the current band */
min = max = arc[0].y;
y = arc[1].y;
if ( y < min ) min = y;
if ( y > max ) max = y;
y = arc[2].y;
if ( y < min ) min = y;
if ( y > max ) max = y;
if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
goto Draw;
level = 0;
do
{
dx >>= 2;
level++;
} while ( dx > ONE_PIXEL / 4 );
levels[0] = level;
do
{
level = levels[top];
if ( level > 0 )
{
gray_split_conic( arc );
arc += 2;
top++;
levels[top] = levels[top - 1] = level - 1;
continue;
}
Draw:
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
top--;
arc -= 2;
} while ( top >= 0 );
}
static void
gray_split_cubic( FT_Vector* base )
{
TPos a, b, c, d;
base[6].x = base[3].x;
c = base[1].x;
d = base[2].x;
base[1].x = a = ( base[0].x + c ) / 2;
base[5].x = b = ( base[3].x + d ) / 2;
c = ( c + d ) / 2;
base[2].x = a = ( a + c ) / 2;
base[4].x = b = ( b + c ) / 2;
base[3].x = ( a + b ) / 2;
base[6].y = base[3].y;
c = base[1].y;
d = base[2].y;
base[1].y = a = ( base[0].y + c ) / 2;
base[5].y = b = ( base[3].y + d ) / 2;
c = ( c + d ) / 2;
base[2].y = a = ( a + c ) / 2;
base[4].y = b = ( b + c ) / 2;
base[3].y = ( a + b ) / 2;
}
static void
gray_render_cubic( RAS_ARG_ const FT_Vector* control1,
const FT_Vector* control2,
const FT_Vector* to )
{
FT_Vector* arc;
TPos min, max, y;
arc = ras.bez_stack;
arc[0].x = UPSCALE( to->x );
arc[0].y = UPSCALE( to->y );
arc[1].x = UPSCALE( control2->x );
arc[1].y = UPSCALE( control2->y );
arc[2].x = UPSCALE( control1->x );
arc[2].y = UPSCALE( control1->y );
arc[3].x = ras.x;
arc[3].y = ras.y;
/* Short-cut the arc that crosses the current band. */
min = max = arc[0].y;
y = arc[1].y;
if ( y < min )
min = y;
if ( y > max )
max = y;
y = arc[2].y;
if ( y < min )
min = y;
if ( y > max )
max = y;
y = arc[3].y;
if ( y < min )
min = y;
if ( y > max )
max = y;
if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
goto Draw;
for (;;)
{
/* Decide whether to split or draw. See `Rapid Termination */
/* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
/* F. Hain, at */
/* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
{
TPos dx, dy, dx_, dy_;
TPos dx1, dy1, dx2, dy2;
TPos L, s, s_limit;
/* dx and dy are x and y components of the P0-P3 chord vector. */
dx = dx_ = arc[3].x - arc[0].x;
dy = dy_ = arc[3].y - arc[0].y;
L = FT_HYPOT( dx_, dy_ );
/* Avoid possible arithmetic overflow below by splitting. */
if ( L > 32767 )
goto Split;
/* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
s_limit = L * (TPos)( ONE_PIXEL / 6 );
/* s is L * the perpendicular distance from P1 to the line P0-P3. */
dx1 = arc[1].x - arc[0].x;
dy1 = arc[1].y - arc[0].y;
s = FT_ABS( dy * dx1 - dx * dy1 );
if ( s > s_limit )
goto Split;
/* s is L * the perpendicular distance from P2 to the line P0-P3. */
dx2 = arc[2].x - arc[0].x;
dy2 = arc[2].y - arc[0].y;
s = FT_ABS( dy * dx2 - dx * dy2 );
if ( s > s_limit )
goto Split;
/* Split super curvy segments where the off points are so far
from the chord that the angles P0-P1-P3 or P0-P2-P3 become
acute as detected by appropriate dot products. */
if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 ||
dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 )
goto Split;
/* No reason to split. */
goto Draw;
}
Split:
gray_split_cubic( arc );
arc += 3;
continue;
Draw:
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
if ( arc == ras.bez_stack )
return;
arc -= 3;
}
}
static int
gray_move_to( const FT_Vector* to,
gray_PWorker worker )
{
TPos x, y;
/* record current cell, if any */
if ( !ras.invalid )
gray_record_cell( RAS_VAR );
/* start to a new position */
x = UPSCALE( to->x );
y = UPSCALE( to->y );
gray_start_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) );
worker->x = x;
worker->y = y;
return 0;
}
static int
gray_line_to( const FT_Vector* to,
gray_PWorker worker )
{
gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
return 0;
}
static int
gray_conic_to( const FT_Vector* control,
const FT_Vector* to,
gray_PWorker worker )
{
gray_render_conic( RAS_VAR_ control, to );
return 0;
}
static int
gray_cubic_to( const FT_Vector* control1,
const FT_Vector* control2,
const FT_Vector* to,
gray_PWorker worker )
{
gray_render_cubic( RAS_VAR_ control1, control2, to );
return 0;
}
static void
gray_render_span( int y,
int count,
const FT_Span* spans,
gray_PWorker worker )
{
unsigned char* p;
FT_Bitmap* map = &worker->target;
/* first of all, compute the scanline offset */
p = (unsigned char*)map->buffer - y * map->pitch;
if ( map->pitch >= 0 )
p += ( map->rows - 1 ) * (unsigned int)map->pitch;
for ( ; count > 0; count--, spans++ )
{
unsigned char coverage = spans->coverage;
if ( coverage )
{
/* For small-spans it is faster to do it by ourselves than
* calling `memset'. This is mainly due to the cost of the
* function call.
*/
if ( spans->len >= 8 )
FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );
else
{
unsigned char* q = p + spans->x;
switch ( spans->len )
{
case 7: *q++ = (unsigned char)coverage;
case 6: *q++ = (unsigned char)coverage;
case 5: *q++ = (unsigned char)coverage;
case 4: *q++ = (unsigned char)coverage;
case 3: *q++ = (unsigned char)coverage;
case 2: *q++ = (unsigned char)coverage;
case 1: *q = (unsigned char)coverage;
default:
;
}
}
}
}
}
static void
gray_hline( RAS_ARG_ TCoord x,
TCoord y,
TPos area,
TCoord acount )
{
int coverage;
/* compute the coverage line's coverage, depending on the */
/* outline fill rule */
/* */
/* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
/* */
coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
/* use range 0..256 */
if ( coverage < 0 )
coverage = -coverage;
if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL )
{
coverage &= 511;
if ( coverage > 256 )
coverage = 512 - coverage;
else if ( coverage == 256 )
coverage = 255;
}
else
{
/* normal non-zero winding rule */
if ( coverage >= 256 )
coverage = 255;
}
y += (TCoord)ras.min_ey;
x += (TCoord)ras.min_ex;
/* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
if ( x >= 32767 )
x = 32767;
/* FT_Span.y is an integer, so limit our coordinates appropriately */
if ( y >= FT_INT_MAX )
y = FT_INT_MAX;
if ( coverage )
{
FT_Span* span;
int count;
/* see whether we can add this span to the current list */
count = ras.num_gray_spans;
span = ras.gray_spans + count - 1;
if ( count > 0 &&
ras.span_y == y &&
(int)span->x + span->len == (int)x &&
span->coverage == coverage )
{
span->len = (unsigned short)( span->len + acount );
return;
}
if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )
{
if ( ras.render_span && count > 0 )
ras.render_span( ras.span_y, count, ras.gray_spans,
ras.render_span_data );
#ifdef FT_DEBUG_LEVEL_TRACE
if ( count > 0 )
{
int n;
FT_TRACE7(( "y = %3d ", ras.span_y ));
span = ras.gray_spans;
for ( n = 0; n < count; n++, span++ )
FT_TRACE7(( "[%d..%d]:%02x ",
span->x, span->x + span->len - 1, span->coverage ));
FT_TRACE7(( "\n" ));
}
#endif /* FT_DEBUG_LEVEL_TRACE */
ras.num_gray_spans = 0;
ras.span_y = (int)y;
span = ras.gray_spans;
}
else
span++;
/* add a gray span to the current list */
span->x = (short)x;
span->len = (unsigned short)acount;
span->coverage = (unsigned char)coverage;
ras.num_gray_spans++;
}
}
#ifdef FT_DEBUG_LEVEL_TRACE
/* to be called while in the debugger -- */
/* this function causes a compiler warning since it is unused otherwise */
static void
gray_dump_cells( RAS_ARG )
{
int yindex;
for ( yindex = 0; yindex < ras.ycount; yindex++ )
{
PCell cell;
printf( "%3d:", yindex );
for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
printf( " (%3ld, c:%4ld, a:%6ld)",
cell->x, cell->cover, cell->area );
printf( "\n" );
}
}
#endif /* FT_DEBUG_LEVEL_TRACE */
static void
gray_sweep( RAS_ARG_ const FT_Bitmap* target )
{
int yindex;
FT_UNUSED( target );
if ( ras.num_cells == 0 )
return;
ras.num_gray_spans = 0;
FT_TRACE7(( "gray_sweep: start\n" ));
for ( yindex = 0; yindex < ras.ycount; yindex++ )
{
PCell cell = ras.ycells[yindex];
TCoord cover = 0;
TCoord x = 0;
for ( ; cell != NULL; cell = cell->next )
{
TPos area;
if ( cell->x > x && cover != 0 )
gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
cell->x - x );
cover += cell->cover;
area = cover * ( ONE_PIXEL * 2 ) - cell->area;
if ( area != 0 && cell->x >= 0 )
gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
x = cell->x + 1;
}
if ( cover != 0 )
gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
ras.count_ex - x );
}
if ( ras.render_span && ras.num_gray_spans > 0 )
ras.render_span( ras.span_y, ras.num_gray_spans,
ras.gray_spans, ras.render_span_data );
#ifdef FT_DEBUG_LEVEL_TRACE
if ( ras.num_gray_spans > 0 )
{
FT_Span* span;
int n;
FT_TRACE7(( "y = %3d ", ras.span_y ));
span = ras.gray_spans;
for ( n = 0; n < ras.num_gray_spans; n++, span++ )
FT_TRACE7(( "[%d..%d]:%02x ",
span->x, span->x + span->len - 1, span->coverage ));
FT_TRACE7(( "\n" ));
}
FT_TRACE7(( "gray_sweep: end\n" ));
#endif /* FT_DEBUG_LEVEL_TRACE */
}
#ifdef STANDALONE_
/*************************************************************************/
/* */
/* The following function should only compile in stand-alone mode, */
/* i.e., when building this component without the rest of FreeType. */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* FT_Outline_Decompose */
/* */
/* <Description> */
/* Walk over an outline's structure to decompose it into individual */
/* segments and Bézier arcs. This function is also able to emit */
/* `move to' and `close to' operations to indicate the start and end */
/* of new contours in the outline. */
/* */
/* <Input> */
/* outline :: A pointer to the source target. */
/* */
/* func_interface :: A table of `emitters', i.e., function pointers */
/* called during decomposition to indicate path */
/* operations. */
/* */
/* <InOut> */
/* user :: A typeless pointer which is passed to each */
/* emitter during the decomposition. It can be */
/* used to store the state during the */
/* decomposition. */
/* */
/* <Return> */
/* Error code. 0 means success. */
/* */
static int
FT_Outline_Decompose( const FT_Outline* outline,
const FT_Outline_Funcs* func_interface,
void* user )
{
#undef SCALED
#define SCALED( x ) ( ( (x) << shift ) - delta )
FT_Vector v_last;
FT_Vector v_control;
FT_Vector v_start;
FT_Vector* point;
FT_Vector* limit;
char* tags;
int error;
int n; /* index of contour in outline */
int first; /* index of first point in contour */
char tag; /* current point's state */
int shift;
TPos delta;
if ( !outline )
return FT_THROW( Invalid_Outline );
if ( !func_interface )
return FT_THROW( Invalid_Argument );
shift = func_interface->shift;
delta = func_interface->delta;
first = 0;
for ( n = 0; n < outline->n_contours; n++ )
{
int last; /* index of last point in contour */
FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n ));
last = outline->contours[n];
if ( last < 0 )
goto Invalid_Outline;
limit = outline->points + last;
v_start = outline->points[first];
v_start.x = SCALED( v_start.x );
v_start.y = SCALED( v_start.y );
v_last = outline->points[last];
v_last.x = SCALED( v_last.x );
v_last.y = SCALED( v_last.y );
v_control = v_start;
point = outline->points + first;
tags = outline->tags + first;
tag = FT_CURVE_TAG( tags[0] );
/* A contour cannot start with a cubic control point! */
if ( tag == FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
/* check first point to determine origin */
if ( tag == FT_CURVE_TAG_CONIC )
{
/* first point is conic control. Yes, this happens. */
if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
{
/* start at last point if it is on the curve */
v_start = v_last;
limit--;
}
else
{
/* if both first and last points are conic, */
/* start at their middle and record its position */
/* for closure */
v_start.x = ( v_start.x + v_last.x ) / 2;
v_start.y = ( v_start.y + v_last.y ) / 2;
v_last = v_start;
}
point--;
tags--;
}
FT_TRACE5(( " move to (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0 ));
error = func_interface->move_to( &v_start, user );
if ( error )
goto Exit;
while ( point < limit )
{
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
switch ( tag )
{
case FT_CURVE_TAG_ON: /* emit a single line_to */
{
FT_Vector vec;
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
FT_TRACE5(( " line to (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0 ));
error = func_interface->line_to( &vec, user );
if ( error )
goto Exit;
continue;
}
case FT_CURVE_TAG_CONIC: /* consume conic arcs */
v_control.x = SCALED( point->x );
v_control.y = SCALED( point->y );
Do_Conic:
if ( point < limit )
{
FT_Vector vec;
FT_Vector v_middle;
point++;
tags++;
tag = FT_CURVE_TAG( tags[0] );
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
if ( tag == FT_CURVE_TAG_ON )
{
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &vec, user );
if ( error )
goto Exit;
continue;
}
if ( tag != FT_CURVE_TAG_CONIC )
goto Invalid_Outline;
v_middle.x = ( v_control.x + vec.x ) / 2;
v_middle.y = ( v_control.y + vec.y ) / 2;
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
v_middle.x / 64.0, v_middle.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &v_middle, user );
if ( error )
goto Exit;
v_control = vec;
goto Do_Conic;
}
FT_TRACE5(( " conic to (%.2f, %.2f)"
" with control (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0,
v_control.x / 64.0, v_control.y / 64.0 ));
error = func_interface->conic_to( &v_control, &v_start, user );
goto Close;
default: /* FT_CURVE_TAG_CUBIC */
{
FT_Vector vec1, vec2;
if ( point + 1 > limit ||
FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
goto Invalid_Outline;
point += 2;
tags += 2;
vec1.x = SCALED( point[-2].x );
vec1.y = SCALED( point[-2].y );
vec2.x = SCALED( point[-1].x );
vec2.y = SCALED( point[-1].y );
if ( point <= limit )
{
FT_Vector vec;
vec.x = SCALED( point->x );
vec.y = SCALED( point->y );
FT_TRACE5(( " cubic to (%.2f, %.2f)"
" with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
vec.x / 64.0, vec.y / 64.0,
vec1.x / 64.0, vec1.y / 64.0,
vec2.x / 64.0, vec2.y / 64.0 ));
error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
if ( error )
goto Exit;
continue;
}
FT_TRACE5(( " cubic to (%.2f, %.2f)"
" with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0,
vec1.x / 64.0, vec1.y / 64.0,
vec2.x / 64.0, vec2.y / 64.0 ));
error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
goto Close;
}
}
}
/* close the contour with a line segment */
FT_TRACE5(( " line to (%.2f, %.2f)\n",
v_start.x / 64.0, v_start.y / 64.0 ));
error = func_interface->line_to( &v_start, user );
Close:
if ( error )
goto Exit;
first = last + 1;
}
FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
return 0;
Exit:
FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error ));
return error;
Invalid_Outline:
return FT_THROW( Invalid_Outline );
}
#endif /* STANDALONE_ */
typedef struct gray_TBand_
{
TPos min, max;
} gray_TBand;
FT_DEFINE_OUTLINE_FUNCS(
func_interface,
(FT_Outline_MoveTo_Func) gray_move_to,
(FT_Outline_LineTo_Func) gray_line_to,
(FT_Outline_ConicTo_Func)gray_conic_to,
(FT_Outline_CubicTo_Func)gray_cubic_to,
0,
0 )
static int
gray_convert_glyph_inner( RAS_ARG )
{
volatile int error = 0;
#ifdef FT_CONFIG_OPTION_PIC
FT_Outline_Funcs func_interface;
Init_Class_func_interface(&func_interface);
#endif
if ( ft_setjmp( ras.jump_buffer ) == 0 )
{
error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras );
if ( !ras.invalid )
gray_record_cell( RAS_VAR );
}
else
error = FT_THROW( Memory_Overflow );
return error;
}
static int
gray_convert_glyph( RAS_ARG )
{
gray_TBand bands[40];
gray_TBand* volatile band;
int volatile n, num_bands;
TPos volatile min, max, max_y;
FT_BBox* clip;
/* Set up state in the raster object */
gray_compute_cbox( RAS_VAR );
/* clip to target bitmap, exit if nothing to do */
clip = &ras.clip_box;
if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
return 0;
if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
ras.count_ex = ras.max_ex - ras.min_ex;
ras.count_ey = ras.max_ey - ras.min_ey;
/* set up vertical bands */
num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
if ( num_bands == 0 )
num_bands = 1;
if ( num_bands >= 39 )
num_bands = 39;
ras.band_shoot = 0;
min = ras.min_ey;
max_y = ras.max_ey;
for ( n = 0; n < num_bands; n++, min = max )
{
max = min + ras.band_size;
if ( n == num_bands - 1 || max > max_y )
max = max_y;
bands[0].min = min;
bands[0].max = max;
band = bands;
do
{
TPos bottom, top, middle;
int error;
{
PCell cells_max;
int yindex;
long cell_start, cell_end, cell_mod;
ras.ycells = (PCell*)ras.buffer;
ras.ycount = band->max - band->min;
cell_start = (long)sizeof ( PCell ) * ras.ycount;
cell_mod = cell_start % (long)sizeof ( TCell );
if ( cell_mod > 0 )
cell_start += (long)sizeof ( TCell ) - cell_mod;
cell_end = ras.buffer_size;
cell_end -= cell_end % (long)sizeof ( TCell );
cells_max = (PCell)( (char*)ras.buffer + cell_end );
ras.cells = (PCell)( (char*)ras.buffer + cell_start );
if ( ras.cells >= cells_max )
goto ReduceBands;
ras.max_cells = cells_max - ras.cells;
if ( ras.max_cells < 2 )
goto ReduceBands;
for ( yindex = 0; yindex < ras.ycount; yindex++ )
ras.ycells[yindex] = NULL;
}
ras.num_cells = 0;
ras.invalid = 1;
ras.min_ey = band->min;
ras.max_ey = band->max;
ras.count_ey = band->max - band->min;
error = gray_convert_glyph_inner( RAS_VAR );
if ( !error )
{
gray_sweep( RAS_VAR_ &ras.target );
band--;
continue;
}
else if ( error != ErrRaster_Memory_Overflow )
return 1;
ReduceBands:
/* render pool overflow; we will reduce the render band by half */
bottom = band->min;
top = band->max;
middle = bottom + ( ( top - bottom ) >> 1 );
/* This is too complex for a single scanline; there must */
/* be some problems. */
if ( middle == bottom )
{
#ifdef FT_DEBUG_LEVEL_TRACE
FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" ));
#endif
return 1;
}
if ( bottom-top >= ras.band_size )
ras.band_shoot++;
band[1].min = bottom;
band[1].max = middle;
band[0].min = middle;
band[0].max = top;
band++;
} while ( band >= bands );
}
if ( ras.band_shoot > 8 && ras.band_size > 16 )
ras.band_size = ras.band_size / 2;
return 0;
}
static int
gray_raster_render( gray_PRaster raster,
const FT_Raster_Params* params )
{
const FT_Outline* outline = (const FT_Outline*)params->source;
const FT_Bitmap* target_map = params->target;
gray_TWorker worker[1];
TCell buffer[FT_MAX( FT_RENDER_POOL_SIZE, 2048 ) / sizeof ( TCell )];
long buffer_size = sizeof ( buffer );
int band_size = (int)( buffer_size /
(long)( sizeof ( TCell ) * 8 ) );
if ( !raster )
return FT_THROW( Invalid_Argument );
if ( !outline )
return FT_THROW( Invalid_Outline );
/* return immediately if the outline is empty */
if ( outline->n_points == 0 || outline->n_contours <= 0 )
return 0;
if ( !outline->contours || !outline->points )
return FT_THROW( Invalid_Outline );
if ( outline->n_points !=
outline->contours[outline->n_contours - 1] + 1 )
return FT_THROW( Invalid_Outline );
/* if direct mode is not set, we must have a target bitmap */
if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) )
{
if ( !target_map )
return FT_THROW( Invalid_Argument );
/* nothing to do */
if ( !target_map->width || !target_map->rows )
return 0;
if ( !target_map->buffer )
return FT_THROW( Invalid_Argument );
}
/* this version does not support monochrome rendering */
if ( !( params->flags & FT_RASTER_FLAG_AA ) )
return FT_THROW( Invalid_Mode );
/* compute clipping box */
if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) )
{
/* compute clip box from target pixmap */
ras.clip_box.xMin = 0;
ras.clip_box.yMin = 0;
ras.clip_box.xMax = (FT_Pos)target_map->width;
ras.clip_box.yMax = (FT_Pos)target_map->rows;
}
else if ( params->flags & FT_RASTER_FLAG_CLIP )
ras.clip_box = params->clip_box;
else
{
ras.clip_box.xMin = -32768L;
ras.clip_box.yMin = -32768L;
ras.clip_box.xMax = 32767L;
ras.clip_box.yMax = 32767L;
}
gray_init_cells( RAS_VAR_ buffer, buffer_size );
ras.outline = *outline;
ras.num_cells = 0;
ras.invalid = 1;
ras.band_size = band_size;
ras.num_gray_spans = 0;
ras.span_y = 0;
if ( params->flags & FT_RASTER_FLAG_DIRECT )
{
ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
ras.render_span_data = params->user;
}
else
{
ras.target = *target_map;
ras.render_span = (FT_Raster_Span_Func)gray_render_span;
ras.render_span_data = &ras;
}
return gray_convert_glyph( RAS_VAR );
}
/**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/
/**** a static object. *****/
#ifdef STANDALONE_
static int
gray_raster_new( void* memory,
FT_Raster* araster )
{
static gray_TRaster the_raster;
FT_UNUSED( memory );
*araster = (FT_Raster)&the_raster;
FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
return 0;
}
static void
gray_raster_done( FT_Raster raster )
{
/* nothing */
FT_UNUSED( raster );
}
#else /* !STANDALONE_ */
static int
gray_raster_new( FT_Memory memory,
FT_Raster* araster )
{
FT_Error error;
gray_PRaster raster = NULL;
*araster = 0;
if ( !FT_ALLOC( raster, sizeof ( gray_TRaster ) ) )
{
raster->memory = memory;
*araster = (FT_Raster)raster;
}
return error;
}
static void
gray_raster_done( FT_Raster raster )
{
FT_Memory memory = (FT_Memory)((gray_PRaster)raster)->memory;
FT_FREE( raster );
}
#endif /* !STANDALONE_ */
static void
gray_raster_reset( FT_Raster raster,
char* pool_base,
long pool_size )
{
FT_UNUSED( raster );
FT_UNUSED( pool_base );
FT_UNUSED( pool_size );
}
static int
gray_raster_set_mode( FT_Raster raster,
unsigned long mode,
void* args )
{
FT_UNUSED( raster );
FT_UNUSED( mode );
FT_UNUSED( args );
return 0; /* nothing to do */
}
FT_DEFINE_RASTER_FUNCS(
ft_grays_raster,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Raster_New_Func) gray_raster_new,
(FT_Raster_Reset_Func) gray_raster_reset,
(FT_Raster_Set_Mode_Func)gray_raster_set_mode,
(FT_Raster_Render_Func) gray_raster_render,
(FT_Raster_Done_Func) gray_raster_done )
/* END */
/* Local Variables: */
/* coding: utf-8 */
/* End: */
src/smooth/ftsmooth.c
Not Covered Functions
/***************************************************************************/
/* */
/* ftsmooth.c */
/* */
/* Anti-aliasing renderer interface (body). */
/* */
/* Copyright 2000-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_OBJECTS_H
#include FT_OUTLINE_H
#include "ftsmooth.h"
#include "ftgrays.h"
#include "ftspic.h"
#include "ftsmerrs.h"
/* initialize renderer -- init its raster */
static FT_Error
ft_smooth_init( FT_Renderer render )
{
FT_Library library = FT_MODULE_LIBRARY( render );
render->clazz->raster_class->raster_reset( render->raster,
library->raster_pool,
library->raster_pool_size );
return 0;
}
/* sets render-specific mode */
static FT_Error
ft_smooth_set_mode( FT_Renderer render,
FT_ULong mode_tag,
FT_Pointer data )
{
/* we simply pass it to the raster */
return render->clazz->raster_class->raster_set_mode( render->raster,
mode_tag,
data );
}
/* transform a given glyph image */
static FT_Error
ft_smooth_transform( FT_Renderer render,
FT_GlyphSlot slot,
const FT_Matrix* matrix,
const FT_Vector* delta )
{
FT_Error error = FT_Err_Ok;
if ( slot->format != render->glyph_format )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
if ( matrix )
FT_Outline_Transform( &slot->outline, matrix );
if ( delta )
FT_Outline_Translate( &slot->outline, delta->x, delta->y );
Exit:
return error;
}
/* return the glyph's control box */
static void
ft_smooth_get_cbox( FT_Renderer render,
FT_GlyphSlot slot,
FT_BBox* cbox )
{
FT_MEM_ZERO( cbox, sizeof ( *cbox ) );
if ( slot->format == render->glyph_format )
FT_Outline_Get_CBox( &slot->outline, cbox );
}
/* convert a slot's glyph image into a bitmap */
static FT_Error
ft_smooth_render_generic( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin,
FT_Render_Mode required_mode )
{
FT_Error error;
FT_Outline* outline = &slot->outline;
FT_Bitmap* bitmap = &slot->bitmap;
FT_Memory memory = render->root.memory;
FT_BBox cbox;
FT_Pos x_shift = 0;
FT_Pos y_shift = 0;
FT_Pos x_left, y_top;
FT_Pos width, height, pitch;
#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
FT_Pos height_org, width_org;
#endif
FT_Int hmul = mode == FT_RENDER_MODE_LCD;
FT_Int vmul = mode == FT_RENDER_MODE_LCD_V;
FT_Raster_Params params;
FT_Bool have_outline_shifted = FALSE;
FT_Bool have_buffer = FALSE;
/* check glyph image format */
if ( slot->format != render->glyph_format )
{
error = FT_THROW( Invalid_Argument );
goto Exit;
}
/* check mode */
if ( mode != required_mode )
{
error = FT_THROW( Cannot_Render_Glyph );
goto Exit;
}
if ( origin )
{
x_shift = origin->x;
y_shift = origin->y;
}
/* compute the control box, and grid fit it */
/* taking into account the origin shift */
FT_Outline_Get_CBox( outline, &cbox );
cbox.xMin = FT_PIX_FLOOR( cbox.xMin + x_shift );
cbox.yMin = FT_PIX_FLOOR( cbox.yMin + y_shift );
cbox.xMax = FT_PIX_CEIL( cbox.xMax + x_shift );
cbox.yMax = FT_PIX_CEIL( cbox.yMax + y_shift );
x_shift -= cbox.xMin;
y_shift -= cbox.yMin;
x_left = cbox.xMin >> 6;
y_top = cbox.yMax >> 6;
width = (FT_ULong)( cbox.xMax - cbox.xMin ) >> 6;
height = (FT_ULong)( cbox.yMax - cbox.yMin ) >> 6;
#ifndef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
width_org = width;
height_org = height;
#endif
pitch = width;
if ( hmul )
{
width *= 3;
pitch = FT_PAD_CEIL( width, 4 );
}
if ( vmul )
height *= 3;
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
if ( slot->library->lcd_filter_func )
{
FT_Int extra = slot->library->lcd_extra;
if ( hmul )
{
x_shift += 64 * ( extra >> 1 );
x_left -= extra >> 1;
width += 3 * extra;
pitch = FT_PAD_CEIL( width, 4 );
}
if ( vmul )
{
y_shift += 64 * ( extra >> 1 );
y_top += extra >> 1;
height += 3 * extra;
}
}
#endif
/*
* XXX: on 16bit system, we return an error for huge bitmap
* to prevent an overflow.
*/
if ( x_left > FT_INT_MAX || y_top > FT_INT_MAX ||
x_left < FT_INT_MIN || y_top < FT_INT_MIN )
{
error = FT_THROW( Invalid_Pixel_Size );
goto Exit;
}
/* Required check is (pitch * height < FT_ULONG_MAX), */
/* but we care realistic cases only. Always pitch <= width. */
if ( width > 0x7FFF || height > 0x7FFF )
{
FT_ERROR(( "ft_smooth_render_generic: glyph too large: %u x %u\n",
width, height ));
error = FT_THROW( Raster_Overflow );
goto Exit;
}
/* release old bitmap buffer */
if ( slot->internal->flags & FT_GLYPH_OWN_BITMAP )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
/* allocate new one */
if ( FT_ALLOC( bitmap->buffer, (FT_ULong)( pitch * height ) ) )
goto Exit;
else
have_buffer = TRUE;
slot->internal->flags |= FT_GLYPH_OWN_BITMAP;
slot->format = FT_GLYPH_FORMAT_BITMAP;
slot->bitmap_left = (FT_Int)x_left;
slot->bitmap_top = (FT_Int)y_top;
bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
bitmap->num_grays = 256;
bitmap->width = (unsigned int)width;
bitmap->rows = (unsigned int)height;
bitmap->pitch = pitch;
/* translate outline to render it into the bitmap */
if ( x_shift || y_shift )
{
FT_Outline_Translate( outline, x_shift, y_shift );
have_outline_shifted = TRUE;
}
/* set up parameters */
params.target = bitmap;
params.source = outline;
params.flags = FT_RASTER_FLAG_AA;
#ifdef FT_CONFIG_OPTION_SUBPIXEL_RENDERING
/* implode outline if needed */
{
FT_Vector* points = outline->points;
FT_Vector* points_end = points + outline->n_points;
FT_Vector* vec;
if ( hmul )
for ( vec = points; vec < points_end; vec++ )
vec->x *= 3;
if ( vmul )
for ( vec = points; vec < points_end; vec++ )
vec->y *= 3;
}
/* render outline into the bitmap */
error = render->raster_render( render->raster, ¶ms );
/* deflate outline if needed */
{
FT_Vector* points = outline->points;
FT_Vector* points_end = points + outline->n_points;
FT_Vector* vec;
if ( hmul )
for ( vec = points; vec < points_end; vec++ )
vec->x /= 3;
if ( vmul )
for ( vec = points; vec < points_end; vec++ )
vec->y /= 3;
}
if ( error )
goto Exit;
if ( slot->library->lcd_filter_func )
slot->library->lcd_filter_func( bitmap, mode, slot->library );
#else /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* render outline into bitmap */
error = render->raster_render( render->raster, ¶ms );
if ( error )
goto Exit;
/* expand it horizontally */
if ( hmul )
{
FT_Byte* line = bitmap->buffer;
FT_UInt hh;
for ( hh = height_org; hh > 0; hh--, line += pitch )
{
FT_UInt xx;
FT_Byte* end = line + width;
for ( xx = width_org; xx > 0; xx-- )
{
FT_UInt pixel = line[xx-1];
end[-3] = (FT_Byte)pixel;
end[-2] = (FT_Byte)pixel;
end[-1] = (FT_Byte)pixel;
end -= 3;
}
}
}
/* expand it vertically */
if ( vmul )
{
FT_Byte* read = bitmap->buffer + ( height - height_org ) * pitch;
FT_Byte* write = bitmap->buffer;
FT_UInt hh;
for ( hh = height_org; hh > 0; hh-- )
{
ft_memcpy( write, read, pitch );
write += pitch;
ft_memcpy( write, read, pitch );
write += pitch;
ft_memcpy( write, read, pitch );
write += pitch;
read += pitch;
}
}
#endif /* !FT_CONFIG_OPTION_SUBPIXEL_RENDERING */
/* everything is fine; don't deallocate buffer */
have_buffer = FALSE;
error = FT_Err_Ok;
Exit:
if ( have_outline_shifted )
FT_Outline_Translate( outline, -x_shift, -y_shift );
if ( have_buffer )
{
FT_FREE( bitmap->buffer );
slot->internal->flags &= ~FT_GLYPH_OWN_BITMAP;
}
return error;
}
/* convert a slot's glyph image into a bitmap */
static FT_Error
ft_smooth_render( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin )
{
if ( mode == FT_RENDER_MODE_LIGHT )
mode = FT_RENDER_MODE_NORMAL;
return ft_smooth_render_generic( render, slot, mode, origin,
FT_RENDER_MODE_NORMAL );
}
/* convert a slot's glyph image into a horizontal LCD bitmap */
static FT_Error
ft_smooth_render_lcd( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin )
{
FT_Error error;
error = ft_smooth_render_generic( render, slot, mode, origin,
FT_RENDER_MODE_LCD );
if ( !error )
slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD;
return error;
}
/* convert a slot's glyph image into a vertical LCD bitmap */
static FT_Error
ft_smooth_render_lcd_v( FT_Renderer render,
FT_GlyphSlot slot,
FT_Render_Mode mode,
const FT_Vector* origin )
{
FT_Error error;
error = ft_smooth_render_generic( render, slot, mode, origin,
FT_RENDER_MODE_LCD_V );
if ( !error )
slot->bitmap.pixel_mode = FT_PIXEL_MODE_LCD_V;
return error;
}
FT_DEFINE_RENDERER( ft_smooth_renderer_class,
FT_MODULE_RENDERER,
sizeof ( FT_RendererRec ),
"smooth",
0x10000L,
0x20000L,
0, /* module specific interface */
(FT_Module_Constructor)ft_smooth_init,
(FT_Module_Destructor) 0,
(FT_Module_Requester) 0
,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Renderer_RenderFunc) ft_smooth_render,
(FT_Renderer_TransformFunc)ft_smooth_transform,
(FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
(FT_Renderer_SetModeFunc) ft_smooth_set_mode,
(FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET
)
FT_DEFINE_RENDERER( ft_smooth_lcd_renderer_class,
FT_MODULE_RENDERER,
sizeof ( FT_RendererRec ),
"smooth-lcd",
0x10000L,
0x20000L,
0, /* module specific interface */
(FT_Module_Constructor)ft_smooth_init,
(FT_Module_Destructor) 0,
(FT_Module_Requester) 0
,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Renderer_RenderFunc) ft_smooth_render_lcd,
(FT_Renderer_TransformFunc)ft_smooth_transform,
(FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
(FT_Renderer_SetModeFunc) ft_smooth_set_mode,
(FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET
)
FT_DEFINE_RENDERER( ft_smooth_lcdv_renderer_class,
FT_MODULE_RENDERER,
sizeof ( FT_RendererRec ),
"smooth-lcdv",
0x10000L,
0x20000L,
0, /* module specific interface */
(FT_Module_Constructor)ft_smooth_init,
(FT_Module_Destructor) 0,
(FT_Module_Requester) 0
,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Renderer_RenderFunc) ft_smooth_render_lcd_v,
(FT_Renderer_TransformFunc)ft_smooth_transform,
(FT_Renderer_GetCBoxFunc) ft_smooth_get_cbox,
(FT_Renderer_SetModeFunc) ft_smooth_set_mode,
(FT_Raster_Funcs*) &FT_GRAYS_RASTER_GET
)
/* END */
src/truetype/ttobjs.c
Not Covered Functions
/***************************************************************************/
/* */
/* ttobjs.c */
/* */
/* Objects manager (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_TAGS_H
#include FT_INTERNAL_SFNT_H
#include FT_TRUETYPE_DRIVER_H
#include "ttgload.h"
#include "ttpload.h"
#include "tterrors.h"
#ifdef TT_USE_BYTECODE_INTERPRETER
#include "ttinterp.h"
#endif
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include "ttgxvar.h"
#endif
/*************************************************************************/
/* */
/* 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_ttobjs
#ifdef TT_USE_BYTECODE_INTERPRETER
/*************************************************************************/
/* */
/* GLYPH ZONE FUNCTIONS */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* tt_glyphzone_done */
/* */
/* <Description> */
/* Deallocate a glyph zone. */
/* */
/* <Input> */
/* zone :: A pointer to the target glyph zone. */
/* */
FT_LOCAL_DEF( void )
tt_glyphzone_done( TT_GlyphZone zone )
{
FT_Memory memory = zone->memory;
if ( memory )
{
FT_FREE( zone->contours );
FT_FREE( zone->tags );
FT_FREE( zone->cur );
FT_FREE( zone->org );
FT_FREE( zone->orus );
zone->max_points = zone->n_points = 0;
zone->max_contours = zone->n_contours = 0;
zone->memory = NULL;
}
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_glyphzone_new */
/* */
/* <Description> */
/* Allocate a new glyph zone. */
/* */
/* <Input> */
/* memory :: A handle to the current memory object. */
/* */
/* maxPoints :: The capacity of glyph zone in points. */
/* */
/* maxContours :: The capacity of glyph zone in contours. */
/* */
/* <Output> */
/* zone :: A pointer to the target glyph zone record. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_glyphzone_new( FT_Memory memory,
FT_UShort maxPoints,
FT_Short maxContours,
TT_GlyphZone zone )
{
FT_Error error;
FT_MEM_ZERO( zone, sizeof ( *zone ) );
zone->memory = memory;
if ( FT_NEW_ARRAY( zone->org, maxPoints ) ||
FT_NEW_ARRAY( zone->cur, maxPoints ) ||
FT_NEW_ARRAY( zone->orus, maxPoints ) ||
FT_NEW_ARRAY( zone->tags, maxPoints ) ||
FT_NEW_ARRAY( zone->contours, maxContours ) )
{
tt_glyphzone_done( zone );
}
else
{
zone->max_points = maxPoints;
zone->max_contours = maxContours;
}
return error;
}
#endif /* TT_USE_BYTECODE_INTERPRETER */
/* Compare the face with a list of well-known `tricky' fonts. */
/* This list shall be expanded as we find more of them. */
static FT_Bool
tt_check_trickyness_family( FT_String* name )
{
#define TRICK_NAMES_MAX_CHARACTERS 19
#define TRICK_NAMES_COUNT 9
static const char trick_names[TRICK_NAMES_COUNT]
[TRICK_NAMES_MAX_CHARACTERS + 1] =
{
"DFKaiSho-SB", /* dfkaisb.ttf */
"DFKaiShu",
"DFKai-SB", /* kaiu.ttf */
"HuaTianKaiTi?", /* htkt2.ttf */
"HuaTianSongTi?", /* htst3.ttf */
"Ming(for ISO10646)", /* hkscsiic.ttf & iicore.ttf */
"MingLiU", /* mingliu.ttf & mingliu.ttc */
"PMingLiU", /* mingliu.ttc */
"MingLi43", /* mingli.ttf */
};
int nn;
for ( nn = 0; nn < TRICK_NAMES_COUNT; nn++ )
if ( ft_strstr( name, trick_names[nn] ) )
return TRUE;
return FALSE;
}
/* XXX: This function should be in the `sfnt' module. */
/* Some PDF generators clear the checksums in the TrueType header table. */
/* For example, Quartz ContextPDF clears all entries, or Bullzip PDF */
/* Printer clears the entries for subsetted subtables. We thus have to */
/* recalculate the checksums where necessary. */
static FT_UInt32
tt_synth_sfnt_checksum( FT_Stream stream,
FT_ULong length )
{
FT_Error error;
FT_UInt32 checksum = 0;
FT_UInt i;
if ( FT_FRAME_ENTER( length ) )
return 0;
for ( ; length > 3; length -= 4 )
checksum += (FT_UInt32)FT_GET_ULONG();
for ( i = 3; length > 0; length--, i-- )
checksum += (FT_UInt32)FT_GET_BYTE() << ( i * 8 );
FT_FRAME_EXIT();
return checksum;
}
/* XXX: This function should be in the `sfnt' module. */
static FT_ULong
tt_get_sfnt_checksum( TT_Face face,
FT_UShort i )
{
#if 0 /* if we believe the written value, use following part. */
if ( face->dir_tables[i].CheckSum )
return face->dir_tables[i].CheckSum;
#endif
if ( !face->goto_table )
return 0;
if ( face->goto_table( face,
face->dir_tables[i].Tag,
face->root.stream,
NULL ) )
return 0;
return (FT_ULong)tt_synth_sfnt_checksum( face->root.stream,
face->dir_tables[i].Length );
}
typedef struct tt_sfnt_id_rec_
{
FT_ULong CheckSum;
FT_ULong Length;
} tt_sfnt_id_rec;
static FT_Bool
tt_check_trickyness_sfnt_ids( TT_Face face )
{
#define TRICK_SFNT_IDS_PER_FACE 3
#define TRICK_SFNT_IDS_NUM_FACES 18
static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES]
[TRICK_SFNT_IDS_PER_FACE] = {
#define TRICK_SFNT_ID_cvt 0
#define TRICK_SFNT_ID_fpgm 1
#define TRICK_SFNT_ID_prep 2
{ /* MingLiU 1995 */
{ 0x05BCF058UL, 0x000002E4UL }, /* cvt */
{ 0x28233BF1UL, 0x000087C4UL }, /* fpgm */
{ 0xA344A1EAUL, 0x000001E1UL } /* prep */
},
{ /* MingLiU 1996- */
{ 0x05BCF058UL, 0x000002E4UL }, /* cvt */
{ 0x28233BF1UL, 0x000087C4UL }, /* fpgm */
{ 0xA344A1EBUL, 0x000001E1UL } /* prep */
},
{ /* DFKaiShu */
{ 0x11E5EAD4UL, 0x00000350UL }, /* cvt */
{ 0x5A30CA3BUL, 0x00009063UL }, /* fpgm */
{ 0x13A42602UL, 0x0000007EUL } /* prep */
},
{ /* DFKaiShu2 */
{ 0x11E5EAD4UL, 0x00000350UL }, /* cvt */
{ 0xA6E78C01UL, 0x00008998UL }, /* fpgm */
{ 0x13A42602UL, 0x0000007EUL } /* prep */
},
{ /* HuaTianKaiTi */
{ 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */
{ 0x9C9E48B8UL, 0x0000BEA2UL }, /* fpgm */
{ 0x70020112UL, 0x00000008UL } /* prep */
},
{ /* HuaTianSongTi */
{ 0xFFFBFFFCUL, 0x00000008UL }, /* cvt */
{ 0x0A5A0483UL, 0x00017C39UL }, /* fpgm */
{ 0x70020112UL, 0x00000008UL } /* prep */
},
{ /* NEC fadpop7.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x40C92555UL, 0x000000E5UL }, /* fpgm */
{ 0xA39B58E3UL, 0x0000117CUL } /* prep */
},
{ /* NEC fadrei5.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x33C41652UL, 0x000000E5UL }, /* fpgm */
{ 0x26D6C52AUL, 0x00000F6AUL } /* prep */
},
{ /* NEC fangot7.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x6DB1651DUL, 0x0000019DUL }, /* fpgm */
{ 0x6C6E4B03UL, 0x00002492UL } /* prep */
},
{ /* NEC fangyo5.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x40C92555UL, 0x000000E5UL }, /* fpgm */
{ 0xDE51FAD0UL, 0x0000117CUL } /* prep */
},
{ /* NEC fankyo5.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x85E47664UL, 0x000000E5UL }, /* fpgm */
{ 0xA6C62831UL, 0x00001CAAUL } /* prep */
},
{ /* NEC fanrgo5.ttf */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x2D891CFDUL, 0x0000019DUL }, /* fpgm */
{ 0xA0604633UL, 0x00001DE8UL } /* prep */
},
{ /* NEC fangot5.ttc */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x40AA774CUL, 0x000001CBUL }, /* fpgm */
{ 0x9B5CAA96UL, 0x00001F9AUL } /* prep */
},
{ /* NEC fanmin3.ttc */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x0D3DE9CBUL, 0x00000141UL }, /* fpgm */
{ 0xD4127766UL, 0x00002280UL } /* prep */
},
{ /* NEC FA-Gothic, 1996 */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x4A692698UL, 0x000001F0UL }, /* fpgm */
{ 0x340D4346UL, 0x00001FCAUL } /* prep */
},
{ /* NEC FA-Minchou, 1996 */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0xCD34C604UL, 0x00000166UL }, /* fpgm */
{ 0x6CF31046UL, 0x000022B0UL } /* prep */
},
{ /* NEC FA-RoundGothicB, 1996 */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0x5DA75315UL, 0x0000019DUL }, /* fpgm */
{ 0x40745A5FUL, 0x000022E0UL } /* prep */
},
{ /* NEC FA-RoundGothicM, 1996 */
{ 0x00000000UL, 0x00000000UL }, /* cvt */
{ 0xF055FC48UL, 0x000001C2UL }, /* fpgm */
{ 0x3900DED3UL, 0x00001E18UL } /* prep */
}
};
FT_ULong checksum;
int num_matched_ids[TRICK_SFNT_IDS_NUM_FACES];
FT_Bool has_cvt, has_fpgm, has_prep;
FT_UShort i;
int j, k;
FT_MEM_SET( num_matched_ids, 0,
sizeof ( int ) * TRICK_SFNT_IDS_NUM_FACES );
has_cvt = FALSE;
has_fpgm = FALSE;
has_prep = FALSE;
for ( i = 0; i < face->num_tables; i++ )
{
checksum = 0;
switch( face->dir_tables[i].Tag )
{
case TTAG_cvt:
k = TRICK_SFNT_ID_cvt;
has_cvt = TRUE;
break;
case TTAG_fpgm:
k = TRICK_SFNT_ID_fpgm;
has_fpgm = TRUE;
break;
case TTAG_prep:
k = TRICK_SFNT_ID_prep;
has_prep = TRUE;
break;
default:
continue;
}
for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ )
if ( face->dir_tables[i].Length == sfnt_id[j][k].Length )
{
if ( !checksum )
checksum = tt_get_sfnt_checksum( face, i );
if ( sfnt_id[j][k].CheckSum == checksum )
num_matched_ids[j]++;
if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE )
return TRUE;
}
}
for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ )
{
if ( !has_cvt && !sfnt_id[j][TRICK_SFNT_ID_cvt].Length )
num_matched_ids[j]++;
if ( !has_fpgm && !sfnt_id[j][TRICK_SFNT_ID_fpgm].Length )
num_matched_ids[j]++;
if ( !has_prep && !sfnt_id[j][TRICK_SFNT_ID_prep].Length )
num_matched_ids[j]++;
if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE )
return TRUE;
}
return FALSE;
}
static FT_Bool
tt_check_trickyness( FT_Face face )
{
if ( !face )
return FALSE;
/* For first, check the face name for quick check. */
if ( face->family_name &&
tt_check_trickyness_family( face->family_name ) )
return TRUE;
/* Type42 fonts may lack `name' tables, we thus try to identify */
/* tricky fonts by checking the checksums of Type42-persistent */
/* sfnt tables (`cvt', `fpgm', and `prep'). */
if ( tt_check_trickyness_sfnt_ids( (TT_Face)face ) )
return TRUE;
return FALSE;
}
/* Check whether `.notdef' is the only glyph in the `loca' table. */
static FT_Bool
tt_check_single_notdef( FT_Face ttface )
{
FT_Bool result = FALSE;
TT_Face face = (TT_Face)ttface;
FT_UInt asize;
FT_ULong i;
FT_ULong glyph_index = 0;
FT_UInt count = 0;
for( i = 0; i < face->num_locations; i++ )
{
tt_face_get_location( face, i, &asize );
if ( asize > 0 )
{
count += 1;
if ( count > 1 )
break;
glyph_index = i;
}
}
/* Only have a single outline. */
if ( count == 1 )
{
if ( glyph_index == 0 )
result = TRUE;
else
{
/* FIXME: Need to test glyphname == .notdef ? */
FT_Error error;
char buf[8];
error = FT_Get_Glyph_Name( ttface, glyph_index, buf, 8 );
if ( !error &&
buf[0] == '.' && !ft_strncmp( buf, ".notdef", 8 ) )
result = TRUE;
}
}
return result;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_init */
/* */
/* <Description> */
/* Initialize a given TrueType face object. */
/* */
/* <Input> */
/* stream :: The source font stream. */
/* */
/* face_index :: The index of the TrueType font, if we are opening a */
/* collection, in bits 0-15. The numbered instance */
/* index~+~1 of a GX (sub)font, if applicable, in bits */
/* 16-30. */
/* */
/* num_params :: Number of additional generic parameters. Ignored. */
/* */
/* params :: Additional generic parameters. Ignored. */
/* */
/* <InOut> */
/* face :: The newly built face object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_face_init( FT_Stream stream,
FT_Face ttface, /* TT_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
FT_Error error;
FT_Library library;
SFNT_Service sfnt;
TT_Face face = (TT_Face)ttface;
FT_TRACE2(( "TTF driver\n" ));
library = ttface->driver->root.library;
sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
if ( !sfnt )
{
FT_ERROR(( "tt_face_init: cannot access `sfnt' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
/* create input stream from resource */
if ( FT_STREAM_SEEK( 0 ) )
goto Exit;
/* check that we have a valid TrueType file */
error = sfnt->init_face( stream, face, face_index, num_params, params );
/* Stream may have changed. */
stream = face->root.stream;
if ( error )
goto Exit;
/* We must also be able to accept Mac/GX fonts, as well as OT ones. */
/* The 0x00020000 tag is completely undocumented; some fonts from */
/* Arphic made for Chinese Windows 3.1 have this. */
if ( face->format_tag != 0x00010000L && /* MS fonts */
face->format_tag != 0x00020000L && /* CJK fonts for Win 3.1 */
face->format_tag != TTAG_true ) /* Mac fonts */
{
FT_TRACE2(( " not a TTF font\n" ));
goto Bad_Format;
}
#ifdef TT_USE_BYTECODE_INTERPRETER
ttface->face_flags |= FT_FACE_FLAG_HINTER;
#endif
/* If we are performing a simple font format check, exit immediately. */
if ( face_index < 0 )
return FT_Err_Ok;
/* Load font directory */
error = sfnt->load_face( stream, face, face_index, num_params, params );
if ( error )
goto Exit;
if ( tt_check_trickyness( ttface ) )
ttface->face_flags |= FT_FACE_FLAG_TRICKY;
error = tt_face_load_hdmx( face, stream );
if ( error )
goto Exit;
if ( FT_IS_SCALABLE( ttface ) )
{
#ifdef FT_CONFIG_OPTION_INCREMENTAL
if ( !ttface->internal->incremental_interface )
error = tt_face_load_loca( face, stream );
if ( !error )
error = tt_face_load_cvt( face, stream );
if ( !error )
error = tt_face_load_fpgm( face, stream );
if ( !error )
error = tt_face_load_prep( face, stream );
/* Check the scalable flag based on `loca'. */
if ( !ttface->internal->incremental_interface &&
ttface->num_fixed_sizes &&
face->glyph_locations &&
tt_check_single_notdef( ttface ) )
{
FT_TRACE5(( "tt_face_init:"
" Only the `.notdef' glyph has an outline.\n"
" "
" Resetting scalable flag to FALSE.\n" ));
ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE;
}
#else /* !FT_CONFIG_OPTION_INCREMENTAL */
if ( !error )
error = tt_face_load_loca( face, stream );
if ( !error )
error = tt_face_load_cvt( face, stream );
if ( !error )
error = tt_face_load_fpgm( face, stream );
if ( !error )
error = tt_face_load_prep( face, stream );
/* Check the scalable flag based on `loca'. */
if ( ttface->num_fixed_sizes &&
face->glyph_locations &&
tt_check_single_notdef( ttface ) )
{
FT_TRACE5(( "tt_face_init:"
" Only the `.notdef' glyph has an outline.\n"
" "
" Resetting scalable flag to FALSE.\n" ));
ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE;
}
#endif /* !FT_CONFIG_OPTION_INCREMENTAL */
}
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
{
FT_Int instance_index = face_index >> 16;
if ( FT_HAS_MULTIPLE_MASTERS( ttface ) &&
instance_index > 0 )
{
error = TT_Get_MM_Var( face, NULL );
if ( error )
goto Exit;
if ( face->blend->mmvar->namedstyle )
{
FT_Memory memory = ttface->memory;
FT_Var_Named_Style* named_style;
FT_String* style_name;
/* in `face_index', the instance index starts with value 1 */
named_style = face->blend->mmvar->namedstyle + instance_index - 1;
error = sfnt->get_name( face,
(FT_UShort)named_style->strid,
&style_name );
if ( error )
goto Exit;
/* set style name; if already set, replace it */
if ( face->root.style_name )
FT_FREE( face->root.style_name );
face->root.style_name = style_name;
/* finally, select the named instance */
error = TT_Set_Var_Design( face,
face->blend->mmvar->num_axis,
named_style->coords );
if ( error )
goto Exit;
}
}
}
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
/* initialize standard glyph loading routines */
TT_Init_Glyph_Loading( face );
Exit:
return error;
Bad_Format:
error = FT_THROW( Unknown_File_Format );
goto Exit;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_face_done */
/* */
/* <Description> */
/* Finalize a given face object. */
/* */
/* <Input> */
/* face :: A pointer to the face object to destroy. */
/* */
FT_LOCAL_DEF( void )
tt_face_done( FT_Face ttface ) /* TT_Face */
{
TT_Face face = (TT_Face)ttface;
FT_Memory memory;
FT_Stream stream;
SFNT_Service sfnt;
if ( !face )
return;
memory = ttface->memory;
stream = ttface->stream;
sfnt = (SFNT_Service)face->sfnt;
/* for `extended TrueType formats' (i.e. compressed versions) */
if ( face->extra.finalizer )
face->extra.finalizer( face->extra.data );
if ( sfnt )
sfnt->done_face( face );
/* freeing the locations table */
tt_face_done_loca( face );
tt_face_free_hdmx( face );
/* freeing the CVT */
FT_FREE( face->cvt );
face->cvt_size = 0;
/* freeing the programs */
FT_FRAME_RELEASE( face->font_program );
FT_FRAME_RELEASE( face->cvt_program );
face->font_program_size = 0;
face->cvt_program_size = 0;
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
tt_done_blend( memory, face->blend );
face->blend = NULL;
#endif
}
/*************************************************************************/
/* */
/* SIZE FUNCTIONS */
/* */
/*************************************************************************/
#ifdef TT_USE_BYTECODE_INTERPRETER
/*************************************************************************/
/* */
/* <Function> */
/* tt_size_run_fpgm */
/* */
/* <Description> */
/* Run the font program. */
/* */
/* <Input> */
/* size :: A handle to the size object. */
/* */
/* pedantic :: Set if bytecode execution should be pedantic. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_size_run_fpgm( TT_Size size,
FT_Bool pedantic )
{
TT_Face face = (TT_Face)size->root.face;
TT_ExecContext exec;
FT_Error error;
exec = size->context;
error = TT_Load_Context( exec, face, size );
if ( error )
return error;
exec->callTop = 0;
exec->top = 0;
exec->period = 64;
exec->phase = 0;
exec->threshold = 0;
exec->instruction_trap = FALSE;
exec->F_dot_P = 0x4000L;
exec->pedantic_hinting = pedantic;
{
FT_Size_Metrics* metrics = &exec->metrics;
TT_Size_Metrics* tt_metrics = &exec->tt_metrics;
metrics->x_ppem = 0;
metrics->y_ppem = 0;
metrics->x_scale = 0;
metrics->y_scale = 0;
tt_metrics->ppem = 0;
tt_metrics->scale = 0;
tt_metrics->ratio = 0x10000L;
}
/* allow font program execution */
TT_Set_CodeRange( exec,
tt_coderange_font,
face->font_program,
(FT_Long)face->font_program_size );
/* disable CVT and glyph programs coderange */
TT_Clear_CodeRange( exec, tt_coderange_cvt );
TT_Clear_CodeRange( exec, tt_coderange_glyph );
if ( face->font_program_size > 0 )
{
TT_Goto_CodeRange( exec, tt_coderange_font, 0 );
FT_TRACE4(( "Executing `fpgm' table.\n" ));
error = face->interpreter( exec );
}
else
error = FT_Err_Ok;
size->bytecode_ready = error;
if ( !error )
TT_Save_Context( exec, size );
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_size_run_prep */
/* */
/* <Description> */
/* Run the control value program. */
/* */
/* <Input> */
/* size :: A handle to the size object. */
/* */
/* pedantic :: Set if bytecode execution should be pedantic. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_size_run_prep( TT_Size size,
FT_Bool pedantic )
{
TT_Face face = (TT_Face)size->root.face;
TT_ExecContext exec;
FT_Error error;
exec = size->context;
error = TT_Load_Context( exec, face, size );
if ( error )
return error;
exec->callTop = 0;
exec->top = 0;
exec->instruction_trap = FALSE;
exec->pedantic_hinting = pedantic;
TT_Set_CodeRange( exec,
tt_coderange_cvt,
face->cvt_program,
(FT_Long)face->cvt_program_size );
TT_Clear_CodeRange( exec, tt_coderange_glyph );
if ( face->cvt_program_size > 0 )
{
TT_Goto_CodeRange( exec, tt_coderange_cvt, 0 );
FT_TRACE4(( "Executing `prep' table.\n" ));
error = face->interpreter( exec );
}
else
error = FT_Err_Ok;
size->cvt_ready = error;
/* UNDOCUMENTED! The MS rasterizer doesn't allow the following */
/* graphics state variables to be modified by the CVT program. */
exec->GS.dualVector.x = 0x4000;
exec->GS.dualVector.y = 0;
exec->GS.projVector.x = 0x4000;
exec->GS.projVector.y = 0x0;
exec->GS.freeVector.x = 0x4000;
exec->GS.freeVector.y = 0x0;
exec->GS.rp0 = 0;
exec->GS.rp1 = 0;
exec->GS.rp2 = 0;
exec->GS.gep0 = 1;
exec->GS.gep1 = 1;
exec->GS.gep2 = 1;
exec->GS.loop = 1;
/* save as default graphics state */
size->GS = exec->GS;
TT_Save_Context( exec, size );
return error;
}
static void
tt_size_done_bytecode( FT_Size ftsize )
{
TT_Size size = (TT_Size)ftsize;
TT_Face face = (TT_Face)ftsize->face;
FT_Memory memory = face->root.memory;
if ( size->context )
{
TT_Done_Context( size->context );
size->context = NULL;
}
FT_FREE( size->cvt );
size->cvt_size = 0;
/* free storage area */
FT_FREE( size->storage );
size->storage_size = 0;
/* twilight zone */
tt_glyphzone_done( &size->twilight );
FT_FREE( size->function_defs );
FT_FREE( size->instruction_defs );
size->num_function_defs = 0;
size->max_function_defs = 0;
size->num_instruction_defs = 0;
size->max_instruction_defs = 0;
size->max_func = 0;
size->max_ins = 0;
size->bytecode_ready = -1;
size->cvt_ready = -1;
}
/* Initialize bytecode-related fields in the size object. */
/* We do this only if bytecode interpretation is really needed. */
static FT_Error
tt_size_init_bytecode( FT_Size ftsize,
FT_Bool pedantic )
{
FT_Error error;
TT_Size size = (TT_Size)ftsize;
TT_Face face = (TT_Face)ftsize->face;
FT_Memory memory = face->root.memory;
FT_UShort n_twilight;
TT_MaxProfile* maxp = &face->max_profile;
/* clean up bytecode related data */
FT_FREE( size->function_defs );
FT_FREE( size->instruction_defs );
FT_FREE( size->cvt );
FT_FREE( size->storage );
if ( size->context )
TT_Done_Context( size->context );
tt_glyphzone_done( &size->twilight );
size->bytecode_ready = -1;
size->cvt_ready = -1;
size->context = TT_New_Context( (TT_Driver)face->root.driver );
size->max_function_defs = maxp->maxFunctionDefs;
size->max_instruction_defs = maxp->maxInstructionDefs;
size->num_function_defs = 0;
size->num_instruction_defs = 0;
size->max_func = 0;
size->max_ins = 0;
size->cvt_size = face->cvt_size;
size->storage_size = maxp->maxStorage;
/* Set default metrics */
{
TT_Size_Metrics* metrics = &size->ttmetrics;
metrics->rotated = FALSE;
metrics->stretched = FALSE;
/* set default engine compensation */
metrics->compensations[0] = 0; /* gray */
metrics->compensations[1] = 0; /* black */
metrics->compensations[2] = 0; /* white */
metrics->compensations[3] = 0; /* reserved */
}
/* allocate function defs, instruction defs, cvt, and storage area */
if ( FT_NEW_ARRAY( size->function_defs, size->max_function_defs ) ||
FT_NEW_ARRAY( size->instruction_defs, size->max_instruction_defs ) ||
FT_NEW_ARRAY( size->cvt, size->cvt_size ) ||
FT_NEW_ARRAY( size->storage, size->storage_size ) )
goto Exit;
/* reserve twilight zone */
n_twilight = maxp->maxTwilightPoints;
/* there are 4 phantom points (do we need this?) */
n_twilight += 4;
error = tt_glyphzone_new( memory, n_twilight, 0, &size->twilight );
if ( error )
goto Exit;
size->twilight.n_points = n_twilight;
size->GS = tt_default_graphics_state;
/* set `face->interpreter' according to the debug hook present */
{
FT_Library library = face->root.driver->root.library;
face->interpreter = (TT_Interpreter)
library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE];
if ( !face->interpreter )
face->interpreter = (TT_Interpreter)TT_RunIns;
}
/* Fine, now run the font program! */
/* In case of an error while executing `fpgm', we intentionally don't */
/* clean up immediately – bugs in the `fpgm' are so fundamental that */
/* all following hinting calls should fail. Additionally, `fpgm' is */
/* to be executed just once; calling it again is completely useless */
/* and might even lead to extremely slow behaviour if it is malformed */
/* (containing an infinite loop, for example). */
error = tt_size_run_fpgm( size, pedantic );
return error;
Exit:
if ( error )
tt_size_done_bytecode( ftsize );
return error;
}
FT_LOCAL_DEF( FT_Error )
tt_size_ready_bytecode( TT_Size size,
FT_Bool pedantic )
{
FT_Error error = FT_Err_Ok;
if ( size->bytecode_ready < 0 )
error = tt_size_init_bytecode( (FT_Size)size, pedantic );
if ( error || size->bytecode_ready )
goto Exit;
/* rescale CVT when needed */
if ( size->cvt_ready < 0 )
{
FT_UInt i;
TT_Face face = (TT_Face)size->root.face;
/* Scale the cvt values to the new ppem. */
/* We use by default the y ppem to scale the CVT. */
for ( i = 0; i < size->cvt_size; i++ )
size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale );
/* all twilight points are originally zero */
for ( i = 0; i < (FT_UInt)size->twilight.n_points; i++ )
{
size->twilight.org[i].x = 0;
size->twilight.org[i].y = 0;
size->twilight.cur[i].x = 0;
size->twilight.cur[i].y = 0;
}
/* clear storage area */
for ( i = 0; i < (FT_UInt)size->storage_size; i++ )
size->storage[i] = 0;
size->GS = tt_default_graphics_state;
error = tt_size_run_prep( size, pedantic );
}
Exit:
return error;
}
#endif /* TT_USE_BYTECODE_INTERPRETER */
/*************************************************************************/
/* */
/* <Function> */
/* tt_size_init */
/* */
/* <Description> */
/* Initialize a new TrueType size object. */
/* */
/* <InOut> */
/* size :: A handle to the size object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_size_init( FT_Size ttsize ) /* TT_Size */
{
TT_Size size = (TT_Size)ttsize;
FT_Error error = FT_Err_Ok;
#ifdef TT_USE_BYTECODE_INTERPRETER
size->bytecode_ready = -1;
size->cvt_ready = -1;
#endif
size->ttmetrics.valid = FALSE;
size->strike_index = 0xFFFFFFFFUL;
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_size_done */
/* */
/* <Description> */
/* The TrueType size object finalizer. */
/* */
/* <Input> */
/* size :: A handle to the target size object. */
/* */
FT_LOCAL_DEF( void )
tt_size_done( FT_Size ttsize ) /* TT_Size */
{
TT_Size size = (TT_Size)ttsize;
#ifdef TT_USE_BYTECODE_INTERPRETER
tt_size_done_bytecode( ttsize );
#endif
size->ttmetrics.valid = FALSE;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_size_reset */
/* */
/* <Description> */
/* Reset a TrueType size when resolutions and character dimensions */
/* have been changed. */
/* */
/* <Input> */
/* size :: A handle to the target size object. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_size_reset( TT_Size size )
{
TT_Face face;
FT_Error error = FT_Err_Ok;
FT_Size_Metrics* metrics;
size->ttmetrics.valid = FALSE;
face = (TT_Face)size->root.face;
metrics = &size->metrics;
/* copy the result from base layer */
*metrics = size->root.metrics;
if ( metrics->x_ppem < 1 || metrics->y_ppem < 1 )
return FT_THROW( Invalid_PPem );
/* This bit flag, if set, indicates that the ppems must be */
/* rounded to integers. Nearly all TrueType fonts have this bit */
/* set, as hinting won't work really well otherwise. */
/* */
if ( face->header.Flags & 8 )
{
metrics->x_scale = FT_DivFix( metrics->x_ppem << 6,
face->root.units_per_EM );
metrics->y_scale = FT_DivFix( metrics->y_ppem << 6,
face->root.units_per_EM );
metrics->ascender =
FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) );
metrics->descender =
FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) );
metrics->height =
FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) );
metrics->max_advance =
FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width,
metrics->x_scale ) );
}
/* compute new transformation */
if ( metrics->x_ppem >= metrics->y_ppem )
{
size->ttmetrics.scale = metrics->x_scale;
size->ttmetrics.ppem = metrics->x_ppem;
size->ttmetrics.x_ratio = 0x10000L;
size->ttmetrics.y_ratio = FT_DivFix( metrics->y_ppem,
metrics->x_ppem );
}
else
{
size->ttmetrics.scale = metrics->y_scale;
size->ttmetrics.ppem = metrics->y_ppem;
size->ttmetrics.x_ratio = FT_DivFix( metrics->x_ppem,
metrics->y_ppem );
size->ttmetrics.y_ratio = 0x10000L;
}
#ifdef TT_USE_BYTECODE_INTERPRETER
size->cvt_ready = -1;
#endif /* TT_USE_BYTECODE_INTERPRETER */
if ( !error )
size->ttmetrics.valid = TRUE;
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_driver_init */
/* */
/* <Description> */
/* Initialize a given TrueType driver object. */
/* */
/* <Input> */
/* driver :: A handle to the target driver object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_driver_init( FT_Module ttdriver ) /* TT_Driver */
{
#ifdef TT_USE_BYTECODE_INTERPRETER
TT_Driver driver = (TT_Driver)ttdriver;
#ifdef TT_CONFIG_OPTION_SUBPIXEL_HINTING
driver->interpreter_version = TT_INTERPRETER_VERSION_38;
#else
driver->interpreter_version = TT_INTERPRETER_VERSION_35;
#endif
#else /* !TT_USE_BYTECODE_INTERPRETER */
FT_UNUSED( ttdriver );
#endif /* !TT_USE_BYTECODE_INTERPRETER */
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_driver_done */
/* */
/* <Description> */
/* Finalize a given TrueType driver. */
/* */
/* <Input> */
/* driver :: A handle to the target TrueType driver. */
/* */
FT_LOCAL_DEF( void )
tt_driver_done( FT_Module ttdriver ) /* TT_Driver */
{
FT_UNUSED( ttdriver );
}
/*************************************************************************/
/* */
/* <Function> */
/* tt_slot_init */
/* */
/* <Description> */
/* Initialize a new slot object. */
/* */
/* <InOut> */
/* slot :: A handle to the slot object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
tt_slot_init( FT_GlyphSlot slot )
{
return FT_GlyphLoader_CreateExtra( slot->internal->loader );
}
/* END */
src/type1/t1objs.c
Not Covered Functions
/***************************************************************************/
/* */
/* t1objs.c */
/* */
/* Type 1 objects manager (body). */
/* */
/* Copyright 1996-2016 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 <ft2build.h>
#include FT_INTERNAL_CALC_H
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_TRUETYPE_IDS_H
#include "t1gload.h"
#include "t1load.h"
#include "t1errors.h"
#ifndef T1_CONFIG_OPTION_NO_AFM
#include "t1afm.h"
#endif
#include FT_SERVICE_POSTSCRIPT_CMAPS_H
#include FT_INTERNAL_POSTSCRIPT_AUX_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_t1objs
/*************************************************************************/
/* */
/* SIZE FUNCTIONS */
/* */
/* note that we store the global hints in the size's "internal" root */
/* field */
/* */
/*************************************************************************/
static PSH_Globals_Funcs
T1_Size_Get_Globals_Funcs( T1_Size size )
{
T1_Face face = (T1_Face)size->root.face;
PSHinter_Service pshinter = (PSHinter_Service)face->pshinter;
FT_Module module;
module = FT_Get_Module( size->root.face->driver->root.library,
"pshinter" );
return ( module && pshinter && pshinter->get_globals_funcs )
? pshinter->get_globals_funcs( module )
: 0;
}
FT_LOCAL_DEF( void )
T1_Size_Done( FT_Size t1size ) /* T1_Size */
{
T1_Size size = (T1_Size)t1size;
if ( size->root.internal )
{
PSH_Globals_Funcs funcs;
funcs = T1_Size_Get_Globals_Funcs( size );
if ( funcs )
funcs->destroy( (PSH_Globals)size->root.internal );
size->root.internal = NULL;
}
}
FT_LOCAL_DEF( FT_Error )
T1_Size_Init( FT_Size t1size ) /* T1_Size */
{
T1_Size size = (T1_Size)t1size;
FT_Error error = FT_Err_Ok;
PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size );
if ( funcs )
{
PSH_Globals globals;
T1_Face face = (T1_Face)size->root.face;
error = funcs->create( size->root.face->memory,
&face->type1.private_dict, &globals );
if ( !error )
size->root.internal = (FT_Size_Internal)(void*)globals;
}
return error;
}
FT_LOCAL_DEF( FT_Error )
T1_Size_Request( FT_Size t1size, /* T1_Size */
FT_Size_Request req )
{
T1_Size size = (T1_Size)t1size;
PSH_Globals_Funcs funcs = T1_Size_Get_Globals_Funcs( size );
FT_Request_Metrics( size->root.face, req );
if ( funcs )
funcs->set_scale( (PSH_Globals)size->root.internal,
size->root.metrics.x_scale,
size->root.metrics.y_scale,
0, 0 );
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* SLOT FUNCTIONS */
/* */
/*************************************************************************/
FT_LOCAL_DEF( void )
T1_GlyphSlot_Done( FT_GlyphSlot slot )
{
slot->internal->glyph_hints = NULL;
}
FT_LOCAL_DEF( FT_Error )
T1_GlyphSlot_Init( FT_GlyphSlot slot )
{
T1_Face face;
PSHinter_Service pshinter;
face = (T1_Face)slot->face;
pshinter = (PSHinter_Service)face->pshinter;
if ( pshinter )
{
FT_Module module;
module = FT_Get_Module( slot->face->driver->root.library,
"pshinter" );
if ( module )
{
T1_Hints_Funcs funcs;
funcs = pshinter->get_t1_funcs( module );
slot->internal->glyph_hints = (void*)funcs;
}
}
return 0;
}
/*************************************************************************/
/* */
/* FACE FUNCTIONS */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* <Function> */
/* T1_Face_Done */
/* */
/* <Description> */
/* The face object destructor. */
/* */
/* <Input> */
/* face :: A typeless pointer to the face object to destroy. */
/* */
FT_LOCAL_DEF( void )
T1_Face_Done( FT_Face t1face ) /* T1_Face */
{
T1_Face face = (T1_Face)t1face;
FT_Memory memory;
T1_Font type1;
if ( !face )
return;
memory = face->root.memory;
type1 = &face->type1;
#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
/* release multiple masters information */
FT_ASSERT( ( face->len_buildchar == 0 ) == ( face->buildchar == NULL ) );
if ( face->buildchar )
{
FT_FREE( face->buildchar );
face->buildchar = NULL;
face->len_buildchar = 0;
}
T1_Done_Blend( face );
face->blend = NULL;
#endif
/* release font info strings */
{
PS_FontInfo info = &type1->font_info;
FT_FREE( info->version );
FT_FREE( info->notice );
FT_FREE( info->full_name );
FT_FREE( info->family_name );
FT_FREE( info->weight );
}
/* release top dictionary */
FT_FREE( type1->charstrings_len );
FT_FREE( type1->charstrings );
FT_FREE( type1->glyph_names );
FT_FREE( type1->subrs );
FT_FREE( type1->subrs_len );
ft_hash_num_free( type1->subrs_hash, memory );
FT_FREE( type1->subrs_hash );
FT_FREE( type1->subrs_block );
FT_FREE( type1->charstrings_block );
FT_FREE( type1->glyph_names_block );
FT_FREE( type1->encoding.char_index );
FT_FREE( type1->encoding.char_name );
FT_FREE( type1->font_name );
#ifndef T1_CONFIG_OPTION_NO_AFM
/* release afm data if present */
if ( face->afm_data )
T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );
#endif
/* release unicode map, if any */
#if 0
FT_FREE( face->unicode_map_rec.maps );
face->unicode_map_rec.num_maps = 0;
face->unicode_map = NULL;
#endif
face->root.family_name = NULL;
face->root.style_name = NULL;
}
/*************************************************************************/
/* */
/* <Function> */
/* T1_Face_Init */
/* */
/* <Description> */
/* The face object constructor. */
/* */
/* <Input> */
/* stream :: input stream where to load font data. */
/* */
/* face_index :: The index of the font face in the resource. */
/* */
/* num_params :: Number of additional generic parameters. Ignored. */
/* */
/* params :: Additional generic parameters. Ignored. */
/* */
/* <InOut> */
/* face :: The face record to build. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
T1_Face_Init( FT_Stream stream,
FT_Face t1face, /* T1_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
T1_Face face = (T1_Face)t1face;
FT_Error error;
FT_Service_PsCMaps psnames;
PSAux_Service psaux;
T1_Font type1 = &face->type1;
PS_FontInfo info = &type1->font_info;
FT_UNUSED( num_params );
FT_UNUSED( params );
FT_UNUSED( stream );
face->root.num_faces = 1;
FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
face->psnames = psnames;
face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
"psaux" );
psaux = (PSAux_Service)face->psaux;
if ( !psaux )
{
FT_ERROR(( "T1_Face_Init: cannot access `psaux' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
face->pshinter = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
"pshinter" );
FT_TRACE2(( "Type 1 driver\n" ));
/* open the tokenizer; this will also check the font format */
error = T1_Open_Face( face );
if ( error )
goto Exit;
/* if we just wanted to check the format, leave successfully now */
if ( face_index < 0 )
goto Exit;
/* check the face index */
if ( ( face_index & 0xFFFF ) > 0 )
{
FT_ERROR(( "T1_Face_Init: invalid face index\n" ));
error = FT_THROW( Invalid_Argument );
goto Exit;
}
/* now load the font program into the face object */
/* initialize the face object fields */
/* set up root face fields */
{
FT_Face root = (FT_Face)&face->root;
root->num_glyphs = type1->num_glyphs;
root->face_index = 0;
root->face_flags |= FT_FACE_FLAG_SCALABLE |
FT_FACE_FLAG_HORIZONTAL |
FT_FACE_FLAG_GLYPH_NAMES |
FT_FACE_FLAG_HINTER;
if ( info->is_fixed_pitch )
root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
if ( face->blend )
root->face_flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
/* The following code to extract the family and the style is very */
/* simplistic and might get some things wrong. For a full-featured */
/* algorithm you might have a look at the whitepaper given at */
/* */
/* http://blogs.msdn.com/text/archive/2007/04/23/wpf-font-selection-model.aspx */
/* get style name -- be careful, some broken fonts only */
/* have a `/FontName' dictionary entry! */
root->family_name = info->family_name;
root->style_name = NULL;
if ( root->family_name )
{
char* full = info->full_name;
char* family = root->family_name;
if ( full )
{
FT_Bool the_same = TRUE;
while ( *full )
{
if ( *full == *family )
{
family++;
full++;
}
else
{
if ( *full == ' ' || *full == '-' )
full++;
else if ( *family == ' ' || *family == '-' )
family++;
else
{
the_same = FALSE;
if ( !*family )
root->style_name = full;
break;
}
}
}
if ( the_same )
root->style_name = (char *)"Regular";
}
}
else
{
/* do we have a `/FontName'? */
if ( type1->font_name )
root->family_name = type1->font_name;
}
if ( !root->style_name )
{
if ( info->weight )
root->style_name = info->weight;
else
/* assume `Regular' style because we don't know better */
root->style_name = (char *)"Regular";
}
/* compute style flags */
root->style_flags = 0;
if ( info->italic_angle )
root->style_flags |= FT_STYLE_FLAG_ITALIC;
if ( info->weight )
{
if ( !ft_strcmp( info->weight, "Bold" ) ||
!ft_strcmp( info->weight, "Black" ) )
root->style_flags |= FT_STYLE_FLAG_BOLD;
}
/* no embedded bitmap support */
root->num_fixed_sizes = 0;
root->available_sizes = NULL;
root->bbox.xMin = type1->font_bbox.xMin >> 16;
root->bbox.yMin = type1->font_bbox.yMin >> 16;
/* no `U' suffix here to 0xFFFF! */
root->bbox.xMax = ( type1->font_bbox.xMax + 0xFFFF ) >> 16;
root->bbox.yMax = ( type1->font_bbox.yMax + 0xFFFF ) >> 16;
/* Set units_per_EM if we didn't set it in t1_parse_font_matrix. */
if ( !root->units_per_EM )
root->units_per_EM = 1000;
root->ascender = (FT_Short)( root->bbox.yMax );
root->descender = (FT_Short)( root->bbox.yMin );
root->height = (FT_Short)( ( root->units_per_EM * 12 ) / 10 );
if ( root->height < root->ascender - root->descender )
root->height = (FT_Short)( root->ascender - root->descender );
/* now compute the maximum advance width */
root->max_advance_width =
(FT_Short)( root->bbox.xMax );
{
FT_Pos max_advance;
error = T1_Compute_Max_Advance( face, &max_advance );
/* in case of error, keep the standard width */
if ( !error )
root->max_advance_width = (FT_Short)FIXED_TO_INT( max_advance );
else
error = FT_Err_Ok; /* clear error */
}
root->max_advance_height = root->height;
root->underline_position = (FT_Short)info->underline_position;
root->underline_thickness = (FT_Short)info->underline_thickness;
}
{
FT_Face root = &face->root;
if ( psnames )
{
FT_CharMapRec charmap;
T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes;
FT_CMap_Class clazz;
charmap.face = root;
/* first of all, try to synthesize a Unicode charmap */
charmap.platform_id = TT_PLATFORM_MICROSOFT;
charmap.encoding_id = TT_MS_ID_UNICODE_CS;
charmap.encoding = FT_ENCODING_UNICODE;
error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL );
if ( error &&
FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) )
goto Exit;
error = FT_Err_Ok;
/* now, generate an Adobe Standard encoding when appropriate */
charmap.platform_id = TT_PLATFORM_ADOBE;
clazz = NULL;
switch ( type1->encoding_type )
{
case T1_ENCODING_TYPE_STANDARD:
charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
charmap.encoding_id = TT_ADOBE_ID_STANDARD;
clazz = cmap_classes->standard;
break;
case T1_ENCODING_TYPE_EXPERT:
charmap.encoding = FT_ENCODING_ADOBE_EXPERT;
charmap.encoding_id = TT_ADOBE_ID_EXPERT;
clazz = cmap_classes->expert;
break;
case T1_ENCODING_TYPE_ARRAY:
charmap.encoding = FT_ENCODING_ADOBE_CUSTOM;
charmap.encoding_id = TT_ADOBE_ID_CUSTOM;
clazz = cmap_classes->custom;
break;
case T1_ENCODING_TYPE_ISOLATIN1:
charmap.encoding = FT_ENCODING_ADOBE_LATIN_1;
charmap.encoding_id = TT_ADOBE_ID_LATIN_1;
clazz = cmap_classes->unicode;
break;
default:
;
}
if ( clazz )
error = FT_CMap_New( clazz, NULL, &charmap, NULL );
#if 0
/* Select default charmap */
if (root->num_charmaps)
root->charmap = root->charmaps[0];
#endif
}
}
Exit:
return error;
}
/*************************************************************************/
/* */
/* <Function> */
/* T1_Driver_Init */
/* */
/* <Description> */
/* Initializes a given Type 1 driver object. */
/* */
/* <Input> */
/* driver :: A handle to the target driver object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
T1_Driver_Init( FT_Module driver )
{
FT_UNUSED( driver );
return FT_Err_Ok;
}
/*************************************************************************/
/* */
/* <Function> */
/* T1_Driver_Done */
/* */
/* <Description> */
/* Finalizes a given Type 1 driver. */
/* */
/* <Input> */
/* driver :: A handle to the target Type 1 driver. */
/* */
FT_LOCAL_DEF( void )
T1_Driver_Done( FT_Module driver )
{
FT_UNUSED( driver );
}
/* END */
src/type42/t42objs.c
Not Covered Functions
/***************************************************************************/
/* */
/* t42objs.c */
/* */
/* Type 42 objects manager (body). */
/* */
/* Copyright 2002-2016 by */
/* Roberto Alameda. */
/* */
/* 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 "t42objs.h"
#include "t42parse.h"
#include "t42error.h"
#include FT_INTERNAL_DEBUG_H
#include FT_LIST_H
#include FT_TRUETYPE_IDS_H
#undef FT_COMPONENT
#define FT_COMPONENT trace_t42
static FT_Error
T42_Open_Face( T42_Face face )
{
T42_LoaderRec loader;
T42_Parser parser;
T1_Font type1 = &face->type1;
FT_Memory memory = face->root.memory;
FT_Error error;
PSAux_Service psaux = (PSAux_Service)face->psaux;
t42_loader_init( &loader, face );
parser = &loader.parser;
if ( FT_ALLOC( face->ttf_data, 12 ) )
goto Exit;
/* while parsing the font we always update `face->ttf_size' so that */
/* even in case of buggy data (which might lead to premature end of */
/* scanning without causing an error) the call to `FT_Open_Face' in */
/* `T42_Face_Init' passes the correct size */
face->ttf_size = 12;
error = t42_parser_init( parser,
face->root.stream,
memory,
psaux);
if ( error )
goto Exit;
error = t42_parse_dict( face, &loader,
parser->base_dict, parser->base_len );
if ( error )
goto Exit;
if ( type1->font_type != 42 )
{
FT_ERROR(( "T42_Open_Face: cannot handle FontType %d\n",
type1->font_type ));
error = FT_THROW( Unknown_File_Format );
goto Exit;
}
/* now, propagate the charstrings and glyphnames tables */
/* to the Type1 data */
type1->num_glyphs = loader.num_glyphs;
if ( !loader.charstrings.init )
{
FT_ERROR(( "T42_Open_Face: no charstrings array in face\n" ));
error = FT_THROW( Invalid_File_Format );
}
loader.charstrings.init = 0;
type1->charstrings_block = loader.charstrings.block;
type1->charstrings = loader.charstrings.elements;
type1->charstrings_len = loader.charstrings.lengths;
/* we copy the glyph names `block' and `elements' fields; */
/* the `lengths' field must be released later */
type1->glyph_names_block = loader.glyph_names.block;
type1->glyph_names = (FT_String**)loader.glyph_names.elements;
loader.glyph_names.block = NULL;
loader.glyph_names.elements = NULL;
/* we must now build type1.encoding when we have a custom array */
if ( type1->encoding_type == T1_ENCODING_TYPE_ARRAY )
{
FT_Int charcode, idx, min_char, max_char;
FT_Byte* glyph_name;
/* OK, we do the following: for each element in the encoding */
/* table, look up the index of the glyph having the same name */
/* as defined in the CharStrings array. */
/* The index is then stored in type1.encoding.char_index, and */
/* the name in type1.encoding.char_name */
min_char = 0;
max_char = 0;
charcode = 0;
for ( ; charcode < loader.encoding_table.max_elems; charcode++ )
{
FT_Byte* char_name;
type1->encoding.char_index[charcode] = 0;
type1->encoding.char_name [charcode] = (char *)".notdef";
char_name = loader.encoding_table.elements[charcode];
if ( char_name )
for ( idx = 0; idx < type1->num_glyphs; idx++ )
{
glyph_name = (FT_Byte*)type1->glyph_names[idx];
if ( ft_strcmp( (const char*)char_name,
(const char*)glyph_name ) == 0 )
{
type1->encoding.char_index[charcode] = (FT_UShort)idx;
type1->encoding.char_name [charcode] = (char*)glyph_name;
/* Change min/max encoded char only if glyph name is */
/* not /.notdef */
if ( ft_strcmp( (const char*)".notdef",
(const char*)glyph_name ) != 0 )
{
if ( charcode < min_char )
min_char = charcode;
if ( charcode >= max_char )
max_char = charcode + 1;
}
break;
}
}
}
type1->encoding.code_first = min_char;
type1->encoding.code_last = max_char;
type1->encoding.num_chars = loader.num_chars;
}
Exit:
t42_loader_done( &loader );
return error;
}
/***************** Driver Functions *************/
FT_LOCAL_DEF( FT_Error )
T42_Face_Init( FT_Stream stream,
FT_Face t42face, /* T42_Face */
FT_Int face_index,
FT_Int num_params,
FT_Parameter* params )
{
T42_Face face = (T42_Face)t42face;
FT_Error error;
FT_Service_PsCMaps psnames;
PSAux_Service psaux;
FT_Face root = (FT_Face)&face->root;
T1_Font type1 = &face->type1;
PS_FontInfo info = &type1->font_info;
FT_UNUSED( num_params );
FT_UNUSED( params );
FT_UNUSED( stream );
face->ttf_face = NULL;
face->root.num_faces = 1;
FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
face->psnames = psnames;
face->psaux = FT_Get_Module_Interface( FT_FACE_LIBRARY( face ),
"psaux" );
psaux = (PSAux_Service)face->psaux;
if ( !psaux )
{
FT_ERROR(( "T42_Face_Init: cannot access `psaux' module\n" ));
error = FT_THROW( Missing_Module );
goto Exit;
}
FT_TRACE2(( "Type 42 driver\n" ));
/* open the tokenizer, this will also check the font format */
error = T42_Open_Face( face );
if ( error )
goto Exit;
/* if we just wanted to check the format, leave successfully now */
if ( face_index < 0 )
goto Exit;
/* check the face index */
if ( ( face_index & 0xFFFF ) > 0 )
{
FT_ERROR(( "T42_Face_Init: invalid face index\n" ));
error = FT_THROW( Invalid_Argument );
goto Exit;
}
/* Now load the font program into the face object */
/* Init the face object fields */
/* Now set up root face fields */
root->num_glyphs = type1->num_glyphs;
root->num_charmaps = 0;
root->face_index = 0;
root->face_flags |= FT_FACE_FLAG_SCALABLE |
FT_FACE_FLAG_HORIZONTAL |
FT_FACE_FLAG_GLYPH_NAMES;
if ( info->is_fixed_pitch )
root->face_flags |= FT_FACE_FLAG_FIXED_WIDTH;
#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
root->face_flags |= FT_FACE_FLAG_HINTER;
#endif
/* XXX: TODO -- add kerning with .afm support */
/* get style name -- be careful, some broken fonts only */
/* have a `/FontName' dictionary entry! */
root->family_name = info->family_name;
/* assume "Regular" style if we don't know better */
root->style_name = (char *)"Regular";
if ( root->family_name )
{
char* full = info->full_name;
char* family = root->family_name;
if ( full )
{
while ( *full )
{
if ( *full == *family )
{
family++;
full++;
}
else
{
if ( *full == ' ' || *full == '-' )
full++;
else if ( *family == ' ' || *family == '-' )
family++;
else
{
if ( !*family )
root->style_name = full;
break;
}
}
}
}
}
else
{
/* do we have a `/FontName'? */
if ( type1->font_name )
root->family_name = type1->font_name;
}
/* no embedded bitmap support */
root->num_fixed_sizes = 0;
root->available_sizes = NULL;
/* Load the TTF font embedded in the T42 font */
{
FT_Open_Args args;
args.flags = FT_OPEN_MEMORY | FT_OPEN_DRIVER;
args.driver = FT_Get_Module( FT_FACE_LIBRARY( face ),
"truetype" );
args.memory_base = face->ttf_data;
args.memory_size = face->ttf_size;
if ( num_params )
{
args.flags |= FT_OPEN_PARAMS;
args.num_params = num_params;
args.params = params;
}
error = FT_Open_Face( FT_FACE_LIBRARY( face ),
&args, 0, &face->ttf_face );
}
if ( error )
goto Exit;
FT_Done_Size( face->ttf_face->size );
/* Ignore info in FontInfo dictionary and use the info from the */
/* loaded TTF font. The PostScript interpreter also ignores it. */
root->bbox = face->ttf_face->bbox;
root->units_per_EM = face->ttf_face->units_per_EM;
root->ascender = face->ttf_face->ascender;
root->descender = face->ttf_face->descender;
root->height = face->ttf_face->height;
root->max_advance_width = face->ttf_face->max_advance_width;
root->max_advance_height = face->ttf_face->max_advance_height;
root->underline_position = (FT_Short)info->underline_position;
root->underline_thickness = (FT_Short)info->underline_thickness;
/* compute style flags */
root->style_flags = 0;
if ( info->italic_angle )
root->style_flags |= FT_STYLE_FLAG_ITALIC;
if ( face->ttf_face->style_flags & FT_STYLE_FLAG_BOLD )
root->style_flags |= FT_STYLE_FLAG_BOLD;
if ( face->ttf_face->face_flags & FT_FACE_FLAG_VERTICAL )
root->face_flags |= FT_FACE_FLAG_VERTICAL;
{
if ( psnames )
{
FT_CharMapRec charmap;
T1_CMap_Classes cmap_classes = psaux->t1_cmap_classes;
FT_CMap_Class clazz;
charmap.face = root;
/* first of all, try to synthesize a Unicode charmap */
charmap.platform_id = TT_PLATFORM_MICROSOFT;
charmap.encoding_id = TT_MS_ID_UNICODE_CS;
charmap.encoding = FT_ENCODING_UNICODE;
error = FT_CMap_New( cmap_classes->unicode, NULL, &charmap, NULL );
if ( error &&
FT_ERR_NEQ( error, No_Unicode_Glyph_Name ) )
goto Exit;
error = FT_Err_Ok;
/* now, generate an Adobe Standard encoding when appropriate */
charmap.platform_id = TT_PLATFORM_ADOBE;
clazz = NULL;
switch ( type1->encoding_type )
{
case T1_ENCODING_TYPE_STANDARD:
charmap.encoding = FT_ENCODING_ADOBE_STANDARD;
charmap.encoding_id = TT_ADOBE_ID_STANDARD;
clazz = cmap_classes->standard;
break;
case T1_ENCODING_TYPE_EXPERT:
charmap.encoding = FT_ENCODING_ADOBE_EXPERT;
charmap.encoding_id = TT_ADOBE_ID_EXPERT;
clazz = cmap_classes->expert;
break;
case T1_ENCODING_TYPE_ARRAY:
charmap.encoding = FT_ENCODING_ADOBE_CUSTOM;
charmap.encoding_id = TT_ADOBE_ID_CUSTOM;
clazz = cmap_classes->custom;
break;
case T1_ENCODING_TYPE_ISOLATIN1:
charmap.encoding = FT_ENCODING_ADOBE_LATIN_1;
charmap.encoding_id = TT_ADOBE_ID_LATIN_1;
clazz = cmap_classes->unicode;
break;
default:
;
}
if ( clazz )
error = FT_CMap_New( clazz, NULL, &charmap, NULL );
#if 0
/* Select default charmap */
if ( root->num_charmaps )
root->charmap = root->charmaps[0];
#endif
}
}
Exit:
return error;
}
FT_LOCAL_DEF( void )
T42_Face_Done( FT_Face t42face )
{
T42_Face face = (T42_Face)t42face;
T1_Font type1;
PS_FontInfo info;
FT_Memory memory;
if ( !face )
return;
type1 = &face->type1;
info = &type1->font_info;
memory = face->root.memory;
/* delete internal ttf face prior to freeing face->ttf_data */
if ( face->ttf_face )
FT_Done_Face( face->ttf_face );
/* release font info strings */
FT_FREE( info->version );
FT_FREE( info->notice );
FT_FREE( info->full_name );
FT_FREE( info->family_name );
FT_FREE( info->weight );
/* release top dictionary */
FT_FREE( type1->charstrings_len );
FT_FREE( type1->charstrings );
FT_FREE( type1->glyph_names );
FT_FREE( type1->charstrings_block );
FT_FREE( type1->glyph_names_block );
FT_FREE( type1->encoding.char_index );
FT_FREE( type1->encoding.char_name );
FT_FREE( type1->font_name );
FT_FREE( face->ttf_data );
#if 0
/* release afm data if present */
if ( face->afm_data )
T1_Done_AFM( memory, (T1_AFM*)face->afm_data );
#endif
/* release unicode map, if any */
FT_FREE( face->unicode_map.maps );
face->unicode_map.num_maps = 0;
face->root.family_name = NULL;
face->root.style_name = NULL;
}
/*************************************************************************/
/* */
/* <Function> */
/* T42_Driver_Init */
/* */
/* <Description> */
/* Initializes a given Type 42 driver object. */
/* */
/* <Input> */
/* driver :: A handle to the target driver object. */
/* */
/* <Return> */
/* FreeType error code. 0 means success. */
/* */
FT_LOCAL_DEF( FT_Error )
T42_Driver_Init( FT_Module module ) /* T42_Driver */
{
T42_Driver driver = (T42_Driver)module;
FT_Module ttmodule;
ttmodule = FT_Get_Module( module->library, "truetype" );
if ( !ttmodule )
{
FT_ERROR(( "T42_Driver_Init: cannot access `truetype' module\n" ));
return FT_THROW( Missing_Module );
}
driver->ttclazz = (FT_Driver_Class)ttmodule->clazz;
return FT_Err_Ok;
}
FT_LOCAL_DEF( void )
T42_Driver_Done( FT_Module module )
{
FT_UNUSED( module );
}
FT_LOCAL_DEF( FT_Error )
T42_Size_Init( FT_Size size ) /* T42_Size */
{
T42_Size t42size = (T42_Size)size;
FT_Face face = size->face;
T42_Face t42face = (T42_Face)face;
FT_Size ttsize;
FT_Error error;
error = FT_New_Size( t42face->ttf_face, &ttsize );
t42size->ttsize = ttsize;
FT_Activate_Size( ttsize );
return error;
}
FT_LOCAL_DEF( FT_Error )
T42_Size_Request( FT_Size t42size, /* T42_Size */
FT_Size_Request req )
{
T42_Size size = (T42_Size)t42size;
T42_Face face = (T42_Face)t42size->face;
FT_Error error;
FT_Activate_Size( size->ttsize );
error = FT_Request_Size( face->ttf_face, req );
if ( !error )
t42size->metrics = face->ttf_face->size->metrics;
return error;
}
FT_LOCAL_DEF( FT_Error )
T42_Size_Select( FT_Size t42size, /* T42_Size */
FT_ULong strike_index )
{
T42_Size size = (T42_Size)t42size;
T42_Face face = (T42_Face)t42size->face;
FT_Error error;
FT_Activate_Size( size->ttsize );
error = FT_Select_Size( face->ttf_face, (FT_Int)strike_index );
if ( !error )
t42size->metrics = face->ttf_face->size->metrics;
return error;
}
FT_LOCAL_DEF( void )
T42_Size_Done( FT_Size t42size ) /* T42_Size */
{
T42_Size size = (T42_Size)t42size;
FT_Face face = t42size->face;
T42_Face t42face = (T42_Face)face;
FT_ListNode node;
node = FT_List_Find( &t42face->ttf_face->sizes_list, size->ttsize );
if ( node )
{
FT_Done_Size( size->ttsize );
size->ttsize = NULL;
}
}
FT_LOCAL_DEF( FT_Error )
T42_GlyphSlot_Init( FT_GlyphSlot t42slot ) /* T42_GlyphSlot */
{
T42_GlyphSlot slot = (T42_GlyphSlot)t42slot;
FT_Face face = t42slot->face;
T42_Face t42face = (T42_Face)face;
FT_GlyphSlot ttslot;
FT_Error error = FT_Err_Ok;
if ( face->glyph == NULL )
{
/* First glyph slot for this face */
slot->ttslot = t42face->ttf_face->glyph;
}
else
{
error = FT_New_GlyphSlot( t42face->ttf_face, &ttslot );
slot->ttslot = ttslot;
}
return error;
}
FT_LOCAL_DEF( void )
T42_GlyphSlot_Done( FT_GlyphSlot t42slot ) /* T42_GlyphSlot */
{
T42_GlyphSlot slot = (T42_GlyphSlot)t42slot;
FT_Done_GlyphSlot( slot->ttslot );
}
static void
t42_glyphslot_clear( FT_GlyphSlot slot )
{
/* free bitmap if needed */
ft_glyphslot_free_bitmap( slot );
/* clear all public fields in the glyph slot */
FT_ZERO( &slot->metrics );
FT_ZERO( &slot->outline );
FT_ZERO( &slot->bitmap );
slot->bitmap_left = 0;
slot->bitmap_top = 0;
slot->num_subglyphs = 0;
slot->subglyphs = NULL;
slot->control_data = NULL;
slot->control_len = 0;
slot->other = NULL;
slot->format = FT_GLYPH_FORMAT_NONE;
slot->linearHoriAdvance = 0;
slot->linearVertAdvance = 0;
}
FT_LOCAL_DEF( FT_Error )
T42_GlyphSlot_Load( FT_GlyphSlot glyph,
FT_Size size,
FT_UInt glyph_index,
FT_Int32 load_flags )
{
FT_Error error;
T42_GlyphSlot t42slot = (T42_GlyphSlot)glyph;
T42_Size t42size = (T42_Size)size;
T42_Face t42face = (T42_Face)size->face;
FT_Driver_Class ttclazz = ((T42_Driver)glyph->face->driver)->ttclazz;
FT_TRACE1(( "T42_GlyphSlot_Load: glyph index %d\n", glyph_index ));
/* map T42 glyph index to embedded TTF's glyph index */
glyph_index = (FT_UInt)ft_atol(
(const char *)t42face->type1.charstrings[glyph_index] );
t42_glyphslot_clear( t42slot->ttslot );
error = ttclazz->load_glyph( t42slot->ttslot,
t42size->ttsize,
glyph_index,
load_flags | FT_LOAD_NO_BITMAP );
if ( !error )
{
glyph->metrics = t42slot->ttslot->metrics;
glyph->linearHoriAdvance = t42slot->ttslot->linearHoriAdvance;
glyph->linearVertAdvance = t42slot->ttslot->linearVertAdvance;
glyph->format = t42slot->ttslot->format;
glyph->outline = t42slot->ttslot->outline;
glyph->bitmap = t42slot->ttslot->bitmap;
glyph->bitmap_left = t42slot->ttslot->bitmap_left;
glyph->bitmap_top = t42slot->ttslot->bitmap_top;
glyph->num_subglyphs = t42slot->ttslot->num_subglyphs;
glyph->subglyphs = t42slot->ttslot->subglyphs;
glyph->control_data = t42slot->ttslot->control_data;
glyph->control_len = t42slot->ttslot->control_len;
}
return error;
}
/* END */
Error reading file: : No such file or directory(2)
About
Coverage files:- freetype2_raster_san_cov_fuzzer.40728.sancov (2016-03-14 11:47:25.000000000)