summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c5258
1 files changed, 0 insertions, 5258 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c b/Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c
deleted file mode 100644
index e56cc7a0092..00000000000
--- a/Build/source/texk/dvisvgm/dvisvgm-src/libs/ff-woff/fontforge/nowakowskittfinstr.c
+++ /dev/null
@@ -1,5258 +0,0 @@
-/* Copyright (C) 2000-2012 by
- George Williams, Michal Nowakowski & Alexey Kryukov */
-
-/*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
-
- * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
-
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-#include "fontforgevw.h"
-#define _USE_MATH_DEFINES
-#include <ctype.h>
-#include <math.h>
-
-#include "ttf.h"
-#include "splinefont.h"
-#include "stemdb.h"
-
-extern int autohint_before_generate;
-
-int instruct_diagonal_stems = 1,
- instruct_serif_stems = 1,
- instruct_ball_terminals = 1,
- interpolate_strong = 1,
- interpolate_more_strong = 1, /* not applicable if interpolate_strong==0 */
- control_counters = 0;
-
-/* non-optimized instructions will be using a stack of depth 6, allowing
- * for easy testing whether the code leaves trash on the stack or not.
- */
-#define OPTIMIZE_TTF_INSTRS 1
-#if OPTIMIZE_TTF_INSTRS
-#define STACK_DEPTH 256
-#else
-#define STACK_DEPTH 6
-#endif
-
-/* define some often used instructions */
-#define SVTCA_y (0x00)
-#define SVTCA_x (0x01)
-#define SRP0 (0x10)
-#define SRP1 (0x11)
-#define SRP2 (0x12)
-#define SZP0 (0x13)
-#define SLOOP (0x17)
-#define RTG (0x18)
-#define SMD (0x1a)
-#define DUP (0x20)
-#define DEPTH (0x24)
-#define CALL (0x2b)
-#define MDAP (0x2e)
-#define MDAP_rnd (0x2f)
-#define IUP_y (0x30)
-#define IUP_x (0x31)
-#define SHP_rp2 (0x32)
-#define SHP_rp1 (0x33)
-#define SHPIX (0x38)
-#define IP (0x39)
-#define ALIGNRP (0x3c)
-#define MIAP_rnd (0x3f)
-#define ADD (0x60)
-#define MUL (0x63)
-#define NEG (0x65)
-#define SROUND (0x76)
-#define FLIPPT (0x80)
-#define MDRP_grey (0xc0)
-#define MDRP_min_black (0xc9)
-#define MDRP_min_white (0xca)
-#define MDRP_min_rnd_black (0xcd)
-#define MDRP_rp0_rnd_white (0xd6)
-#define MDRP_rp0_min_rnd_grey (0xdc)
-#define MDRP_rp0_min_rnd_black (0xdd)
-#define MIRP_min_black (0xe9)
-#define MIRP_min_rnd_black (0xed)
-#define MIRP_rp0_min_black (0xf9)
-#define MIRP_rp0_min_rnd_black (0xfd)
-
-
-/******************************************************************************
- *
- * Low-level routines to add data for PUSHes to bytecode instruction stream.
- * pushheader() adds PUSH preamble, then repeating addpoint() adds items.
- *
- * Numbers larger than 65535 are not supported (according to TrueType spec,
- * there can't be more points in a glyph, simple or compound). Negative
- * numbers aren't supported, either. So don't use these functions as they
- * are - there are higher-level ones further below, that handle things nicely.
- *
- ******************************************************************************/
-
-static uint8 *pushheader(uint8 *instrs, int isword, int tot) {
- if ( isword ) {
- if ( tot>8 ) {
- *instrs++ = 0x41; /* N(next word) Push words */
- *instrs++ = tot;
- } else
- *instrs++ = 0xb8+(tot-1); /* Push Words */
- } else {
- if ( tot>8 ) {
- *instrs++ = 0x40; /* N(next byte) Push bytes */
- *instrs++ = tot;
- } else
- *instrs++ = 0xb0+(tot-1); /* Push bytes */
- }
-return( instrs );
-}
-
-static uint8 *addpoint(uint8 *instrs,int isword,int pt) {
- if ( !isword ) {
- *instrs++ = pt;
- } else {
- *instrs++ = pt>>8;
- *instrs++ = pt&0xff;
- }
-return( instrs );
-}
-
-/* Exemplary high-level routines to add PUSH-es to bytecode instruction
- * stream. They handle negative numbers correctly. As they are used
- * in various roles here, some aliases are defined, so that the name
- * speaks for itself in the code.
- */
-
-static uint8 *pushpoint(uint8 *instrs,int pt) {
- instrs = pushheader(instrs,(pt>255)||(pt<0),1);
-return( addpoint(instrs,(pt>255)||(pt<0),pt));
-}
-
-#define pushnum(a, b) pushpoint(a, b)
-
-static uint8 *pushpointstem(uint8 *instrs, int pt, int stem) {
- int isword = pt>255 || stem>255 || pt<0 || stem<0;
- instrs = pushheader(instrs,isword,2);
- instrs = addpoint(instrs,isword,pt);
-return( addpoint(instrs,isword,stem));
-}
-
-#define push2points(a, b, c) pushpointstem(a, b, c)
-#define push2nums(a, b, c) pushpointstem(a, b, c)
-
-/* Push a bunch of point numbers (or other numbers) onto the stack.
- * TODO!
- * Possible strategies:
- * - push point by point (poor space efficiency)
- * - push all the stock at once (currently used, better, but has
- * poor space efficiency in case of a word among several bytes).
- * - push bytes and words separately
- */
-static uint8 *pushpoints(uint8 *instrs, int ptcnt, const int *pts) {
- int i, isword = 0;
- for (i=0; i<ptcnt; i++) if (pts[i]>255 || pts[i]<0) isword=1;
-
- /* It's an error to push more than STACK_DEPTH points. */
- if (ptcnt > STACK_DEPTH)
- IError("Truetype stack overflow will occur.");
-
- if (ptcnt > 255 && !isword) {
- instrs = pushpoints(instrs, 255, pts);
- ptcnt-=255;
- pts+=255;
- }
-
- instrs = pushheader(instrs,isword,ptcnt);
- for (i=0; i<ptcnt; i++) instrs = addpoint(instrs, isword, pts[i]);
-return( instrs );
-}
-
-#define pushnums(a, b, c) pushpoints(a, b, c)
-
-/* As we don't have "push F26dot6" command in truetype instructions,
- * we need to do this by hand. As we can explicitly push only 16-bit
- * quantities, we need to push a F26dot6 value in halves, shift left
- * the more significant half and add halves.
- *
- * There are no checks for overflow!
- */
-static uint8 *pushF26Dot6(uint8 *instrs, double num) {
- int a, elems[3];
- int negative=0;
-
- if (num < 0) {
- negative=1;
- num*=-1.0;
- }
-
- num *= 64;
- a = rint(num);
- elems[0] = a % 65536;
- elems[1] = (int)rint(a / 65536.0) % 65536;
- elems[2] = 16384;
-
- if (elems[1]) {
- instrs = pushpoints(instrs, 3, elems);
- *instrs++ = DUP;
- *instrs++ = MUL;
- *instrs++ = MUL;
- *instrs++ = ADD;
- }
- else instrs = pushpoint(instrs, elems[0]);
-
- if (negative) *instrs++ = NEG;
-
-return( instrs );
-}
-
-/* Compute an EF2Dot14 representation of a floating point number.
- * The number must be in range [-2.0 ... 1.0+(2^14-1)/(2^14) = 1.99993896...]
- *
- * There are no checks for overflow!
- */
-static int EF2Dot14(double num) {
-return( rint(num*16384) );
-}
-
-/* An apparatus for instructing sets of points with given truetype command.
- * The command must pop exactly 1 element from the stack and mustn't push any.
- * These points must be marked as 'touched' elsewhere! this function only
- * generates intructions.
- */
-static uint8 *instructpoints(uint8 *instrs, int ptcnt, const int *pts, uint8 command) {
- int i, use_sloop;
-
- use_sloop = 0;
- use_sloop |= (command == SHP_rp1);
- use_sloop |= (command == SHP_rp2);
- use_sloop |= (command == SHPIX);
- use_sloop |= (command == IP);
- use_sloop |= (command == FLIPPT);
- use_sloop |= (command == ALIGNRP);
- use_sloop = use_sloop && (ptcnt > 3);
-
- instrs = pushpoints(instrs, ptcnt<STACK_DEPTH?ptcnt:STACK_DEPTH-1, pts);
-
- if (use_sloop) {
- *instrs++ = DEPTH;
- *instrs++ = SLOOP;
- *instrs++ = command;
- }
- else for (i=0; i<(ptcnt<STACK_DEPTH?ptcnt:STACK_DEPTH-1); i++)
- *instrs++ = command;
-
- if (ptcnt>=STACK_DEPTH)
- instrs=instructpoints(instrs, ptcnt-(STACK_DEPTH-1), pts+(STACK_DEPTH-1), command);
-
-return( instrs );
-}
-
-/******************************************************************************
- *
- * Low-level routines for getting a cvt index for a stem width, assuming there
- * are any numbers in cvt. Includes legacy code for importing PS Private into
- * CVT.
- *
- ******************************************************************************/
-
-struct ttf_table *SFFindTable(SplineFont *sf,uint32 tag) {
- struct ttf_table *tab;
-
- for ( tab=sf->ttf_tables; tab!=NULL && tab->tag!=tag; tab=tab->next );
-return( tab );
-}
-
-int TTF__getcvtval(SplineFont *sf,int val) {
- int i;
- struct ttf_table *cvt_tab = SFFindTable(sf,CHR('c','v','t',' '));
-
- if ( cvt_tab==NULL ) {
- cvt_tab = chunkalloc(sizeof(struct ttf_table));
- cvt_tab->tag = CHR('c','v','t',' ');
- cvt_tab->maxlen = 200;
- cvt_tab->data = malloc(100*sizeof(short));
- cvt_tab->next = sf->ttf_tables;
- sf->ttf_tables = cvt_tab;
- }
- for ( i=0; (int)sizeof(uint16)*i<cvt_tab->len; ++i ) {
- int tval = (int16) memushort(cvt_tab->data,cvt_tab->len, sizeof(uint16)*i);
- if ( val>=tval-1 && val<=tval+1 )
-return( i );
- }
- if ( (int)sizeof(uint16)*i>=cvt_tab->maxlen ) {
- if ( cvt_tab->maxlen==0 ) cvt_tab->maxlen = cvt_tab->len;
- cvt_tab->maxlen += 200;
- cvt_tab->data = realloc(cvt_tab->data,cvt_tab->maxlen);
- }
- memputshort(cvt_tab->data,sizeof(uint16)*i,val);
- cvt_tab->len += sizeof(uint16);
-return( i );
-}
-
-/* by default sign is unimportant in the cvt
- * For some instructions anyway, but not for MIAP so this routine has
- * been broken in two.
- */
-int TTF_getcvtval(SplineFont *sf,int val) {
- if ( val<0 ) val = -val;
-return( TTF__getcvtval(sf,val));
-}
-
-/* We are given a stem weight and try to find matching one in CVT.
- * If none found, we return -1.
- */
-static StdStem *CVTSeekStem(int xdir, GlobalInstrCt *gic, double value, int can_fail) {
- StdStem *mainstem = xdir?&(gic->stdvw):&(gic->stdhw);
- StdStem *otherstems = xdir?gic->stemsnapv:gic->stemsnaph;
- StdStem *closest = NULL;
- int otherstemcnt = xdir?gic->stemsnapvcnt:gic->stemsnaphcnt;
- int i;
- double mindelta=1e20, delta, closestwidth=1e20;
-
- if (mainstem->width == -1)
-return NULL;
-
- value = fabs(value);
- delta = fabs(mainstem->width - value);
-
- if (delta < mindelta) {
- mindelta = delta;
- closestwidth = rint(mainstem->width);
- closest = mainstem;
- }
-
- for (i=0; i<otherstemcnt; i++) {
- delta = fabs(otherstems[i].width - value);
-
- if (delta < mindelta) {
- mindelta = delta;
- closestwidth = otherstems[i].width;
- closest = otherstems+i;
- }
- }
-
- if (mindelta <= gic->fudge)
-return closest;
- if (value/closestwidth < 1.11 && value/closestwidth > 0.9)
-return closest;
- if (can_fail)
-return NULL;
-return closest;
-}
-
-/******************************************************************************
- ******************************************************************************
- **
- ** We need to initialize global instructing context before autoinstructing
- ** a glyph, because we want to be sure that global hinting tables (cvt, prep,
- ** fpgm) were (or weren't) properly set up.
- **
- ******************************************************************************
- ******************************************************************************/
-
-/* Helper routines: read PS private entry and return its contents.
- */
-static int GetBlueFuzz(SplineFont *sf) {
- char *str, *end;
-
- if ( sf->private==NULL || (str=PSDictHasEntry(sf->private,"BlueFuzz"))==NULL || !isdigit(str[0]) )
-return 1;
-return strtod(str, &end);
-}
-
-/* Return BlueScale as PPEM at which we have to stop suppressing overshoots */
-static int GetBlueScale(SplineFont *sf) {
- char *str, *end;
- double bs;
- int result;
- if ( sf->private==NULL || (str=PSDictHasEntry(sf->private,"BlueScale"))==NULL )
-return 42;
-
- bs = strtod(str, &end);
- if (end==str || bs<=0.0) bs=0.039625;
- bs*=240;
- bs+=0.49;
- bs*=300.0/72.0;
-
- result = (int)rint(bs);
- if (result>255) result = 255; /* Who would need such blue scale??? */
-
-return result;
-}
-
-static real *ParsePSArray(const char *str, int *rescnt) {
- char *end;
- real d, *results=NULL;
-
- if ((rescnt == NULL) || (str == NULL))
-return NULL;
-
- *rescnt = 0;
-
- while (*str)
- {
- while (!isdigit(*str) && *str!='-' && *str!='+' && *str!='.' && *str!='\0')
- ++str;
-
- if ( *str=='\0' )
- break;
-
- d = strtod(str, &end);
-
- if ( d>=-32768 && d<=32767 ) {
- if (*rescnt) {
- results = realloc(results, sizeof(real)*(++(*rescnt)));
- results[*rescnt-1] = d;
- }
- else (results = calloc(*rescnt=1, sizeof(real)))[0] = d;
- }
-
- str = end;
- }
-
-return results;
-}
-
-static real *GetNParsePSArray(SplineFont *sf, const char *name, int *rescnt) {
-return ParsePSArray(PSDictHasEntry(sf->private, name), rescnt);
-}
-
-/* Tell if the two segments, [b1,o1] and [b2,o2] intersect.
- * This can be used to determine whether blues or stems overlap.
- */
-static int SegmentsOverlap(real b1, real o1, real b2, real o2) {
- real t;
-
- if (b1 > o1) {
- t = o1;
- o1 = b1;
- b1 = t;
- }
-
- if (b2 > o2) {
- t = o2;
- o2 = b2;
- b2 = t;
- }
-
-return !((b2 > o1) || (o2 < b1));
-}
-
-/* To be used with qsort() - sorts BlueZone array by base in ascending order.
- */
-static int SortBlues(const void *a, const void *b) {
- return ((BlueZone *)a)->base > ((BlueZone *)b)->base;
-}
-
-/* Import blue data into global instructing context. Include family blues too.
- * We assume that blues are needed for family blues to make sense. If there are
- * only family blues, we treat them as normal blues. Otherwise, if a family blue
- * zone doesn't match any normal blue zone, or if they match perfectly,
- * it is ignored.
- */
-static void GICImportBlues(GlobalInstrCt *gic) {
- int bluecnt = 0;
- int i, j, cnt;
- real *values;
-
- int HasPSBlues =
- (PSDictHasEntry(gic->sf->private, "BlueValues") != NULL) ||
- (PSDictHasEntry(gic->sf->private, "OtherBlues") != NULL);
-
- int HasPSFamilyBlues =
- (PSDictHasEntry(gic->sf->private, "FamilyBlues") != NULL) ||
- (PSDictHasEntry(gic->sf->private, "FamilyOtherBlues") != NULL);
-
- const char *PrimaryBlues = HasPSBlues ? "BlueValues" : "FamilyBlues";
- const char *OtherBlues = HasPSBlues ? "OtherBlues" : "FamilyOtherBlues";
-
- if (HasPSBlues || HasPSFamilyBlues){
- values = GetNParsePSArray(gic->sf, PrimaryBlues, &cnt);
- cnt /= 2;
- if (cnt > 7) cnt = 7;
-
- if (values != NULL) {
- gic->bluecnt = bluecnt = cnt;
-
- /* First pair is a bottom zone (see Type1 specification). */
- gic->blues[0].base = values[1];
- gic->blues[0].overshoot = values[0];
- gic->blues[0].family_base = strtod("NAN", NULL);
-
- /* Next pairs are top zones (see Type1 specification). */
- for (i=1; i<bluecnt; i++) {
- gic->blues[i].family_base = strtod("NAN", NULL);
- gic->blues[i].base = values[2*i];
- gic->blues[i].overshoot = values[2*i+1];
- }
-
- free(values);
- }
-
- values = GetNParsePSArray(gic->sf, OtherBlues, &cnt);
- cnt /= 2;
- if (cnt > 5) cnt = 5;
-
- if (values != NULL) {
- gic->bluecnt += cnt;
-
- /* All pairs are bottom zones (see Type1 specification). */
- for (i=0; i<cnt; i++) {
- gic->blues[i+bluecnt].family_base = strtod("NAN", NULL);
- gic->blues[i+bluecnt].base = values[2*i+1];
- gic->blues[i+bluecnt].overshoot = values[2*i];
- }
-
- free(values);
- bluecnt += cnt;
- }
-
- /* Add family data to blues */
- if (HasPSBlues && HasPSFamilyBlues) {
- values = GetNParsePSArray(gic->sf, "FamilyBlues", &cnt);
- cnt /= 2;
- if (cnt > 7) cnt = 7;
-
- if (values != NULL) {
- /* First pair is a bottom zone (see Type1 specification). */
- for (j=0; j<bluecnt; j++)
- if (isfinite(gic->blues[j].family_base))
- continue;
- else if (values[1] != gic->blues[j].base &&
- SegmentsOverlap(gic->blues[j].base,
- gic->blues[j].overshoot,
- values[0], values[1]))
- gic->blues[j].family_base = values[1];
-
- /* Next pairs are top zones (see Type1 specification). */
- for (i=1; i<cnt; i++) {
- for (j=0; j<bluecnt; j++)
- if (isfinite(gic->blues[j].family_base))
- continue;
- else if (values[2*i] != gic->blues[j].base &&
- SegmentsOverlap(gic->blues[j].base,
- gic->blues[j].overshoot,
- values[2*i], values[2*i+1]))
- gic->blues[j].family_base = values[2*i];
- }
-
- free(values);
- }
-
- values = GetNParsePSArray(gic->sf, "FamilyOtherBlues", &cnt);
- cnt /= 2;
- if (cnt > 5) cnt = 5;
-
- if (values != NULL) {
- /* All pairs are bottom zones (see Type1 specification). */
- for (i=0; i<cnt; i++) {
- for (j=0; j<bluecnt; j++)
- if (isfinite(gic->blues[j].family_base))
- continue;
- else if (values[2*i+1] != gic->blues[j].base &&
- SegmentsOverlap(gic->blues[j].base,
- gic->blues[j].overshoot,
- values[2*i], values[2*i+1]))
- gic->blues[j].family_base = values[2*i+1];
- }
-
- free(values);
- }
- }
- }
- else if (gic->bd->bluecnt) {
- /* If there are no PS private entries, we have */
- /* to use FF's quickly guessed fallback blues. */
- gic->bluecnt = bluecnt = gic->bd->bluecnt;
-
- for (i=0; i<bluecnt; i++) {
- gic->blues[i].family_base = strtod("NAN", NULL);
- gic->blues[i].family_cvtindex = -1;
-
- if (gic->bd->blues[i][1] <= 0) {
- gic->blues[i].base = gic->bd->blues[i][1];
- gic->blues[i].overshoot = gic->bd->blues[i][0];
- }
- else {
- gic->blues[i].base = gic->bd->blues[i][0];
- gic->blues[i].overshoot = gic->bd->blues[i][1];
- }
- }
- }
-
- /* 'highest' and 'lowest' are not to be set yet. */
- for (i=0; i<gic->bluecnt; i++)
- gic->blues[i].highest = gic->blues[i].lowest = -1;
-
- /* I assume ascending order in snap_to_blues(). */
- qsort(gic->blues, gic->bluecnt, sizeof(BlueZone), SortBlues);
-}
-
-/* To be used with qsort() - sorts StdStem array by width in ascending order.
- */
-static int SortStems(const void *a, const void *b) {
- return ((StdStem *)a)->width > ((StdStem *)b)->width;
-}
-
-/* Import stem data into global instructing context. We deal only with
- * horizontal or vertical stems (xdir decides) here. If Std*W is not specified,
- * but there exists StemSnap*, we'll make up a fake Std*V as a fallback.
- * Subtle manipulations with Std*W's value can result in massive change of
- * font appearance at some pixel sizes, because it's used as a base for
- * normalization of all other stems.
- */
-static void GICImportStems(int xdir, GlobalInstrCt *gic) {
- int i, cnt, next;
- real *values;
- const char *s_StdW = xdir?"StdVW":"StdHW";
- const char *s_StemSnap = xdir?"StemSnapV":"StemSnapH";
- StdStem *stdw = xdir?&(gic->stdvw):&(gic->stdhw);
- StdStem **stemsnap = xdir?&(gic->stemsnapv):&(gic->stemsnaph);
- int *stemsnapcnt = xdir?&(gic->stemsnapvcnt):&(gic->stemsnaphcnt);
-
- if ((values = GetNParsePSArray(gic->sf, s_StdW, &cnt)) != NULL) {
- stdw->width = *values;
- free(values);
- }
-
- if ((values = GetNParsePSArray(gic->sf, s_StemSnap, &cnt)) != NULL) {
- *stemsnap = (StdStem *)calloc(cnt, sizeof(StdStem));
-
- for (next=i=0; i<cnt; i++)
- if (values[i] != gic->stdhw.width)
- (*stemsnap)[next++].width = values[i];
-
- if (!next) {
- free(*stemsnap);
- *stemsnap = NULL;
- }
-
- *stemsnapcnt = next;
- free(values);
-
- /* I assume ascending order here and in normalize_stems(). */
- qsort(*stemsnap, *stemsnapcnt, sizeof(StdStem), SortStems);
- }
-
- /* No StdW, but StemSnap exists? */
- if (stdw->width == -1 && *stemsnap != NULL) {
- cnt = *stemsnapcnt;
- i = cnt/2;
- stdw->width = (*stemsnap)[i].width;
- memmove((*stemsnap)+i, (*stemsnap)+i+1, cnt-i-1);
-
- if (--(*stemsnapcnt) == 0) {
- free(*stemsnap);
- *stemsnap = NULL;
- }
- }
-}
-
-/* Assign CVT indices to blues and stems in global instructing context. In case
- * we can't implant it because of already existent cvt table, reassign the cvt
- * indices, picking them from existing cvt table (thus a cvt value can't be
- * considered 'horizontal' or 'vertical', and reliable stem normalization is
- * thus impossible) and adding some for new values.
- */
-static void init_cvt(GlobalInstrCt *gic) {
- int i, cvtindex, cvtsize;
- struct ttf_table *tab;
- uint8 *cvt;
-
- cvtsize = 1;
- if (gic->stdhw.width != -1) cvtsize++;
- if (gic->stdvw.width != -1) cvtsize++;
- cvtsize += gic->stemsnaphcnt;
- cvtsize += gic->stemsnapvcnt;
- cvtsize += gic->bluecnt * 2; /* possible family blues */
-
- cvt = calloc(cvtsize, cvtsize * sizeof(int16));
- cvtindex = 0;
-
- /* Assign cvt indices */
- for (i=0; i<gic->bluecnt; i++) {
- gic->blues[i].cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->blues[i].base));
-
- if (isfinite(gic->blues[i].family_base)) {
- gic->blues[i].family_cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->blues[i].family_base));
- }
- }
-
- if (gic->stdhw.width != -1) {
- gic->stdhw.cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->stdhw.width));
- }
-
- for (i=0; i<gic->stemsnaphcnt; i++) {
- gic->stemsnaph[i].cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->stemsnaph[i].width));
- }
-
- if (gic->stdvw.width != -1) {
- gic->stdvw.cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->stdvw.width));
- }
-
- for (i=0; i<gic->stemsnapvcnt; i++) {
- gic->stemsnapv[i].cvtindex = cvtindex;
- memputshort(cvt, 2*cvtindex++, rint(gic->stemsnapv[i].width));
- }
-
- cvtsize = cvtindex;
- cvt = realloc(cvt, cvtsize * sizeof(int16));
-
- /* Try to implant the new cvt table */
- gic->cvt_done = 0;
-
- tab = SFFindTable(gic->sf, CHR('c','v','t',' '));
-
- if ( tab==NULL ) {
- tab = chunkalloc(sizeof(struct ttf_table));
- tab->next = gic->sf->ttf_tables;
- gic->sf->ttf_tables = tab;
- tab->tag = CHR('c','v','t',' ');
-
- tab->len = tab->maxlen = cvtsize * sizeof(int16);
- if (tab->maxlen >256) tab->maxlen = 256;
- tab->data = cvt;
-
- gic->cvt_done = 1;
- }
- else {
- if (tab->len >= cvtsize * (int)sizeof(int16) &&
- memcmp(cvt, tab->data, cvtsize * sizeof(int16)) == 0)
- gic->cvt_done = 1;
-
- free(cvt);
-
- if (!gic->cvt_done) {
- ff_post_error(_("Can't insert 'cvt'"),
- _("There already exists a 'cvt' table, perhaps legacy. "
- "FontForge can use it, but can't make any assumptions on "
- "values stored there, so generated instructions will be of "
- "lower quality. If legacy hinting is to be scrapped, it is "
- "suggested to clear the `cvt` and repeat autoinstructing. "
- ));
- }
- }
-
- if (gic->cvt_done)
-return;
-
- /* Fallback mode starts here. */
-
- for (i=0; i<gic->bluecnt; i++)
- gic->blues[i].cvtindex =
- TTF_getcvtval(gic->sf, gic->blues[i].base);
-
- if (gic->stdhw.width != -1)
- gic->stdhw.cvtindex =
- TTF_getcvtval(gic->sf, gic->stdhw.width);
-
- for (i=0; i<gic->stemsnaphcnt; i++)
- gic->stemsnaph[i].cvtindex =
- TTF_getcvtval(gic->sf, gic->stemsnaph[i].width);
-
- if (gic->stdvw.width != -1)
- gic->stdvw.cvtindex =
- TTF_getcvtval(gic->sf, gic->stdvw.width);
-
- for (i=0; i<gic->stemsnapvcnt; i++)
- gic->stemsnapv[i].cvtindex =
- TTF_getcvtval(gic->sf, gic->stemsnapv[i].width);
-}
-
-/* We'll need at least STACK_DEPTH stack levels and a twilight point (and thus
- * also a twilight zone). We also currently define some functions in fpgm.
- * We must ensure this is indicated in the 'maxp' table.
- *
- * We also need two storage cells. As we now use SPVFS to set projection
- * vector for diagonal hinting, we have to adjust values taken by SPVFS,
- * so that diagonals look cleanly in all aspect ratios. Adjustments are
- * not trivial to compute, so we do this once (in prep) and store them
- * in storage[0] (for X direction) and storage[1] (for Y direction).
- */
-static void init_maxp(GlobalInstrCt *gic) {
- struct ttf_table *tab = SFFindTable(gic->sf, CHR('m','a','x','p'));
- uint16 zones, twpts, store, fdefs, stack;
-
- if ( tab==NULL ) {
- tab = chunkalloc(sizeof(struct ttf_table));
- tab->next = gic->sf->ttf_tables;
- gic->sf->ttf_tables = tab;
- tab->tag = CHR('m','a','x','p');
- }
-
- if ( tab->len<32 ) {
- tab->data = realloc(tab->data,32);
- memset(tab->data+tab->len,0,32-tab->len);
- tab->len = tab->maxlen = 32;
- }
-
- zones = memushort(tab->data, 32, 7*sizeof(uint16));
- twpts = memushort(tab->data, 32, 8*sizeof(uint16));
- store = memushort(tab->data, 32, 9*sizeof(uint16));
- fdefs = memushort(tab->data, 32, 10*sizeof(uint16));
- stack = memushort(tab->data, 32, 12*sizeof(uint16));
-
- if (gic->fpgm_done && zones<2) zones=2;
- if (gic->fpgm_done && twpts<1) twpts=1;
- if (gic->fpgm_done && gic->prep_done && store<2) store=2;
- if (gic->fpgm_done && fdefs<22) fdefs=22;
- if (stack<STACK_DEPTH) stack=STACK_DEPTH;
-
- memputshort(tab->data, 7*sizeof(uint16), zones);
- memputshort(tab->data, 8*sizeof(uint16), twpts);
- memputshort(tab->data, 9*sizeof(uint16), store);
- memputshort(tab->data,10*sizeof(uint16), fdefs);
- memputshort(tab->data,12*sizeof(uint16), stack);
-}
-
-/* Other hinting software puts certain actions in FPGM to ease developer's life
- * and compress the code. I feel that having a 'standard' library of functions
- * could also help FF users.
- *
- * Caution! This code is heavily relied by autohinting. Any other code should
- * be placed below it. It's good to first clear font's hinting tables, then
- * autohint it, and then insert user's own code and do the manual hinting of
- * glyphs that do need it.
- */
-static void init_fpgm(GlobalInstrCt *gic) {
- uint8 new_fpgm[] =
- {
- /* Function 0: position a point within a blue zone (given via cvt).
- * Note: in case of successful init of 'cvt' and 'prep' this function
- * could be much simpler.
- * Syntax: PUSHB_3 point cvt_of_blue 0 CALL
- */
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x2c, // FDEF
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x13, // SZP0
- 0x4b, // MPPEM
- 0xb0, // PUSHB_1 - under this ppem blues will be specially rounded
- GetBlueScale(gic->sf),
- 0x50, // LT
- 0x58, // IF
- 0xb0, // PUSHB_0
- 0x4a, // 74
- 0x76, // SROUND - round blues a bit up to grid
- 0x59, // EIF
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x23, // SWAP
- 0x3f, // MIAP[rnd] - blue zone positioned here
- 0x18, // RTG - round state for overshoots in monochrome mode
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0x3d, // RTDG - round state for overshoots in antialiased mode
- 0x59, // EIF
- 0x4b, // MPPEM
- 0xb0, // PUSHB_1 - under following ppem overshoots will be suppressed
- GetBlueScale(gic->sf),
- 0x50, // LT
- 0x58, // IF
- 0x7d, // RDTG - suppress overshoots
- 0x59, // EIF
- 0x20, // DUP
- 0xd4, // MDRP[rp0,rnd,grey]
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x13, // SZP0
- 0x2e, // MDAP[no-rnd]
- 0x18, // RTG
- 0x2d, // ENDF
-
- /* Function 1: Place given point relatively to previous, maintaining the
- * minimum distance. Then call FPGM 12 to check if the point's gridfitted
- * position is too far from its original position, and correct it, if necessary.
- * Syntax: PUSB_2 point 1 CALL
- */
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x2c, // FDEF
- 0x20, // DUP
- 0xda, // MDRP[rp0,min,white]
- 0xb0, // PUSHB_1
- 0x0c, // 12
- 0x2b, // CALL
- 0x2d, // ENDF
-
- /* Function 2: Below given ppem, substitute the width with cvt entry.
- * Leave the resulting width on the stack. Used as the first step in
- * normalizing cvt stems, see normalize_stem().
- * Syntax: PUSHX_3 width cvt_index ppem 2 CALL
- */
- 0xb0, // PUSHB_1
- 0x02, // 2
- 0x2c, // FDEF
- 0x4b, // MPPEM
- 0x52, // GT
- 0x58, // IF
- 0x45, // RCVT
- 0x23, // SWAP
- 0x59, // EIF
- 0x21, // POP
- 0x2d, // ENDF
-
- /* Function 3: round a stack element as a black distance, respecting
- * minimum distance of 1px. This is used for rounding stems after width
- * normalization. Often preceeded with SROUND, so finally sets RTG.
- * Leaves the rounded width on the stack.
- * Syntax: PUSHX_2 width_to_be_rounded 3 CALL
- */
- 0xb0, // PUSHB_1
- 0x03, // 3
- 0x2c, // FDEF
- 0x69, // ROUND[black]
- 0x18, // RTG
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x40, // 64, that's one pixel as F26Dot6
- 0x50, // LT
- 0x58, // IF
- 0x21, // POP
- 0xb0, // PUSHB_1
- 0x40, // 64
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 4: Position the second edge of a stem that is not normally
- * regularized via cvt (but we snap it to cvt width below given ppem).
- * Vertical stems need special round state when not snapped to cvt
- * (basically, they are shortened by 0.25px before being rounded).
- * Syntax: PUSHX_5 pt cvt_index chg_rp0 ppem 4 CALL
- */
- 0xb0, // PUSHB_1
- 0x04, // 4
- 0x2c, // FDEF
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0x21, // POP
- 0x23, // SWAP
- 0x21, // POP
- 0x7a, // ROFF
- 0x58, // IF
- 0xdd, // MDRP[rp0,min,rnd,black]
- 0x1b, // ELSE
- 0xcd, // MDRP[min,rnd,black]
- 0x59, // EIF
- 0x1b, // ELSE
- 0x4b, // MPPEM
- 0x52, // GT
- 0x58, // IF
- 0x58, // IF
- 0xfd, // MIRP[rp0,min,rnd,black]
- 0x1b, // ELSE
- 0xed, // MIRP[min,rnd,black]
- 0x59, // EIF
- 0x1b, // ELSE
- 0x23, // SWAP
- 0x21, // POP
- 0xb0, // PUSHB_1
- 0x05, // 5
- 0x2b, // CALL
- 0x58, // IF
- 0xb0, // PUSHB_1
- 0x46, // 70
- 0x76, // SROUND
- 0x59, // EIF
- 0x58, // IF
- 0xdd, // MDRP[rp0,min,rnd,black]
- 0x1b, // ELSE
- 0xcd, // MDRP[min,rnd,black]
- 0x59, // EIF
- 0x59, // EIF
- 0x59, // EIF
- 0x18, // RTG
- 0x2d, // ENDF
-
- /* Function 5: determine if we are hinting vertically. The function
- * is crude and it's use is limited to conditions set by SVTCA[].
- * Syntax: PUSHB_1 5 CALL; leaves boolean on the stack.
- */
- 0xb0, // PUSHB_1
- 0x05, // 5
- 0x2c, // FDEF
- 0x0d, // GFV
- 0x5c, // NOT
- 0x5a, // AND
- 0x2d, // ENDF
-
- /* Function 6: check if we are hinting in grayscale.
- * CAUTION! Older FreeType versions lie if asked.
- * Syntax: PUSHB_1 6 CALL; leaves boolean on the stack.
- */
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2c, // FDEF
- 0xb1, // PUSHB_2
- 0x22, // 34
- 0x01, // 1
- 0x88, // GETINFO
- 0x50, // LT
- 0x58, // IF
- 0xb0, // PUSHB_1
- 0x20, // 32
- 0x88, // GETINFO
- 0x5c, // NOT
- 0x5c, // NOT
- 0x1b, // ELSE
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 7: check if we are hinting in cleartype.
- * CAUTION! FreeType doesn't support that, as subpixel
- * filtering is usually done by higher level library.
- * Syntax: PUSHB_1 7 CALL; leaves boolean on the stack.
- */
- 0xb0, // PUSHB_1
- 0x07, // 7
- 0x2c, // FDEF
- 0xb1, // PUSHB_2
- 0x24, // 36
- 0x01, // 1
- 0x88, // GETINFO
- 0x50, // LT
- 0x58, // IF
- 0xb0, // PUSHB_1
- 0x40, // 64
- 0x88, // GETINFO
- 0x5c, // NOT
- 0x5c, // NOT
- 0x1b, // ELSE
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 8: Interpolate a point between
- * two other points and snap it to the grid.
- * Syntax: PUSHX_4 pt_to_ip rp1 rp2 8 CALL;
- */
- 0xb0, // PUSHB_1
- 0x08, // 8
- 0x2c, // FDEF
- 0x12, // SRP2
- 0x11, // SRP1
- 0x20, // DUP
- 0x39, // IP
- 0x2f, // MDAP[rnd]
- 0x2d, // ENDF
-
- /* Function 9: Link a serif-like element edge to the opposite
- * edge of the base stem when rounding down to grid, but ensure
- * that its distance from the reference point is larger than
- * the base stem width at least to a specified amount of pixels.
- * Syntax: PUSHX_3 min_dist inner_pt outer_pt CALL;
- */
- 0xb0, // PUSHB_1
- 0x09, // 9
- 0x2c, // FDEF
- 0x20, // DUP
- 0x7d, // RDTG
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0xc4, // MDRP[min,grey]
- 0x1b, // ELSE
- 0xcd, // MDRP[min,rnd,black]
- 0x59, // EIF
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x03, // 3
- 0x25, // CINDEX
- 0x49, // MD[grid]
- 0x23, // SWAP
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x04, // 4
- 0x26, // MINDEX
- 0x4a, // MD[orig]
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x50, // LT
- 0x58, // IF
- 0x8a, // ROLL
- 0x65, // NEG
- 0x8a, // ROLL
- 0x61, // SUB
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x50, // LT
- 0x58, // IF
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x8a, // ROLL
- 0x8a, // ROLL
- 0x61, // SUB
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x52, // GT
- 0x58, // IF
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x59, // EIF
- 0x18, // RTG
- 0x2d, // ENDF
-
- /* Function 10: depending from the hinting mode (grayscale or mono) set
- * rp0 either to pt1 or to pt2. This is used to link serif-like elements
- * either to the opposite side of the base stem or to the same side (i. e.
- * left-to-left and right-to-right).
- * Syntax: PUSHX_3 pt2 pt1 10 CALL
- */
- 0xb0, // PUSHB_1
- 0x0a, // 10
- 0x2c, // FDEF
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0x21, // POP
- 0x10, // SRP0
- 0x1b, // ELSE
- 0x10, // SRP0
- 0x21, // POP
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 11: similar to FPGM 1, but places a point without
- * maintaining the minimum distance.
- * Syntax: PUSHX_2 point 11 CALL
- */
- 0xb0, // PUSHB_1
- 0x0b, // 11
- 0x2c, // FDEF
- 0x20, // DUP
- 0xd2, // MDRP[rp0,white]
- 0xb0, // PUSHB_1
- 0x0c, // 12
- 0x2b, // CALL
- 0x2d, // ENDF
-
- /* Function 12: Check if the gridfitted position of the point is too far
- * from its original position, and shift it, if necessary. The function is
- * used to place vertical stems, it assures almost linear advance width
- * to PPEM scaling. Shift amount is capped to at most 1 px to prevent some
- * weird artifacts at very small ppems. In cleartype mode, no shift
- * is made at all.
- * Syntax: PUSHX_2 point 12 CALL
- */
- 0xb0, // PUSHB_1
- 0x0c, // 12
- 0x2c, // FDEF
- 0x20, // DUP
- 0x2f, // MDAP[rnd], this is needed for grayscale mode
- 0xb0, // PUSHB_1
- 0x07, // 7
- 0x2b, // CALL
- 0x5c, // NOT
- 0x58, // IF
- 0x20, // DUP
- 0x20, // DUP
- 0x47, // GC[cur]
- 0x23, // SWAP
- 0x46, // GC[orig]
- 0x61, // SUB
- 0x6a, // ROUND[white]
- 0x20, // DUP
- 0x58, // IF
- 0x20, // DUP
- 0x64, // ABS
- 0x62, // DIV
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x21, // POP
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 13: Interpolate a HStem edge's reference point between two other points
- * and snap it to the grid. Then compare its new position with the ungridfitted
- * position of the second edge. If the gridfitted point belongs to the bottom edge
- * and now it is positioned above the top edge's original coordinate, then shift it
- * one pixel down; similarly, if the interpolation resulted in positioning the top
- * edge below the original coordinate of the bottom edge, shift it one pixel up.
- * Syntax: PUSHX_6 other_edge_refpt pt_to_ip rp1 rp2 13 CALL
- */
- 0xb0, // PUSHB_1
- 0x0d, // 13
- 0x2c, // FDEF
- 0x12, // SRP2
- 0x11, // SRP1
- 0x20, // DUP
- 0x20, // DUP
- 0x39, // IP
- 0x2f, // MDAP[rnd]
- 0x20, // DUP
- 0x8a, // ROLL
- 0x20, // DUP
- 0x47, // GC[orig]
- 0x8a, // ROLL
- 0x46, // GC[cur]
- 0x61, // SUB
- 0x23, // SWAP
- 0x8a, // ROLL
- 0x20, // DUP
- 0x8a, // ROLL
- 0x23, // SWAP
- 0x4A, // MD[orig]
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x50, // LT
- 0x58, // IF
- 0x23, // SWAP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x52, // GT
- 0x58, // IF
- 0xb0, // PUSHB_1
- 0x40, // 64
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x23, // SWAP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x50, // LT
- 0x58, // IF
- 0xb0, // PUSHB_1
- 0x40, // 64
- 0x65, // NEG
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x59, // EIF
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 14: Link two points using MDRP without maintaining
- * the minimum distance. In antialiased mode use rounding to
- * double grid for this operation, otherwise ensure there is no
- * distance between those two points below the given PPEM (i. e.
- * points are aligned). The function is used for linking nested
- * stems to each other, and guarantees their relative positioning
- * is preserved in the gridfitted outline.
- * Syntax: PUSHX_4 ppem ref_pt base_pt 14 CALL;
- */
- 0xb0, // PUSHB_1
- 0x0e, // 14
- 0x2c, // FDEF
- 0xb0, // PUSHB_1
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0x3d, // RTDG
- 0xd6, // MDRP[rp0,rnd,white]
- 0x18, // RTG
- 0x21, // POP
- 0x21, // POP
- 0x1b, // ELSE
- 0x20, // DUP
- 0xd6, // MDRP[rp0,rnd,white]
- 0x8a, // ROLL
- 0x4b, // MPPEM
- 0x52, // GT
- 0x58, // IF
- 0x20, // DUP
- 0x8a, // ROLL
- 0x23, // SWAP
- 0x49, // MD[grid]
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x55, // NEQ
- 0x58, // IF
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 15: similar to FPGM 1, but used to position a stem
- * relatively to the previous stem preserving the counter width
- * equal to the distance between another pair of previously positioned
- * stems. Thus it serves nearly the same purpose as PS counter hints.
- * Syntax: PUSHX_6 master_counter_start_pt master_counter_end_pt
- * current_counter_start_pt current_counter_end_pt ppem 15 CALL;
- */
- 0xb0, // PUSHB_1
- 0x0f, // 15
- 0x2c, // FDEF
- 0x23, // SWAP
- 0x20, // DUP
- 0xd6, // MDRP[rp0,rnd,white]
- 0x20, // DUP
- 0x2f, // MDAP[rnd], this is needed for grayscale mode
- 0xb0, // PUSHB_1
- 0x07, // 7
- 0x2b, // CALL
- 0x5c, // NOT
- 0x58, // IF
- 0x23, // SWAP
- 0x20, // DUP
- 0x58, // IF
- 0x4b, // MPPEM
- 0x53, // GTEQ
- 0x1b, // ELSE
- 0x21, // POP
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x59, // EIF
- 0x58, // IF
- 0x8a, // ROLL
- 0xb0, // PUSHB_1
- 0x04, // 4
- 0x26, // MINDEX
- 0x49, // MD[grid]
- 0x23, // SWAP
- 0x8a, // ROLL
- 0x23, // SWAP
- 0x20, // DUP
- 0x8a, // ROLL
- 0x49, // MD[grid]
- 0x8a, // ROLL
- 0x23, // SWAP
- 0x61, // SUB
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x21, // POP
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 16: Same as FPGM 1, but calls FPGM 18 rather than FPGM 12
- * and thus takes 3 arguments.
- * Syntax: PUSHX_3 ref_point point 16 CALL
- */
- 0xb0, // PUSHB_1
- 0x10, // 16
- 0x2c, // FDEF
- 0x20, // DUP
- 0xda, // MDRP[rp0,min,white]
- 0xb0, // PUSHB_1
- 0x12, // 18
- 0x2b, // CALL
- 0x2d, // ENDF
-
- /* Function 17: Same as FPGM 11, but calls FPGM 18 rather than FPGM 12
- * and thus takes 3 arguments.
- * Syntax: PUSHX_3 ref_point point 17 CALL
- */
- 0xb0, // PUSHB_1
- 0x11, // 17
- 0x2c, // FDEF
- 0x20, // DUP
- 0xd2, // MDRP[rp0,white]
- 0xb0, // PUSHB_1
- 0x12, // 18
- 0x2b, // CALL
- 0x2d, // ENDF
-
- /* Function 18: this is a special version of FPGM 12, used when the counter
- * control is enabled but doesn't directly affect the stem which is going to
- * be positioned. Unlike FPGM 12, it doesn't just attempt to position a point
- * closely enough to its original coordinate, but also checks if the previous
- * stem has already been shifted relatively to its "ideal" position FPGM 12 would
- * determine. If so, then the desired point position is corrected relatively to
- * the current placement of the previous stem.
- * Syntax: PUSHX_3 ref_point point 18 CALL
- */
- 0xb0, // PUSHB_1
- 0x12, // 18
- 0x2c, // FDEF
- 0x20, // DUP
- 0x2f, // MDAP[rnd], this is needed for grayscale mode
- 0xb0, // PUSHB_1
- 0x07, // 7
- 0x2b, // CALL
- 0x5c, // NOT
- 0x58, // IF
- 0x20, // DUP
- 0x20, // DUP
- 0x47, // GC[cur]
- 0x23, // SWAP
- 0x46, // GC[orig]
- 0x61, // SUB
- 0x6a, // ROUND[white]
- 0x8a, // ROLL
- 0x20, // DUP
- 0x47, // GC[cur]
- 0x23, // SWAP
- 0x46, // GC[orig]
- 0x23, // SWAP
- 0x61, // SUB
- 0x6a, // ROUND[white]
- 0x60, // ADD
- 0x20, // DUP
- 0x58, // IF
- 0x20, // DUP
- 0x64, // ABS
- 0x62, // DIV
- 0x38, // SHPIX
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 19: used to align a point relatively to a diagonal line,
- * specified by two other points. First we check if the point going
- * to be positioned doesn't deviate too far from the line in the original
- * outline. If the deviation is small enough to neglect it, we use ALIGNRP
- * to position the point, otherwise MDRP is used instead. We can't just
- * always use MDRP, because this command may produce wrong results at
- * small PPEMs, if the original and gridfitted coordinates of the line end
- * points specify slightly different unit vectors.
- * Syntax: point diag_start_point diag_end_point 19 CALL
- */
- 0xb0, // PUSHB_1
- 0x13, // 19
- 0x2c, // FDEF
- 0x20, // DUP
- 0x8a, // ROLL
- 0x20, // DUP
- 0x8a, // ROLL
- 0x87, // SDPVTL[orthogonal]
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x03, // 4
- 0x25, // CINDEX
- 0x4a, // MD[orig]
- 0x64, // ABS
- 0x23, // SWAP
- 0x8a, // ROLL
- 0x07, // SPVTL[orthogonal]
- 0xb0, // PUSHB_1
- 0x20, // 32
- 0x50, // LT
- 0x58, // IF
- 0x3c, // ALIGNRP
- 0x1b, // ELSE
- 0xc0, // MDRP[grey]
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 20: compute adjustments for X and Y components of projection
- * vector, for aspect ratios different than 1:1, and store them
- * in storage[0] and storage[1] respectively.
- * Syntax: 20 CALL (use it only ONCE, from PREP table).
- */
- 0xb0, // PUSHB_1
- 0x14, // 20
- 0x2c, // FDEF
- 0xb3, // PUSHB_4 (we normally need no adjustments)
- 0x00, // 0
- 0x40, // 1.0 (F26Dot6)
- 0x01, // 1
- 0x40, // 1.0 (F26Dot6)
- 0x42, // WS
- 0x42, // WS
- 0x01, // SVTCA[x-axis]
- 0x4b, // MPPEM
- 0xb8, // PUSHW_1
- 0x10, // 4096
- 0x00, // ...still that 4096
- 0x63, // MUL (so we have PPEM along X casted to F26Dot6)
- 0x00, // SVTCA[y-axis]
- 0x4b, // MPPEM
- 0xb8, // PUSHW_1
- 0x10, // 4096
- 0x00, // ...still that 4096
- 0x63, // MUL (so we have PPEM along Y casted to F26Dot6)
- 0x20, // DUP
- 0x8a, // ROLL
- 0x20, // DUP
- 0x8a, // ROLL
- 0x55, // NEQ
- 0x58, // IF (if PPEM along X != PPEM along Y)
- 0x20, // DUP
- 0x8a, // ROLL
- 0x20, // DUP
- 0x8a, // ROLL
- 0x52, // GT
- 0x58, // IF (if PPEM along X < PPEM along Y)
- 0x23, // SWAP
- 0x62, // DIV
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x23, // SWAP
- 0x42, // WS
- 0x1b, // ELSE (if PPEM along X > PPEM along Y)
- 0x62, // DIV
- 0x20, // DUP
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x23, // SWAP
- 0x42, // WS
- 0x59, // EIF
- 0x20, // DUP [A LOOP STARTS HERE]
- 0xb0, // PUSHB_1
- 0x40, // 1.0 (F26Dot6)
- 0x52, // GT
- 0x58, // IF (bigger adjustment is greater than 1.0 => needs fixing)
- 0xb2, // PUSHB_3
- 0x00, // 0
- 0x20, // 0.5 (F26Dot6)
- 0x00, // 0
- 0x43, // RS
- 0x63, // MUL
- 0x42, // WS (we halved adjustment for X)
- 0xb2, // PUSHB_3
- 0x01, // 1
- 0x20, // 0.5 (F26Dot6)
- 0x01, // 1
- 0x43, // RS
- 0x63, // MUL
- 0x42, // WS (we halved adjustment for Y)
- 0xb0, // PUSHB_1
- 0x20, // 0.5 (F26Dot6)
- 0x63, // MUL (we halved the bigger adjustment)
- 0xb0, // PUSHB_1
- 0x19, // 25
- 0x65, // NEG
- 0x1c, // JMPR (go back to the start of the loop)
- 0x21, // POP
- 0x59, // EIF
- 0x1b, // ELSE (if PPEM along X == PPEM along Y)
- 0x21, // POP
- 0x21, // POP
- 0x59, // EIF
- 0x2d, // ENDF
-
- /* Function 21: call it before SFVFS or SPVFS, so that the vector
- * passed is aspect-ratio corrected.
- * Syntax: x y 21 CALL
- */
- 0xb0, // PUSHB_1
- 0x15, // 21
- 0x2c, // FDEF
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x43, // RS
- 0x63, // MUL
- 0x23, // SWAP
- 0xb0, // PUSHB_1
- 0x00, // 0
- 0x43, // RS
- 0x63, // MUL
- 0x23, // SWAP
- 0x2d // ENDF
- };
-
- struct ttf_table *tab = SFFindTable(gic->sf, CHR('f','p','g','m'));
-
- if ( tab==NULL ) {
- /* We have to create such table. */
- tab = chunkalloc(sizeof(struct ttf_table));
- tab->next = gic->sf->ttf_tables;
- gic->sf->ttf_tables = tab;
- tab->tag = CHR('f','p','g','m');
- tab->len = 0;
- }
-
- if (tab->len==0 ||
- (tab->len < (int)sizeof(new_fpgm) &&
- !memcmp(tab->data, new_fpgm, tab->len)))
- {
- /* We can safely update font program. */
- tab->len = tab->maxlen = sizeof(new_fpgm);
- tab->data = realloc(tab->data, sizeof(new_fpgm));
- memmove(tab->data, new_fpgm, sizeof(new_fpgm));
- gic->fpgm_done = 1;
- }
- else {
- /* there already is a font program. */
- gic->fpgm_done = 0;
- if (tab->len >= (int)sizeof(new_fpgm))
- if (!memcmp(tab->data, new_fpgm, sizeof(new_fpgm)))
- gic->fpgm_done = 1; /* it's ours. */
-
- /* Log warning message. */
- if (!gic->fpgm_done)
- ff_post_error(_("Can't insert 'fpgm'"),
- _("There exists a 'fpgm' code that seems incompatible with "
- "FontForge's. Instructions generated will be of lower "
- "quality. If legacy hinting is to be scrapped, it is "
- "suggested to clear the `fpgm` and repeat autoinstructing. "
- "It will be then possible to append user's code to "
- "FontForge's 'fpgm', but due to possible future updates, "
- "it is extremely advised to use high numbers for user's "
- "functions."
- ));
- }
-}
-
-/* When initializing global instructing context, we want to set up the 'prep'
- * table in order to apply family blues and normalize stem widths for monochrome
- * display.
- *
- * The stem normalizer is heavily based on simple concept from FreeType2.
- *
- * First round the StdW. Then for each StemSnap (going outwards from StdW) check
- * if it's within 1px from its already rounded neighbor, and if so, snap it
- * before rounding. From all vertical stems (but not StdHW itself), 0.25px is
- * subtracted before rounding. Similar method is used for non-cvt stems, they're
- * snapped to the closest standard width if possible.
- *
- * NOTE: because of tiny scaling issues, we have to compute ppem at which each
- * stem stops being snapped to its already-rounded neighbor here instead of
- * relegating this to the truetype bytecide interpreter. We can't simply rely
- * on cvt cut-in.
- */
-
-static int compute_blue_height(real val, int EM, int bluescale, int ppem) {
- int scaled_val = rint((rint(fabs(val)) * ppem * 64)/EM);
- if (ppem < bluescale) scaled_val += 16;
-return (scaled_val + 32) / 64 * (val / fabs(val));
-}
-
-static uint8 *use_family_blues(uint8 *prep_head, GlobalInstrCt *gic) {
- int i, h1, h2, stopat;
- int bs = GetBlueScale(gic->sf);
- int EM = gic->sf->ascent + gic->sf->descent;
- int callargs[3];
-
- for (i=0; i<gic->bluecnt; i++) {
- if (isfinite(gic->blues[i].family_base))
- {
- for (stopat=0; stopat<32768; stopat++) {
- h1 = compute_blue_height(gic->blues[i].base, EM, bs, stopat);
- h2 = compute_blue_height(gic->blues[i].family_base, EM, bs, stopat);
- if (abs(h1 - h2) > 1) break;
- }
-
- callargs[0] = gic->blues[i].family_cvtindex;
- callargs[1] = stopat;
- callargs[2] = 2;
-
- prep_head = pushnum(prep_head, gic->blues[i].cvtindex);
- *prep_head++ = DUP;
- *prep_head++ = 0x45; //RCVT
- prep_head = pushnums(prep_head, 3, callargs);
- *prep_head++ = CALL;
- *prep_head++ = 0x44; //WCVTP
- }
- }
-
- return prep_head;
-}
-
-/* Return width (in pixels) of given stem, taking snaps into account.
- */
-#define SNAP_THRESHOLD (64)
-static int compute_stem_width(int xdir, StdStem *stem, int EM, int ppem) {
- int scaled_width; /* in 1/64th pixels */
- int snapto_width; /* in 1/64th pixels */
-
- scaled_width = (int)rint((rint(fabs(stem->width)) * ppem * 64.0)/EM);
- if (scaled_width < 64) scaled_width = 64;
-
- if (stem->snapto != NULL)
- {
- if (stem->stopat > ppem) {
- snapto_width = 64*compute_stem_width(xdir, stem->snapto, EM, ppem);
-
- if (abs(snapto_width - scaled_width) < SNAP_THRESHOLD)
- scaled_width = snapto_width;
- }
-
- if (xdir) scaled_width -= 16;
- }
-
-return (scaled_width + 32) / 64;
-}
-
-/* Normalize a single stem. The code generated assumes there is a scaled stem
- * width on bytecode interpreter's stack, and leaves normalized width there.
- */
-static uint8 *normalize_stem(uint8 *prep_head, int xdir, StdStem *stem, GlobalInstrCt *gic) {
- int callargs[3];
- int i;
-
- stem->stopat = 32767;
-
- if (stem->snapto != NULL)
- {
- /* compute ppem at which to stop snapping stem to stem->snapto */
- int EM = gic->sf->ascent + gic->sf->descent;
-
- for (i=7; i<32768; i++) {
- int width_parent = compute_stem_width(xdir, stem->snapto, EM, i);
- int width_me = compute_stem_width(xdir, stem, EM, i);
-
- if (width_parent != width_me) {
- stem->stopat = i;
- break;
- }
- }
-
- /* snap if below given ppem */
- callargs[0] = stem->snapto->cvtindex;
- callargs[1] = stem->stopat;
- callargs[2] = 2;
- prep_head = pushnums(prep_head, 3, callargs);
- *prep_head++ = CALL;
-
- /* Round[black], respecting minimum distance of 1 px */
- /* Vertical stems (but not StdVW) use special rounding threshold. */
- /* The rounding function restores default round state at the end. */
- if (xdir) {
- prep_head = push2nums(prep_head, 3, 70);
- *prep_head++ = SROUND;
- }
- else prep_head = pushnum(prep_head, 3);
-
- *prep_head++ = CALL;
- }
- else {
- /* simply round[black] respecting minimum distance of 1 px */
- prep_head = pushnum(prep_head, 3);
- *prep_head++ = CALL;
- }
-
-return prep_head;
-}
-
-/* Append the code for normalizing standard stems' widths to 'prep'.
- */
-static uint8 *normalize_stems(uint8 *prep_head, int xdir, GlobalInstrCt *gic) {
- int i, t;
- StdStem *mainstem = xdir?&(gic->stdvw):&(gic->stdhw);
- StdStem *otherstems = xdir?gic->stemsnapv:gic->stemsnaph;
- int otherstemcnt = xdir?gic->stemsnapvcnt:gic->stemsnaphcnt;
-
- if (mainstem->width == -1)
-return prep_head;
-
- /* set up the standard width */
- mainstem->snapto = NULL;
- *prep_head++ = xdir?SVTCA_x:SVTCA_y;
- prep_head = pushnum(prep_head, mainstem->cvtindex);
- *prep_head++ = DUP;
- *prep_head++ = 0x45; //RCVT
- prep_head = normalize_stem(prep_head, xdir, mainstem, gic);
- *prep_head++ = 0x44; //WCVTP
-
- /* set up other standard widths */
- for (i=0; i<otherstemcnt && otherstems[i].width < mainstem->width; i++);
- t = i-1;
-
- for (i=t; i>=0; i--) {
- otherstems[i].snapto = i==t?mainstem:otherstems+i+1;
- prep_head = pushnum(prep_head, otherstems[i].cvtindex);
- *prep_head++ = DUP;
- *prep_head++ = 0x45; //RCVT
- prep_head = normalize_stem(prep_head, xdir, otherstems+i, gic);
- *prep_head++ = 0x44; //WCVTP
- }
-
- for (i=t+1; i<otherstemcnt; i++) {
- otherstems[i].snapto = i==t+1?mainstem:otherstems+i-1;
- prep_head = pushnum(prep_head, otherstems[i].cvtindex);
- *prep_head++ = DUP;
- *prep_head++ = 0x45; //RCVT
- prep_head = normalize_stem(prep_head, xdir, otherstems+i, gic);
- *prep_head++ = 0x44; //WCVTP
- }
-
-return prep_head;
-}
-
-/* Turning dropout control on will dramatically improve mono rendering, even
- * without further hinting, especcialy for light typefaces. And turning hinting
- * off at veeery small pixel sizes is required, because hints tend to visually
- * tear outlines apart when not having enough workspace.
- *
- * We also normalize stem widths here, this usually massively improves overall
- * consistency. We currently do this only for monochrome rendering (this
- * includes WinXP's cleartype).
- *
- * TODO! We should take 'gasp' table into account and set up blues here.
- */
-static void init_prep(GlobalInstrCt *gic) {
- uint8 new_prep_preamble[] =
- {
- /* Enable dropout control. FreeType 2.3.7 need explicit SCANTYPE. */
- 0xb8, // PUSHW_1
- 0x01, // 511
- 0xff, // ...still that 511
- 0x85, // SCANCTRL
- 0xb0, // PUSHB_1
- 0x01, // 1
- 0x8d, // SCANTYPE
-
- /* Measurements are taken along Y axis */
- 0x00, // SVTCA[y-axis]
-
- /* Turn hinting off at very small pixel sizes */
- 0x4b, // MPPEM
- 0xb0, // PUSHB_1
- 0x08, // 8 - hinting threshold - should be configurable
- 0x50, // LT
- 0x58, // IF
- 0xb1, // PUSHB_2
- 0x01, // 1
- 0x01, // 1
- 0x8e, // INSTCTRL
- 0x59, // EIF
-
- /* Determine the cvt cut-in used */
- 0xb1, // PUSHB_2
- 0x46, // 70/64 = about 1.094 pixel (that's our default setting)
- 0x06, // 6
- 0x2b, // CALL
- 0x58, // IF
- 0x21, // POP
- 0xb0, // PUSHB_1
- 0x10, // 16/64 = 0.25 pixel (very low cut-in for grayscale mode)
- 0x59, // EIF
- 0x4b, // MPPEM
- 0xb0, // PUSHB_1
- 0x14, // 20 PPEM - a threshold below which we'll use larger CVT cut-in
- 0x52, // GT
- 0x58, // IF
- 0x21, // POP
- 0xb0, // PUSHB_1
- 0x80, // 128/64 = 2 pixels (extreme regularization for small ppems)
- 0x59, // EIF
- 0x1d // SCVTCI
- };
-
- int preplen = sizeof(new_prep_preamble);
- int prepmaxlen = preplen;
- uint8 *new_prep, *prep_head;
- struct ttf_table *tab;
-
- if (gic->cvt_done) {
- prepmaxlen += 48 + 38*(gic->stemsnaphcnt + gic->stemsnapvcnt);
- prepmaxlen += 14*(gic->bluecnt);
- }
-
- if (gic->fpgm_done)
- prepmaxlen += 3;
-
- new_prep = calloc(prepmaxlen, sizeof(uint8));
- memmove(new_prep, new_prep_preamble, preplen*sizeof(uint8));
- prep_head = new_prep + preplen;
-
- if (gic->cvt_done && gic->fpgm_done) {
- /* Apply family blues. */
- prep_head = use_family_blues(prep_head, gic);
-
- /* Normalize stems (only in monochrome mode) */
- prep_head = pushnum(prep_head, 6);
- *prep_head++ = CALL;
- *prep_head++ = 0x5c; // NOT
- *prep_head++ = 0x58; // IF
- prep_head = normalize_stems(prep_head, 0, gic);
- prep_head = normalize_stems(prep_head, 1, gic);
- *prep_head++ = 0x59; // EIF
- }
-
- /* compute adjustments for projection vector */
- if (gic->fpgm_done) {
- prep_head = pushnum(prep_head, 20);
- *prep_head++ = CALL;
- }
-
- preplen = prep_head - new_prep;
-
- tab = SFFindTable(gic->sf, CHR('p','r','e','p'));
-
- if ( tab==NULL ) {
- /* We have to create such table. */
- tab = chunkalloc(sizeof(struct ttf_table));
- tab->next = gic->sf->ttf_tables;
- gic->sf->ttf_tables = tab;
- tab->tag = CHR('p','r','e','p');
- tab->len = 0;
- }
-
- if (tab->len==0 ||
- (tab->len < preplen && !memcmp(tab->data, new_prep, tab->len)))
- {
- /* We can safely update cvt program. */
- tab->len = tab->maxlen = preplen;
- tab->data = realloc(tab->data, preplen);
- memmove(tab->data, new_prep, preplen);
- gic->prep_done = 1;
- }
- else {
- /* there already is a font program. */
- gic->prep_done = 0;
- if (tab->len >= preplen)
- if (!memcmp(tab->data, new_prep, preplen))
- gic->prep_done = 1; /* it's ours */
-
- /* Log warning message. */
- if (!gic->prep_done)
- ff_post_error(_("Can't insert 'prep'"),
- _("There exists a 'prep' code incompatible with FontForge's. "
- "It can't be guaranteed it will work well. It is suggested "
- "to allow FontForge to insert its code and then append user"
- "'s own."
- ));
- }
-
- free(new_prep);
-}
-
-/*
- * Initialize Global Instructing Context
- */
-#define EDGE_FUZZ (500.0)
-void InitGlobalInstrCt(GlobalInstrCt *gic, SplineFont *sf, int layer,
- BlueData *bd) {
- BlueData _bd;
-
- if (bd == NULL) {
- QuickBlues(sf,layer,&_bd);
- bd = &_bd;
- }
-
- gic->sf = sf;
- gic->bd = bd;
- gic->layer = layer;
- gic->fudge = (sf->ascent+sf->descent)/EDGE_FUZZ;
-
- gic->cvt_done = false;
- gic->fpgm_done = false;
- gic->prep_done = false;
-
- gic->bluecnt = 0;
- gic->stdhw.width = -1;
- gic->stemsnaph = NULL;
- gic->stemsnaphcnt = 0;
- gic->stdvw.width = -1;
- gic->stemsnapv = NULL;
- gic->stemsnapvcnt = 0;
-
- GICImportBlues(gic);
- GICImportStems(0, gic); /* horizontal stems */
- GICImportStems(1, gic); /* vertical stems */
-
- init_cvt(gic);
- init_fpgm(gic);
- init_prep(gic);
- init_maxp(gic);
-}
-
-/*
- * Finalize Global Instructing Context
- */
-void FreeGlobalInstrCt(GlobalInstrCt *gic) {
- gic->sf = NULL;
- gic->bd = NULL;
- gic->fudge = 0;
-
- gic->cvt_done = false;
- gic->fpgm_done = false;
- gic->prep_done = false;
-
- gic->bluecnt = 0;
- gic->stdhw.width = -1;
- if (gic->stemsnaphcnt != 0) free(gic->stemsnaph);
- gic->stemsnaphcnt = 0;
- gic->stemsnaph = NULL;
- gic->stdvw.width = -1;
- if (gic->stemsnapvcnt != 0) free(gic->stemsnapv);
- gic->stemsnapvcnt = 0;
- gic->stemsnapv = NULL;
-}
-
-/******************************************************************************
- ******************************************************************************
- **
- ** Stuff for managing global instructing context ends here. Now we'll deal
- ** with single glyphs.
- **
- ** Many functions here need large or similar sets of arguments. I decided to
- ** define an '(local) instructing context' to have them in one place and keep
- ** functions' argument lists reasonably short. I first need to define some
- ** internal sub-structures for instructing diagonal stems. Similar structures
- ** for CVT management (based on PS Private) are defined in splinefont.h, and
- ** were initialized handled above.
- **
- ******************************************************************************
- ******************************************************************************/
-
-/* A line, described by two points */
-typedef struct pointvector {
- PointData *pd1, *pd2;
- int done;
-} PointVector;
-
-/* In this structure we store information about diagonales,
- relatively to which the given point should be positioned */
-typedef struct diagpointinfo {
- struct pointvector line[2];
- int count;
-} DiagPointInfo;
-
-typedef struct instrct {
- /* Things that are global for font and should be
- initialized before instructing particular glyph. */
- GlobalInstrCt *gic;
-
- /* Here things for this particular glyph start. */
- SplineChar *sc;
- SplineSet *ss;
-
- /* instructions */
- uint8 *instrs; /* the beginning of the instructions */
- uint8 *pt; /* the current position in the instructions */
-
- /* properties indexed by contour number */
- int *contourends; /* points ending their contours. Null-terminated. */
- uint8 *clockwise; /* is given contour clockwise? */
-
- /* properties, indexed by ttf point index. Some could be compressed. */
- int ptcnt; /* number of points in this glyph */
- BasePoint *bp; /* point coordinates */
- uint8 *touched; /* touchflags; points explicitly instructed */
- uint8 *affected; /* touchflags; almost touched, but optimized out */
-
- /* data from stem detector */
- GlyphData *gd;
-
- /* stuff for hinting diagonals */
- int diagcnt;
- StemData **diagstems;
- DiagPointInfo *diagpts; /* indexed by ttf point index */
-
- /* stuff for hinting edges (stems, blues, strong point interpolation). */
- int xdir; /* direction flag: x=true, y=false */
- int cdir; /* is current contour outer? - blues need this */
- struct __edge {
- real base; /* where the edge is */
- int refpt; /* best ref. point for an edge, ttf index, -1 if none */
- int refscore; /* its quality, for searching better one; 0 if none */
- int othercnt; /* count of other points to instruct for this edge */
- int *others; /* their ttf indices, optimize_edge() is advised */
- } edge;
-
- /* Some variables for tracking graphics state */
- int rp0;
-} InstrCt;
-
-/******************************************************************************
- *
- * Low-level routines for manipulting and classifying splinepoints
- *
- ******************************************************************************/
-
-/* Find previous point index on the contour. */
-static int PrevOnContour(int *contourends, int p) {
- int i;
-
- if (p == 0) return contourends[0];
- else {
- for (i=0; contourends[i+1]; i++)
- if (contourends[i]+1 == p)
- return contourends[i+1];
-
- return p-1;
- }
-}
-
-/* Find next point index on the contour. */
-static int NextOnContour(int *contourends, int p) {
- int i;
-
- if (p == 0) return 1;
- else {
- for (i=0; contourends[i]; i++) {
- if (contourends[i] == p) {
- if (i==0) return 0;
- else return contourends[i-1]+1;
- }
- }
- return p+1;
- }
-}
-
-/* For hinting stems, I found it needed to check if candidate point for
- * instructing is pararell to hint's direction to avoid snapping wrong points.
- * I splitted the routine into two, as sometimes it may be needed to check
- * the angle to be strictly almost the same, not just pararell.
- */
-static int __same_angle(int *contourends, BasePoint *bp, int p, double angle) {
- int PrevPoint, NextPoint;
- double PrevTangent, NextTangent;
-
- PrevPoint = PrevOnContour(contourends, p);
- NextPoint = NextOnContour(contourends, p);
- PrevTangent = atan2(bp[p].y - bp[PrevPoint].y, bp[p].x - bp[PrevPoint].x);
- NextTangent = atan2(bp[NextPoint].y - bp[p].y, bp[NextPoint].x - bp[p].x);
-
- /* If at least one of the tangents is close to the given angle, return */
- /* true. 'Close' means about 5 deg, i.e. about 0.087 rad. */
- PrevTangent = fabs(PrevTangent-angle);
- NextTangent = fabs(NextTangent-angle);
- while (PrevTangent > M_PI) PrevTangent -= 2*M_PI;
- while (NextTangent > M_PI) NextTangent -= 2*M_PI;
-return (fabs(PrevTangent) <= 0.087) || (fabs(NextTangent) <= 0.087);
-}
-
-static int same_angle(int *contourends, BasePoint *bp, int p, double angle) {
-return __same_angle(contourends, bp, p, angle) || __same_angle(contourends, bp, p, angle+M_PI);
-}
-
-/* I found it needed to write some simple functions to classify points snapped
- * to hint's edges. Classification helps to establish the most accurate leading
- * point for an edge.
- */
-static int _IsExtremum(int xdir, SplinePoint *sp) {
-return xdir?
- (!sp->nonextcp && !sp->noprevcp && sp->nextcp.x==sp->me.x && sp->prevcp.x==sp->me.x):
- (!sp->nonextcp && !sp->noprevcp && sp->nextcp.y==sp->me.y && sp->prevcp.y==sp->me.y);
-}
-
-static int IsExtremum(int xdir, int p, SplinePoint *sp) {
- int ret = _IsExtremum(xdir, sp);
-
- if ((sp->nextcpindex == p) && (sp->next != NULL) && (sp->next->to != NULL))
- ret = ret || _IsExtremum(xdir, sp->next->to);
- else if ((sp->ttfindex != p) && (sp->prev != NULL) && (sp->prev->from != NULL))
- ret = ret || _IsExtremum(xdir, sp->prev->from);
-
-return ret;
-}
-
-static int IsCornerExtremum(int xdir, int *contourends, BasePoint *bp, int p) {
- int PrevPoint = PrevOnContour(contourends, p);
- int NextPoint = NextOnContour(contourends, p);
-
-return xdir?
- ((bp[PrevPoint].x > bp[p].x && bp[NextPoint].x > bp[p].x) ||
- (bp[PrevPoint].x < bp[p].x && bp[NextPoint].x < bp[p].x)):
- ((bp[PrevPoint].y > bp[p].y && bp[NextPoint].y > bp[p].y) ||
- (bp[PrevPoint].y < bp[p].y && bp[NextPoint].y < bp[p].y));
-}
-
-static int IsAnglePoint(int *contourends, BasePoint *bp, SplinePoint *sp) {
- int PrevPoint, NextPoint, p=sp->ttfindex;
- double PrevTangent, NextTangent;
-
- if ((sp->pointtype != pt_corner) || (p == 0xffff))
-return 0;
-
- PrevPoint = PrevOnContour(contourends, p);
- NextPoint = NextOnContour(contourends, p);
- PrevTangent = atan2(bp[p].y - bp[PrevPoint].y, bp[p].x - bp[PrevPoint].x);
- NextTangent = atan2(bp[NextPoint].y - bp[p].y, bp[NextPoint].x - bp[p].x);
-
-return fabs(PrevTangent - NextTangent) > 0.261;
-}
-
-static int IsInflectionPoint(int *contourends, BasePoint *bp, SplinePoint *sp) {
- double CURVATURE_THRESHOLD = 1e-9;
- struct spline *prev, *next;
- double in, out;
-
- if (IsAnglePoint(contourends, bp, sp))
-return 0;
-
- /* point of a single-point contour can't be an inflection point. */
- if (sp->prev != NULL && sp->prev->from != NULL && sp->prev->from == sp)
-return 0;
-
- prev = sp->prev;
- in = 0;
- while (prev != NULL && fabs(in) < CURVATURE_THRESHOLD) {
- in = SplineCurvature(prev, 1);
- if (fabs(in) < CURVATURE_THRESHOLD) in = SplineCurvature(prev, 0);
- if (fabs(in) < CURVATURE_THRESHOLD) prev = prev->from->prev;
- if ((prev != NULL && IsAnglePoint(contourends, bp, prev->to)) || (prev == sp->prev))
- break;
- }
-
- next = sp->next;
- out = 0;
- while (next != NULL && fabs(out) < CURVATURE_THRESHOLD) {
- out = SplineCurvature(next, 0);
- if (fabs(out) < CURVATURE_THRESHOLD) out = SplineCurvature(next, 1);
- if (fabs(out) < CURVATURE_THRESHOLD) next = next->to->next;
- if ((next != NULL && IsAnglePoint(contourends, bp, next->from)) || (next == sp->next))
- break;
- }
-
- if (in==0 || out==0 || (prev != sp->prev && next != sp->next))
-return 0;
-
- in/=fabs(in);
- out/=fabs(out);
-
-return (in*out < 0);
-}
-
-/******************************************************************************
- *
- * I found it easier to write an iterator that calls given function for each
- * point worth instructing than repeating the same loops all the time.
- *
- * The control points are not skipped, but runmes often eliminate them as
- * instructing them seems to cause more damages than profits. They are included
- * here because edge optimizer cam be simpler and work more reliably then.
- *
- * The contour_direction option is for blues - snapping internal contour to a
- * blue zone is plain wrong, unless there is a stem hint tat don't fit to any
- * other blue zone.
- *
- ******************************************************************************/
-#define EXTERNAL_CONTOURS 0
-#define ALL_CONTOURS 1
-#define INTERNAL_CONTOURS 2
-static void RunOnPoints(InstrCt *ct, int contour_direction,
- void (*runme)(int p, SplinePoint *sp, InstrCt *ct))
-{
- SplineSet *ss = ct->ss;
- SplinePoint *sp;
- uint8 *done;
- int c, p;
-
- done = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
-
- for ( c=0; ss!=NULL; ss=ss->next, ++c ) {
- ct->cdir = ct->clockwise[c];
-
- if (((contour_direction == EXTERNAL_CONTOURS) && !ct->cdir) ||
- ((contour_direction == INTERNAL_CONTOURS) && ct->cdir)) continue;
-
- for ( sp=ss->first; ; ) {
- if (sp->ttfindex != 0xffff) {
- if (!sp->noprevcp &&
- !done[p = PrevOnContour(ct->contourends, sp->ttfindex)])
- {
- runme(p, sp, ct);
- done[p] = true;
- }
-
- if (!done[p = sp->ttfindex]) {
- runme(p, sp, ct);
- done[p] = true;
- }
-
- if (!sp->nonextcp && !done[p = sp->nextcpindex])
- {
- runme(p, sp, ct);
- done[p] = true;
- }
- }
- else if (!sp->nonextcp) {
- if (!done[p = PrevOnContour(ct->contourends, sp->nextcpindex)]) {
- runme(p, sp, ct);
- done[p] = true;
- }
-
- if (!done[p = sp->nextcpindex]) {
- runme(p, sp, ct);
- done[p] = true;
- }
- }
-
- if ( sp->next==NULL ) break;
- sp = sp->next->to;
- if ( sp==ss->first ) break;
- }
- }
-
- free(done);
-}
-
-/******************************************************************************
- *
- * Hinting is mostly aligning 'edges' (in FreeType's sense). Each stem hint
- * consists of two edges (or one, for ghost hints). And each blue zone can be
- * represented as an edge with extended fudge (overshoot).
- *
- * Hinting a stem edge is broken in two steps. First: init_stem_edge() seeks for
- * points to snap and chooses one that will be used as a reference point - it
- * should be then instructed elsewhere (a general method of edge positioning).
- * Old init_edge() is still used instead for blue zones and strong points.
- * Finally, finish_edge() instructs the rest of points found with given command,
- * using instructpoints(). It normally optimizes an edge before instructing,
- * but not in presence of diagonal hints.
- *
- * The contour_direction option of init_edge() is for hinting blues - snapping
- * internal contour to a bluezone seems just plainly wrong.
- *
- ******************************************************************************/
-
-/* The following operations have been separated from search_edge(), */
-/* because sometimes it is important to be able to determine, if the */
-/* given point is about to be gridfitted or interpolated */
-static int value_point(InstrCt *ct, int p, SplinePoint *sp, real fudge) {
- int score = 0;
- int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
- uint8 touchflag = ct->xdir?tf_x:tf_y;
-
- if (IsCornerExtremum(ct->xdir, ct->contourends, ct->bp, p) ||
- IsExtremum(ct->xdir, p, sp))
- score+=4;
-
- if (same_angle(ct->contourends, ct->bp, p, ct->xdir?0.5*M_PI:0.0))
- score++;
-
- if (p == sp->ttfindex && IsAnglePoint(ct->contourends, ct->bp, sp))
- score++;
-
- if (interpolate_more_strong && (fudge > (EM/EDGE_FUZZ+0.0001)))
- if (IsExtremum(!ct->xdir, p, sp))
- score++;
-
- if (IsInflectionPoint(ct->contourends, ct->bp, sp))
- score++;
-
- if (score && ct->gd->points[p].sp != NULL) /* oncurve */
- score+=2;
-
- if (!score)
-return( 0 );
-
- if (ct->diagstems != NULL && ct->diagpts[p].count) score+=9;
- if (ct->touched[p] & touchflag) score+=26;
-return( score );
-}
-
-/* search for points to be snapped to an edge - to be used in RunOnPoints() */
-static void search_edge(int p, SplinePoint *sp, InstrCt *ct) {
- int tmp, score;
- real fudge = ct->gic->fudge;
- uint8 touchflag = ct->xdir?tf_x:tf_y;
- real refcoord, coord = ct->xdir?ct->bp[p].x:ct->bp[p].y;
-
- if (fabs(coord - ct->edge.base) <= fudge)
- {
- score = value_point(ct, p, sp, ct->gic->fudge);
- if (!score)
- return;
- else if (ct->edge.refpt == -1) {
- ct->edge.refpt = p;
- ct->edge.refscore = score;
- return;
- }
-
- refcoord = ct->xdir?ct->bp[ct->edge.refpt].x:ct->bp[ct->edge.refpt].y;
-
- if ((score > ct->edge.refscore) ||
- (score == ct->edge.refscore &&
- fabs(coord - ct->edge.base) < fabs(refcoord - ct->edge.base)))
- {
- tmp = ct->edge.refpt;
- ct->edge.refpt = p;
- ct->edge.refscore = score;
- p = tmp;
- }
-
- if ((p!=-1) && !((ct->touched[p] | ct->affected[p]) & touchflag)) {
- ct->edge.othercnt++;
-
- if (ct->edge.othercnt==1) ct->edge.others=(int *)calloc(1, sizeof(int));
- else ct->edge.others=(int *)realloc(ct->edge.others, ct->edge.othercnt*sizeof(int));
-
- ct->edge.others[ct->edge.othercnt-1] = p;
- }
- }
-}
-
-static int StemPreferredForPoint(PointData *pd, StemData *stem,int is_next ) {
- StemData **stems;
- BasePoint bp;
- real off, bestoff;
- int i, is_l, best=0, *stemcnt;
-
- stems = ( is_next ) ? pd->nextstems : pd->prevstems;
- stemcnt = ( is_next) ? &pd->nextcnt : &pd->prevcnt;
-
- bestoff = 1e4;
- for ( i=0; i<*stemcnt; i++ ) {
- /* Ghost hints are always assigned to both sides of a point, no matter
- * what the next/previous spline direction is. So we need an additional
- * check for stem unit parallelity */
- if (stems[i]->toobig > stem->toobig ||
- stems[i]->unit.x != stem->unit.x || stems[i]->unit.y != stem->unit.y)
- continue;
- is_l = is_next ? pd->next_is_l[i] : pd->prev_is_l[i];
- bp = is_l ? stems[i]->left : stems[i]->right;
- off = fabs(( pd->base.x - bp.x )*stem->l_to_r.x +
- ( pd->base.y - bp.y )*stem->l_to_r.y );
- if (off < bestoff || (RealNear(off, bestoff) && stems[i] == stem)) {
- best = i;
- bestoff = off;
- }
- }
- if (best < *stemcnt && stem == stems[best])
- return( best );
-
- return( -1 );
-}
-
-static int has_valid_dstem( PointData *pd,int next ) {
- int i, cnt;
- StemData *test;
-
- cnt = next ? pd->nextcnt : pd->prevcnt;
- for ( i=0; i<cnt; i++ ) {
- test = next ? pd->nextstems[i] : pd->prevstems[i];
- if ( !test->toobig && test->lpcnt > 1 && test->rpcnt > 1 &&
- fabs( test->unit.x ) > .05 && fabs( test->unit.y ) > .05 )
- return( i );
- }
- return( -1 );
-}
-
-/* init_stem_edge(): Initialize the InstrCt for instructing given edge.
- *
- * Finds points that should be snapped to this hint's given edge.
- * It will return two types of points: a 'chosen one' ct->edge.refpt, that
- * should be used as a reference for this hint, and ct->edge.others that should
- * be positioned after ct.refpt with, for example, SHP.
- *
- * assign_points_to_edge() is a helper function, only to use from init_stem_edge().
- */
-static void assign_points_to_edge(InstrCt *ct, StemData *stem, int is_l, int *refidx) {
- int i, previdx, nextidx, test_l, dint_inner = false, flag;
- PointData *pd;
-
- flag = RealNear( stem->unit.y,1 ) ? tf_x : tf_y;
-
- for ( i=0; i<ct->gd->realcnt; i++ ) {
- pd = &ct->gd->points[i];
- previdx = StemPreferredForPoint( pd,stem,false );
- nextidx = StemPreferredForPoint( pd,stem,true );
- if (!pd->ticked && (previdx != -1 || nextidx != -1)) {
- pd->ticked = true;
- /* Don't attempt to position inner points at diagonal intersections:
- * our diagonal stem hinter will handle them better */
- if ( ct->diagcnt > 0 && (
- ( stem->unit.y == 1 && pd->x_corner == 2 ) ||
- ( stem->unit.x == 1 && pd->y_corner == 2 ))) {
-
- dint_inner= has_valid_dstem( pd,true ) != -1 &&
- has_valid_dstem( pd,false ) != -1;
- }
- test_l = (nextidx != -1) ?
- pd->next_is_l[nextidx] : pd->prev_is_l[previdx];
- if (test_l == is_l && !dint_inner &&
- !(ct->touched[pd->ttfindex] & flag) && !(ct->affected[pd->ttfindex] & flag)) {
- ct->edge.others = (int *)realloc(
- ct->edge.others, (ct->edge.othercnt+1)*sizeof(int));
- ct->edge.others[ct->edge.othercnt++] = pd->ttfindex;
- if ( *refidx == -1 ) *refidx = pd->ttfindex;
- }
- }
- }
-}
-
-static void init_stem_edge(InstrCt *ct, StemData *stem, int is_l) {
- real left, right, base;
- struct dependent_stem *slave;
- PointData *rpd = NULL;
- int i, *refidx = NULL;
-
- left = ( stem->unit.x == 0 ) ? stem->left.x : stem->left.y;
- right = ( stem->unit.x == 0 ) ? stem->right.x : stem->right.y;
- base = ( is_l ) ? left : right;
-
- ct->edge.base = base;
- ct->edge.refpt = -1;
- ct->edge.refscore = 0;
- ct->edge.othercnt = 0;
- ct->edge.others = NULL;
-
- refidx = ( is_l ) ? &stem->leftidx : &stem->rightidx;
- if ( *refidx != -1 )
- rpd = &ct->gd->points[*refidx];
-
- /* Don't attempt to position inner points at diagonal intersections:
- * our diagonal stem hinter will handle them better */
- if ( rpd != NULL && ct->diagcnt > 0 && (
- ( stem->unit.y == 1 && rpd->x_corner == 2 ) ||
- ( stem->unit.x == 1 && rpd->y_corner == 2 )) &&
- has_valid_dstem( rpd,true ) != -1 && has_valid_dstem( rpd,false ) != -1 )
- *refidx = -1;
-
- for ( i=0; i<ct->gd->realcnt; i++ )
- ct->gd->points[i].ticked = false;
- assign_points_to_edge(ct, stem, is_l, refidx);
-
- for ( i=0; i<stem->dep_cnt; i++ ) {
- slave = &stem->dependent[i];
- if (slave->dep_type == 'a' &&
- ((is_l && slave->lbase) || (!is_l && !slave->lbase))) {
-
- if ( is_l ) slave->stem->leftidx = *refidx;
- else slave->stem->rightidx = *refidx;
- assign_points_to_edge(ct, slave->stem, is_l, refidx);
- }
- }
- ct->edge.refpt = *refidx;
-}
-
-/* Initialize the InstrCt for instructing given edge. */
-static void init_edge(InstrCt *ct, real base, int contour_direction) {
- ct->edge.base = base;
- ct->edge.refpt = -1;
- ct->edge.refscore = 0;
- ct->edge.othercnt = 0;
- ct->edge.others = NULL;
-
- RunOnPoints(ct, contour_direction, &search_edge);
-}
-
-/* Apparatus for edge hinting optimization. For given 'others' in ct,
- * it detects 'segments' (in FreeType's sense) and leaves only one point per
- * segment. A segment to which refpt belong is completely removed (refpt is
- * enough).
- *
- * optimize_edge() is the right high-level function to call with instructing
- * context (an edge must be previously initialized with init_edge). It calls
- * optimize_segment() internally - a function that is otherwise unsafe.
- *
- * optimize_blue() is even higher-level function to call before optimize_edge
- * if init_edge() was used to collect points in a blue zone (or other narrow
- * zone).
- *
- * Optimizers keep points used by diagonal hinter.
- *
- * optimize_strongpts() is used instead of two routines above when hinting
- * inter-stem zones (see interpolate_strong option). It's invoked after
- * instructing diagonal stems.
- */
-
-/* To be used with qsort() - sorts integer array in ascending order. */
-static int sortbynum(const void *a, const void *b) {
- return *(int *)a > *(int *)b;
-}
-
-/* Find element's index within an array - return -1 if element not found. */
-static int findoffs(const int *elems, int elemcnt, int val) {
- int i;
- for (i=0; i<elemcnt; i++) if (elems[i]==val) return i;
- return -1;
-}
-
-/* In given ct, others[segstart...segend] form a continuous segment on an edge
- * parallel to one of coordinate axes. If there are no diagonal hints, we can
- * instruct just one point of a segment, preferring refpt if included, and
- * preferring on-curve points ovef off-curve. Otherwise we must instruct all
- * points used by diagonal hinter along with refpt if included. We mark points
- * that are not to be instructed as 'affected'.
- */
-static void optimize_segment(int segstart, int segend, InstrCt *ct) {
- int i, local_refpt=-1;
- int *others = ct->edge.others;
- int touchflag = (ct->xdir)?tf_x:tf_y;
- int ondiags = 0;
-
- if (segstart==segend)
-return;
-
- /* purely for aesthetic reasons - can be safely removed. */
- qsort(others+segstart, segend+1-segstart, sizeof(int), sortbynum);
-
- /* are there any to be used with dstems? */
- if (ct->diagstems)
- for (i=segstart; !ondiags && i<=segend; i++)
- ondiags = ct->diagpts[others[i]].count;
-
- if (ondiags) {
- for (i=segstart; i<=segend; i++)
- ct->affected[others[i]] |= ct->diagpts[others[i]].count?0:touchflag;
- }
- else {
- for (i=segstart; i<=segend && ct->gd->points[others[i]].sp == NULL; i++);
- if (i<=segend) local_refpt = others[i];
-
- if (findoffs(others+segstart, segend+1-segstart, ct->edge.refpt) != -1)
- local_refpt = ct->edge.refpt;
-
- if (local_refpt==-1) local_refpt = others[segstart];
-
- for (i=segstart; i<=segend; i++)
- ct->affected[others[i]] |= local_refpt==others[i]?0:touchflag;
- }
-}
-
-/* Subdivide an edge into segments and optimize segments separately.
- * A segment consists oh a point, his neighbours, their neighbours...
- */
-static void optimize_edge(InstrCt *ct) {
- int i, p, segstart, next;
- int refpt = ct->edge.refpt;
- int *others = ct->edge.others;
- int othercnt = ct->edge.othercnt;
- int touchflag = (ct->xdir)?tf_x:tf_y;
-
- if (othercnt == 0)
-return;
-
- /* add edge.refpt to edge.others */
- ct->edge.othercnt = ++othercnt;
- ct->edge.others = others = (int *)realloc(others, othercnt*sizeof(int));
- others[othercnt-1]=refpt;
-
- next = 0;
- while (next < othercnt) {
- p = others[segstart = next++];
-
- while((next < othercnt) && (i = findoffs(others+next, othercnt-next,
- NextOnContour(ct->contourends, p))) != -1) {
- p = others[i+=next];
- others[i] = others[next];
- others[next++] = p;
- }
-
- p=others[segstart];
-
- while((next < othercnt) && (i = findoffs(others+next, othercnt-next,
- PrevOnContour(ct->contourends, p))) != -1) {
- p = others[i+=next];
- others[i] = others[next];
- others[next++] = p;
- }
-
- optimize_segment(segstart, next-1, ct);
- }
-
- for (i=next=0; i<othercnt; i++)
- if (!(ct->affected[others[i]] & touchflag) && (others[i] != refpt))
- others[next++] = others[i];
-
- if ((ct->edge.othercnt = next) == 0) {
- free(others);
- ct->edge.others = NULL;
- }
- else /* purely for aesthetic reasons - could be safely removed. */
- qsort(others, ct->edge.othercnt, sizeof(int), sortbynum);
-}
-
-/* For any given point on edge, if there exists a path to other point snapped
- * or to-be-snapped in that zone, such that any points on this path are within
- * that zone, then this given point may be optimized out.
- */
-static void optimize_blue(InstrCt *ct) {
- int i, j, curr;
- int *others = ct->edge.others;
- int othercnt = ct->edge.othercnt;
- int touchflag = (ct->xdir)?tf_x:tf_y;
- int *contourends = ct->contourends;
- uint8 *touched = ct->touched;
- uint8 *affected = ct->affected;
- uint8 *tosnap;
-
- if (othercnt == 0)
-return;
-
- tosnap = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
-
- for(i=0; i<ct->edge.othercnt; i++)
- {
- if (ct->diagpts && ct->diagpts[others[i]].count) continue;
-
- /* check path forward */
- curr=NextOnContour(contourends, others[i]);
- while(curr!=others[i]) {
- double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
- if (fabs(ct->edge.base - coord) > ct->gic->fudge) break;
- if ((touched[curr] | affected[curr]) & touchflag || tosnap[curr]) {
- affected[others[i]] |= touchflag;
- break;
- }
- curr=NextOnContour(contourends, curr);
- }
-
- if (affected[others[i]] & touchflag) continue;
-
- /* check path backward */
- curr=PrevOnContour(contourends, others[i]);
- while(curr!=others[i]) {
- double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
- if (fabs(ct->edge.base - coord) > ct->gic->fudge) break;
- if ((touched[curr] | affected[curr]) & touchflag || tosnap[curr]) {
- affected[others[i]] |= touchflag;
- break;
- }
- curr=PrevOnContour(contourends, curr);
- }
-
- if (!(affected[others[i]] & touchflag)) tosnap[others[i]] = 1;
- }
-
- free(tosnap);
-
- /* remove optimized-out points from list to be instructed. */
- for(i=0; i<ct->edge.othercnt; i++)
- if (affected[others[i]]) {
- ct->edge.othercnt--;
- for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
- i--;
- }
-}
-
-/* For any strong point, check whether it's position can rely on other
- * points (if so, we don't have to instruct it explicitly).
- * This optimization is two-pass. 'Obvious' Off-curve points are sweeped
- * first. Some remaining unneeded points (off- and on-curve) may then be
- * optimized out in second pass.
- *
- * TODO! This optimizer could be even more aggressive - it currently
- * skips some features too small or unexposed to benefit from hinting.
- */
-static void optimize_strongpts_step1(InstrCt *ct);
-static void optimize_strongpts_step2(InstrCt *ct);
-
-static void optimize_strongpts(InstrCt *ct) {
- optimize_strongpts_step1(ct);
- optimize_strongpts_step2(ct);
-}
-
-static void optimize_strongpts_step1(InstrCt *ct) {
- int i, j;
- int *others = ct->edge.others;
- int othercnt = ct->edge.othercnt;
- int *contourends = ct->contourends;
- uint8 *tocull, *tocheck;
-
- if (othercnt == 0)
-return;
-
- tocull = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
- tocheck = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
- for(i=0; i<ct->edge.othercnt; i++) tocheck[ct->edge.others[i]] = 1;
-
- /* for each point of "edge" (would be better called "zone") */
- for(i=0; i<ct->edge.othercnt; i++)
- {
- int pt = others[i];
- double pt_x = ct->bp[pt].x;
- double pt_y = ct->bp[pt].y;
-
- int pt_next = NextOnContour(contourends, pt);
- double pt_next_x = ct->bp[pt_next].x;
- double pt_next_y = ct->bp[pt_next].y;
-
- int pt_prev = PrevOnContour(contourends, pt);
- double pt_prev_x = ct->bp[pt_prev].x;
- double pt_prev_y = ct->bp[pt_prev].y;
-
- /* We sweep only off-curve points here */
- if (ct->gd->points[pt].sp != NULL)
- continue;
-
- if (IsCornerExtremum(ct->xdir, ct->contourends, ct->bp, pt))
- continue;
-
- /* Some off-curve points may 'belong' to extrema from other zone. */
-
- if (/*tocheck[pt_next] &&*/ (ct->gd->points[pt_next].sp != NULL) &&
- (pt_x == pt_next_x || pt_y == pt_next_y))
- tocull[pt] = 1;
-
- if (/*tocheck[pt_prev] &&*/ (ct->gd->points[pt_prev].sp != NULL) &&
- (pt_x == pt_prev_x || pt_y == pt_prev_y))
- tocull[pt] = 1;
- }
-
- /* remove optimized-out points from list to be instructed. */
- for(i=0; i<ct->edge.othercnt; i++)
- if (tocull[others[i]]) {
- ct->edge.othercnt--;
- for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
- i--;
- }
-
- free(tocull);
- free(tocheck);
-}
-
-static void optimize_strongpts_step2(InstrCt *ct) {
- int pass, i, j, forward;
- int next_closed, prev_closed;
- int next_pt_max, next_pt_min, prev_pt_max, prev_pt_min;
- int next_coord_max, next_coord_min, prev_coord_max, prev_coord_min;
- int *others = ct->edge.others;
- int othercnt = ct->edge.othercnt;
- int touchflag = (ct->xdir)?tf_x:tf_y;
- int *contourends = ct->contourends;
- uint8 *touched = ct->touched;
- uint8 *affected = ct->affected;
- uint8 *toinstr, *tocull, *tocheck;
-
- if (othercnt == 0)
-return;
-
- toinstr = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
- tocull = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
- tocheck = (uint8 *)calloc(ct->ptcnt, sizeof(uint8));
- for(i=0; i<ct->edge.othercnt; i++) tocheck[ct->edge.others[i]] = 1;
-
- /* two passes... */
- for(pass=0; pass<2; pass++)
- {
- /* ...for each point of "edge" (would be better called "zone" here) */
- for(i=0; i<ct->edge.othercnt; i++)
- {
- int pt = others[i];
- double pt_coord = (ct->xdir) ? ct->bp[pt].x : ct->bp[pt].y;
-
- /* In first pass, we sweep only off-curve points */
- if ((pass==0) && (ct->gd->points[pt].sp != NULL))
- continue;
-
- if (tocull[pt] || toinstr[pt])
- continue;
-
- /* check path backward and forward */
- for (forward=0; forward<2; forward++)
- {
- int closed = 0;
- int pt_max = pt, pt_min = pt;
- double coord_max = pt_coord, coord_min = pt_coord;
- int curr = forward ? NextOnContour(contourends, pt):
- PrevOnContour(contourends, pt);
-
- while(curr!=pt)
- {
- double coord = (ct->xdir) ? ct->bp[curr].x : ct->bp[curr].y;
-
- if (fabs(ct->edge.base - coord) > ct->gic->fudge)
- break;
-
- if ((touched[curr] | affected[curr]) & touchflag || tocheck[curr])
- {
- if (coord > coord_max) { coord_max = coord; pt_max = curr; }
- else if ((coord == coord_max) && (curr < pt_max)) pt_max = curr;
-
- if (coord < coord_min) { coord_min = coord; pt_min = curr; }
- else if ((coord == coord_min) && (curr < pt_min)) pt_min = curr;
-
- closed = 1;
- }
-
- if ((touched[curr] | affected[curr]) & touchflag || toinstr[curr])
- break;
-
- curr = forward ? NextOnContour(contourends, curr):
- PrevOnContour(contourends, curr);
- }
-
- if (forward) {
- next_closed = closed;
- next_pt_max = pt_max;
- next_pt_min = pt_min;
- next_coord_max = coord_max;
- next_coord_min = coord_min;
- }
- else {
- prev_closed = closed;
- prev_pt_max = pt_max;
- prev_pt_min = pt_min;
- prev_coord_max = coord_max;
- prev_coord_min = coord_min;
- }
- }
-
- if (prev_closed && next_closed && (
- (prev_coord_max >= pt_coord && pt != prev_pt_max &&
- next_coord_min <= pt_coord && pt != next_pt_min) ||
- (prev_coord_min <= pt_coord && pt != prev_pt_min &&
- next_coord_max >= pt_coord && pt != next_pt_max)))
- tocull[pt] = 1;
- else
- toinstr[pt] = 1;
- }
- }
-
- /* remove optimized-out points from list to be instructed. */
- for(i=0; i<ct->edge.othercnt; i++)
- if (tocull[others[i]]) {
- ct->edge.othercnt--;
- for(j=i; j<ct->edge.othercnt; j++) others[j] = others[j+1];
- i--;
- }
-
- free(tocheck);
- free(toinstr);
- free(tocull);
-}
-
-/* Finish instructing the edge. Try to hint only those points on edge that are
- * necessary - IUP should do the rest.
- */
-static void finish_edge(InstrCt *ct, uint8 command) {
- int i;
-
- optimize_edge(ct);
- if (ct->edge.othercnt==0)
-return;
-
- ct->pt=instructpoints(ct->pt, ct->edge.othercnt, ct->edge.others, command);
- for(i=0; i<ct->edge.othercnt; i++)
- ct->touched[ct->edge.others[i]] |= (ct->xdir?tf_x:tf_y);
-
- free(ct->edge.others);
- ct->edge.others=NULL;
- ct->edge.othercnt = 0;
-}
-
-/******************************************************************************
- *
- * Routines for hinting single stems.
- *
- ******************************************************************************/
-
-/* Each stem hint has 'ldone' and 'rdone' flag, indicating whether 'left'
- * or 'right' edge is hinted or not. This functions marks as done all edges at
- * specified coordinate, starting from given hint (hints sometimes share edges).
- */
-static void mark_startenddones(StemData *stem, int is_l ) {
- struct dependent_stem *slave;
- int i;
- uint8 *done;
-
- done = is_l ? &stem->ldone : &stem->rdone;
- *done = true;
- for (i=0; i<stem->dep_cnt; i++) {
- slave = &stem->dependent[i];
- if ( slave->dep_type == 'a' && slave->lbase == is_l ) {
- done = is_l ? &slave->stem->ldone : &slave->stem->rdone;
- *done = true;
- }
- }
-}
-
-static void build_cvt_stem(InstrCt *ct, real width, StdStem *cvt_stem) {
- int i, width_parent, width_me;
- int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
-
- cvt_stem->width = (int)rint(fabs(width));
- cvt_stem->stopat = 32767;
- cvt_stem->snapto =
- CVTSeekStem(ct->xdir, ct->gic, width, false);
-
- for (i=7; i<32768; i++) {
- width_parent = compute_stem_width(ct->xdir, cvt_stem->snapto, EM, i);
- width_me = compute_stem_width(ct->xdir, cvt_stem, EM, i);
-
- if (width_parent != width_me) {
- cvt_stem->stopat = i;
- break;
- }
- }
-}
-
-/* This function has been separated from finish_stem(), because sometimes
- * it is necessary to maintain the distance between two points (usually on
- * opposite stem edges) without instructing the whole stem. Currently we use this
- * to achieve proper positioning of the left edge of a vertical stem in antialiased
- * mode, if instructing this stem has to be started from the right edge
- */
-static void maintain_black_dist(InstrCt *ct, real width, int refpt, int chg_rp0) {
- int callargs[5];
- StdStem *StdW = ct->xdir?&(ct->gic->stdvw):&(ct->gic->stdhw);
- StdStem *ClosestStem;
- StdStem cvt_stem;
-
- ClosestStem = CVTSeekStem(ct->xdir, ct->gic, width, true);
-
- if (ClosestStem != NULL) {
- ct->pt = push2nums(ct->pt, refpt, ClosestStem->cvtindex);
-
- if (ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done)
- *(ct->pt)++ = chg_rp0?MIRP_rp0_min_black:MIRP_min_black;
- else *(ct->pt)++ = chg_rp0?MIRP_min_rnd_black:MIRP_rp0_min_rnd_black;
- }
- else {
- if (ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done &&
- StdW->width!=-1)
- {
- build_cvt_stem(ct, width, &cvt_stem);
-
- callargs[0] = ct->edge.refpt;
- callargs[1] = cvt_stem.snapto->cvtindex;
- callargs[2] = chg_rp0?1:0;
- callargs[3] = cvt_stem.stopat;
- callargs[4] = 4;
- ct->pt = pushnums(ct->pt, 5, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = chg_rp0?MDRP_rp0_min_rnd_black:MDRP_min_rnd_black;
- }
- }
-}
-
-/* Given the refpt for one of this hint's edges is already positioned, this
- * function aligns 'others' (SHP with given shp_rp) for this edge and positions
- * the second edge, optionally setting its refpt as rp0. It frees edge.others
- * and sets edge.othercnt to zero, but it leaves edge.refpt set to last
- * instructed edge.
- */
-#define use_rp1 (true)
-#define use_rp2 (false)
-#define set_new_rp0 (true)
-#define keep_old_rp0 (false)
-static void finish_stem(StemData *stem, int shp_rp1, int chg_rp0, InstrCt *ct)
-{
- int is_l, basedone, oppdone, reverse;
- real hleft, hright, width;
-
- if (stem == NULL)
- return;
- hleft = ((real *) &stem->left.x)[!ct->xdir];
- hright= ((real *) &stem->right.x)[!ct->xdir];
-
- is_l = (fabs(hleft - ct->edge.base) < fabs(hright - ct->edge.base));
- basedone = ( is_l && stem->ldone ) || ( !is_l && stem->rdone );
- oppdone = ( is_l && stem->rdone ) || ( !is_l && stem->ldone );
- reverse = ( ct->xdir && !is_l && !stem->ldone && !stem->ghost );
- width = stem->width;
-
- if ( !reverse && !basedone ) {
- ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
- finish_edge(ct, shp_rp1?SHP_rp1:SHP_rp2);
- mark_startenddones(stem, is_l );
- }
-
- if (oppdone || (stem->ghost && ((stem->width==20) || (stem->width==21)))) {
- stem->ldone = stem->rdone = 1;
- return;
- }
-
- init_stem_edge(ct, stem, !is_l);
- if (ct->edge.refpt == -1) {
- /* We have skipped the right edge to start instructing this stem from
- * left. But its left edge appears to have no points to be instructed.
- * So return to the right edge and instruct it before exiting */
- if ( reverse && !basedone ) {
- init_stem_edge(ct, stem, is_l);
- ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
- finish_edge(ct, shp_rp1?SHP_rp1:SHP_rp2);
- mark_startenddones(stem, is_l );
- }
- return;
- }
- maintain_black_dist(ct, width, ct->edge.refpt, chg_rp0);
-
- if ( reverse ) {
- is_l = !is_l;
- ct->rp0 = ct->edge.refpt;
- ct->pt = pushpoint(ct->pt, ct->rp0);
- *(ct->pt)++ = MDAP_rnd;
- ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
- finish_edge(ct, SHP_rp1);
- mark_startenddones( stem, is_l );
- if ( !stem->rdone ) {
- init_stem_edge(ct, stem, false);
- if (ct->edge.refpt == -1)
- return;
- maintain_black_dist(ct, width, ct->edge.refpt, chg_rp0);
- }
- }
-
- if (chg_rp0) ct->rp0 = ct->edge.refpt;
- ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
- finish_edge(ct, SHP_rp2);
- mark_startenddones( stem, !is_l );
-}
-
-static void mark_points_affected(InstrCt *ct,StemData *target,PointData *opd,int next) {
- Spline *s;
- PointData *pd, *cpd;
- int cpidx;
-
- s = next ? opd->sp->next : opd->sp->prev;
- pd = next ? &ct->gd->points[s->to->ptindex] : &ct->gd->points[s->from->ptindex];
- while (IsStemAssignedToPoint(pd, target, !next) == -1) {
- if (pd->ttfindex < ct->gd->realcnt &&
- value_point(ct, pd->ttfindex, pd->sp, ct->gd->emsize))
- ct->affected[pd->ttfindex] |= ct->xdir?tf_x:tf_y;
-
- if (!pd->sp->noprevcp) {
- cpidx = pd->sp->prev->from->nextcpindex;
- cpd = &ct->gd->points[cpidx];
- if (value_point(ct, cpd->ttfindex, pd->sp, ct->gd->emsize))
- ct->affected[cpd->ttfindex] |= ct->xdir?tf_x:tf_y;
- }
- if (!pd->sp->nonextcp) {
- cpidx = pd->sp->nextcpindex;
- cpd = &ct->gd->points[cpidx];
- if (value_point(ct, cpd->ttfindex, pd->sp, ct->gd->emsize))
- ct->affected[cpd->ttfindex] |= ct->xdir?tf_x:tf_y;
- }
- s = next ? pd->sp->next : pd->sp->prev;
- pd = next ? &ct->gd->points[s->to->ptindex] : &ct->gd->points[s->from->ptindex];
- if ( pd == opd ) {
- IError( "The ball terminal with a key point at %.3f,%.3f\n"
- "appears to be incorrectly linked to the %s stem\n"
- "<%.3f, %.3f>",
- pd->base.x,pd->base.y,
- ct->xdir?"vertical":"horizontal",
- ct->xdir?target->left.x:target->right.y,target->width );
- break;
- }
- }
-}
-
-static void finish_serif(StemData *slave, StemData *master, int lbase, int is_ball, InstrCt *ct)
-{
- int inner_pt, callargs[4];
- struct stem_chunk *chunk;
- PointData *opd;
- int i;
-
- if (slave == NULL || master == NULL)
-return;
- inner_pt = ( lbase ) ? master->rightidx : master->leftidx;
-
- init_stem_edge(ct, slave, !lbase);
- if (ct->edge.refpt == -1)
-return;
-
- if (ct->gic->fpgm_done) {
- callargs[0] = is_ball ? 0 : 64;
- callargs[1] = inner_pt;
- callargs[2] = ct->edge.refpt;
- callargs[3] = 9;
- ct->pt = pushnums(ct->pt, 4, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- *(ct->pt)++ = 0x7D; /* RDTG */
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = MDRP_min_rnd_black;
- *(ct->pt)++ = 0x18; /* RTG */
- }
-
- ct->touched[ct->edge.refpt] |= ct->xdir?tf_x:tf_y;
- finish_edge(ct, SHP_rp2);
- mark_startenddones( slave, !lbase );
-
- if ( !interpolate_strong || !instruct_ball_terminals )
-return;
-
- /* Preserve points on ball terminals from being interpolated
- * between edges by marking them as affected */
- for ( i=0; i<slave->chunk_cnt; i++ ) {
- chunk = &slave->chunks[i];
- opd = lbase ? chunk->r : chunk->l;
-
- if (chunk->is_ball && opd != NULL) {
- mark_points_affected(ct, chunk->ball_m, opd, true);
- mark_points_affected(ct, chunk->ball_m, opd, false);
- }
- }
-}
-
-static void link_serifs_to_edge(InstrCt *ct, StemData *stem, int is_l) {
- int i, callargs[3];
- struct dependent_serif *serif;
-
- /* We use an FPGM function to set rp0, and thus the exact value
- * is not known at the compilation time. So it is safer to reset
- * ct->rp0 to -1
- */
- if ( ct->gic->fpgm_done ) {
- ct->rp0 = -1;
- callargs[0] = is_l ? stem->rightidx : stem->leftidx;
- callargs[1] = is_l ? stem->leftidx : stem->rightidx;
- callargs[2] = 10;
- ct->pt = pushnums(ct->pt, 3, callargs);
- *(ct->pt)++ = CALL;
- } else {
- init_stem_edge(ct, stem, !is_l);
- if ( ct->rp0 != ct->edge.refpt ) {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = SRP0;
- ct->rp0 = ct->edge.refpt;
- }
- }
- for (i=0; i<stem->serif_cnt; i++) {
- serif = &stem->serifs[i];
- if (serif->lbase == is_l &&
- ((serif->is_ball && instruct_ball_terminals) ||
- (!serif->is_ball && instruct_serif_stems)))
- finish_serif( serif->stem,stem,is_l,serif->is_ball,ct );
- }
-}
-
-static void instruct_serifs(InstrCt *ct, StemData *stem) {
- int i, lcnt, rcnt;
- struct dependent_serif *serif;
-
- if ( stem->leftidx == -1 || stem->rightidx == -1 )
- return;
- lcnt = rcnt = 0;
- for (i=0; i<stem->serif_cnt; i++) {
- serif = &stem->serifs[i];
- if ((serif->is_ball && !instruct_ball_terminals) ||
- (!serif->is_ball && !instruct_serif_stems))
- continue;
- if ( serif->lbase )
- lcnt++;
- else if ( !serif->lbase )
- rcnt++;
- }
-
- if (stem->ldone && lcnt > 0)
- link_serifs_to_edge(ct, stem, true);
- if (stem->rdone && rcnt > 0)
- link_serifs_to_edge(ct, stem, false);
-}
-
-static void instruct_dependent(InstrCt *ct, StemData *stem) {
- int i, j, rp, rp1, rp2, stopat, callargs[4];
- struct dependent_stem *slave;
- int w_master, w_slave;
- StdStem *std_master, *std_slave, norm_master, norm_slave;
- StdStem *StdW = ct->xdir?&(ct->gic->stdvw):&(ct->gic->stdhw);
-
- for (i=0; i<stem->dep_cnt; i++) {
- slave = &stem->dependent[i];
- if (slave->stem->master == NULL)
- continue;
-
- init_stem_edge(ct, slave->stem, slave->lbase);
- if (ct->edge.refpt == -1) continue;
-
- if (slave->dep_type == 'i' && stem->ldone && stem->rdone) {
- rp1 = ct->xdir ? stem->leftidx : stem->rightidx;
- rp2 = ct->xdir ? stem->rightidx : stem->leftidx;
- callargs[0] = ct->edge.refpt;
- callargs[1] = rp2;
- callargs[2] = rp1;
- if (ct->gic->fpgm_done) {
- callargs[3] = 8;
- ct->pt = pushpoints(ct->pt, 4, callargs);
- *(ct->pt)++ = CALL;
- } else {
- ct->pt = pushpoints(ct->pt, 3, callargs);
- *(ct->pt)++ = SRP1;
- *(ct->pt)++ = SRP2;
- *(ct->pt)++ = DUP;
- *(ct->pt)++ = IP;
- *(ct->pt)++ = MDAP_rnd;
- }
- }
- else if (slave->dep_type == 'm' &&
- ((slave->lbase && stem->ldone) || (!slave->lbase && stem->rdone))) {
-
- rp = slave->lbase ? stem->leftidx : stem->rightidx;
- if ( rp != ct->rp0 ) {
- ct->pt = pushpoint(ct->pt, rp);
- *(ct->pt)++ = SRP0;
- ct->rp0 = rp;
- }
-
- /* It is possible that at certain PPEMs both the master and slave stems are
- * regularized, say, to 1 pixel, but the difference between their positions
- * is rounded to 1 pixel too. Thus one stem is shifted relatively to another,
- * so that the overlap disappears. This looks especially odd for nesting/nested
- * stems. We use a special FPGM function to prevent this.
- */
- if ( ct->gic->cvt_done && ct->gic->fpgm_done && ct->gic->prep_done && StdW->width!=-1 && (
- ((&stem->left.x)[!ct->xdir] <= (&slave->stem->left.x)[!ct->xdir] &&
- ( &stem->right.x)[!ct->xdir] >= (&slave->stem->right.x)[!ct->xdir] ) ||
- ((&stem->left.x)[!ct->xdir] >= (&slave->stem->left.x)[!ct->xdir] &&
- ( &stem->right.x)[!ct->xdir] <= (&slave->stem->right.x)[!ct->xdir] ))) {
-
- std_master = CVTSeekStem(ct->xdir, ct->gic, stem->width, true);
- std_slave = CVTSeekStem(ct->xdir, ct->gic, slave->stem->width, true);
- if ( std_master == NULL ) {
- build_cvt_stem(ct, stem->width, &norm_master);
- std_master = &norm_master;
- }
- if ( std_slave == NULL ) {
- build_cvt_stem(ct, slave->stem->width, &norm_slave);
- std_slave = &norm_slave;
- }
-
- stopat = 32768;
- for (j=7; j<=stopat; j++) {
- w_master = compute_stem_width(ct->xdir, std_master, ct->gd->emsize, j);
- w_slave = compute_stem_width(ct->xdir, std_slave , ct->gd->emsize, j);
-
- if (w_master != w_slave)
- stopat = j;
- }
- callargs[0] = stopat;
- callargs[1] = ct->rp0;
- callargs[2] = ct->edge.refpt;
- callargs[3] = 14;
- ct->pt = pushpoints(ct->pt, 4, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = DUP;
- *(ct->pt)++ = MDRP_rp0_rnd_white;
- *(ct->pt)++ = SRP1;
- }
- }
- else if (slave->dep_type == 'a' &&
- ((slave->lbase && stem->ldone) || (!slave->lbase && stem->rdone))) {
- if ( ct->edge.refpt != ct->rp0 ) {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = SRP0;
- }
- }
- else
- continue;
-
- ct->rp0 = ct->edge.refpt;
- finish_stem(slave->stem, use_rp1, keep_old_rp0, ct);
- if ( instruct_serif_stems || instruct_ball_terminals )
- instruct_serifs(ct, slave->stem);
-
- instruct_dependent(ct, slave->stem);
- }
-}
-
-/******************************************************************************
- *
- * I decided to do snapping to blues at the very beginning of the instructing.
- *
- * Blues are processed in certain (important) order: baseline, descenders
- * (from deeper to shorter), ascenders (from taller to shorter).
- *
- * For each blue, one of the edges is put into CVT: lower if is't > zero,
- * the upper otherwise. A twilight point 0 is established at this height. All
- * the glyph's points decided to be worth snapping are then moved relative to
- * this twilight point, being subject to rounding 'down-to-int'. Space taken
- * is at most 8*ptcnt.
- *
- * For each blue, all yet unprocessed HStems affected are instructed. Ghost
- * hints are reckognised. If there is at least one stem hint in given blue zone,
- * autoinstructor will seek for other interesting features, so there is no need
- * to hint them explicitly.
- *
- * TODO! We currently instruct hints dependent on those controlled by blues.
- * This may be not always corrrect (e.g. if a dependent hint is itself
- * controlled by blue zone - possibly even different). Research needed.
- *
- * Important notes:
- *
- * The zone count must be set to 2, the twilight point count must be nonzero.
- * This is done automagically in init_maxp(), otherwise this method wouldn't
- * work at all. Currently there is only one twilight point used, but there
- * may be needed one or even two points per blue zone if some advanced snapping
- * and counter managing is to be done.
- *
- * Snapping relies on function 0 in FPGM, see init_fpgm().
- *
- * Using MIAP (single cvt, relying on cut-in) instead of twilight points
- * causes overshoots to appear/disappear inconsistently at small pixel sizes.
- * This flickering is disastrous to soft, wavy horizontal lines. We could use
- * any glyph's point at needed height, but we're not certain we'll find any.
- *
- * The inner (leftwards) contours aren't snapped to the blue zone.
- * This could have created weird artifacts. Of course this will fail for
- * glyphs with wrong direction, but I won't handle it for now.
- *
- * TODO! Remind the user to correct direction or do it for him.
- * TODO! Try to instruct 'free points' with single push and LOOPCALL.
- *
- * If we didn't snapped any point to a blue zone, we shouldn't mark any HStem
- * edges done. This could made some important points on inner contours missed.
- *
- ******************************************************************************/
-
-/* Each blue zone has two TTF point indices associated with it: 'highest' and
- * 'lowest'. These have to be points with highest and lowest Y coordinates that
- * are snapped to that blue zone (directly or by horizontal stem). Currently
- * we register only edge.refpt. These points are later to be used for horizontal
- * stems' positioning.
- */
-static void update_blue_pts(int blueindex, InstrCt *ct)
-{
- BasePoint *bp = ct->bp;
- BlueZone *blues = ct->gic->blues;
-
- if (ct->edge.refpt == -1)
-return;
-
- if (blues[blueindex].highest == -1 ||
- bp[ct->edge.refpt].y > bp[blues[blueindex].highest].y)
- blues[blueindex].highest = ct->edge.refpt;
-
- if (blues[blueindex].lowest == -1 ||
- bp[ct->edge.refpt].y < bp[blues[blueindex].lowest].y)
- blues[blueindex].lowest = ct->edge.refpt;
-}
-
-/* It is theoretically possible that 'highest' and 'lowest' points of neighbour
- * blue zones overlap, and thus may spoil horizontal stems' positioning.
- * Here we fix this up.
- */
-static void fixup_blue_pts(BlueZone *b1, BlueZone *b2) {
- if (b1->lowest > b2->lowest) b1->lowest = b2->lowest;
- if (b1->highest < b2->highest) b1->highest = b2->highest;
-}
-
-static void check_blue_pts(InstrCt *ct) {
- BasePoint *bp = ct->bp;
- BlueZone *blues = ct->gic->blues;
- int i, j, bluecnt = ct->gic->bluecnt;
-
- for (i=0; i<bluecnt; i++)
- if (blues[i].lowest != -1)
- for (j=0; j<bluecnt; j++)
- if (i != j && blues[j].lowest != -1 && SegmentsOverlap(
- bp[blues[i].lowest].y, bp[blues[i].highest].y,
- bp[blues[j].lowest].y, bp[blues[j].highest].y))
- fixup_blue_pts(blues+i, blues+j);
-}
-
-static int snap_stem_to_blue(InstrCt *ct,StemData *stem, BlueZone *blue, int idx) {
- int i, is_l, ret = 0;
- int callargs[3] = { 0/*pt*/, 0/*cvt*/, 0 };
- real base, advance, tmp;
- real fuzz = GetBlueFuzz(ct->gic->sf);
- StemData *slave;
-
- /* Which edge to start at? */
- /* Starting at the other would usually be wrong. */
- if (blue->overshoot < blue->base && ( !stem->ghost || stem->width == 21 ))
- {
- is_l = false;
- base = stem->right.y;
- advance = stem->left.y;
- }
- else {
- is_l = true;
- base = stem->left.y;
- advance = stem->right.y;
- }
-
- /* This is intended as a fallback if the base edge wasn't within
- * this bluezone, and advance edge was.
- */
- if (!stem->ghost &&
- !SegmentsOverlap(base+fuzz, base-fuzz, blue->base, blue->overshoot) &&
- SegmentsOverlap(advance+fuzz, advance-fuzz, blue->base, blue->overshoot))
- {
- tmp = base;
- base = advance;
- advance = tmp;
- is_l = !is_l;
- }
-
- /* instruct the stem */
- init_stem_edge(ct, stem, is_l);
- if (ct->edge.refpt == -1) {
- for ( i=0; i<stem->dep_cnt; i++ ) {
- slave = stem->dependent[i].stem;
- /* A hack which allows single-edge hints to tie features
- * to remote blue zones. */
- if ( stem->ghost ) slave->blue = idx;
- if ( slave->blue == idx )
- ret += snap_stem_to_blue(ct, slave, blue, idx);
- }
- return( ret );
- }
- update_blue_pts(idx, ct);
- callargs[0] = ct->rp0 = ct->edge.refpt;
- callargs[1] = blue->cvtindex;
-
- if (ct->gic->fpgm_done) {
- ct->pt = pushpoints(ct->pt, 3, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoints(ct->pt, 2, callargs);
- *(ct->pt)++ = MIAP_rnd;
- }
-
- finish_stem(stem, use_rp1, keep_old_rp0, ct);
- for ( i=0; i<stem->dep_cnt; i++ ) {
- slave = stem->dependent[i].stem;
- if ( slave->blue == idx ) {
- ret += snap_stem_to_blue(ct, slave, blue, idx);
- slave->master = NULL;
- }
- }
-
- if( instruct_serif_stems || instruct_ball_terminals )
- instruct_serifs(ct, stem);
- instruct_dependent(ct, stem);
- update_blue_pts(idx, ct); /* this uses only refpt: who cares? */
- return( ret + 1 );
-}
-
-/* Snap stems and perhaps also some other points to given bluezone and set up
- * its 'highest' and 'lowest' point indices.
- */
-static void snap_to_blues(InstrCt *ct) {
- int i, j;
- int therewerestems; /* were there any HStems snapped to this blue? */
- StemData *stem; /* for HStems affected by blues */
- real base; /* for the hint */
- int callargs[3] = { 0/*pt*/, 0/*cvt*/, 0 };
- real fudge;
- int bluecnt=ct->gic->bluecnt;
- int queue[12]; /* Blue zones' indices in processing order */
- BlueZone *blues = ct->gic->blues;
- real fuzz = GetBlueFuzz(ct->gic->sf);
-
- if (bluecnt == 0)
-return;
-
- /* Fill the processing queue - baseline goes first, then bottom zones */
- /* sorted by base in ascending order, then top zones sorted in descending */
- /* order. I assume the blues are sorted in ascending order first. */
- for (i=0; (i < bluecnt) && (blues[i].base < 0); i++);
- queue[0] = i;
- for (i=0; i<queue[0]; i++) queue[i+1] = i;
- for (i=queue[0]+1; i<bluecnt; i++) queue[i] = bluecnt - i + queue[0];
-
- /* Process the blues. */
- for (i=0; i<bluecnt; i++) {
- therewerestems = 0;
-
- /* Process all hints with edges within current blue zone. */
- for ( j=0; j<ct->gd->hbundle->cnt; j++ ) {
- stem = ct->gd->hbundle->stemlist[j];
- if (stem->master != NULL || stem->blue != queue[i] || stem->ldone || stem->rdone)
- continue;
-
- therewerestems += snap_stem_to_blue(ct, stem, &blues[queue[i]], queue[i]);
- }
-
- /* Now I'll try to find points not snapped by any previous stem hint. */
- if (therewerestems) {
- base = (blues[queue[i]].base + blues[queue[i]].overshoot) / 2.0;
- fudge = ct->gic->fudge;
- ct->gic->fudge = fabs(base - blues[queue[i]].base) + fuzz;
- init_edge(ct, base, EXTERNAL_CONTOURS);
- optimize_blue(ct);
- optimize_edge(ct);
-
- if (ct->edge.refpt == -1) {
- ct->gic->fudge = fudge;
- continue;
- }
-
- if (!(ct->touched[ct->edge.refpt]&tf_y || ct->affected[ct->edge.refpt]&tf_y)) {
- callargs[0] = ct->rp0 = ct->edge.refpt;
-
- if (ct->gic->fpgm_done) {
- ct->pt = pushpoints(ct->pt, 3, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoints(ct->pt, 2, callargs);
- *(ct->pt)++ = MIAP_rnd;
- }
-
- ct->touched[ct->edge.refpt] |= tf_y;
- }
-
- for (j=0; j<ct->edge.othercnt; j++) {
- callargs[0] = ct->rp0 = ct->edge.others[j];
-
- if (ct->gic->fpgm_done) {
- ct->pt = pushpoints(ct->pt, 3, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoints(ct->pt, 2, callargs);
- *(ct->pt)++ = MIAP_rnd;
- }
-
- ct->touched[ct->edge.others[j]] |= tf_y;
- }
-
- update_blue_pts(queue[i], ct);
-
- if (ct->edge.others != NULL) {
- free(ct->edge.others);
- ct->edge.others = NULL;
- ct->edge.othercnt = 0;
- }
-
- ct->gic->fudge = fudge;
- }
- }
-
- check_blue_pts(ct);
-}
-
-static int get_counters_cut_in(InstrCt *ct, int m1, int m2, int c1, int c2) {
- real s1, e1, s2, e2, width1, width2;
- int i, swidth1, swidth2;
- int EM = ct->gic->sf->ascent + ct->gic->sf->descent;
-
- s1 = (&ct->gd->points[m1].base.x)[!ct->xdir];
- e1 = (&ct->gd->points[m2].base.x)[!ct->xdir];
- s2 = (&ct->gd->points[c1].base.x)[!ct->xdir];
- e2 = (&ct->gd->points[c2].base.x)[!ct->xdir];
- width1 = e1 - s1; width2 = e2 - s2;
-
- if ( RealNear( width1, width2 ))
- return( 0 );
-
- for (i=7; i<32768; i++) {
- swidth1 = (int)rint((rint(fabs(width1)) * i * 64.0)/EM);
- swidth2 = (int)rint((rint(fabs(width2)) * i * 64.0)/EM);
- if ( fabs(swidth1 - swidth2) >= SNAP_THRESHOLD )
- break;
- }
- return( i );
-}
-
-/******************************************************************************
- *
- * High-level functions for instructing horizontal and vertical stems.
- * Both use 'geninstrs' for positioning single, elementary stems.
- *
- ******************************************************************************/
-
-/* geninstrs's main burden is to choose the better of two reference points
- * found by init_stem_edge() - one for each edge - and position it relatively
- * to other stems (if not already done).
- *
- * If none of the edges is positioned:
- * If this hint is the first, previously overlapped, or simply horizontal,
- * position the reference point at the base where it is using MDAP; otherwise
- * position the hint's base rp0 relatively to the previous hint's end using
- * MDRP with white minimum distance (fpgm function 1).
- *
- * Calling finish_stem() will deal with the rest of points needing explicit
- * positioning. Then we instruct serifs and dependent stems, if wanted.
- */
-static void geninstrs(InstrCt *ct, StemData *stem, StemData *prev, int lbase) {
- int shp_rp1, chg_rp0, c_m_pt1 = -1, c_m_pt2 = -1;
- int callargs[6];
- real prev_pos = 0, cur_pos;
-
- if (stem->ldone && stem->rdone)
- return;
- if ((lbase && stem->rdone) || (!lbase && stem->ldone))
- lbase = !lbase;
- init_stem_edge(ct, stem, lbase);
- if (ct->edge.refpt == -1) {
- lbase = !lbase;
- init_stem_edge(ct, stem, lbase);
- }
- if (ct->edge.refpt == -1)
- return;
-
- if (ct->rp0 < ct->gd->realcnt && ct->rp0 >= 0)
- prev_pos = (&ct->gd->points[ct->rp0].base.x)[!ct->xdir];
- cur_pos = (&ct->gd->points[ct->edge.refpt].base.x)[!ct->xdir];
-
- if (prev != NULL && stem->prev_c_m != NULL && prev->next_c_m != NULL ) {
- c_m_pt1 = ct->xdir ? prev->next_c_m->rightidx : prev->next_c_m->leftidx;
- c_m_pt2 = ct->xdir ? stem->prev_c_m->leftidx : stem->prev_c_m->rightidx;
- }
-
- /* Now the stem's origin must be placed in respect to others... */
- /* TODO! What's really needed here is an iterative procedure that */
- /* would preserve counters and widths, like in freetype2. */
- /* For horizontal stems, interpolating between blues is being be done. */
-
- if (stem->ldone || stem->rdone ) {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = MDAP; /* sets rp0 and rp1 */
- shp_rp1 = use_rp1;
- chg_rp0 = (ct->xdir && !lbase) || (!ct->xdir && lbase);
- }
- else if (!ct->xdir) { /* horizontal stem */
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = MDAP_rnd;
- shp_rp1 = use_rp1;
- chg_rp0 = keep_old_rp0;
- }
- else if (prev == NULL) { /* first vertical stem */
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- *(ct->pt)++ = MDRP_rp0_rnd_white;
- shp_rp1 = use_rp2;
- chg_rp0 = keep_old_rp0;
- }
- else {
- if (ct->gic->fpgm_done) {
- if ( control_counters && c_m_pt1 != -1 && c_m_pt2 != -1 ) {
- callargs[0] = c_m_pt1;
- callargs[1] = c_m_pt2;
- callargs[2] = ct->rp0;
- callargs[3] = ct->edge.refpt;
- callargs[4] = get_counters_cut_in(ct, c_m_pt1, c_m_pt2, ct->rp0, ct->edge.refpt);
- callargs[5] = 15;
- ct->pt = pushpoints(ct->pt, 6, callargs);
-
- } else if ( control_counters && prev != NULL && prev->leftidx != -1 && prev->rightidx != -1 ) {
- callargs[0] = ct->xdir ? prev->leftidx : prev->rightidx;
- callargs[1] = ct->edge.refpt;
- callargs[2] = ( cur_pos - prev_pos ) > ct->gic->fudge ? 16 : 17;
- ct->pt = pushpoints(ct->pt, 3, callargs);
-
- } else if ( fabs( cur_pos - prev_pos ) > ct->gic->fudge ) {
- ct->pt = push2nums(ct->pt, ct->edge.refpt, 1);
- } else {
- ct->pt = push2nums(ct->pt, ct->edge.refpt, 11);
- }
- *(ct->pt)++ = CALL;
- }
- else {
- ct->pt = pushpoint(ct->pt, ct->edge.refpt);
- if ( fabs( cur_pos - prev_pos ) > ct->gic->fudge )
- *(ct->pt)++ = MDRP_rp0_min_rnd_grey;
- else
- *(ct->pt)++ = MDRP_rp0_rnd_white;
- }
- shp_rp1 = use_rp2;
-
- /* Don't switch rp0 to the second edge. Thus, relative distance
- * to the next stem is be larger, and errors are hopefully lesser.
- * TODO! This is disputable.
- * TODO! For the last vstem, we probably want to switch rp0 anyway.
- */
- chg_rp0 = keep_old_rp0;
- }
- ct->rp0 = ct->edge.refpt;
- finish_stem(stem, shp_rp1, chg_rp0, ct);
- if ( instruct_serif_stems || instruct_ball_terminals )
- instruct_serifs(ct, stem);
-
- instruct_dependent(ct, stem);
-}
-
-/* High-level function for instructing horizontal stems.
- *
- * It is assumed that blues (and hstems associated with them) are already
- * done so that remaining stems can be interpolated between them.
- *
- * TODO! CJK hinting will probably need different function (HStemGeninstCJK?)
- * TODO! Instruct top and bottom bearings for fonts which have them.
- */
-static void HStemGeninst(InstrCt *ct) {
- BlueZone *blues = ct->gic->blues;
- int bluecnt = ct->gic->bluecnt;
- BasePoint *bp = ct->bp;
- StemData *stem;
- int i, j, rp1, rp2, opp, bpt, ept;
- double hbase, hend;
- int mdrp_end, mdrp_base, ip_base, *rpts1, *rpts2;
- int callargs[5];
-
- if ( ct->gd->hbundle == NULL )
- return;
- rpts1 = calloc(ct->gd->hbundle->cnt, sizeof(int));
- rpts2 = calloc(ct->gd->hbundle->cnt, sizeof(int));
-
- /* Interpolating between blues is splitted to two stages: first
- * we determine which stems can be interpolated and which cannot
- * and store the numbers of reference points, and then (in the
- * second cycle) proceed to generating actual instructions. The reason is
- * that we need a special handling for dependent stems: if they
- * can be interpolated, we process them separately, but otherwise
- * the normal algorithm for positioning dependent stems relatively
- * to their "masters" is used. It is necessary to know which method
- * to prefer for each stem at the time instructions are generated.
- */
- for ( i=0; i<ct->gd->hbundle->cnt; i++ )
- {
- stem = ct->gd->hbundle->stemlist[i];
- if (!stem->ldone && !stem->rdone)
- {
- /* Set up upper edge (hend) and lower edge (hbase). */
- hbase = stem->right.y;
- hend = stem->left.y;
-
- /* Find two points to interpolate the HStem between.
- rp1 = lower, rp2 = upper. */
- rp1 = -1;
- rp2 = -1;
-
- for (j=0; j<bluecnt; j++) {
- if (blues[j].lowest == -1) // implies blues[j].highest==-1 too
- continue;
-
- if (bp[blues[j].lowest].y < hbase)
- if (rp1==-1 || bp[rp1].y < bp[blues[j].lowest].y)
- rp1=blues[j].lowest;
-
- if (bp[blues[j].highest].y > hend)
- if (rp2==-1 || bp[rp2].y > bp[blues[j].highest].y)
- rp2=blues[j].highest;
- }
- rpts1[i] = rp1; rpts2[i] = rp2;
-
- /* If a dependent stem has to be positioned by interpolating
- * one of its edges between the edges of the master stem and
- * we have found reference points to interpolate it between
- * blues, then we prefer to interpolate it between blues. However
- * we keep the standard handling for other types of dependent
- * stems, since usually positioning relatively to the "master"
- * stem is more important than positioning relatively to blues
- * in such cases.
- * Exception: nested stems marked for interpolation should be
- * positioned by interpolating between the edges of the nesting
- * stem.
- */
- if (rp1!=-1 && rp2!=-1 && stem->master != NULL)
- for (j=0; j<stem->master->dep_cnt; j++) {
- if (stem->master->dependent[j].stem == stem &&
- stem->master->dependent[j].dep_type == 'i' &&
- (stem->master->left.y <= stem->left.y ||
- stem->master->right.y >= stem->right.y)) {
- stem->master = NULL;
- break;
- }
- }
- }
- }
-
- for ( i=0; i<ct->gd->hbundle->cnt; i++ )
- {
- stem = ct->gd->hbundle->stemlist[i];
- if ( stem->master != NULL )
- continue;
- if (!stem->ldone && !stem->rdone)
- {
- hbase = stem->right.y;
- hend = stem->left.y;
-
- rp1 = rpts1[i]; rp2 = rpts2[i];
- /* Reference points not found? Fall back to old method. */
- if (rp1==-1 || rp2==-1) {
- geninstrs(ct, stem, NULL, false);
- continue;
- }
-
- bpt = ept = -1;
- if ( !stem->ghost || stem->width == 21 ) {
- init_stem_edge(ct, stem, false);
- bpt = ct->edge.refpt;
- }
- if ( !stem->ghost || stem->width == 20 ) {
- init_stem_edge(ct, stem, true);
- ept = ct->edge.refpt;
- }
- if ( bpt == -1 && ept == -1 )
- continue;
-
- /* Align the stem relatively to rp0 and rp1. */
- mdrp_end = ept != -1 &&
- fabs(bp[rp2].y - hbase) < 0.2*fabs(bp[rp2].y - bp[rp1].y);
- mdrp_base = bpt != -1 &&
- fabs(bp[rp1].y - hend) < 0.2*fabs(bp[rp2].y - bp[rp1].y);
-
- if (mdrp_end || mdrp_base) {
- if (mdrp_end) init_stem_edge(ct, stem, true);
- else init_stem_edge(ct, stem, false);
-
- if (ct->edge.refpt == -1) continue;
-
- if (mdrp_end) ct->pt = push2points(ct->pt, ct->edge.refpt, rp2);
- else ct->pt = push2points(ct->pt, ct->edge.refpt, rp1);
-
- *(ct->pt)++ = SRP0;
- *(ct->pt)++ = DUP;
- *(ct->pt)++ = MDRP_grey;
- *(ct->pt)++ = MDAP_rnd;
- }
- else if ( bpt == -1 || ept == -1 ) {
- ip_base = ( ept == -1 );
- init_stem_edge(ct, stem, !ip_base);
- if ( ct->gic->fpgm_done ) {
- callargs[0] = ct->edge.refpt;
- callargs[1] = rp1;
- callargs[2] = rp2;
- callargs[3] = 8;
- ct->pt = pushnums(ct->pt, 4, callargs);
- *(ct->pt)++ = CALL;
- }
- else {
- callargs[0] = ct->edge.refpt;
- callargs[1] = rp1;
- callargs[2] = rp2;
- ct->pt = pushnums(ct->pt, 3, callargs);
- *(ct->pt)++ = SRP2;
- *(ct->pt)++ = SRP1;
- *(ct->pt)++ = DUP;
- *(ct->pt)++ = IP;
- *(ct->pt)++ = MDAP_rnd;
- }
- }
- else {
- ip_base = fabs(bp[rp2].y - hend) < fabs(bp[rp1].y - hbase);
- opp = ip_base ? ept : bpt;
- init_stem_edge(ct, stem, !ip_base);
-
- if (ct->edge.refpt == -1) continue;
-
- if ( ct->gic->fpgm_done ) {
- callargs[0] = opp;
- callargs[1] = ct->edge.refpt;
- callargs[2] = rp1;
- callargs[3] = rp2;
- callargs[4] = 13;
- ct->pt = pushnums(ct->pt, 5, callargs);
- *(ct->pt)++ = CALL;
- } else {
- callargs[0] = ct->edge.refpt;
- callargs[1] = rp1;
- callargs[2] = rp2;
- ct->pt = pushnums(ct->pt, 3, callargs);
- *(ct->pt)++ = SRP2;
- *(ct->pt)++ = SRP1;
- *(ct->pt)++ = DUP;
- *(ct->pt)++ = IP;
- *(ct->pt)++ = MDAP_rnd;
- }
- }
-
- ct->rp0 = ct->edge.refpt;
- finish_stem(stem, use_rp1, keep_old_rp0, ct);
- if ( instruct_serif_stems || instruct_ball_terminals )
- instruct_serifs(ct, stem);
-
- instruct_dependent(ct, stem);
- }
- }
- free(rpts1);
- free(rpts2);
-}
-
-/*
- * High-level function for instructing vertical stems.
- *
- * TODO! CJK hinting may need different function (VStemGeninstCJK?)
- */
-static void VStemGeninst(InstrCt *ct) {
- StemData *stem, *prev=NULL;
- int i;
-
- if (ct->rp0 != ct->ptcnt) {
- ct->pt = pushpoint(ct->pt, ct->ptcnt);
- *(ct->pt)++ = MDAP_rnd;
- ct->rp0 = ct->ptcnt;
- }
-
- if ( ct->gd->vbundle != NULL ) {
- for ( i=0; i<ct->gd->vbundle->cnt; i++ ) {
- stem = ct->gd->vbundle->stemlist[i];
- if ((!stem->ldone || !stem->rdone) && stem->master == NULL) {
-
- if (prev != NULL && prev->rightidx != -1 && ct->rp0 != prev->rightidx) {
- ct->pt = pushpoint(ct->pt, prev->rightidx);
- *(ct->pt)++ = SRP0;
- ct->rp0 = prev->rightidx;
- }
- geninstrs(ct, stem, prev, true);
- prev = stem;
- }
- }
- }
-
- /* instruct right sidebearing */
- if (ct->sc->width != 0) {
- if ( ct->gic->fpgm_done && !control_counters ) {
- ct->pt = push2nums(ct->pt, ct->ptcnt+1, 1);
- *(ct->pt)++ = CALL;
- } else {
- /* select rp0 at the right edge of last stem - geninstrs() didn't. */
- /* TODO! after some time, move this to geninstrs(), to save space. */
- if (prev != NULL && prev->rightidx != -1 && ct->rp0 != prev->rightidx) {
- ct->pt = pushpoint(ct->pt, prev->rightidx);
- *(ct->pt)++ = SRP0;
- ct->rp0 = prev->rightidx;
- }
- ct->pt = pushpoint(ct->pt, ct->ptcnt+1);
- *(ct->pt)++ = MDRP_rp0_rnd_white;
- }
- ct->rp0 = ct->ptcnt+1;
- }
-}
-
-/******************************************************************************
- *
- * Everything related with diagonal hinting goes here
- *
- ******************************************************************************/
-
-#define DIAG_MIN_DISTANCE (0.84375)
-
-static int ds_cmp( const void *_s1, const void *_s2 ) {
- StemData * const *s1 = _s1, * const *s2 = _s2;
-
- BasePoint *bp1, *bp2;
- bp1 = (*s1)->unit.y > 0 ? &(*s1)->keypts[0]->base : &(*s1)->keypts[2]->base;
- bp2 = (*s2)->unit.y > 0 ? &(*s2)->keypts[0]->base : &(*s2)->keypts[2]->base;
- if ( bp1->x < bp2->x || ( bp1->x == bp2->x && bp1->y < bp2->y ))
-return( -1 );
- else if ( bp2->x < bp1->x || ( bp2->x == bp1->x && bp2->y < bp1->y ))
-return( 1 );
-
-return( 0 );
-}
-
-/* Takes a line defined by two points and returns a vector decribed as a
- * pair of x and y values, such that the value (x2 + y2) is equal to 1.
- * Note that the BasePoint structure is used to store the vector, although
- * it is not a point itself. This is just because that structure has "x"
- * and "y" fields which can be used for our purpose.
- */
-static BasePoint GetVector ( BasePoint *top,BasePoint *bottom,int orth ) {
- real catx, caty, hyp, temp;
- BasePoint ret;
-
- catx = top->x - bottom->x; caty = top->y - bottom->y;
- hyp = sqrt(( catx*catx ) + ( caty*caty ));
- ret.y = caty/hyp; ret.x = catx/hyp;
-
- if( orth ) {
- temp = ret.x; ret.x = -ret.y; ret.y = temp;
- }
-return( ret );
-}
-
-static int SetDStemKeyPoint( InstrCt *ct,StemData *stem,PointData *pd,int aindex ) {
-
- int nextidx, previdx, cpidx, prev_outer, next_outer, is_start;
- int nsidx, psidx, sidx;
- uint8 flag;
- PointData *ncpd, *pcpd, *cpd, *best = NULL;
- real prevdot, nextdot, cpdist;
-
- if ( pd == NULL )
-return( false );
-
- flag = fabs( stem->unit.y ) > fabs( stem->unit.x ) ? tf_y : tf_x;
- is_start = ( aindex == 0 || aindex == 2 );
- prevdot = ( pd->prevunit.x * stem->unit.x ) +
- ( pd->prevunit.y * stem->unit.y );
- nextdot = ( pd->nextunit.x * stem->unit.x ) +
- ( pd->nextunit.y * stem->unit.y );
- prev_outer = IsStemAssignedToPoint( pd,stem,false ) != -1 &&
- (( is_start && prevdot < 0 ) || ( !is_start && prevdot > 0 ));
- next_outer = IsStemAssignedToPoint( pd,stem,true ) != -1 &&
- (( is_start && nextdot < 0 ) || ( !is_start && nextdot > 0 ));
-
- if ( pd->ttfindex >= ct->gd->realcnt ) {
- nextidx = pd->sp->nextcpindex;
- previdx = pd->sp->prev->from->nextcpindex;
- ncpd = &ct->gd->points[nextidx];
- pcpd = &ct->gd->points[previdx];
- psidx = IsStemAssignedToPoint( pcpd,stem,true );
- nsidx = IsStemAssignedToPoint( ncpd,stem,false );
-
- if ( psidx == -1 && nsidx == -1 )
-return( false );
-
- if ( psidx > -1 && nsidx > -1 )
- best = ( prev_outer ) ? pcpd : ncpd;
- else
- best = ( psidx > -1 ) ? pcpd : ncpd;
-
- } else if (( !pd->sp->nonextcp && next_outer ) || ( !pd->sp->noprevcp && prev_outer )) {
- cpidx = ( prev_outer ) ? pd->sp->prev->from->nextcpindex : pd->sp->nextcpindex;
- cpd = &ct->gd->points[cpidx];
- sidx = IsStemAssignedToPoint( cpd,stem,prev_outer );
-
- if ( sidx != -1 ) {
- cpdist = fabs(( pd->base.x - cpd->base.x ) * stem->unit.x +
- ( pd->base.y - cpd->base.y ) * stem->unit.y );
- if (( cpdist > stem->clen/2 ) ||
- (!(ct->touched[pd->ttfindex] & flag) && !(ct->affected[pd->ttfindex] & flag) &&
- ( ct->touched[cpd->ttfindex] & flag || ct->affected[cpd->ttfindex] & flag )))
- best = cpd;
- }
- if ( best == NULL ) best = pd;
- } else
- best = pd;
-
- stem->keypts[aindex] = best;
-return( true );
-}
-
-static void AssignLineToPoint( DiagPointInfo *diagpts,StemData *stem,int idx,int is_l ) {
- int num, base, i;
- PointData *pd1, *pd2;
-
- num = diagpts[idx].count;
- base = ( is_l ) ? 0 : 2;
- pd1 = stem->keypts[base];
- pd2 = stem->keypts[base+1];
- for ( i=0; i<num; i++ ) {
- if ( diagpts[idx].line[i].pd1 == pd1 && diagpts[idx].line[i].pd2 == pd2 )
-return;
- }
-
- diagpts[idx].line[num].pd1 = stem->keypts[base];
- diagpts[idx].line[num].pd2 = stem->keypts[base+1];
- diagpts[idx].line[num].done = false;
- diagpts[idx].count++;
-return;
-}
-
-/* Convert the existing diagonal stem layout to glyph data, containing
- * information about points assigned to each stem. Then run on stem chunks
- * and associate with each point the line it should be aligned by. Note that
- * we have to do this on a relatively early stage, as it may be important
- * to know, if the given point is subject to the subsequent diagonale hinting,
- * before any actual processing of diagonal stems is started.
- */
-static void InitDStemData( InstrCt *ct ) {
- DiagPointInfo *diagpts = ct->diagpts;
- int i, j, idx, previdx, nextidx, num1, num2, psidx, nsidx, is_l, cnt=0;
- real prevlsp, prevrsp, prevlep, prevrep, lpos, rpos;
- GlyphData *gd;
- StemData *stem;
- PointData *ls, *rs, *le, *re, *tpd, *ppd, *npd;
- struct stem_chunk *chunk;
-
- gd = ct->gd;
-
- for ( i=0; i<gd->stemcnt; i++ ) {
- stem = &gd->stems[i];
- if ( stem->toobig )
- continue;
- if (( stem->unit.y > -.05 && stem->unit.y < .05 ) ||
- ( stem->unit.x > -.05 && stem->unit.x < .05 ))
- continue;
- if ( stem->lpcnt < 2 || stem->rpcnt < 2 )
- continue;
-
- prevlsp = prevrsp = 1e4;
- prevlep = prevrep = -1e4;
- ls = rs = le = re = NULL;
- for ( j=0; j<stem->chunk_cnt; j++ ) {
- chunk = &stem->chunks[j];
- if ( chunk->l != NULL ) {
- lpos = ( chunk->l->base.x - stem->left.x )*stem->unit.x +
- ( chunk->l->base.y - stem->left.y )*stem->unit.y;
- if ( lpos < prevlsp ) {
- ls = chunk->l; prevlsp = lpos;
- }
- if ( lpos > prevlep ) {
- le = chunk->l; prevlep = lpos;
- }
- }
- if ( chunk->r != NULL ) {
- rpos = ( chunk->r->base.x - stem->right.x )*stem->unit.x +
- ( chunk->r->base.y - stem->right.y )*stem->unit.y;
- if ( rpos < prevrsp ) {
- rs = chunk->r; prevrsp = rpos;
- }
- if ( rpos > prevrep ) {
- re = chunk->r; prevrep = rpos;
- }
- }
- }
-
- /* Swap "left" and "right" sides for vectors pointing north-east,
- * so that the "left" side is always determined along the x axis
- * rather than relatively to the vector direction */
- num1 = ( stem->unit.y > 0 ) ? 0 : 2;
- num2 = ( stem->unit.y > 0 ) ? 2 : 0;
- if (!SetDStemKeyPoint( ct,stem,ls,num1 ) || !SetDStemKeyPoint( ct,stem,rs,num2 ))
- continue;
-
- num1 = ( stem->unit.y > 0 ) ? 1 : 3;
- num2 = ( stem->unit.y > 0 ) ? 3 : 1;
- if (!SetDStemKeyPoint( ct,stem,le,num1 ) || !SetDStemKeyPoint( ct,stem,re,num2 ))
- continue;
-
- for ( j=0; j<gd->pcnt; j++ )
- gd->points[j].ticked = false;
- for ( j=0; j<gd->pcnt; j++ ) if ( gd->points[j].sp != NULL ) {
- tpd = &gd->points[j];
- idx = tpd->ttfindex;
- psidx = nsidx = -1;
- if ( idx < gd->realcnt ) {
- if ( !tpd->ticked && diagpts[idx].count < 2 && (
- ( psidx = IsStemAssignedToPoint( tpd,stem,false )) > -1 ||
- ( nsidx = IsStemAssignedToPoint( tpd,stem,true )) > -1)) {
-
- is_l = ( nsidx > -1 ) ? tpd->next_is_l[nsidx] : tpd->prev_is_l[psidx];
- if ( stem->unit.y < 0 ) is_l = !is_l;
- AssignLineToPoint( diagpts,stem,idx,is_l );
- tpd->ticked = true;
- }
- } else {
- previdx = tpd->sp->prev->from->nextcpindex;
- nextidx = tpd->sp->nextcpindex;
- ppd = &gd->points[previdx];
- npd = &gd->points[nextidx];
- if (!ppd->ticked && diagpts[previdx].count < 2 &&
- ( nsidx = IsStemAssignedToPoint( ppd,stem,true )) > -1 ) {
-
- is_l = ppd->next_is_l[nsidx];
- if ( stem->unit.y < 0 ) is_l = !is_l;
- AssignLineToPoint( diagpts,stem,previdx,is_l );
- ppd->ticked = true;
- }
- if (!npd->ticked && diagpts[nextidx].count < 2 &&
- ( psidx = IsStemAssignedToPoint( npd,stem,false )) > -1 ) {
-
- is_l = npd->prev_is_l[psidx];
- if ( stem->unit.y < 0 ) is_l = !is_l;
- AssignLineToPoint( diagpts,stem,nextidx,is_l );
- npd->ticked = true;
- }
- }
- }
- ct->diagstems[cnt++] = stem;
- }
- qsort( ct->diagstems,cnt,sizeof( StemData *),ds_cmp );
- ct->diagcnt = cnt;
-}
-
-/* Usually we have to start doing each diagonal stem from the point which
- * is most touched in any directions.
- */
-static int FindDiagStartPoint( StemData *stem, uint8 *touched ) {
- int i;
-
- for ( i=0; i<4; ++i ) {
- if (( touched[stem->keypts[i]->ttfindex] & tf_x ) &&
- ( touched[stem->keypts[i]->ttfindex] & tf_y ))
-return( i );
- }
-
- for ( i=0; i<4; ++i ) {
- if (( stem->unit.x > stem->unit.y &&
- touched[stem->keypts[i]->ttfindex] & tf_y ) ||
- ( stem->unit.y > stem->unit.x &&
- touched[stem->keypts[i]->ttfindex] & tf_x ))
-return( i );
- }
-
- for ( i=0; i<4; ++i ) {
- if ( touched[stem->keypts[i]->ttfindex] & ( tf_x | tf_y ))
-return( i );
- }
-return( 0 );
-}
-
-/* Check the directions at which the given point still can be moved
- * (i. e. has not yet been touched) and set freedom vector to that
- * direction in case it has not already been set.
- */
-static int SetFreedomVector( uint8 **instrs,int pnum,
- uint8 *touched,DiagPointInfo *diagpts,BasePoint *norm,BasePoint *fv,int pvset,int fpgm_ok ) {
-
- int i, pushpts[3];
- PointData *start=NULL, *end=NULL;
- BasePoint newfv;
-
- if (( touched[pnum] & tf_d ) && !( touched[pnum] & tf_x ) && !( touched[pnum] & tf_y )) {
- for ( i=0 ; i<diagpts[pnum].count ; i++) {
- if ( diagpts[pnum].line[i].done ) {
- start = diagpts[pnum].line[i].pd1;
- end = diagpts[pnum].line[i].pd2;
- }
- }
-
- /* This should never happen */
- if ( start == NULL || end == NULL )
-return( false );
-
- newfv = GetVector( &start->base,&end->base,false );
- if ( !UnitsParallel( fv,&newfv,true )) {
- fv->x = newfv.x; fv->y = newfv.y;
-
- pushpts[0] = start->ttfindex; pushpts[1] = end->ttfindex;
- *instrs = pushpoints( *instrs,2,pushpts );
- *(*instrs)++ = 0x08; /*SFVTL[parallel]*/
- }
-
-return( true );
-
- } else if ( touched[pnum] & tf_x && !(touched[pnum] & tf_d) && !(touched[pnum] & tf_y)) {
- if (!( RealNear( fv->x,0 ) && RealNear( fv->y,1 ))) {
- *(*instrs)++ = 0x04; /*SFVTCA[y-axis]*/
- fv->x = 0; fv->y = 1;
- }
-return( true );
-
- } else if ( touched[pnum] & tf_y && !(touched[pnum] & tf_d) && !(touched[pnum] & tf_x)) {
- if (!( RealNear( fv->x,1 ) && RealNear( fv->y,0 ))) {
- *(*instrs)++ = 0x05; /*SFVTCA[x-axis]*/
- fv->x = 1; fv->y = 0;
- }
-return( true );
- } else if ( !(touched[pnum] & (tf_x|tf_y|tf_d))) {
- if ( !UnitsParallel( fv,norm,true )) {
- fv->x = norm->x; fv->y = norm->y;
-
- if ( pvset )
- *(*instrs)++ = 0x0E; /*SFVTPV*/
- else {
- pushpts[0] = EF2Dot14(norm->x);
- pushpts[1] = EF2Dot14(norm->y);
- if ( fpgm_ok ) {
- pushpts[2] = 21;
- *instrs = pushpoints( *instrs,3,pushpts );
- *(*instrs)++ = CALL; /* aspect-ratio correction */
- } else
- *instrs = pushpoints( *instrs,2,pushpts );
-
- *(*instrs)++ = 0x0b; /* SFVFS */
- }
- }
-return( true );
- }
-return( false );
-}
-
-static int MarkLineFinished( int pnum,int startnum,int endnum,DiagPointInfo *diagpts ) {
- int i;
-
- for ( i=0; i<diagpts[pnum].count; i++ ) {
- if (( diagpts[pnum].line[i].pd1->ttfindex == startnum ) &&
- ( diagpts[pnum].line[i].pd2->ttfindex == endnum )) {
-
- diagpts[pnum].line[i].done = 2;
-return( true );
- }
- }
-return( false );
-}
-
-static uint8 *FixDStemPoint ( InstrCt *ct,StemData *stem,
- int pt,int refpt,int firstedge,int cvt,BasePoint *fv ) {
- uint8 *instrs, *touched;
- DiagPointInfo *diagpts;
-
- diagpts = ct->diagpts;
- touched = ct->touched;
- instrs = ct->pt;
-
- if ( SetFreedomVector( &instrs,pt,touched,diagpts,&stem->l_to_r,fv,true,
- ct->gic->fpgm_done && ct->gic->prep_done )) {
- if ( refpt == -1 ) {
- if (( fv->x == 1 && !( touched[pt] & tf_x )) ||
- ( fv->y == 1 && !( touched[pt] & tf_y ))) {
-
- instrs = pushpoint( instrs,pt );
- *instrs++ = MDAP;
- } else {
- instrs = pushpoint( instrs,pt );
- *instrs++ = SRP0;
- }
- ct->rp0 = pt;
- } else {
- if ( refpt != ct->rp0 ) {
- instrs = pushpoint( instrs,refpt );
- *instrs++ = SRP0;
- ct->rp0 = refpt;
- }
-
- if ( cvt < 0 ) {
- instrs = pushpoint( instrs,pt );
- *instrs++ = MDRP_grey;
- } else {
- instrs = pushpointstem( instrs,pt,cvt );
- *instrs++ = MIRP_rp0_min_black;
- ct->rp0 = pt;
- }
- }
- touched[pt] |= tf_d;
-
- if (!MarkLineFinished( pt,stem->keypts[0]->ttfindex,stem->keypts[1]->ttfindex,diagpts ))
- MarkLineFinished( pt,stem->keypts[2]->ttfindex,stem->keypts[3]->ttfindex,diagpts );
- }
-return( instrs );
-}
-
-static int DStemHasSnappableCorners ( InstrCt *ct,StemData *stem,PointData *pd1,PointData *pd2 ) {
- uint8 *touched = ct->touched;
-
- /* We should be dealing with oncurve points */
- if ( pd1->sp == NULL || pd2->sp == NULL )
-return( false );
-
- /* points should not be lined up vertically or horizontally */
- if (fabs( pd1->base.x - pd2->base.x ) <= ct->gic->fudge ||
- fabs( pd1->base.y - pd2->base.y ) <= ct->gic->fudge )
-return( false );
-
- if (( pd1->x_corner == 1 && !( touched[pd1->ttfindex] & tf_y ) &&
- pd2->y_corner == 1 && !( touched[pd2->ttfindex] & tf_x )) ||
- ( pd1->y_corner == 1 && !( touched[pd1->ttfindex] & tf_x ) &&
- pd2->x_corner == 1 && !( touched[pd2->ttfindex] & tf_y )))
-return( true );
-
-return( false );
-}
-
-static uint8 *SnapDStemCorners ( InstrCt *ct,StemData *stem,PointData *pd1,PointData *pd2,BasePoint *fv ) {
- uint8 *instrs, *touched;
- int xbase, ybase;
-
- instrs = ct->pt;
- touched = ct->touched;
-
- if ( pd1->x_corner && pd2->y_corner ) {
- xbase = pd1->ttfindex; ybase = pd2->ttfindex;
- } else {
- xbase = pd2->ttfindex; ybase = pd1->ttfindex;
- }
-
- *(ct->pt)++ = SVTCA_x;
- ct->pt = push2points( ct->pt,ybase,xbase );
- *(ct->pt)++ = touched[xbase] & tf_x ? MDAP : MDAP_rnd;
- *(ct->pt)++ = MDRP_min_black;
- *(ct->pt)++ = SVTCA_y;
- ct->pt = push2points( ct->pt,xbase,ybase );
- *(ct->pt)++ = touched[ybase] & tf_y ? MDAP : MDAP_rnd;
- *(ct->pt)++ = MDRP_min_black;
-
- touched[xbase] |= ( tf_x | tf_y );
- touched[ybase] |= ( tf_x | tf_y );
- fv->x = 0; fv->y = 1;
-
-return( instrs );
-}
-
-/* A basic algorithm for hinting diagonal stems:
- * -- iterate through diagonal stems, ordered from left to right;
- * -- for each stem, find the most touched point, to start from,
- * and fix that point. TODO: the positioning should be done
- * relatively to points already touched by x or y;
- * -- position the second point on the same edge, using dual projection
- * vector;
- * -- link to the second edge and repeat the same operation.
- *
- * For each point we first determine a direction at which it still can
- * be moved. If a point has already been positioned relatively to another
- * diagonal line, then we move it along that diagonale. Thus this algorithm
- * can handle things like "V" where one line's ending point is another
- * line's starting point without special exceptions.
- */
-static uint8 *FixDstem( InstrCt *ct, StemData *ds, BasePoint *fv ) {
- int startnum, a1, a2, b1, b2, firstedge, cvt;
- int x_ldup, y_ldup, x_edup, y_edup, dsc1, dsc2;
- PointData *v1, *v2;
- uint8 *touched;
- int pushpts[3];
-
- if ( ds->ldone && ds->rdone )
-return( ct->pt );
-
- touched = ct->touched;
-
- dsc1 = DStemHasSnappableCorners( ct,ds,ds->keypts[0],ds->keypts[2] );
- dsc2 = DStemHasSnappableCorners( ct,ds,ds->keypts[1],ds->keypts[3] );
-
- if ( dsc1 || dsc2 ) {
- ct->pt = pushF26Dot6( ct->pt,.59662 );
- *(ct->pt)++ = SMD;
-
- if ( dsc1 )
- SnapDStemCorners( ct,ds,ds->keypts[0],ds->keypts[2],fv );
- if ( dsc2 )
- SnapDStemCorners( ct,ds,ds->keypts[1],ds->keypts[3],fv );
-
- ct->pt = pushF26Dot6( ct->pt,DIAG_MIN_DISTANCE );
- *(ct->pt)++ = SMD;
- }
-
- if ( !dsc1 || !dsc2 ) {
- startnum = FindDiagStartPoint( ds,touched );
- a1 = ds->keypts[startnum]->ttfindex;
- if (( startnum == 0 ) || ( startnum == 1 )) {
- firstedge = true;
- v1 = ds->keypts[0]; v2 = ds->keypts[1];
- a2 = ( startnum == 1 ) ? ds->keypts[0]->ttfindex : ds->keypts[1]->ttfindex;
- b1 = ( startnum == 1 ) ? ds->keypts[3]->ttfindex : ds->keypts[2]->ttfindex;
- b2 = ( startnum == 1 ) ? ds->keypts[2]->ttfindex : ds->keypts[3]->ttfindex;
- } else {
- firstedge = false;
- v1 = ds->keypts[2]; v2 = ds->keypts[3];
- a2 = ( startnum == 3 ) ? ds->keypts[2]->ttfindex : ds->keypts[3]->ttfindex;
- b1 = ( startnum == 3 ) ? ds->keypts[1]->ttfindex : ds->keypts[0]->ttfindex;
- b2 = ( startnum == 3 ) ? ds->keypts[0]->ttfindex : ds->keypts[1]->ttfindex;
- }
-
- /* Always put the calculated stem width into the CVT table, unless it is
- * already there. This approach would be wrong for vertical or horizontal
- * stems, but for diagonales it is just unlikely that we can find an
- * acceptable predefined value in StemSnapH or StemSnapV
- */
- cvt = TTF_getcvtval( ct->gic->sf,ds->width );
-
- pushpts[0] = EF2Dot14(ds->l_to_r.x);
- pushpts[1] = EF2Dot14(ds->l_to_r.y);
- if ( ct->gic->fpgm_done && ct->gic->prep_done ) {
- pushpts[2] = 21;
- ct->pt = pushnums( ct->pt, 3, pushpts );
- *(ct->pt)++ = CALL; /* Aspect ratio correction */
- } else
- ct->pt = pushnums( ct->pt, 2, pushpts );
- *(ct->pt)++ = 0x0A; /* SPVFS */
-
- pushpts[0] = v1->ttfindex; pushpts[1] = v2->ttfindex;
-
- x_ldup =( touched[a1] & tf_x && touched[a2] & tf_x ) ||
- ( touched[b1] & tf_x && touched[b2] & tf_x );
- y_ldup =( touched[a1] & tf_y && touched[a2] & tf_y ) ||
- ( touched[b1] & tf_y && touched[b2] & tf_y );
- x_edup =( touched[a1] & tf_x && touched[b1] & tf_x ) ||
- ( touched[a2] & tf_x && touched[b2] & tf_x );
- y_edup =( touched[a1] & tf_y && touched[b1] & tf_y ) ||
- ( touched[a2] & tf_y && touched[b2] & tf_y );
-
- if (( x_ldup && !y_edup ) || ( y_ldup && !x_edup)) {
-
- ct->pt = FixDStemPoint ( ct,ds,a1,-1,firstedge,-1,fv );
- ct->pt = FixDStemPoint ( ct,ds,b2,-1,firstedge,-1,fv );
- ct->pt = FixDStemPoint ( ct,ds,b1,a1,firstedge,cvt,fv );
- ct->pt = FixDStemPoint ( ct,ds,a2,b2,firstedge,cvt,fv );
- } else {
- ct->pt = FixDStemPoint ( ct,ds,a1,-1,firstedge,-1,fv );
- ct->pt = FixDStemPoint ( ct,ds,a2,a1,firstedge,-1,fv );
- ct->pt = FixDStemPoint ( ct,ds,b1,a1,firstedge,cvt,fv );
- ct->pt = FixDStemPoint ( ct,ds,b2,b1,firstedge,-1,fv );
- }
- }
-
- ds->ldone = ds->rdone = true;
-return( ct->pt );
-}
-
-static uint8 *FixPointOnLine ( DiagPointInfo *diagpts,PointVector *line,
- PointData *pd,InstrCt *ct,BasePoint *fv,BasePoint *pv,int *rp1,int *rp2 ) {
-
- uint8 *instrs, *touched;
- BasePoint newpv;
- int pushpts[4];
-
- touched = ct->touched;
- instrs = ct->pt;
-
- newpv = GetVector( &line->pd1->base,&line->pd2->base,true );
-
- if ( SetFreedomVector( &instrs,pd->ttfindex,touched,diagpts,&newpv,fv,false,
- ct->gic->fpgm_done && ct->gic->prep_done )) {
- if ( ct->rp0 != line->pd1->ttfindex ) {
- instrs = pushpoint( instrs,line->pd1->ttfindex );
- *instrs++ = SRP0;
- ct->rp0 = line->pd1->ttfindex;
- }
- if ( ct->gic->fpgm_done ) {
- pv->x = newpv.x; pv->y = newpv.y;
-
- pushpts[0] = pd->ttfindex;
- pushpts[1] = line->pd1->ttfindex;
- pushpts[2] = line->pd2->ttfindex;
- pushpts[3] = 19;
- instrs = pushpoints( instrs,4,pushpts );
- *instrs++ = CALL;
- } else {
- if ( !UnitsParallel( pv,&newpv,true )) {
- pv->x = newpv.x; pv->y = newpv.y;
-
- pushpts[0] = line->pd1->ttfindex; pushpts[1] = line->pd2->ttfindex;
- instrs = pushpoints( instrs,2,pushpts );
- *instrs++ = 0x07; /*SPVTL[orthogonal]*/
- }
-
- instrs = pushpoint( instrs,pd->ttfindex );
- *instrs++ = MDRP_grey;
- }
- }
-return( instrs );
-}
-
-/* If a point has to be positioned just relatively to the diagonal
- * line (no intersections, no need to maintain other directions),
- * then we can interpolate it along that line. This usually produces
- * better results for things like a Danish slashed "O".
- */
-static uint8 *InterpolateAlongDiag ( DiagPointInfo *diagpts,PointVector *line,
- PointData *pd,InstrCt *ct,BasePoint *fv,BasePoint *pv,int *rp1,int *rp2 ) {
-
- uint8 *instrs, *touched;
- BasePoint newpv;
- int pushpts[3];
-
- touched = ct->touched;
- instrs = ct->pt;
-
- if (diagpts[pd->ttfindex].count != 1 || touched[pd->ttfindex] & ( tf_x|tf_y ) ||
- diagpts[pd->ttfindex].line[0].done > 1 )
-return( instrs );
-
- newpv = GetVector( &line->pd1->base,&line->pd2->base,false );
-
- if ( !UnitsParallel( pv,&newpv,false ) ||
- *rp1 != line->pd1->ttfindex || *rp2 != line->pd1->ttfindex ) {
-
- pushpts[0] = pd->ttfindex;
- pushpts[1] = line->pd1->ttfindex;
- pushpts[2] = line->pd2->ttfindex;
- instrs = pushpoints( instrs,3,pushpts );
- } else
- instrs = pushpoint ( instrs,pd->ttfindex );
-
- if ( !UnitsParallel( pv,&newpv,true )) {
- pv->x = newpv.x; pv->y = newpv.y;
-
- if ( *rp1 != line->pd1->ttfindex || *rp2 != line->pd1->ttfindex ) {
- *instrs++ = DUP;
- *instrs++ = 0x8a; /* ROLL */
- *instrs++ = DUP;
- *instrs++ = 0x8a; /* ROLL */
- *instrs++ = 0x23; /* SWAP */
- }
- *instrs++ = 0x06; /* SPVTL[parallel] */
- }
-
- if ( !UnitsParallel( fv,&newpv,true )) {
- *instrs++ = 0x0E; /* SFVTPV */
- fv->x = newpv.x; fv->y = newpv.y;
- }
- if ( *rp1 != line->pd1->ttfindex || *rp2 != line->pd1->ttfindex ) {
- *rp1 = line->pd1->ttfindex;
- *rp2 = line->pd1->ttfindex;
-
- *instrs++ = SRP1;
- *instrs++ = SRP2;
- }
- *instrs++ = IP;
- touched[pd->ttfindex] |= tf_d;
- diagpts[pd->ttfindex].line[0].done = 2;
-return( instrs );
-}
-
-/* When all stem edges have already been positioned, run through other
- * points which are known to be related with some diagonales and position
- * them too. This may include both intersections and points which just
- * lie on a diagonal line. This function does not care about starting/ending
- * points of stems, unless they should be additionally positioned relatively
- * to another stem. Thus is can handle things like "X" or "K".
- */
-static uint8 *MovePointsToIntersections( InstrCt *ct,BasePoint *fv ) {
-
- int i, j, ptcnt, rp1=-1, rp2=-1;
- uint8 *touched;
- BasePoint pv;
- PointData *curpd, *npd, *ppd;
- DiagPointInfo *diagpts;
- StemData *ds;
-
- touched = ct->touched;
- ptcnt = ct->gd->realcnt;
- diagpts = ct->diagpts;
- pv.x = 1; pv.y = 0;
-
- for ( i=0; i<ptcnt; i++ ) {
- if ( diagpts[i].count > 0 ) {
- for ( j=0; j<diagpts[i].count; j++ ) {
- if ( !diagpts[i].line[j].done ) {
- curpd = &ct->gd->points[i];
-
- ct->pt = FixPointOnLine( diagpts,&diagpts[i].line[j],
- curpd,ct,fv,&pv,&rp1,&rp2 );
-
- diagpts[i].line[j].done = true;
- touched[i] |= tf_d;
- }
- }
- }
- }
-
- /* Second pass to interpolate points lying on diagonal lines (but not
- * starting/ending stem points) along those lines. This operation, unlike
- * moving points to diagonals, requires vectors to be set parallel to lines,
- * and this is the reason for which it is done in a separate cycle
- */
- for ( i=0; i<ct->diagcnt; i++ ) {
- ds = ct->diagstems[i];
- if ( ds->ldone ) {
- for ( j=0; j<ds->chunk_cnt; j++ ) if (( curpd = ds->chunks[j].l ) != NULL ) {
- if ( curpd->ttfindex < ct->ptcnt ) {
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[curpd->ttfindex].line[0],
- curpd,ct,fv,&pv,&rp1,&rp2 );
- } else {
- ppd = &ct->gd->points[curpd->sp->prev->from->nextcpindex];
- npd = &ct->gd->points[curpd->sp->nextcpindex];
- if ( IsStemAssignedToPoint(ppd, ds, true) != -1 )
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[ppd->ttfindex].line[0],
- ppd,ct,fv,&pv,&rp1,&rp2 );
- if ( IsStemAssignedToPoint(npd, ds, false) != -1 )
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[npd->ttfindex].line[0],
- npd,ct,fv,&pv,&rp1,&rp2 );
- }
- }
- }
- if ( ds->rdone ) {
- for ( j=0; j<ds->chunk_cnt; j++ ) if (( curpd = ds->chunks[j].r ) != NULL ) {
- if ( curpd->ttfindex < ct->ptcnt ) {
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[curpd->ttfindex].line[0],
- curpd,ct,fv,&pv,&rp1,&rp2 );
- } else {
- ppd = &ct->gd->points[curpd->sp->prev->from->nextcpindex];
- npd = &ct->gd->points[curpd->sp->nextcpindex];
- if ( IsStemAssignedToPoint(ppd, ds, true) != -1 )
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[ppd->ttfindex].line[0],
- ppd,ct,fv,&pv,&rp1,&rp2 );
- if ( IsStemAssignedToPoint(npd, ds, false) != -1 )
- ct->pt = InterpolateAlongDiag ( diagpts,&diagpts[npd->ttfindex].line[0],
- npd,ct,fv,&pv,&rp1,&rp2 );
- }
- }
- }
- }
-return( ct->pt );
-}
-
-static void TouchControlPoint( InstrCt *ct,PointData *pd,
- int next,int *tobefixedy,int *tobefixedx,int *numx,int *numy ) {
-
- int idx, cpidx;
- PointData *cpd;
- uint8 *touched = ct->touched;
-
- idx = pd->ttfindex;
- cpidx = next ? pd->sp->nextcpindex : pd->sp->prev->from->nextcpindex;
- cpd = &ct->gd->points[cpidx];
-
- if ( has_valid_dstem( cpd, !next ) != -1 ) {
- /* if this control point is used to describe an implied spline
- * point, then it is instructed as if it was an oncurve point */
- if ( idx == 0xffff && touched[cpidx] & tf_d ) {
- if (!( touched[cpidx] & tf_y )) {
- tobefixedy[(*numy)++] = cpidx;
- touched[cpidx] |= tf_y;
- }
-
- if (!( touched[cpidx] & tf_x )) {
- tobefixedx[(*numx)++] = cpidx;
- touched[cpidx] |= tf_x;
- }
- /* otherwise we just mark it as affected to prevent undesired
- * interpolations */
- } else if ( idx < ct->gd->realcnt && touched[idx] & tf_d ) {
- ct->affected[cpidx] |= tf_x;
- ct->affected[cpidx] |= tf_y;
- }
- }
-}
-
-/* Finally explicitly touch all affected points by X and Y (unless they
- * have already been), so that subsequent IUP's can't distort our stems.
- */
-static uint8 *TouchDStemPoints( InstrCt *ct,BasePoint *fv ) {
-
- int i, ptcnt, numx=0, numy=0, idx;
- int *tobefixedy, *tobefixedx;
- uint8 *instrs, *touched;
- PointData *pd;
-
- touched = ct->touched;
- instrs = ct->pt;
- ptcnt = ct->gd->pcnt;
-
- tobefixedy = calloc( ptcnt,sizeof( int ));
- tobefixedx = calloc( ptcnt,sizeof( int ));
-
- /* Ensure that the projection vector is no longer set to a diagonal line */
- if ( fv->x == 1 && fv->y == 0 )
- *instrs++ = 0x03; /* SPVTCA[x] */
- else if ( fv->x == 0 && fv->y == 1 )
- *instrs++ = 0x02; /* SPVTCA[y] */
-
- for ( i=0; i<ptcnt; i++ ) if ( ct->gd->points[i].sp != NULL ) {
- pd = &ct->gd->points[i];
- if (( has_valid_dstem( pd,false )) != -1 ||
- ( has_valid_dstem( pd,true )) != -1 ) {
-
- idx = pd->ttfindex;
- if ( idx < ct->gd->realcnt && touched[idx] & tf_d ) {
- if (!( touched[idx] & tf_y )) {
- tobefixedy[numy++] = idx;
- touched[idx] |= tf_y;
- }
-
- if (!( touched[idx] & tf_x )) {
- tobefixedx[numx++] = idx;
- touched[idx] |= tf_x;
- }
- }
- if ( !pd->sp->noprevcp )
- TouchControlPoint( ct,pd,false,tobefixedy,tobefixedx,&numx,&numy );
- if ( !pd->sp->nonextcp )
- TouchControlPoint( ct,pd,true,tobefixedy,tobefixedx,&numx,&numy );
- }
- }
-
- if ( numy>0 ) {
- if ( !(fv->x == 0 && fv->y == 1) ) *instrs++ = SVTCA_y;
- instrs = instructpoints ( instrs,numy,tobefixedy,MDAP );
- }
-
- if ( numx>0 ) {
- if ( !(fv->x == 1 && fv->y == 0) || numy > 0 ) *instrs++ = SVTCA_x;
- instrs = instructpoints ( instrs,numx,tobefixedx,MDAP );
- }
-
- if ( numx == 0 && numy == 0 ) *instrs++ = SVTCA_x;
-
- free( tobefixedy );
- free( tobefixedx );
-return( instrs );
-}
-
-static void DStemInfoGeninst( InstrCt *ct ) {
- BasePoint fv;
- int i;
-
- if (ct->diagcnt == 0)
-return;
-
- fv.x = 1; fv.y = 0;
-
- ct->pt = pushF26Dot6( ct->pt,DIAG_MIN_DISTANCE );
- *(ct->pt)++ = SMD; /* Set Minimum Distance */
-
- for ( i=0; i<ct->diagcnt; i++ )
- ct->pt = FixDstem ( ct,ct->diagstems[i],&fv );
-
- ct->pt = MovePointsToIntersections( ct,&fv );
- ct->pt = TouchDStemPoints ( ct,&fv);
-
- ct->pt = pushF26Dot6( ct->pt,1.0 );
- *(ct->pt)++ = SMD; /* Set Minimum Distance */
-
- ct->xdir = fv.x;
-}
-
-/******************************************************************************
- *
- * Strong point interpolation
- *
- * TODO! Better optimization, if possible.
- * TODO! leftmost and righmost bounds, if not already controlled by stems.
- *
- ******************************************************************************/
-
-/* To be used with qsort() - sorts edge array in ascending order. */
-struct stemedge {
- int refpt;
- double pos;
-};
-
-/* To be used with qsort() - sorts edge array in ascending order. */
-static int sortedges(const void *_e1, const void *_e2) {
- const struct stemedge *e1 = _e1, *e2 = _e2;
- return ( e1->pos > e2->pos );
-}
-
-static int AddEdge(InstrCt *ct, StemData *stem, int is_l, struct stemedge *edgelist, int cnt) {
- real coord;
- int i, skip, refidx;
-
- if (!stem->ghost ||
- (is_l && stem->width == 20) || (!is_l && stem->width == 21)) {
-
- coord = is_l ?
- ((real *) &stem->left.x)[!ct->xdir] : ((real *) &stem->right.x)[!ct->xdir];
- refidx = is_l ? stem->leftidx : stem->rightidx;
- for (i=skip=0; i<cnt; i++)
- if (abs(coord - edgelist[i].pos) <= ct->gic->fudge ||
- edgelist[i].refpt == refidx) {
- skip=1;
- break;
- }
- if (!skip && refidx != -1) {
- edgelist[cnt ].refpt = refidx;
- edgelist[cnt++].pos = coord;
- }
- }
- return( cnt );
-}
-
-/* Optional feature: tries to maintain relative position of some important
- * points between stems' edges, so that glyph's shape is mostly preserved
- * when strongly gridfitted. This in terms of FreeType is called 'Strong Point
- * Interpolation'. It now does more or else what it should, but generates large
- * and sometimes incomplete code - see 'todos' above, and optimize_strongpts().
- * Note: it would affect diagonals if done before instructing them.
- *
- * TODO: it now intrpolates strong points only between hints' edges.
- * What about between leftmost/rightmost edge and leftmost/rightmost
- * glyph extents, if they protrude beyond the edges?
- */
-static void InterpolateStrongPoints(InstrCt *ct) {
- StemBundle *bundle;
- StemData *stem;
- uint8 touchflag = ct->xdir?tf_x:tf_y;
- real fudge;
- struct stemedge edgelist[192];
- int edgecnt=0, i, j;
- int lpoint = -1, ledge=0;
- int rpoint = -1;
- int nowrp1 = 1;
- int ldone = 0;
-
- bundle = ( ct->xdir ) ? ct->gd->vbundle : ct->gd->hbundle;
- if (bundle == NULL || bundle->cnt == 0)
- return;
-
- /* List all stem edges. List only active edges for ghost hints. */
- for(i=0; i<bundle->cnt; i++) {
- stem = bundle->stemlist[i];
-
- edgecnt = AddEdge(ct, stem, ct->xdir, edgelist, edgecnt);
- edgecnt = AddEdge(ct, stem, !ct->xdir, edgelist, edgecnt);
- }
-
- if (edgecnt < 2)
-return;
-
- qsort(edgelist, edgecnt, sizeof(struct stemedge), sortedges);
-
- /* Interpolate important points between subsequent edges */
- for (i=0; i<edgecnt; i++) {
- rpoint = edgelist[i].refpt;
- if (rpoint == -1 || !(ct->touched[rpoint] & touchflag)) continue;
-
- if (lpoint==-1) {
- /* first edge */
- lpoint = rpoint;
- ledge = i;
- }
- else {
- fudge = ct->gic->fudge;
- ct->gic->fudge = (edgelist[i].pos-edgelist[ledge].pos)/2;
- init_edge(ct, (edgelist[i].pos+edgelist[ledge].pos)/2, ALL_CONTOURS);
- optimize_strongpts(ct); /* Special way is needed here. */
- ct->gic->fudge = fudge;
-
- if (!ct->edge.othercnt) {
- nowrp1 = 1;
- lpoint = rpoint;
- ledge = i;
- ldone = 0;
- }
- else if (ct->edge.refscore) {
- if (!ldone) {
- ct->pt = push2points(ct->pt, rpoint, lpoint);
- *ct->pt++ = SRP1;
- *ct->pt++ = SRP2;
- }
- else {
- ct->pt = pushpoint(ct->pt, rpoint);
- if (nowrp1) *ct->pt++ = SRP1;
- else *ct->pt++ = SRP2;
- nowrp1 = !nowrp1;
- }
-
- lpoint = rpoint;
- ledge = i;
- ldone = 1;
-
- /* instruct points */
- ct->pt = instructpoints(ct->pt, ct->edge.othercnt,
- ct->edge.others, IP);
- for (j=0; j<ct->edge.othercnt; j++)
- ct->touched[ct->edge.others[j]] |= touchflag;
- }
-
- if (ct->edge.othercnt) {
- free(ct->edge.others);
- ct->edge.othercnt = 0;
- }
- }
- }
-}
-
-/******************************************************************************
- *
- * Generate instructions for a glyph.
- *
- ******************************************************************************/
-
-static uint8 *dogeninstructions(InstrCt *ct) {
- StemData *stem;
- int max, i;
- DStemInfo *dstem;
- BlueData nbd;
-
- /* Fill a temporary BlueData structure basing on the data stored in the global
- * instruction context. This is needed for GlyphDataBuild(), as it accepts
- * blue data only in this format
- */
- for ( i=0; i<ct->gic->bluecnt; i++ ) {
- if ( ct->gic->blues[i].base < ct->gic->blues[i].overshoot ) {
- nbd.blues[i][0] = ct->gic->blues[i].base;
- nbd.blues[i][1] = ct->gic->blues[i].overshoot;
- } else {
- nbd.blues[i][0] = ct->gic->blues[i].overshoot;
- nbd.blues[i][1] = ct->gic->blues[i].base;
- }
- }
- nbd.bluecnt = ct->gic->bluecnt;
- ct->gd = GlyphDataBuild( ct->sc,ct->gic->layer,&nbd,instruct_diagonal_stems );
-
- /* Maximum instruction length is 6 bytes for each point in each dimension
- * 2 extra bytes to finish up. And one byte to switch from x to y axis
- * Diagonal take more space because we need to set the orientation on
- * each stem, and worry about intersections, etc.
- * That should be an over-estimate
- */
- max=2;
- if ( ct->gd->hbundle!=NULL ) max += ct->ptcnt*8;
- if ( ct->gd->vbundle!=NULL ) max += ct->ptcnt*8+4;
- for ( dstem=ct->sc->dstem; dstem!=NULL; max+=7+4*6+100, dstem=dstem->next );
- if ( ct->sc->md!=NULL ) max += ct->ptcnt*12;
- max += ct->ptcnt*6; /* in case there are any rounds */
- max += ct->ptcnt*6; /* paranoia */
- ct->instrs = ct->pt = malloc(max);
-
- /* Initially no stem hints are done */
- if ( ct->gd->hbundle!=NULL ) {
- for ( i=0; i<ct->gd->hbundle->cnt; i++ ) {
- stem = ct->gd->hbundle->stemlist[i];
- stem->ldone = stem->rdone = false;
- }
- }
- if ( ct->gd->vbundle!=NULL ) {
- for ( i=0; i<ct->gd->vbundle->cnt; i++ ) {
- stem = ct->gd->vbundle->stemlist[i];
- stem->ldone = stem->rdone = false;
- }
- }
-
- if ( instruct_diagonal_stems ) {
- /* Prepare info about diagonal stems to be used during edge optimization. */
- /* These contents need to be explicitly freed after hinting diagonals. */
- ct->diagstems = calloc(ct->gd->stemcnt, sizeof(StemData *));
- ct->diagpts = calloc(ct->ptcnt, sizeof(struct diagpointinfo));
- InitDStemData(ct);
- }
-
- /* We start from instructing horizontal features (=> movement in y)
- * Do this first so that the diagonal hinter will have everything moved
- * properly when it sets the projection vector
- * Even if we aren't doing the diagonals, we do the blues.
- */
- ct->xdir = false;
- *(ct->pt)++ = SVTCA_y;
- snap_to_blues(ct);
- HStemGeninst(ct);
-
- /* Next instruct vertical features (=> movement in x). */
- ct->xdir = true;
- *(ct->pt)++ = SVTCA_x;
- VStemGeninst(ct);
-
- /* Then instruct diagonal stems (=> movement in x)
- * This is done after vertical stems because it involves
- * moving some points out-of their vertical stems.
- */
- if (instruct_diagonal_stems && ct->diagcnt > 0) DStemInfoGeninst(ct);
-
- if ( interpolate_strong ) {
- /* Adjust important points between hint edges. */
- if (ct->xdir == false) *(ct->pt)++ = SVTCA_x;
- ct->xdir = true;
- InterpolateStrongPoints(ct);
- ct->xdir = false;
- *(ct->pt)++ = SVTCA_y;
- InterpolateStrongPoints(ct);
- }
-
- /* Interpolate untouched points */
- *(ct->pt)++ = IUP_y;
- *(ct->pt)++ = IUP_x;
-
- if ((ct->pt)-(ct->instrs) > max) IError(
- "We're about to crash.\n"
- "We miscalculated the glyph's instruction set length\n"
- "When processing TTF instructions (hinting) of %s", ct->sc->name
- );
-
- if ( instruct_diagonal_stems ) {
- free(ct->diagstems);
- free(ct->diagpts);
- }
- GlyphDataFree( ct->gd );
-
- ct->sc->ttf_instrs_len = (ct->pt)-(ct->instrs);
- ct->sc->instructions_out_of_date = false;
-return ct->sc->ttf_instrs = realloc(ct->instrs,(ct->pt)-(ct->instrs));
-}
-
-void NowakowskiSCAutoInstr(GlobalInstrCt *gic, SplineChar *sc) {
- int cnt, contourcnt;
- BasePoint *bp;
- int *contourends;
- uint8 *clockwise;
- uint8 *touched;
- uint8 *affected;
- SplineSet *ss;
- RefChar *ref;
- InstrCt ct;
- int i;
-
- if ( !sc->layers[gic->layer].order2 )
-return;
-
- if ( sc->layers[gic->layer].refs!=NULL && sc->layers[gic->layer].splines!=NULL ) {
- ff_post_error(_("Can't instruct this glyph"),
- _("TrueType does not support mixed references and contours.\nIf you want instructions for %.30s you should either:\n * Unlink the reference(s)\n * Copy the inline contours into their own (unencoded\n glyph) and make a reference to that."),
- sc->name );
-return;
- }
- for ( ref = sc->layers[gic->layer].refs; ref!=NULL; ref=ref->next ) {
- if ( ref->transform[0]>=2 || ref->transform[0]<-2 ||
- ref->transform[1]>=2 || ref->transform[1]<-2 ||
- ref->transform[2]>=2 || ref->transform[2]<-2 ||
- ref->transform[3]>=2 || ref->transform[3]<-2 )
- break;
- }
- if ( ref!=NULL ) {
- ff_post_error(_("Can't instruct this glyph"),
- _("TrueType does not support references which\nare scaled by more than 200%%. But %1$.30s\nhas been in %2$.30s. Any instructions\nadded would be meaningless."),
- ref->sc->name, sc->name );
-return;
- }
-
- if ( sc->ttf_instrs ) {
- free(sc->ttf_instrs);
- sc->ttf_instrs = NULL;
- sc->ttf_instrs_len = 0;
- }
- SCNumberPoints(sc,gic->layer);
- if ( autohint_before_generate && sc->changedsincelasthinted &&
- !sc->manualhints )
- SplineCharAutoHint(sc,gic->layer,NULL);
-
- if ( sc->vstem==NULL && sc->hstem==NULL && sc->dstem==NULL && sc->md==NULL)
-return;
-
- /* TODO!
- *
- * We're having problems with references utilizing 'use my metrics' that are
- * rotated or flipped horizontally. Basically, such glyphs can get negative
- * width and behave strangely when the glyph referred is instructed. Such
- * widths are treated very differently under Freetype (OK) and Windows
- * (terribly shifted), and I suppose other rasterizers can also complain.
- * Perhaps we should advise turning 'use my metrics' off.
- */
-
- if ( sc->layers[gic->layer].splines==NULL )
-return;
-
- /* Start dealing with the glyph */
- contourcnt = 0;
- for ( ss=sc->layers[gic->layer].splines; ss!=NULL; ss=ss->next, ++contourcnt );
- cnt = SSTtfNumberPoints(sc->layers[gic->layer].splines);
-
- contourends = malloc((contourcnt+1)*sizeof(int));
- clockwise = calloc(contourcnt,1);
- bp = malloc(cnt*sizeof(BasePoint));
- touched = calloc(cnt,1);
- affected = calloc(cnt,1);
-
- contourcnt = cnt = 0;
- for ( ss=sc->layers[gic->layer].splines; ss!=NULL; ss=ss->next ) {
- touched[cnt] |= tf_startcontour;
- cnt = SSAddPoints(ss,cnt,bp,NULL);
- touched[cnt-1] |= tf_endcontour;
- contourends[contourcnt] = cnt-1;
- clockwise[contourcnt++] = SplinePointListIsClockwise(ss);
- }
- contourends[contourcnt] = 0;
-
- for (i=0; i<gic->bluecnt; i++)
- gic->blues[i].highest = gic->blues[i].lowest = -1;
-
- ct.gic = gic;
-
- ct.sc = sc;
- ct.ss = sc->layers[gic->layer].splines;
- ct.instrs = NULL;
- ct.pt = NULL;
- ct.ptcnt = cnt;
- ct.contourends = contourends;
- ct.clockwise = clockwise;
- ct.bp = bp;
- ct.touched = touched;
- ct.affected = affected;
- ct.diagstems = NULL;
- ct.diagpts = NULL;
-
- ct.rp0 = 0;
-
- dogeninstructions(&ct);
-
- free(touched);
- free(affected);
- free(bp);
- free(contourends);
- free(clockwise);
-}