From a83bc088ecd14f315d40bd50bbac7959ea2631f6 Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 18 Oct 2001 11:38:43 +0000 Subject: [PATCH] adding new postscript hinter --- include/freetype/internal/psglobal.h | 141 +++ include/freetype/internal/pshints.h | 529 +++++++++ src/pshinter/Jamfile | 23 + src/pshinter/pshalgo.h | 19 + src/pshinter/pshalgo1.c | 736 +++++++++++++ src/pshinter/pshalgo1.h | 100 ++ src/pshinter/pshalgo2.c | 1484 ++++++++++++++++++++++++++ src/pshinter/pshalgo2.h | 185 ++++ src/pshinter/pshglob.c | 553 ++++++++++ src/pshinter/pshglob.h | 171 +++ src/pshinter/pshinter.c | 29 + src/pshinter/pshmod.c | 116 ++ src/pshinter/pshmod.h | 37 + src/pshinter/pshrec.c | 1112 +++++++++++++++++++ src/pshinter/pshrec.h | 165 +++ 15 files changed, 5400 insertions(+) create mode 100644 include/freetype/internal/psglobal.h create mode 100644 include/freetype/internal/pshints.h create mode 100644 src/pshinter/Jamfile create mode 100644 src/pshinter/pshalgo.h create mode 100644 src/pshinter/pshalgo1.c create mode 100644 src/pshinter/pshalgo1.h create mode 100644 src/pshinter/pshalgo2.c create mode 100644 src/pshinter/pshalgo2.h create mode 100644 src/pshinter/pshglob.c create mode 100644 src/pshinter/pshglob.h create mode 100644 src/pshinter/pshinter.c create mode 100644 src/pshinter/pshmod.c create mode 100644 src/pshinter/pshmod.h create mode 100644 src/pshinter/pshrec.c create mode 100644 src/pshinter/pshrec.h diff --git a/include/freetype/internal/psglobal.h b/include/freetype/internal/psglobal.h new file mode 100644 index 000000000..9c961f598 --- /dev/null +++ b/include/freetype/internal/psglobal.h @@ -0,0 +1,141 @@ +#ifndef __PSGLOBALS_H__ +#define __PSGLOBALS_H__ + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** PUBLIC STRUCTURES & API *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + +#if 0 + /**************************************************************** + * + * @constant: PS_GLOBALS_MAX_BLUE_ZONES + * + * @description: + * the maximum number of blue zones in a font global hints + * structure. See @PS_Globals_BluesRec + */ +#define PS_GLOBALS_MAX_BLUE_ZONES 16 + + /**************************************************************** + * + * @constant: PS_GLOBALS_MAX_STD_WIDTHS + * + * @description: + * the maximum number of standard and snap widths in either the + * horizontal or vertical direction. See @PS_Globals_WidthsRec + */ +#define PS_GLOBALS_MAX_STD_WIDTHS 16 + + /**************************************************************** + * + * @type: PS_Globals + * + * @description: + * a handle to a @PS_GlobalsRec structure used to + * describe the global hints of a given font + */ + typedef struct PS_GlobalsRec_* PS_Globals; + + /**************************************************************** + * + * @struct: PS_Globals_BluesRec + * + * @description: + * a structure used to model the global blue zones of a given + * font + * + * @fields: + * count :: number of blue zones + * zones :: an array of (count*2) coordinates describing the zones + * + * count_family :: number of family blue zones + * zones_family :: an array of (count_family*2) coordinates describing + * the family blue zones + * + * scale :: the blue scale to be used (fixed float) + * shift :: the blue shift to be used + * fuzz :: the blue fuzz to be used + * + * @note: + * each blue zone is modeled by a (reference,overshoot) coordinate pair + * in the table. zones can be placed in any order.. + */ + typedef struct PS_Globals_BluesRec + { + FT_UInt count; + FT_Int16 zones[ 2*PS_GLOBALS_MAX_BLUE_ZONES ]; + + FT_UInt count_family; + FT_Int16 zones_family[ 2*PS_GLOBALS_MAX_BLUE_ZONES ]; + + FT_Fixed scale; + FT_Int16 shift; + FT_Int16 fuzz; + + } PS_Globals_BluesRec, *PS_Globals_Blues; + + + /**************************************************************** + * + * @type: PS_Global_Widths; + * + * @description: + * a handle to a @PS_Globals_WidthsRec structure used to model + * the global standard and snap widths in a given direction + */ + typedef struct PS_Globals_WidthsRec_* PS_Globals_Widths; + + + /**************************************************************** + * + * @struct: PS_Globals_WidthsRec + * + * @description: + * a structure used to model the global standard and snap widths + * in a given font + * + * @fields: + * count :: number of widths + * widths :: an array of 'count' widths in font units. + * + * @note: + * 'widths[0]' must be the standard width or height, while + * remaining elements of the array are snap widths or heights + */ + typedef struct PS_Globals_WidthsRec_ + { + FT_UInt count; + FT_Int16 widths[ PS_GLOBALS_MAX_STD_WIDTHS ]; + + } PS_Globals_WidthsRec; + + + /**************************************************************** + * + * @struct: PS_Globals_GlobalsRec + * + * @description: + * a structure used to model the global hints for a given font + * + * @fields: + * horizontal :: horizontal widths + * vertical :: vertical heights + * blues :: blue zones + */ + typedef struct PS_GlobalsRec_ + { + PS_Globals_WidthsRec horizontal; + PS_Globals_WidthsRec vertical; + PS_Globals_BluesRec blues; + + } PS_GlobalsRec; + + +#endif + /* */ + +#endif /* __PS_GLOBALS_H__ */ diff --git a/include/freetype/internal/pshints.h b/include/freetype/internal/pshints.h new file mode 100644 index 000000000..4384ad791 --- /dev/null +++ b/include/freetype/internal/pshints.h @@ -0,0 +1,529 @@ +/***************************************************************************/ +/* */ +/* pshints.h */ +/* */ +/* Interface to Postscript-specific (Type 1 and Type 2) hints */ +/* recorders. These are used to support native T1/T2 hints */ +/* in the "type1", "cid" and "cff" font drivers */ +/* */ +/* Copyright 2001 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. */ +/* */ +/***************************************************************************/ + +#ifndef __PSHINTS_H__ +#define __PSHINTS_H__ + +#include +#include FT_FREETYPE_H +#include FT_TYPE1_TABLES_H +#include FT_INTERNAL_POSTSCRIPT_GLOBALS_H + +FT_BEGIN_HEADER + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** INTERNAL REPRESENTATION OF GLOBALS *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + + typedef struct PSH_GlobalsRec_* PSH_Globals; + + typedef FT_Error (*PSH_Globals_NewFunc)( FT_Memory memory, + T1_Private* private_dict, + PSH_Globals* aglobals ); + + typedef FT_Error (*PSH_Globals_SetScaleFunc)( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ); + + typedef void (*PSH_Globals_DestroyFunc)( PSH_Globals globals ); + + typedef struct + { + PSH_Globals_NewFunc create; + PSH_Globals_SetScaleFunc set_scale; + PSH_Globals_DestroyFunc destroy; + + } PSH_Globals_FuncsRec, *PSH_Globals_Funcs; + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 1 HINTS RECORDER *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + + /************************************************************************ + * + * @type: T1_Hints + * + * @description: + * this is a handle to an opaque structure used to record glyph + * hints from a Type 1 character glyph character string. + * + * the methods used to operate on this object are defined by the + * @T1_Hints_FuncsRec structure. Recording glyph hints is normally + * achieved through the following scheme: + * + * - open a new hint recording session by calling the "open" + * method. This will rewind the recorder and prepare it for + * new input + * + * - for each hint found in the glyph charstring, call the + * corresponding method ("stem", "stem3" or "reset"). + * note that these functions do not return an error code + * + * - close the recording session by calling the "close" method + * it will return an error code if the hints were invalid or + * something strange happened (e.g. memory shortage) + * + * the hints accumulated in the object can later be used by the + * Postscript hinter + */ + typedef struct T1_HintsRec_* T1_Hints; + + /************************************************************************ + * + * @type: T1_Hints_Funcs + * + * @description: + * a pointer to the @T1_Hints_FuncsRec structure that defines the + * API of a given @T1_Hints object + */ + typedef const struct T1_Hints_FuncsRec_* T1_Hints_Funcs; + + + /************************************************************************ + * + * @functype: T1_Hints_OpenFunc + * + * @description: + * a method of the @T1_Hints class used to prepare it for a new + * Type 1 hints recording session + * + * @input: + * hints :: handle to Type 1 hints recorder + * + * @note: + * You should always call the @T1_Hints_CloseFunc method in order + * to close an opened recording session + */ + typedef void (*T1_Hints_OpenFunc) ( T1_Hints hints ); + + /************************************************************************ + * + * @functype: T1_Hints_SetStemFunc + * + * @description: + * a method of the @T1_Hints class used to record a new horizontal or + * vertical stem. This corresponds to the Type 1 "hstem" and "vstem" + * operators + * + * @input: + * hints :: handle to Type 1 hints recorder + * dimension :: 0 for horizontal stems (hstem), 1 for vertical ones (vstem) + * coords :: array of 2 integers, used as (position,length) stem descriptor + * + * @note: + * use vertical coordinates (y) for horizontal stems (dim=0) + * use horizontal coordinates (x) for vertical stems (dim=1) + * + * "coords[0]" is the absolute stem position (lowest coordinate) + * "coords[1]" is the length. + * + * the length can be negative, in which case it must be either + * -20 or -21 in order and will be interpreted as a "ghost" stem, + * according to the Type 1 specification. + * + * if the length is -21 (corresponding to a bottom ghost stem), then + * the real stem position is "coords[0]+coords[1]" + */ + typedef void (*T1_Hints_SetStemFunc) ( T1_Hints hints, + FT_UInt dimension, + FT_Long* coords ); + + /************************************************************************ + * + * @functype: T1_Hints_SetStem3Func + * + * @description: + * a method of the @T1_Hints class used to record three counter-controlled + * horizontal or vertical stems at once + * + * @input: + * hints :: handle to Type 1 hints recorder + * dimension :: 0 for horizontal stems, 1 for vertical ones + * coords :: array of 6 integers, i.e. 3 (position,length) pairs + * for the counter-controlled stems + * + * @note: + * use vertical coordinates (y) for horizontal stems (dim=0) + * use horizontal coordinates (x) for vertical stems (dim=1) + * + * the lengths cannot be negative (ghost stems are never counter-controlled) + */ + typedef void (*T1_Hints_SetStem3Func) ( T1_Hints hints, + FT_UInt dimension, + FT_Long* coords ); + + /************************************************************************ + * + * @functype: T1_Hints_ResetFunc + * + * @description: + * a method of the @T1_Hints class used to reset the stems hints + * in a recording session. This is equivalent to the Type 1 ... + * + * @input: + * hints :: handle to Type 1 hints recorder + * end_point :: index of last point in the input glyph in which + * the previously defined hints apply + */ + typedef void (*T1_Hints_ResetFunc)( T1_Hints hints, + FT_UInt end_point ); + + /************************************************************************ + * + * @functype: T1_Hints_CloseFunc + * + * @description: + * a method of the @T1_Hints class used to close a hint recording + * session. + * + * @input: + * hints :: handle to Type 1 hints recorder + * end_point :: index of last point in the input glyph + * + * @return: + * error code. 0 means success + * + * @note: + * the error code will be set to indicate that an error occured + * during the recording session + */ + typedef FT_Error (*T1_Hints_CloseFunc)( T1_Hints hints, + FT_UInt end_point ); + + + /************************************************************************ + * + * @functype: T1_Hints_ApplyFunc + * + * @description: + * a method of the @T1_Hints class used to apply hints to the + * corresponding glyph outline. Must be called once all hints + * have been recorded. + * + * @input: + * hints :: handle to Type 1 hints recorder + * outline :: pointer to target outline descriptor + * globals :: the hinter globals for this font + * + * @return: + * error code. 0 means success + * + * @note: + * on input, all points within the outline are in font coordinates. + * on output, they're in 1/64th of pixels. + * + * the scaling transform is taken from the "globals" object, which + * must correspond to the same font than the glyph + */ + typedef FT_Error (*T1_Hints_ApplyFunc)( T1_Hints hints, + FT_Outline* outline, + PSH_Globals globals ); + + + /************************************************************************ + * + * @struct: T1_Hints_FuncsRec + * + * @description: + * the structure used to provide the API to @T1_Hints objects + * + * @fields: + * hints :: handle to T1 Hints recorder + * open :: open recording session + * close :: close recording session + * stem :: set simple stem + * stem3 :: set counter-controlled stems + * reset :: reset stem hints + * apply :: apply the hints to the corresponding glyph outline + */ + typedef struct T1_Hints_FuncsRec_ + { + T1_Hints hints; + T1_Hints_OpenFunc open; + T1_Hints_CloseFunc close; + T1_Hints_SetStemFunc stem; + T1_Hints_SetStem3Func stem3; + T1_Hints_ResetFunc reset; + T1_Hints_ApplyFunc apply; + + } T1_Hints_FuncsRec; + + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** PUBLIC TYPE 2 HINTS RECORDER *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + + + /************************************************************************ + * + * @type: T2_Hints + * + * @description: + * this is a handle to an opaque structure used to record glyph + * hints from a Type 2 character glyph character string. + * + * the methods used to operate on this object are defined by the + * @T2_Hints_FuncsRec structure. Recording glyph hints is normally + * achieved through the following scheme: + * + * - open a new hint recording session by calling the "open" + * method. This will rewind the recorder and prepare it for + * new input + * + * - for each hint found in the glyph charstring, call the + * corresponding method ("stems", "hintmask", "counters"). + * note that these functions do not return an error code + * + * - close the recording session by calling the "close" method + * it will return an error code if the hints were invalid or + * something strange happened (e.g. memory shortage) + * + * the hints accumulated in the object can later be used by the + * Postscript hinter + */ + typedef struct T2_HintsRec_* T2_Hints; + + /************************************************************************ + * + * @type: T2_Hints_Funcs + * + * @description: + * a pointer to the @T1_Hints_FuncsRec structure that defines the + * API of a given @T2_Hints object + */ + typedef const struct T2_Hints_FuncsRec_* T2_Hints_Funcs; + + /************************************************************************ + * + * @functype: T2_Hints_OpenFunc + * + * @description: + * a method of the @T2_Hints class used to prepare it for a new + * Type 2 hints recording session + * + * @input: + * hints :: handle to Type 2 hints recorder + * + * @note: + * You should always call the @T2_Hints_CloseFunc method in order + * to close an opened recording session + */ + typedef void (*T2_Hints_OpenFunc) ( T2_Hints hints ); + + + /************************************************************************ + * + * @functype: T2_Hints_StemsFunc + * + * @description: + * a method of the @T2_Hints class used to set the table of stems + * in either the vertical or horizontal dimension. Equivalent to the + * "hstem", "vstem", "hstemhm" and "vstemhm" Type 2 operators + * + * @input: + * hints :: handle to Type 2 hints recorder + * dimension :: 0 for horizontal stems (hstem), 1 for vertical ones (vstem) + * count :: number of stems + * coordinates :: an array of "count" (position,length) pairs + * + * @note: + * use vertical coordinates (y) for horizontal stems (dim=0) + * use horizontal coordinates (x) for vertical stems (dim=1) + * + * there are "2*count" elements in the "coordinates" array. Each + * even element is an absolute position in font units, each odd + * element is a length in font units + * + * a length can be negative, in which case it must be either + * -20 or -21 in order and will be interpreted as a "ghost" stem, + * according to the Type 1 specification. + */ + typedef void (*T2_Hints_StemsFunc) ( T2_Hints hints, + FT_UInt dimension, + FT_UInt count, + FT_Fixed* coordinates ); + + /************************************************************************ + * + * @functype: T2_Hints_MaskFunc + * + * @description: + * a method of the @T2_Hints class used to set a given hintmask + * (correspond to the "hintmask" Type 2 operator) + * + * @input: + * hints :: handle to Type 2 hints recorder + * end_point :: glyph index of the last point to which the previously + * defined/active hints apply. + * bit_count :: number of bits in the hint mask. + * bytes :: an array of bytes modelling the hint mask + * + * @note: + * if the hintmask starts the charstring (before any glyph point + * definition), the value of "end_point" should be 0 + * + * "bit_count" is the number of meaningful bits in the "bytes" array, + * and must be equal to the total number of hints defined so far + * (i.e. horizontal+verticals) + * + * the "bytes" array can come directly from the Type 2 charstring + * and respect the same format. + */ + typedef void (*T2_Hints_MaskFunc) ( T2_Hints hints, + FT_UInt end_point, + FT_UInt bit_count, + const FT_Byte* bytes ); + + /************************************************************************ + * + * @functype: T2_Hints_CounterFunc + * + * @description: + * a method of the @T2_Hints class used to set a given counter + * mask (correspond to the "hintmask" Type 2 operator) + * + * @input: + * hints :: handle to Type 2 hints recorder + * end_point :: glyph index of the last point to which the previously + * defined/active hints apply. + * bit_count :: number of bits in the hint mask. + * bytes :: an array of bytes modelling the hint mask + * + * @note: + * if the hintmask starts the charstring (before any glyph point + * definition), the value of "end_point" should be 0 + * + * "bit_count" is the number of meaningful bits in the "bytes" array, + * and must be equal to the total number of hints defined so far + * (i.e. horizontal+verticals) + * + * the "bytes" array can come directly from the Type 2 charstring + * and respect the same format. + */ + typedef void (*T2_Hints_CounterFunc)( T2_Hints hints, + FT_UInt bit_count, + const FT_Byte* bytes ); + + /************************************************************************ + * + * @functype: T2_Hints_CloseFunc + * + * @description: + * a method of the @T2_Hints class used to close a hint recording + * session. + * + * @input: + * hints :: handle to Type 2 hints recorder + * end_point :: index of last point in the input glyph + * + * @return: + * error code. 0 means success + * + * @note: + * the error code will be set to indicate that an error occured + * during the recording session + */ + typedef FT_Error (*T2_Hints_CloseFunc) ( T2_Hints hints, + FT_UInt end_point ); + + + /************************************************************************ + * + * @functype: T2_Hints_ApplyFunc + * + * @description: + * a method of the @T2_Hints class used to apply hints to the + * corresponding glyph outline. Must be called after the "close" method + * + * @input: + * hints :: handle to Type 2 hints recorder + * outline :: pointer to target outline descriptor + * globals :: the hinter globals for this font + * + * @return: + * error code. 0 means success + * + * @note: + * on input, all points within the outline are in font coordinates. + * on output, they're in 1/64th of pixels. + * + * the scaling transform is taken from the "globals" object, which + * must correspond to the same font than the glyph + */ + typedef FT_Error (*T2_Hints_ApplyFunc)( T2_Hints hints, + FT_Outline* outline, + PSH_Globals globals ); + + + /************************************************************************ + * + * @struct: T2_Hints_FuncsRec + * + * @description: + * the structure used to provide the API to @T2_Hints objects + * + * @fields: + * hints :: handle to T2 hints recorder object + * open :: open recording session + * close :: close recording session + * stems :: set dimension's stems table + * hintmask :: set hint masks + * counter :: set counter masks + * apply :: apply the hints on the corresponding glyph outline + */ + typedef struct T2_Hints_FuncsRec_ + { + T2_Hints hints; + T2_Hints_OpenFunc open; + T2_Hints_CloseFunc close; + T2_Hints_StemsFunc stems; + T2_Hints_MaskFunc hintmask; + T2_Hints_CounterFunc counter; + T2_Hints_ApplyFunc apply; + + } T2_Hints_FuncsRec; + + + /* */ + + typedef struct PSHinter_Interface_ + { + PSH_Globals_Funcs (*get_globals_funcs)( FT_Module module ); + T1_Hints_Funcs (*get_t1_funcs) ( FT_Module module ); + T2_Hints_Funcs (*get_t2_funcs) ( FT_Module module ); + + } PSHinter_Interface, *PSHinter_InterfacePtr; + +FT_END_HEADER + +#endif /* __PSHINTS_H__ */ diff --git a/src/pshinter/Jamfile b/src/pshinter/Jamfile new file mode 100644 index 000000000..3b749f171 --- /dev/null +++ b/src/pshinter/Jamfile @@ -0,0 +1,23 @@ +# FreeType 2 src/pshinter Jamfile (c) 2001 David Turner +# + +SubDir FT2_TOP src pshinter ; + +SubDirHdrs [ FT2_SubDir src pshinter ] ; + +{ + local _sources ; + + if $(FT2_MULTI) + { + _sources = pshrec pshglob pshalgo1 pshalgo2 pshmod ; + } + else + { + _sources = pshinter ; + } + + Library $(FT2_LIB) : $(_sources).c ; +} + +# end of src/psaux Jamfile diff --git a/src/pshinter/pshalgo.h b/src/pshinter/pshalgo.h new file mode 100644 index 000000000..641931124 --- /dev/null +++ b/src/pshinter/pshalgo.h @@ -0,0 +1,19 @@ +#ifndef __PS_HINTER_ALGO_H__ +#define __PS_HINTER_ALGO_H__ + +FT_BEGIN_HEADER + +/* define to choose hinting algorithm */ +#define PSH_ALGORITHM_2 + +#ifdef PSH_ALGORITHM_1 +# include "pshalgo1.h" +# define PS_HINTS_APPLY_FUNC ps1_hints_apply +#else +# include "pshalgo2.h" +# define PS_HINTS_APPLY_FUNC ps2_hints_apply +#endif + +FT_END_HEADER + +#endif /* __PS_HINTER_ALGO_H__ */ diff --git a/src/pshinter/pshalgo1.c b/src/pshinter/pshalgo1.c new file mode 100644 index 000000000..eaa82de3d --- /dev/null +++ b/src/pshinter/pshalgo1.c @@ -0,0 +1,736 @@ +#include +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include "pshalgo1.h" + +#ifdef DEBUG_HINTER + extern PSH1_Hint_Table ps1_debug_hint_table = 0; + extern PSH1_HintFunc ps1_debug_hint_func = 0; +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** BASIC HINTS RECORDINGS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + /* return true iff two stem hints overlap */ + static FT_Int + psh1_hint_overlap( PSH1_Hint hint1, + PSH1_Hint hint2 ) + { + return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && + hint2->org_pos + hint2->org_len >= hint1->org_pos ); + } + + + /* destroy hints table */ + static void + psh1_hint_table_done( PSH1_Hint_Table table, + FT_Memory memory ) + { + FREE( table->zones ); + table->num_zones = 0; + table->zone = 0; + + FREE( table->sort ); + FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + table->sort_global = 0; + } + + + /* deactivate all hints in a table */ + static void + psh1_hint_table_deactivate( PSH1_Hint_Table table ) + { + FT_UInt count = table->max_hints; + PSH1_Hint hint = table->hints; + + for ( ; count > 0; count--, hint++ ) + { + psh1_hint_deactivate(hint); + hint->order = -1; + } + } + + + /* internal function used to record a new hint */ + static void + psh1_hint_table_record( PSH1_Hint_Table table, + FT_UInt index ) + { + PSH1_Hint hint = table->hints + index; + + if ( index >= table->max_hints ) + { + FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); + return; + } + + /* ignore active hints */ + if ( psh1_hint_is_active(hint) ) + return; + + psh1_hint_activate(hint); + + /* now scan the current active hint set in order to determine */ + /* if we're overlapping with another segment.. */ + { + PSH1_Hint* sorted = table->sort_global; + FT_UInt count = table->num_hints; + PSH1_Hint hint2; + + hint->parent = 0; + for ( ; count > 0; count--, sorted++ ) + { + hint2 = sorted[0]; + + if ( psh1_hint_overlap( hint, hint2 ) ) + { + hint->parent = hint2; + break; + } + } + } + + if ( table->num_hints < table->max_hints ) + table->sort_global[ table->num_hints++ ] = hint; + else + { + FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", + "ps.fitter" )); + } + } + + + static void + psh1_hint_table_record_mask( PSH1_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit; + + limit = hint_mask->num_bits; + + if ( limit != table->max_hints ) + { + FT_ERROR(( "%s.activate_mask: invalid bit count (%d instead of %d)\n", + "ps.fitter", hint_mask->num_bits, table->max_hints )); + } + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + psh1_hint_table_record( table, index ); + + mask >>= 1; + } + } + + + /* create hints table */ + static FT_Error + psh1_hint_table_init( PSH1_Hint_Table table, + PS_Hint_Table hints, + PS_Mask_Table hint_masks, + PS_Mask_Table counter_masks, + FT_Memory memory ) + { + FT_UInt count = hints->num_hints; + FT_Error error; + + FT_UNUSED(counter_masks); + + /* allocate our tables */ + if ( ALLOC_ARRAY( table->sort, 2*count, PSH1_Hint ) || + ALLOC_ARRAY( table->hints, count, PSH1_HintRec ) || + ALLOC_ARRAY( table->zones, 2*count+1, PSH1_ZoneRec ) ) + goto Exit; + + table->max_hints = count; + table->sort_global = table->sort + count; + table->num_hints = 0; + table->num_zones = 0; + table->zone = 0; + + /* now, initialise the "hints" array */ + { + PSH1_Hint write = table->hints; + PS_Hint read = hints->hints; + + for ( ; count > 0; count--, write++, read++ ) + { + write->org_pos = read->pos; + write->org_len = read->len; + write->flags = read->flags; + } + } + + /* we now need to determine the initial "parent" stems, first */ + /* activate the hints that are given by the initial hint masks */ + if ( hint_masks ) + { + FT_UInt count = hint_masks->num_masks; + PS_Mask mask = hint_masks->masks; + + table->hint_masks = hint_masks; + + for ( ; count > 0; count--, mask++ ) + psh1_hint_table_record_mask( table, mask ); + } + + /* now, do a linear parse in case some hints were left alone */ + if ( table->num_hints != table->max_hints ) + { + FT_UInt index, count; + + FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); + count = table->max_hints; + for ( index = 0; index < count; index++ ) + psh1_hint_table_record( table, index ); + } + + Exit: + return error; + } + + + + static void + psh1_hint_table_activate_mask( PSH1_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit, count; + + limit = hint_mask->num_bits; + count = 0; + + psh1_hint_table_deactivate( table ); + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + { + PSH1_Hint hint = &table->hints[index]; + + if ( !psh1_hint_is_active(hint) ) + { + PSH1_Hint* sort = table->sort; + FT_UInt count2; + PSH1_Hint hint2; + + for ( count2 = count; count2 > 0; count2--, sort++ ) + { + hint2 = sort[0]; + if ( psh1_hint_overlap( hint, hint2 ) ) + { + FT_ERROR(( "%s.activate_mask: found overlapping hints\n", + "psf.hint" )); + break; + } + } + + if ( count2 == 0 ) + { + psh1_hint_activate( hint ); + if ( count < table->max_hints ) + table->sort[count++] = hint; + else + { + FT_ERROR(( "%s.activate_mask: too many active hints\n", + "psf.hint" )); + } + } + } + } + + mask >>= 1; + } + table->num_hints = count; + + /* now, sort the hints, they're guaranteed to not overlap */ + /* so we can compare their "org_pos" field directly.. */ + { + FT_Int i1, i2; + PSH1_Hint hint1, hint2; + PSH1_Hint* sort = table->sort; + + /* a simple bubble sort will do, since in 99% of cases, the hints */ + /* will be already sorted.. and the sort will be linear */ + for ( i1 = 1; i1 < (FT_Int)count; i1++ ) + { + hint1 = sort[i1]; + for ( i2 = i1-1; i2 >= 0; i2-- ) + { + hint2 = sort[i2]; + if ( hint2->org_pos < hint1->org_pos ) + break; + + sort[i1] = hint2; + sort[i2] = hint1; + } + } + } + } + + + + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTS GRID-FITTING AND OPTIMISATION *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#ifdef DEBUG_HINTER + void + ps_simple_scale( PSH1_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta, + FT_Bool vertical ) + { + PSH1_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->num_hints; count++ ) + { + hint = table->sort[count]; + if ( psh1_hint_is_active(hint) ) + { + hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; + hint->cur_len = FT_MulFix( hint->org_len, scale ); + + if (ps1_debug_hint_func) + ps1_debug_hint_func( hint, vertical ); + } + } + } +#endif + + FT_LOCAL_DEF FT_Error + psh1_hint_table_optimize( PSH1_Hint_Table table, + PSH_Globals globals, + FT_Outline* outline, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + +#ifdef DEBUG_HINTER + if ( ps_debug_no_vert_hints && vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return 0; + } + + if ( ps_debug_no_horz_hints && !vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return 0; + } +#endif + + /* XXXX: for now, we only scale the hints to test all other aspects */ + /* of the Postscript Hinter.. */ + { + PSH1_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->num_hints; count++ ) + { + hint = table->sort[count]; + if ( psh1_hint_is_active(hint) ) + { +# if 1 + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_center; + FT_Pos fit_len; + + PSH_AlignmentRec align; + + /* compute fitted width/height */ + fit_len = psh_dimension_snap_width( dim, hint->org_len ); + if ( fit_len < 64 ) + fit_len = 64; + else + fit_len = (fit_len + 32 ) & -64; + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = 0; + if (!vertical) + { + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + } + + switch (align.align) + { + case PSH_BLUE_ALIGN_TOP: + { + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + } + + case PSH_BLUE_ALIGN_BOT: + { + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + } + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + { + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + } + break; + + default: + /* normal processing */ + if ( (fit_len/64) & 1 ) + { + /* odd number of pixels */ + fit_center = ((pos + (len >> 1)) & -64) + 32; + } + else + { + /* even number of pixels */ + fit_center = (pos + (len >> 1) + 32) & -64; + } + + hint->cur_pos = fit_center - (fit_len >> 1); + } +# else + hint->cur_pos = (FT_MulFix( hint->org_pos, scale ) + delta + 32) & -64; + hint->cur_len = FT_MulFix( hint->org_len, scale ); +# endif + +#ifdef DEBUG_HINTER + if (ps1_debug_hint_func) + ps1_debug_hint_func( hint, vertical ); +#endif + } + } + } + + return 0; + } + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POINTS INTERPOLATION ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#define PSH1_ZONE_MIN -3200000 +#define PSH1_ZONE_MAX +3200000 + + +#define xxDEBUG_ZONES + +#ifdef DEBUG_ZONES + +#include + + static void + print_zone( PSH1_Zone zone ) + { + printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", + zone->scale/65536.0, + zone->delta/64.0, + zone->min, + zone->max ); + } + +#else +# define print_zone(x) do { } while (0) +#endif + + /* setup interpolation zones once the hints have been grid-fitted */ + /* by the optimizer.. */ + static void + psh1_hint_table_setup_zones( PSH1_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta ) + { + FT_UInt count; + PSH1_Zone zone; + PSH1_Hint *sort, hint, hint2; + + zone = table->zones; + + /* special case, no hints defined */ + if ( table->num_hints == 0 ) + { + zone->scale = scale; + zone->delta = delta; + zone->min = PSH1_ZONE_MIN; + zone->max = PSH1_ZONE_MAX; + + table->num_zones = 1; + table->zone = zone; + return; + } + + /* the first zone is before the first hint */ + /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ + sort = table->sort; + hint = sort[0]; + + zone->scale = scale; + zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); + zone->min = PSH1_ZONE_MIN; + zone->max = hint->org_pos; + + print_zone( zone ); + + zone++; + + for ( count = table->num_hints; count > 0; count-- ) + { + FT_Fixed scale2; + + if ( hint->org_len > 0 ) + { + /* setup a zone for inner-stem interpolation */ + /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ + /* x' = x*s2 + x0' - x0*s2 */ + + scale2 = FT_DivFix( hint->cur_len, hint->org_len ); + zone->scale = scale2; + zone->min = hint->org_pos; + zone->max = hint->org_pos + hint->org_len; + zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + } + + if ( count == 1 ) + break; + + sort++; + hint2 = sort[0]; + + /* setup zone for inter-stem interpolation */ + /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ + /* x' = x*s3 + x1' - x1*s3 */ + scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), + hint2->org_pos - (hint->org_pos + hint->org_len) ); + zone->scale = scale2; + zone->min = hint->org_pos + hint->org_len; + zone->max = hint2->org_pos; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + + hint = hint2; + } + + /* the last zone */ + zone->scale = scale; + zone->min = hint->org_pos + hint->org_len; + zone->max = PSH1_ZONE_MAX; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); + + print_zone( zone ); + + zone++; + + table->num_zones = zone - table->zones; + table->zone = table->zones; + } + + + /* tune a single coordinate with the current interpolation zones */ + static FT_Pos + psh1_hint_table_tune_coord( PSH1_Hint_Table table, + FT_Int coord ) + { + PSH1_Zone zone; + + zone = table->zone; + + if ( coord < zone->min ) + { + do + { + if ( zone == table->zones ) + break; + + zone--; + } + while ( coord < zone->min ); + table->zone = zone; + } + else if ( coord > zone->max ) + { + do + { + if ( zone == table->zones + table->num_zones - 1 ) + break; + + zone++; + } + while ( coord > zone->max ); + table->zone = zone; + } + + return FT_MulFix( coord, zone->scale ) + zone->delta; + } + + + /* tune a given outline with current interpolation zones */ + /* the function only works in a single dimension.. */ + static void + psh1_hint_table_tune_outline( PSH1_Hint_Table table, + FT_Outline* outline, + PSH_Globals globals, + FT_Bool vertical ) + + { + FT_UInt count, first, last; + PS_Mask_Table hint_masks = table->hint_masks; + PS_Mask mask; + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( hint_masks && hint_masks->num_masks > 0 ) + { + first = 0; + mask = hint_masks->masks; + count = hint_masks->num_masks; + for ( ; count > 0; count--, mask++ ) + { + last = mask->end_point; + + if ( last > first ) + { + FT_Vector* vec; + FT_Int count2; + + psh1_hint_table_activate_mask( table, mask ); + psh1_hint_table_optimize( table, globals, outline, vertical ); + psh1_hint_table_setup_zones( table, scale, delta ); + last = mask->end_point; + + vec = outline->points + first; + count2 = last - first; + for ( ; count2 > 0; count2--, vec++ ) + { + FT_Pos x, *px; + + px = vertical ? &vec->x : &vec->y; + x = *px; + + *px = psh1_hint_table_tune_coord( table, (FT_Int)x ); + } + } + + first = last; + } + } + else /* no hints in this glyph, simply scale the outline */ + { + FT_Vector* vec; + + vec = outline->points; + count = outline->n_points; + + if ( vertical ) + { + for ( ; count > 0; count--, vec++ ) + vec->x = FT_MulFix( vec->x, scale ) + delta; + } + else + { + for ( ; count > 0; count--, vec++ ) + vec->y = FT_MulFix( vec->y, scale ) + delta; + } + } + } + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HIGH-LEVEL INTERFACE *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + FT_Error + ps1_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ) + { + PSH1_Hint_TableRec hints; + FT_Error error; + FT_Int dimension; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + PS_Dimension dim = &ps_hints->dimension[dimension]; + + /* initialise hints table */ + memset( &hints, 0, sizeof(hints) ); + error = psh1_hint_table_init( &hints, + &dim->hints, + &dim->masks, + &dim->counters, + ps_hints->memory ); + if (error) goto Exit; + + psh1_hint_table_tune_outline( &hints, + outline, + globals, + dimension ); + + psh1_hint_table_done( &hints, ps_hints->memory ); + } + + Exit: + return error; + } diff --git a/src/pshinter/pshalgo1.h b/src/pshinter/pshalgo1.h new file mode 100644 index 000000000..1b0dd5dc2 --- /dev/null +++ b/src/pshinter/pshalgo1.h @@ -0,0 +1,100 @@ +/***************************************************************************/ +/* */ +/* pshalgo1.h */ +/* */ +/* First (basic) Postscript hinting routines */ +/* */ +/* Copyright 2001 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. */ +/* */ +/***************************************************************************/ + +#ifndef __PS_HINTER_ALGO1_H__ +#define __PS_HINTER_ALGO1_H__ + +#include "pshrec.h" + +FT_BEGIN_HEADER + + typedef struct PSH1_HintRec_* PSH1_Hint; + + typedef enum + { + PSH1_HINT_FLAG_GHOST = PS_HINT_FLAG_GHOST, + PSH1_HINT_FLAG_BOTTOM = PS_HINT_FLAG_BOTTOM, + PSH1_HINT_FLAG_ACTIVE = 4 + + } PSH1_Hint_Flags; + +#define psh1_hint_is_active(x) (((x)->flags & PSH1_HINT_FLAG_ACTIVE) != 0) +#define psh1_hint_is_ghost(x) (((x)->flags & PSH1_HINT_FLAG_GHOST ) != 0) + +#define psh1_hint_activate(x) (x)->flags |= PSH1_HINT_FLAG_ACTIVE +#define psh1_hint_deactivate(x) (x)->flags &= ~PSH1_HINT_FLAG_ACTIVE + + typedef struct PSH1_HintRec_ + { + FT_Int org_pos; + FT_Int org_len; + FT_Pos cur_pos; + FT_Pos cur_len; + + FT_UInt flags; + + PSH1_Hint parent; + FT_Int order; + + } PSH1_HintRec; + + + /* this is an interpolation zone used for strong points */ + /* weak points are interpolated according to their strong */ + /* neighbours.. */ + typedef struct PSH1_ZoneRec_ + { + FT_Fixed scale; + FT_Fixed delta; + FT_Pos min; + FT_Pos max; + + } PSH1_ZoneRec, *PSH1_Zone; + + + typedef struct PSH1_Hint_TableRec_ + { + FT_UInt max_hints; + FT_UInt num_hints; + PSH1_Hint hints; + PSH1_Hint* sort; + PSH1_Hint* sort_global; + FT_UInt num_zones; + PSH1_Zone zones; + PSH1_Zone zone; + PS_Mask_Table hint_masks; + PS_Mask_Table counter_masks; + + } PSH1_Hint_TableRec, *PSH1_Hint_Table; + + + extern FT_Error + ps1_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ); + + +#ifdef DEBUG_HINTER + extern PSH1_Hint_Table ps1_debug_hint_table; + + typedef void (*PSH1_HintFunc)( PSH1_Hint hint, FT_Bool vertical ); + extern PSH1_HintFunc ps1_debug_hint_func; +#endif + +FT_END_HEADER + +#endif /* __PS_HINTER_FITTER_H__ */ diff --git a/src/pshinter/pshalgo2.c b/src/pshinter/pshalgo2.c new file mode 100644 index 000000000..c98aa78d2 --- /dev/null +++ b/src/pshinter/pshalgo2.c @@ -0,0 +1,1484 @@ +#include +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include "pshalgo2.h" + + +#ifdef DEBUG_HINTER + extern PSH2_Hint_Table ps2_debug_hint_table = 0; + extern PSH2_HintFunc ps2_debug_hint_func = 0; + extern PSH2_Glyph ps2_debug_glyph = 0; +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** BASIC HINTS RECORDINGS *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + /* return true iff two stem hints overlap */ + static FT_Int + psh2_hint_overlap( PSH2_Hint hint1, + PSH2_Hint hint2 ) + { + return ( hint1->org_pos + hint1->org_len >= hint2->org_pos && + hint2->org_pos + hint2->org_len >= hint1->org_pos ); + } + + + /* destroy hints table */ + static void + psh2_hint_table_done( PSH2_Hint_Table table, + FT_Memory memory ) + { + FREE( table->zones ); + table->num_zones = 0; + table->zone = 0; + + FREE( table->sort ); + FREE( table->hints ); + table->num_hints = 0; + table->max_hints = 0; + table->sort_global = 0; + } + + + /* deactivate all hints in a table */ + static void + psh2_hint_table_deactivate( PSH2_Hint_Table table ) + { + FT_UInt count = table->max_hints; + PSH2_Hint hint = table->hints; + + for ( ; count > 0; count--, hint++ ) + { + psh2_hint_deactivate(hint); + hint->order = -1; + } + } + + + /* internal function used to record a new hint */ + static void + psh2_hint_table_record( PSH2_Hint_Table table, + FT_UInt index ) + { + PSH2_Hint hint = table->hints + index; + + if ( index >= table->max_hints ) + { + FT_ERROR(( "%s.activate: invalid hint index %d\n", index )); + return; + } + + /* ignore active hints */ + if ( psh2_hint_is_active(hint) ) + return; + + psh2_hint_activate(hint); + + /* now scan the current active hint set in order to determine */ + /* if we're overlapping with another segment.. */ + { + PSH2_Hint* sorted = table->sort_global; + FT_UInt count = table->num_hints; + PSH2_Hint hint2; + + hint->parent = 0; + for ( ; count > 0; count--, sorted++ ) + { + hint2 = sorted[0]; + + if ( psh2_hint_overlap( hint, hint2 ) ) + { + hint->parent = hint2; + break; + } + } + } + + if ( table->num_hints < table->max_hints ) + table->sort_global[ table->num_hints++ ] = hint; + else + { + FT_ERROR(( "%s.activate: too many sorted hints !! BUG !!\n", + "ps.fitter" )); + } + } + + + static void + psh2_hint_table_record_mask( PSH2_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit; + + limit = hint_mask->num_bits; + + if ( limit != table->max_hints ) + { + FT_ERROR(( "%s.activate_mask: invalid bit count (%d instead of %d)\n", + "ps.fitter", hint_mask->num_bits, table->max_hints )); + } + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + psh2_hint_table_record( table, index ); + + mask >>= 1; + } + } + + + /* create hints table */ + static FT_Error + psh2_hint_table_init( PSH2_Hint_Table table, + PS_Hint_Table hints, + PS_Mask_Table hint_masks, + PS_Mask_Table counter_masks, + FT_Memory memory ) + { + FT_UInt count = hints->num_hints; + FT_Error error; + + FT_UNUSED(counter_masks); + + /* allocate our tables */ + if ( ALLOC_ARRAY( table->sort, 2*count, PSH2_Hint ) || + ALLOC_ARRAY( table->hints, count, PSH2_HintRec ) || + ALLOC_ARRAY( table->zones, 2*count+1, PSH2_ZoneRec ) ) + goto Exit; + + table->max_hints = count; + table->sort_global = table->sort + count; + table->num_hints = 0; + table->num_zones = 0; + table->zone = 0; + + /* now, initialise the "hints" array */ + { + PSH2_Hint write = table->hints; + PS_Hint read = hints->hints; + + for ( ; count > 0; count--, write++, read++ ) + { + write->org_pos = read->pos; + write->org_len = read->len; + write->flags = read->flags; + } + } + + /* we now need to determine the initial "parent" stems, first */ + /* activate the hints that are given by the initial hint masks */ + if ( hint_masks ) + { + FT_UInt count = hint_masks->num_masks; + PS_Mask mask = hint_masks->masks; + + table->hint_masks = hint_masks; + + for ( ; count > 0; count--, mask++ ) + psh2_hint_table_record_mask( table, mask ); + } + + /* now, do a linear parse in case some hints were left alone */ + if ( table->num_hints != table->max_hints ) + { + FT_UInt index, count; + + FT_ERROR(( "%s.init: missing/incorrect hint masks !!\n" )); + count = table->max_hints; + for ( index = 0; index < count; index++ ) + psh2_hint_table_record( table, index ); + } + + Exit: + return error; + } + + + + static void + psh2_hint_table_activate_mask( PSH2_Hint_Table table, + PS_Mask hint_mask ) + { + FT_Int mask = 0, val = 0; + FT_Byte* cursor = hint_mask->bytes; + FT_UInt index, limit, count; + + limit = hint_mask->num_bits; + count = 0; + + psh2_hint_table_deactivate( table ); + + for ( index = 0; index < limit; index++ ) + { + if ( mask == 0 ) + { + val = *cursor++; + mask = 0x80; + } + + if ( val & mask ) + { + PSH2_Hint hint = &table->hints[index]; + + if ( !psh2_hint_is_active(hint) ) + { + PSH2_Hint* sort = table->sort; + FT_UInt count2; + PSH2_Hint hint2; + +#if 0 + for ( count2 = count; count2 > 0; count2--, sort++ ) + { + hint2 = sort[0]; + if ( psh2_hint_overlap( hint, hint2 ) ) + { + FT_ERROR(( "%s.activate_mask: found overlapping hints\n", + "psf.hint" )); + } + } +#else + count2 = 0; +#endif + + if ( count2 == 0 ) + { + psh2_hint_activate( hint ); + if ( count < table->max_hints ) + table->sort[count++] = hint; + else + { + FT_ERROR(( "%s.activate_mask: too many active hints\n", + "psf.hint" )); + } + } + } + } + + mask >>= 1; + } + table->num_hints = count; + + /* now, sort the hints, they're guaranteed to not overlap */ + /* so we can compare their "org_pos" field directly.. */ + { + FT_Int i1, i2; + PSH2_Hint hint1, hint2; + PSH2_Hint* sort = table->sort; + + /* a simple bubble sort will do, since in 99% of cases, the hints */ + /* will be already sorted.. and the sort will be linear */ + for ( i1 = 1; i1 < (FT_Int)count; i1++ ) + { + hint1 = sort[i1]; + for ( i2 = i1-1; i2 >= 0; i2-- ) + { + hint2 = sort[i2]; + if ( hint2->org_pos < hint1->org_pos ) + break; + + sort[i1] = hint2; + sort[i2] = hint1; + } + } + } + } + + + + + + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTS GRID-FITTING AND OPTIMISATION *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#ifdef DEBUG_HINTER + static void + ps_simple_scale( PSH2_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta, + FT_Bool vertical ) + { + PSH2_Hint hint; + FT_UInt count; + + for ( count = 0; count < table->max_hints; count++ ) + { + hint = table->hints + count; + + hint->cur_pos = FT_MulFix( hint->org_pos, scale ) + delta; + hint->cur_len = FT_MulFix( hint->org_len, scale ); + + if (ps2_debug_hint_func) + ps2_debug_hint_func( hint, vertical ); + } + } +#endif + + + static void + psh2_hint_align( PSH2_Hint hint, + PSH_Globals globals, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( !psh2_hint_is_fitted(hint) ) + { + FT_Pos pos = FT_MulFix( hint->org_pos, scale ) + delta; + FT_Pos len = FT_MulFix( hint->org_len, scale ); + + FT_Pos fit_center; + FT_Pos fit_len; + + PSH_AlignmentRec align; + + /* compute fitted width/height */ + fit_len = 0; + if ( hint->org_len ) + { + fit_len = psh_dimension_snap_width( dim, hint->org_len ); + if ( fit_len < 64 ) + fit_len = 64; + else + fit_len = (fit_len + 32 ) & -64; + } + + hint->cur_len = fit_len; + + /* check blue zones for horizontal stems */ + align.align = 0; + if (!vertical) + { + psh_blues_snap_stem( &globals->blues, + hint->org_pos + hint->org_len, + hint->org_pos, + &align ); + } + + switch (align.align) + { + case PSH_BLUE_ALIGN_TOP: + { + /* the top of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_top - fit_len; + break; + } + + case PSH_BLUE_ALIGN_BOT: + { + /* the bottom of the stem is aligned against a blue zone */ + hint->cur_pos = align.align_bot; + break; + } + + case PSH_BLUE_ALIGN_TOP | PSH_BLUE_ALIGN_BOT: + { + /* both edges of the stem are aligned against blue zones */ + hint->cur_pos = align.align_bot; + hint->cur_len = align.align_top - align.align_bot; + break; + } + + default: + { + PSH2_Hint parent = hint->parent; + + if ( parent ) + { + FT_Pos par_org_center, par_cur_center; + FT_Pos cur_org_center, cur_delta; + + /* ensure that parent is already fitted */ + if ( !psh2_hint_is_fitted(parent) ) + psh2_hint_align( parent, globals, vertical ); + + par_org_center = parent->org_pos + (parent->org_len/2); + par_cur_center = parent->cur_pos + (parent->cur_len/2); + cur_org_center = hint->org_pos + (hint->org_len/2); + + cur_delta = FT_MulFix( cur_org_center - par_org_center, scale ); +#if 0 + if ( cur_delta >= 0 ) + cur_delta = (cur_delta+16) & -64; + else + cur_delta = -((-cur_delta+16) & -64); +#endif + pos = par_cur_center + cur_delta - (len >> 1); + } + + /* normal processing */ + if ( (fit_len/64) & 1 ) + { + /* odd number of pixels */ + fit_center = ((pos + (len >> 1)) & -64) + 32; + } + else + { + /* even number of pixels */ + fit_center = (pos + (len >> 1) + 32) & -64; + } + + hint->cur_pos = fit_center - (fit_len >> 1); + } + } + + psh2_hint_set_fitted(hint); + +#ifdef DEBUG_HINTER + if (ps2_debug_hint_func) + ps2_debug_hint_func( hint, vertical ); +#endif + } + } + + + static void + psh2_hint_table_align_hints( PSH2_Hint_Table table, + PSH_Globals globals, + FT_Bool vertical ) + { + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + PSH2_Hint hint; + FT_UInt count; + +#ifdef DEBUG_HINTER + if ( ps_debug_no_vert_hints && vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return; + } + + if ( ps_debug_no_horz_hints && !vertical ) + { + ps_simple_scale( table, scale, delta, vertical ); + return; + } +#endif + + hint = table->hints; + count = table->max_hints; + for ( ; count > 0; count--, hint++ ) + psh2_hint_align( hint, globals, vertical ); + } + + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** POINTS INTERPOLATION ROUTINES *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + +#define PSH2_ZONE_MIN -3200000 +#define PSH2_ZONE_MAX +3200000 + + +#define xxDEBUG_ZONES + +#ifdef DEBUG_ZONES + +#include + + static void + print_zone( PSH2_Zone zone ) + { + printf( "zone [scale,delta,min,max] = [%.3f,%.3f,%d,%d]\n", + zone->scale/65536.0, + zone->delta/64.0, + zone->min, + zone->max ); + } + +#else +# define print_zone(x) do { } while (0) +#endif + + /* setup interpolation zones once the hints have been grid-fitted */ + /* by the optimizer.. */ + static void + psh2_hint_table_setup_zones( PSH2_Hint_Table table, + FT_Fixed scale, + FT_Fixed delta ) + { + FT_UInt count; + PSH2_Zone zone; + PSH2_Hint *sort, hint, hint2; + + zone = table->zones; + + /* special case, no hints defined */ + if ( table->num_hints == 0 ) + { + zone->scale = scale; + zone->delta = delta; + zone->min = PSH2_ZONE_MIN; + zone->max = PSH2_ZONE_MAX; + + table->num_zones = 1; + table->zone = zone; + return; + } + + /* the first zone is before the first hint */ + /* x' = (x-x0)*s + x0' = x*s + ( x0' - x0*s ) */ + sort = table->sort; + hint = sort[0]; + + zone->scale = scale; + zone->delta = hint->cur_pos - FT_MulFix( hint->org_pos, scale ); + zone->min = PSH2_ZONE_MIN; + zone->max = hint->org_pos; + + print_zone( zone ); + + zone++; + + for ( count = table->num_hints; count > 0; count-- ) + { + FT_Fixed scale2; + + if ( hint->org_len > 0 ) + { + /* setup a zone for inner-stem interpolation */ + /* (x' - x0') = (x - x0)*(x1'-x0')/(x1-x0) */ + /* x' = x*s2 + x0' - x0*s2 */ + + scale2 = FT_DivFix( hint->cur_len, hint->org_len ); + zone->scale = scale2; + zone->min = hint->org_pos; + zone->max = hint->org_pos + hint->org_len; + zone->delta = hint->cur_pos - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + } + + if ( count == 1 ) + break; + + sort++; + hint2 = sort[0]; + + /* setup zone for inter-stem interpolation */ + /* (x'-x1') = (x-x1)*(x2'-x1')/(x2-x1) */ + /* x' = x*s3 + x1' - x1*s3 */ + scale2 = FT_DivFix( hint2->cur_pos - (hint->cur_pos + hint->cur_len), + hint2->org_pos - (hint->org_pos + hint->org_len) ); + zone->scale = scale2; + zone->min = hint->org_pos + hint->org_len; + zone->max = hint2->org_pos; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale2 ); + + print_zone( zone ); + + zone++; + + hint = hint2; + } + + /* the last zone */ + zone->scale = scale; + zone->min = hint->org_pos + hint->org_len; + zone->max = PSH2_ZONE_MAX; + zone->delta = hint->cur_pos + hint->cur_len - FT_MulFix( zone->min, scale ); + + print_zone( zone ); + + zone++; + + table->num_zones = zone - table->zones; + table->zone = table->zones; + } + + + /* tune a single coordinate with the current interpolation zones */ + static FT_Pos + psh2_hint_table_tune_coord( PSH2_Hint_Table table, + FT_Int coord ) + { + PSH2_Zone zone; + + zone = table->zone; + + if ( coord < zone->min ) + { + do + { + if ( zone == table->zones ) + break; + + zone--; + } + while ( coord < zone->min ); + table->zone = zone; + } + else if ( coord > zone->max ) + { + do + { + if ( zone == table->zones + table->num_zones - 1 ) + break; + + zone++; + } + while ( coord > zone->max ); + table->zone = zone; + } + + return FT_MulFix( coord, zone->scale ) + zone->delta; + } + + +#if 0 + /* tune a given outline with current interpolation zones */ + /* the function only works in a single dimension.. */ + static void + psh2_hint_table_tune_outline( PSH2_Hint_Table table, + FT_Outline* outline, + PSH_Globals globals, + FT_Bool vertical ) + + { + FT_UInt count, first, last; + PS_Mask_Table hint_masks = table->hint_masks; + PS_Mask mask; + PSH_Dimension dim = &globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + if ( hint_masks && hint_masks->num_masks > 0 ) + { + first = 0; + mask = hint_masks->masks; + count = hint_masks->num_masks; + for ( ; count > 0; count--, mask++ ) + { + last = mask->end_point; + + if ( last > first ) + { + FT_Vector* vec; + FT_Int count2; + + psh2_hint_table_activate_mask( table, mask ); + psh2_hint_table_optimize( table, globals, outline, vertical ); + psh2_hint_table_setup_zones( table, scale, delta ); + last = mask->end_point; + + vec = outline->points + first; + count2 = last - first; + for ( ; count2 > 0; count2--, vec++ ) + { + FT_Pos x, *px; + + px = vertical ? &vec->x : &vec->y; + x = *px; + + *px = psh2_hint_table_tune_coord( table, (FT_Int)x ); + } + } + + first = last; + } + } + else /* no hints in this glyph, simply scale the outline */ + { + FT_Vector* vec; + + vec = outline->points; + count = outline->n_points; + + if ( vertical ) + { + for ( ; count > 0; count--, vec++ ) + vec->x = FT_MulFix( vec->x, scale ) + delta; + } + else + { + for ( ; count > 0; count--, vec++ ) + vec->y = FT_MulFix( vec->y, scale ) + delta; + } + } + } +#endif + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HINTER GLYPH MANAGEMENT *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + static int + psh2_point_is_extremum( PSH2_Point point ) + { + PSH2_Point before = point; + PSH2_Point after = point; + FT_Pos d_before; + FT_Pos d_after; + + do + { + before = before->prev; + if ( before == point ) + return 0; + + d_before = before->org_u - point->org_u; + } + while ( d_before == 0 ); + + do + { + after = after->next; + if ( after == point ) + return 0; + + d_after = after->org_u - point->org_u; + } + while ( d_after == 0 ); + + return ( ( d_before > 0 && d_after > 0 ) || + ( d_before < 0 && d_after < 0 ) ); + } + + + + static void + psh2_glyph_done( PSH2_Glyph glyph ) + { + FT_Memory memory = glyph->memory; + + psh2_hint_table_done( &glyph->hint_tables[1], memory ); + psh2_hint_table_done( &glyph->hint_tables[0], memory ); + + FREE( glyph->points ); + FREE( glyph->contours ); + + glyph->num_points = 0; + glyph->num_contours = 0; + + glyph->memory = 0; + } + + + static int + psh2_compute_dir( FT_Pos dx, FT_Pos dy ) + { + FT_Pos ax, ay; + int result = PSH2_DIR_NONE; + + ax = ( dx >= 0 ) ? dx : -dx; + ay = ( dy >= 0 ) ? dy : -dy; + + if ( ay*12 < ax ) + { + /* |dy| <<< |dx| means a near-horizontal segment */ + result = ( dx >= 0 ) ? PSH2_DIR_RIGHT : PSH2_DIR_LEFT; + } + else if ( ax*12 < ay ) + { + /* |dx| <<< |dy| means a near-vertical segment */ + result = ( dy >= 0 ) ? PSH2_DIR_UP : PSH2_DIR_DOWN; + } + return result; + } + + + static FT_Error + psh2_glyph_init( PSH2_Glyph glyph, + FT_Outline* outline, + PS_Hints ps_hints, + PSH_Globals globals ) + { + FT_Error error; + FT_Memory memory; + + /* clear all fields */ + memset( glyph, 0, sizeof(*glyph) ); + + memory = globals->memory; + + /* allocate and setup points + contours arrays */ + if ( ALLOC_ARRAY( glyph->points, outline->n_points, PSH2_PointRec ) || + ALLOC_ARRAY( glyph->contours, outline->n_contours, PSH2_ContourRec ) ) + goto Exit; + + glyph->num_points = outline->n_points; + glyph->num_contours = outline->n_contours; + + { + FT_UInt first = 0, next, n; + PSH2_Point points = glyph->points; + PSH2_Contour contour = glyph->contours; + + for ( n = 0; n < glyph->num_contours; n++ ) + { + FT_Int count; + PSH2_Point point; + + next = outline->contours[n] + 1; + count = next - first; + + contour->start = points + first; + contour->count = (FT_UInt)count; + + if ( count > 0 ) + { + point = points + first; + + point->prev = points + next - 1; + point->contour = contour; + for ( ; count > 1; count-- ) + { + point[0].next = point + 1; + point[1].prev = point; + point++; + point->contour = contour; + } + point->next = points + first; + } + + contour++; + first = next; + } + } + + { + PSH2_Point points = glyph->points; + PSH2_Point point = points; + FT_Vector* vec = outline->points; + FT_UInt n; + + for ( n = 0; n < glyph->num_points; n++, point++ ) + { + FT_Int n_prev = point->prev - points; + FT_Int n_next = point->next - points; + FT_Pos dxi, dyi, dxo, dyo; + + if ( !(outline->tags[n] & FT_Curve_Tag_On) ) + point->flags = PSH2_POINT_OFF; + + dxi = vec[n].x - vec[n_prev].x; + dyi = vec[n].y - vec[n_prev].y; + + point->dir_in = psh2_compute_dir( dxi, dyi ); + + dxo = vec[n_next].x - vec[n].x; + dyo = vec[n_next].y - vec[n].y; + + point->dir_out = psh2_compute_dir( dxo, dyo ); + + /* detect smooth points */ + if ( point->flags & PSH2_POINT_OFF ) + { + point->flags |= PSH2_POINT_SMOOTH; + } + else if ( point->dir_in != PSH2_DIR_NONE || + point->dir_out != PSH2_DIR_NONE ) + { + if ( point->dir_in == point->dir_out ) + point->flags |= PSH2_POINT_SMOOTH; + } + else + { + FT_Angle angle_in, angle_out, diff; + + angle_in = FT_Atan2( dxi, dyi ); + angle_out = FT_Atan2( dxo, dyo ); + + diff = angle_in - angle_out; + if ( diff < 0 ) + diff = -diff; + + if ( diff > FT_ANGLE_PI ) + diff = FT_ANGLE_2PI - diff; + + if ( (diff < FT_ANGLE_PI/16) ) + point->flags |= PSH2_POINT_SMOOTH; + } + } + } + + glyph->memory = memory; + glyph->outline = outline; + glyph->globals = globals; + + /* now deal with hints tables */ + error = psh2_hint_table_init( &glyph->hint_tables [0], + &ps_hints->dimension[0].hints, + &ps_hints->dimension[0].masks, + &ps_hints->dimension[0].counters, + memory ); + if (error) goto Exit; + + error = psh2_hint_table_init( &glyph->hint_tables [1], + &ps_hints->dimension[1].hints, + &ps_hints->dimension[1].masks, + &ps_hints->dimension[1].counters, + memory ); + if (error) goto Exit; + + Exit: + return error; + } + + + /* load outline point coordinates into hinter glyph */ + static void + psh2_glyph_load_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + FT_Vector* vec = glyph->outline->points; + PSH2_Point point = glyph->points; + FT_UInt count = glyph->num_points; + + for ( ; count > 0; count--, point++, vec++ ) + { + point->flags &= PSH2_POINT_OFF | PSH2_POINT_SMOOTH; + point->hint = 0; + if (vertical) + point->org_u = vec->x; + else + point->org_u = vec->y; + +#ifdef DEBUG_HINTER + point->org_x = vec->x; + point->org_y = vec->y; +#endif + } + } + + + /* save hinted point coordinates back to outline */ + static void + psh2_glyph_save_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + FT_UInt n; + PSH2_Point point = glyph->points; + FT_Vector* vec = glyph->outline->points; + char* tags = glyph->outline->tags; + + for ( n = 0; n < glyph->num_points; n++ ) + { + if (vertical) + vec[n].x = point->cur_u; + else + vec[n].y = point->cur_u; + + if ( psh2_point_is_strong(point) ) + tags[n] |= vertical ? 32 : 64; + +#ifdef DEBUG_HINTER + if (vertical) + { + point->cur_x = point->cur_u; + point->flags_x = point->flags; + } + else + { + point->cur_y = point->cur_u; + point->flags_y = point->flags; + } +#endif + point++; + } + } + + + static void + psh2_hint_table_find_strong_point( PSH2_Hint_Table table, + PSH2_Point point, + FT_Int major_dir ) + { + PSH2_Hint* sort = table->sort; + FT_UInt num_hints = table->num_hints; + + for ( ; num_hints > 0; num_hints--, sort++ ) + { + PSH2_Hint hint = sort[0]; + + if ( ABS(point->dir_in) == major_dir || + ABS(point->dir_out) == major_dir ) + { + FT_Pos d; + + d = point->org_u - hint->org_pos; + if ( ABS(d) < 3 ) + { + Is_Strong: + psh2_point_set_strong(point); + point->hint = hint; + break; + } + + d -= hint->org_len; + if ( ABS(d) < 3 ) + goto Is_Strong; + } + +#if 1 + if ( point->org_u >= hint->org_pos && + point->org_u <= hint->org_pos + hint->org_len && + psh2_point_is_extremum( point ) ) + { + /* attach to hint, but don't mark as strong */ + point->hint = hint; + break; + } +#endif + } + } + + + + /* find strong points in a glyph */ + static void + psh2_glyph_find_strong_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + /* a point is strong if it is located on a stem */ + /* edge and has an "in" or "out" tangent to the hint's direction */ + { + PSH2_Hint_Table table = &glyph->hint_tables[vertical]; + PS_Mask mask = table->hint_masks->masks; + FT_UInt num_masks = table->hint_masks->num_masks; + FT_UInt first = 0; + FT_Int major_dir = vertical ? PSH2_DIR_UP : PSH2_DIR_RIGHT; + FT_Int minor_dir = vertical ? PSH2_DIR_RIGHT : PSH2_DIR_UP; + + /* process secondary hints to "selected" points */ + if ( num_masks > 1 ) + { + mask++; + for ( ; num_masks > 1; num_masks--, mask++ ) + { + FT_UInt next; + FT_Int count; + + next = mask->end_point; + count = next - first; + if ( count > 0 ) + { + PSH2_Point point = glyph->points + first; + + psh2_hint_table_activate_mask( table, mask ); + + for ( ; count > 0; count--, point++ ) + psh2_hint_table_find_strong_point( table, point, major_dir ); + } + first = next; + } + } + + /* process primary hints for all points */ + if ( num_masks == 1 ) + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + psh2_hint_table_activate_mask( table, table->hint_masks->masks ); + for ( ; count > 0; count--, point++ ) + { + if ( !psh2_point_is_strong(point) ) + psh2_hint_table_find_strong_point( table, point, major_dir ); + } + } + + /* now, certain points may have been attached to hint and */ + /* not marked as strong, update their flags then.. */ + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + if ( point->hint && !psh2_point_is_strong(point) ) + psh2_point_set_strong(point); + } + } + } + + + + /* interpolate strong points with the help of hinted coordinates */ + static void + psh2_glyph_interpolate_strong_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + { + PSH2_Hint hint = point->hint; + + if ( hint ) + { + FT_Pos delta; + + delta = point->org_u - hint->org_pos; + + if ( delta <= 0 ) + point->cur_u = hint->cur_pos + FT_MulFix( delta, scale ); + + else if ( delta >= hint->org_len ) + point->cur_u = hint->cur_pos + hint->cur_len + + FT_MulFix( delta - hint->org_len, scale ); + + else if ( hint->org_len > 0 ) + point->cur_u = hint->cur_pos + + FT_MulDiv( delta, hint->cur_len, hint->org_len ); + else + point->cur_u = hint->cur_pos; + + psh2_point_set_fitted(point); + } + } + } + } + + + static void + psh2_glyph_interpolate_normal_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { +#if 1 + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + + /* first technique: a point is strong if it is a local extrema */ + { + FT_UInt count = glyph->num_points; + PSH2_Point point = glyph->points; + + for ( ; count > 0; count--, point++ ) + { + if ( psh2_point_is_strong(point) ) + continue; + + /* sometimes, some local extremas are smooth points */ + if ( psh2_point_is_smooth(point) ) + { + if ( point->dir_in == PSH2_DIR_NONE || + point->dir_in != point->dir_out ) + continue; + + if ( !psh2_point_is_extremum( point ) ) + continue; + + point->flags &= ~PSH2_POINT_SMOOTH; + } + + /* find best enclosing point coordinates */ + { + PSH2_Point before = 0; + PSH2_Point after = 0; + + FT_Pos diff_before = -32000; + FT_Pos diff_after = 32000; + FT_Pos u = point->org_u; + + FT_Int count2 = glyph->num_points; + PSH2_Point cur = glyph->points; + + for ( ; count2 > 0; count2--, cur++ ) + { + if ( psh2_point_is_strong(cur) ) + { + FT_Pos diff = cur->org_u - u;; + + if ( diff <= 0 ) + { + if ( diff > diff_before ) + { + diff_before = diff; + before = cur; + } + } + else if ( diff >= 0 ) + { + if ( diff < diff_after ) + { + diff_after = diff; + after = cur; + } + } + } + } + + if ( !before ) + { + if ( !after ) + continue; + + /* we're before the first strong point coordinate */ + /* simply translate the point.. */ + point->cur_u = after->cur_u + + FT_MulFix( point->org_u - after->org_u, scale ); + } + else if ( !after ) + { + /* we're after the last strong point coordinate */ + /* simply translate the point.. */ + point->cur_u = before->cur_u + + FT_MulFix( point->org_u - before->org_u, scale ); + } + else + { + if ( diff_before == 0 ) + point->cur_u = before->cur_u; + + else if ( diff_after == 0 ) + point->cur_u = after->cur_u; + + else + point->cur_u = before->cur_u + + FT_MulDiv( u - before->org_u, + after->cur_u - before->cur_u, + after->org_u - before->org_u ); + } + + psh2_point_set_fitted(point); + } + } + } +#endif + } + + + + /* interpolate other points */ + static void + psh2_glyph_interpolate_other_points( PSH2_Glyph glyph, + FT_Bool vertical ) + { + PSH_Dimension dim = &glyph->globals->dimension[vertical]; + FT_Fixed scale = dim->scale_mult; + FT_Fixed delta = dim->scale_delta; + PSH2_Contour contour = glyph->contours; + FT_UInt num_contours = glyph->num_contours; + + for ( ; num_contours > 0; num_contours--, contour++ ) + { + PSH2_Point start = contour->start; + PSH2_Point first, next, point; + FT_UInt fit_count; + + /* count the number of strong points in this contour */ + next = start + contour->count; + fit_count = 0; + first = 0; + + for ( point = start; point < next; point++ ) + if ( psh2_point_is_fitted(point) ) + { + if ( !first ) + first = point; + + fit_count++; + } + + /* if there is less than 2 fitted points in the contour, we'll */ + /* simply scale and eventually translate the contour points */ + if ( fit_count < 2 ) + { + if ( fit_count == 1 ) + delta = first->cur_u - FT_MulFix( first->org_u, scale ); + + for ( point = start; point < next; point++ ) + if ( point != first ) + point->cur_u = FT_MulFix( point->org_u, scale ) + delta; + + goto Next_Contour; + } + + /* there are more than 2 strong points in this contour, we'll */ + /* need to interpolate weak points between them.. */ + start = first; + do + { + point = first; + + /* skip consecutive fitted points */ + for (;;) + { + next = first->next; + if ( next == start ) + goto Next_Contour; + + if ( !psh2_point_is_fitted(next) ) + break; + + first = next; + } + + /* find next fitted point after unfitted one */ + for (;;) + { + next = next->next; + if ( psh2_point_is_fitted(next) ) + break; + } + + /* now interpolate between them */ + { + FT_Pos org_a, org_ab, cur_a, cur_ab; + FT_Pos org_c, org_ac, cur_c; + FT_Fixed scale_ab; + + if ( first->org_u <= next->org_u ) + { + org_a = first->org_u; + cur_a = first->cur_u; + org_ab = next->org_u - org_a; + cur_ab = next->cur_u - cur_a; + } + else + { + org_a = next->org_u; + cur_a = next->cur_u; + org_ab = first->org_u - org_a; + cur_ab = first->cur_u - cur_a; + } + + scale_ab = 0x10000L; + if ( org_ab > 0 ) + scale_ab = FT_DivFix( cur_ab, org_ab ); + + point = first->next; + do + { + org_c = point->org_u; + org_ac = org_c - org_a; + + if ( org_ac <= 0 ) + { + /* on the left of the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale ); + } + else if ( org_ac >= org_ab ) + { + /* on the right on the interpolation zone */ + cur_c = cur_a + cur_ab + FT_MulFix( org_ac - org_ab, scale ); + } + else + { + /* within the interpolation zone */ + cur_c = cur_a + FT_MulFix( org_ac, scale_ab ); + } + + point->cur_u = cur_c; + + point = point->next; + } + while ( point != next ); + } + + /* keep going until all points in the contours have been processed */ + first = next; + } + while ( first != start ); + + Next_Contour: + ; + } + } + + /************************************************************************/ + /************************************************************************/ + /***** *****/ + /***** HIGH-LEVEL INTERFACE *****/ + /***** *****/ + /************************************************************************/ + /************************************************************************/ + + FT_Error + ps2_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ) + { + PSH2_Glyph glyph; + PSH2_GlyphRec glyphrec; + FT_Error error; + FT_Memory memory; + FT_Int dimension; + + memory = globals->memory; + +#ifdef DEBUG_HINTER + if ( ps2_debug_glyph ) + { + psh2_glyph_done( ps2_debug_glyph ); + FREE( ps2_debug_glyph ); + } + + if ( ALLOC( glyph, sizeof(*glyph) ) ) + return error; + + ps2_debug_glyph = glyph; +#else + glyph = &glyphrec; +#endif + + error = psh2_glyph_init( glyph, outline, ps_hints, globals ); + if (error) goto Exit; + + for ( dimension = 1; dimension >= 0; dimension-- ) + { + /* load outline coordinates into glyph */ + psh2_glyph_load_points( glyph, dimension ); + + /* compute aligned stem/hints positions */ + psh2_hint_table_align_hints( &glyph->hint_tables[dimension], + glyph->globals, + dimension ); + + /* find strong points, align them, then interpolate others */ + psh2_glyph_find_strong_points( glyph, dimension ); + psh2_glyph_interpolate_strong_points( glyph, dimension ); + psh2_glyph_interpolate_normal_points( glyph, dimension ); + psh2_glyph_interpolate_other_points( glyph, dimension ); + + /* save hinted coordinates back to outline */ + psh2_glyph_save_points( glyph, dimension ); + } + + Exit: +#ifndef DEBUG_HINTER + psh2_glyph_done( glyph ); +#endif + return error; + } diff --git a/src/pshinter/pshalgo2.h b/src/pshinter/pshalgo2.h new file mode 100644 index 000000000..575712cc3 --- /dev/null +++ b/src/pshinter/pshalgo2.h @@ -0,0 +1,185 @@ +/***************************************************************************/ +/* */ +/* pshalgo2.h */ +/* */ +/* First (basic) Postscript hinting routines */ +/* */ +/* Copyright 2001 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. */ +/* */ +/***************************************************************************/ + +#ifndef __PS_HINTER_ALGO2_H__ +#define __PS_HINTER_ALGO2_H__ + +#include "pshrec.h" +#include "pshglob.h" +#include FT_TRIGONOMETRY_H + +FT_BEGIN_HEADER + + typedef struct PSH2_HintRec_* PSH2_Hint; + + typedef enum + { + PSH2_HINT_GHOST = PS_HINT_FLAG_GHOST, + PSH2_HINT_BOTTOM = PS_HINT_FLAG_BOTTOM, + PSH2_HINT_ACTIVE = 4, + PSH2_HINT_FITTED = 8 + } PSH2_Hint_Flags; + +#define psh2_hint_is_active(x) (((x)->flags & PSH2_HINT_ACTIVE) != 0) +#define psh2_hint_is_ghost(x) (((x)->flags & PSH2_HINT_GHOST) != 0) +#define psh2_hint_is_fitted(x) (((x)->flags & PSH2_HINT_FITTED) != 0) + +#define psh2_hint_activate(x) (x)->flags |= PSH2_HINT_ACTIVE +#define psh2_hint_deactivate(x) (x)->flags &= ~PSH2_HINT_ACTIVE +#define psh2_hint_set_fitted(x) (x)->flags |= PSH2_HINT_FITTED + + typedef struct PSH2_HintRec_ + { + FT_Int org_pos; + FT_Int org_len; + FT_Pos cur_pos; + FT_Pos cur_len; + FT_UInt flags; + PSH2_Hint parent; + FT_Int order; + + } PSH2_HintRec; + + + /* this is an interpolation zone used for strong points */ + /* weak points are interpolated according to their strong */ + /* neighbours.. */ + typedef struct PSH2_ZoneRec_ + { + FT_Fixed scale; + FT_Fixed delta; + FT_Pos min; + FT_Pos max; + + } PSH2_ZoneRec, *PSH2_Zone; + + + typedef struct PSH2_Hint_TableRec_ + { + FT_UInt max_hints; + FT_UInt num_hints; + PSH2_Hint hints; + PSH2_Hint* sort; + PSH2_Hint* sort_global; + FT_UInt num_zones; + PSH2_Zone zones; + PSH2_Zone zone; + PS_Mask_Table hint_masks; + PS_Mask_Table counter_masks; + + } PSH2_Hint_TableRec, *PSH2_Hint_Table; + + typedef struct PSH2_PointRec_* PSH2_Point; + typedef struct PSH2_ContourRec_* PSH2_Contour; + + enum + { + PSH2_DIR_NONE = 4, + PSH2_DIR_UP = 1, + PSH2_DIR_DOWN = -1, + PSH2_DIR_LEFT = -2, + PSH2_DIR_RIGHT = 2 + }; + + enum + { + PSH2_POINT_OFF = 1, /* point is off the curve */ + PSH2_POINT_STRONG = 2, /* point is strong */ + PSH2_POINT_SMOOTH = 4, /* point is smooth */ + PSH2_POINT_FITTED = 8 /* point is already fitted */ + }; + + + typedef struct PSH2_PointRec_ + { + PSH2_Point prev; + PSH2_Point next; + PSH2_Contour contour; + FT_UInt flags; + FT_Char dir_in; + FT_Char dir_out; + FT_Angle angle_in; + FT_Angle angle_out; + PSH2_Hint hint; + FT_Pos org_u; + FT_Pos cur_u; +#ifdef DEBUG_HINTER + FT_Pos org_x; + FT_Pos cur_x; + FT_Pos org_y; + FT_Pos cur_y; + FT_UInt flags_x; + FT_UInt flags_y; +#endif + + } PSH2_PointRec; + +#define psh2_point_is_strong(p) ((p)->flags & PSH2_POINT_STRONG) +#define psh2_point_is_fitted(p) ((p)->flags & PSH2_POINT_FITTED) +#define psh2_point_is_smooth(p) ((p)->flags & PSH2_POINT_SMOOTH) + +#define psh2_point_set_strong(p) (p)->flags |= PSH2_POINT_STRONG +#define psh2_point_set_fitted(p) (p)->flags |= PSH2_POINT_FITTED +#define psh2_point_set_smooth(p) (p)->flags |= PSH2_POINT_SMOOTH + + typedef struct PSH2_ContourRec_ + { + PSH2_Point start; + FT_UInt count; + + } PSH2_ContourRec; + + + + typedef struct PSH2_GlyphRec_ + { + FT_UInt num_points; + FT_UInt num_contours; + + PSH2_Point points; + PSH2_Contour contours; + + FT_Memory memory; + FT_Outline* outline; + PSH_Globals globals; + PSH2_Hint_TableRec hint_tables[2]; + + FT_Bool vertical; + FT_Int major_dir; + FT_Int minor_dir; + + } PSH2_GlyphRec, *PSH2_Glyph; + + +#ifdef DEBUG_HINTER + extern PSH2_Hint_Table ps2_debug_hint_table; + + typedef void (*PSH2_HintFunc)( PSH2_Hint hint, FT_Bool vertical ); + extern PSH2_HintFunc ps2_debug_hint_func; + + extern PSH2_Glyph ps2_debug_glyph; +#endif + + + extern FT_Error + ps2_hints_apply( PS_Hints ps_hints, + FT_Outline* outline, + PSH_Globals globals ); + +FT_END_HEADER + +#endif /* __PS_HINTS_ALGO_2_H__ */ diff --git a/src/pshinter/pshglob.c b/src/pshinter/pshglob.c new file mode 100644 index 000000000..3dfdd2400 --- /dev/null +++ b/src/pshinter/pshglob.c @@ -0,0 +1,553 @@ +#include +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include "pshglob.h" + +#ifdef DEBUG_HINTER + extern PSH_Globals ps_debug_globals = 0; +#endif + +/* "simple" ps hinter globals management, inspired from the new auto-hinter */ + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** STANDARD WIDTHS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + + /* scale the widths/heights table */ + static void + psh_globals_scale_widths( PSH_Globals globals, + FT_UInt direction ) + { + PSH_Dimension dim = &globals->dimension[direction]; + PSH_Widths std = &dim->std; + FT_UInt count = std->count; + PSH_Width width = std->widths; + FT_Fixed scale = dim->scale_mult; + + for ( ; count > 0; count--, width++ ) + { + width->cur = FT_MulFix( width->org, scale ); + width->fit = FT_RoundFix( width->cur ); + } + } + + + + /* org_width is is font units, result in device pixels, 26.6 format */ + FT_LOCAL_DEF FT_Pos + psh_dimension_snap_width( PSH_Dimension dimension, + FT_Int org_width ) + { + FT_UInt n; + FT_Pos width = FT_MulFix( org_width, dimension->scale_mult ); + FT_Pos best = 64 + 32 + 2; + FT_Pos reference = width; + + for ( n = 0; n < dimension->std.count; n++ ) + { + FT_Pos w; + FT_Pos dist; + + w = dimension->std.widths[n].cur; + dist = width - w; + if ( dist < 0 ) + dist = -dist; + if ( dist < best ) + { + best = dist; + reference = w; + } + } + + if ( width >= reference ) + { + width -= 0x21; + if ( width < reference ) + width = reference; + } + else + { + width += 0x21; + if ( width > reference ) + width = reference; + } + + return width; + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** BLUE ZONES *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + psh_blues_set_zones_0( PSH_Blues target, + FT_Bool is_others, + FT_UInt read_count, + FT_Short* read, + PSH_Blue_Table top_table, + PSH_Blue_Table bot_table ) + { + FT_UInt count_top = top_table->count; + FT_UInt count_bot = bot_table->count; + FT_Bool first = 1; + + for ( ; read_count > 0; read_count -= 2 ) + { + FT_Int reference, delta; + FT_UInt count; + PSH_Blue_Zone zones, zone; + FT_Bool top; + + /* read blue zone entry, and select target top/bottom zone */ + top = 0; + if ( first || is_others ) + { + reference = read[1]; + delta = read[0] - reference; + + zones = bot_table->zones; + count = count_bot; + first = 0; + } + else + { + reference = read[0]; + delta = read[1] - reference; + + zones = top_table->zones; + count = count_top; + top = 1; + } + + /* insert into sorted table */ + zone = zones; + for ( ; count > 0; count--, zone++ ) + { + if ( reference < zone->org_ref ) + break; + + if ( reference == zone->org_ref ) + { + FT_Int delta0 = zone->org_delta; + + /* we have two zones on the same reference position */ + /* only keep the largest one.. */ + if ( delta < 0 ) + { + if ( delta < delta0 ) + zone->org_delta = delta; + } + else + { + if ( delta > delta0 ) + zone->org_delta = delta; + } + goto Skip; + } + } + + for ( ; count > 0; count-- ) + zone[count] = zone[count-1]; + + zone->org_ref = reference; + zone->org_delta = delta; + + if ( top ) + count_top ++; + else + count_bot ++; + + Skip: + read += 2; + } + + top_table->count = count_top; + bot_table->count = count_bot; + } + + + /* re-read blue zones from the original fonts, and store them into out */ + /* private structure. This function re-orders, sanitizes and fuzz-expands */ + /* the zones as well.. */ + static void + psh_blues_set_zones( PSH_Blues target, + FT_UInt count, + FT_Short* blues, + FT_UInt count_others, + FT_Short* other_blues, + FT_Int fuzz, + FT_Int family ) + { + PSH_Blue_Table top_table, bot_table; + FT_Int count_top, count_bot; + + if ( family ) + { + top_table = &target->family_top; + bot_table = &target->family_bottom; + } + else + { + top_table = &target->normal_top; + bot_table = &target->normal_bottom; + } + + /* read the input blue zones, and build two sorted tables */ + /* (one for the top zones, the other for the bottom zones */ + top_table->count = 0; + bot_table->count = 0; + + /* first, the blues */ + psh_blues_set_zones_0( target, 0, count, blues, top_table, bot_table ); + psh_blues_set_zones_0( target, 1, count_others, other_blues, top_table, bot_table ); + + count_top = top_table->count; + count_bot = bot_table->count; + + /* sanitize top table */ + if ( count_top > 0 ) + { + PSH_Blue_Zone zone = top_table->zones; + + for ( count = count_top; count > 0; count--, zone++ ) + { + FT_Int delta; + + if ( count > 1 ) + { + delta = zone[1].org_ref - zone[0].org_ref; + if ( zone->org_delta > delta ) + zone->org_delta = delta; + } + + zone->org_bottom = zone->org_ref; + zone->org_top = zone->org_delta + zone->org_ref; + } + } + + /* sanitize bottom table */ + if ( count_bot > 0 ) + { + PSH_Blue_Zone zone = bot_table->zones; + + for ( count = count_bot; count > 0; count--, zone++ ) + { + FT_Int delta; + + if ( count > 1 ) + { + delta = zone[0].org_ref - zone[1].org_ref; + if ( zone->org_delta < delta ) + zone->org_delta = delta; + } + + zone->org_top = zone->org_ref; + zone->org_bottom = zone->org_delta + zone->org_ref; + } + } + + /* expand top and bottom tables with blue fuzz */ + { + FT_Int dim, top, bot, delta; + PSH_Blue_Zone zone; + + zone = top_table->zones; + count = count_top; + + for ( dim = 1; dim >= 0; dim-- ) + { + if ( count > 0 ) + { + /* expand the bottom of the lowest zone normally */ + zone->org_bottom -= fuzz; + + /* expand the top and bottom of intermediate zones */ + /* checking that the interval is smaller than the fuzz */ + top = zone->org_top; + + for ( count--; count > 0; count-- ) + { + bot = zone[1].org_bottom; + delta = bot - top; + if ( delta < 2*fuzz ) + { + zone[0].org_top = zone[1].org_bottom = top + delta/2; + } + else + { + zone[0].org_top = top + fuzz; + zone[1].org_bottom = bot - fuzz; + } + + zone++; + top = zone->org_top; + } + + /* expand the top of the highest zone normally */ + zone->org_top = top + fuzz; + } + zone = bot_table->zones; + count = count_bot; + } + } + } + + + + /* reset the blues table when the device transform changes */ + static void + psh_blues_scale_zones( PSH_Blues blues, + FT_Fixed scale, + FT_Pos delta ) + { + FT_UInt count; + FT_UInt num; + PSH_Blue_Table table = 0; + + for ( num = 0; num < 4; num++ ) + { + PSH_Blue_Zone zone; + + switch (num) + { + case 0: table = &blues->normal_top; break; + case 1: table = &blues->normal_bottom; break; + case 2: table = &blues->family_top; break; + default: table = &blues->family_bottom; + } + + zone = table->zones; + count = table->count; + for ( ; count > 0; count--, zone++ ) + { + zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta; + zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta; + zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta; + zone->cur_delta = FT_MulFix( zone->org_delta, scale ); + + /* round scaled reference position */ + zone->cur_ref = ( zone->cur_ref + 32 ) & -64; + +#if 0 + if ( zone->cur_ref > zone->cur_top ) + zone->cur_ref -= 64; + else if ( zone->cur_ref < zone->cur_bottom ) + zone->cur_ref += 64; +#endif + } + } + + /* XXX: we should process the family / normal tables here !! */ + } + + + FT_LOCAL_DEF void + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ) + { + PSH_Blue_Table table; + FT_UInt count; + PSH_Blue_Zone zone; + + alignment->align = 0; + + /* lookup stem top in top zones table */ + table = &blues->normal_top; + count = table->count; + zone = table->zones; + for ( ; count > 0; count--, zone++ ) + { + if ( stem_top < zone->org_bottom ) + break; + + if ( stem_top <= zone->org_top ) + { + alignment->align |= PSH_BLUE_ALIGN_TOP; + alignment->align_top = zone->cur_ref; + break; + } + } + + /* lookup stem bottom in bottom zones table */ + table = &blues->normal_bottom; + count = table->count; + zone = table->zones; + for ( ; count > 0; count--, zone++ ) + { + if ( stem_bot < zone->org_bottom ) + break; + + if ( stem_bot <= zone->org_top ) + { + alignment->align |= PSH_BLUE_ALIGN_BOT; + alignment->align_bot = zone->cur_ref; + break; + } + } + } + + + /*************************************************************************/ + /*************************************************************************/ + /***** *****/ + /***** GLOBAL HINTS *****/ + /***** *****/ + /*************************************************************************/ + /*************************************************************************/ + + static void + psh_globals_destroy( PSH_Globals globals ) + { + if (globals) + { + FT_Memory memory; + + memory = globals->memory; + globals->dimension[0].std.count = 0; + globals->dimension[1].std.count = 0; + + globals->blues.normal_top.count = 0; + globals->blues.normal_bottom.count = 0; + globals->blues.family_top.count = 0; + globals->blues.family_bottom.count = 0; + + FREE( globals ); + +#ifdef DEBUG_HINTER + ps_debug_globals = 0; +#endif + } + } + + + static FT_Error + psh_globals_new( FT_Memory memory, + T1_Private* priv, + PSH_Globals *aglobals ) + { + PSH_Globals globals; + FT_Error error; + + if ( !ALLOC( globals, sizeof(*globals) ) ) + { + FT_UInt count; + FT_Short* read; + + globals->memory = memory; + + /* copy standard widths */ + { + PSH_Dimension dim = &globals->dimension[1]; + PSH_Width write = dim->std.widths; + + write->org = priv->standard_width[1]; + write++; + + read = priv->snap_widths; + for ( count = priv->num_snap_widths; count > 0; count-- ) + { + write->org = *read; + write++; + read++; + } + + dim->std.count = write - dim->std.widths; + } + + /* copy standard heights */ + { + PSH_Dimension dim = &globals->dimension[0]; + PSH_Width write = dim->std.widths; + + write->org = priv->standard_height[1]; + write++; + + read = priv->snap_heights; + for ( count = priv->num_snap_heights; count > 0; count-- ) + { + write->org = *read; + write++; + read++; + } + + dim->std.count = write - dim->std.widths; + } + + /* copy blue zones */ + psh_blues_set_zones( &globals->blues, priv->num_blue_values, + priv->blue_values, priv->num_other_blues, + priv->other_blues, priv->blue_fuzz, 0 ); + + psh_blues_set_zones( &globals->blues, priv->num_family_blues, + priv->family_blues, priv->num_family_other_blues, + priv->family_other_blues, priv->blue_fuzz, 1 ); + + globals->dimension[0].scale_mult = 0; + globals->dimension[0].scale_delta = 0; + globals->dimension[1].scale_mult = 0; + globals->dimension[1].scale_delta = 0; + +#ifdef DEBUG_HINTER + ps_debug_globals = globals; +#endif + } + + *aglobals = globals; + return error; + } + + + + static FT_Error + psh_globals_set_scale( PSH_Globals globals, + FT_Fixed x_scale, + FT_Fixed y_scale, + FT_Fixed x_delta, + FT_Fixed y_delta ) + { + PSH_Dimension dim = &globals->dimension[0]; + + dim = &globals->dimension[0]; + if ( x_scale != dim->scale_mult || + x_delta != dim->scale_delta ) + { + dim->scale_mult = x_scale; + dim->scale_delta = x_delta; + + psh_globals_scale_widths( globals, 0 ); + } + + dim = &globals->dimension[1]; + if ( y_scale != dim->scale_mult || + y_delta != dim->scale_delta ) + { + dim->scale_mult = y_scale; + dim->scale_delta = y_delta; + + psh_globals_scale_widths( globals, 1 ); + psh_blues_scale_zones( &globals->blues, y_scale, y_delta ); + } + + return 0; + } + + + FT_LOCAL_DEF void + psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ) + { + funcs->create = psh_globals_new; + funcs->set_scale = psh_globals_set_scale; + funcs->destroy = psh_globals_destroy; + } diff --git a/src/pshinter/pshglob.h b/src/pshinter/pshglob.h new file mode 100644 index 000000000..dae74b471 --- /dev/null +++ b/src/pshinter/pshglob.h @@ -0,0 +1,171 @@ +/***************************************************************************/ +/* */ +/* pshglob.h */ +/* */ +/* Postscript hinter globals hints management. */ +/* */ +/* Copyright 2001 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. */ +/* */ +/***************************************************************************/ + +#ifndef __PS_HINTER_GLOBALS_H__ +#define __PS_HINTER_GLOBALS_H__ + +#include FT_FREETYPE_H +#include FT_INTERNAL_POSTSCRIPT_GLOBALS_H +#include FT_INTERNAL_POSTSCRIPT_HINTS_H + +FT_BEGIN_HEADER + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** GLOBAL HINTS INTERNALS *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + /**************************************************************** + * + * @constant: PS_GLOBALS_MAX_BLUE_ZONES + * + * @description: + * the maximum number of blue zones in a font global hints + * structure. See @PS_Globals_BluesRec + */ +#define PS_GLOBALS_MAX_BLUE_ZONES 16 + + /**************************************************************** + * + * @constant: PS_GLOBALS_MAX_STD_WIDTHS + * + * @description: + * the maximum number of standard and snap widths in either the + * horizontal or vertical direction. See @PS_Globals_WidthsRec + */ +#define PS_GLOBALS_MAX_STD_WIDTHS 16 + + + /* standard and snap width */ + typedef struct PSH_WidthRec_ + { + FT_Int org; + FT_Pos cur; + FT_Pos fit; + + } PSH_WidthRec, *PSH_Width; + + + /* standard and snap widths table */ + typedef struct PSH_WidthsRec_ + { + FT_UInt count; + PSH_WidthRec widths[ PS_GLOBALS_MAX_STD_WIDTHS ]; + + } PSH_WidthsRec, *PSH_Widths; + + + typedef struct PSH_DimensionRec_ + { + PSH_WidthsRec std; + FT_Fixed scale_mult; + FT_Fixed scale_delta; + + } PSH_DimensionRec, *PSH_Dimension; + + + + /* blue zone descriptor */ + typedef struct PSH_Blue_ZoneRec_ + { + FT_Int org_ref; + FT_Int org_delta; + FT_Int org_top; + FT_Int org_bottom; + + FT_Pos cur_ref; + FT_Pos cur_delta; + FT_Pos cur_bottom; + FT_Pos cur_top; + + } PSH_Blue_ZoneRec, *PSH_Blue_Zone; + + + typedef struct PSH_Blue_TableRec_ + { + FT_UInt count; + PSH_Blue_ZoneRec zones[ PS_GLOBALS_MAX_BLUE_ZONES ]; + + } PSH_Blue_TableRec, *PSH_Blue_Table; + + + /* blue zones table */ + typedef struct PSH_BluesRec_ + { + PSH_Blue_TableRec normal_top; + PSH_Blue_TableRec normal_bottom; + PSH_Blue_TableRec family_top; + PSH_Blue_TableRec family_bottom; + FT_Fixed blue_scale; + + } PSH_BluesRec, *PSH_Blues; + + + /* font globals */ + typedef struct PSH_GlobalsRec_ + { + FT_Memory memory; + PSH_DimensionRec dimension[2]; + PSH_BluesRec blues; + + } PSH_GlobalsRec; + + + typedef enum + { + PSH_BLUE_ALIGN_TOP = 1, + PSH_BLUE_ALIGN_BOT = 2 + + } PSH_Blue_Align; + + + typedef struct + { + PSH_Blue_Align align; + FT_Pos align_top; + FT_Pos align_bot; + + } PSH_AlignmentRec, *PSH_Alignment; + + + FT_LOCAL void + psh_globals_funcs_init( PSH_Globals_FuncsRec* funcs ); + + + /* snap a stem width to fitter coordinates. org_width is in font units */ + /* result is in device pixels (26.6 format).. */ + FT_LOCAL FT_Pos + psh_dimension_snap_width( PSH_Dimension dimension, + FT_Int org_width ); + + /* snap a stem to one or two blue zones */ + FT_LOCAL void + psh_blues_snap_stem( PSH_Blues blues, + FT_Int stem_top, + FT_Int stem_bot, + PSH_Alignment alignment ); + /* */ + +#ifdef DEBUG_HINTER + extern PSH_Globals ps_debug_globals; +#endif + +FT_END_HEADER + +#endif __T1_FITTER_GLOBALS_H__ diff --git a/src/pshinter/pshinter.c b/src/pshinter/pshinter.c new file mode 100644 index 000000000..07c63303c --- /dev/null +++ b/src/pshinter/pshinter.c @@ -0,0 +1,29 @@ +/***************************************************************************/ +/* */ +/* pshinter.c */ +/* */ +/* FreeType Postscript Hinting module */ +/* */ +/* Copyright 1996-2000 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + + +#define FT_MAKE_OPTION_SINGLE_OBJECT + +#include +#include "pshrec.c" +#include "pshglob.c" +#include "pshalgo1.c" +#include "pshalgo2.c" +#include "pshmod.c" + + +/* END */ diff --git a/src/pshinter/pshmod.c b/src/pshinter/pshmod.c new file mode 100644 index 000000000..65b360d07 --- /dev/null +++ b/src/pshinter/pshmod.c @@ -0,0 +1,116 @@ +/***************************************************************************/ +/* */ +/* pshmod.c */ +/* */ +/* FreeType Postscript hinter module implementation (body). */ +/* */ +/* Copyright 2000 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#include +#include FT_INTERNAL_OBJECTS_H +#include "pshrec.h" +#include "pshalgo.h" + + /* the Postscript Hinter module structure */ + typedef struct + { + 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 ); + } + + + /* initialise 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; + + 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) & module->ps_hints; + + t2_hints_funcs_init( &module->t2_funcs ); + module->t2_funcs.hints = (T2_Hints) & module->ps_hints; + + 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_CALLBACK_DEF + PSHinter_Interface pshinter_interface = + { + pshinter_get_globals_funcs, + pshinter_get_t1_funcs, + pshinter_get_t2_funcs + }; + + + FT_CALLBACK_TABLE_DEF + const FT_Module_Class pshinter_module_class = + { + 0, + sizeof( PS_Hinter_ModuleRec ), + "pshinter", + 0x10000L, + 0x20000L, + + &pshinter_interface, /* module-specific interface */ + + (FT_Module_Constructor) ps_hinter_init, + (FT_Module_Destructor) ps_hinter_done, + (FT_Module_Requester) 0 /* no additional interface for now */ + }; + + \ No newline at end of file diff --git a/src/pshinter/pshmod.h b/src/pshinter/pshmod.h new file mode 100644 index 000000000..6bda325cc --- /dev/null +++ b/src/pshinter/pshmod.h @@ -0,0 +1,37 @@ +/***************************************************************************/ +/* */ +/* pshmod.h */ +/* */ +/* Postscript hinter module interface */ +/* */ +/* Copyright 1996-2000 by */ +/* David Turner, Robert Wilhelm, and Werner Lemberg. */ +/* */ +/* This file is part of the FreeType project, and may only be used, */ +/* modified, and distributed under the terms of the FreeType project */ +/* license, LICENSE.TXT. By continuing to use, modify, or distribute */ +/* this file you indicate that you have read the license and */ +/* understand and accept it fully. */ +/* */ +/***************************************************************************/ + +#ifndef __PS_HINTER_MODULE_H__ +#define __PS_HINTER_MODULE_H__ + + +#include +#include FT_MODULE_H + + +FT_BEGIN_HEADER + + + FT_EXPORT_VAR( const FT_Module_Class ) pshinter_module_class; + + +FT_END_HEADER + +#endif /* __PS_HINTER_MODULE_H__ */ + + +/* END */ diff --git a/src/pshinter/pshrec.c b/src/pshinter/pshrec.c new file mode 100644 index 000000000..8eb6810f7 --- /dev/null +++ b/src/pshinter/pshrec.c @@ -0,0 +1,1112 @@ +#include +#include FT_FREETYPE_H +#include FT_INTERNAL_OBJECTS_H +#include FT_INTERNAL_DEBUG_H +#include "pshrec.h" +#include "pshalgo.h" + + +#ifdef DEBUG_HINTER + extern PS_Hints ps_debug_hints = 0; + extern int ps_debug_no_horz_hints = 0; + extern 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 ) + { + 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 = 0; + + if ( new_max > old_max ) + { + /* try to grow the table */ + new_max = ( new_max + 7 ) & -8; + if ( !REALLOC_ARRAY( table->hints, old_max, new_max, PS_HintRec ) ) + 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 = 0; + FT_UInt count; + PS_Hint hint = 0; + + 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 ) + { + 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 = 0; + + if ( new_max > old_max ) + { + new_max = ( new_max + 7 ) & -8; + if ( !REALLOC_ARRAY( mask->bytes, old_max, new_max, FT_Byte ) ) + 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 index ) + { + if ( (FT_UInt)index >= mask->num_bits ) + return 0; + + return mask->bytes[index >> 3] & (0x80 >> (index & 7)); + } + + + /* clear a given bit */ + static void + ps_mask_clear_bit( PS_Mask mask, + FT_Int index ) + { + FT_Byte* p; + + if ( (FT_UInt)index >= mask->num_bits ) + return; + + p = mask->bytes + (index >> 3); + p[0] = (FT_Byte)( p[0] & ~(0x80 >> (index & 7)) ); + } + + + /* set a given bit, eventually grow the mask */ + static FT_Error + ps_mask_set_bit( PS_Mask mask, + FT_Int index, + FT_Memory memory ) + { + FT_Error error = 0; + FT_Byte* p; + + if ( index < 0 ) + goto Exit; + + if ( (FT_UInt)index >= mask->num_bits ) + { + error = ps_mask_ensure( mask, index+1, memory ); + if (error) goto Exit; + + mask->num_bits = index+1; + } + + p = mask->bytes + (index >> 3); + p[0] = (FT_Byte)( p[0] | (0x80 >> (index & 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 ); + + 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 = 0; + + if ( new_max > old_max ) + { + new_max = (new_max+7) & -8; + if ( !REALLOC_ARRAY( table->masks, old_max, new_max, PS_MaskRec ) ) + 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 = 0; + PS_Mask mask = 0; + + 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 = 0; + 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, + FT_Byte* source, + FT_UInt bit_pos, + FT_UInt bit_count, + FT_Memory memory ) + { + FT_Error error = 0; + PS_Mask mask; + + /* allocate new mask, and grow it to "bit_count" bits */ + error = ps_mask_table_alloc( 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 = 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 wether two masks in a table intersect */ + static FT_Int + ps_mask_table_test_intersect( PS_Mask_Table table, + FT_Int index1, + FT_Int 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 = ( count1 <= count2 ) ? 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_Int index1, + FT_Int index2, + FT_Memory memory ) + { + FT_UInt temp; + FT_Error error = 0; + + /* swap index1 and index2 so that index1 < index2 */ + if ( index1 > index2 ) + { + temp = index1; + index1 = index2; + index2 = index1; + } + + if ( index1 < index2 && index1 >= 0 && index2 < (FT_Int)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 (union) the bitsets */ + read = mask2->bytes; + write = mask1->bytes; + pos = (FT_UInt)((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; + + delta = table->num_masks-1 - index2; /* number of masks to move */ + if ( delta > 0 ) + { + /* move to end of table for reuse */ + PS_MaskRec dummy = *mask2; + + memmove( mask2, mask2+1, delta*sizeof(PS_MaskRec) ); + + mask2[delta] = dummy; + } + + table->num_masks--; + } + else + FT_ERROR(( "%s: 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 = 0; + + for ( index1 = table->num_masks-1; index1 > 0; index1-- ) + { + for ( index2 = index1-1; index2 >= 0; index2-- ) + { + if ( ps_mask_table_test_intersect( table, index1, index2 ) ) + { + error = ps_mask_table_merge( table, index2, 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 ); + } + + + /* initialise 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 index, + FT_Memory memory ) + { + PS_Mask mask; + FT_Error error = 0; + + /* get last hint mask */ + error = ps_mask_table_last( &dim->masks, memory, &mask ); + if (error) goto Exit; + + error = ps_mask_set_bit( mask, index, 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; + PS_Mask mask; + + if ( count > 0 ) + { + 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 = 0; + + /* 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, (FT_Byte*)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 = 0; + 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 index; + FT_UInt max = dim->hints.num_hints; + PS_Hint hint = dim->hints.hints; + + for ( index = 0; index < max; index++, hint++ ) + { + if ( hint->pos == pos && hint->len == len ) + break; + } + + /* we need to create a new hint in the table */ + if ( index >= 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, index, memory ); + if (error) goto Exit; + + if ( aindex ) + *aindex = (FT_Int)index; + } + 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 = 0; + 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; + } + + /* creat 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 */ + error = ps_mask_set_bit( counter, hint1, memory ); + if (error) goto Exit; + + error = ps_mask_set_bit( counter, hint2, memory ); + if (error) goto Exit; + + error = ps_mask_set_bit( counter, 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 = 0; + hints->memory = 0; + } + + FT_LOCAL FT_Error + ps_hints_init( PS_Hints hints, + FT_Memory memory ) + { + memset( hints, 0, sizeof(*hints) ); + hints->memory = memory; + return 0; + } + + /* initialise a hints for a new session */ + static void + ps_hints_open( PS_Hints hints, + PS_Hint_Type hint_type ) + { + switch (hint_type) + { + case PS_HINT_TYPE_1: + case PS_HINT_TYPE_2: + { + hints->error = 0; + hints->hint_type = hint_type; + + ps_dimension_init( &hints->dimension[0] ); + ps_dimension_init( &hints->dimension[1] ); + } + break; + + default: + hints->error = FT_Err_Invalid_Argument; + hints->hint_type = hint_type; + + FT_ERROR(( "%s.init: invalid charstring type !!\n", "t1fitter.hints" )); + } + } + + + + /* add one or more stems to the current hints table */ + static void + ps_hints_stem( PS_Hints hints, + FT_Int dimension, + FT_UInt count, + FT_Long* stems ) + { + if ( !hints->error ) + { + /* limit "dimension" to 0..1 */ + if ( dimension < 0 || dimension > 1 ) + { + FT_ERROR(( "ps.hints.stem: invalid dimension (%d) used\n", + dimension )); + dimension = (dimension != 0); + } + + /* record the stems in the current hints/masks table */ + switch ( hints->hint_type ) + { + case PS_HINT_TYPE_1: /* Type 1 "hstem" or "vstem" operator */ + case PS_HINT_TYPE_2: /* Type 2 "hstem" or "vstem" operator */ + { + PS_Dimension dim = &hints->dimension[dimension]; + + for ( ; count > 0; count--, stems += 2 ) + { + FT_Error error; + FT_Memory memory = hints->memory; + + error = ps_dimension_add_t1stem( dim, stems[0], stems[1], + memory, NULL ); + if (error) + { + FT_ERROR(( "t1f.hints.stem: could not add stem" + " (%d,%d) to hints table\n", stems[0], stems[1] )); + + hints->error = error; + return; + }; + } + } + break; + + default: + FT_ERROR(( "t1f.hints.stem: called with invalid hint type (%d)\n", + hints->hint_type )); + ; + } + } + } + + + /* add one Type1 counter stem to the current hints table */ + static void + ps_hints_t1stem3( PS_Hints hints, + FT_Int dimension, + FT_Long* stems ) + { + FT_Error error = 0; + + if (!hints->error) + { + PS_Dimension dim; + FT_Memory memory = hints->memory; + FT_Int count; + FT_Int index[3]; + + /* limit "dimension" to 0..1 */ + if ( dimension < 0 || dimension > 1 ) + { + FT_ERROR(( "t1f.hints.stem: 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, stems[0], stems[1], + memory, &index[count] ); + if (error) goto Fail; + } + + /* now, add the hints to the counters table */ + error = ps_dimension_add_counter( dim, index[0], index[1], + index[2], memory ); + if (error) goto Fail; + } + else + { + FT_ERROR(( "t1f.hints.stem3: called with invalid hint type !!\n" )); + error = FT_Err_Invalid_Argument; + goto Fail; + } + } + + return; + + Fail: + FT_ERROR(( "t1f.hints.stem3: 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 = 0; + + 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_Err_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 ) + { + error = FT_Err_Invalid_Argument; + FT_ERROR(( "%s: called with invalid bitcount %d (instead of %d)\n", + bit_count, count1+count2 )); + goto Fail; + } + + /* set-up new horizontal and vertical hint mask now */ + error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1, + end_point, memory ); + if (error) goto Fail; + + error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, 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 ) + { + error = FT_Err_Invalid_Argument; + FT_ERROR(( "%s: called with invalid bitcount %d (instead of %d)\n", + bit_count, count1+count2 )); + goto Fail; + } + + /* 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_Error 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_VIEW + if (!error) + the_ps_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_Int dimension, + FT_Long* coords ) + { + ps_hints_stem( (PS_Hints)hints, dimension, 1, coords ); + } + + + FT_LOCAL_DEF void + t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ) + { + memset( (char*)funcs, 0, 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_FUNC; + } + + + + /***********************************************************************/ + /***********************************************************************/ + /***** *****/ + /***** 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_Int dimension, + FT_Int count, + FT_Fixed* coords ) + { + FT_Long stems[32], n, total = count; + + while (total > 0) + { + /* determine number of stems to write */ + count = total; + if ( count > 32 ) + count = 32; + + /* compute integer stem position in font units */ + for ( n = 0; n < count*2; n++ ) + stems[n] = (coords[n] + 0x8000) >> 16; + + /* add them to the current dimension */ + ps_hints_stem( (PS_Hints)hints, dimension, count, stems ); + + total -= (count >> 1); + } + } + + + FT_LOCAL_DEF void + t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ) + { + memset( funcs, 0, 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_FUNC; + } + + + \ No newline at end of file diff --git a/src/pshinter/pshrec.h b/src/pshinter/pshrec.h new file mode 100644 index 000000000..cdcf05777 --- /dev/null +++ b/src/pshinter/pshrec.h @@ -0,0 +1,165 @@ +/***************************************************************************/ +/* */ +/* pshrec.h */ +/* */ +/* Postscript (Type1/Type2) hints recorder. */ +/* */ +/* Copyright 2001 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 functions defined here are called from the Type 1, CID and CFF */ +/* font drivers to record the hints of a given character/glyph. */ +/* */ +/* The hints are recorded in a unified format, and are later processed */ +/* by the "optimiser" and "fitter" to adjust the outlines to the pixel */ +/* grid. */ +/* */ +/***************************************************************************/ + +#ifndef __PS_HINTER_RECORD_H__ +#define __PS_HINTER_RECORD_H__ + +#include +#include FT_INTERNAL_POSTSCRIPT_HINTS_H +#include "pshglob.h" + +FT_BEGIN_HEADER + + /**********************************************************************/ + /**********************************************************************/ + /***** *****/ + /***** GLYPH HINTS RECORDER INTERNALS *****/ + /***** *****/ + /**********************************************************************/ + /**********************************************************************/ + + /* handle to hint record */ + typedef struct PS_HintRec_* PS_Hint; + + /* hint types */ + typedef enum + { + PS_HINT_TYPE_1 = 1, + PS_HINT_TYPE_2 = 2 + + } PS_Hint_Type; + + + /* hint flags */ + typedef enum + { + PS_HINT_FLAG_GHOST = 1, + PS_HINT_FLAG_BOTTOM = 2 + + } PS_Hint_Flags; + + + /* hint descriptor */ + typedef struct PS_HintRec_ + { + FT_Int pos; + FT_Int len; + FT_UInt flags; + + } PS_HintRec; + + +#define ps_hint_is_active(x) ((x)->flags & PS_HINT_FLAG_ACTIVE) +#define ps_hint_is_ghost(x) ((x)->flags & PS_HINT_FLAG_GHOST) +#define ps_hint_is_bottom(x) ((x)->flags & PS_HINT_FLAG_BOTTOM) + + + /* hints table descriptor */ + typedef struct PS_Hint_TableRec_ + { + FT_UInt num_hints; + FT_UInt max_hints; + PS_Hint hints; + + } PS_Hint_TableRec, *PS_Hint_Table; + + + /* hint and counter mask descriptor */ + typedef struct PS_MaskRec_ + { + FT_UInt num_bits; + FT_UInt max_bits; + FT_Byte* bytes; + FT_UInt end_point; + + } PS_MaskRec, *PS_Mask; + + + /* masks and counters table descriptor */ + typedef struct PS_Mask_TableRec_ + { + FT_UInt num_masks; + FT_UInt max_masks; + PS_Mask masks; + + } PS_Mask_TableRec, *PS_Mask_Table; + + + /* dimension-specific hints descriptor */ + typedef struct PS_DimensionRec_ + { + PS_Hint_TableRec hints; + PS_Mask_TableRec masks; + PS_Mask_TableRec counters; + + } PS_DimensionRec, *PS_Dimension; + + + /* magic value used within PS_HintsRec */ +#define PS_HINTS_MAGIC 0x68696e74 /* "hint" */ + + + /* glyph hints descriptor */ + typedef struct PS_HintsRec_ + { + FT_Memory memory; + FT_Error error; + FT_UInt32 magic; + PS_Hint_Type hint_type; + PS_DimensionRec dimension[2]; + + } PS_HintsRec, *PS_Hints; + + /* */ + + /* initialise hints recorder */ + FT_LOCAL FT_Error + ps_hints_init( PS_Hints hints, + FT_Memory memory ); + + /* finalize hints recorder */ + FT_LOCAL void + ps_hints_done( PS_Hints hints ); + + /* initialise Type1 hints recorder interface */ + FT_LOCAL void + t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ); + + /* initialise Type2 hints recorder interface */ + FT_LOCAL void + t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ); + + +#ifdef DEBUG_HINTER + extern PS_Hints ps_debug_hints; + extern int ps_debug_no_horz_hints; + extern int ps_debug_no_vert_hints; +#endif + + /* */ + +FT_END_HEADER + +#endif /* __PS_HINTER_RECORD_H__ */