Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
1836 lines (1682 sloc) 55.3 KB
/* Copyright (C) 2000-2012 by George Williams */
/*
* 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 "fontforge.h"
#include <stdio.h>
#include <string.h>
#include <ustring.h>
#include <math.h>
#include "gdraw.h"
#include "splinefont.h"
#include "edgelist.h"
Color default_background = 0xffffff; /* white */
static void HintsFree(Hints *h) {
Hints *hnext;
for ( ; h!=NULL; h = hnext ) {
hnext = h->next;
free(h);
}
}
static void _FreeEdgeList(EdgeList *es) {
int i;
/* edges will be NULL if the user tries to make an enormous bitmap */
/* if the linear size is bigger than several thousand, we just */
/* ignore the request */
if ( es->edges!=NULL ) {
for ( i=0; i<es->cnt; ++i ) {
Edge *e, *next;
for ( e = es->edges[i]; e!=NULL; e = next ) {
next = e->esnext;
free(e);
}
es->edges[i] = NULL;
}
}
}
void FreeEdges(EdgeList *es) {
_FreeEdgeList(es);
free(es->edges);
free(es->interesting);
HintsFree(es->hhints);
HintsFree(es->vhints);
}
bigreal TOfNextMajor(Edge *e, EdgeList *es, bigreal sought_m ) {
/* We want to find t so that Mspline(t) = sought_m */
/* the curve is monotonic */
Spline1D *msp = &e->spline->splines[es->major];
bigreal new_t;
if ( es->is_overlap ) {
/* if we've adjusted the height then we won't be able to find it restricting */
/* t between [0,1] as we do. So it's a special case. (this is to handle */
/* hstem hints) */
if ( e->max_adjusted && sought_m==e->mmax ) {
e->m_cur = sought_m;
return( e->up?1.0:0.0 );
}
new_t = IterateSplineSolve(msp,e->t_mmin,e->t_mmax,(sought_m+es->mmin)/es->scale);
if ( new_t==-1 )
IError( "No Solution");
e->m_cur = (((msp->a*new_t + msp->b)*new_t+msp->c)*new_t + msp->d)*es->scale - es->mmin;
return( new_t );
} else {
Spline *sp = e->spline;
if ( sp->islinear ) {
new_t = e->t_cur + (sought_m-e->m_cur)/(es->scale * msp->c);
e->m_cur = (msp->c*new_t + msp->d)*es->scale - es->mmin;
return( new_t );
}
/* if we have a spline that is nearly horizontal at its max. endpoint */
/* then finding A value of t for which y has the right value isn't good */
/* enough (at least not when finding intersections) */
if ( sought_m+1>e->mmax ) {
e->m_cur = e->mmax;
return( e->t_mmax );
}
/* if we've adjusted the height then we won't be able to find it restricting */
/* t between [0,1] as we do. So it's a special case. (this is to handle */
/* hstem hints) */
if ( e->max_adjusted && sought_m==e->mmax ) {
e->m_cur = sought_m;
return( e->up?1.0:0.0 );
}
new_t = IterateSplineSolve(msp,e->t_mmin,e->t_mmax,(sought_m+es->mmin)/es->scale);
if ( new_t==-1 )
IError( "No Solution");
e->m_cur = (((msp->a*new_t + msp->b)*new_t+msp->c)*new_t + msp->d)*es->scale - es->mmin;
return( new_t );
}
}
static int SlopeLess(Edge *e, Edge *p, int other) {
Spline1D *osp = &e->spline->splines[other];
Spline1D *psp = &p->spline->splines[other];
Spline1D *msp = &e->spline->splines[!other];
Spline1D *qsp = &p->spline->splines[!other];
real os = (3*osp->a*e->t_cur+2*osp->b)*e->t_cur+osp->c,
ps = (3*psp->a*p->t_cur+2*psp->b)*p->t_cur+psp->c;
real ms = (3*msp->a*e->t_cur+2*msp->b)*e->t_cur+msp->c,
qs = (3*qsp->a*p->t_cur+2*qsp->b)*p->t_cur+qsp->c;
if ( ms<.0001 && ms>-.0001 ) ms = 0;
if ( qs<.0001 && qs>-.0001 ) qs = 0;
if ( qs==0 ) {
if ( p->t_cur==1 ) {
qs = (3*qsp->a*.9999+2*qsp->b)*.9999+qsp->c;
ps = (3*psp->a*.9999+2*psp->b)*.9999+psp->c;
} else {
qs = (3*qsp->a*(p->t_cur+.0001)+2*qsp->b)*(p->t_cur+.0001)+qsp->c;
ps = (3*psp->a*(p->t_cur+.0001)+2*psp->b)*(p->t_cur+.0001)+psp->c;
}
}
if ( ms==0 ) {
if ( e->t_cur==1 ) {
ms = (3*msp->a*.9999+2*msp->b)*.9999+msp->c;
os = (3*osp->a*.9999+2*osp->b)*.9999+osp->c;
} else {
ms = (3*msp->a*(e->t_cur+.0001)+2*msp->b)*(e->t_cur+.0001)+msp->c;
os = (3*osp->a*(e->t_cur+.0001)+2*osp->b)*(e->t_cur+.0001)+osp->c;
}
}
if ( e->t_cur-e->tmin > e->tmax-e->t_cur ) { os = -os; ms = -ms; }
if ( p->t_cur-p->tmin > p->tmax-p->t_cur ) { ps = -ps; qs = -qs; }
if ( ms!=0 && qs!=0 ) { os /= ms; ps /= qs; }
else if ( ms==0 && qs==0 ) /* Do Nothing */;
else if ( (ms==0 && os>0) || (qs==0 && ps<0) ) /* Does this make sense? */
return( false );
else if ( (ms==0 && os<0) || (qs==0 && ps>0) ) /* Does this make sense? */
return( true );
if ( os==ps || ms==0 || qs==0 )
return( e->o_mmax<p->o_mmax );
return( os<ps );
}
static void AddEdge(EdgeList *es, Spline *sp, real tmin, real tmax ) {
Edge *e, *pr;
real m1, m2;
int mpos;
Hints *hint;
Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
e = calloc(1,sizeof(Edge));
e->spline = sp;
m1 = ( ((msp->a*tmin+msp->b)*tmin+msp->c)*tmin + msp->d ) * es->scale;
m2 = ( ((msp->a*tmax+msp->b)*tmax+msp->c)*tmax + msp->d ) * es->scale;
if ( m1>m2 ) {
e->mmin = m2;
e->t_mmin = tmax;
e->mmax = m1;
e->t_mmax = tmin;
e->up = false;
} else {
e->mmax = m2;
e->t_mmax = tmax;
e->mmin = m1;
e->t_mmin = tmin;
e->up = true;
}
if ( RealNear(e->mmin,es->mmin)) e->mmin = es->mmin;
e->o_mmin = ( ((osp->a*e->t_mmin+osp->b)*e->t_mmin+osp->c)*e->t_mmin + osp->d ) * es->scale;
e->o_mmax = ( ((osp->a*e->t_mmax+osp->b)*e->t_mmax+osp->c)*e->t_mmax + osp->d ) * es->scale;
e->mmin -= es->mmin; e->mmax -= es->mmin;
e->t_cur = e->t_mmin;
e->o_cur = e->o_mmin;
e->m_cur = e->mmin;
e->last_opos = e->last_mpos = -2;
e->tmin = tmin; e->tmax = tmax;
if ( e->mmin<0 || e->mmin>=e->mmax ) {
/*IError("Probably not serious, but we've got a zero length spline in AddEdge in %s",es->sc==NULL?<nameless>:es->sc->name);*/
free(e);
return;
}
if ( es->sc!=NULL ) for ( hint=es->hhints; hint!=NULL; hint=hint->next ) {
if ( hint->adjustb ) {
if ( e->m_cur>hint->b1 && e->m_cur<hint->b2 ) {
e->m_cur = e->mmin = hint->ab;
e->min_adjusted = true;
} else if ( e->mmax>hint->b1 && e->mmax<hint->b2 ) {
e->mmax = hint->ab;
e->max_adjusted = true;
}
} else if ( hint->adjuste ) {
if ( e->m_cur>hint->e1 && e->m_cur<hint->e2 ) {
e->m_cur = e->mmin = hint->ae;
e->min_adjusted = true;
} else if ( e->mmax>hint->e1 && e->mmax<hint->e2 ) {
e->mmax = hint->ae;
e->max_adjusted = true;
}
}
}
mpos = (int) ceil(e->m_cur);
if ( mpos>e->mmax || mpos>=es->cnt ) {
free(e);
return;
}
if ( e->m_cur!=ceil(e->m_cur) ) {
/* bring the new edge up to its first scan line */
e->t_cur = TOfNextMajor(e,es,ceil(e->m_cur));
e->o_cur = ( ((osp->a*e->t_cur+osp->b)*e->t_cur+osp->c)*e->t_cur + osp->d ) * es->scale;
}
e->before = es->last;
if ( es->last!=NULL )
es->last->after = e;
if ( es->last==NULL )
es->splinesetfirst = e;
es->last = e;
if ( es->edges[mpos]==NULL || e->o_cur<es->edges[mpos]->o_cur ||
(e->o_cur==es->edges[mpos]->o_cur && SlopeLess(e,es->edges[mpos],es->other))) {
e->esnext = es->edges[mpos];
es->edges[mpos] = e;
} else {
for ( pr=es->edges[mpos]; pr->esnext!=NULL && pr->esnext->o_cur<e->o_cur ;
pr = pr->esnext );
/* When two splines share a vertex which is a local minimum, then */
/* o_cur will be equal for both (to the vertex's o value) and so */
/* the above code randomly picked one to go first. That screws up */
/* the overlap code, which wants them properly ordered from the */
/* start. so look at the end point, nope the end point isn't always */
/* meaningful, look at the slope... */
if ( pr->esnext!=NULL && pr->esnext->o_cur==e->o_cur &&
SlopeLess(e,pr->esnext,es->other)) {
pr = pr->esnext;
}
e->esnext = pr->esnext;
pr->esnext = e;
}
if ( es->interesting ) {
/* Mark the other end of the spline as interesting */
es->interesting[(int) ceil(e->mmax)]=1;
}
}
static void AddMajorEdge(EdgeList *es, Spline *sp) {
Edge *e, *pr;
real m1;
Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
e = calloc(1,sizeof(Edge));
e->spline = sp;
e->mmin = e->mmax = m1 = msp->d * es->scale - es->mmin;
e->t_mmin = 0;
e->t_mmax = 1;
e->up = false;
e->o_mmin = osp->d * es->scale;
e->o_mmax = ( osp->a + osp->b + osp->c + osp->d ) * es->scale;
if ( e->o_mmin == e->o_mmax ) { /* Just a point? */
free(e);
return;
}
if ( e->mmin<0 )
IError("Grg!");
if ( ceil(e->m_cur)>e->mmax ) {
free(e);
return;
}
if ( es->majors==NULL || es->majors->mmin>=m1 ) {
e->esnext = es->majors;
es->majors = e;
} else {
for ( pr=es->majors; pr->esnext!=NULL && pr->esnext->mmin<m1; pr = pr->esnext );
e->esnext = pr->esnext;
pr->esnext = e;
}
}
static void AddSpline(EdgeList *es, Spline *sp ) {
real t1=2, t2=2, t;
real b2_fourac;
real fm, tm;
Spline1D *msp = &sp->splines[es->major], *osp = &sp->splines[es->other];
/* Find the points of extrema on the curve discribing y behavior */
if ( !RealNear(msp->a,0) ) {
/* cubic, possibly 2 extrema (possibly none) */
b2_fourac = 4*msp->b*msp->b - 12*msp->a*msp->c;
if ( b2_fourac>=0 ) {
b2_fourac = sqrt(b2_fourac);
t1 = (-2*msp->b - b2_fourac) / (6*msp->a);
t2 = (-2*msp->b + b2_fourac) / (6*msp->a);
t1 = CheckExtremaForSingleBitErrors(msp,t1,t2);
t2 = CheckExtremaForSingleBitErrors(msp,t2,t1);
if ( t1>t2 ) { real temp = t1; t1 = t2; t2 = temp; }
else if ( t1==t2 ) t2 = 2.0;
/* check for curves which have such a small slope they might */
/* as well be horizontal */
fm = es->major==1?sp->from->me.y:sp->from->me.x;
tm = es->major==1?sp->to->me.y:sp->to->me.x;
if ( fm==tm ) {
real m1, m2, d1, d2;
m1 = m2 = fm;
if ( t1>0 && t1<1 )
m1 = ((msp->a*t1+msp->b)*t1+msp->c)*t1 + msp->d;
if ( t2>0 && t2<1 )
m2 = ((msp->a*t2+msp->b)*t2+msp->c)*t2 + msp->d;
d1 = (m1-fm)*es->scale;
d2 = (m2-fm)*es->scale;
if ( d1>-.5 && d1<.5 && d2>-.5 && d2<.5 ) {
sp->ishorvert = true;
if ( es->genmajoredges )
AddMajorEdge(es,sp);
return; /* Pretend it's horizontal, ignore it */
}
}
}
} else if ( !RealNear(msp->b,0) ) {
/* Quadratic, at most one extremum */
t1 = -msp->c/(2.0*msp->b);
} else if ( !RealNear(msp->c,0) ) {
/* linear, no points of extrema */
} else {
sp->ishorvert = true;
if ( es->genmajoredges )
AddMajorEdge(es,sp);
return; /* Horizontal line, ignore it */
}
if ( RealNear(t1,0)) t1=0;
if ( RealNear(t1,1)) t1=1;
if ( RealNear(t2,0)) t2=0;
if ( RealNear(t2,1)) t2=1;
if ( RealNear(t1,t2)) t2=2;
t=0;
if ( t1>0 && t1<1 ) {
AddEdge(es,sp,0,t1);
t = t1;
}
if ( t2>0 && t2<1 ) {
AddEdge(es,sp,t,t2);
t = t2;
}
AddEdge(es,sp,t,1.0);
if ( es->interesting ) {
/* Also store up points of extrema in X as interesting (we got the endpoints, just internals now)*/
extended ot1, ot2;
int mpos;
SplineFindExtrema(osp,&ot1,&ot2);
if ( ot1>0 && ot1<1 ) {
mpos = (int) ceil( ( ((msp->a*ot1+msp->b)*ot1+msp->c)*ot1+msp->d )*es->scale-es->mmin );
es->interesting[mpos] = 1;
}
if ( ot2>0 && ot2<1 ) {
mpos = (int) ceil( ( ((msp->a*ot2+msp->b)*ot2+msp->c)*ot2+msp->d )*es->scale-es->mmin );
es->interesting[mpos] = 1;
}
}
}
void FindEdgesSplineSet(SplinePointList *spl, EdgeList *es, int ignore_clip) {
Spline *spline, *first;
for ( ; spl!=NULL; spl = spl->next ) {
if ( spl->first->prev!=NULL && spl->first->prev->from!=spl->first &&
(!ignore_clip || (ignore_clip==1 && !spl->is_clip_path) || (ignore_clip==2 && spl->is_clip_path))) {
first = NULL;
es->last = es->splinesetfirst = NULL;
/* Set so there is no previous point!!! */
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
AddSpline(es,spline);
if ( first==NULL ) first = spline;
}
if ( es->last!=NULL ) {
es->splinesetfirst->before = es->last;
es->last->after = es->splinesetfirst;
}
}
}
}
static void FindEdges(SplineChar *sc, EdgeList *es) {
RefChar *rf;
for ( rf=sc->layers[es->layer].refs; rf!=NULL; rf = rf->next )
FindEdgesSplineSet(rf->layers[0].splines,es,false);
FindEdgesSplineSet(sc->layers[es->layer].splines,es,false);
}
Edge *ActiveEdgesInsertNew(EdgeList *es, Edge *active,int i) {
Edge *apt, *pr, *npt;
for ( pr=NULL, apt=active, npt=es->edges[(int) i]; apt!=NULL && npt!=NULL; ) {
if ( npt->o_cur<apt->o_cur ) {
npt->aenext = apt;
if ( pr==NULL )
active = npt;
else
pr->aenext = npt;
pr = npt;
npt = npt->esnext;
} else {
pr = apt;
apt = apt->aenext;
}
}
while ( npt!=NULL ) {
npt->aenext = NULL;
if ( pr==NULL )
active = npt;
else
pr->aenext = npt;
pr = npt;
npt = npt->esnext;
}
return( active );
}
Edge *ActiveEdgesRefigure(EdgeList *es, Edge *active,real i) {
Edge *apt, *pr;
int any;
/* first remove any entry which doesn't intersect the new scan line */
/* (ie. stopped on last line) */
for ( pr=NULL, apt=active; apt!=NULL; apt = apt->aenext ) {
if ( apt->mmax<i ) {
if ( pr==NULL )
active = apt->aenext;
else
pr->aenext = apt->aenext;
} else
pr = apt;
}
/* then move the active list to the next line */
for ( apt=active; apt!=NULL; apt = apt->aenext ) {
Spline1D *osp = &apt->spline->splines[es->other];
apt->t_cur = TOfNextMajor(apt,es,i);
apt->o_cur = ( ((osp->a*apt->t_cur+osp->b)*apt->t_cur+osp->c)*apt->t_cur + osp->d ) * es->scale;
}
/* reorder list */
if ( active!=NULL ) {
any = true;
while ( any ) {
any = false;
for ( pr=NULL, apt=active; apt->aenext!=NULL; ) {
if ( apt->o_cur <= apt->aenext->o_cur ) {
/* still ordered */;
pr = apt;
apt = apt->aenext;
} else if ( pr==NULL ) {
active = apt->aenext;
apt->aenext = apt->aenext->aenext;
active->aenext = apt;
/* don't need to set any, since this reorder can't disorder the list */
pr = active;
} else {
pr->aenext = apt->aenext;
apt->aenext = apt->aenext->aenext;
pr->aenext->aenext = apt;
any = true;
pr = pr->aenext;
}
}
}
}
/* Insert new nodes */
active = ActiveEdgesInsertNew(es,active,i);
return( active );
}
Edge *ActiveEdgesFindStem(Edge *apt, Edge **prev, real i) {
int cnt=apt->up?1:-1;
Edge *pr, *e;
for ( pr=apt, e=apt->aenext; e!=NULL && cnt!=0; pr=e, e=e->aenext ) {
if ( pr->up!=e->up )
cnt += (e->up?1:-1);
else if ( (pr->before==e || pr->after==e ) &&
(( pr->mmax==i && e->mmin==i ) ||
( pr->mmin==i && e->mmax==i )) )
/* This just continues the line and doesn't change count */;
else
cnt += (e->up?1:-1);
}
/* color a horizontal line that comes out of the last vertex */
if ( e!=NULL && (e->before==pr || e->after==pr) &&
(( pr->mmax==i && e->mmin==i ) ||
( pr->mmin==i && e->mmax==i )) ) {
pr = e;
e = e->aenext;
} else if ( e!=NULL && ((pr->up && !e->up) || (!pr->up && e->up)) &&
pr->spline!=e->spline &&
((pr->after==e && pr->spline->to->next!=NULL &&
pr->spline->to->next!=e->spline &&
pr->spline->to->next->to->next==e->spline ) ||
(pr->before==e && pr->spline->from->prev!=NULL &&
pr->spline->from->prev!=e->spline &&
pr->spline->from->prev->from->prev!=e->spline )) &&
((pr->mmax == i && e->mmax==i ) ||
(pr->mmin == i && e->mmin==i )) ) {
pr = e;
}
*prev = pr;
return( e );
}
static int isvstem(EdgeList *es,real stem,int *vval) {
Hints *hint;
for ( hint=es->vhints; hint!=NULL ; hint=hint->next ) {
if ( stem>=hint->b1 && stem<=hint->b2 ) {
*vval = hint->ab;
return( true );
} else if ( stem>=hint->e1 && stem<=hint->e2 ) {
*vval = hint->ae;
return( true );
}
}
return( false );
}
static void FillChar(EdgeList *es) {
Edge *active=NULL, *apt, *pr, *e, *prev;
int i, k, end, width, oldk;
uint8 *bpt;
for ( i=0; i<es->cnt; ++i ) {
active = ActiveEdgesRefigure(es,active,i);
/* process scanline */
bpt = es->bitmap+((es->cnt-1-i)*es->bytes_per_line);
for ( apt=active; apt!=NULL; ) {
e = ActiveEdgesFindStem(apt,&prev,i);
pr = prev;
width = rint(pr->o_cur-apt->o_cur);
if ( width<=0 ) width=1;
k=rint(apt->o_cur-es->omin);
if ( k<0 ) k=0;
end =rint(pr->o_cur-es->omin);
if ( end-k > width-1 ) {
int kval = -999999, eval= -999999;
if ( isvstem(es,apt->o_cur,&kval) )
k = kval;
if ( isvstem(es,pr->o_cur,&eval))
end = eval;
if ( end-k > width-1 ) {
if ( k!=kval && (end==eval || (apt->o_cur-es->omin)-k > end-(pr->o_cur-es->omin) ))
++k;
else if ( end!=eval )
--end;
}
}
oldk = k;
if ( apt->last_mpos==i-1 || pr->last_mpos==i-1 ) {
int lx1=apt->last_opos, lx2=pr->last_opos;
if ( apt->last_mpos!=i-1 ) lx1 = lx2;
else if ( pr->last_mpos!=i-1 ) lx2 = lx1;
if ( lx1>lx2 ) { int temp = lx1; lx1=lx2; lx2=temp; }
if ( lx2<k-1 ) {
int mid = (lx2+k)/2;
for ( ; lx2<mid; ++lx2 )
bpt[(lx2>>3)+es->bytes_per_line] |= (1<<(7-(lx2&7)));
for ( ; lx2<k; ++lx2 )
bpt[(lx2>>3)] |= (1<<(7-(lx2&7)));
} else if ( lx1>end+1 ) {
int mid = (lx1+end)/2;
for ( ; lx1<mid; --lx1 )
bpt[(lx1>>3)+es->bytes_per_line] |= (1<<(7-(lx1&7)));
for ( ; lx1<end; --lx1 )
bpt[(lx1>>3)] |= (1<<(7-(lx1&7)));
}
}
for ( ; k<=end; ++k )
bpt[k>>3] |= (1<<(7-(k&7)));
apt->last_mpos = pr->last_mpos = i;
apt->last_opos = oldk;
pr->last_opos = end;
apt = e;
}
}
}
static void InitializeHints(SplineChar *sc, EdgeList *es) {
Hints *hint, *last;
StemInfo *s;
real t1, t2;
int k,end,width;
/* we only care about hstem hints, and only if they fail to cross a */
/* vertical pixel boundary. If that happens, adjust either the top */
/* or bottom position so that a boundary forcing is crossed. Any */
/* vertexes at those points will be similarly adjusted later... */
last = NULL; es->hhints = NULL;
for ( s=sc->hstem; s!=NULL; s=s->next ) {
hint = calloc(1,sizeof(Hints));
hint->base = s->start; hint->width = s->width;
if ( last==NULL ) es->hhints = hint;
else last->next = hint;
last = hint;
hint->adjuste = hint->adjustb = false;
t1 = hint->base*es->scale -es->mmin;
t2 = (hint->base+hint->width)*es->scale -es->mmin;
if ( floor(t1)==floor(t2) && t1!=floor(t1) && t2!=floor(t2) ) {
if ( t1<t2 ) {
if ( t1-floor(t1) > ceil(t2)-t2 ) {
hint->adjuste = true;
hint->ae = ceil(t2);
} else {
hint->adjustb = true;
hint->ab = floor(t1);
}
} else {
if ( t2-floor(t2) > ceil(t1)-t1 ) {
hint->adjustb = true;
hint->ab = ceil(t1);
} else {
hint->adjuste = true;
hint->ae = floor(t2);
}
}
}
hint->b1 = t1-.2; hint->b2 = t1 + .2;
hint->e1 = t2-.2; hint->e2 = t2 + .2;
}
/* Nope. We care about vstems too now, but in a different case */
last = NULL; es->vhints = NULL;
for ( s=sc->vstem; s!=NULL; s=s->next ) {
hint = calloc(1,sizeof(Hints));
hint->base = s->start; hint->width = s->width;
if ( last==NULL ) es->vhints = hint;
else last->next = hint;
last = hint;
if ( hint->width<0 ) {
width = -rint(-hint->width*es->scale);
t1 = (hint->base+hint->width)*es->scale;
t2 = hint->base*es->scale;
} else {
width = rint(hint->width*es->scale);
t1 = hint->base*es->scale;
t2 = (hint->base+hint->width)*es->scale;
}
k = rint(t1-es->omin); end = rint(t2-es->omin);
if ( end-k > width-1 )
if ( end-k > width-1 ) {
if ( (t1-es->omin)-k > end-(t2-es->omin) )
++k;
else
--end;
}
hint->ab = k; hint->ae = end;
hint->b1 = t1-.2; hint->b2 = t1 + .2;
hint->e1 = t2-.2; hint->e2 = t2 + .2;
}
}
/* After a bitmap has been compressed, it's sizes may not comply with the */
/* expectations for saving images */
void BCRegularizeBitmap(BDFChar *bdfc) {
int bpl =(bdfc->xmax-bdfc->xmin)/8+1;
int i;
if ( bdfc->bytes_per_line!=bpl ) {
uint8 *bitmap = malloc(bpl*(bdfc->ymax-bdfc->ymin+1));
for ( i=0; i<=(bdfc->ymax-bdfc->ymin); ++i )
memcpy(bitmap+i*bpl,bdfc->bitmap+i*bdfc->bytes_per_line,bpl);
free(bdfc->bitmap);
bdfc->bitmap= bitmap;
bdfc->bytes_per_line = bpl;
}
}
void BCRegularizeGreymap(BDFChar *bdfc) {
int bpl = bdfc->xmax-bdfc->xmin+1;
int i;
if ( bdfc->bytes_per_line!=bpl ) {
uint8 *bitmap = malloc(bpl*(bdfc->ymax-bdfc->ymin+1));
for ( i=0; i<=(bdfc->ymax-bdfc->ymin); ++i )
memcpy(bitmap+i*bpl,bdfc->bitmap+i*bdfc->bytes_per_line,bpl);
free(bdfc->bitmap);
bdfc->bitmap= bitmap;
bdfc->bytes_per_line = bpl;
}
}
void BCCompressBitmap(BDFChar *bdfc) {
/* Now we may have allocated a bit more than we need to the bitmap */
/* check to see if there are any unused rows or columns.... */
int i,j,any, off, last;
/* we can use the same code to lop off rows whether we deal with bytes or bits */
for ( i=0; i<bdfc->ymax-bdfc->ymin; ++i ) {
any = 0;
for ( j=0; j<bdfc->bytes_per_line; ++j )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+j]!=0 ) any = 1;
if ( any )
break;
}
if ( i!=0 ) {
bdfc->ymax -= i;
memmove(bdfc->bitmap,bdfc->bitmap+i*bdfc->bytes_per_line,(bdfc->ymax-bdfc->ymin+1)*bdfc->bytes_per_line );
}
for ( i=bdfc->ymax-bdfc->ymin; i>0; --i ) {
any = 0;
for ( j=0; j<bdfc->bytes_per_line; ++j )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+j]!=0 ) any = 1;
if ( any )
break;
}
if ( i!=bdfc->ymax-bdfc->ymin ) {
bdfc->ymin += bdfc->ymax-bdfc->ymin-i;
}
if ( !bdfc->byte_data ) {
for ( j=0; j<bdfc->xmax-bdfc->xmin; ++j ) {
any = 0;
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+(j>>3)] & (1<<(7-(j&7))) )
any = 1;
if ( any )
break;
}
off = j;
if ( off/8>0 ) {
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
memmove(bdfc->bitmap+i*bdfc->bytes_per_line,
bdfc->bitmap+i*bdfc->bytes_per_line+off/8,
bdfc->bytes_per_line-off/8);
memset(bdfc->bitmap+(i+1)*bdfc->bytes_per_line-off/8,
0, off/8);
}
bdfc->xmin += off-off%8;
off %= 8;
}
if ( off!=0 ) {
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
last = 0;
for ( j=bdfc->bytes_per_line-1; j>=0; --j ) {
int index = i*bdfc->bytes_per_line+j;
int temp = bdfc->bitmap[index]>>(8-off);
bdfc->bitmap[index] = (bdfc->bitmap[index]<<off)|last;
last = temp;
}
if ( last!=0 )
IError("Sigh");
}
bdfc->xmin += off;
}
for ( j=bdfc->xmax-bdfc->xmin; j>0; --j ) {
any = 0;
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+(j>>3)] & (1<<(7-(j&7))) )
any = 1;
if ( any )
break;
}
if ( j!=bdfc->xmax+bdfc->xmin ) {
bdfc->xmax -= bdfc->xmax-bdfc->xmin-j;
}
BCRegularizeBitmap(bdfc);
} else {
for ( j=0; j<bdfc->xmax-bdfc->xmin; ++j ) {
any = 0;
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+j] != 0 )
any = 1;
if ( any )
break;
}
off = j;
if ( off!=0 ) {
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i ) {
memmove(bdfc->bitmap+i*bdfc->bytes_per_line,
bdfc->bitmap+i*bdfc->bytes_per_line+off,
bdfc->bytes_per_line-off);
memset(bdfc->bitmap+(i+1)*bdfc->bytes_per_line-off,
0, off);
}
bdfc->xmin += off;
}
for ( j=bdfc->xmax-bdfc->xmin; j>0; --j ) {
any = 0;
for ( i=0; i<bdfc->ymax-bdfc->ymin+1; ++i )
if ( bdfc->bitmap[i*bdfc->bytes_per_line+j] != 0 )
any = 1;
if ( any )
break;
}
if ( j!=bdfc->xmax+bdfc->xmin ) {
bdfc->xmax -= bdfc->xmax-bdfc->xmin-j;
}
BCRegularizeGreymap(bdfc);
}
if ( bdfc->xmax<bdfc->xmin || bdfc->ymax<bdfc->ymin ) {
bdfc->ymax = bdfc->ymin-1;
bdfc->xmax = bdfc->xmin-1;
}
}
static void Bresenham(uint8 *bytemap,EdgeList *es,int x1,int x2,int y1,int y2,
int grey,uint8 *clipmask) {
int dx, dy, incr1, incr2, d, x, y;
int incr3;
int bytes_per_line = es->bytes_per_line<<3;
int ymax = es->cnt;
/* We are guarenteed x1<=x2 */
dx = x2-x1;
if ( (dy = y1-y2)<0 ) dy=-dy;
if ( dx>=dy ) {
d = 2 * dy - dx;
incr1 = 2*dy;
incr2 = 2*(dy-dx);
incr3 = y2>y1 ? 1 : -1;
x = x1;
y = y1;
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
bytemap[y*bytes_per_line+x] = grey;
while ( x<x2 ) {
++x;
if ( d<0 )
d += incr1;
else {
y += incr3;
d += incr2;
}
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
bytemap[y*bytes_per_line+x] = grey;
}
} else {
if ( y1>y2 ) {
incr1 = y1; y1 = y2; y2 = incr1;
incr1 = x1; x1 = x2; x2 = incr1;
}
d = 2 * dx - dy;
incr1 = 2*dx;
incr2 = 2*(dx-dy);
incr3 = x2>x1 ? 1 : -1;
x = x1;
y = y1;
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
bytemap[y*bytes_per_line+x] = grey;
while ( y<y2 ) {
++y;
if ( d<0 )
d += incr1;
else {
x += incr3;
d += incr2;
}
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
if ( clipmask==NULL || (clipmask[y*es->bytes_per_line+(x>>3)]&(0x80>>(x&7))) )
bytemap[y*bytes_per_line+x] = grey;
}
}
}
static void BresenhamT(uint8 *bytemap,EdgeList *es,int x1,int x2,int y1,int y2,
int grey,uint8 *clipmask) {
if ( x1>x2 ) {
int dx, dy;
dx = x1; x1 = x2; x2 = dx;
dy = y1; y1 = y2; y2 = dy;
}
Bresenham(bytemap,es,x1,x2,y1,y2,grey,clipmask);
}
static void StrokeLine(uint8 *bytemap,IPoint *from, IPoint *to,EdgeList *es,int grey,int width, uint8 *clipmask) {
int x1, x2, y1, y2;
int dx, dy;
BasePoint vector;
bigreal len;
int xoff, yoff;
x1 = from->x - es->omin;
x2 = to->x - es->omin;
if ( (y1 = (es->cnt-1 - (from->y - es->mmin)))<0 ) y1=0;
if ( (y2 = (es->cnt-1 - (to->y - es->mmin)))<0 ) y2=0;
if ( width>1 ) {
int incr1, incr2, d, x, y;
int incr3;
int bytes_per_line = es->bytes_per_line<<3;
int ymax = es->cnt;
vector.x = x1-x2; vector.y = y1-y2;
len = vector.x*vector.x + vector.y*vector.y;
if ( len==0 )
return;
len = sqrt(len);
yoff = -vector.x*width/(2.0*len);
xoff = vector.y*width/(2.0*len);
if ( xoff<0 ) {
xoff = -xoff; yoff = -yoff;
}
if ( x1>x2 ) {
dx = x1; x1 = x2; x2 = dx;
dy = y1; y1 = y2; y2 = dy;
}
/* This is just Bresenham's algorithem. We use it twice to draw a rectangle */
dx = x2-x1;
if ( (dy = y1-y2)<0 ) dy=-dy;
if ( dx>=dy ) {
d = 2 * dy - dx;
incr1 = 2*dy;
incr2 = 2*(dy-dx);
incr3 = y2>y1 ? 1 : -1;
x = x1;
y = y1;
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
while ( x<x2 ) {
++x;
if ( d<0 )
d += incr1;
else {
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
y += incr3;
d += incr2;
}
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
}
} else {
if ( y1>y2 ) {
incr1 = y1; y1 = y2; y2 = incr1;
incr1 = x1; x1 = x2; x2 = incr1;
}
d = 2 * dx - dy;
incr1 = 2*dx;
incr2 = 2*(dx-dy);
incr3 = x2>x1 ? 1 : -1;
x = x1;
y = y1;
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
while ( y<y2 ) {
++y;
if ( d<0 )
d += incr1;
else {
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
x += incr3;
d += incr2;
}
if ( x>=0 && y>=0 && x<bytes_per_line && y<ymax )
Bresenham(bytemap,es,x-xoff,x+xoff,y-yoff,y+yoff,grey,clipmask);
}
}
} else
BresenhamT(bytemap,es,x1,x2,y1,y2,grey,clipmask);
}
static void StrokeSS(uint8 *bytemap,EdgeList *es,int width,int grey,SplineSet *ss,
uint8 *clipmask) {
LinearApprox *lap;
LineList *line, *prev;
Spline *spline, *first;
for ( ; ss!=NULL; ss=ss->next ) {
first = NULL;
for ( spline = ss->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
lap = SplineApproximate(spline,es->scale);
if ( lap->lines!=NULL ) {
for ( prev = lap->lines, line=prev->next; line!=NULL; prev = line, line=line->next )
StrokeLine(bytemap,&prev->here,&line->here,es,grey,width,clipmask);
}
if ( first == NULL ) first = spline;
}
}
}
static void StrokeGlyph(uint8 *bytemap,EdgeList *es,real wid, SplineChar *sc) {
RefChar *ref;
int width = rint(wid*es->scale);
StrokeSS(bytemap,es,width,0xff,sc->layers[ly_fore].splines,NULL);
for ( ref=sc->layers[ly_fore].refs; ref!=NULL; ref = ref->next )
StrokeSS(bytemap,es,width,0xff,ref->layers[0].splines,NULL);
}
static void StrokePaths(uint8 *bytemap,EdgeList *es,Layer *layer,Layer *alt,
uint8 *clipmask) {
uint32 col;
int width;
int grey;
if ( layer->stroke_pen.brush.col!=COLOR_INHERITED )
col = layer->stroke_pen.brush.col;
else if ( alt!=NULL && alt->stroke_pen.brush.col!=COLOR_INHERITED )
col = alt->stroke_pen.brush.col;
else
col = 0x000000;
if ( layer->stroke_pen.width!=WIDTH_INHERITED )
width = rint(layer->stroke_pen.width*layer->stroke_pen.trans[0]*es->scale);
else if ( alt!=NULL && alt->stroke_pen.width!=WIDTH_INHERITED )
width = rint(alt->stroke_pen.width*alt->stroke_pen.trans[0]*es->scale);
else
width = 1;
grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
/* but our internal greymap convention is backwards */
grey = 255-grey;
StrokeSS(bytemap,es,width,grey,layer->splines,clipmask);
}
static int PatternHere(bigreal scale,DBounds *bbox,int iy,int ix,
struct pattern *pat) {
real x,y,tempx;
int grey;
BDFChar *bdfc;
if ( pat==NULL || pat->pat==NULL )
return( 0 );
x = (ix+.5)/scale + bbox->minx;
y = bbox->maxy-(iy-.5)/scale;
tempx = pat->invtrans[0]*x + pat->invtrans[2]*y + pat->invtrans[4];
y = pat->invtrans[1]*x + pat->invtrans[3]*y + pat->invtrans[5];
x = tempx;
if ( (x = fmod(x,pat->width))<0 ) x+=pat->width;
if ( (y = fmod(y,pat->height))<0 ) y+=pat->height;
bdfc = pat->pat;
ix = pat->bminx + rint(x*pat->bwidth/pat->width);
iy = pat->bminy + rint(y*pat->bheight/pat->height);
ix -= bdfc->xmin;
iy = bdfc->ymax-1-iy;
if ( ix<0 || iy<0 || ix>=bdfc->xmax || iy>=bdfc->ymax )
return( 0 );
/* bdfc has a 4bit grey, convert to 8bit grey by multiplying by 17 */
grey = 17*bdfc->bitmap[ iy*bdfc->bytes_per_line + ix];
return( grey );
}
int GradientHere(bigreal scale,DBounds *bbox,int iy,int ix,
struct gradient *grad, struct pattern *pat,
int defgrey) {
real x,y;
BasePoint unit, offset;
bigreal len, relpos;
int i, col, grey;
if ( grad==NULL ) {
if ( pat!=NULL && pat->pat!=NULL )
return( PatternHere(scale,bbox,iy,ix,pat));
else
return( defgrey );
}
x = (ix+.5)/scale + bbox->minx;
y = bbox->maxy-(iy-.5)/scale;
if ( grad->radius==0 ) { /* Linear gradient */
unit.x = grad->stop.x - grad->start.x;
unit.y = grad->stop.y - grad->start.y;
len = sqrt(unit.x*unit.x + unit.y*unit.y);
if ( len==0 )
return ( defgrey );
unit.x /= len;
unit.y /= len;
offset.x = x-grad->start.x;
offset.y = y-grad->start.y;
relpos = unit.x*offset.x + unit.y*offset.y;
relpos /= len;
} else /*if ( grad->start.x==grad->stop.x && grad->start.y==grad->stop.y )*/ {
offset.x = x-grad->start.x;
offset.y = y-grad->start.y;
len = sqrt(offset.x*offset.x + offset.y*offset.y);
relpos = len/grad->radius;
}
if ( grad->sm==sm_repeat ) {
relpos = fmod(relpos,1.0);
if ( relpos<0 ) relpos += 1.0;
} else if ( grad->sm==sm_reflect ) {
relpos = fmod(relpos,2.0);
if ( relpos<0 ) relpos += 2.0;
if ( relpos>1 ) relpos = 2.0-relpos;
} else { /* sm_pad */
if ( relpos<0 ) relpos = 0;
if ( relpos>1 ) relpos = 1;
}
for ( i=0; i<grad->stop_cnt; ++i )
if ( relpos<=grad->grad_stops[i].offset )
break;
if ( i>=grad->stop_cnt )
col = grad->grad_stops[i-1].col;
else if ( relpos==grad->grad_stops[i].offset || i==0 )
col = grad->grad_stops[i].col;
else {
bigreal percent = (relpos-grad->grad_stops[i-1].offset)/ (grad->grad_stops[i].offset-grad->grad_stops[i-1].offset);
uint32 col1 = grad->grad_stops[i-1].col;
uint32 col2 = grad->grad_stops[i ].col;
if ( col1==COLOR_INHERITED ) col1 = 0x000000;
if ( col2==COLOR_INHERITED ) col2 = 0x000000;
int red = ((col1>>16)&0xff)*(1-percent) + ((col2>>16)&0xff)*percent;
int green = ((col1>>8 )&0xff)*(1-percent) + ((col2>>8 )&0xff)*percent;
int blue = ((col1 )&0xff)*(1-percent) + ((col2 )&0xff)*percent;
col = (red<<16) | (green<<8) | blue;
}
if ( col==COLOR_INHERITED ) col = 0x000000;
grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
/* but our internal greymap convention is backwards */
return( 255-grey );
}
void PatternPrep(SplineChar *sc,struct brush *brush,bigreal scale) {
SplineChar *psc;
int pixelsize;
SplineFont *sf;
struct pattern *pattern;
DBounds b;
if ( brush->gradient!=NULL )
return;
if ( (pattern = brush->pattern)==NULL )
return;
if ( pattern->pat!=NULL ) /* Recursive pattern? */
return;
sf = sc->parent;
psc = SFGetChar(sf,-1,pattern->pattern);
if ( psc==NULL )
return;
PatternSCBounds(psc,&b);
pixelsize = rint(scale*pattern->height*(sf->ascent+sf->descent)/(b.maxy-b.miny));
if ( pixelsize<=1 ) /* Pattern too small to show anything */
return;
pattern->bheight = rint(scale*pattern->height);
pattern->bwidth = rint(scale*pattern->width*(b.maxx-b.minx)/(b.maxy-b.miny));
pattern->bminx = rint(scale*b.minx*pattern->width/(b.maxx-b.minx));
pattern->bminy = rint(scale*b.miny*pattern->height/(b.maxy-b.miny));
pattern->pat = SplineCharAntiAlias(psc,ly_fore,pixelsize,4);
MatInverse(pattern->invtrans,pattern->transform);
}
static void SetByteMapToGrey(uint8 *bytemap,EdgeList *es,Layer *layer,Layer *alt,
uint8 *clipmask,SplineChar *sc) {
uint32 col;
int grey,i,j;
uint8 *pt, *bpt, *cpt;
struct gradient *grad = layer->fill_brush.gradient;
struct pattern *pat = layer->fill_brush.pattern;
if ( layer->fill_brush.col!=COLOR_INHERITED )
col = layer->fill_brush.col;
else if ( alt!=NULL && alt->fill_brush.col!=COLOR_INHERITED )
col = alt->fill_brush.col;
else
col = 0x000000;
grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
/* but our internal greymap convention is backwards */
grey = 255-grey;
PatternPrep(sc,&layer->fill_brush,es->scale);
for ( i=0; i<es->cnt; ++i ) {
bpt = es->bitmap + i*es->bytes_per_line;
cpt = clipmask + i*es->bytes_per_line;
pt = bytemap + i*8*es->bytes_per_line;
for ( j=0; j<8*es->bytes_per_line; ++j ) {
if ( clipmask==NULL || (cpt[j>>3]&(0x80>>(j&7))) )
if ( bpt[j>>3]&(0x80>>(j&7)) ) {
pt[j] = GradientHere(es->scale,&es->bbox,i,j,grad,pat,grey);
}
}
}
if ( pat!=NULL ) {
BDFCharFree(pat->pat);
pat->pat = NULL;
}
}
static void FillImages(uint8 *bytemap,EdgeList *es,ImageList *img,Layer *layer,
Layer *alt, uint8 *clipmask) {
uint32 fillcol, col;
int grey,i,j,x1,x2,y1,y2,jj,ii;
if ( layer->fill_brush.col!=COLOR_INHERITED )
fillcol = layer->fill_brush.col;
else if ( alt!=NULL && alt->fill_brush.col!=COLOR_INHERITED )
fillcol = alt->fill_brush.col;
else
fillcol = 0x000000;
while ( img!=NULL ) {
struct _GImage *base = img->image->list_len==0?
img->image->u.image:img->image->u.images[0];
y1 = es->cnt-1 - (img->yoff*es->scale - es->mmin);
y2 = es->cnt-1 - ((img->yoff-base->height*img->yscale)*es->scale -es->mmin);
x1 = img->xoff*es->scale - es->omin;
x2 = (img->xoff+base->width*img->xscale)*es->scale - es->omin;
if ( y1==y2 || x1==x2 ) /* too small to show */
continue;
for ( i=0; i<=y2-y1; ++i ) {
if ( i+y1<0 || i+y1>=es->cnt ) /* Shouldn't happen, rounding errors might gives us off by 1s though */
continue;
ii = i*(base->height-1)/(y2-y1);
for ( j=0; j<x2-x1; ++j ) {
if ( j+x1<0 || j+x1>=8*es->bytes_per_line )
continue;
jj = j*(base->width-1)/(x2-x1);
if ( base->image_type==it_true )
col = ((uint32 *) (base->data + ii*base->bytes_per_line))[jj];
else if ( base->image_type==it_index ) {
col = (base->data + ii*base->bytes_per_line)[jj];
col = base->clut->clut[col];
} else if ( layer->dofill && base->trans!=(Color)-1) { /* Equivalent to imagemask */
if ( (base->trans==0 && !( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(jj&7)) ) ) ||
(base->trans!=0 && ( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(jj&7)) ) ))
continue; /* transparent */
col = fillcol;
} else {
int index = 0;
if ( (base->data + ii*base->bytes_per_line)[jj>>3]&(0x80>>(j&7)) )
index = 1;
if ( base->clut!=NULL ) {
col = base->clut->clut[index];
if ( col==0xb0b0b0 ) col = 0xffffff;
} else if ( index==1 )
col = 0xffffff;
else
col = 0x000000;
}
grey = ( ((col>>16)&0xff)*3 + ((col>>8)&0xff)*6 + (col&0xff) )/ 10;
/* but our internal greymap convention is backwards */
grey = 255-grey;
if ( clipmask==NULL || (clipmask[(i+y1)*es->bytes_per_line + ((j+x1)>>3)]&(0x80>>((j+x1)&7)) ))
bytemap[(i+y1)*8*es->bytes_per_line + j+x1] = grey;
}
}
img = img->next;
}
}
static void ProcessLayer(uint8 *bytemap,EdgeList *es,Layer *layer,
Layer *alt, uint8 *clipmask,SplineChar *sc) {
ImageList *img;
if ( !layer->fillfirst && layer->dostroke )
StrokePaths(bytemap,es,layer,alt,clipmask);
if ( layer->dofill ) {
memset(es->bitmap,0,es->cnt*es->bytes_per_line);
FindEdgesSplineSet(layer->splines,es,true);
FillChar(es);
SetByteMapToGrey(bytemap,es,layer,alt, clipmask,sc);
_FreeEdgeList(es);
}
for ( img = layer->images; img!=NULL; img=img->next )
FillImages(bytemap,es,img,layer,alt,clipmask);
if ( layer->fillfirst && layer->dostroke )
StrokePaths(bytemap,es,layer,alt,clipmask);
}
static uint8 *ProcessClipMask(EdgeList *es,Layer *layer) {
uint8 *clipmask;
if ( !SSHasClip(layer->splines) )
return( NULL );
clipmask = calloc(es->cnt*es->bytes_per_line,1);
memset(es->bitmap,0,es->cnt*es->bytes_per_line);
FindEdgesSplineSet(layer->splines,es,2);
FillChar(es);
memcpy(clipmask,es->bitmap,es->cnt*es->bytes_per_line);
_FreeEdgeList(es);
return( clipmask );
}
static void FlattenBytemap(EdgeList *es,uint8 *bytemap) {
int i,j;
uint8 *bpt, *pt;
memset(es->bitmap,0,es->cnt*es->bytes_per_line);
for ( i=0; i<es->cnt; ++i ) {
bpt = es->bitmap + i*es->bytes_per_line;
pt = bytemap + i*8*es->bytes_per_line;
for ( j=0; j<8*es->bytes_per_line; ++j )
if ( pt[j]>=128 )
bpt[j>>3] |= (0x80>>(j&7));
}
}
static int FigureBitmap(EdgeList *es,uint8 *bytemap, int is_aa) {
if ( is_aa ) {
free(es->bitmap);
es->bitmap = bytemap;
return( 8 );
} else {
FlattenBytemap(es,bytemap);
free(bytemap);
return( 0 );
}
}
/* Yes, I really do want a double, even though it will almost always be an */
/* integer value there are a few cases (fill pattern for charview) where */
/* I need more precision and the pixelsize itself is largely irrelevant */
/* (I care about the scale though) */
static BDFChar *_SplineCharRasterize(SplineChar *sc, int layer, bigreal pixelsize, int is_aa) {
EdgeList es;
BDFChar *bdfc;
int depth = 0;
if ( sc==NULL )
return( NULL );
memset(&es,'\0',sizeof(es));
if ( sc==NULL ) {
es.mmin = es.mmax = es.omin = es.omax = 0;
es.bitmap = calloc(1,1);
es.bytes_per_line = 1;
is_aa = false;
} else {
if ( sc->parent->multilayer )
/* I need to include the clipping path, FindBounds excludes it */
SplineCharQuickConservativeBounds(sc,&es.bbox);
else
SplineCharFindBounds(sc,&es.bbox);
es.scale = (pixelsize-.1) / (real) (sc->parent->ascent+sc->parent->descent);
es.mmin = floor(es.bbox.miny*es.scale);
es.mmax = ceil(es.bbox.maxy*es.scale);
es.omin = es.bbox.minx*es.scale;
es.omax = es.bbox.maxx*es.scale;
es.cnt = (int) (es.mmax-es.mmin) + 1;
es.layer = layer;
if ( es.cnt<8000 && es.omax-es.omin<8000 && es.cnt>1 ) {
es.edges = calloc(es.cnt,sizeof(Edge *));
es.sc = sc;
es.major = 1; es.other = 0;
es.bytes_per_line = ((int) ceil(es.omax-es.omin) + 8)/8;
es.bitmap = calloc(es.cnt*es.bytes_per_line,1);
InitializeHints(sc,&es);
if ( sc->parent->multilayer ) {
uint8 *bytemap = calloc(es.cnt*es.bytes_per_line*8,1);
int layer, i;
RefChar *rf;
for ( layer=ly_fore; layer<sc->layer_cnt; ++layer ) {
uint8 *clipmask = ProcessClipMask(&es,&sc->layers[layer]);
ProcessLayer(bytemap,&es,&sc->layers[layer],NULL,clipmask,sc);
for ( rf=sc->layers[layer].refs; rf!=NULL; rf = rf->next ) {
for ( i=0; i<rf->layer_cnt; ++i ) {
ProcessLayer(bytemap,&es,(Layer *) (&rf->layers[i]),
&sc->layers[layer],clipmask,sc);
}
}
free(clipmask);
}
depth = FigureBitmap(&es,bytemap,is_aa);
} else if ( sc->parent->strokedfont ) {
uint8 *bytemap = calloc(es.cnt*es.bytes_per_line*8,1);
StrokeGlyph(bytemap,&es,sc->parent->strokewidth,sc);
depth = FigureBitmap(&es,bytemap,is_aa);
} else {
FindEdges(sc,&es);
FillChar(&es);
depth = 0;
}
} else {
/* If they want a bitmap so enormous it threatens our memory */
/* then just give 'em a blank. It's probably by mistake anyway */
es.mmin = es.mmax = es.omin = es.omax = 0;
es.bitmap = calloc(1,1);
es.bytes_per_line = 1;
}
}
bdfc = chunkalloc(sizeof(BDFChar));
memset( bdfc,'\0',sizeof( BDFChar ));
bdfc->sc = sc;
bdfc->xmin = rint(es.omin);
bdfc->ymin = es.mmin;
bdfc->xmax = (int) ceil(es.omax-es.omin) + bdfc->xmin;
bdfc->ymax = es.mmax;
if ( sc!=NULL ) {
bdfc->width = rint(sc->width*pixelsize / (real) (sc->parent->ascent+sc->parent->descent));
bdfc->vwidth = rint(sc->vwidth*pixelsize / (real) (sc->parent->ascent+sc->parent->descent));
bdfc->orig_pos = sc->orig_pos;
}
bdfc->bitmap = es.bitmap;
bdfc->depth = depth;
bdfc->bytes_per_line = es.bytes_per_line;
if ( depth==8 ) {
bdfc->byte_data = true;
bdfc->bytes_per_line *= 8;
}
BCCompressBitmap(bdfc);
FreeEdges(&es);
return( bdfc );
}
BDFChar *SplineCharRasterize(SplineChar *sc, int layer, bigreal pixelsize) {
return( _SplineCharRasterize(sc,layer,pixelsize,false));
}
BDFFont *SplineFontToBDFHeader(SplineFont *_sf, int pixelsize, int indicate) {
BDFFont *bdf = calloc(1,sizeof(BDFFont));
int i;
real scale;
char size[40];
char aa[200];
int max;
SplineFont *sf; /* The complexity here is to pick the appropriate subfont of a CID font */
sf = _sf;
max = sf->glyphcnt;
for ( i=0; i<_sf->subfontcnt; ++i ) {
sf = _sf->subfonts[i];
if ( sf->glyphcnt>max ) max = sf->glyphcnt;
}
scale = pixelsize / (real) (sf->ascent+sf->descent);
if ( indicate ) {
sprintf(size,_("%d pixels"), pixelsize );
strcpy(aa,_("Generating bitmap font"));
if ( sf->fontname!=NULL ) {
strcat(aa,": ");
strncat(aa,sf->fontname,sizeof(aa)-strlen(aa)-1);
aa[sizeof(aa)-1] = '\0';
}
ff_progress_start_indicator(10,_("Rasterizing..."),
aa,size,sf->glyphcnt,1);
ff_progress_enable_stop(0);
}
bdf->sf = _sf;
bdf->glyphcnt = bdf->glyphmax = max;
bdf->pixelsize = pixelsize;
bdf->glyphs = malloc(max*sizeof(BDFChar *));
bdf->ascent = rint(sf->ascent*scale);
bdf->descent = pixelsize-bdf->ascent;
bdf->res = -1;
return( bdf );
}
BDFFont *SplineFontRasterize(SplineFont *_sf, int layer, int pixelsize, int indicate) {
BDFFont *bdf = SplineFontToBDFHeader(_sf,pixelsize,indicate);
int i,k;
SplineFont *sf=_sf; /* The complexity here is to pick the appropriate subfont of a CID font */
for ( i=0; i<bdf->glyphcnt; ++i ) {
if ( _sf->subfontcnt!=0 ) {
for ( k=0; k<_sf->subfontcnt; ++k ) if ( _sf->subfonts[k]->glyphcnt>i ) {
sf = _sf->subfonts[k];
if ( SCWorthOutputting(sf->glyphs[i]))
break;
}
}
bdf->glyphs[i] = SplineCharRasterize(sf->glyphs[i],layer,pixelsize);
if ( indicate ) ff_progress_next();
}
if ( indicate ) ff_progress_end_indicator();
return( bdf );
}
void BDFCAntiAlias(BDFChar *bc, int linear_scale) {
BDFChar new;
int i,j, l2 = linear_scale*linear_scale, max = l2-1;
uint8 *bpt, *pt;
if ( bc==NULL )
return;
memset(&new,'\0',sizeof(new));
new.xmin = floor( ((real) bc->xmin)/linear_scale );
new.ymin = floor( ((real) bc->ymin)/linear_scale );
new.xmax = new.xmin + (bc->xmax-bc->xmin+linear_scale-1)/linear_scale;
new.ymax = new.ymin + (bc->ymax-bc->ymin+linear_scale-1)/linear_scale;
new.width = rint( ((real) bc->width)/linear_scale );
new.bytes_per_line = (new.xmax-new.xmin+1);
new.orig_pos = bc->orig_pos;
new.sc = bc->sc;
new.byte_data = true;
new.depth = max==3 ? 2 : max==15 ? 4 : 8;
new.bitmap = calloc( (new.ymax-new.ymin+1) * new.bytes_per_line, sizeof(uint8));
if ( bc->depth>1 ) {
uint32 *sum = calloc(new.bytes_per_line,sizeof(uint32));
for ( i=0; i<=bc->ymax-bc->ymin; ++i ) {
bpt = bc->bitmap + i*bc->bytes_per_line;
for ( j=0; j<=bc->xmax-bc->xmin; ++j ) {
sum[j/linear_scale] += bpt[j];
}
if ( (i+1)%linear_scale==0 ) {
pt = new.bitmap + (i/linear_scale)*new.bytes_per_line;
for ( j=(bc->xmax-bc->xmin)/linear_scale-1; j>=0 ; --j ) {
int val = (sum[j]+128)/255;
if ( val>max ) val = max;
pt[j] = val;
}
memset(sum,0,new.bytes_per_line*sizeof(uint32));
}
}
} else {
for ( i=0; i<=bc->ymax-bc->ymin; ++i ) {
bpt = bc->bitmap + i*bc->bytes_per_line;
pt = new.bitmap + (i/linear_scale)*new.bytes_per_line;
for ( j=0; j<=bc->xmax-bc->xmin; ++j ) {
if ( bpt[(j>>3)] & (1<<(7-(j&7))) )
/* we can get values between 0..2^n we want values between 0..(2^n-1) */
if ( pt[ j/linear_scale ]!=max )
++pt[ j/linear_scale ];
}
}
}
free(bc->bitmap);
*bc = new;
}
GClut *_BDFClut(int linear_scale) {
int scale = linear_scale*linear_scale, i;
Color bg = default_background;
int bgr=((bg>>16)&0xff), bgg=((bg>>8)&0xff), bgb= (bg&0xff);
GClut *clut;
clut = calloc(1,sizeof(GClut));
clut->clut_len = scale;
clut->is_grey = (bgr==bgg && bgb==bgr);
clut->trans_index = -1;
for ( i=0; i<scale; ++i ) {
clut->clut[i] =
COLOR_CREATE( bgr- (i*(bgr))/(scale-1),
bgg- (i*(bgg))/(scale-1),
bgb- (i*(bgb))/(scale-1));
}
clut->clut[scale-1] = 0; /* avoid rounding errors */
return( clut );
}
void BDFClut(BDFFont *bdf, int linear_scale) {
bdf->clut = _BDFClut(linear_scale);
}
int BDFDepth(BDFFont *bdf) {
if ( bdf->clut==NULL )
return( 1 );
return( bdf->clut->clut_len==256 ? 8 :
bdf->clut->clut_len==16 ? 4 : 2);
}
BDFChar *SplineCharAntiAlias(SplineChar *sc, int layer, int pixelsize, int linear_scale) {
BDFChar *bc;
bc = _SplineCharRasterize(sc,layer, pixelsize*linear_scale,true);
if ( linear_scale!=1 )
BDFCAntiAlias(bc,linear_scale);
BCCompressBitmap(bc);
return( bc );
}
BDFFont *SplineFontAntiAlias(SplineFont *_sf, int layer, int pixelsize, int linear_scale) {
BDFFont *bdf;
int i,k;
real scale;
char size[40];
char aa[200];
int max;
SplineFont *sf; /* The complexity here is to pick the appropriate subfont of a CID font */
if ( linear_scale==1 )
return( SplineFontRasterize(_sf,layer,pixelsize,true));
bdf = calloc(1,sizeof(BDFFont));
sf = _sf;
max = sf->glyphcnt;
for ( i=0; i<_sf->subfontcnt; ++i ) {
sf = _sf->subfonts[i];
if ( sf->glyphcnt>max ) max = sf->glyphcnt;
}
scale = pixelsize / (real) (sf->ascent+sf->descent);
sprintf(size,_("%d pixels"), pixelsize );
strcpy(aa,_("Generating anti-alias font"));
if ( sf->fontname!=NULL ) {
strcat(aa,": ");
strncat(aa,sf->fontname,sizeof(aa)-strlen(aa));
aa[sizeof(aa)-1] = '\0';
}
ff_progress_start_indicator(10,_("Rasterizing..."),
aa,size,sf->glyphcnt,1);
ff_progress_enable_stop(0);
if ( linear_scale>16 ) linear_scale = 16; /* can't deal with more than 256 levels of grey */
if ( linear_scale<=1 ) linear_scale = 2;
bdf->sf = _sf;
bdf->glyphcnt = bdf->glyphmax = max;
bdf->pixelsize = pixelsize;
bdf->glyphs = malloc(max*sizeof(BDFChar *));
bdf->ascent = rint(sf->ascent*scale);
bdf->descent = pixelsize-bdf->ascent;
bdf->res = -1;
for ( i=0; i<max; ++i ) {
if ( _sf->subfontcnt!=0 ) {
for ( k=0; k<_sf->subfontcnt; ++k ) if ( _sf->subfonts[k]->glyphcnt>i ) {
sf = _sf->subfonts[k];
if ( SCWorthOutputting(sf->glyphs[i]))
break;
}
scale = pixelsize / (real) (sf->ascent+sf->descent);
}
bdf->glyphs[i] = SplineCharRasterize(sf->glyphs[i],layer,pixelsize*linear_scale);
BDFCAntiAlias(bdf->glyphs[i],linear_scale);
ff_progress_next();
}
BDFClut(bdf,linear_scale);
ff_progress_end_indicator();
return( bdf );
}
static void ByteMult(BDFChar *bc,int factor) {
/* Internal rasterizer uses bit depth 4, but font expects 8. Correct by multiplying each byte by 17 */
uint8 *pt, *end;
pt = bc->bitmap;
end = pt + bc->bytes_per_line*(bc->ymax-bc->ymin+1);
while ( pt<end )
*pt++ *= factor;
}
BDFChar *BDFPieceMeal(BDFFont *bdf, int index) {
SplineChar *sc;
if ( index<0 )
return( NULL );
if ( bdf->glyphcnt<bdf->sf->glyphcnt ) {
if ( bdf->glyphmax<bdf->sf->glyphcnt )
bdf->glyphs = realloc(bdf->glyphs,(bdf->glyphmax = bdf->sf->glyphmax)*sizeof(BDFChar *));
memset(bdf->glyphs+bdf->glyphcnt,0,(bdf->glyphmax-bdf->glyphcnt)*sizeof(SplineChar *));
bdf->glyphcnt = bdf->sf->glyphcnt;
}
if ( index >= bdf->glyphcnt )
return( NULL );
sc = bdf->sf->glyphs[index];
if ( sc==NULL )
return(NULL);
if ( bdf->freetype_context )
bdf->glyphs[index] = SplineCharFreeTypeRasterize(bdf->freetype_context,
sc->orig_pos,bdf->ptsize,bdf->dpi,bdf->clut?8:1);
else if ( bdf->recontext_freetype ) {
void *ft_context = FreeTypeFontContext(bdf->sf,sc,NULL,bdf->layer);
if ( ft_context!=NULL ) {
bdf->glyphs[index] = SplineCharFreeTypeRasterize(ft_context,
sc->orig_pos,bdf->ptsize,bdf->dpi,bdf->clut?8:1);
FreeTypeFreeContext(ft_context);
}
} else if ( bdf->unhinted_freetype )
bdf->glyphs[index] = SplineCharFreeTypeRasterizeNoHints(sc,
bdf->layer,bdf->ptsize,bdf->dpi,bdf->clut?4:1);
else
bdf->glyphs[index] = NULL;
if ( bdf->glyphs[index]==NULL ) {
if ( bdf->clut ) {
bdf->glyphs[index] = SplineCharAntiAlias(sc,bdf->layer,bdf->truesize,4);
if ( bdf->freetype_context!=NULL || bdf->recontext_freetype || bdf->unhinted_freetype )
ByteMult(bdf->glyphs[index],17); /* Internal rasterizer uses bit depth 4, but font expects 8. Correct by multiplying each byte by 17 */
} else
bdf->glyphs[index] = SplineCharRasterize(sc,bdf->layer,bdf->truesize);
}
return( bdf->glyphs[index] );
}
BDFChar *BDFPieceMealCheck(BDFFont *bdf, int index) {
if ( index<0 )
return( NULL );
if ( index>=bdf->glyphcnt )
return(BDFPieceMeal(bdf,index));
if ( bdf->glyphs[index]!=NULL )
return( bdf->glyphs[index]);
return(BDFPieceMeal(bdf,index));
}
/* Piecemeal fonts are used as the display font in the fontview, metricsview and other places*/
/* as such they are simple fonts (ie. we only display the current cid subfont) */
BDFFont *SplineFontPieceMeal(SplineFont *sf,int layer,int ptsize,int dpi,
int flags,void *ftc) {
BDFFont *bdf = calloc(1,sizeof(BDFFont));
real scale;
int pixelsize = (int) rint( (ptsize*dpi)/72.0 );
int truesize = pixelsize;
if ( flags&pf_bbsized ) {
DBounds bb;
if ( sf->multilayer )
/* Quick bounds doesn't handle clipping paths, and we want that */
/* handled for type3 */
SplineFontFindBounds(sf,&bb);
else
SplineFontQuickConservativeBounds(sf,&bb);
if ( bb.maxy<sf->ascent ) bb.maxy = sf->ascent;
if ( bb.miny>-sf->descent ) bb.miny = -sf->descent;
/* Ignore absurd values */
if ( bb.maxy>10*(sf->ascent+sf->descent) ) bb.maxy = 2*(sf->ascent+sf->descent);
if ( bb.maxx>10*(sf->ascent+sf->descent) ) bb.maxx = 2*(sf->ascent+sf->descent);
if ( bb.miny<-10*(sf->ascent+sf->descent) ) bb.miny = -2*(sf->ascent+sf->descent);
if ( bb.minx<-10*(sf->ascent+sf->descent) ) bb.minx = -2*(sf->ascent+sf->descent);
scale = pixelsize/ (real) (bb.maxy-bb.miny);
bdf->ascent = rint(bb.maxy*scale);
} else {
real ascent = sf->pfminfo.os2_typoascent;
real descent = sf->pfminfo.os2_typodescent;
ascent += sf->pfminfo.typoascent_add ? sf->ascent : 0;
descent -= sf->pfminfo.typodescent_add ? sf->descent: 0;
// 1.2 is just an arbitrary value to make the glyph look less tightly fit
// in the font view.
ascent *= 1.2;
descent *= 1.2;
scale = pixelsize / (real) (ascent - descent);
bdf->ascent = rint (ascent * scale);
}
truesize = rint ((sf->ascent + sf->descent) * scale);
if (pixelsize != 0)
ptsize = rint (ptsize * (double) truesize / pixelsize);
if ( flags&pf_ft_nohints )
{
printf("SplineFontPieceMeal() going unhinted...\n");
bdf->unhinted_freetype = true;
}
else if ( flags&pf_ft_recontext )
bdf->recontext_freetype = true;
bdf->sf = sf;
bdf->layer = layer;
bdf->glyphcnt = bdf->glyphmax = sf->glyphcnt;
bdf->pixelsize = pixelsize;
bdf->truesize = truesize;
bdf->ptsize = ptsize;
bdf->dpi = dpi;
bdf->glyphs = calloc(sf->glyphcnt,sizeof(BDFChar *));
bdf->descent = pixelsize-bdf->ascent;
bdf->piecemeal = true;
bdf->bbsized = (flags&pf_bbsized)?1:0;
bdf->res = -1;
bdf->freetype_context = ftc;
if ( ftc==NULL && (bdf->recontext_freetype || bdf->unhinted_freetype) &&
!hasFreeType())
{
printf("WARNING: SplineFontPieceMeal() turning off unhinted_freetype\n");
bdf->recontext_freetype = bdf->unhinted_freetype = false;
}
if ( (ftc || bdf->recontext_freetype || bdf->recontext_freetype) && (flags&pf_antialias) )
BDFClut(bdf,16);
else if ( flags&pf_antialias )
BDFClut(bdf,4);
return( bdf );
}
void BDFCharFree(BDFChar *bdfc) {
BDFRefChar *head, *cur;
if ( bdfc==NULL )
return;
for ( head = bdfc->refs; head != NULL; ) {
cur = head; head = head->next; free( cur );
}
free(bdfc->bitmap);
chunkfree(bdfc,sizeof(BDFChar));
}
void BDFPropsFree(BDFFont *bdf) {
int i;
for ( i=0; i<bdf->prop_cnt; ++i ) {
free(bdf->props[i].name);
if ( (bdf->props[i].type&~prt_property)==prt_string ||
(bdf->props[i].type&~prt_property)==prt_atom )
free(bdf->props[i].u.str);
}
free( bdf->props );
}
void BDFFontFree(BDFFont *bdf) {
int i;
if ( bdf==NULL )
return;
for ( i=0; i<bdf->glyphcnt; ++i )
BDFCharFree(bdf->glyphs[i]);
free(bdf->glyphs);
free(bdf->clut);
if ( bdf->freetype_context!=NULL )
FreeTypeFreeContext(bdf->freetype_context);
BDFPropsFree(bdf);
free( bdf->foundry );
free(bdf);
}
Jump to Line
Something went wrong with that request. Please try again.