1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/***************************************************************************/ 2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* cf2hints.c */ 4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* Adobe's code for handling CFF hints (body). */ 6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* Copyright 2007-2013 Adobe Systems Incorporated. */ 8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* This software, and all works of authorship, whether in source or */ 10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* object code form as indicated by the copyright notice(s) included */ 11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* herein (collectively, the "Work") is made available, and may only be */ 12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* used, modified, and distributed under the FreeType Project License, */ 13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ 14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* FreeType Project License, each contributor to the Work hereby grants */ 15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* to any individual or legal entity exercising permissions granted by */ 16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* the FreeType Project License and this section (hereafter, "You" or */ 17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ 18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* royalty-free, irrevocable (except as stated in this section) patent */ 19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* license to make, have made, use, offer to sell, sell, import, and */ 20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* otherwise transfer the Work, where such license applies only to those */ 21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* patent claims licensable by such contributor that are necessarily */ 22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* infringed by their contribution(s) alone or by combination of their */ 23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* contribution(s) with the Work to which such contribution(s) was */ 24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* submitted. If You institute patent litigation against any entity */ 25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* (including a cross-claim or counterclaim in a lawsuit) alleging that */ 26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* the Work or a contribution incorporated within the Work constitutes */ 27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* direct or contributory patent infringement, then any patent licenses */ 28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* granted to You under this License for that Work shall terminate as of */ 29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* the date such litigation is filed. */ 30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* By using, modifying, or distributing the Work you indicate that you */ 32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* have read and understood the terms and conditions of the */ 33ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* FreeType Project License as well as those provided in this section, */ 34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* and you accept them fully. */ 35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* */ 36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/***************************************************************************/ 37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "cf2ft.h" 40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "../../include/freetype/internal/ftdebug.h" 41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "cf2glue.h" 43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "cf2font.h" 44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "cf2hints.h" 45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "cf2intrp.h" 46ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /*************************************************************************/ 49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* */ 50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* messages during execution. */ 53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* */ 54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#undef FT_COMPONENT 55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#define FT_COMPONENT trace_cf2hints 56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov typedef struct CF2_HintMoveRec_ 59ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t j; /* index of upper hint map edge */ 61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed moveUp; /* adjustment to optimum position */ 62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } CF2_HintMoveRec, *CF2_HintMove; 64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Compute angular momentum for winding order detection. It is called */ 67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* for all lines and curves, but not necessarily in element order. */ 68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static CF2_Int 69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_getWindingMomentum( CF2_Fixed x1, 70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y1, 71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x2, 72ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y2 ) 73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* cross product of pt1 position from origin with pt2 position from */ 75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* pt1; we reduce the precision so that the result fits into 32 bits */ 76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return ( x1 >> 16 ) * ( ( y2 - y1 ) >> 16 ) - 78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( y1 >> 16 ) * ( ( x2 - x1 ) >> 16 ); 79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Construct from a StemHint; this is used as a parameter to 84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * `cf2_blues_capture'. 85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * `hintOrigin' is the character space displacement of a seac accent. 86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Adjust stem hint for darkening here. 87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_init( CF2_Hint hint, 91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const CF2_ArrStack stemHintArray, 92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t indexStemHint, 93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const CF2_Font font, 94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed hintOrigin, 95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed scale, 96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool bottom ) 97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed width; 99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const CF2_StemHintRec* stemHint; 100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ZERO( hint ); 103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer( 105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov stemHintArray, 106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov indexStemHint ); 107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov width = stemHint->max - stemHint->min; 109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( width == cf2_intToFixed( -21 ) ) 111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* ghost bottom */ 113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( bottom ) 115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->max; 117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_GhostBottom; 118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = 0; 121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( width == cf2_intToFixed( -20 ) ) 124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* ghost top */ 126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( bottom ) 128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = 0; 129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->min; 132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_GhostTop; 133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( width < 0 ) 137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* inverted pair */ 139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Hints with negative widths were produced by an early version of a 142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * non-Adobe font tool. The Type 2 spec allows edge (ghost) hints 143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * with negative widths, but says 144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * All other negative widths have undefined meaning. 146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * CoolType has a silent workaround that negates the hint width; for 148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * permissive mode, we do the same here. 149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Note: Such fonts cannot use ghost hints, but should otherwise work. 151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Note: Some poor hints in our faux fonts can produce negative 152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * widths at some blends. For example, see a light weight of 153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * `u' in ASerifMM. 154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( bottom ) 157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->max; 159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_PairBottom; 160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->min; 164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_PairTop; 165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* normal pair */ 171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( bottom ) 173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->min; 175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_PairBottom; 176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord = stemHint->max; 180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags = CF2_PairTop; 181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Now that ghost hints have been detected, adjust this edge for */ 185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* darkening. Bottoms are not changed; tops are incremented by twice */ 186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* `darkenY'. */ 187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isTop( hint ) ) 188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord += 2 * font->darkenY; 189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->csCoord += hintOrigin; 191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->scale = scale; 192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->index = indexStemHint; /* index in original stem hint array */ 193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* if original stem hint has been used, use the same position */ 195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hint->flags != 0 && stemHint->used ) 196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isTop( hint ) ) 198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->dsCoord = stemHint->maxDS; 199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->dsCoord = stemHint->minDS; 201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_lock( hint ); 203ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 204ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 205ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->dsCoord = FT_MulFix( hint->csCoord, scale ); 206ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 207ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 208ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 209ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* initialize an invalid hint map element */ 210ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 211ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_initZero( CF2_Hint hint ) 212ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 213ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ZERO( hint ); 214ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 215ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 216ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 217ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( FT_Bool ) 218ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isValid( const CF2_Hint hint ) 219ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 220ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( hint->flags != 0 ); 221ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 222ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 223ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 224ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 225ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isPair( const CF2_Hint hint ) 226ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 227ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & 228ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( CF2_PairBottom | CF2_PairTop ) ) != 0 ); 229ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 230ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 231ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 232ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 233ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isPairTop( const CF2_Hint hint ) 234ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 235ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & CF2_PairTop ) != 0 ); 236ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 237ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 238ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 239ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( FT_Bool ) 240ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isTop( const CF2_Hint hint ) 241ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 242ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & 243ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( CF2_PairTop | CF2_GhostTop ) ) != 0 ); 244ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 245ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 246ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 247ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( FT_Bool ) 248ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isBottom( const CF2_Hint hint ) 249ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 250ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & 251ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( CF2_PairBottom | CF2_GhostBottom ) ) != 0 ); 252ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 253ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 254ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 255ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 256ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isLocked( const CF2_Hint hint ) 257ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 258ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & CF2_Locked ) != 0 ); 259ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 260ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 261ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 262ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 263ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isSynthetic( const CF2_Hint hint ) 264ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 265ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (FT_Bool)( ( hint->flags & CF2_Synthetic ) != 0 ); 266ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 267ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 268ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 269ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 270ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_lock( CF2_Hint hint ) 271ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 272ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hint->flags |= CF2_Locked; 273ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 274ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 275ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 276ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 277ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_init( CF2_HintMap hintmap, 278ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Font font, 279ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMap initialMap, 280ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_ArrStack hintMoves, 281ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed scale ) 282ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 283ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ZERO( hintmap ); 284ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 285ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* copy parameters from font instance */ 286ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->hinted = font->hinted; 287ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale = scale; 288ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->font = font; 289ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->initialHintMap = initialMap; 290ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* will clear in `cf2_hintmap_adjustHints' */ 291ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->hintMoves = hintMoves; 292ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 293ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 294ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 295ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 296ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_isValid( const CF2_HintMap hintmap ) 297ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 298ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return hintmap->isValid; 299ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 300ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 301ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 302ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* transform character space coordinate to device space using hint map */ 303ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static CF2_Fixed 304ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_map( CF2_HintMap hintmap, 305ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed csCoord ) 306ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 307ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( hintmap->isValid ); /* must call Build before Map */ 308ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES ); 309ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 310ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->count == 0 || ! hintmap->hinted ) 311ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 312ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* there are no hints; use uniform scale and zero offset */ 313ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FT_MulFix( csCoord, hintmap->scale ); 314ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 315ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 316ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 317ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* start linear search from last hit */ 318ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_UInt i = hintmap->lastIndex; 319ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 320ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 321ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* search up */ 322ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov while ( i < hintmap->count - 1 && 323ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov csCoord >= hintmap->edge[i + 1].csCoord ) 324ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i += 1; 325ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 326ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* search down */ 327ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov while ( i > 0 && csCoord < hintmap->edge[i].csCoord ) 328ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i -= 1; 329ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 330ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->lastIndex = i; 331ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 332ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( i == 0 && csCoord < hintmap->edge[0].csCoord ) 333ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 334ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* special case for points below first edge: use uniform scale */ 335ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FT_MulFix( csCoord - hintmap->edge[0].csCoord, 336ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale ) + 337ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[0].dsCoord; 338ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 339ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 340ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 341ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 342ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Note: entries with duplicate csCoord are allowed. 343ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Use edge[i], the highest entry where csCoord >= entry[i].csCoord 344ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 345ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FT_MulFix( csCoord - hintmap->edge[i].csCoord, 346ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].scale ) + 347ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord; 348ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 349ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 350ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 351ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 352ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 353ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 354ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * This hinting policy moves a hint pair in device space so that one of 355ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * its two edges is on a device pixel boundary (its fractional part is 356ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * zero). `cf2_hintmap_insertHint' guarantees no overlap in CS 357ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * space. Ensure here that there is no overlap in DS. 358ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 359ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * In the first pass, edges are adjusted relative to adjacent hints. 360ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Those that are below have already been adjusted. Those that are 361ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * above have not yet been adjusted. If a hint above blocks an 362ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * adjustment to an optimal position, we will try again in a second 363ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * pass. The second pass is top-down. 364ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 365ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 366ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 367ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 368ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_adjustHints( CF2_HintMap hintmap ) 369ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 370ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t i, j; 371ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 372ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 373ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_clear( hintmap->hintMoves ); /* working storage */ 374ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 375ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 376ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * First pass is bottom-up (font hint order) without look-ahead. 377ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Locked edges are already adjusted. 378ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Unlocked edges begin with dsCoord from `initialHintMap'. 379ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Save edges that are not optimally adjusted in `hintMoves' array, 380ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * and process them in second pass. 381ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 382ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 383ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( i = 0; i < hintmap->count; i++ ) 384ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 385ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool isPair = cf2_hint_isPair( &hintmap->edge[i] ); 386ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 387ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 388ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* index of upper edge (same value for ghost hint) */ 389ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov j = isPair ? i + 1 : i; 390ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 391ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( j < hintmap->count ); 392ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) ); 393ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) ); 394ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) == 395ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isLocked( &hintmap->edge[j] ) ); 396ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 397ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hint_isLocked( &hintmap->edge[i] ) ) 398ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 399ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* hint edge is not locked, we can adjust it */ 400ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord ); 401ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed fracUp = cf2_fixedFraction( hintmap->edge[j].dsCoord ); 402ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 403ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* calculate all four possibilities; moves down are negative */ 404ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed downMoveDown = 0 - fracDown; 405ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed upMoveDown = 0 - fracUp; 406ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed downMoveUp = fracDown == 0 407ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ? 0 408ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov : cf2_intToFixed( 1 ) - fracDown; 409ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed upMoveUp = fracUp == 0 410ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ? 0 411ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov : cf2_intToFixed( 1 ) - fracUp; 412ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 413ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* smallest move up */ 414ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed moveUp = FT_MIN( downMoveUp, upMoveUp ); 415ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* smallest move down */ 416ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed moveDown = FT_MAX( downMoveDown, upMoveDown ); 417ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 418ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* final amount to move edge or edge pair */ 419ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed move; 420ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 421ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed downMinCounter = CF2_MIN_COUNTER; 422ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed upMinCounter = CF2_MIN_COUNTER; 423ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool saveEdge = FALSE; 424ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 425ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 426ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* minimum counter constraint doesn't apply when adjacent edges */ 427ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* are synthetic */ 428ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* TODO: doesn't seem a big effect; for now, reduce the code */ 429ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if 0 430ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( i == 0 || 431ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) ) 432ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov downMinCounter = 0; 433ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 434ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( j >= hintmap->count - 1 || 435ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) ) 436ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov upMinCounter = 0; 437ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif 438ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 439ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* is there room to move up? */ 440ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* there is if we are at top of array or the next edge is at or */ 441ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* beyond proposed move up? */ 442ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( j >= hintmap->count - 1 || 443ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j + 1].dsCoord >= 444ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].dsCoord + moveUp + upMinCounter ) 445ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 446ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* there is room to move up; is there also room to move down? */ 447ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( i == 0 || 448ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i - 1].dsCoord <= 449ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord + moveDown - downMinCounter ) 450ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 451ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* move smaller absolute amount */ 452ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov move = ( -moveDown < moveUp ) ? moveDown : moveUp; /* optimum */ 453ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 454ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 455ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov move = moveUp; 456ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 457ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 458ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 459ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* is there room to move down? */ 460ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( i == 0 || 461ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i - 1].dsCoord <= 462ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord + moveDown - downMinCounter ) 463ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 464ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov move = moveDown; 465ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* true if non-optimum move */ 466ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov saveEdge = (FT_Bool)( moveUp < -moveDown ); 467ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 468ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 469ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 470ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* no room to move either way without overlapping or reducing */ 471ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* the counter too much */ 472ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov move = 0; 473ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov saveEdge = TRUE; 474ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 475ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 476ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 477ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Identify non-moves and moves down that aren't optimal, and save */ 478ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* them for second pass. */ 479ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Do this only if there is an unlocked edge above (which could */ 480ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* possibly move). */ 481ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( saveEdge && 482ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov j < hintmap->count - 1 && 483ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cf2_hint_isLocked( &hintmap->edge[j + 1] ) ) 484ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 485ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMoveRec savedMove; 486ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 487ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 488ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov savedMove.j = j; 489ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* desired adjustment in second pass */ 490ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov savedMove.moveUp = moveUp - move; 491ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 492ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_push( hintmap->hintMoves, &savedMove ); 493ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 494ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 495ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* move the edge(s) */ 496ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord += move; 497ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair ) 498ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].dsCoord += move; 499ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 500ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 501ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* assert there are no overlaps in device space */ 502ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( i == 0 || 503ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord ); 504ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( i < j || 505ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord ); 506ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 507ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* adjust the scales, avoiding divide by zero */ 508ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( i > 0 ) 509ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 510ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord ) 511ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i - 1].scale = 512ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_DivFix( 513ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].dsCoord - hintmap->edge[i - 1].dsCoord, 514ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].csCoord - hintmap->edge[i - 1].csCoord ); 515ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 516ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 517ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair ) 518ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 519ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord ) 520ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j - 1].scale = 521ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_DivFix( 522ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].dsCoord - hintmap->edge[j - 1].dsCoord, 523ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].csCoord - hintmap->edge[j - 1].csCoord ); 524ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 525ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i += 1; /* skip upper edge on next loop */ 526ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 527ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 528ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 529ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* second pass tries to move non-optimal hints up, in case there is */ 530ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* room now */ 531ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- ) 532ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 533ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMove hintMove = (CF2_HintMove) 534ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 ); 535ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 536ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 537ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov j = hintMove->j; 538ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 539ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* this was tested before the push, above */ 540ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( j < hintmap->count - 1 ); 541ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 542ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* is there room to move up? */ 543ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->edge[j + 1].dsCoord >= 544ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].dsCoord + hintMove->moveUp + CF2_MIN_COUNTER ) 545ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 546ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* there is more room now, move edge up */ 547ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j].dsCoord += hintMove->moveUp; 548ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 549ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isPair( &hintmap->edge[j] ) ) 550ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 551ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( j > 0 ); 552ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[j - 1].dsCoord += hintMove->moveUp; 553ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 554ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 555ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 556ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 557ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 558ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 559ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert hint edges into map, sorted by csCoord */ 560ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 561ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( CF2_HintMap hintmap, 562ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Hint bottomHintEdge, 563ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Hint topHintEdge ) 564ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 565ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_UInt indexInsert; 566ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 567ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* set default values, then check for edge hints */ 568ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool isPair = TRUE; 569ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Hint firstHintEdge = bottomHintEdge; 570ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Hint secondHintEdge = topHintEdge; 571ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 572ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 573ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* one or none of the input params may be invalid when dealing with */ 574ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* edge hints; at least one edge must be valid */ 575ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) || 576ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isValid( topHintEdge ) ); 577ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 578ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* determine how many and which edges to insert */ 579ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hint_isValid( bottomHintEdge ) ) 580ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 581ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert only the top edge */ 582ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge = topHintEdge; 583ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov isPair = FALSE; 584ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 585ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( !cf2_hint_isValid( topHintEdge ) ) 586ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 587ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert only the bottom edge */ 588ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov isPair = FALSE; 589ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 590ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 591ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* paired edges must be in proper order */ 592ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( !isPair || 593ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov topHintEdge->csCoord >= bottomHintEdge->csCoord ); 594ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 595ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* linear search to find index value of insertion point */ 596ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov indexInsert = 0; 597ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( ; indexInsert < hintmap->count; indexInsert++ ) 598ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 599ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->edge[indexInsert].csCoord > firstHintEdge->csCoord ) 600ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 601ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 602ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 603ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 604ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Discard any hints that overlap in character space. Most often, 605ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * this is while building the initial map, but in theory, it can also 606ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * occur because of darkening. 607ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 608ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 609ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( indexInsert < hintmap->count ) 610ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 611ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* we are inserting before an existing edge: */ 612ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* verify that a new pair does not straddle the next edge */ 613ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair && 614ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[indexInsert].csCoord < secondHintEdge->csCoord ) 615ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; /* ignore overlapping stem hint */ 616ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 617ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* verify that we are not inserting between paired edges */ 618ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) ) 619ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; /* ignore overlapping stem hint */ 620ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 621ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 622ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* recompute device space locations using initial hint map */ 623ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hintmap_isValid( hintmap->initialHintMap ) && 624ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cf2_hint_isLocked( firstHintEdge ) ) 625ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 626ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair ) 627ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 628ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Use hint map to position the center of stem, and nominal scale */ 629ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* to position the two edges. This preserves the stem width. */ 630ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed midpoint = cf2_hintmap_map( 631ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->initialHintMap, 632ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( secondHintEdge->csCoord + 633ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge->csCoord ) / 2 ); 634ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed halfWidth = FT_MulFix( 635ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( secondHintEdge->csCoord - 636ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge->csCoord ) / 2, 637ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale ); 638ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 639ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 640ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge->dsCoord = midpoint - halfWidth; 641ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov secondHintEdge->dsCoord = midpoint + halfWidth; 642ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 643ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 644ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap, 645ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov firstHintEdge->csCoord ); 646ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 647ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 648ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* discard any hints that overlap in device space; this can occur */ 649ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* because locked hints have been moved to align with blue zones */ 650ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( indexInsert > 0 ) 651ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 652ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* we are inserting after an existing edge */ 653ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord ) 654ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 655ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 656ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 657ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( indexInsert < hintmap->count ) 658ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 659ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* we are inserting before an existing edge */ 660ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair ) 661ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 662ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) 663ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 664ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 665ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 666ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 667ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord ) 668ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 669ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 670ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 671ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 672ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* make room to insert */ 673ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 674ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Int iSrc = hintmap->count - 1; 675ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Int iDst = isPair ? hintmap->count + 1 : hintmap->count; 676ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 677ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Int count = hintmap->count - indexInsert; 678ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 679ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 680ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( iDst >= CF2_MAX_HINT_EDGES ) 681ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 682ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" )); 683ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 684ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 685ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 686ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov while ( count-- ) 687ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[iDst--] = hintmap->edge[iSrc--]; 688ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 689ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert first edge */ 690ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[indexInsert] = *firstHintEdge; /* copy struct */ 691ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->count += 1; 692ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 693ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( isPair ) 694ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 695ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert second edge */ 696ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[indexInsert + 1] = *secondHintEdge; /* copy struct */ 697ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->count += 1; 698ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 699ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 700ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 701ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 702ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 703ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 704ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 705ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 706ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Build a map from hints and mask. 707ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 708ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * This function may recur one level if `hintmap->initialHintMap' is not yet 709ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * valid. 710ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * If `initialMap' is true, simply build initial map. 711ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 712ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Synthetic hints are used in two ways. A hint at zero is inserted, if 713ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * needed, in the initial hint map, to prevent translations from 714ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * propagating across the origin. If synthetic em box hints are enabled 715ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * for ideographic dictionaries, then they are inserted in all hint 716ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * maps, including the initial one. 717ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 718ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 719ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 720ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_build( CF2_HintMap hintmap, 721ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_ArrStack hStemHintArray, 722ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_ArrStack vStemHintArray, 723ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMask hintMask, 724ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed hintOrigin, 725ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool initialMap ) 726ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 727ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Byte* maskPtr; 728ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Byte* maskEndPtr; // add by Xiaochuan_Liu 729ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 730ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Font font = hintmap->font; 731ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMaskRec tempHintMask; 732ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 733ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov size_t bitCount, i; 734ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Byte maskByte; 735ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 736ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 737ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* check whether initial map is constructed */ 738ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) ) 739ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 740ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* make recursive call with initialHintMap and temporary mask; */ 741ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* temporary mask will get all bits set, below */ 742ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmask_init( &tempHintMask, hintMask->error ); 743ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_build( hintmap->initialHintMap, 744ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hStemHintArray, 745ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov vStemHintArray, 746ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &tempHintMask, 747ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintOrigin, 748ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TRUE ); 749ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 750ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 751ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hintmask_isValid( hintMask ) ) 752ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 753ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* without a hint mask, assume all hints are active */ 754ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmask_setAll( hintMask, 755ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_size( hStemHintArray ) + 756ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_size( vStemHintArray ) ); 757ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 758ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 759ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* begin by clearing the map */ 760ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->count = 0; 761ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->lastIndex = 0; 762ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 763ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* make a copy of the hint mask so we can modify it */ 764ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov tempHintMask = *hintMask; 765ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); 766ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; 767ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 768ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 769ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* use the hStem hints only, which are first in the mask */ 770ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* TODO: compare this to cffhintmaskGetBitCount */ 771ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bitCount = cf2_arrstack_size( hStemHintArray ); 772ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 773ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* synthetic embox hints get highest priority */ 774ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( font->blues.doEmBoxHints ) 775ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 776ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintRec dummy; 777ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 778ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 779ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_initZero( &dummy ); /* invalid hint map element */ 780ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 781ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* ghost bottom */ 782ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( hintmap, 783ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &font->blues.emBoxBottomEdge, 784ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &dummy ); 785ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* ghost top */ 786ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( hintmap, 787ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &dummy, 788ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &font->blues.emBoxTopEdge ); 789ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 790ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 791ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert hints captured by a blue zone or already locked (higher */ 792ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* priority) */ 793ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) 794ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 795ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( maskByte & *maskPtr ) 796ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 797ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* expand StemHint into two `CF2_Hint' elements */ 798ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintRec bottomHintEdge, topHintEdge; 799ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 800ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 801ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_init( &bottomHintEdge, 802ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hStemHintArray, 803ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i, 804ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 805ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintOrigin, 806ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale, 807ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TRUE /* bottom */ ); 808ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_init( &topHintEdge, 809ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hStemHintArray, 810ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i, 811ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 812ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintOrigin, 813ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale, 814ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE /* top */ ); 815ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 816ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isLocked( &bottomHintEdge ) || 817ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_isLocked( &topHintEdge ) || 818ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_blues_capture( &font->blues, 819ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &bottomHintEdge, 820ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &topHintEdge ) ) 821ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 822ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert captured hint into map */ 823ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); 824ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 825ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *maskPtr &= ~maskByte; /* turn off the bit for this hint */ 826ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 827ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 828ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 829ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( ( i & 7 ) == 7 ) 830ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 831ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* move to next mask byte */ 832ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskPtr++; 833ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (maskPtr >= maskEndPtr) 834ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 835ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 836ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 837ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 838ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskByte = 0x80; 839ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 840ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 841ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskByte >>= 1; 842ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 843ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 844ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* initial hint map includes only captured hints plus maybe one at 0 */ 845ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 846ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 847ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * TODO: There is a problem here because we are trying to build a 848ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * single hint map containing all captured hints. It is 849ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * possible for there to be conflicts between captured hints, 850ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * either because of darkening or because the hints are in 851ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * separate hint zones (we are ignoring hint zones for the 852ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * initial map). An example of the latter is MinionPro-Regular 853ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem. 854ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * A stem hint for the psili conflicts with the top edge hint 855ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * for the base character. The stem hint gets priority because 856ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * of its sort order. In glyph 884 (Greek Capital Alpha with 857ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Psili and Oxia), the top of the base character gets a stem 858ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * hint, and the psili does not. This creates different initial 859ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * maps for the two glyphs resulting in different renderings of 860ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * the base character. Will probably defer this either as not 861ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * worth the cost or as a font bug. I don't think there is any 862ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * good reason for an accent to be captured by an alignment 863ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * zone. -darnold 2/12/10 864ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 865ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 866ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( initialMap ) 867ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 868ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Apply a heuristic that inserts a point for (0,0), unless it's */ 869ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* already covered by a mapping. This locks the baseline for glyphs */ 870ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* that have no baseline hints. */ 871ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 872ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( hintmap->count == 0 || 873ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[0].csCoord > 0 || 874ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[hintmap->count - 1].csCoord < 0 ) 875ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 876ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* all edges are above 0 or all edges are below 0; */ 877ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* construct a locked edge hint at 0 */ 878ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 879ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintRec edge, invalid; 880ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 881ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 882ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_initZero( &edge ); 883ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 884ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov edge.flags = CF2_GhostBottom | 885ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Locked | 886ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Synthetic; 887ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov edge.scale = hintmap->scale; 888ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 889ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_initZero( &invalid ); 890ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( hintmap, &edge, &invalid ); 891ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 892ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 893ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 894ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 895ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert remaining hints */ 896ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 897ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask ); 898ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskEndPtr = maskPtr + ( CF2_MAX_HINTS + 7 ) / 8; 899ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 900ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( i = 0, maskByte = 0x80; i < bitCount; i++ ) 901ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 902ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( maskByte & *maskPtr ) 903ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 904ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintRec bottomHintEdge, topHintEdge; 905ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 906ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 907ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_init( &bottomHintEdge, 908ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hStemHintArray, 909ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i, 910ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 911ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintOrigin, 912ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale, 913ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TRUE /* bottom */ ); 914ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hint_init( &topHintEdge, 915ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hStemHintArray, 916ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov i, 917ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 918ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintOrigin, 919ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->scale, 920ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE /* top */ ); 921ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 922ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge ); 923ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 924ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 925ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( ( i & 7 ) == 7 ) 926ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 927ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* move to next mask byte */ 928ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskPtr++; 929ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (maskPtr >= maskEndPtr) 930ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 931ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 932ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 933ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskByte = 0x80; 934ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 935ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 936ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov maskByte >>= 1; 937ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 938ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 939ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 940ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 941ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Note: The following line is a convenient place to break when 942ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * debugging hinting. Examine `hintmap->edge' for the list of 943ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * enabled hints, then step over the call to see the effect of 944ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * adjustment. We stop here first on the recursive call that 945ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * creates the initial map, and then on each counter group and 946ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * hint zone. 947ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 948ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 949ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* adjust positions of hint edges that are not locked to blue zones */ 950ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_adjustHints( hintmap ); 951ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 952ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* save the position of all hints that were used in this hint map; */ 953ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* if we use them again, we'll locate them in the same position */ 954ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !initialMap ) 955ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 956ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for ( i = 0; i < hintmap->count; i++ ) 957ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 958ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) ) 959ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 960ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Note: include both valid and invalid edges */ 961ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Note: top and bottom edges are copied back separately */ 962ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_StemHint stemhint = (CF2_StemHint) 963ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_getPointer( hStemHintArray, 964ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->edge[i].index ); 965ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 966ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 967ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hint_isTop( &hintmap->edge[i] ) ) 968ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov stemhint->maxDS = hintmap->edge[i].dsCoord; 969ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 970ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov stemhint->minDS = hintmap->edge[i].dsCoord; 971ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 972ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov stemhint->used = TRUE; 973ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 974ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 975ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 976ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 977ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* hint map is ready to use */ 978ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap->isValid = TRUE; 979ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 980ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* remember this mask has been used */ 981ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmask_setNew( hintMask, FALSE ); 982ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 983ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 984ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 985ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 986ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_init( CF2_GlyphPath glyphpath, 987ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Font font, 988ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_OutlineCallbacks callbacks, 989ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed scaleY, 990ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* CF2_Fixed hShift, */ 991ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_ArrStack hStemHintArray, 992ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_ArrStack vStemHintArray, 993ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMask hintMask, 994ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed hintOriginY, 995ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const CF2_Blues blues, 996ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const FT_Vector* fractionalTranslation ) 997ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 998ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ZERO( glyphpath ); 999ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1000ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->font = font; 1001ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks = callbacks; 1002ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1003ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_init( &glyphpath->hintMoves, 1004ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font->memory, 1005ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &font->error, 1006ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov sizeof ( CF2_HintMoveRec ) ); 1007ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1008ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_init( &glyphpath->initialHintMap, 1009ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 1010ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->initialHintMap, 1011ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMoves, 1012ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov scaleY ); 1013ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_init( &glyphpath->firstHintMap, 1014ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 1015ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->initialHintMap, 1016ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMoves, 1017ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov scaleY ); 1018ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_init( &glyphpath->hintMap, 1019ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov font, 1020ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->initialHintMap, 1021ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMoves, 1022ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov scaleY ); 1023ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1024ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->scaleX = font->innerTransform.a; 1025ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->scaleC = font->innerTransform.c; 1026ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->scaleY = font->innerTransform.d; 1027ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1028ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->fractionalTranslation = *fractionalTranslation; 1029ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1030ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#if 0 1031ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hShift = hShift; /* for fauxing */ 1032ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#endif 1033ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1034ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hStemHintArray = hStemHintArray; 1035ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->vStemHintArray = vStemHintArray; 1036ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintMask = hintMask; /* ptr to current mask */ 1037ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintOriginY = hintOriginY; 1038ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->blues = blues; 1039ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->darken = font->darkened; /* TODO: should we make copies? */ 1040ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->xOffset = font->darkenX; 1041ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->yOffset = font->darkenY; 1042ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->miterLimit = 2 * FT_MAX( 1043ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( glyphpath->xOffset ), 1044ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( glyphpath->yOffset ) ); 1045ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1046ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* .1 character space unit */ 1047ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->snapThreshold = cf2_floatToFixed( 0.1f ); 1048ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1049ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->moveIsPending = TRUE; 1050ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->pathIsOpen = FALSE; 1051ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->elemIsQueued = FALSE; 1052ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1053ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1054ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1055ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 1056ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_finalize( CF2_GlyphPath glyphpath ) 1057ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1058ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_arrstack_finalize( &glyphpath->hintMoves ); 1059ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1060ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1061ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1062ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1063ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Hint point in y-direction and apply outerTransform. 1064ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Input `current' hint map (which is actually delayed by one element). 1065ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Input x,y point in Character Space. 1066ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Output x,y point in Device Space, including translation. 1067ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1068ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 1069ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( CF2_GlyphPath glyphpath, 1070ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMap hintmap, 1071ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector* ppt, 1072ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x, 1073ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y ) 1074ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1075ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector pt; /* hinted point in upright DS */ 1076ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1077ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1078ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov pt.x = FT_MulFix( glyphpath->scaleX, x ) + 1079ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_MulFix( glyphpath->scaleC, y ); 1080ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov pt.y = cf2_hintmap_map( hintmap, y ); 1081ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1082ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ppt->x = FT_MulFix( glyphpath->font->outerTransform.a, pt.x ) + 1083ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_MulFix( glyphpath->font->outerTransform.c, pt.y ) + 1084ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->fractionalTranslation.x; 1085ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ppt->y = FT_MulFix( glyphpath->font->outerTransform.b, pt.x ) + 1086ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_MulFix( glyphpath->font->outerTransform.d, pt.y ) + 1087ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->fractionalTranslation.y; 1088ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1089ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1090ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1091ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1092ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * From two line segments, (u1,u2) and (v1,v2), compute a point of 1093ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * intersection on the corresponding lines. 1094ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Return false if no intersection is found, or if the intersection is 1095ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * too far away from the ends of the line segments, u2 and v1. 1096ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1097ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1098ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static FT_Bool 1099ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_computeIntersection( CF2_GlyphPath glyphpath, 1100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const FT_Vector* u1, 1101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const FT_Vector* u2, 1102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const FT_Vector* v1, 1103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const FT_Vector* v2, 1104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector* intersection ) 1105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Let `u' be a zero-based vector from the first segment, `v' from the 1108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * second segment. 1109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Let `w 'be the zero-based vector from `u1' to `v1'. 1110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * `perp' is the `perpendicular dot product'; see 1111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * http://mathworld.wolfram.com/PerpDotProduct.html. 1112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * `s' is the parameter for the parametric line for the first segment 1113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * (`u'). 1114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * See notation in 1116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm. 1117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Calculations are done in 16.16, but must handle the squaring of 1118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * line lengths in character space. We scale all vectors by 1/32 to 1119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * avoid overflow. This allows values up to 4095 to be squared. The 1120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * scale factor cancels in the divide. 1121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * TODO: the scale factor could be computed from UnitsPerEm. 1123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#define cf2_perp( a, b ) \ 1127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) ) 1128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* round and divide by 32 */ 1130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#define CF2_CS_SCALE( x ) \ 1131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ( ( (x) + 0x10 ) >> 5 ) 1132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector u, v, w; /* scaled vectors */ 1134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed denominator, s; 1135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov u.x = CF2_CS_SCALE( u2->x - u1->x ); 1138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov u.y = CF2_CS_SCALE( u2->y - u1->y ); 1139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov v.x = CF2_CS_SCALE( v2->x - v1->x ); 1140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov v.y = CF2_CS_SCALE( v2->y - v1->y ); 1141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov w.x = CF2_CS_SCALE( v1->x - u1->x ); 1142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov w.y = CF2_CS_SCALE( v1->y - u1->y ); 1143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov denominator = cf2_perp( u, v ); 1145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( denominator == 0 ) 1147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; /* parallel or coincident lines */ 1148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov s = FT_DivFix( cf2_perp( w, v ), denominator ); 1150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->x = u1->x + FT_MulFix( s, u2->x - u1->x ); 1152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->y = u1->y + FT_MulFix( s, u2->y - u1->y ); 1153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Special case snapping for horizontal and vertical lines. 1156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * This cleans up intersections and reduces problems with winding 1157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * order detection. 1158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685. 1159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Note: these calculations are in character space. 1160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( u1->x == u2->x && 1164ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( intersection->x - u1->x ) < glyphpath->snapThreshold ) 1165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->x = u1->x; 1166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( u1->y == u2->y && 1167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( intersection->y - u1->y ) < glyphpath->snapThreshold ) 1168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->y = u1->y; 1169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( v1->x == v2->x && 1171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( intersection->x - v1->x ) < glyphpath->snapThreshold ) 1172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->x = v1->x; 1173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( v1->y == v2->y && 1174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( intersection->y - v1->y ) < glyphpath->snapThreshold ) 1175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov intersection->y = v1->y; 1176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* limit the intersection distance from midpoint of u2 and v1 */ 1178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_fixedAbs( intersection->x - ( u2->x + v1->x ) / 2 ) > 1179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->miterLimit || 1180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_fixedAbs( intersection->y - ( u2->y + v1->y ) / 2 ) > 1181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->miterLimit ) 1182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 1183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 1185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Push the cached element (glyphpath->prevElem*) to the outline 1190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * consumer. When a darkening offset is used, the end point of the 1191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * cached element may be adjusted to an intersection point or it may be 1192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * connected by a line to the current element. This calculation must 1193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * use a HintMap that was valid at the time the element was saved. For 1194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * the first point in a subpath, that is a saved HintMap. For most 1195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * elements, it just means the caller has delayed building a HintMap 1196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * from the current HintMask. 1197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * Transform each point with outerTransform and call the outline 1199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * callbacks. This is a general 3x3 transform: 1200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * x' = a*x + c*y + tx, y' = b*x + d*y + ty 1202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1203ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * but it uses 4 elements from CF2_Font and the translation part 1204ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * from CF2_GlyphPath. 1205ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1206ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1207ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 1208ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushPrevElem( CF2_GlyphPath glyphpath, 1209ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_HintMap hintmap, 1210ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector* nextP0, 1211ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector nextP1, 1212ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool close ) 1213ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1214ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_CallbackParamsRec params; 1215ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1216ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector* prevP0; 1217ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector* prevP1; 1218ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1219ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector intersection = { 0, 0 }; 1220ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Bool useIntersection = FALSE; 1221ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1222ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1223ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo || 1224ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemOp == CF2_PathOpCubeTo ); 1225ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1226ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->prevElemOp == CF2_PathOpLineTo ) 1227ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1228ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP0 = &glyphpath->prevElemP0; 1229ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP1 = &glyphpath->prevElemP1; 1230ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1231ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1232ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1233ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP0 = &glyphpath->prevElemP2; 1234ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP1 = &glyphpath->prevElemP3; 1235ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1236ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1237ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* optimization: if previous and next elements are offset by the same */ 1238ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* amount, then there will be no gap, and no need to compute an */ 1239ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* intersection. */ 1240ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y ) 1241ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1242ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* previous element does not join next element: */ 1243ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* adjust end point of previous element to the intersection */ 1244ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov useIntersection = cf2_glyphpath_computeIntersection( glyphpath, 1245ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP0, 1246ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov prevP1, 1247ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nextP0, 1248ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &nextP1, 1249ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &intersection ); 1250ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( useIntersection ) 1251ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1252ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* modify the last point of the cached element (either line or */ 1253ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* curve) */ 1254ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *prevP1 = intersection; 1255ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1256ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1257ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1258ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.pt0 = glyphpath->currentDS; 1259ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1260ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov switch( glyphpath->prevElemOp ) 1261ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1262ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case CF2_PathOpLineTo: 1263ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.op = CF2_PathOpLineTo; 1264ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1265ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: pt2 and pt3 are unused */ 1266ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1267ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap, 1268ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt1, 1269ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1.x, 1270ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1.y ); 1271ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1272ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); 1273ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1274ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentDS = params.pt1; 1275ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1276ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 1277ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1278ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case CF2_PathOpCubeTo: 1279ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.op = CF2_PathOpCubeTo; 1280ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1281ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */ 1282ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1283ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap, 1284ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt1, 1285ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1.x, 1286ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1.y ); 1287ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1288ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap, 1289ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt2, 1290ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP2.x, 1291ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP2.y ); 1292ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1293ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap, 1294ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt3, 1295ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP3.x, 1296ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP3.y ); 1297ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1298ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->cubeTo( glyphpath->callbacks, ¶ms ); 1299ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1300ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentDS = params.pt3; 1301ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1302ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 1303ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1304ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1305ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !useIntersection || close ) 1306ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1307ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* insert connecting line between end of previous element and start */ 1308ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* of current one */ 1309ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: at the end of a subpath, we might do both, so use `nextP0' */ 1310ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* before we change it, below */ 1311ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1312ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1313ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hintmap, 1314ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt1, 1315ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nextP0->x, 1316ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nextP0->y ); 1317ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1318ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( params.pt1.x != glyphpath->currentDS.x || 1319ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.pt1.y != glyphpath->currentDS.y ) 1320ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1321ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* length is nonzero */ 1322ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.op = CF2_PathOpLineTo; 1323ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.pt0 = glyphpath->currentDS; 1324ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1325ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: pt2 and pt3 are unused */ 1326ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->lineTo( glyphpath->callbacks, ¶ms ); 1327ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1328ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentDS = params.pt1; 1329ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1330ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1331ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1332ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( useIntersection ) 1333ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1334ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* return intersection point to caller */ 1335ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *nextP0 = intersection; 1336ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1337ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1338ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1339ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1340ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* push a MoveTo element based on current point and offset of current */ 1341ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* element */ 1342ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 1343ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushMove( CF2_GlyphPath glyphpath, 1344ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector start ) 1345ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1346ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_CallbackParamsRec params; 1347ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1348ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1349ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.op = CF2_PathOpMoveTo; 1350ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov params.pt0 = glyphpath->currentDS; 1351ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1352ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Test if move has really happened yet; it would have called */ 1353ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* `cf2_hintmap_build' to set `isValid'. */ 1354ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ) 1355ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1356ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* we are here iff first subpath is missing a moveto operator: */ 1357ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* synthesize first moveTo to finish initialization of hintMap */ 1358ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_moveTo( glyphpath, 1359ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->start.x, 1360ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->start.y ); 1361ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1362ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1363ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_hintPoint( glyphpath, 1364ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMap, 1365ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ¶ms.pt1, 1366ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov start.x, 1367ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov start.y ); 1368ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1369ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: pt2 and pt3 are unused */ 1370ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->moveTo( glyphpath->callbacks, ¶ms ); 1371ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1372ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentDS = params.pt1; 1373ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->offsetStart0 = start; 1374ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1375ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1376ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1377ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* 1378ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * All coordinates are in character space. 1379ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * On input, (x1, y1) and (x2, y2) give line segment. 1380ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * On output, (x, y) give offset vector. 1381ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * We use a piecewise approximation to trig functions. 1382ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * 1383ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * TODO: Offset true perpendicular and proper length 1384ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * supply the y-translation for hinting here, too, 1385ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov * that adds yOffset unconditionally to *y. 1386ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov */ 1387ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static void 1388ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_computeOffset( CF2_GlyphPath glyphpath, 1389ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x1, 1390ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y1, 1391ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x2, 1392ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y2, 1393ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed* x, 1394ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed* y ) 1395ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1396ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed dx = x2 - x1; 1397ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed dy = y2 - y1; 1398ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1399ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1400ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: negative offsets don't work here; negate deltas to change */ 1401ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* quadrants, below */ 1402ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->font->reverseWinding ) 1403ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1404ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dx = -dx; 1405ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dy = -dy; 1406ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1407ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1408ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = *y = 0; 1409ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1410ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !glyphpath->darken ) 1411ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 1412ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1413ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* add momentum for this path element */ 1414ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->windingMomentum += 1415ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_getWindingMomentum( x1, y1, x2, y2 ); 1416ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1417ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: allow mixed integer and fixed multiplication here */ 1418ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( dx >= 0 ) 1419ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1420ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( dy >= 0 ) 1421ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1422ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* first quadrant, +x +y */ 1423ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1424ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( dx > 2 * dy ) 1425ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1426ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +x */ 1427ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = 0; 1428ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = 0; 1429ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1430ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( dy > 2 * dx ) 1431ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1432ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +y */ 1433ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = glyphpath->xOffset; 1434ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = glyphpath->yOffset; 1435ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1436ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1437ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1438ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +x +y */ 1439ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = FT_MulFix( cf2_floatToFixed( 0.7 ), 1440ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->xOffset ); 1441ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ), 1442ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->yOffset ); 1443ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1444ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1445ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1446ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1447ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* fourth quadrant, +x -y */ 1448ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1449ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( dx > -2 * dy ) 1450ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1451ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +x */ 1452ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = 0; 1453ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = 0; 1454ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1455ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( -dy > 2 * dx ) 1456ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1457ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -y */ 1458ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = -glyphpath->xOffset; 1459ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = glyphpath->yOffset; 1460ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1461ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1462ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1463ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +x -y */ 1464ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = FT_MulFix( cf2_floatToFixed( -0.7 ), 1465ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->xOffset ); 1466ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ), 1467ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->yOffset ); 1468ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1469ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1470ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1471ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1472ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1473ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( dy >= 0 ) 1474ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1475ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* second quadrant, -x +y */ 1476ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1477ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( -dx > 2 * dy ) 1478ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1479ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -x */ 1480ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = 0; 1481ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = 2 * glyphpath->yOffset; 1482ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1483ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( dy > -2 * dx ) 1484ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1485ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* +y */ 1486ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = glyphpath->xOffset; 1487ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = glyphpath->yOffset; 1488ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1489ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1490ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1491ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -x +y */ 1492ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = FT_MulFix( cf2_floatToFixed( 0.7 ), 1493ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->xOffset ); 1494ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), 1495ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->yOffset ); 1496ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1497ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1498ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1499ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1500ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* third quadrant, -x -y */ 1501ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1502ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( -dx > -2 * dy ) 1503ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1504ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -x */ 1505ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = 0; 1506ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = 2 * glyphpath->yOffset; 1507ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1508ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else if ( -dy > -2 * dx ) 1509ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1510ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -y */ 1511ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = -glyphpath->xOffset; 1512ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = glyphpath->xOffset; 1513ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1514ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 1515ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1516ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* -x -y */ 1517ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *x = FT_MulFix( cf2_floatToFixed( -0.7 ), 1518ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->xOffset ); 1519ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ), 1520ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->yOffset ); 1521ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1522ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1523ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1524ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1525ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1526ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1527ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 1528ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_moveTo( CF2_GlyphPath glyphpath, 1529ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x, 1530ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y ) 1531ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1532ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_closeOpenPath( glyphpath ); 1533ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1534ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* save the parameters of the move for later, when we'll know how to */ 1535ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* offset it; */ 1536ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* also save last move point */ 1537ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.x = glyphpath->start.x = x; 1538ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.y = glyphpath->start.y = y; 1539ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1540ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->moveIsPending = TRUE; 1541ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1542ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* ensure we have a valid map with current mask */ 1543ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) || 1544ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmask_isNew( glyphpath->hintMask ) ) 1545ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_build( &glyphpath->hintMap, 1546ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hStemHintArray, 1547ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->vStemHintArray, 1548ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintMask, 1549ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintOriginY, 1550ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE ); 1551ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1552ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* save a copy of current HintMap to use when drawing initial point */ 1553ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->firstHintMap = glyphpath->hintMap; /* structure copy */ 1554ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1555ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1556ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1557ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 1558ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_lineTo( CF2_GlyphPath glyphpath, 1559ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x, 1560ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y ) 1561ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1562ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed xOffset, yOffset; 1563ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector P0, P1; 1564ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1565ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1566ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* can't compute offset of zero length line, so ignore them */ 1567ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->currentCS.x == x && glyphpath->currentCS.y == y ) 1568ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 1569ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1570ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_computeOffset( glyphpath, 1571ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.x, 1572ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.y, 1573ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov x, 1574ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov y, 1575ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &xOffset, 1576ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &yOffset ); 1577ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1578ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* construct offset points */ 1579ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P0.x = glyphpath->currentCS.x + xOffset; 1580ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P0.y = glyphpath->currentCS.y + yOffset; 1581ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1.x = x + xOffset; 1582ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1.y = y + yOffset; 1583ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1584ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->moveIsPending ) 1585ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1586ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* emit offset 1st point as MoveTo */ 1587ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushMove( glyphpath, P0 ); 1588ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1589ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->moveIsPending = FALSE; /* adjust state machine */ 1590ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->pathIsOpen = TRUE; 1591ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1592ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->offsetStart1 = P1; /* record second point */ 1593ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1594ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1595ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->elemIsQueued ) 1596ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1597ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); 1598ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1599ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushPrevElem( glyphpath, 1600ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMap, 1601ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &P0, 1602ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1, 1603ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE ); 1604ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1605ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1606ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* queue the current element with offset points */ 1607ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->elemIsQueued = TRUE; 1608ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemOp = CF2_PathOpLineTo; 1609ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP0 = P0; 1610ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1 = P1; 1611ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1612ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* update current map */ 1613ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) 1614ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_build( &glyphpath->hintMap, 1615ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hStemHintArray, 1616ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->vStemHintArray, 1617ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintMask, 1618ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintOriginY, 1619ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE ); 1620ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1621ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.x = x; /* pre-offset current point */ 1622ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.y = y; 1623ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1624ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1625ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1626ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 1627ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_curveTo( CF2_GlyphPath glyphpath, 1628ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x1, 1629ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y1, 1630ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x2, 1631ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y2, 1632ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed x3, 1633ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed y3 ) 1634ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1635ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CF2_Fixed xOffset1, yOffset1, xOffset3, yOffset3; 1636ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_Vector P0, P1, P2, P3; 1637ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1638ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1639ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* TODO: ignore zero length portions of curve?? */ 1640ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_computeOffset( glyphpath, 1641ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.x, 1642ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.y, 1643ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov x1, 1644ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov y1, 1645ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &xOffset1, 1646ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &yOffset1 ); 1647ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_computeOffset( glyphpath, 1648ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov x2, 1649ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov y2, 1650ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov x3, 1651ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov y3, 1652ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &xOffset3, 1653ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &yOffset3 ); 1654ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1655ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* add momentum from the middle segment */ 1656ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->callbacks->windingMomentum += 1657ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_getWindingMomentum( x1, y1, x2, y2 ); 1658ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1659ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* construct offset points */ 1660ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P0.x = glyphpath->currentCS.x + xOffset1; 1661ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P0.y = glyphpath->currentCS.y + yOffset1; 1662ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1.x = x1 + xOffset1; 1663ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1.y = y1 + yOffset1; 1664ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* note: preserve angle of final segment by using offset3 at both ends */ 1665ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P2.x = x2 + xOffset3; 1666ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P2.y = y2 + yOffset3; 1667ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P3.x = x3 + xOffset3; 1668ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P3.y = y3 + yOffset3; 1669ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1670ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->moveIsPending ) 1671ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1672ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* emit offset 1st point as MoveTo */ 1673ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushMove( glyphpath, P0 ); 1674ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1675ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->moveIsPending = FALSE; 1676ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->pathIsOpen = TRUE; 1677ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1678ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->offsetStart1 = P1; /* record second point */ 1679ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1680ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1681ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->elemIsQueued ) 1682ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1683ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) ); 1684ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1685ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushPrevElem( glyphpath, 1686ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->hintMap, 1687ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &P0, 1688ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov P1, 1689ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE ); 1690ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1691ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1692ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* queue the current element with offset points */ 1693ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->elemIsQueued = TRUE; 1694ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemOp = CF2_PathOpCubeTo; 1695ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP0 = P0; 1696ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP1 = P1; 1697ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP2 = P2; 1698ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->prevElemP3 = P3; 1699ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1700ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* update current map */ 1701ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( cf2_hintmask_isNew( glyphpath->hintMask ) ) 1702ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_hintmap_build( &glyphpath->hintMap, 1703ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hStemHintArray, 1704ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->vStemHintArray, 1705ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintMask, 1706ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->hintOriginY, 1707ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FALSE ); 1708ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1709ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.x = x3; /* pre-offset current point */ 1710ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->currentCS.y = y3; 1711ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1712ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1713ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1714ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_LOCAL_DEF( void ) 1715ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_closeOpenPath( CF2_GlyphPath glyphpath ) 1716ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1717ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( glyphpath->pathIsOpen ) 1718ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1719ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( cf2_hintmap_isValid( &glyphpath->firstHintMap ) ); 1720ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1721ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* since we need to apply an offset to the implicit lineto, we make */ 1722ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* it explicit here */ 1723ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_lineTo( glyphpath, 1724ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->start.x, 1725ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->start.y ); 1726ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1727ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Draw previous element (the explicit LineTo we just created, */ 1728ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* above) and connect it to the start point, but with the offset we */ 1729ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* saved from the first element. */ 1730ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* Use the saved HintMap, too. */ 1731ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FT_ASSERT( glyphpath->elemIsQueued ); 1732ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1733ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cf2_glyphpath_pushPrevElem( glyphpath, 1734ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->firstHintMap, 1735ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &glyphpath->offsetStart0, 1736ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->offsetStart1, 1737ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TRUE ); 1738ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1739ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov /* reset state machine */ 1740ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->moveIsPending = TRUE; 1741ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->pathIsOpen = FALSE; 1742ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov glyphpath->elemIsQueued = FALSE; 1743ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1744ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1745ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1746ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1747ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov/* END */ 1748