2 * Handling of the bitmapped glyphs
4 * Copyright (c) 2001 by the TTF2PT1 project
5 * Copyright (c) 2001 by Sergey Babkin
7 * see COPYRIGHT for the full copyright notice
16 /* possible values of limits */
17 #define L_NONE 0 /* nothing here */
18 #define L_ON 1 /* black is on up/right */
19 #define L_OFF 2 /* black is on down/left */
21 static int warnedhints = 0;
25 #include <autotrace/autotrace.h>
28 * Produce an autotraced outline from a bitmap.
29 * scale - factor to scale the sizes
30 * bmap - array of dots by lines, xsz * ysz
31 * xoff, yoff - offset of the bitmap's lower left corner
32 * from the logical position (0,0)
36 autotraced_bmp_outline(
47 at_splines_type *atsp;
48 at_fitting_opts_type *atoptsp;
49 at_spline_list_type *slp;
56 fscale = (double)scale;
58 /* provide a white margin around the bitmap */
59 xbmap = malloc((ysz+2)*(xsz+2));
61 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
64 memset(xbmap, 0, xsz+2); /* top margin */
65 for(i=0, j=xsz+2; i<ysz; i++, j+=xsz+2) {
66 xbmap[j] = 0; /* left margin */
67 memcpy(&xbmap[j+1], &bmap[xsz*(ysz-1-i)], xsz); /* a line of bitmap */
68 xbmap[j+xsz+1] = 0; /* right margin */
70 memset(xbmap+j, 0, xsz+2); /* bottom margin */
71 xoff--; yoff-=2; /* compensate for the margins */
73 atoptsp = at_fitting_opts_new();
80 atsp = at_splines_new(&atb, atoptsp);
83 for(i=0; i<atsp->length; i++) {
86 fprintf(stderr, "%s: contour %d: %d entries clockwise=%d color=%02X%02X%02X\n",
87 g->name, i, slp->length, slp->clockwise, slp->color.r, slp->color.g, slp->color.b);
92 if(slp->color.r + slp->color.g + slp->color.b == 0)
95 fg_rmoveto(g, fscale*(slp->data[0].v[0].x+xoff), fscale*(slp->data[0].v[0].y+yoff));
96 for(j=0; j<slp->length; j++) {
100 fprintf(stderr, "(%g %g) ",
101 fscale*(slp->data[j].v[k].x+xoff),
102 fscale*(ysz-slp->data[j].v[k].y+yoff)
104 fprintf(stderr, "\n");
107 fscale*(slp->data[j].v[1].x+xoff), fscale*(slp->data[j].v[1].y+yoff),
108 fscale*(slp->data[j].v[2].x+xoff), fscale*(slp->data[j].v[2].y+yoff),
109 fscale*(slp->data[j].v[3].x+xoff), fscale*(slp->data[j].v[3].y+yoff) );
114 at_splines_free(atsp);
115 at_fitting_opts_free(atoptsp);
119 #endif /*USE_AUTOTRACE*/
121 /* an extension of gentry for description of the fragments */
122 typedef struct gex_frag GEX_FRAG;
124 /* indexes to len, the exact values and order are important */
125 #define GEXFI_NONE -1
126 #define GEXFI_CONVEX 0
127 #define GEXFI_CONCAVE 1
128 #define GEXFI_LINE 2 /* a line with steps varying by +-1 pixel */
129 #define GEXFI_EXACTLINE 3 /* a line with exactly the same steps */
130 #define GEXFI_COUNT 4 /* maximal index + 1 */
131 unsigned short len[GEXFI_COUNT]; /* length of various fragment types starting here */
132 unsigned short lenback[GEXFI_COUNT]; /* length back to the start of curve */
134 signed char ixstart; /* index of the frag type that starts here */
135 signed char ixcont; /* index of the frag type that continues here */
138 #define GEXFF_HLINE 0x0001 /* the exact line is longer in "horizontal" dimension */
139 #define GEXFF_EXTR 0x0002 /* this gentry is an extremum in some direction */
140 #define GEXFF_CIRC 0x0004 /* the joint at this gentry is for a circular curve */
141 #define GEXFF_DRAWCURVE 0x0008 /* vect[] describes a curve to draw */
142 #define GEXFF_DRAWLINE 0x0010 /* vect[] describes a line to draw */
143 #define GEXFF_SPLIT 0x0020 /* is a result of splitting a line */
144 #define GEXFF_SYMNEXT 0x0040 /* this subfrag is symmetric with next one */
145 #define GEXFF_DONE 0x0080 /* this subfrag has been vectorized */
146 #define GEXFF_LONG 0x0100 /* this gentry is longer than 1 pixel */
148 unsigned short aidx; /* index of gentry in the array representing the contour */
150 unsigned short vectlen; /* number of gentries represented by vect[] */
152 /* coordinates for vectored replacement of this fragment */
153 /* (already scaled because it's needed for curve approximation) */
154 double vect[4 /*ref.points*/][2 /*X,Y*/];
156 double bbox[2 /*X,Y*/]; /* absolute sizes of bounding box of a subfragment */
158 /* used when splitting the curved frags into subfrags */
159 GENTRY *prevsub; /* to gentries describing neighboring subfrags */
161 GENTRY *ordersub; /* single-linked list describing the order of calculation */
163 int sublen; /* length of this subfrag */
164 /* the symmetry across the subfrags */
165 int symaxis; /* the symmetry axis, with next subfrag */
166 int symxlen; /* min length of adjacent symmetric frags */
167 /* the symmetry within this subfrag (the axis is always diagonal) */
168 GENTRY *symge; /* symge->i{x,y}3 is the symmetry point of symge==0 if none */
171 #define X_FRAG(ge) ((GEX_FRAG *)((ge)->ext))
173 /* various interesting tables related to GEX_FRAG */
174 static char *gxf_name[GEXFI_COUNT] = {"Convex", "Concave", "Line", "ExLine"};
175 static int gxf_cvk[2] = {-1, 1}; /* coefficients of concaveness */
178 * Dump the contents of X_EXT()->len and ->lenback for a contour
188 for(j = 0; j < GEXFI_COUNT; j++) {
189 fprintf(stderr, "%-8s", gxf_name[j]);
190 for(i = 0; i < clen; i++, ge = ge->frwd)
191 fprintf(stderr, " %2d", X_FRAG(ge)->len[j]);
192 fprintf(stderr, " %p\n (back) ", ge);
193 for(i = 0; i < clen; i++, ge = ge->frwd)
194 fprintf(stderr, " %2d", X_FRAG(ge)->lenback[j]);
195 fprintf(stderr, "\n");
200 * Calculate values of X_EXT()->lenback[] for all entries in
201 * a contour. The contour is identified by:
202 * ge - any gentry of the contour
203 * clen - contour length
215 int len[GEXFI_COUNT]; /* length of the most recent fragment */
216 int count[GEXFI_COUNT]; /* steps since beginning of the fragment */
218 for(j = 0; j < GEXFI_COUNT; j++)
219 len[j] = count[j] = 0;
222 for(i = 0; i < end; i++, ge = ge->frwd) {
224 for(j = 0; j < GEXFI_COUNT; j++) {
225 if(len[j] != count[j]) {
226 f->lenback[j] = count[j]++;
231 count[j] = 1; /* start with the next gentry */
232 /* if the fragment will wrap over the start, process to its end */
233 if(i < clen && i + len[j] > end)
238 gex_dump_contour(ge, clen);
241 /* Limit a curve to not exceed the given coordinates
247 double curve[4][2 /*X,Y*/],
248 double lim[2 /*X,Y*/],
249 int where /* 0 - start, 3 - end */
252 int other = 3-where; /* the other end */
253 int sgn[2 /*X,Y*/]; /* sign for comparison */
254 double t, from, to, nt, t2, nt2, tt[4];
255 double val[2 /*X,Y*/];
259 sgn[i] = fsign(curve[other][i] - curve[where][i]);
262 fprintf(stderr, " limit (%g,%g)-(%g,%g) at %d by (%g,%g), sgn(%d,%d)\n",
263 curve[0][0], curve[0][1], curve[3][0], curve[3][1],
264 where, lim[0], lim[1], sgn[0], sgn[1]);
266 /* a common special case */
267 if( sgn[0]*(curve[where][0] - lim[0]) >= 0.
268 && sgn[1]*(curve[where][1] - lim[1]) >= 0. )
269 return; /* nothing to do */
279 fprintf(stderr, "t=");
281 while( fabs(from-to) > 0.00001 ) {
291 val[i] = curve[0][i]*tt[0] + curve[1][i]*tt[1]
292 + curve[2][i]*tt[2] + curve[3][i]*tt[3];
294 fprintf(stderr, "%g(%g,%g) ", t, val[0], val[1]);
296 if(fabs(val[0] - lim[0]) < 0.1
297 || fabs(val[1] - lim[1]) < 0.1)
300 if(sgn[0] * (val[0] - lim[0]) < 0.
301 || sgn[1] * (val[1] - lim[1]) < 0.)
306 /* now t is the point of splitting */
307 #define SPLIT(pt1, pt2) ( (pt1) + t*((pt2)-(pt1)) ) /* order is important! */
309 double v11, v12, v13, v21, v22, v31; /* intermediate points */
311 v11 = SPLIT(curve[0][i], curve[1][i]);
312 v12 = SPLIT(curve[1][i], curve[2][i]);
313 v13 = SPLIT(curve[2][i], curve[3][i]);
314 v21 = SPLIT(v11, v12);
315 v22 = SPLIT(v12, v13);
316 v31 = SPLIT(v21, v22);
320 curve[3][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31;
322 curve[0][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31;
329 fprintf(stderr, "\n");
334 * Vectorize a subfragment of a curve fragment. All the data has been already
335 * collected by this time
341 int fti, /* fragment type index */
342 GENTRY *firstge, /* first gentry of fragment */
343 GENTRY *ge, /* first gentry of subfragment */
347 GENTRY *gel, *gei; /* last gentry of this subfrag */
348 GEX_FRAG *f, *ff, *lf, *pf, *xf;
349 /* maximal amount of space that can be used at the beginning and the end */
350 double fixfront[2], fixend[2]; /* fixed points - used to show direction */
351 double mvfront[2], mvend[2]; /* movable points */
352 double limfront[2], limend[2]; /* limit of movement for movabel points */
354 int outfront, outend; /* the beginning/end is going outwards */
355 int symfront, symend; /* a ready symmetric fragment is present at front/end */
356 int drnd[2 /*X,Y*/]; /* size of the round part */
357 int i, j, a1, a2, ndots;
358 double avg2, max2; /* squared distances */
359 struct dot_dist *dots, *usedots;
361 ff = X_FRAG(firstge);
366 pf = X_FRAG(f->prevsub);
371 drnd[i] = gel->bkwd->ipoints[i][2] - ge->ipoints[i][2];
373 if(f->prevsub==0 && f->ixcont == GEXFI_NONE) {
374 /* nothing to join with : may use the whole length */
375 for(i = 0; i < 2; i++)
376 limfront[i] = ge->bkwd->ipoints[i][2];
378 /* limit to a half */
379 for(i = 0; i < 2; i++)
380 limfront[i] = 0.5 * (ge->ipoints[i][2] + ge->bkwd->ipoints[i][2]);
382 if( (ge->ix3 == ge->bkwd->ix3) /* vert */
383 ^ (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3))
384 ^ (fti == GEXFI_CONCAVE) /* counter-clockwise */ ) {
385 /* the beginning is not a flat 90-degree end */
387 for(i = 0; i < 2; i++)
388 fixfront[i] = ge->frwd->ipoints[i][2];
391 for(i = 0; i < 2; i++)
392 fixfront[i] = ge->ipoints[i][2];
395 if(lf->nextsub==0 && lf->ixstart == GEXFI_NONE) {
396 /* nothing to join with : may use the whole length */
397 for(i = 0; i < 2; i++)
398 limend[i] = gel->ipoints[i][2];
400 /* limit to a half */
401 for(i = 0; i < 2; i++)
402 limend[i] = 0.5 * (gel->ipoints[i][2] + gel->bkwd->ipoints[i][2]);
404 gei = gel->bkwd->bkwd;
405 if( (gel->ix3 == gel->bkwd->ix3) /* vert */
406 ^ (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3))
407 ^ (fti == GEXFI_CONVEX) /* clockwise */ ) {
408 /* the end is not a flat 90-degree end */
410 for(i = 0; i < 2; i++)
411 fixend[i] = gel->bkwd->bkwd->ipoints[i][2];
414 for(i = 0; i < 2; i++)
415 fixend[i] = gel->bkwd->ipoints[i][2];
418 for(i = 0; i < 2; i++) {
419 fixfront[i] *= fscale;
420 limfront[i] *= fscale;
425 fprintf(stderr, " %d out(%d[%d %d %d],%d[%d %d %d]) drnd(%d, %d)\n",
428 (ge->ix3 == ge->bkwd->ix3),
429 (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3)),
430 (fti == GEXFI_CONCAVE),
432 (gel->ix3 == gel->bkwd->ix3),
433 (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3)),
434 (fti == GEXFI_CONVEX),
437 /* prepare to calculate the distances */
438 ndots = f->sublen - 1;
439 dots = malloc(sizeof(*dots) * ndots);
441 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
444 for(i = 0, gei = ge; i < ndots; i++, gei = gei->frwd) {
445 for(a1 = 0; a1 < 2; a1++)
446 dots[i].p[a1] = fscale * gei->ipoints[a1][2];
449 /* see if we can mirror a ready symmetric curve */
451 /* check symmetry with the fragment before this */
452 symfront = (pf != 0 && (pf->flags & GEXFF_SYMNEXT) && (pf->flags & GEXFF_DONE)
453 && ( outend && f->sublen <= pf->sublen
454 || ( pf->sublen == f->sublen
456 || ( abs(limfront[0]-limend[0]) >= abs(pf->vect[0][0]-pf->vect[3][0])
457 && abs(limfront[1]-limend[1]) >= abs(pf->vect[0][1]-pf->vect[3][1]) ))
461 /* check symmetry with the fragment after this */
462 symend = ( (f->flags & GEXFF_SYMNEXT) && (lf->flags & GEXFF_DONE)
463 && ( outfront && f->sublen <= lf->sublen
464 || ( lf->sublen == f->sublen
466 || ( abs(limfront[0]-limend[0]) >= abs(lf->vect[0][0]-lf->vect[3][0])
467 && abs(limfront[1]-limend[1]) >= abs(lf->vect[0][1]-lf->vect[3][1]) ))
471 if(symfront || symend) {
472 /* mirror the symmetric neighbour subfrag */
474 a1 = (ge->ix3 != ge->bkwd->ix3); /* the symmetry axis */
475 a2 = !a1; /* the other axis (goes along the extremum gentry) */
477 /* the symmetry point on a2 */
478 sympt = fscale * 0.5 * (ge->ipoints[a2][2] + ge->bkwd->ipoints[a2][2]);
479 xf = pf; /* the symmetric fragment */
481 a1 = (gel->ix3 != gel->bkwd->ix3); /* the symmetry axis */
482 a2 = !a1; /* the other axis (goes along the extremum gentry) */
484 /* the symmetry point on a2 */
485 sympt = fscale * 0.5 * (gel->ipoints[a2][2] + gel->bkwd->ipoints[a2][2]);
486 xf = lf; /* the symmetric fragment */
488 fprintf(stderr, " sym with %p f=%d(%p) e=%d(%p) a1=%c a2=%c sympt=%g\n",
489 xf, symfront, pf, symend, lf,
490 a1 ? 'Y': 'X', a2 ? 'Y': 'X', sympt
493 f->vect[3-i][a1] = xf->vect[i][a1];
494 f->vect[3-i][a2] = sympt - (xf->vect[i][a2]-sympt);
497 if(outend || lf->sublen==0)
498 limcurve(f->vect, limend, 3);
500 if(outfront || pf == 0)
501 limcurve(f->vect, limfront, 0);
503 avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2);
504 fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale);
505 if(max2 <= fscale*fscale) {
506 f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE);
507 f->vectlen = f->sublen;
513 if( !outfront && !outend && f->symge != 0) {
514 /* a special case: try a circle segment as an approximation */
515 double lenfront, lenend, len, maxlen;
517 /* coefficient for a Bezier approximation of a circle */
518 #define CIRCLE_FRAC 0.55
520 a1 = (ge->ix3 == ge->bkwd->ix3); /* get the axis along the front */
521 a2 = !a1; /* axis along the end */
523 lenfront = fixfront[a1] - limfront[a1];
524 lenend = fixend[a2] - limend[a2];
525 if(fabs(lenfront) < fabs(lenend))
526 len = fabs(lenfront);
530 /* make it go close to the round shape */
537 maxlen = fscale * 2.;
540 maxlen = fscale * abs(ge->frwd->frwd->ipoints[a1][2]
541 - ge->ipoints[a1][2]);
547 mvfront[a1] = fixfront[a1] - fsign(lenfront) * len;
548 mvfront[a2] = limfront[a2];
549 mvend[a2] = fixend[a2] - fsign(lenend) * len;
550 mvend[a1] = limend[a1];
553 f->vect[0][i] = mvfront[i];
554 f->vect[3][i] = mvend[i];
556 f->vect[1][a1] = mvfront[a1] + CIRCLE_FRAC*(mvend[a1]-mvfront[a1]);
557 f->vect[1][a2] = mvfront[a2];
558 f->vect[2][a1] = mvend[a1];
559 f->vect[2][a2] = mvend[a2] + CIRCLE_FRAC*(mvfront[a2]-mvend[a2]);
561 avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2);
562 fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale);
563 if(max2 <= fscale*fscale) {
564 f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE);
565 f->vectlen = f->sublen;
572 f->vect[0][i] = limfront[i];
573 f->vect[1][i] = fixfront[i];
574 f->vect[2][i] = fixend[i];
575 f->vect[3][i] = limend[i];
583 if( fcrossrayscv(f->vect, NULL, NULL) == 0) {
584 fprintf(stderr, "**** Internal error: rays must cross but don't at %p-%p\n",
586 fprintf(stderr, " (%g, %g) (%g, %g) (%g, %g) (%g, %g)\n",
587 limfront[0], limfront[1],
588 fixfront[0], fixfront[1],
589 fixend[0], fixend[1],
592 dumppaths(g, NULL, NULL);
596 fapproxcurve(f->vect, usedots, ndots);
597 f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE);
598 f->vectlen = f->sublen;
604 * Produce an outline from a bitmap.
605 * scale - factor to scale the sizes
606 * bmap - array of dots by lines, xsz * ysz
607 * xoff, yoff - offset of the bitmap's lower left corner
608 * from the logical position (0,0)
622 char *hlm, *vlm; /* arrays of the limits of outlines */
623 char *amp; /* map of ambiguous points */
633 autotraced_bmp_outline(g, scale, bmap, xsz, ysz, xoff, yoff);
636 #endif /*USE_AUTOTRACE*/
638 fscale = (double)scale;
639 g->flags &= ~GF_FLOAT; /* build it as int first */
643 if(hints && subhints) {
644 WARNING_2 fprintf(stderr,
645 "Use of hint substitution on bitmap fonts is not recommended\n");
650 printbmap(bmap, xsz, ysz, xoff, yoff);
653 /* now find the outlines */
654 hlm=calloc( xsz, ysz+1 ); /* horizontal limits */
655 vlm=calloc( xsz+1, ysz ); /* vertical limits */
656 amp=calloc( xsz, ysz ); /* ambiguous points */
658 if(hlm==0 || vlm==0 || amp==0) {
659 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
664 * hlm and vlm represent a grid of horisontal and
665 * vertical lines. Each pixel is surrounded by the grid
666 * from all the sides. The values of [hv]lm mark the
667 * parts of grid where the pixel value switches from white
671 /* find the horizontal limits */
674 for(x=0; x<xsz; x++) {
680 for(y=1; y<ysz; y++) {
681 for(x=0; x<xsz; x++) {
695 for(x=0; x<xsz; x++) {
700 /* find the vertical limits */
702 for(y=0; y<ysz; y++) {
705 for(x=1; x<xsz; x++) {
720 * Ambiguous points are the nodes of the grids
721 * that are between two white and two black pixels
722 * located in a checkerboard style. Actually
723 * there are only two patterns that may be
724 * around an ambiguous point:
730 * where "|" and "-" represent the grid (respectively members
731 * of vlm and hlm), "*" represents an ambiguous point
732 * and "X" and "." represent black and white pixels.
734 * If these sample pattern occur in the lower left corner
735 * of the bitmap then this ambiguous point will be
736 * located at amp[1][1] or in other words amp[1*xsz+1].
738 * These points are named "ambiguous" because it's
739 * not easy to guess what did the font creator mean
740 * at these points. So we are going to treat them
741 * specially, doing no aggressive smoothing.
744 /* find the ambiguous points */
745 for(y=ysz-1; y>0; y--)
746 for(x=xsz-1; x>0; x--) {
748 if( !bmap[y*xsz+x-1] && !bmap[y*xsz-xsz+x] && bmap[y*xsz-xsz+x-1] )
751 if( bmap[y*xsz+x-1] && bmap[y*xsz-xsz+x] && !bmap[y*xsz-xsz+x-1] )
757 printlimits(hlm, vlm, amp, xsz, ysz);
760 /* generate the vectored (stepping) outline */
764 int outer; /* flag: this is an outer contour */
765 int hor, newhor; /* flag: the current contour direction is horizontal */
766 int dir; /* previous direction of the coordinate, 1 - L_ON, 0 - L_OFF */
767 int startx, starty; /* start of a contour */
768 int firstx, firsty; /* start of the current line */
769 int newx, newy; /* new coordinates to try */
773 for(y=ysz; !found && y>0; y--)
775 if(hlm[y*xsz+x] > L_NONE)
777 break; /* have no contours left */
780 ig_rmoveto(g, x+xoff, y+yoff); /* intermediate as int */
785 if(hlm[y*xsz+x] == L_OFF) {
787 hlm[y*xsz+x] = -hlm[y*xsz+x]; /* mark as seen */
792 vlm[y*(xsz+1)+x] = -vlm[y*(xsz+1)+x]; /* mark as seen */
795 while(x!=startx || y!=starty) {
797 printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir);
800 /* initialization common for try1 and try2 */
802 lm = vlm; maxx = xsz+1; maxy = ysz; newhor = 0;
804 lm = hlm; maxx = xsz; maxy = ysz+1; newhor = 1;
806 xor = (outer ^ hor ^ dir);
809 /* first we try to change axis, to keep the
810 * contour as long as possible
814 if(!hor && (!outer ^ dir))
816 if(hor && (!outer ^ dir))
819 if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy)
827 printf("try 1, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx],
828 (newhor ? 'h':'v'), newx, newy);
830 if( lm[newy*maxx + newx] == val )
834 /* try to change the axis anyway */
837 if(!hor && (outer ^ dir))
839 if(hor && (outer ^ dir))
842 if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy)
850 printf("try 2, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx],
851 (newhor ? 'h':'v'), newx, newy);
853 if( lm[newy*maxx + newx] == val )
857 /* try to continue in the old direction */
860 lm = hlm; maxx = xsz; maxy = ysz+1;
862 lm = vlm; maxx = xsz+1; maxy = ysz;
871 if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy)
879 printf("try 3, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx],
880 (newhor ? 'h':'v'), newx, newy);
882 if( lm[newy*maxx + newx] == val )
886 fprintf(stderr, "**** Internal error in the contour detection code at (%d, %d)\n", x, y);
887 fprintf(stderr, "glyph='%s' outer=%d hor=%d dir=%d\n", g->name, outer, hor, dir);
892 if(hor != newhor) { /* changed direction, end the previous line */
893 ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */
894 firstx = x; firsty = y;
896 lm[newy*maxx + newx] = -lm[newy*maxx + newx];
905 printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir);
907 ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */
912 /* try to vectorize the curves and sloped lines in the bitmap */
914 GENTRY *ge, *pge, *cge, *loopge;
918 dumppaths(g, NULL, NULL);
920 /* allocate the extensions */
921 for(cge=g->entries; cge!=0; cge=cge->next) {
922 cge->ext = calloc(1, sizeof(GEX_FRAG) );
924 fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__);
929 for(cge=g->entries; cge!=0; cge=cge->next) {
930 if(cge->type != GE_MOVE)
933 /* we've found the beginning of a contour */
935 int d, vert, count, stepmore, delaystop;
936 int vdir, hdir, fullvdir, fullhdir, len;
937 int dx, dy, lastdx, lastdy;
938 int k1, k2, reversal, smooth, good;
939 int line[2 /*H,V*/], maxlen[2 /*H,V*/], minlen[2 /*H,V*/];
940 GENTRY **age; /* array of gentries in a contour */
941 int clen; /* contour length, size of ths array */
945 /* we know that all the contours start at the top-left corner,
946 * so at most it might be before/after the last element of
947 * the last/first fragment
952 if(ge->ix3 == pge->ix3) { /* a vertical line */
953 /* we want to start always from a horizontal line because
954 * then we always start from top and that is quaranteed to be a
955 * fragment boundary, so move the start point of the contour
957 pge->prev->next = pge->next;
958 pge->next->prev = pge->prev;
963 ge = pge; pge = ge->bkwd;
964 cge->ix3 = pge->ix3; cge->iy3 = pge->iy3;
967 /* fill the array of gentries */
969 for(ge = cge->next->frwd; ge != cge->next; ge = ge->frwd)
971 age = (GENTRY **)malloc(sizeof(*age) * clen);
976 X_FRAG(ge)->aidx = count++;
978 /* and by the way find the extremums */
980 if( isign(ge->frwd->ipoints[i][2] - ge->ipoints[i][2])
981 * isign(ge->bkwd->bkwd->ipoints[i][2] - ge->bkwd->ipoints[i][2]) == 1) {
982 X_FRAG(ge)->flags |= GEXFF_EXTR;
983 fprintf(stderr, " %s extremum at %p\n", (i?"vert":"hor"), ge);
985 if(abs(ge->ipoints[i][2] - ge->bkwd->ipoints[i][2]) > 1)
986 X_FRAG(ge)->flags |= GEXFF_LONG;
990 } while(ge != cge->next);
992 /* Find the convex and concave fragments, defined as:
993 * convex (clockwise): dy/dx <= dy0/dx0,
994 * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx > 0
995 * concave (counter-clockwise): dy/dx >= dy0/dx0,
996 * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx < 0
998 * Where dx and dy are measured between the end of this gentry
999 * and the start of the previous one (dx0 and dy0 are the same
1000 * thing calculated for the previous gentry and its previous one),
1001 * dxthis is between the end and begginning of this gentry.
1003 * A reversal is a situation when the curve changes its direction
1004 * along the x axis, so it passes through a momentary vertical
1007 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1009 pge = ge->bkwd; /* the beginning of the fragment */
1011 lastdx = pge->ix3 - pge->bkwd->bkwd->ix3;
1012 lastdy = pge->iy3 - pge->bkwd->bkwd->iy3;
1014 #define CHKCURVCONN(ge, msg) do { \
1015 dx = (ge)->ix3 - (ge)->bkwd->bkwd->ix3; \
1016 dy = (ge)->iy3 - (ge)->bkwd->bkwd->iy3; \
1018 fprintf(stderr, " %p: dx=%d dy=%d dx0=%d dy0=%d ", \
1019 (ge), dx, dy, lastdx, lastdy); \
1021 k1 = X_FRAG(ge)->flags; \
1022 k2 = X_FRAG((ge)->bkwd)->flags; \
1024 fprintf(stderr, "fl=%c%c%c%c ", \
1025 (k1 & GEXFF_EXTR) ? 'X' : '-', \
1026 (k1 & GEXFF_LONG) ? 'L' : '-', \
1027 (k2 & GEXFF_EXTR) ? 'X' : '-', \
1028 (k2 & GEXFF_LONG) ? 'L' : '-' \
1031 if( (k1 & GEXFF_EXTR) && (k2 & GEXFF_LONG) \
1032 || (k2 & GEXFF_EXTR) && (k1 & GEXFF_LONG) ) { \
1034 good = reversal = -1; /* for debugging */ \
1038 smooth = (abs(dx)==1 || abs(dy)==1); \
1039 good = (k1 - k2)*gxf_cvk[d] >= 0; \
1040 if(smooth && !good) { \
1041 reversal = (k1 == -k2 && abs((ge)->ix3 - (ge)->bkwd->ix3)==1 \
1042 && dy*dx*gxf_cvk[d] < 0); \
1047 fprintf(stderr, "k1=%d k2=%d pge=%p count=%d %s good=%d rev=%d\n", \
1048 k1, k2, pge, count, gxf_name[d], good, reversal); \
1055 if(smooth && (good || reversal) )
1058 /* can't continue */
1060 if(count >= 4) { /* worth remembering */
1061 fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count);
1064 X_FRAG(pge)->len[d] = count;
1073 lastdx = dx; lastdy = dy;
1075 } while(ge != cge->next);
1077 /* see if we can connect the last fragment to the first */
1080 if(smooth && (good || reversal) ) {
1081 /* -1 to avoid ge->bkwd being counted twice */
1082 if( X_FRAG(ge->bkwd)->len[d] >= 2 )
1083 count += X_FRAG(ge->bkwd)->len[d] - 1;
1084 else if(count == clen+1) {
1085 /* we are joining a circular (closed) curve, check whether it
1086 * can be joined at any point or whether it has a discontinuity
1087 * at the point where we join it now
1089 lastdx = dx; lastdy = dy;
1090 CHKCURVCONN(ge->frwd, 0);
1092 if(smooth && (good || reversal) ) {
1093 /* yes, the curve is truly a circular one and can be
1094 * joined at any point
1098 fprintf(stderr, " found a circular joint point at %p\n", pge);
1100 /* make sure that in a circular fragment we start from an extremum */
1101 while( ! (X_FRAG(pge)->flags & GEXFF_EXTR) )
1103 X_FRAG(pge)->flags |= GEXFF_CIRC;
1107 fprintf(stderr, " %s joined %p to %p count=%d bk_count=%d\n", gxf_name[d], pge, ge->bkwd,
1108 count, X_FRAG(ge->bkwd)->len[d] );
1110 X_FRAG(ge->bkwd)->len[d] = 0;
1112 X_FRAG(pge)->len[d] = count;
1114 if(count >= 4) { /* worth remembering */
1115 fprintf(stderr, " %s last frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count);
1120 /* do postprocessing */
1126 fprintf(stderr, " %p %s len=%d clen=%d\n", ge, gxf_name[d], len, clen);
1128 if(len < 3) /* get rid of the fragments that are too short */
1132 * drop the |_| - shaped fragments, leave alone the _| - shaped
1133 * (and even those only if not too short in pixels),
1134 * those left alone are further filtered later
1136 k1 = (ge->ix3 == ge->bkwd->ix3); /* axis of the start */
1137 if(isign(ge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2])
1138 != isign(ge->frwd->ipoints[k1][2] - ge->frwd->frwd->ipoints[k1][2])
1139 && abs(ge->frwd->frwd->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) > 2) {
1141 fprintf(stderr, " %s frag %p count=%d good shape\n",
1142 gxf_name[d], ge, count);
1146 } else if(len == clen+1)
1147 break; /* a closed fragment, nothing else interesting */
1148 else { /* only for open fragments */
1149 GENTRY *gem, *gex, *gei, *ges;
1151 ges = ge; /* the start entry */
1152 gem = age[(f->aidx + f->len[d])%clen]; /* entry past the end of the fragment */
1155 if( (ge->ix3 == ge->bkwd->ix3) /* vert */
1156 ^ (isign(ge->bkwd->ix3 - gei->ix3)==isign(ge->bkwd->iy3 - gei->iy3))
1157 ^ !(d == GEXFI_CONVEX) /* counter-clockwise */ ) {
1160 fprintf(stderr, " %p: %s potential spurious start\n", ge, gxf_name[d]);
1162 /* the beginning may be a spurious entry */
1164 gex = 0; /* the extremum closest to the beginning - to be found */
1165 for(gei = ge->frwd; gei != gem; gei = gei->frwd) {
1166 if(X_FRAG(gei)->flags & GEXFF_EXTR) {
1174 /* A special case: ignore the spurious ends on small curves that
1175 * either enclose an 1-pixel-wide extremum or are 1-pixel deep.
1176 * Any 5-or-less-pixel-long curve with extremum 2 steps away
1177 * qualifies for that.
1180 if(len <= 5 && gex == ge->frwd->frwd) {
1183 fprintf(stderr, " E");
1186 good = 1; /* assume that ge is not spurious */
1188 /* gei goes backwards, gex goes forwards from the extremum */
1190 /* i is the symmetry axis, j is the other axis (X=0 Y=1) */
1191 i = (gex->ix3 != gex->bkwd->ix3);
1193 for( ; gei!=ge && gex!=gem; gei=gei->bkwd, gex=gex->frwd) {
1194 if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2]
1195 || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]
1196 != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]
1198 good = 0; /* no symmetry - must be spurious */
1200 fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)",
1202 i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2],
1203 j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2],
1204 gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] );
1209 if(gex == gem) { /* oops, the other side is too short */
1212 fprintf(stderr, " X");
1215 if(good && gei == ge) {
1216 if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2])
1217 != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) {
1218 good = 0; /* oops, goes into another direction */
1220 fprintf(stderr, " D");
1225 if(!good) { /* it is spurious, drop it */
1227 fprintf(stderr, " %p: %s spurious start\n", ge, gxf_name[d]);
1232 X_FRAG(ges)->len[d] = len;
1236 gei = gem->bkwd->bkwd->bkwd;
1237 if( (gem->ix3 != gem->bkwd->ix3) /* gem->bkwd is vert */
1238 ^ (isign(gem->bkwd->ix3 - gei->ix3)==isign(gem->bkwd->iy3 - gei->iy3))
1239 ^ (d == GEXFI_CONVEX) /* clockwise */ ) {
1242 fprintf(stderr, " %p: %s potential spurious end\n", gem->bkwd, gxf_name[d]);
1244 /* the end may be a spurious entry */
1246 gex = 0; /* the extremum closest to the end - to be found */
1247 for(gei = gem->bkwd->bkwd; gei != ges->bkwd; gei = gei->bkwd) {
1248 if(X_FRAG(gei)->flags & GEXFF_EXTR) {
1256 good = 1; /* assume that gem->bkwd is not spurious */
1257 /* gei goes backwards, gex goes forwards from the extremum */
1259 /* i is the symmetry axis, j is the other axis (X=0 Y=1) */
1260 i = (gex->ix3 != gex->bkwd->ix3);
1262 for( ; gei!=ges->bkwd && gex!=gem->bkwd; gei=gei->bkwd, gex=gex->frwd) {
1263 if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2]
1264 || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]
1265 != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]
1267 good = 0; /* no symmetry - must be spurious */
1269 fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)",
1271 i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2],
1272 j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2],
1273 gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] );
1278 if(gei == ges->bkwd) { /* oops, the other side is too short */
1281 fprintf(stderr, " X");
1284 if(good && gex == gem->bkwd) {
1285 if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2])
1286 != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) {
1287 good = 0; /* oops, goes into another direction */
1289 fprintf(stderr, " D");
1293 if(!good) { /* it is spurious, drop it */
1295 fprintf(stderr, " %p: %s spurious end\n", gem->bkwd, gxf_name[d]);
1297 X_FRAG(ges)->len[d] = --len;
1301 X_FRAG(ges)->len[d] = 0;
1303 fprintf(stderr, " %p: %s frag discarded, too small now\n", ge, gxf_name[d]);
1307 if(ges == cge->next)
1308 break; /* went around the loop */
1310 ge = ges->frwd; /* don't look at this fragment twice */
1317 } while(ge != cge->next);
1320 /* Find the straight line fragments.
1321 * Even though the lines are sloped, they are called
1322 * "vertical" or "horizontal" according to their longer
1323 * dimension. All the steps in the shother dimension must
1324 * be 1 pixel long, all the steps in the longer dimension
1325 * must be within the difference of 1 pixel.
1327 for(d = GEXFI_LINE; d<= GEXFI_EXACTLINE; d++) {
1329 pge = ge->bkwd; /* the beginning of the fragment */
1336 hdir = isign(ge->ix3 - ge->bkwd->ix3);
1337 vdir = isign(ge->iy3 - ge->bkwd->iy3);
1340 /* at this point pge==ge->bkwd */
1341 /* account for the previous gentry, which was !vert */
1342 if(!vert) { /* prev was vertical */
1343 maxlen[0] = minlen[0] = 0;
1344 maxlen[1] = minlen[1] = abs(pge->iy3 - pge->bkwd->iy3);
1345 line[0] = (maxlen[1] == 1);
1348 fullvdir = isign(pge->iy3 - pge->bkwd->iy3);
1350 maxlen[0] = minlen[0] = abs(pge->ix3 - pge->bkwd->ix3);
1351 maxlen[1] = minlen[1] = 0;
1353 line[1] = (maxlen[0] == 1);
1354 fullhdir = isign(pge->ix3 - pge->bkwd->ix3);
1358 h = line[0]; /* remember the prevalent direction */
1360 fprintf(stderr, " %p: v=%d(%d) h=%d(%d) vl(%d,%d,%d) hl=(%d,%d,%d) %s count=%d ",
1361 ge, vdir, fullvdir, hdir, fullhdir,
1362 line[1], minlen[1], maxlen[1],
1363 line[0], minlen[0], maxlen[0],
1364 gxf_name[d], count);
1367 if(vdir != fullvdir)
1368 line[0] = line[1] = 0;
1369 len = abs(ge->iy3 - ge->bkwd->iy3);
1371 if(hdir != fullhdir)
1372 line[0] = line[1] = 0;
1373 len = abs(ge->ix3 - ge->bkwd->ix3);
1376 fprintf(stderr, "len=%d\n", len);
1378 if(len != 1) /* this is not a continuation in the short dimension */
1381 /* can it be a continuation in the long dimension ? */
1384 maxlen[vert] = minlen[vert] = len;
1385 else if(maxlen[vert]==minlen[vert]) {
1386 if(d == GEXFI_EXACTLINE) {
1387 if(len != maxlen[vert])
1388 line[vert] = 0; /* it can't */
1389 } else if(len < maxlen[vert]) {
1390 if(len < minlen[vert]-1)
1391 line[vert] = 0; /* it can't */
1395 if(len > maxlen[vert]+1)
1396 line[vert] = 0; /* it can't */
1400 } else if(len < minlen[vert] || len > maxlen[vert])
1401 line[vert] = 0; /* it can't */
1404 if(line[0] == 0 && line[1] == 0) {
1407 fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count);
1409 X_FRAG(pge)->len[d] = count;
1410 if(d == GEXFI_EXACTLINE && h) {
1411 X_FRAG(pge)->flags |= GEXFF_HLINE;
1416 stepmore = 1; /* may reconsider the 1st gentry */
1417 pge = ge = ge->bkwd;
1424 if(ge == cge->next && !stepmore)
1425 delaystop = 1; /* consider the first gentry again */
1426 } while(stepmore || ge != cge->next ^ delaystop);
1427 /* see if there is an unfinished line left */
1431 fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count);
1433 X_FRAG(ge->bkwd->bkwd)->len[d] = 0;
1434 X_FRAG(pge)->len[d] = count;
1438 /* do postprocessing of the lines */
1440 fprintf(stderr, "Line postprocessing\n");
1441 gex_dump_contour(cge->next, clen);
1444 /* the non-exact line frags are related to exact line frags sort
1445 * of like to individual gentries: two kinds of exact frags
1446 * must be interleaved, with one kind having the size of 3
1447 * and the other kind having the size varying within +-2.
1452 GEX_FRAG *pf, *lastf1, *lastf2;
1453 int len1, len2, fraglen;
1457 fraglen = f->len[GEXFI_LINE];
1460 vert = 0; /* vert is a pseudo-directon */
1461 line[0] = line[1] = 1;
1462 maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0;
1463 lastf2 = lastf1 = f;
1465 for(pge = ge, i = 1; i < fraglen; i++, pge=pge->frwd) {
1467 len = pf->len[GEXFI_EXACTLINE];
1469 fprintf(stderr, " pge=%p i=%d of %d ge=%p exLen=%d\n", pge, i,
1470 f->len[GEXFI_LINE], ge, len);
1476 vert = !vert; /* alternate the pseudo-direction */
1479 if(maxlen[vert] == 0)
1480 maxlen[vert] = minlen[vert] = len;
1481 else if(maxlen[vert]-2 >= len && minlen[vert]+2 <= len) {
1482 if(len > maxlen[vert])
1484 else if(len < minlen[vert])
1488 if(line[0] == 0 && line[1] == 0) {
1490 fprintf(stderr, " Line breaks at %p %c(%d, %d) %c(%d, %d) len=%d fl=%d l2=%d l1=%d\n",
1491 pge, (!vert)?'*':' ', minlen[0], maxlen[0],
1492 vert?'*':' ', minlen[1], maxlen[1], len, fraglen, len2, len1);
1494 if(lastf2 != lastf1) {
1495 lastf2->len[GEXFI_LINE] = len2-len1;
1497 lastf1->len[GEXFI_LINE] = len1+1;
1498 pf->len[GEXFI_LINE] = fraglen+1 - i;
1499 gex_dump_contour(pge, clen);
1501 /* continue with the line */
1502 vert = 0; /* vert is a pseudo-directon */
1503 line[0] = line[1] = 1;
1504 maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0;
1505 lastf2 = lastf1 = f;
1515 } while(ge != cge->next);
1517 gex_dump_contour(cge->next, clen);
1524 if(f->len[GEXFI_LINE] >= 4) {
1525 len = f->len[GEXFI_EXACTLINE];
1526 /* if a non-exact line covers precisely two exact lines,
1529 if(len > 0 && f->len[GEXFI_LINE] > len+1) {
1531 pge = age[(f->aidx + len - 1)%clen]; /* last gentry of exact line */
1533 if(f->len[GEXFI_LINE] + 1 == len + pf->len[GEXFI_EXACTLINE]) {
1534 f->len[GEXFI_LINE] = len;
1535 f->flags |= GEXFF_SPLIT;
1536 pf->len[GEXFI_LINE] = pf->len[GEXFI_EXACTLINE];
1537 pf->flags |= GEXFF_SPLIT;
1543 } while(ge != cge->next);
1548 /* too small lines are of no interest */
1549 if( (f->flags & GEXFF_SPLIT)==0 && f->len[GEXFI_LINE] < 4)
1550 f->len[GEXFI_LINE] = 0;
1552 len = f->len[GEXFI_EXACTLINE];
1553 /* too small exact lines are of no interest */
1554 if(len < 3) /* exact lines may be shorter */
1555 f->len[GEXFI_EXACTLINE] = 0;
1556 /* get rid of inexact additions to the end of the exact lines */
1557 else if(f->len[GEXFI_LINE] == len+1)
1558 f->len[GEXFI_LINE] = len;
1559 /* same at the beginning */
1561 int diff = X_FRAG(ge->bkwd)->len[GEXFI_LINE] - len;
1563 if(diff == 1 || diff == 2) {
1564 X_FRAG(ge->bkwd)->len[GEXFI_LINE] = 0;
1565 f->len[GEXFI_LINE] = len;
1570 } while(ge != cge->next);
1572 gex_dump_contour(cge->next, clen);
1575 gex_calc_lenback(cge->next, clen); /* prepare data */
1577 /* resolve conflicts between lines and curves */
1580 * the short (3-gentry) curve frags must have one of the ends
1581 * coinciding with another curve frag of the same type
1584 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1589 if(f->len[d] == 3) {
1590 pge = age[(f->aidx + 2)%clen]; /* last gentry of this frag */
1591 if(f->lenback[d] == 0 && X_FRAG(pge)->len[d] == 0) {
1592 fprintf(stderr, " discarded small %s at %p-%p\n", gxf_name[d], ge, pge);
1594 X_FRAG(ge->frwd)->lenback[d] = 0;
1595 X_FRAG(ge->frwd->frwd)->lenback[d] = 0;
1599 } while(ge != cge->next);
1603 * longer exact lines take priority over curves, shorter lines
1604 * and inexact lines are resolved with convex/concave conflicts
1610 len = f->len[GEXFI_EXACTLINE];
1612 if(len < 6) { /* line is short */
1617 fprintf(stderr, " line at %p len=%d\n", ge, f->len[GEXFI_EXACTLINE]);
1618 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1621 /* check if we overlap the end of some fragment */
1623 /* chop off the end of conflicting fragment */
1624 len = f->lenback[d];
1625 pge = age[(f->aidx + clen - len)%clen];
1627 if(pf->len[d] == clen+1 && pf->flags & GEXFF_CIRC) {
1628 /* the conflicting fragment is self-connected */
1631 /* calculate the new value for lenback */
1632 len = clen+1 - f->len[GEXFI_EXACTLINE];
1633 for(pge = ge; len > 0; pge = pge->bkwd, len--)
1634 X_FRAG(pge)->lenback[d] = len;
1635 /* now pge points to the last entry of the line,
1636 * which is also the new first entry of the curve
1638 X_FRAG(pge)->len[d] = clen+2 - f->len[GEXFI_EXACTLINE];
1639 /* clean lenback of gentries covered by the line */
1640 for(pge = ge->frwd, j = f->len[GEXFI_EXACTLINE]-1; j > 0; pge = pge->frwd, j--)
1641 X_FRAG(pge)->lenback[d] = 0;
1642 fprintf(stderr, " cut %s circular frag to %p-%p\n",
1643 gxf_name[d], pge, ge);
1644 gex_dump_contour(ge, clen);
1646 /* when we chop off a piece of fragment, we leave the remaining
1647 * piece(s) overlapping with the beginning and possibly the end
1648 * of the line fragment under consideration
1650 fprintf(stderr, " cut %s frag at %p from len=%d to len=%d (end %p)\n",
1651 gxf_name[d], pge, pf->len[d], len+1, ge);
1652 j = pf->len[d] - len - 1; /* how many gentries are chopped off */
1653 pf->len[d] = len + 1;
1654 i = f->len[GEXFI_EXACTLINE] - 1;
1655 for(pge = ge->frwd; j > 0 && i > 0; j--, i--, pge = pge->frwd)
1656 X_FRAG(pge)->lenback[d] = 0;
1657 gex_dump_contour(ge, clen);
1660 /* the conflicting fragment is split in two by this line
1661 * fragment, fix up its tail
1664 fprintf(stderr, " end of %s frag len=%d %p-",
1665 gxf_name[d], j+1, pge->bkwd);
1666 X_FRAG(pge->bkwd)->len[d] = j+1; /* the overlapping */
1667 for(i = 1; j > 0; j--, i++, pge = pge->frwd)
1668 X_FRAG(pge)->lenback[d] = i;
1669 fprintf(stderr, "%p\n", pge->bkwd);
1670 gex_dump_contour(ge, clen);
1674 /* check if we overlap the beginning of some fragments */
1675 i = f->len[GEXFI_EXACTLINE]-1; /* getntries remaining to consider */
1676 j = 0; /* gentries remaining in the overlapping fragment */
1677 for(pge = ge; i > 0; i--, pge = pge->frwd) {
1679 X_FRAG(pge)->lenback[d] = 0;
1682 /* the beginning of one fragment may be the end of another
1683 * fragment, in this case if j-- above results in 0, that will
1684 * cause it to check the same gentry for the beginning
1690 fprintf(stderr, " removed %s frag at %p len=%d\n",
1691 gxf_name[d], pge, j);
1692 gex_dump_contour(ge, clen);
1698 /* pge points at the last gentry of the line fragment */
1699 if(j > 1) { /* we have the tail of a fragment left */
1700 fprintf(stderr, " end of %s frag len=%d %p-",
1701 gxf_name[d], j, pge);
1702 X_FRAG(pge)->len[d] = j; /* the overlapping */
1703 for(i = 0; j > 0; j--, i++, pge = pge->frwd)
1704 X_FRAG(pge)->lenback[d] = i;
1705 fprintf(stderr, "%p\n", pge->bkwd);
1706 gex_dump_contour(ge, clen);
1708 X_FRAG(pge)->lenback[d] = 0;
1713 } while(ge != cge->next);
1716 * The exact lines take priority over curves that coincide
1717 * with them or extend by only one gentry on either side
1718 * (but not both sides). By this time it applies only to the
1719 * small exact lines.
1722 /* Maybe we should remove only exact coincidences ? */
1728 len = f->len[GEXFI_EXACTLINE];
1730 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1731 if(f->len[d] == len || f->len[d] == len+1) {
1733 fprintf(stderr, " removed %s frag at %p len=%d linelen=%d\n",
1734 gxf_name[d], ge, f->len[d], len);
1736 for(i = f->len[d]; i > 1; i--, pge = pge->frwd)
1737 X_FRAG(pge)->lenback[d] = 0;
1739 gex_dump_contour(ge, clen);
1740 } else if(X_FRAG(ge->bkwd)->len[d] == len+1) {
1741 fprintf(stderr, " removed %s frag at %p len=%d next linelen=%d\n",
1742 gxf_name[d], ge->bkwd, X_FRAG(ge->bkwd)->len[d], len);
1744 for(i = len; i > 0; i--, pge = pge->frwd)
1745 X_FRAG(pge)->lenback[d] = 0;
1746 X_FRAG(ge->bkwd)->len[d] = 0;
1747 gex_dump_contour(ge, clen);
1753 } while(ge != cge->next);
1756 * The lines may cover only whole curves (or otherwise empty space),
1757 * so cut them where they overlap parts of the curves. If 2 or less
1758 * gentries are left in the line, remove the line.
1759 * If a line and a curve fully coincide, remove the line. Otherwise
1760 * remove the curves that are completely covered by the lines.
1768 len = f->len[GEXFI_LINE];
1775 if(f->len[GEXFI_CONVEX] >= len
1776 || f->len[GEXFI_CONCAVE] >= len) {
1777 line_completely_covered:
1778 fprintf(stderr, " removed covered Line frag at %p len=%d\n",
1780 f->len[GEXFI_LINE] = 0;
1781 for(pge = ge->frwd; len > 1; len--, pge = pge->frwd)
1782 X_FRAG(pge)->lenback[GEXFI_LINE] = 0;
1783 gex_dump_contour(ge, clen);
1788 k1 = 0; /* how much to cut at the front */
1789 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1791 pge = age[(f->aidx + clen - f->lenback[d])%clen];
1792 i = X_FRAG(pge)->len[d] - f->lenback[d] - 1;
1798 k2 = 0; /* how much to cut at the end */
1799 pge = age[(f->aidx + len)%clen]; /* gentry after the end */
1800 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1801 i = X_FRAG(pge)->lenback[d] - 1;
1806 if(k1+k2 > 0 && k1+k2 >= len-3)
1807 goto line_completely_covered;
1809 if(k2 != 0) { /* cut the end */
1811 f->len[GEXFI_LINE] = len;
1812 /* pge still points after the end */
1813 for(i = k2, pge = pge->bkwd; i > 0; i--, pge = pge->bkwd)
1814 X_FRAG(pge)->lenback[GEXFI_LINE] = 0;
1816 if(k1 != 0) { /* cut the beginning */
1818 f->len[GEXFI_LINE] = 0;
1819 for(i = 1, pge = ge->frwd; i < k1; i++, pge = pge->frwd)
1820 X_FRAG(pge)->lenback[GEXFI_LINE] = 0;
1821 X_FRAG(pge)->len[GEXFI_LINE] = len;
1822 for(i = 0; i < len; i++, pge = pge->frwd)
1823 X_FRAG(pge)->lenback[GEXFI_LINE] = i;
1825 if(k1 != 0 || k2 != 0) {
1826 fprintf(stderr, " cut Line frag at %p by (%d,%d) to len=%d\n",
1828 gex_dump_contour(ge, clen);
1830 goto reconsider_line; /* the line may have to be cut again */
1832 pge = age[(f->aidx + k1)%clen]; /* new beginning */
1833 good = 1; /* flag: no need do do a debugging dump */
1834 for(i=1; i<len; i++, pge = pge->frwd)
1835 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1836 if(X_FRAG(pge)->len[d]) {
1837 fprintf(stderr, " removed %s frag at %p len=%d covered by line\n",
1838 gxf_name[d], pge, X_FRAG(pge)->len[d], len);
1841 X_FRAG(pge)->len[d] = 0;
1843 pge = age[(f->aidx + k1 + 1)%clen]; /* next after new beginning */
1844 for(i=1; i<len; i++, pge = pge->frwd)
1845 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++)
1846 X_FRAG(pge)->lenback[d] = 0;
1848 gex_dump_contour(ge, clen);
1851 } while(ge != cge->next);
1853 /* Resolve conflicts between curves */
1854 for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) {
1855 dx = (GEXFI_CONVEX + GEXFI_CONCAVE) - d; /* the other type */
1866 sge = ge; /* the start of fragment */
1869 if(i != 0) { /* two curved frags starting here */
1870 /* should be i!=len because otherwise they would be
1871 * covered by an exact line
1874 curve_completely_covered:
1875 /* remove the convex frag */
1876 fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n",
1877 gxf_name[d], ge, len, gxf_name[dx]);
1879 for(pge = ge->frwd, j = 1; j < len; j++, pge = pge->frwd)
1880 X_FRAG(pge)->lenback[d] = 0;
1881 gex_dump_contour(ge, clen);
1883 ge = ge->frwd; /* the frag is gone, nothing more to do */
1886 /* remove the concave frag */
1887 fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n",
1888 gxf_name[dx], ge, i, gxf_name[d]);
1890 for(pge = ge->frwd, j = 1; j < i; j++, pge = pge->frwd)
1891 X_FRAG(pge)->lenback[dx] = 0;
1892 gex_dump_contour(ge, clen);
1897 k1 = X_FRAG(ge->frwd)->lenback[dx];
1898 if(k1 != 0) { /* conflict at the front */
1899 GENTRY *gels, *gele, *gei;
1901 pge = age[(f->aidx + clen - (k1-1))%clen]; /* first gentry of concave frag */
1902 k2 = X_FRAG(pge)->len[dx]; /* its length */
1904 i = k2 - (k1-1); /* amount of overlap */
1907 /* i >= 2 by definition */
1908 if(i >= k2-1) { /* covers the other frag - maybe with 1 gentry showing */
1909 fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n",
1910 gxf_name[dx], pge, k2, gxf_name[d]);
1911 X_FRAG(pge)->len[dx] = 0;
1912 for(pge = pge->frwd, j = 1; j < k2; j++, pge = pge->frwd)
1913 X_FRAG(pge)->lenback[dx] = 0;
1914 if(i >= len-1) { /* covers our frag too - maybe with 1 gentry showing */
1915 /* our frag will be removed as well, prepare a line to replace it */
1917 gele = age[(f->aidx + i - 1)%clen];
1918 fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i);
1919 X_FRAG(gels)->len[GEXFI_LINE] = i;
1920 for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++)
1921 X_FRAG(gei)->lenback[GEXFI_LINE] = j;
1923 gex_dump_contour(ge, clen);
1928 if(i >= len-1) /* covers our frag - maybe with 1 gentry showing */
1929 goto curve_completely_covered;
1931 /* XXX need to do something better for the case when a curve frag
1932 * is actually nothing but an artifact of two other curves of
1933 * the opposite type touching each other, like on the back of "3"
1936 /* change the overlapping part to a line */
1938 gele = age[(f->aidx + i - 1)%clen];
1939 /* give preference to local extremums */
1940 if(X_FRAG(gels)->flags & GEXFF_EXTR) {
1944 if(X_FRAG(gele)->flags & GEXFF_EXTR) {
1948 if(gels->bkwd == gele) {
1949 /* Oops the line became negative. Probably should
1950 * never happen but I can't think of any formal reasoning
1951 * leading to that, so check just in case. Restore
1952 * the previous state.
1954 gels = gele; gele = gels->frwd; i = 2;
1957 j = X_FRAG(gels)->lenback[dx] + 1; /* new length */
1959 X_FRAG(pge)->len[dx] = j;
1960 fprintf(stderr, " cut %s frag at %p len=%d to %p len=%d end overlap with %s\n",
1961 gxf_name[dx], pge, k2, gels, j, gxf_name[d]);
1962 for(gei = gels->frwd; j < k2; gei = gei->frwd, j++)
1963 X_FRAG(gei)->lenback[dx] = 0;
1969 fprintf(stderr, " cut %s frag at %p len=%d ", gxf_name[d], ge, len);
1971 for(gei = ge->frwd; gei != gele; gei = gei->frwd, len--)
1972 X_FRAG(gei)->lenback[d] = 0;
1973 X_FRAG(gele)->len[d] = len;
1974 X_FRAG(gele)->lenback[d] = 0;
1975 fprintf(stderr, "to %p len=%d start overlap with %s\n",
1976 sge, len, gxf_name[dx]);
1977 for(gei = gei->frwd, j = 1; j < len; gei = gei->frwd, j++)
1978 X_FRAG(gei)->lenback[d] = j;
1982 fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i);
1983 X_FRAG(gels)->len[GEXFI_LINE] = i;
1984 for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++)
1985 X_FRAG(gei)->lenback[GEXFI_LINE] = j;
1987 gex_dump_contour(ge, clen);
1991 } while(ge != cge->next);
1995 * Assert that there are no conflicts any more and
1996 * for each gentry find the fragment types that start
1997 * and continue here.
2002 dx = GEXFI_NONE; /* type that starts here */
2003 dy = GEXFI_NONE; /* type that goes through here */
2004 for(d = GEXFI_CONVEX; d<= GEXFI_LINE; d++) {
2007 fprintf(stderr, "**** Internal error in vectorization\n");
2008 fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n",
2009 g->name, ge, gxf_name[dx], gxf_name[d]);
2010 dumppaths(g, cge->next, cge->next->bkwd);
2011 gex_dump_contour(ge, clen);
2018 fprintf(stderr, "**** Internal error in vectorization\n");
2019 fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n",
2020 g->name, ge, gxf_name[dy], gxf_name[d]);
2021 dumppaths(g, cge->next, cge->next->bkwd);
2022 gex_dump_contour(ge, clen);
2031 } while(ge != cge->next);
2034 * make sure that the contour does not start in the
2035 * middle of a fragment
2037 ge = cge->next; /* old start of the contour */
2039 if(f->ixstart == GEXFI_NONE && f->ixcont != GEXFI_NONE) {
2040 /* oops, it's mid-fragment, move the start */
2043 xge = ge->bkwd->next; /* entry following the contour */
2045 /* find the first gentry of this frag */
2046 pge = age[(f->aidx + clen - f->lenback[f->ixcont])%clen];
2048 ge->prev = ge->bkwd;
2049 ge->bkwd->next = ge;
2054 pge->bkwd->next = xge;
2056 xge->prev = pge->bkwd;
2058 cge->ix3 = pge->bkwd->ix3; cge->iy3 = pge->bkwd->iy3;
2061 /* vectorize each fragment separately */
2064 /* data for curves */
2065 GENTRY *firstge, *lastge, *gef, *gel, *gei, *gex;
2066 GENTRY *ordhd; /* head of the order list */
2068 int nsub; /* number of subfrags */
2069 GEX_FRAG *ff, *lf, *xf;
2072 switch(f->ixstart) {
2074 len = f->len[GEXFI_LINE];
2075 pge = age[(f->aidx + len - 1)%clen]; /* last gentry */
2077 if(ge->iy3 == ge->bkwd->iy3) { /* frag starts and ends horizontally */
2078 k1 = 1/*Y*/ ; /* across the direction of start */
2079 k2 = 0/*X*/ ; /* along the direction of start */
2080 } else { /* frag starts and ends vertically */
2081 k1 = 0/*X*/ ; /* across the direction of start */
2082 k2 = 1/*Y*/ ; /* along the direction of start */
2086 /* odd number of entries in the frag */
2087 double halfstep, halfend;
2089 f->vect[0][k1] = fscale * ge->ipoints[k1][2];
2090 f->vect[3][k1] = fscale * pge->ipoints[k1][2];
2092 halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2])
2093 * 0.5 / ((len+1)/2);
2094 if(f->ixcont != GEXFI_NONE) {
2095 halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5;
2096 if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */
2099 if(X_FRAG(pge)->ixstart != GEXFI_NONE) {
2100 halfend = (pge->ipoints[k2][2] - pge->bkwd->ipoints[k2][2]) * 0.5;
2101 if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */
2104 f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep);
2105 f->vect[3][k2] = fscale * (pge->ipoints[k2][2] - halfstep);
2107 /* even number of entries */
2108 double halfstep, halfend;
2110 f->vect[0][k1] = fscale * ge->ipoints[k1][2];
2111 halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2])
2113 if(f->ixcont != GEXFI_NONE) {
2114 halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5;
2115 if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */
2118 f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep);
2120 halfstep = (pge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2])
2122 if(X_FRAG(pge)->ixstart != GEXFI_NONE) {
2123 halfend = (pge->ipoints[k1][2] - pge->bkwd->ipoints[k1][2]) * 0.5;
2124 if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */
2127 f->vect[3][k1] = fscale * (pge->ipoints[k1][2] - halfstep);
2128 f->vect[3][k2] = fscale * pge->ipoints[k2][2];
2131 f->flags |= GEXFF_DRAWLINE;
2135 len = f->len[f->ixstart];
2137 lastge = age[(f->aidx + len - 1)%clen]; /* last gentry */
2144 xf->flags &= ~GEXFF_DONE;
2145 for(gei = firstge->frwd; gei != lastge; gei = gei->frwd) {
2147 if(X_FRAG(gei)->flags & GEXFF_EXTR) {
2150 xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]);
2155 xf->flags &= ~GEXFF_DONE;
2162 xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]);
2164 ff = xf; /* remember the beginning of the last subfrag */
2167 if(firstge != lastge) {
2171 /* correct the bounding box of the last and first subfrags for
2172 * intersections with other fragments
2174 if(xf->ixstart != GEXFI_NONE) {
2175 /* ff points to the beginning of the last subfrag */
2177 ff->bbox[i] -= 0.5 * abs(lastge->ipoints[i][2] - lastge->bkwd->ipoints[i][2]);
2179 ff = X_FRAG(firstge);
2180 if(ff->ixcont != GEXFI_NONE) {
2182 ff->bbox[i] -= 0.5 * abs(firstge->ipoints[i][2] - firstge->bkwd->ipoints[i][2]);
2186 fprintf(stderr, " %s frag %p%s nsub=%d\n", gxf_name[f->ixstart],
2187 ge, (f->flags&GEXFF_CIRC)?" circular":"", nsub);
2189 /* find the symmetry between the subfragments */
2190 for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) {
2196 ff->flags &= ~GEXFF_SYMNEXT;
2197 break; /* not a circular frag */
2199 good = 1; /* assume that we have symmetry */
2200 /* gei goes backwards, gex goes forwards from the extremum */
2202 /* i is the symmetry axis, j is the other axis (X=0 Y=1) */
2203 ff->symaxis = i = (gex->ix3 != gex->bkwd->ix3);
2205 for( ; gei!=gef && gex!=gel; gei=gei->bkwd, gex=gex->frwd) {
2206 if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2]
2207 || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]
2208 != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]
2210 good = 0; /* no symmetry */
2215 if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2])
2216 != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) {
2217 good = 0; /* oops, goes into another direction */
2221 ff->flags |= GEXFF_SYMNEXT;
2223 ff->flags &= ~GEXFF_SYMNEXT;
2226 for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) {
2228 if((ff->flags & GEXFF_SYMNEXT)==0) {
2233 if(gex == 0 || (X_FRAG(gex)->flags & GEXFF_SYMNEXT)==0) {
2237 ff->symxlen = X_FRAG(gex)->sublen;
2238 xf = X_FRAG(ff->nextsub);
2239 if(xf->sublen < ff->symxlen)
2240 ff->symxlen = xf->sublen;
2243 /* find the symmetry inside the subfragments */
2244 for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) {
2247 if(ff->sublen % 2) {
2248 /* we must have an even number of gentries for diagonal symmetry */
2253 /* gei goes forwards from the front */
2255 /* gex goes backwards from the back */
2256 gex = ff->nextsub->bkwd;
2258 /* i is the direction of gei, j is the direction of gex */
2259 i = (gei->iy3 != gei->bkwd->iy3);
2261 for( ; gei->bkwd != gex; gei=gei->frwd, gex=gex->bkwd) {
2262 if( abs(gei->bkwd->ipoints[i][2] - gei->ipoints[i][2])
2263 != abs(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) )
2264 break; /* no symmetry */
2268 if(gei->bkwd == gex)
2271 ff->symge = 0; /* no symmetry */
2274 /* find the order of calculation:
2275 * prefer to start from long fragments that have the longest
2276 * neighbours symmetric with them, with all being equal prefer
2277 * the fragments that have smaller physical size
2280 for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) {
2283 for(ordlast = &ordhd; *ordlast != 0; ordlast = &xf->ordersub) {
2284 xf = X_FRAG(*ordlast);
2285 if(ff->sublen > xf->sublen)
2287 if(ff->sublen < xf->sublen)
2289 if(ff->symxlen > xf->symxlen)
2291 if(ff->symxlen < xf->symxlen)
2293 if(ff->bbox[0] < xf->bbox[0] || ff->bbox[1] < xf->bbox[1])
2297 ff->ordersub = *ordlast;
2301 /* vectorize the subfragments */
2302 for(gef = ordhd; gef != 0; gef = ff->ordersub) {
2304 /* debugging stuff */
2306 fprintf(stderr, " %p-%p bbox[%g,%g] sym=%p %s len=%d xlen=%d\n",
2307 gef, ff->nextsub, ff->bbox[0], ff->bbox[1], ff->symge,
2308 (ff->flags & GEXFF_SYMNEXT) ? "symnext" : "",
2309 ff->sublen, ff->symxlen);
2311 dosubfrag(g, f->ixstart, firstge, gef, fscale);
2317 } while(ge != cge->next);
2325 /* all the fragments are found, extract the vectorization */
2327 g->entries = g->lastentry = 0;
2328 g->flags |= GF_FLOAT;
2332 for(ge = pge; ge != 0; ge = ge->next) {
2339 if(f->flags & (GEXFF_DRAWLINE|GEXFF_DRAWCURVE)) {
2340 /* draw a line to the start point */
2341 fg_rlineto(g, f->vect[0][0], f->vect[0][1]);
2342 /* draw the fragment */
2343 if(f->flags & GEXFF_DRAWCURVE)
2345 f->vect[1][0], f->vect[1][1],
2346 f->vect[2][0], f->vect[2][1],
2347 f->vect[3][0], f->vect[3][1]);
2349 fg_rlineto(g, f->vect[3][0], f->vect[3][1]);
2350 skip = f->vectlen - 2;
2352 fg_rlineto(g, fscale * ge->ix3, fscale * ge->iy3);
2358 fg_rmoveto(g, -1e6, -1e6); /* will be fixed by GE_PATH */
2360 /* remember the reference to update it later */
2361 loopge = g->lastentry;
2364 /* update the first MOVE of this contour */
2366 loopge->fx3 = g->lastentry->fx3;
2367 loopge->fy3 = g->lastentry->fy3;
2374 for(ge = pge; ge != 0; ge = cge) {
2379 dumppaths(g, NULL, NULL);
2381 /* end of vectorization logic */
2383 /* convert the data to float */
2387 for(ge = g->entries; ge != 0; ge = ge->next) {
2388 ge->flags |= GEF_FLOAT;
2389 if(ge->type != GE_MOVE && ge->type != GE_LINE)
2392 x = fscale * ge->ix3;
2393 y = fscale * ge->iy3;
2398 g->flags |= GF_FLOAT;
2401 free(hlm); free(vlm); free(amp);
2405 /* print out the bitmap */
2406 printbmap(bmap, xsz, ysz, xoff, yoff)
2408 int xsz, ysz, xoff, yoff;
2412 for(y=ysz-1; y>=0; y--) {
2413 putchar( (y%10==0) ? y/10+'0' : ' ' );
2414 putchar( y%10+'0' );
2415 for(x=0; x<xsz; x++)
2416 putchar( bmap[y*xsz+x] ? 'X' : '.' );
2418 putchar('_'); /* mark the baseline */
2421 putchar(' '); putchar(' ');
2422 for(x=0; x<xsz; x++)
2423 putchar( x%10+'0' );
2424 putchar('\n'); putchar(' '); putchar(' ');
2425 for(x=0; x<xsz; x++)
2426 putchar( (x%10==0) ? x/10+'0' : ' ' );
2430 /* print out the limits of outlines */
2431 printlimits(hlm, vlm, amp, xsz, ysz)
2432 char *hlm, *vlm, *amp;
2436 static char h_char[]={ ' ', '~', '^' };
2437 static char v_char[]={ ' ', '(', ')' };
2439 for(y=ysz-1; y>=0; y--) {
2440 for(x=0; x<xsz; x++) {
2442 putchar('!'); /* ambigouos point is always on a limit */
2444 putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]);
2445 putchar(h_char[ hlm[(y+1)*xsz+x] & (L_ON|L_OFF) ]);
2447 putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]);
2451 for(x=0; x<xsz; x++) {
2453 putchar(h_char[ hlm[x] & (L_ON|L_OFF) ]);