[2000] | 1 | /* |
---|
| 2 | * Handling of the bitmapped glyphs |
---|
| 3 | * |
---|
| 4 | * Copyright (c) 2001 by the TTF2PT1 project |
---|
| 5 | * Copyright (c) 2001 by Sergey Babkin |
---|
| 6 | * |
---|
| 7 | * see COPYRIGHT for the full copyright notice |
---|
| 8 | */ |
---|
| 9 | |
---|
| 10 | #include <stdio.h> |
---|
| 11 | #include <stdlib.h> |
---|
| 12 | #include <math.h> |
---|
| 13 | #include "pt1.h" |
---|
| 14 | #include "global.h" |
---|
| 15 | |
---|
| 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 */ |
---|
| 20 | |
---|
| 21 | static int warnedhints = 0; |
---|
| 22 | |
---|
| 23 | |
---|
| 24 | #ifdef USE_AUTOTRACE |
---|
| 25 | #include <autotrace/autotrace.h> |
---|
| 26 | |
---|
| 27 | /* |
---|
| 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) |
---|
| 33 | */ |
---|
| 34 | |
---|
| 35 | static void |
---|
| 36 | autotraced_bmp_outline( |
---|
| 37 | GLYPH *g, |
---|
| 38 | int scale, |
---|
| 39 | char *bmap, |
---|
| 40 | int xsz, |
---|
| 41 | int ysz, |
---|
| 42 | int xoff, |
---|
| 43 | int yoff |
---|
| 44 | ) |
---|
| 45 | { |
---|
| 46 | at_bitmap_type atb; |
---|
| 47 | at_splines_type *atsp; |
---|
| 48 | at_fitting_opts_type *atoptsp; |
---|
| 49 | at_spline_list_type *slp; |
---|
| 50 | at_spline_type *sp; |
---|
| 51 | int i, j, k; |
---|
| 52 | double lastx, lasty; |
---|
| 53 | double fscale; |
---|
| 54 | char *xbmap; |
---|
| 55 | |
---|
| 56 | fscale = (double)scale; |
---|
| 57 | |
---|
| 58 | /* provide a white margin around the bitmap */ |
---|
| 59 | xbmap = malloc((ysz+2)*(xsz+2)); |
---|
| 60 | if(xbmap == 0) { |
---|
| 61 | fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); |
---|
| 62 | exit(255); |
---|
| 63 | } |
---|
| 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 */ |
---|
| 69 | } |
---|
| 70 | memset(xbmap+j, 0, xsz+2); /* bottom margin */ |
---|
| 71 | xoff--; yoff-=2; /* compensate for the margins */ |
---|
| 72 | |
---|
| 73 | atoptsp = at_fitting_opts_new(); |
---|
| 74 | |
---|
| 75 | atb.width = xsz+2; |
---|
| 76 | atb.height = ysz+2; |
---|
| 77 | atb.np = 1; |
---|
| 78 | atb.bitmap = xbmap; |
---|
| 79 | |
---|
| 80 | atsp = at_splines_new(&atb, atoptsp); |
---|
| 81 | |
---|
| 82 | lastx = lasty = -1.; |
---|
| 83 | for(i=0; i<atsp->length; i++) { |
---|
| 84 | slp = &atsp->data[i]; |
---|
| 85 | #if 0 |
---|
| 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); |
---|
| 88 | #endif |
---|
| 89 | if(slp->length == 0) |
---|
| 90 | continue; |
---|
| 91 | #if 0 |
---|
| 92 | if(slp->color.r + slp->color.g + slp->color.b == 0) |
---|
| 93 | continue; |
---|
| 94 | #endif |
---|
| 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++) { |
---|
| 97 | #if 0 |
---|
| 98 | fprintf(stderr, " "); |
---|
| 99 | for(k=0; k<4; k++) |
---|
| 100 | fprintf(stderr, "(%g %g) ", |
---|
| 101 | fscale*(slp->data[j].v[k].x+xoff), |
---|
| 102 | fscale*(ysz-slp->data[j].v[k].y+yoff) |
---|
| 103 | ); |
---|
| 104 | fprintf(stderr, "\n"); |
---|
| 105 | #endif |
---|
| 106 | fg_rrcurveto(g, |
---|
| 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) ); |
---|
| 110 | } |
---|
| 111 | g_closepath(g); |
---|
| 112 | } |
---|
| 113 | |
---|
| 114 | at_splines_free(atsp); |
---|
| 115 | at_fitting_opts_free(atoptsp); |
---|
| 116 | free(xbmap); |
---|
| 117 | } |
---|
| 118 | |
---|
| 119 | #endif /*USE_AUTOTRACE*/ |
---|
| 120 | |
---|
| 121 | /* an extension of gentry for description of the fragments */ |
---|
| 122 | typedef struct gex_frag GEX_FRAG; |
---|
| 123 | struct 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_SERIF 4 /* small serifs at the ends of stemsi - must be last */ |
---|
| 131 | #define GEXFI_COUNT 5 /* maximal index + 1 */ |
---|
| 132 | unsigned short len[GEXFI_COUNT]; /* length of various fragment types starting here */ |
---|
| 133 | unsigned short lenback[GEXFI_COUNT]; /* length back to the start of curve */ |
---|
| 134 | |
---|
| 135 | signed char ixstart; /* index of the frag type that starts here */ |
---|
| 136 | signed char ixcont; /* index of the frag type that continues here */ |
---|
| 137 | |
---|
| 138 | short flags; |
---|
| 139 | #define GEXFF_HLINE 0x0001 /* the exact line is longer in "horizontal" dimension */ |
---|
| 140 | #define GEXFF_EXTR 0x0002 /* this gentry is an extremum in some direction */ |
---|
| 141 | #define GEXFF_CIRC 0x0004 /* the joint at this gentry is for a circular curve */ |
---|
| 142 | #define GEXFF_DRAWCURVE 0x0008 /* vect[] describes a curve to draw */ |
---|
| 143 | #define GEXFF_DRAWLINE 0x0010 /* vect[] describes a line to draw */ |
---|
| 144 | #define GEXFF_SPLIT 0x0020 /* is a result of splitting a line */ |
---|
| 145 | #define GEXFF_SYMNEXT 0x0040 /* this subfrag is symmetric with next one */ |
---|
| 146 | #define GEXFF_DONE 0x0080 /* this subfrag has been vectorized */ |
---|
| 147 | #define GEXFF_LONG 0x0100 /* this gentry is longer than 1 pixel */ |
---|
| 148 | |
---|
| 149 | unsigned short aidx; /* index of gentry in the array representing the contour */ |
---|
| 150 | |
---|
| 151 | unsigned short vectlen; /* number of gentries represented by vect[] */ |
---|
| 152 | |
---|
| 153 | /* coordinates for vectored replacement of this fragment */ |
---|
| 154 | /* (already scaled because it's needed for curve approximation) */ |
---|
| 155 | double vect[4 /*ref.points*/][2 /*X,Y*/]; |
---|
| 156 | |
---|
| 157 | double bbox[2 /*X,Y*/]; /* absolute sizes of bounding box of a subfragment */ |
---|
| 158 | |
---|
| 159 | /* used when splitting the curved frags into subfrags */ |
---|
| 160 | GENTRY *prevsub; /* to gentries describing neighboring subfrags */ |
---|
| 161 | GENTRY *nextsub; |
---|
| 162 | GENTRY *ordersub; /* single-linked list describing the order of calculation */ |
---|
| 163 | |
---|
| 164 | int sublen; /* length of this subfrag */ |
---|
| 165 | /* the symmetry across the subfrags */ |
---|
| 166 | int symaxis; /* the symmetry axis, with next subfrag */ |
---|
| 167 | int symxlen; /* min length of adjacent symmetric frags */ |
---|
| 168 | /* the symmetry within this subfrag (the axis is always diagonal) */ |
---|
| 169 | GENTRY *symge; /* symge->i{x,y}3 is the symmetry point of symge==0 if none */ |
---|
| 170 | |
---|
| 171 | }; |
---|
| 172 | #define X_FRAG(ge) ((GEX_FRAG *)((ge)->ext)) |
---|
| 173 | |
---|
| 174 | /* various interesting tables related to GEX_FRAG */ |
---|
| 175 | static char *gxf_name[GEXFI_COUNT] = {"Convex", "Concave", "Line", "ExLine", "Serif"}; |
---|
| 176 | static int gxf_cvk[2] = {-1, 1}; /* coefficients of concaveness */ |
---|
| 177 | |
---|
| 178 | /* |
---|
| 179 | * Dump the contents of X_EXT()->len and ->lenback for a contour |
---|
| 180 | */ |
---|
| 181 | static void |
---|
| 182 | gex_dump_contour( |
---|
| 183 | GENTRY *ge, |
---|
| 184 | int clen |
---|
| 185 | ) |
---|
| 186 | { |
---|
| 187 | int i, j; |
---|
| 188 | |
---|
| 189 | for(j = 0; j < GEXFI_COUNT; j++) { |
---|
| 190 | fprintf(stderr, "%-8s", gxf_name[j]); |
---|
| 191 | for(i = 0; i < clen; i++, ge = ge->frwd) |
---|
| 192 | fprintf(stderr, " %2d", X_FRAG(ge)->len[j]); |
---|
| 193 | fprintf(stderr, " %p\n (back) ", ge); |
---|
| 194 | for(i = 0; i < clen; i++, ge = ge->frwd) |
---|
| 195 | fprintf(stderr, " %2d", X_FRAG(ge)->lenback[j]); |
---|
| 196 | fprintf(stderr, "\n"); |
---|
| 197 | } |
---|
| 198 | } |
---|
| 199 | |
---|
| 200 | /* |
---|
| 201 | * Calculate values of X_EXT()->lenback[] for all entries in |
---|
| 202 | * a contour. The contour is identified by: |
---|
| 203 | * ge - any gentry of the contour |
---|
| 204 | * clen - contour length |
---|
| 205 | */ |
---|
| 206 | |
---|
| 207 | static void |
---|
| 208 | gex_calc_lenback( |
---|
| 209 | GENTRY *ge, |
---|
| 210 | int clen |
---|
| 211 | ) |
---|
| 212 | { |
---|
| 213 | int i, j; |
---|
| 214 | int end; |
---|
| 215 | GEX_FRAG *f; |
---|
| 216 | int len[GEXFI_COUNT]; /* length of the most recent fragment */ |
---|
| 217 | int count[GEXFI_COUNT]; /* steps since beginning of the fragment */ |
---|
| 218 | |
---|
| 219 | for(j = 0; j < GEXFI_COUNT; j++) |
---|
| 220 | len[j] = count[j] = 0; |
---|
| 221 | |
---|
| 222 | end = clen; |
---|
| 223 | for(i = 0; i < end; i++, ge = ge->frwd) { |
---|
| 224 | f = X_FRAG(ge); |
---|
| 225 | for(j = 0; j < GEXFI_COUNT; j++) { |
---|
| 226 | if(len[j] != count[j]) { |
---|
| 227 | f->lenback[j] = count[j]++; |
---|
| 228 | } else |
---|
| 229 | f->lenback[j] = 0; |
---|
| 230 | if(f->len[j] != 0) { |
---|
| 231 | len[j] = f->len[j]; |
---|
| 232 | count[j] = 1; /* start with the next gentry */ |
---|
| 233 | /* if the fragment will wrap over the start, process to its end */ |
---|
| 234 | if(i < clen && i + len[j] > end) |
---|
| 235 | end = i + len[j]; |
---|
| 236 | } |
---|
| 237 | } |
---|
| 238 | } |
---|
| 239 | gex_dump_contour(ge, clen); |
---|
| 240 | } |
---|
| 241 | |
---|
| 242 | /* Limit a curve to not exceed the given coordinates |
---|
| 243 | * at its given side |
---|
| 244 | */ |
---|
| 245 | |
---|
| 246 | static void |
---|
| 247 | limcurve( |
---|
| 248 | double curve[4][2 /*X,Y*/], |
---|
| 249 | double lim[2 /*X,Y*/], |
---|
| 250 | int where /* 0 - start, 3 - end */ |
---|
| 251 | ) |
---|
| 252 | { |
---|
| 253 | int other = 3-where; /* the other end */ |
---|
| 254 | int sgn[2 /*X,Y*/]; /* sign for comparison */ |
---|
| 255 | double t, from, to, nt, t2, nt2, tt[4]; |
---|
| 256 | double val[2 /*X,Y*/]; |
---|
| 257 | int i; |
---|
| 258 | |
---|
| 259 | for(i=0; i<2; i++) |
---|
| 260 | sgn[i] = fsign(curve[other][i] - curve[where][i]); |
---|
| 261 | |
---|
| 262 | #if 0 |
---|
| 263 | fprintf(stderr, " limit (%g,%g)-(%g,%g) at %d by (%g,%g), sgn(%d,%d)\n", |
---|
| 264 | curve[0][0], curve[0][1], curve[3][0], curve[3][1], |
---|
| 265 | where, lim[0], lim[1], sgn[0], sgn[1]); |
---|
| 266 | #endif |
---|
| 267 | /* a common special case */ |
---|
| 268 | if( sgn[0]*(curve[where][0] - lim[0]) >= 0. |
---|
| 269 | && sgn[1]*(curve[where][1] - lim[1]) >= 0. ) |
---|
| 270 | return; /* nothing to do */ |
---|
| 271 | |
---|
| 272 | if(other==0) { |
---|
| 273 | from = 0.; |
---|
| 274 | to = 1.; |
---|
| 275 | } else { |
---|
| 276 | from = 1.; |
---|
| 277 | to = 0.; |
---|
| 278 | } |
---|
| 279 | #if 0 |
---|
| 280 | fprintf(stderr, "t="); |
---|
| 281 | #endif |
---|
| 282 | while( fabs(from-to) > 0.00001 ) { |
---|
| 283 | t = 0.5 * (from+to); |
---|
| 284 | t2 = t*t; |
---|
| 285 | nt = 1.-t; |
---|
| 286 | nt2 = nt*nt; |
---|
| 287 | tt[0] = nt2*nt; |
---|
| 288 | tt[1] = 3*nt2*t; |
---|
| 289 | tt[2] = 3*nt*t2; |
---|
| 290 | tt[3] = t*t2; |
---|
| 291 | for(i=0; i<2; i++) |
---|
| 292 | val[i] = curve[0][i]*tt[0] + curve[1][i]*tt[1] |
---|
| 293 | + curve[2][i]*tt[2] + curve[3][i]*tt[3]; |
---|
| 294 | #if 0 |
---|
| 295 | fprintf(stderr, "%g(%g,%g) ", t, val[0], val[1]); |
---|
| 296 | #endif |
---|
| 297 | if(fabs(val[0] - lim[0]) < 0.1 |
---|
| 298 | || fabs(val[1] - lim[1]) < 0.1) |
---|
| 299 | break; |
---|
| 300 | |
---|
| 301 | if(sgn[0] * (val[0] - lim[0]) < 0. |
---|
| 302 | || sgn[1] * (val[1] - lim[1]) < 0.) |
---|
| 303 | to = t; |
---|
| 304 | else |
---|
| 305 | from = t; |
---|
| 306 | } |
---|
| 307 | /* now t is the point of splitting */ |
---|
| 308 | #define SPLIT(pt1, pt2) ( (pt1) + t*((pt2)-(pt1)) ) /* order is important! */ |
---|
| 309 | for(i=0; i<2; i++) { |
---|
| 310 | double v11, v12, v13, v21, v22, v31; /* intermediate points */ |
---|
| 311 | |
---|
| 312 | v11 = SPLIT(curve[0][i], curve[1][i]); |
---|
| 313 | v12 = SPLIT(curve[1][i], curve[2][i]); |
---|
| 314 | v13 = SPLIT(curve[2][i], curve[3][i]); |
---|
| 315 | v21 = SPLIT(v11, v12); |
---|
| 316 | v22 = SPLIT(v12, v13); |
---|
| 317 | v31 = SPLIT(v21, v22); |
---|
| 318 | if(other==0) { |
---|
| 319 | curve[1][i] = v11; |
---|
| 320 | curve[2][i] = v21; |
---|
| 321 | curve[3][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31; |
---|
| 322 | } else { |
---|
| 323 | curve[0][i] = fabs(v31 - lim[i]) < 0.1 ? lim[i] : v31; |
---|
| 324 | curve[1][i] = v22; |
---|
| 325 | curve[2][i] = v13; |
---|
| 326 | } |
---|
| 327 | } |
---|
| 328 | #undef SPLIT |
---|
| 329 | #if 0 |
---|
| 330 | fprintf(stderr, "\n"); |
---|
| 331 | #endif |
---|
| 332 | } |
---|
| 333 | |
---|
| 334 | /* |
---|
| 335 | * Vectorize a subfragment of a curve fragment. All the data has been already |
---|
| 336 | * collected by this time |
---|
| 337 | */ |
---|
| 338 | |
---|
| 339 | static void |
---|
| 340 | dosubfrag( |
---|
| 341 | GLYPH *g, |
---|
| 342 | int fti, /* fragment type index */ |
---|
| 343 | GENTRY *firstge, /* first gentry of fragment */ |
---|
| 344 | GENTRY *ge, /* first gentry of subfragment */ |
---|
| 345 | double fscale |
---|
| 346 | ) |
---|
| 347 | { |
---|
| 348 | GENTRY *gel, *gei; /* last gentry of this subfrag */ |
---|
| 349 | GEX_FRAG *f, *ff, *lf, *pf, *xf; |
---|
| 350 | /* maximal amount of space that can be used at the beginning and the end */ |
---|
| 351 | double fixfront[2], fixend[2]; /* fixed points - used to show direction */ |
---|
| 352 | double mvfront[2], mvend[2]; /* movable points */ |
---|
| 353 | double limfront[2], limend[2]; /* limit of movement for movabel points */ |
---|
| 354 | double sympt; |
---|
| 355 | int outfront, outend; /* the beginning/end is going outwards */ |
---|
| 356 | int symfront, symend; /* a ready symmetric fragment is present at front/end */ |
---|
| 357 | int drnd[2 /*X,Y*/]; /* size of the round part */ |
---|
| 358 | int i, j, a1, a2, ndots; |
---|
| 359 | double avg2, max2; /* squared distances */ |
---|
| 360 | struct dot_dist *dots, *usedots; |
---|
| 361 | |
---|
| 362 | ff = X_FRAG(firstge); |
---|
| 363 | f = X_FRAG(ge); |
---|
| 364 | gel = f->nextsub; |
---|
| 365 | lf = X_FRAG(gel); |
---|
| 366 | if(f->prevsub != 0) |
---|
| 367 | pf = X_FRAG(f->prevsub); |
---|
| 368 | else |
---|
| 369 | pf = 0; |
---|
| 370 | |
---|
| 371 | for(i=0; i<2; i++) |
---|
| 372 | drnd[i] = gel->bkwd->ipoints[i][2] - ge->ipoints[i][2]; |
---|
| 373 | |
---|
| 374 | if(f->prevsub==0 && f->ixcont == GEXFI_NONE) { |
---|
| 375 | /* nothing to join with : may use the whole length */ |
---|
| 376 | for(i = 0; i < 2; i++) |
---|
| 377 | limfront[i] = ge->bkwd->ipoints[i][2]; |
---|
| 378 | } else { |
---|
| 379 | /* limit to a half */ |
---|
| 380 | for(i = 0; i < 2; i++) |
---|
| 381 | limfront[i] = 0.5 * (ge->ipoints[i][2] + ge->bkwd->ipoints[i][2]); |
---|
| 382 | } |
---|
| 383 | if( (ge->ix3 == ge->bkwd->ix3) /* vert */ |
---|
| 384 | ^ (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3)) |
---|
| 385 | ^ (fti == GEXFI_CONCAVE) /* counter-clockwise */ ) { |
---|
| 386 | /* the beginning is not a flat 90-degree end */ |
---|
| 387 | outfront = 1; |
---|
| 388 | for(i = 0; i < 2; i++) |
---|
| 389 | fixfront[i] = ge->frwd->ipoints[i][2]; |
---|
| 390 | } else { |
---|
| 391 | outfront = 0; |
---|
| 392 | for(i = 0; i < 2; i++) |
---|
| 393 | fixfront[i] = ge->ipoints[i][2]; |
---|
| 394 | } |
---|
| 395 | |
---|
| 396 | if(lf->nextsub==0 && lf->ixstart == GEXFI_NONE) { |
---|
| 397 | /* nothing to join with : may use the whole length */ |
---|
| 398 | for(i = 0; i < 2; i++) |
---|
| 399 | limend[i] = gel->ipoints[i][2]; |
---|
| 400 | } else { |
---|
| 401 | /* limit to a half */ |
---|
| 402 | for(i = 0; i < 2; i++) |
---|
| 403 | limend[i] = 0.5 * (gel->ipoints[i][2] + gel->bkwd->ipoints[i][2]); |
---|
| 404 | } |
---|
| 405 | gei = gel->bkwd->bkwd; |
---|
| 406 | if( (gel->ix3 == gel->bkwd->ix3) /* vert */ |
---|
| 407 | ^ (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3)) |
---|
| 408 | ^ (fti == GEXFI_CONVEX) /* clockwise */ ) { |
---|
| 409 | /* the end is not a flat 90-degree end */ |
---|
| 410 | outend = 1; |
---|
| 411 | for(i = 0; i < 2; i++) |
---|
| 412 | fixend[i] = gel->bkwd->bkwd->ipoints[i][2]; |
---|
| 413 | } else { |
---|
| 414 | outend = 0; |
---|
| 415 | for(i = 0; i < 2; i++) |
---|
| 416 | fixend[i] = gel->bkwd->ipoints[i][2]; |
---|
| 417 | } |
---|
| 418 | |
---|
| 419 | for(i = 0; i < 2; i++) { |
---|
| 420 | fixfront[i] *= fscale; |
---|
| 421 | limfront[i] *= fscale; |
---|
| 422 | fixend[i] *= fscale; |
---|
| 423 | limend[i] *= fscale; |
---|
| 424 | } |
---|
| 425 | |
---|
| 426 | fprintf(stderr, " %d out(%d[%d %d %d],%d[%d %d %d]) drnd(%d, %d)\n", |
---|
| 427 | fti, |
---|
| 428 | outfront, |
---|
| 429 | (ge->ix3 == ge->bkwd->ix3), |
---|
| 430 | (isign(ge->bkwd->ix3 - ge->frwd->ix3)==isign(ge->bkwd->iy3 - ge->frwd->iy3)), |
---|
| 431 | (fti == GEXFI_CONCAVE), |
---|
| 432 | outend, |
---|
| 433 | (gel->ix3 == gel->bkwd->ix3), |
---|
| 434 | (isign(gel->ix3 - gei->ix3)==isign(gel->iy3 - gei->iy3)), |
---|
| 435 | (fti == GEXFI_CONVEX), |
---|
| 436 | drnd[0], drnd[1]); |
---|
| 437 | |
---|
| 438 | /* prepare to calculate the distances */ |
---|
| 439 | ndots = f->sublen - 1; |
---|
| 440 | dots = malloc(sizeof(*dots) * ndots); |
---|
| 441 | if(dots == 0) { |
---|
| 442 | fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); |
---|
| 443 | exit(255); |
---|
| 444 | } |
---|
| 445 | for(i = 0, gei = ge; i < ndots; i++, gei = gei->frwd) { |
---|
| 446 | for(a1 = 0; a1 < 2; a1++) |
---|
| 447 | dots[i].p[a1] = fscale * gei->ipoints[a1][2]; |
---|
| 448 | } |
---|
| 449 | |
---|
| 450 | /* see if we can mirror a ready symmetric curve */ |
---|
| 451 | |
---|
| 452 | /* check symmetry with the fragment before this */ |
---|
| 453 | symfront = (pf != 0 && (pf->flags & GEXFF_SYMNEXT) && (pf->flags & GEXFF_DONE) |
---|
| 454 | && ( outend && f->sublen <= pf->sublen |
---|
| 455 | || ( pf->sublen == f->sublen |
---|
| 456 | && (lf->sublen == 0 |
---|
| 457 | || ( abs(limfront[0]-limend[0]) >= abs(pf->vect[0][0]-pf->vect[3][0]) |
---|
| 458 | && abs(limfront[1]-limend[1]) >= abs(pf->vect[0][1]-pf->vect[3][1]) )) |
---|
| 459 | ) |
---|
| 460 | ) |
---|
| 461 | ); |
---|
| 462 | /* check symmetry with the fragment after this */ |
---|
| 463 | symend = ( (f->flags & GEXFF_SYMNEXT) && (lf->flags & GEXFF_DONE) |
---|
| 464 | && ( outfront && f->sublen <= lf->sublen |
---|
| 465 | || ( lf->sublen == f->sublen |
---|
| 466 | && (pf == 0 |
---|
| 467 | || ( abs(limfront[0]-limend[0]) >= abs(lf->vect[0][0]-lf->vect[3][0]) |
---|
| 468 | && abs(limfront[1]-limend[1]) >= abs(lf->vect[0][1]-lf->vect[3][1]) )) |
---|
| 469 | ) |
---|
| 470 | ) |
---|
| 471 | ); |
---|
| 472 | if(symfront || symend) { |
---|
| 473 | /* mirror the symmetric neighbour subfrag */ |
---|
| 474 | if(symfront) { |
---|
| 475 | a1 = (ge->ix3 != ge->bkwd->ix3); /* the symmetry axis */ |
---|
| 476 | a2 = !a1; /* the other axis (goes along the extremum gentry) */ |
---|
| 477 | |
---|
| 478 | /* the symmetry point on a2 */ |
---|
| 479 | sympt = fscale * 0.5 * (ge->ipoints[a2][2] + ge->bkwd->ipoints[a2][2]); |
---|
| 480 | xf = pf; /* the symmetric fragment */ |
---|
| 481 | } else { |
---|
| 482 | a1 = (gel->ix3 != gel->bkwd->ix3); /* the symmetry axis */ |
---|
| 483 | a2 = !a1; /* the other axis (goes along the extremum gentry) */ |
---|
| 484 | |
---|
| 485 | /* the symmetry point on a2 */ |
---|
| 486 | sympt = fscale * 0.5 * (gel->ipoints[a2][2] + gel->bkwd->ipoints[a2][2]); |
---|
| 487 | xf = lf; /* the symmetric fragment */ |
---|
| 488 | } |
---|
| 489 | fprintf(stderr, " sym with %p f=%d(%p) e=%d(%p) a1=%c a2=%c sympt=%g\n", |
---|
| 490 | xf, symfront, pf, symend, lf, |
---|
| 491 | a1 ? 'Y': 'X', a2 ? 'Y': 'X', sympt |
---|
| 492 | ); |
---|
| 493 | for(i=0; i<4; i++) { |
---|
| 494 | f->vect[3-i][a1] = xf->vect[i][a1]; |
---|
| 495 | f->vect[3-i][a2] = sympt - (xf->vect[i][a2]-sympt); |
---|
| 496 | } |
---|
| 497 | if(symfront) { |
---|
| 498 | if(outend || lf->sublen==0) |
---|
| 499 | limcurve(f->vect, limend, 3); |
---|
| 500 | } else { |
---|
| 501 | if(outfront || pf == 0) |
---|
| 502 | limcurve(f->vect, limfront, 0); |
---|
| 503 | } |
---|
| 504 | avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2); |
---|
| 505 | fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale); |
---|
| 506 | if(max2 <= fscale*fscale) { |
---|
| 507 | f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); |
---|
| 508 | f->vectlen = f->sublen; |
---|
| 509 | free(dots); |
---|
| 510 | return; |
---|
| 511 | } |
---|
| 512 | } |
---|
| 513 | |
---|
| 514 | if( !outfront && !outend && f->symge != 0) { |
---|
| 515 | /* a special case: try a circle segment as an approximation */ |
---|
| 516 | double lenfront, lenend, len, maxlen; |
---|
| 517 | |
---|
| 518 | /* coefficient for a Bezier approximation of a circle */ |
---|
| 519 | #define CIRCLE_FRAC 0.55 |
---|
| 520 | |
---|
| 521 | a1 = (ge->ix3 == ge->bkwd->ix3); /* get the axis along the front */ |
---|
| 522 | a2 = !a1; /* axis along the end */ |
---|
| 523 | |
---|
| 524 | lenfront = fixfront[a1] - limfront[a1]; |
---|
| 525 | lenend = fixend[a2] - limend[a2]; |
---|
| 526 | if(fabs(lenfront) < fabs(lenend)) |
---|
| 527 | len = fabs(lenfront); |
---|
| 528 | else |
---|
| 529 | len = fabs(lenend); |
---|
| 530 | |
---|
| 531 | /* make it go close to the round shape */ |
---|
| 532 | switch(f->sublen) { |
---|
| 533 | case 2: |
---|
| 534 | maxlen = fscale; |
---|
| 535 | break; |
---|
| 536 | case 4: |
---|
| 537 | case 6: |
---|
| 538 | maxlen = fscale * 2.; |
---|
| 539 | break; |
---|
| 540 | default: |
---|
| 541 | maxlen = fscale * abs(ge->frwd->frwd->ipoints[a1][2] |
---|
| 542 | - ge->ipoints[a1][2]); |
---|
| 543 | break; |
---|
| 544 | } |
---|
| 545 | if(len > maxlen) |
---|
| 546 | len = maxlen; |
---|
| 547 | |
---|
| 548 | mvfront[a1] = fixfront[a1] - fsign(lenfront) * len; |
---|
| 549 | mvfront[a2] = limfront[a2]; |
---|
| 550 | mvend[a2] = fixend[a2] - fsign(lenend) * len; |
---|
| 551 | mvend[a1] = limend[a1]; |
---|
| 552 | |
---|
| 553 | for(i=0; i<2; i++) { |
---|
| 554 | f->vect[0][i] = mvfront[i]; |
---|
| 555 | f->vect[3][i] = mvend[i]; |
---|
| 556 | } |
---|
| 557 | f->vect[1][a1] = mvfront[a1] + CIRCLE_FRAC*(mvend[a1]-mvfront[a1]); |
---|
| 558 | f->vect[1][a2] = mvfront[a2]; |
---|
| 559 | f->vect[2][a1] = mvend[a1]; |
---|
| 560 | f->vect[2][a2] = mvend[a2] + CIRCLE_FRAC*(mvfront[a2]-mvend[a2]); |
---|
| 561 | |
---|
| 562 | avg2 = fdotcurvdist2(f->vect, dots, ndots, &max2); |
---|
| 563 | fprintf(stderr, " avg=%g max=%g fscale=%g\n", sqrt(avg2), sqrt(max2), fscale); |
---|
| 564 | if(max2 <= fscale*fscale) { |
---|
| 565 | f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); |
---|
| 566 | f->vectlen = f->sublen; |
---|
| 567 | free(dots); |
---|
| 568 | return; |
---|
| 569 | } |
---|
| 570 | #undef CIRCLE_FRAC |
---|
| 571 | } |
---|
| 572 | for(i=0; i<2; i++) { |
---|
| 573 | f->vect[0][i] = limfront[i]; |
---|
| 574 | f->vect[1][i] = fixfront[i]; |
---|
| 575 | f->vect[2][i] = fixend[i]; |
---|
| 576 | f->vect[3][i] = limend[i]; |
---|
| 577 | } |
---|
| 578 | usedots = dots; |
---|
| 579 | if(outfront) { |
---|
| 580 | usedots++; ndots--; |
---|
| 581 | } |
---|
| 582 | if(outend) |
---|
| 583 | ndots--; |
---|
| 584 | if( fcrossrayscv(f->vect, NULL, NULL) == 0) { |
---|
| 585 | fprintf(stderr, "**** Internal error: rays must cross but don't at %p-%p\n", |
---|
| 586 | ge, gel); |
---|
| 587 | fprintf(stderr, " (%g, %g) (%g, %g) (%g, %g) (%g, %g)\n", |
---|
| 588 | limfront[0], limfront[1], |
---|
| 589 | fixfront[0], fixfront[1], |
---|
| 590 | fixend[0], fixend[1], |
---|
| 591 | limend[0], limend[1] |
---|
| 592 | ); |
---|
| 593 | dumppaths(g, NULL, NULL); |
---|
| 594 | exit(1); |
---|
| 595 | } else { |
---|
| 596 | if(ndots != 0) |
---|
| 597 | fapproxcurve(f->vect, usedots, ndots); |
---|
| 598 | f->flags |= (GEXFF_DONE | GEXFF_DRAWCURVE); |
---|
| 599 | f->vectlen = f->sublen; |
---|
| 600 | } |
---|
| 601 | free(dots); |
---|
| 602 | } |
---|
| 603 | |
---|
| 604 | /* |
---|
| 605 | * Subtract a list of gentries (covered by a fragment of higher |
---|
| 606 | * priority) from the set of fragments of a given |
---|
| 607 | * type. |
---|
| 608 | * |
---|
| 609 | * An example is subtraction of the long exact line fragments |
---|
| 610 | * from the curve fragments which get overridden. |
---|
| 611 | */ |
---|
| 612 | |
---|
| 613 | static void |
---|
| 614 | frag_subtract( |
---|
| 615 | GLYPH *g, |
---|
| 616 | GENTRY **age, /* array of gentries for this contour */ |
---|
| 617 | int clen, /* length of the contour */ |
---|
| 618 | GENTRY *ge, /* first gentry to be subtracted */ |
---|
| 619 | int slen, /* number of gentries in the list to be subtracted */ |
---|
| 620 | int d /* type of fragments from which to subtract, as in GEXFI_... */ |
---|
| 621 | ) |
---|
| 622 | { |
---|
| 623 | GENTRY *pge; |
---|
| 624 | GEX_FRAG *f, *pf; |
---|
| 625 | int len, i, j; |
---|
| 626 | |
---|
| 627 | f = X_FRAG(ge); |
---|
| 628 | len = slen; |
---|
| 629 | |
---|
| 630 | /* check if we overlap the end of some fragment */ |
---|
| 631 | if(f->lenback[d]) { |
---|
| 632 | /* chop off the end of conflicting fragment */ |
---|
| 633 | len = f->lenback[d]; |
---|
| 634 | pge = age[(f->aidx + clen - len)%clen]; |
---|
| 635 | pf = X_FRAG(pge); |
---|
| 636 | if(pf->len[d] == clen+1 && pf->flags & GEXFF_CIRC) { |
---|
| 637 | /* the conflicting fragment is self-connected */ |
---|
| 638 | |
---|
| 639 | pf->len[d] = 0; |
---|
| 640 | /* calculate the new value for lenback */ |
---|
| 641 | len = clen+1 - slen; |
---|
| 642 | for(pge = ge; len > 0; pge = pge->bkwd, len--) |
---|
| 643 | X_FRAG(pge)->lenback[d] = len; |
---|
| 644 | /* now pge points to the last entry of the line, |
---|
| 645 | * which is also the new first entry of the curve |
---|
| 646 | */ |
---|
| 647 | X_FRAG(pge)->len[d] = clen+2 - slen; |
---|
| 648 | /* clean lenback of gentries covered by the line */ |
---|
| 649 | for(pge = ge->frwd, j = slen-1; j > 0; pge = pge->frwd, j--) |
---|
| 650 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 651 | fprintf(stderr, " cut %s circular frag to %p-%p\n", |
---|
| 652 | gxf_name[d], pge, ge); |
---|
| 653 | gex_dump_contour(ge, clen); |
---|
| 654 | } else { |
---|
| 655 | /* when we chop off a piece of fragment, we leave the remaining |
---|
| 656 | * piece(s) overlapping with the beginning and possibly the end |
---|
| 657 | * of the line fragment under consideration |
---|
| 658 | */ |
---|
| 659 | fprintf(stderr, " cut %s frag at %p from len=%d to len=%d (end %p)\n", |
---|
| 660 | gxf_name[d], pge, pf->len[d], len+1, ge); |
---|
| 661 | j = pf->len[d] - len - 1; /* how many gentries are chopped off */ |
---|
| 662 | pf->len[d] = len + 1; |
---|
| 663 | i = slen - 1; |
---|
| 664 | for(pge = ge->frwd; j > 0 && i > 0; j--, i--, pge = pge->frwd) |
---|
| 665 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 666 | gex_dump_contour(ge, clen); |
---|
| 667 | |
---|
| 668 | if(j != 0) { |
---|
| 669 | /* the conflicting fragment is split in two by this line |
---|
| 670 | * fragment, fix up its tail |
---|
| 671 | */ |
---|
| 672 | |
---|
| 673 | fprintf(stderr, " end of %s frag len=%d %p-", |
---|
| 674 | gxf_name[d], j+1, pge->bkwd); |
---|
| 675 | X_FRAG(pge->bkwd)->len[d] = j+1; /* the overlapping */ |
---|
| 676 | for(i = 1; j > 0; j--, i++, pge = pge->frwd) |
---|
| 677 | X_FRAG(pge)->lenback[d] = i; |
---|
| 678 | fprintf(stderr, "%p\n", pge->bkwd); |
---|
| 679 | gex_dump_contour(ge, clen); |
---|
| 680 | } |
---|
| 681 | } |
---|
| 682 | } |
---|
| 683 | /* check if we overlap the beginning of some fragments */ |
---|
| 684 | i = slen-1; /* getntries remaining to consider */ |
---|
| 685 | j = 0; /* gentries remaining in the overlapping fragment */ |
---|
| 686 | for(pge = ge; i > 0; i--, pge = pge->frwd) { |
---|
| 687 | if(j > 0) { |
---|
| 688 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 689 | j--; |
---|
| 690 | } |
---|
| 691 | /* the beginning of one fragment may be the end of another |
---|
| 692 | * fragment, in this case if j-- above results in 0, that will |
---|
| 693 | * cause it to check the same gentry for the beginning |
---|
| 694 | */ |
---|
| 695 | if(j == 0) { |
---|
| 696 | pf = X_FRAG(pge); |
---|
| 697 | j = pf->len[d]; |
---|
| 698 | if(j != 0) { |
---|
| 699 | fprintf(stderr, " removed %s frag at %p len=%d\n", |
---|
| 700 | gxf_name[d], pge, j); |
---|
| 701 | gex_dump_contour(ge, clen); |
---|
| 702 | pf->len[d] = 0; |
---|
| 703 | j--; |
---|
| 704 | } |
---|
| 705 | } |
---|
| 706 | } |
---|
| 707 | /* pge points at the last gentry of the line fragment */ |
---|
| 708 | if(j > 1) { /* we have the tail of a fragment left */ |
---|
| 709 | fprintf(stderr, " end of %s frag len=%d %p-", |
---|
| 710 | gxf_name[d], j, pge); |
---|
| 711 | X_FRAG(pge)->len[d] = j; /* the overlapping */ |
---|
| 712 | for(i = 0; j > 0; j--, i++, pge = pge->frwd) |
---|
| 713 | X_FRAG(pge)->lenback[d] = i; |
---|
| 714 | fprintf(stderr, "%p\n", pge->bkwd); |
---|
| 715 | gex_dump_contour(ge, clen); |
---|
| 716 | } else if(j == 1) { |
---|
| 717 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 718 | } |
---|
| 719 | } |
---|
| 720 | |
---|
| 721 | /* |
---|
| 722 | * Produce an outline from a bitmap. |
---|
| 723 | * scale - factor to scale the sizes |
---|
| 724 | * bmap - array of dots by lines, xsz * ysz |
---|
| 725 | * xoff, yoff - offset of the bitmap's lower left corner |
---|
| 726 | * from the logical position (0,0) |
---|
| 727 | */ |
---|
| 728 | |
---|
| 729 | void |
---|
| 730 | bmp_outline( |
---|
| 731 | GLYPH *g, |
---|
| 732 | int scale, |
---|
| 733 | char *bmap, |
---|
| 734 | int xsz, |
---|
| 735 | int ysz, |
---|
| 736 | int xoff, |
---|
| 737 | int yoff |
---|
| 738 | ) |
---|
| 739 | { |
---|
| 740 | char *hlm, *vlm; /* arrays of the limits of outlines */ |
---|
| 741 | char *amp; /* map of ambiguous points */ |
---|
| 742 | int x, y; |
---|
| 743 | char *ip, *op; |
---|
| 744 | double fscale; |
---|
| 745 | |
---|
| 746 | if(xsz==0 || ysz==0) |
---|
| 747 | return; |
---|
| 748 | |
---|
| 749 | #ifdef USE_AUTOTRACE |
---|
| 750 | if(use_autotrace) { |
---|
| 751 | autotraced_bmp_outline(g, scale, bmap, xsz, ysz, xoff, yoff); |
---|
| 752 | return; |
---|
| 753 | } |
---|
| 754 | #endif /*USE_AUTOTRACE*/ |
---|
| 755 | |
---|
| 756 | fscale = (double)scale; |
---|
| 757 | g->flags &= ~GF_FLOAT; /* build it as int first */ |
---|
| 758 | |
---|
| 759 | if(!warnedhints) { |
---|
| 760 | warnedhints = 1; |
---|
| 761 | if(hints && subhints) { |
---|
| 762 | WARNING_2 fprintf(stderr, |
---|
| 763 | "Use of hint substitution on bitmap fonts is not recommended\n"); |
---|
| 764 | } |
---|
| 765 | } |
---|
| 766 | |
---|
| 767 | #if 0 |
---|
| 768 | printbmap(bmap, xsz, ysz, xoff, yoff); |
---|
| 769 | #endif |
---|
| 770 | |
---|
| 771 | /* now find the outlines */ |
---|
| 772 | hlm=calloc( xsz, ysz+1 ); /* horizontal limits */ |
---|
| 773 | vlm=calloc( xsz+1, ysz ); /* vertical limits */ |
---|
| 774 | amp=calloc( xsz, ysz ); /* ambiguous points */ |
---|
| 775 | |
---|
| 776 | if(hlm==0 || vlm==0 || amp==0) { |
---|
| 777 | fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); |
---|
| 778 | exit(255); |
---|
| 779 | } |
---|
| 780 | |
---|
| 781 | /* |
---|
| 782 | * hlm and vlm represent a grid of horisontal and |
---|
| 783 | * vertical lines. Each pixel is surrounded by the grid |
---|
| 784 | * from all the sides. The values of [hv]lm mark the |
---|
| 785 | * parts of grid where the pixel value switches from white |
---|
| 786 | * to black and back. |
---|
| 787 | */ |
---|
| 788 | |
---|
| 789 | /* find the horizontal limits */ |
---|
| 790 | ip=bmap; op=hlm; |
---|
| 791 | /* 1st row */ |
---|
| 792 | for(x=0; x<xsz; x++) { |
---|
| 793 | if(ip[x]) |
---|
| 794 | op[x]=L_ON; |
---|
| 795 | } |
---|
| 796 | ip+=xsz; op+=xsz; |
---|
| 797 | /* internal rows */ |
---|
| 798 | for(y=1; y<ysz; y++) { |
---|
| 799 | for(x=0; x<xsz; x++) { |
---|
| 800 | if(ip[x]) { |
---|
| 801 | if(!ip[x-xsz]) |
---|
| 802 | op[x]=L_ON; |
---|
| 803 | } else { |
---|
| 804 | if(ip[x-xsz]) |
---|
| 805 | op[x]=L_OFF; |
---|
| 806 | } |
---|
| 807 | } |
---|
| 808 | ip+=xsz; op+=xsz; |
---|
| 809 | } |
---|
| 810 | |
---|
| 811 | /* last row */ |
---|
| 812 | ip-=xsz; |
---|
| 813 | for(x=0; x<xsz; x++) { |
---|
| 814 | if(ip[x]) |
---|
| 815 | op[x]=L_OFF; |
---|
| 816 | } |
---|
| 817 | |
---|
| 818 | /* find the vertical limits */ |
---|
| 819 | ip=bmap; op=vlm; |
---|
| 820 | for(y=0; y<ysz; y++) { |
---|
| 821 | if(ip[0]) |
---|
| 822 | op[0]=L_ON; |
---|
| 823 | for(x=1; x<xsz; x++) { |
---|
| 824 | if(ip[x]) { |
---|
| 825 | if(!ip[x-1]) |
---|
| 826 | op[x]=L_ON; |
---|
| 827 | } else { |
---|
| 828 | if(ip[x-1]) |
---|
| 829 | op[x]=L_OFF; |
---|
| 830 | } |
---|
| 831 | } |
---|
| 832 | if(ip[xsz-1]) |
---|
| 833 | op[xsz]=L_OFF; |
---|
| 834 | ip+=xsz; op+=xsz+1; |
---|
| 835 | } |
---|
| 836 | |
---|
| 837 | /* |
---|
| 838 | * Ambiguous points are the nodes of the grids |
---|
| 839 | * that are between two white and two black pixels |
---|
| 840 | * located in a checkerboard style. Actually |
---|
| 841 | * there are only two patterns that may be |
---|
| 842 | * around an ambiguous point: |
---|
| 843 | * |
---|
| 844 | * X|. .|X |
---|
| 845 | * -*- -*- |
---|
| 846 | * .|X X|. |
---|
| 847 | * |
---|
| 848 | * where "|" and "-" represent the grid (respectively members |
---|
| 849 | * of vlm and hlm), "*" represents an ambiguous point |
---|
| 850 | * and "X" and "." represent black and white pixels. |
---|
| 851 | * |
---|
| 852 | * If these sample pattern occur in the lower left corner |
---|
| 853 | * of the bitmap then this ambiguous point will be |
---|
| 854 | * located at amp[1][1] or in other words amp[1*xsz+1]. |
---|
| 855 | * |
---|
| 856 | * These points are named "ambiguous" because it's |
---|
| 857 | * not easy to guess what did the font creator mean |
---|
| 858 | * at these points. So we are going to treat them |
---|
| 859 | * specially, doing no aggressive smoothing. |
---|
| 860 | */ |
---|
| 861 | |
---|
| 862 | /* find the ambiguous points */ |
---|
| 863 | for(y=ysz-1; y>0; y--) |
---|
| 864 | for(x=xsz-1; x>0; x--) { |
---|
| 865 | if(bmap[y*xsz+x]) { |
---|
| 866 | if( !bmap[y*xsz+x-1] && !bmap[y*xsz-xsz+x] && bmap[y*xsz-xsz+x-1] ) |
---|
| 867 | amp[y*xsz+x]=1; |
---|
| 868 | } else { |
---|
| 869 | if( bmap[y*xsz+x-1] && bmap[y*xsz-xsz+x] && !bmap[y*xsz-xsz+x-1] ) |
---|
| 870 | amp[y*xsz+x]=1; |
---|
| 871 | } |
---|
| 872 | } |
---|
| 873 | |
---|
| 874 | #if 0 |
---|
| 875 | printlimits(hlm, vlm, amp, xsz, ysz); |
---|
| 876 | #endif |
---|
| 877 | |
---|
| 878 | /* generate the vectored (stepping) outline */ |
---|
| 879 | |
---|
| 880 | while(1) { |
---|
| 881 | int found = 0; |
---|
| 882 | int outer; /* flag: this is an outer contour */ |
---|
| 883 | int hor, newhor; /* flag: the current contour direction is horizontal */ |
---|
| 884 | int dir; /* previous direction of the coordinate, 1 - L_ON, 0 - L_OFF */ |
---|
| 885 | int startx, starty; /* start of a contour */ |
---|
| 886 | int firstx, firsty; /* start of the current line */ |
---|
| 887 | int newx, newy; /* new coordinates to try */ |
---|
| 888 | char *lm, val; |
---|
| 889 | int maxx, maxy, xor; |
---|
| 890 | |
---|
| 891 | for(y=ysz; !found && y>0; y--) |
---|
| 892 | for(x=0; x<xsz; x++) |
---|
| 893 | if(hlm[y*xsz+x] > L_NONE) |
---|
| 894 | goto foundcontour; |
---|
| 895 | break; /* have no contours left */ |
---|
| 896 | |
---|
| 897 | foundcontour: |
---|
| 898 | ig_rmoveto(g, x+xoff, y+yoff); /* intermediate as int */ |
---|
| 899 | |
---|
| 900 | startx = firstx = x; |
---|
| 901 | starty = firsty = y; |
---|
| 902 | |
---|
| 903 | if(hlm[y*xsz+x] == L_OFF) { |
---|
| 904 | outer = 1; dir = 0; |
---|
| 905 | hlm[y*xsz+x] = -hlm[y*xsz+x]; /* mark as seen */ |
---|
| 906 | hor = 1; x++; |
---|
| 907 | } else { |
---|
| 908 | outer = 0; dir = 0; |
---|
| 909 | hor = 0; y--; |
---|
| 910 | vlm[y*(xsz+1)+x] = -vlm[y*(xsz+1)+x]; /* mark as seen */ |
---|
| 911 | } |
---|
| 912 | |
---|
| 913 | while(x!=startx || y!=starty) { |
---|
| 914 | #if 0 |
---|
| 915 | printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir); |
---|
| 916 | #endif |
---|
| 917 | |
---|
| 918 | /* initialization common for try1 and try2 */ |
---|
| 919 | if(hor) { |
---|
| 920 | lm = vlm; maxx = xsz+1; maxy = ysz; newhor = 0; |
---|
| 921 | } else { |
---|
| 922 | lm = hlm; maxx = xsz; maxy = ysz+1; newhor = 1; |
---|
| 923 | } |
---|
| 924 | xor = (outer ^ hor ^ dir); |
---|
| 925 | |
---|
| 926 | try1: |
---|
| 927 | /* first we try to change axis, to keep the |
---|
| 928 | * contour as long as possible |
---|
| 929 | */ |
---|
| 930 | |
---|
| 931 | newx = x; newy = y; |
---|
| 932 | if(!hor && (!outer ^ dir)) |
---|
| 933 | newx--; |
---|
| 934 | if(hor && (!outer ^ dir)) |
---|
| 935 | newy--; |
---|
| 936 | |
---|
| 937 | if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) |
---|
| 938 | goto try2; |
---|
| 939 | |
---|
| 940 | if(!xor) |
---|
| 941 | val = L_ON; |
---|
| 942 | else |
---|
| 943 | val = L_OFF; |
---|
| 944 | #if 0 |
---|
| 945 | printf("try 1, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], |
---|
| 946 | (newhor ? 'h':'v'), newx, newy); |
---|
| 947 | #endif |
---|
| 948 | if( lm[newy*maxx + newx] == val ) |
---|
| 949 | goto gotit; |
---|
| 950 | |
---|
| 951 | try2: |
---|
| 952 | /* try to change the axis anyway */ |
---|
| 953 | |
---|
| 954 | newx = x; newy = y; |
---|
| 955 | if(!hor && (outer ^ dir)) |
---|
| 956 | newx--; |
---|
| 957 | if(hor && (outer ^ dir)) |
---|
| 958 | newy--; |
---|
| 959 | |
---|
| 960 | if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) |
---|
| 961 | goto try3; |
---|
| 962 | |
---|
| 963 | if(xor) |
---|
| 964 | val = L_ON; |
---|
| 965 | else |
---|
| 966 | val = L_OFF; |
---|
| 967 | #if 0 |
---|
| 968 | printf("try 2, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], |
---|
| 969 | (newhor ? 'h':'v'), newx, newy); |
---|
| 970 | #endif |
---|
| 971 | if( lm[newy*maxx + newx] == val ) |
---|
| 972 | goto gotit; |
---|
| 973 | |
---|
| 974 | try3: |
---|
| 975 | /* try to continue in the old direction */ |
---|
| 976 | |
---|
| 977 | if(hor) { |
---|
| 978 | lm = hlm; maxx = xsz; maxy = ysz+1; |
---|
| 979 | } else { |
---|
| 980 | lm = vlm; maxx = xsz+1; maxy = ysz; |
---|
| 981 | } |
---|
| 982 | newhor = hor; |
---|
| 983 | newx = x; newy = y; |
---|
| 984 | if(hor && dir) |
---|
| 985 | newx--; |
---|
| 986 | if(!hor && !dir) |
---|
| 987 | newy--; |
---|
| 988 | |
---|
| 989 | if(newx < 0 || newx >= maxx || newy < 0 || newy >= maxy) |
---|
| 990 | goto badtry; |
---|
| 991 | |
---|
| 992 | if(dir) |
---|
| 993 | val = L_ON; |
---|
| 994 | else |
---|
| 995 | val = L_OFF; |
---|
| 996 | #if 0 |
---|
| 997 | printf("try 3, want %d have %d at %c(%d, %d)\n", val, lm[newy*maxx + newx], |
---|
| 998 | (newhor ? 'h':'v'), newx, newy); |
---|
| 999 | #endif |
---|
| 1000 | if( lm[newy*maxx + newx] == val ) |
---|
| 1001 | goto gotit; |
---|
| 1002 | |
---|
| 1003 | badtry: |
---|
| 1004 | fprintf(stderr, "**** Internal error in the contour detection code at (%d, %d)\n", x, y); |
---|
| 1005 | fprintf(stderr, "glyph='%s' outer=%d hor=%d dir=%d\n", g->name, outer, hor, dir); |
---|
| 1006 | fflush(stdout); |
---|
| 1007 | exit(1); |
---|
| 1008 | |
---|
| 1009 | gotit: |
---|
| 1010 | if(hor != newhor) { /* changed direction, end the previous line */ |
---|
| 1011 | ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */ |
---|
| 1012 | firstx = x; firsty = y; |
---|
| 1013 | } |
---|
| 1014 | lm[newy*maxx + newx] = -lm[newy*maxx + newx]; |
---|
| 1015 | hor = newhor; |
---|
| 1016 | dir = (val == L_ON); |
---|
| 1017 | if(newhor) |
---|
| 1018 | x -= (dir<<1)-1; |
---|
| 1019 | else |
---|
| 1020 | y += (dir<<1)-1; |
---|
| 1021 | } |
---|
| 1022 | #if 0 |
---|
| 1023 | printf("trace (%d, %d) outer=%d hor=%d dir=%d\n", x, y, outer, hor, dir); |
---|
| 1024 | #endif |
---|
| 1025 | ig_rlineto(g, x+xoff, y+yoff); /* intermediate as int */ |
---|
| 1026 | g_closepath(g); |
---|
| 1027 | } |
---|
| 1028 | |
---|
| 1029 | |
---|
| 1030 | /* try to vectorize the curves and sloped lines in the bitmap */ |
---|
| 1031 | if(vectorize) { |
---|
| 1032 | GENTRY *ge, *pge, *cge, *loopge; |
---|
| 1033 | int i; |
---|
| 1034 | int skip; |
---|
| 1035 | |
---|
| 1036 | dumppaths(g, NULL, NULL); |
---|
| 1037 | |
---|
| 1038 | /* allocate the extensions */ |
---|
| 1039 | for(cge=g->entries; cge!=0; cge=cge->next) { |
---|
| 1040 | cge->ext = calloc(1, sizeof(GEX_FRAG) ); |
---|
| 1041 | if(cge->ext == 0) { |
---|
| 1042 | fprintf (stderr, "****malloc failed %s line %d\n", __FILE__, __LINE__); |
---|
| 1043 | exit(255); |
---|
| 1044 | } |
---|
| 1045 | } |
---|
| 1046 | |
---|
| 1047 | for(cge=g->entries; cge!=0; cge=cge->next) { |
---|
| 1048 | if(cge->type != GE_MOVE) |
---|
| 1049 | continue; |
---|
| 1050 | |
---|
| 1051 | /* we've found the beginning of a contour */ |
---|
| 1052 | { |
---|
| 1053 | int d, vert, count, stepmore, delaystop; |
---|
| 1054 | int vdir, hdir, fullvdir, fullhdir, len; |
---|
| 1055 | int dx, dy, lastdx, lastdy; |
---|
| 1056 | int k1, k2, reversal, smooth, good; |
---|
| 1057 | int line[2 /*H,V*/], maxlen[2 /*H,V*/], minlen[2 /*H,V*/]; |
---|
| 1058 | GENTRY **age; /* array of gentries in a contour */ |
---|
| 1059 | int clen; /* contour length, size of ths array */ |
---|
| 1060 | int i, j; |
---|
| 1061 | GEX_FRAG *f; |
---|
| 1062 | |
---|
| 1063 | /* we know that all the contours start at the top-left corner, |
---|
| 1064 | * so at most it might be before/after the last element of |
---|
| 1065 | * the last/first fragment |
---|
| 1066 | */ |
---|
| 1067 | |
---|
| 1068 | ge = cge->next; |
---|
| 1069 | pge = ge->bkwd; |
---|
| 1070 | if(ge->ix3 == pge->ix3) { /* a vertical line */ |
---|
| 1071 | /* we want to start always from a horizontal line because |
---|
| 1072 | * then we always start from top and that is quaranteed to be a |
---|
| 1073 | * fragment boundary, so move the start point of the contour |
---|
| 1074 | */ |
---|
| 1075 | pge->prev->next = pge->next; |
---|
| 1076 | pge->next->prev = pge->prev; |
---|
| 1077 | cge->next = pge; |
---|
| 1078 | pge->prev = cge; |
---|
| 1079 | pge->next = ge; |
---|
| 1080 | ge->prev = pge; |
---|
| 1081 | ge = pge; pge = ge->bkwd; |
---|
| 1082 | cge->ix3 = pge->ix3; cge->iy3 = pge->iy3; |
---|
| 1083 | } |
---|
| 1084 | |
---|
| 1085 | /* fill the array of gentries */ |
---|
| 1086 | clen = 1; |
---|
| 1087 | for(ge = cge->next->frwd; ge != cge->next; ge = ge->frwd) |
---|
| 1088 | clen++; |
---|
| 1089 | age = (GENTRY **)malloc(sizeof(*age) * clen); |
---|
| 1090 | ge = cge->next; |
---|
| 1091 | count = 0; |
---|
| 1092 | do { |
---|
| 1093 | age[count] = ge; |
---|
| 1094 | X_FRAG(ge)->aidx = count++; |
---|
| 1095 | |
---|
| 1096 | /* and by the way find the extremums */ |
---|
| 1097 | for(i=0; i<2; i++) { |
---|
| 1098 | if( isign(ge->frwd->ipoints[i][2] - ge->ipoints[i][2]) |
---|
| 1099 | * isign(ge->bkwd->bkwd->ipoints[i][2] - ge->bkwd->ipoints[i][2]) == 1) { |
---|
| 1100 | X_FRAG(ge)->flags |= GEXFF_EXTR; |
---|
| 1101 | fprintf(stderr, " %s extremum at %p\n", (i?"vert":"hor"), ge); |
---|
| 1102 | } |
---|
| 1103 | if(abs(ge->ipoints[i][2] - ge->bkwd->ipoints[i][2]) > 1) |
---|
| 1104 | X_FRAG(ge)->flags |= GEXFF_LONG; |
---|
| 1105 | } |
---|
| 1106 | |
---|
| 1107 | ge = ge->frwd; |
---|
| 1108 | } while(ge != cge->next); |
---|
| 1109 | |
---|
| 1110 | /* Find the serif fragments, looking as either of: |
---|
| 1111 | * -+ | |
---|
| 1112 | * | | |
---|
| 1113 | * +-+ +-+ |
---|
| 1114 | * | | |
---|
| 1115 | * +--... +--... |
---|
| 1116 | * with the thickness of serifs being 1 pixel. We make no |
---|
| 1117 | * difference between serifs on vertical and horizontal stems. |
---|
| 1118 | */ |
---|
| 1119 | |
---|
| 1120 | ge = cge->next; |
---|
| 1121 | do { |
---|
| 1122 | GENTRY *nge; |
---|
| 1123 | int pdx, pdy, ndx, ndy; |
---|
| 1124 | |
---|
| 1125 | /* two gentries of length 1 mean a potential serif */ |
---|
| 1126 | pge = ge->bkwd; |
---|
| 1127 | nge = ge->frwd; |
---|
| 1128 | |
---|
| 1129 | dx = nge->ix3 - pge->ix3; |
---|
| 1130 | dy = nge->iy3 - pge->iy3; |
---|
| 1131 | |
---|
| 1132 | if(abs(dx) != 1 || abs(dy) != 1) /* 2 small ones */ |
---|
| 1133 | continue; |
---|
| 1134 | |
---|
| 1135 | if( |
---|
| 1136 | (!(X_FRAG(ge)->flags & GEXFF_EXTR) |
---|
| 1137 | || !(X_FRAG(ge->bkwd)->flags & GEXFF_EXTR)) |
---|
| 1138 | && (!(X_FRAG(ge->frwd)->flags & GEXFF_EXTR) |
---|
| 1139 | || !(X_FRAG(ge->frwd->frwd)->flags & GEXFF_EXTR)) |
---|
| 1140 | ) |
---|
| 1141 | continue; /* either side must be a couple of extremums */ |
---|
| 1142 | |
---|
| 1143 | pdx = pge->ix3 - pge->bkwd->ix3; |
---|
| 1144 | pdy = pge->iy3 - pge->bkwd->iy3; |
---|
| 1145 | ndx = nge->frwd->ix3 - nge->ix3; |
---|
| 1146 | ndy = nge->frwd->iy3 - nge->iy3; |
---|
| 1147 | |
---|
| 1148 | if(pdx*dx + pdy*dy > 0 && ndx*dx + ndy*dy > 0) |
---|
| 1149 | continue; /* definitely not a serif but a round corner */ |
---|
| 1150 | |
---|
| 1151 | if(abs(pdx) + abs(pdy) == 1 || abs(ndx) + abs(ndy) == 1) |
---|
| 1152 | continue; |
---|
| 1153 | |
---|
| 1154 | /* we've found a serif including this and next gentry */ |
---|
| 1155 | X_FRAG(ge)->len[GEXFI_SERIF] = 2; |
---|
| 1156 | |
---|
| 1157 | } while( (ge = ge->frwd) != cge->next ); |
---|
| 1158 | |
---|
| 1159 | |
---|
| 1160 | /* Find the convex and concave fragments, defined as: |
---|
| 1161 | * convex (clockwise): dy/dx <= dy0/dx0, |
---|
| 1162 | * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx > 0 |
---|
| 1163 | * concave (counter-clockwise): dy/dx >= dy0/dx0, |
---|
| 1164 | * or a reversal: dy/dx == - dy0/dx0 && abs(dxthis) == 1 && dy/dx < 0 |
---|
| 1165 | * |
---|
| 1166 | * Where dx and dy are measured between the end of this gentry |
---|
| 1167 | * and the start of the previous one (dx0 and dy0 are the same |
---|
| 1168 | * thing calculated for the previous gentry and its previous one), |
---|
| 1169 | * dxthis is between the end and begginning of this gentry. |
---|
| 1170 | * |
---|
| 1171 | * A reversal is a situation when the curve changes its direction |
---|
| 1172 | * along the x axis, so it passes through a momentary vertical |
---|
| 1173 | * direction. |
---|
| 1174 | */ |
---|
| 1175 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1176 | ge = cge->next; |
---|
| 1177 | pge = ge->bkwd; /* the beginning of the fragment */ |
---|
| 1178 | count = 1; |
---|
| 1179 | lastdx = pge->ix3 - pge->bkwd->bkwd->ix3; |
---|
| 1180 | lastdy = pge->iy3 - pge->bkwd->bkwd->iy3; |
---|
| 1181 | |
---|
| 1182 | #define CHKCURVCONN(ge, msg) do { \ |
---|
| 1183 | dx = (ge)->ix3 - (ge)->bkwd->bkwd->ix3; \ |
---|
| 1184 | dy = (ge)->iy3 - (ge)->bkwd->bkwd->iy3; \ |
---|
| 1185 | if(0 && msg) { \ |
---|
| 1186 | fprintf(stderr, " %p: dx=%d dy=%d dx0=%d dy0=%d ", \ |
---|
| 1187 | (ge), dx, dy, lastdx, lastdy); \ |
---|
| 1188 | } \ |
---|
| 1189 | k1 = X_FRAG(ge)->flags; \ |
---|
| 1190 | k2 = X_FRAG((ge)->bkwd)->flags; \ |
---|
| 1191 | if(0 && msg) { \ |
---|
| 1192 | fprintf(stderr, "fl=%c%c%c%c ", \ |
---|
| 1193 | (k1 & GEXFF_EXTR) ? 'X' : '-', \ |
---|
| 1194 | (k1 & GEXFF_LONG) ? 'L' : '-', \ |
---|
| 1195 | (k2 & GEXFF_EXTR) ? 'X' : '-', \ |
---|
| 1196 | (k2 & GEXFF_LONG) ? 'L' : '-' \ |
---|
| 1197 | ); \ |
---|
| 1198 | } \ |
---|
| 1199 | if( (k1 & GEXFF_EXTR) && (k2 & GEXFF_LONG) \ |
---|
| 1200 | || (k2 & GEXFF_EXTR) && (k1 & GEXFF_LONG) ) { \ |
---|
| 1201 | smooth = 0; \ |
---|
| 1202 | good = reversal = -1; /* for debugging */ \ |
---|
| 1203 | } else { \ |
---|
| 1204 | k1 = dy * lastdx; \ |
---|
| 1205 | k2 = lastdy * dx; \ |
---|
| 1206 | smooth = (abs(dx)==1 || abs(dy)==1); \ |
---|
| 1207 | good = (k1 - k2)*gxf_cvk[d] >= 0; \ |
---|
| 1208 | if(smooth && !good) { \ |
---|
| 1209 | reversal = (k1 == -k2 && abs((ge)->ix3 - (ge)->bkwd->ix3)==1 \ |
---|
| 1210 | && dy*dx*gxf_cvk[d] < 0); \ |
---|
| 1211 | } else \ |
---|
| 1212 | reversal = 0; \ |
---|
| 1213 | } \ |
---|
| 1214 | if(0 && msg) { \ |
---|
| 1215 | fprintf(stderr, "k1=%d k2=%d pge=%p count=%d %s good=%d rev=%d\n", \ |
---|
| 1216 | k1, k2, pge, count, gxf_name[d], good, reversal); \ |
---|
| 1217 | } \ |
---|
| 1218 | } while(0) |
---|
| 1219 | |
---|
| 1220 | do { |
---|
| 1221 | CHKCURVCONN(ge, 1); |
---|
| 1222 | |
---|
| 1223 | if(smooth && (good || reversal) ) |
---|
| 1224 | count++; |
---|
| 1225 | else { |
---|
| 1226 | /* can't continue */ |
---|
| 1227 | #if 0 |
---|
| 1228 | if(count >= 4) { /* worth remembering */ |
---|
| 1229 | fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); |
---|
| 1230 | } |
---|
| 1231 | #endif |
---|
| 1232 | X_FRAG(pge)->len[d] = count; |
---|
| 1233 | if(smooth) { |
---|
| 1234 | pge = ge->bkwd; |
---|
| 1235 | count = 2; |
---|
| 1236 | } else { |
---|
| 1237 | pge = ge; |
---|
| 1238 | count = 1; |
---|
| 1239 | } |
---|
| 1240 | } |
---|
| 1241 | lastdx = dx; lastdy = dy; |
---|
| 1242 | ge = ge->frwd; |
---|
| 1243 | } while(ge != cge->next); |
---|
| 1244 | |
---|
| 1245 | /* see if we can connect the last fragment to the first */ |
---|
| 1246 | CHKCURVCONN(ge, 1); |
---|
| 1247 | |
---|
| 1248 | if(smooth && (good || reversal) ) { |
---|
| 1249 | /* -1 to avoid ge->bkwd being counted twice */ |
---|
| 1250 | if( X_FRAG(ge->bkwd)->len[d] >= 2 ) |
---|
| 1251 | count += X_FRAG(ge->bkwd)->len[d] - 1; |
---|
| 1252 | else if(count == clen+1) { |
---|
| 1253 | /* we are joining a circular (closed) curve, check whether it |
---|
| 1254 | * can be joined at any point or whether it has a discontinuity |
---|
| 1255 | * at the point where we join it now |
---|
| 1256 | */ |
---|
| 1257 | lastdx = dx; lastdy = dy; |
---|
| 1258 | CHKCURVCONN(ge->frwd, 0); |
---|
| 1259 | |
---|
| 1260 | if(smooth && (good || reversal) ) { |
---|
| 1261 | /* yes, the curve is truly a circular one and can be |
---|
| 1262 | * joined at any point |
---|
| 1263 | */ |
---|
| 1264 | |
---|
| 1265 | #if 0 |
---|
| 1266 | fprintf(stderr, " found a circular joint point at %p\n", pge); |
---|
| 1267 | #endif |
---|
| 1268 | /* make sure that in a circular fragment we start from an extremum */ |
---|
| 1269 | while( ! (X_FRAG(pge)->flags & GEXFF_EXTR) ) |
---|
| 1270 | pge = pge->frwd; |
---|
| 1271 | X_FRAG(pge)->flags |= GEXFF_CIRC; |
---|
| 1272 | } |
---|
| 1273 | } |
---|
| 1274 | #if 0 |
---|
| 1275 | fprintf(stderr, " %s joined %p to %p count=%d bk_count=%d\n", gxf_name[d], pge, ge->bkwd, |
---|
| 1276 | count, X_FRAG(ge->bkwd)->len[d] ); |
---|
| 1277 | #endif |
---|
| 1278 | X_FRAG(ge->bkwd)->len[d] = 0; |
---|
| 1279 | } |
---|
| 1280 | X_FRAG(pge)->len[d] = count; |
---|
| 1281 | #if 0 |
---|
| 1282 | if(count >= 4) { /* worth remembering */ |
---|
| 1283 | fprintf(stderr, " %s last frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); |
---|
| 1284 | } |
---|
| 1285 | #endif |
---|
| 1286 | #undef CHKCURVCONN |
---|
| 1287 | |
---|
| 1288 | /* do postprocessing */ |
---|
| 1289 | ge = cge->next; |
---|
| 1290 | do { |
---|
| 1291 | f = X_FRAG(ge); |
---|
| 1292 | len = f->len[d]; |
---|
| 1293 | #if 0 |
---|
| 1294 | fprintf(stderr, " %p %s len=%d clen=%d\n", ge, gxf_name[d], len, clen); |
---|
| 1295 | #endif |
---|
| 1296 | if(len < 3) /* get rid of the fragments that are too short */ |
---|
| 1297 | f->len[d] = 0; |
---|
| 1298 | else if(len == 3) { |
---|
| 1299 | /* _ |
---|
| 1300 | * drop the |_| - shaped fragments, leave alone the _| - shaped |
---|
| 1301 | * (and even those only if not too short in pixels), |
---|
| 1302 | * those left alone are further filtered later |
---|
| 1303 | */ |
---|
| 1304 | k1 = (ge->ix3 == ge->bkwd->ix3); /* axis of the start */ |
---|
| 1305 | if(isign(ge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) |
---|
| 1306 | != isign(ge->frwd->ipoints[k1][2] - ge->frwd->frwd->ipoints[k1][2]) |
---|
| 1307 | && abs(ge->frwd->frwd->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) > 2) { |
---|
| 1308 | #if 0 |
---|
| 1309 | fprintf(stderr, " %s frag %p count=%d good shape\n", |
---|
| 1310 | gxf_name[d], ge, count); |
---|
| 1311 | #endif |
---|
| 1312 | } else |
---|
| 1313 | f->len[d] = 0; |
---|
| 1314 | } else if(len == clen+1) |
---|
| 1315 | break; /* a closed fragment, nothing else interesting */ |
---|
| 1316 | else { /* only for open fragments */ |
---|
| 1317 | GENTRY *gem, *gex, *gei, *ges; |
---|
| 1318 | |
---|
| 1319 | ges = ge; /* the start entry */ |
---|
| 1320 | gem = age[(f->aidx + f->len[d])%clen]; /* entry past the end of the fragment */ |
---|
| 1321 | |
---|
| 1322 | gei = ge->frwd; |
---|
| 1323 | if( (ge->ix3 == ge->bkwd->ix3) /* vert */ |
---|
| 1324 | ^ (isign(ge->bkwd->ix3 - gei->ix3)==isign(ge->bkwd->iy3 - gei->iy3)) |
---|
| 1325 | ^ !(d == GEXFI_CONVEX) /* counter-clockwise */ ) { |
---|
| 1326 | |
---|
| 1327 | #if 0 |
---|
| 1328 | fprintf(stderr, " %p: %s potential spurious start\n", ge, gxf_name[d]); |
---|
| 1329 | #endif |
---|
| 1330 | /* the beginning may be a spurious entry */ |
---|
| 1331 | |
---|
| 1332 | gex = 0; /* the extremum closest to the beginning - to be found */ |
---|
| 1333 | for(gei = ge->frwd; gei != gem; gei = gei->frwd) { |
---|
| 1334 | if(X_FRAG(gei)->flags & GEXFF_EXTR) { |
---|
| 1335 | gex = gei; |
---|
| 1336 | break; |
---|
| 1337 | } |
---|
| 1338 | } |
---|
| 1339 | if(gex == 0) |
---|
| 1340 | gex = gem->bkwd; |
---|
| 1341 | |
---|
| 1342 | /* A special case: ignore the spurious ends on small curves that |
---|
| 1343 | * either enclose an 1-pixel-wide extremum or are 1-pixel deep. |
---|
| 1344 | * Any 5-or-less-pixel-long curve with extremum 2 steps away |
---|
| 1345 | * qualifies for that. |
---|
| 1346 | */ |
---|
| 1347 | |
---|
| 1348 | if(len <= 5 && gex == ge->frwd->frwd) { |
---|
| 1349 | good = 0; |
---|
| 1350 | #if 0 |
---|
| 1351 | fprintf(stderr, " E"); |
---|
| 1352 | #endif |
---|
| 1353 | } else { |
---|
| 1354 | good = 1; /* assume that ge is not spurious */ |
---|
| 1355 | |
---|
| 1356 | /* gei goes backwards, gex goes forwards from the extremum */ |
---|
| 1357 | gei = gex; |
---|
| 1358 | /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ |
---|
| 1359 | i = (gex->ix3 != gex->bkwd->ix3); |
---|
| 1360 | j = !i; |
---|
| 1361 | for( ; gei!=ge && gex!=gem; gei=gei->bkwd, gex=gex->frwd) { |
---|
| 1362 | if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] |
---|
| 1363 | || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] |
---|
| 1364 | != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] |
---|
| 1365 | ) { |
---|
| 1366 | good = 0; /* no symmetry - must be spurious */ |
---|
| 1367 | #if 0 |
---|
| 1368 | fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)", |
---|
| 1369 | gei, gex, |
---|
| 1370 | i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2], |
---|
| 1371 | j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2], |
---|
| 1372 | gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] ); |
---|
| 1373 | #endif |
---|
| 1374 | break; |
---|
| 1375 | } |
---|
| 1376 | } |
---|
| 1377 | if(gex == gem) { /* oops, the other side is too short */ |
---|
| 1378 | good = 0; |
---|
| 1379 | #if 0 |
---|
| 1380 | fprintf(stderr, " X"); |
---|
| 1381 | #endif |
---|
| 1382 | } |
---|
| 1383 | if(good && gei == ge) { |
---|
| 1384 | if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) |
---|
| 1385 | != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { |
---|
| 1386 | good = 0; /* oops, goes into another direction */ |
---|
| 1387 | #if 0 |
---|
| 1388 | fprintf(stderr, " D"); |
---|
| 1389 | #endif |
---|
| 1390 | } |
---|
| 1391 | } |
---|
| 1392 | } |
---|
| 1393 | if(!good) { /* it is spurious, drop it */ |
---|
| 1394 | #if 0 |
---|
| 1395 | fprintf(stderr, " %p: %s spurious start\n", ge, gxf_name[d]); |
---|
| 1396 | #endif |
---|
| 1397 | f->len[d] = 0; |
---|
| 1398 | ges = ge->frwd; |
---|
| 1399 | len--; |
---|
| 1400 | X_FRAG(ges)->len[d] = len; |
---|
| 1401 | } |
---|
| 1402 | } |
---|
| 1403 | |
---|
| 1404 | gei = gem->bkwd->bkwd->bkwd; |
---|
| 1405 | if( (gem->ix3 != gem->bkwd->ix3) /* gem->bkwd is vert */ |
---|
| 1406 | ^ (isign(gem->bkwd->ix3 - gei->ix3)==isign(gem->bkwd->iy3 - gei->iy3)) |
---|
| 1407 | ^ (d == GEXFI_CONVEX) /* clockwise */ ) { |
---|
| 1408 | |
---|
| 1409 | #if 0 |
---|
| 1410 | fprintf(stderr, " %p: %s potential spurious end\n", gem->bkwd, gxf_name[d]); |
---|
| 1411 | #endif |
---|
| 1412 | /* the end may be a spurious entry */ |
---|
| 1413 | |
---|
| 1414 | gex = 0; /* the extremum closest to the end - to be found */ |
---|
| 1415 | for(gei = gem->bkwd->bkwd; gei != ges->bkwd; gei = gei->bkwd) { |
---|
| 1416 | if(X_FRAG(gei)->flags & GEXFF_EXTR) { |
---|
| 1417 | gex = gei; |
---|
| 1418 | break; |
---|
| 1419 | } |
---|
| 1420 | } |
---|
| 1421 | if(gex == 0) |
---|
| 1422 | gex = ges; |
---|
| 1423 | |
---|
| 1424 | good = 1; /* assume that gem->bkwd is not spurious */ |
---|
| 1425 | /* gei goes backwards, gex goes forwards from the extremum */ |
---|
| 1426 | gei = gex; |
---|
| 1427 | /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ |
---|
| 1428 | i = (gex->ix3 != gex->bkwd->ix3); |
---|
| 1429 | j = !i; |
---|
| 1430 | for( ; gei!=ges->bkwd && gex!=gem->bkwd; gei=gei->bkwd, gex=gex->frwd) { |
---|
| 1431 | if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] |
---|
| 1432 | || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] |
---|
| 1433 | != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] |
---|
| 1434 | ) { |
---|
| 1435 | good = 0; /* no symmetry - must be spurious */ |
---|
| 1436 | #if 0 |
---|
| 1437 | fprintf(stderr, " M(%p,%p)(%d %d,%d)(%d %d,%d)", |
---|
| 1438 | gei, gex, |
---|
| 1439 | i, gei->bkwd->ipoints[i][2], gex->ipoints[i][2], |
---|
| 1440 | j, gei->bkwd->ipoints[j][2] - gei->ipoints[j][2], |
---|
| 1441 | gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] ); |
---|
| 1442 | #endif |
---|
| 1443 | break; |
---|
| 1444 | } |
---|
| 1445 | } |
---|
| 1446 | if(gei == ges->bkwd) { /* oops, the other side is too short */ |
---|
| 1447 | good = 0; |
---|
| 1448 | #if 0 |
---|
| 1449 | fprintf(stderr, " X"); |
---|
| 1450 | #endif |
---|
| 1451 | } |
---|
| 1452 | if(good && gex == gem->bkwd) { |
---|
| 1453 | if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) |
---|
| 1454 | != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { |
---|
| 1455 | good = 0; /* oops, goes into another direction */ |
---|
| 1456 | #if 0 |
---|
| 1457 | fprintf(stderr, " D"); |
---|
| 1458 | #endif |
---|
| 1459 | } |
---|
| 1460 | } |
---|
| 1461 | if(!good) { /* it is spurious, drop it */ |
---|
| 1462 | #if 0 |
---|
| 1463 | fprintf(stderr, " %p: %s spurious end\n", gem->bkwd, gxf_name[d]); |
---|
| 1464 | #endif |
---|
| 1465 | X_FRAG(ges)->len[d] = --len; |
---|
| 1466 | } |
---|
| 1467 | } |
---|
| 1468 | if(len < 4) { |
---|
| 1469 | X_FRAG(ges)->len[d] = 0; |
---|
| 1470 | #if 0 |
---|
| 1471 | fprintf(stderr, " %p: %s frag discarded, too small now\n", ge, gxf_name[d]); |
---|
| 1472 | #endif |
---|
| 1473 | } |
---|
| 1474 | if(ges != ge) { |
---|
| 1475 | if(ges == cge->next) |
---|
| 1476 | break; /* went around the loop */ |
---|
| 1477 | else { |
---|
| 1478 | ge = ges->frwd; /* don't look at this fragment twice */ |
---|
| 1479 | continue; |
---|
| 1480 | } |
---|
| 1481 | } |
---|
| 1482 | } |
---|
| 1483 | |
---|
| 1484 | ge = ge->frwd; |
---|
| 1485 | } while(ge != cge->next); |
---|
| 1486 | } |
---|
| 1487 | |
---|
| 1488 | /* Find the straight line fragments. |
---|
| 1489 | * Even though the lines are sloped, they are called |
---|
| 1490 | * "vertical" or "horizontal" according to their longer |
---|
| 1491 | * dimension. All the steps in the shother dimension must |
---|
| 1492 | * be 1 pixel long, all the steps in the longer dimension |
---|
| 1493 | * must be within the difference of 1 pixel. |
---|
| 1494 | */ |
---|
| 1495 | for(d = GEXFI_LINE; d<= GEXFI_EXACTLINE; d++) { |
---|
| 1496 | ge = cge->next; |
---|
| 1497 | pge = ge->bkwd; /* the beginning of the fragment */ |
---|
| 1498 | count = 1; |
---|
| 1499 | delaystop = 0; |
---|
| 1500 | do { |
---|
| 1501 | int h; |
---|
| 1502 | |
---|
| 1503 | stepmore = 0; |
---|
| 1504 | hdir = isign(ge->ix3 - ge->bkwd->ix3); |
---|
| 1505 | vdir = isign(ge->iy3 - ge->bkwd->iy3); |
---|
| 1506 | vert = (hdir == 0); |
---|
| 1507 | if(count==1) { |
---|
| 1508 | /* at this point pge==ge->bkwd */ |
---|
| 1509 | /* account for the previous gentry, which was !vert */ |
---|
| 1510 | if(!vert) { /* prev was vertical */ |
---|
| 1511 | maxlen[0] = minlen[0] = 0; |
---|
| 1512 | maxlen[1] = minlen[1] = abs(pge->iy3 - pge->bkwd->iy3); |
---|
| 1513 | line[0] = (maxlen[1] == 1); |
---|
| 1514 | line[1] = 1; |
---|
| 1515 | fullhdir = hdir; |
---|
| 1516 | fullvdir = isign(pge->iy3 - pge->bkwd->iy3); |
---|
| 1517 | } else { |
---|
| 1518 | maxlen[0] = minlen[0] = abs(pge->ix3 - pge->bkwd->ix3); |
---|
| 1519 | maxlen[1] = minlen[1] = 0; |
---|
| 1520 | line[0] = 1; |
---|
| 1521 | line[1] = (maxlen[0] == 1); |
---|
| 1522 | fullhdir = isign(pge->ix3 - pge->bkwd->ix3); |
---|
| 1523 | fullvdir = vdir; |
---|
| 1524 | } |
---|
| 1525 | } |
---|
| 1526 | h = line[0]; /* remember the prevalent direction */ |
---|
| 1527 | #if 0 |
---|
| 1528 | fprintf(stderr, " %p: v=%d(%d) h=%d(%d) vl(%d,%d,%d) hl=(%d,%d,%d) %s count=%d ", |
---|
| 1529 | ge, vdir, fullvdir, hdir, fullhdir, |
---|
| 1530 | line[1], minlen[1], maxlen[1], |
---|
| 1531 | line[0], minlen[0], maxlen[0], |
---|
| 1532 | gxf_name[d], count); |
---|
| 1533 | #endif |
---|
| 1534 | if(vert) { |
---|
| 1535 | if(vdir != fullvdir) |
---|
| 1536 | line[0] = line[1] = 0; |
---|
| 1537 | len = abs(ge->iy3 - ge->bkwd->iy3); |
---|
| 1538 | } else { |
---|
| 1539 | if(hdir != fullhdir) |
---|
| 1540 | line[0] = line[1] = 0; |
---|
| 1541 | len = abs(ge->ix3 - ge->bkwd->ix3); |
---|
| 1542 | } |
---|
| 1543 | #if 0 |
---|
| 1544 | fprintf(stderr, "len=%d\n", len); |
---|
| 1545 | #endif |
---|
| 1546 | if(len != 1) /* this is not a continuation in the short dimension */ |
---|
| 1547 | line[!vert] = 0; |
---|
| 1548 | |
---|
| 1549 | /* can it be a continuation in the long dimension ? */ |
---|
| 1550 | if( line[vert] ) { |
---|
| 1551 | if(maxlen[vert]==0) |
---|
| 1552 | maxlen[vert] = minlen[vert] = len; |
---|
| 1553 | else if(maxlen[vert]==minlen[vert]) { |
---|
| 1554 | if(d == GEXFI_EXACTLINE) { |
---|
| 1555 | if(len != maxlen[vert]) |
---|
| 1556 | line[vert] = 0; /* it can't */ |
---|
| 1557 | } else if(len < maxlen[vert]) { |
---|
| 1558 | if(len < minlen[vert]-1) |
---|
| 1559 | line[vert] = 0; /* it can't */ |
---|
| 1560 | else |
---|
| 1561 | minlen[vert] = len; |
---|
| 1562 | } else { |
---|
| 1563 | if(len > maxlen[vert]+1) |
---|
| 1564 | line[vert] = 0; /* it can't */ |
---|
| 1565 | else |
---|
| 1566 | maxlen[vert] = len; |
---|
| 1567 | } |
---|
| 1568 | } else if(len < minlen[vert] || len > maxlen[vert]) |
---|
| 1569 | line[vert] = 0; /* it can't */ |
---|
| 1570 | } |
---|
| 1571 | |
---|
| 1572 | if(line[0] == 0 && line[1] == 0) { |
---|
| 1573 | #if 0 |
---|
| 1574 | if(count >= 3) |
---|
| 1575 | fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); |
---|
| 1576 | #endif |
---|
| 1577 | X_FRAG(pge)->len[d] = count; |
---|
| 1578 | if(d == GEXFI_EXACTLINE && h) { |
---|
| 1579 | X_FRAG(pge)->flags |= GEXFF_HLINE; |
---|
| 1580 | } |
---|
| 1581 | if(count == 1) |
---|
| 1582 | pge = ge; |
---|
| 1583 | else { |
---|
| 1584 | stepmore = 1; /* may reconsider the 1st gentry */ |
---|
| 1585 | pge = ge = ge->bkwd; |
---|
| 1586 | count = 1; |
---|
| 1587 | } |
---|
| 1588 | } else |
---|
| 1589 | count++; |
---|
| 1590 | |
---|
| 1591 | ge = ge->frwd; |
---|
| 1592 | if(ge == cge->next && !stepmore) |
---|
| 1593 | delaystop = 1; /* consider the first gentry again */ |
---|
| 1594 | } while(stepmore || ge != cge->next ^ delaystop); |
---|
| 1595 | /* see if there is an unfinished line left */ |
---|
| 1596 | if(count != 1) { |
---|
| 1597 | #if 0 |
---|
| 1598 | if(count >= 3) |
---|
| 1599 | fprintf(stderr, " %s frag %p-%p count=%d\n", gxf_name[d], pge, ge->bkwd, count); |
---|
| 1600 | #endif |
---|
| 1601 | X_FRAG(ge->bkwd->bkwd)->len[d] = 0; |
---|
| 1602 | X_FRAG(pge)->len[d] = count; |
---|
| 1603 | } |
---|
| 1604 | } |
---|
| 1605 | |
---|
| 1606 | /* do postprocessing of the lines */ |
---|
| 1607 | #if 0 |
---|
| 1608 | fprintf(stderr, "Line postprocessing\n"); |
---|
| 1609 | gex_dump_contour(cge->next, clen); |
---|
| 1610 | #endif |
---|
| 1611 | |
---|
| 1612 | /* the non-exact line frags are related to exact line frags sort |
---|
| 1613 | * of like to individual gentries: two kinds of exact frags |
---|
| 1614 | * must be interleaved, with one kind having the size of 3 |
---|
| 1615 | * and the other kind having the size varying within +-2. |
---|
| 1616 | */ |
---|
| 1617 | |
---|
| 1618 | ge = cge->next; |
---|
| 1619 | do { |
---|
| 1620 | GEX_FRAG *pf, *lastf1, *lastf2; |
---|
| 1621 | int len1, len2, fraglen; |
---|
| 1622 | |
---|
| 1623 | f = X_FRAG(ge); |
---|
| 1624 | |
---|
| 1625 | fraglen = f->len[GEXFI_LINE]; |
---|
| 1626 | if(fraglen >= 4) { |
---|
| 1627 | |
---|
| 1628 | vert = 0; /* vert is a pseudo-directon */ |
---|
| 1629 | line[0] = line[1] = 1; |
---|
| 1630 | maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0; |
---|
| 1631 | lastf2 = lastf1 = f; |
---|
| 1632 | len2 = len1 = 0; |
---|
| 1633 | for(pge = ge, i = 1; i < fraglen; i++, pge=pge->frwd) { |
---|
| 1634 | pf = X_FRAG(pge); |
---|
| 1635 | len = pf->len[GEXFI_EXACTLINE]; |
---|
| 1636 | #if 0 |
---|
| 1637 | fprintf(stderr, " pge=%p i=%d of %d ge=%p exLen=%d\n", pge, i, |
---|
| 1638 | f->len[GEXFI_LINE], ge, len); |
---|
| 1639 | #endif |
---|
| 1640 | len1++; len2++; |
---|
| 1641 | if(len==0) { |
---|
| 1642 | continue; |
---|
| 1643 | } |
---|
| 1644 | vert = !vert; /* alternate the pseudo-direction */ |
---|
| 1645 | if(len > 3) |
---|
| 1646 | line[!vert] = 0; |
---|
| 1647 | if(maxlen[vert] == 0) |
---|
| 1648 | maxlen[vert] = minlen[vert] = len; |
---|
| 1649 | else if(maxlen[vert]-2 >= len && minlen[vert]+2 <= len) { |
---|
| 1650 | if(len > maxlen[vert]) |
---|
| 1651 | maxlen[vert] = len; |
---|
| 1652 | else if(len < minlen[vert]) |
---|
| 1653 | minlen[vert] = len; |
---|
| 1654 | } else |
---|
| 1655 | line[vert] = 0; |
---|
| 1656 | if(line[0] == 0 && line[1] == 0) { |
---|
| 1657 | #if 0 |
---|
| 1658 | fprintf(stderr, " Line breaks at %p %c(%d, %d) %c(%d, %d) len=%d fl=%d l2=%d l1=%d\n", |
---|
| 1659 | pge, (!vert)?'*':' ', minlen[0], maxlen[0], |
---|
| 1660 | vert?'*':' ', minlen[1], maxlen[1], len, fraglen, len2, len1); |
---|
| 1661 | #endif |
---|
| 1662 | if(lastf2 != lastf1) { |
---|
| 1663 | lastf2->len[GEXFI_LINE] = len2-len1; |
---|
| 1664 | } |
---|
| 1665 | lastf1->len[GEXFI_LINE] = len1+1; |
---|
| 1666 | pf->len[GEXFI_LINE] = fraglen+1 - i; |
---|
| 1667 | #if 0 |
---|
| 1668 | gex_dump_contour(pge, clen); |
---|
| 1669 | #endif |
---|
| 1670 | |
---|
| 1671 | /* continue with the line */ |
---|
| 1672 | vert = 0; /* vert is a pseudo-directon */ |
---|
| 1673 | line[0] = line[1] = 1; |
---|
| 1674 | maxlen[0] = minlen[0] = maxlen[1] = minlen[1] = 0; |
---|
| 1675 | lastf2 = lastf1 = f; |
---|
| 1676 | len2 = len1 = 0; |
---|
| 1677 | } else { |
---|
| 1678 | lastf1 = pf; |
---|
| 1679 | len1 = 0; |
---|
| 1680 | } |
---|
| 1681 | } |
---|
| 1682 | } |
---|
| 1683 | |
---|
| 1684 | ge = ge->frwd; |
---|
| 1685 | } while(ge != cge->next); |
---|
| 1686 | #if 0 |
---|
| 1687 | fprintf(stderr, "Line postprocessing part 2\n"); |
---|
| 1688 | gex_dump_contour(cge->next, clen); |
---|
| 1689 | #endif |
---|
| 1690 | |
---|
| 1691 | ge = cge->next; |
---|
| 1692 | do { |
---|
| 1693 | f = X_FRAG(ge); |
---|
| 1694 | |
---|
| 1695 | if(f->len[GEXFI_LINE] >= 4) { |
---|
| 1696 | len = f->len[GEXFI_EXACTLINE]; |
---|
| 1697 | /* if a non-exact line covers precisely two exact lines, |
---|
| 1698 | * split it |
---|
| 1699 | */ |
---|
| 1700 | if(len > 0 && f->len[GEXFI_LINE] >= len+1) { |
---|
| 1701 | GEX_FRAG *pf; |
---|
| 1702 | pge = age[(f->aidx + len - 1)%clen]; /* last gentry of exact line */ |
---|
| 1703 | pf = X_FRAG(pge); |
---|
| 1704 | if(f->len[GEXFI_LINE] + 1 == len + pf->len[GEXFI_EXACTLINE]) { |
---|
| 1705 | f->len[GEXFI_LINE] = len; |
---|
| 1706 | f->flags |= GEXFF_SPLIT; |
---|
| 1707 | pf->len[GEXFI_LINE] = pf->len[GEXFI_EXACTLINE]; |
---|
| 1708 | pf->flags |= GEXFF_SPLIT; |
---|
| 1709 | } |
---|
| 1710 | } |
---|
| 1711 | } |
---|
| 1712 | |
---|
| 1713 | ge = ge->frwd; |
---|
| 1714 | } while(ge != cge->next); |
---|
| 1715 | #if 0 |
---|
| 1716 | fprintf(stderr, "Line postprocessing part 2a\n"); |
---|
| 1717 | gex_dump_contour(cge->next, clen); |
---|
| 1718 | #endif |
---|
| 1719 | ge = cge->next; |
---|
| 1720 | do { |
---|
| 1721 | f = X_FRAG(ge); |
---|
| 1722 | |
---|
| 1723 | /* too small lines are of no interest */ |
---|
| 1724 | if( (f->flags & GEXFF_SPLIT)==0 && f->len[GEXFI_LINE] < 4) |
---|
| 1725 | f->len[GEXFI_LINE] = 0; |
---|
| 1726 | |
---|
| 1727 | len = f->len[GEXFI_EXACTLINE]; |
---|
| 1728 | /* too small exact lines are of no interest */ |
---|
| 1729 | if(len < 3) /* exact lines may be shorter */ |
---|
| 1730 | f->len[GEXFI_EXACTLINE] = 0; |
---|
| 1731 | /* get rid of inexact additions to the end of the exact lines */ |
---|
| 1732 | else if(f->len[GEXFI_LINE] == len+1) |
---|
| 1733 | f->len[GEXFI_LINE] = len; |
---|
| 1734 | /* same at the beginning */ |
---|
| 1735 | else { |
---|
| 1736 | int diff = X_FRAG(ge->bkwd)->len[GEXFI_LINE] - len; |
---|
| 1737 | |
---|
| 1738 | if(diff == 1 || diff == 2) { |
---|
| 1739 | X_FRAG(ge->bkwd)->len[GEXFI_LINE] = 0; |
---|
| 1740 | f->len[GEXFI_LINE] = len; |
---|
| 1741 | } |
---|
| 1742 | } |
---|
| 1743 | |
---|
| 1744 | ge = ge->frwd; |
---|
| 1745 | } while(ge != cge->next); |
---|
| 1746 | #if 0 |
---|
| 1747 | fprintf(stderr, "Line postprocessing is completed\n"); |
---|
| 1748 | gex_dump_contour(cge->next, clen); |
---|
| 1749 | #endif |
---|
| 1750 | |
---|
| 1751 | gex_calc_lenback(cge->next, clen); /* prepare data */ |
---|
| 1752 | |
---|
| 1753 | /* resolve conflicts between lines and curves */ |
---|
| 1754 | |
---|
| 1755 | /* |
---|
| 1756 | * the short (3-gentry) curve frags must have one of the ends |
---|
| 1757 | * coinciding with another curve frag of the same type |
---|
| 1758 | */ |
---|
| 1759 | |
---|
| 1760 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1761 | ge = cge->next; |
---|
| 1762 | do { |
---|
| 1763 | f = X_FRAG(ge); |
---|
| 1764 | |
---|
| 1765 | if(f->len[d] == 3) { |
---|
| 1766 | pge = age[(f->aidx + 2)%clen]; /* last gentry of this frag */ |
---|
| 1767 | if(f->lenback[d] == 0 && X_FRAG(pge)->len[d] == 0) { |
---|
| 1768 | fprintf(stderr, " discarded small %s at %p-%p\n", gxf_name[d], ge, pge); |
---|
| 1769 | f->len[d] = 0; |
---|
| 1770 | X_FRAG(ge->frwd)->lenback[d] = 0; |
---|
| 1771 | X_FRAG(ge->frwd->frwd)->lenback[d] = 0; |
---|
| 1772 | } |
---|
| 1773 | } |
---|
| 1774 | ge = ge->frwd; |
---|
| 1775 | } while(ge != cge->next); |
---|
| 1776 | } |
---|
| 1777 | |
---|
| 1778 | /* the serifs take priority over everything else */ |
---|
| 1779 | ge = cge->next; |
---|
| 1780 | do { |
---|
| 1781 | f = X_FRAG(ge); |
---|
| 1782 | |
---|
| 1783 | len = f->len[GEXFI_SERIF]; |
---|
| 1784 | if(len == 0) |
---|
| 1785 | continue; |
---|
| 1786 | |
---|
| 1787 | if(len != 2) { /* this is used in the code below */ |
---|
| 1788 | fprintf(stderr, "Internal error at %s line %d: serif frags len is %d\n", |
---|
| 1789 | __FILE__, __LINE__, len); |
---|
| 1790 | exit(1); |
---|
| 1791 | } |
---|
| 1792 | |
---|
| 1793 | for(d = 0; d < GEXFI_SERIF; d++) { |
---|
| 1794 | /* serifs may not have common ends with the other fragments, |
---|
| 1795 | * this is expressed as extending them by 1 gentry on each side |
---|
| 1796 | */ |
---|
| 1797 | frag_subtract(g, age, clen, ge->bkwd, len+2, d); |
---|
| 1798 | } |
---|
| 1799 | } while( (ge = ge->frwd) != cge->next); |
---|
| 1800 | |
---|
| 1801 | /* |
---|
| 1802 | * longer exact lines take priority over curves; shorter lines |
---|
| 1803 | * and inexact lines are resolved with convex/concave conflicts |
---|
| 1804 | */ |
---|
| 1805 | ge = cge->next; |
---|
| 1806 | do { |
---|
| 1807 | f = X_FRAG(ge); |
---|
| 1808 | |
---|
| 1809 | len = f->len[GEXFI_EXACTLINE]; |
---|
| 1810 | |
---|
| 1811 | if(len < 6) { /* line is short */ |
---|
| 1812 | ge = ge->frwd; |
---|
| 1813 | continue; |
---|
| 1814 | } |
---|
| 1815 | |
---|
| 1816 | fprintf(stderr, " line at %p len=%d\n", ge, f->len[GEXFI_EXACTLINE]); |
---|
| 1817 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1818 | frag_subtract(g, age, clen, ge, len, d); |
---|
| 1819 | } |
---|
| 1820 | |
---|
| 1821 | ge = ge->frwd; |
---|
| 1822 | } while(ge != cge->next); |
---|
| 1823 | |
---|
| 1824 | /* |
---|
| 1825 | * The exact lines take priority over curves that coincide |
---|
| 1826 | * with them or extend by only one gentry on either side |
---|
| 1827 | * (but not both sides). By this time it applies only to the |
---|
| 1828 | * small exact lines. |
---|
| 1829 | * |
---|
| 1830 | * An interesting general case is when a curve matches more |
---|
| 1831 | * than one exact line going diamond-like. |
---|
| 1832 | */ |
---|
| 1833 | |
---|
| 1834 | ge = cge->next; |
---|
| 1835 | do { |
---|
| 1836 | int done, len2; |
---|
| 1837 | int sharpness; |
---|
| 1838 | GEX_FRAG *pf; |
---|
| 1839 | |
---|
| 1840 | f = X_FRAG(ge); |
---|
| 1841 | |
---|
| 1842 | /* "sharpness" shows how a group of exact line frags is connected: if the gentries |
---|
| 1843 | * of some of them overlap, the curve matching requirement is loosened: it may |
---|
| 1844 | * extend up to 1 gentry beyond each end of the group of exact line frags |
---|
| 1845 | * (sharpness=2); otherwise it may extend to only one end (sharpness=1) |
---|
| 1846 | */ |
---|
| 1847 | sharpness = 1; |
---|
| 1848 | |
---|
| 1849 | len = f->len[GEXFI_EXACTLINE]; |
---|
| 1850 | if(len >= 4) { |
---|
| 1851 | while(len < clen) { |
---|
| 1852 | done = 0; |
---|
| 1853 | pf = X_FRAG(ge->bkwd); |
---|
| 1854 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1855 | if(f->len[d] == len || f->len[d] == len+1) { |
---|
| 1856 | |
---|
| 1857 | fprintf(stderr, " removed %s frag at %p len=%d linelen=%d\n", |
---|
| 1858 | gxf_name[d], ge, f->len[d], len); |
---|
| 1859 | pge = ge->frwd; |
---|
| 1860 | for(i = f->len[d]; i > 1; i--, pge = pge->frwd) |
---|
| 1861 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 1862 | f->len[d] = 0; |
---|
| 1863 | gex_dump_contour(ge, clen); |
---|
| 1864 | done = 1; |
---|
| 1865 | } else if(pf->len[d] == len+1 || pf->len[d] == len+sharpness) { |
---|
| 1866 | fprintf(stderr, " removed %s frag at %p len=%d next linelen=%d\n", |
---|
| 1867 | gxf_name[d], ge->bkwd, pf->len[d], len); |
---|
| 1868 | pge = ge; |
---|
| 1869 | for(i = pf->len[d]; i > 1; i--, pge = pge->frwd) |
---|
| 1870 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 1871 | pf->len[d] = 0; |
---|
| 1872 | gex_dump_contour(ge, clen); |
---|
| 1873 | done = 1; |
---|
| 1874 | } |
---|
| 1875 | } |
---|
| 1876 | if(done) |
---|
| 1877 | break; |
---|
| 1878 | |
---|
| 1879 | /* is there any chance to match a sequence of exect lines ? */ |
---|
| 1880 | if(f->len[GEXFI_CONVEX] < len && f->len[GEXFI_CONCAVE] < len |
---|
| 1881 | && pf->len[GEXFI_CONVEX] < len && pf->len[GEXFI_CONCAVE] < len) |
---|
| 1882 | break; |
---|
| 1883 | |
---|
| 1884 | done = 1; |
---|
| 1885 | /* check whether the line is connected to another exact line at an extremum */ |
---|
| 1886 | pge = age[(f->aidx + len - 1)%clen]; /* last gentry of exact line */ |
---|
| 1887 | len2 = X_FRAG(pge)->len[GEXFI_EXACTLINE]; |
---|
| 1888 | if(len2 > 0) { |
---|
| 1889 | if( len2 >= 4 && (X_FRAG(pge)->flags & GEXFF_EXTR) ) { |
---|
| 1890 | len += len2 - 1; |
---|
| 1891 | sharpness = 2; |
---|
| 1892 | done = 0; |
---|
| 1893 | } |
---|
| 1894 | } else { |
---|
| 1895 | /* see if the extremum is between two exact lines */ |
---|
| 1896 | pge = pge->frwd; |
---|
| 1897 | if(X_FRAG(pge)->flags & GEXFF_EXTR) { |
---|
| 1898 | pge = pge->frwd; |
---|
| 1899 | len2 = X_FRAG(pge)->len[GEXFI_EXACTLINE]; |
---|
| 1900 | if(len2 >= 4) { |
---|
| 1901 | len += len2 + 1; |
---|
| 1902 | done = 0; |
---|
| 1903 | } |
---|
| 1904 | } |
---|
| 1905 | } |
---|
| 1906 | if(done) |
---|
| 1907 | break; |
---|
| 1908 | } |
---|
| 1909 | } |
---|
| 1910 | |
---|
| 1911 | ge = ge->frwd; |
---|
| 1912 | } while(ge != cge->next); |
---|
| 1913 | |
---|
| 1914 | /* |
---|
| 1915 | * The lines may cover only whole curves (or otherwise empty space), |
---|
| 1916 | * so cut them where they overlap parts of the curves. If 2 or less |
---|
| 1917 | * gentries are left in the line, remove the line. |
---|
| 1918 | * If a line and a curve fully coincide, remove the line. Otherwise |
---|
| 1919 | * remove the curves that are completely covered by the lines. |
---|
| 1920 | */ |
---|
| 1921 | |
---|
| 1922 | ge = cge->next; |
---|
| 1923 | do { |
---|
| 1924 | f = X_FRAG(ge); |
---|
| 1925 | |
---|
| 1926 | reconsider_line: |
---|
| 1927 | len = f->len[GEXFI_LINE]; |
---|
| 1928 | |
---|
| 1929 | if(len == 0) { |
---|
| 1930 | ge = ge->frwd; |
---|
| 1931 | continue; |
---|
| 1932 | } |
---|
| 1933 | |
---|
| 1934 | if(f->len[GEXFI_CONVEX] >= len |
---|
| 1935 | || f->len[GEXFI_CONCAVE] >= len) { |
---|
| 1936 | line_completely_covered: |
---|
| 1937 | fprintf(stderr, " removed covered Line frag at %p len=%d\n", |
---|
| 1938 | ge, len); |
---|
| 1939 | f->len[GEXFI_LINE] = 0; |
---|
| 1940 | for(pge = ge->frwd; len > 1; len--, pge = pge->frwd) |
---|
| 1941 | X_FRAG(pge)->lenback[GEXFI_LINE] = 0; |
---|
| 1942 | gex_dump_contour(ge, clen); |
---|
| 1943 | ge = ge->frwd; |
---|
| 1944 | continue; |
---|
| 1945 | } |
---|
| 1946 | |
---|
| 1947 | k1 = 0; /* how much to cut at the front */ |
---|
| 1948 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1949 | if(f->lenback[d]) { |
---|
| 1950 | pge = age[(f->aidx + clen - f->lenback[d])%clen]; |
---|
| 1951 | i = X_FRAG(pge)->len[d] - f->lenback[d] - 1; |
---|
| 1952 | if(i > k1) |
---|
| 1953 | k1 = i; |
---|
| 1954 | } |
---|
| 1955 | } |
---|
| 1956 | |
---|
| 1957 | k2 = 0; /* how much to cut at the end */ |
---|
| 1958 | pge = age[(f->aidx + len)%clen]; /* gentry after the end */ |
---|
| 1959 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1960 | i = X_FRAG(pge)->lenback[d] - 1; |
---|
| 1961 | if(i > k2) |
---|
| 1962 | k2 = i; |
---|
| 1963 | } |
---|
| 1964 | |
---|
| 1965 | if(k1+k2 > 0 && k1+k2 >= len-3) { |
---|
| 1966 | fprintf(stderr, " k1=%d k2=%d\n", k1, k2); |
---|
| 1967 | goto line_completely_covered; |
---|
| 1968 | } |
---|
| 1969 | |
---|
| 1970 | |
---|
| 1971 | if(k2 != 0) { /* cut the end */ |
---|
| 1972 | len -= k2; |
---|
| 1973 | f->len[GEXFI_LINE] = len; |
---|
| 1974 | /* pge still points after the end */ |
---|
| 1975 | for(i = k2, pge = pge->bkwd; i > 0; i--, pge = pge->bkwd) |
---|
| 1976 | X_FRAG(pge)->lenback[GEXFI_LINE] = 0; |
---|
| 1977 | } |
---|
| 1978 | if(k1 != 0) { /* cut the beginning */ |
---|
| 1979 | len -= k1; |
---|
| 1980 | f->len[GEXFI_LINE] = 0; |
---|
| 1981 | for(i = 1, pge = ge->frwd; i < k1; i++, pge = pge->frwd) |
---|
| 1982 | X_FRAG(pge)->lenback[GEXFI_LINE] = 0; |
---|
| 1983 | X_FRAG(pge)->len[GEXFI_LINE] = len; |
---|
| 1984 | for(i = 0; i < len; i++, pge = pge->frwd) |
---|
| 1985 | X_FRAG(pge)->lenback[GEXFI_LINE] = i; |
---|
| 1986 | } |
---|
| 1987 | if(k1 != 0 || k2 != 0) { |
---|
| 1988 | fprintf(stderr, " cut Line frag at %p by (%d,%d) to len=%d\n", |
---|
| 1989 | ge, k1, k2, len); |
---|
| 1990 | gex_dump_contour(ge, clen); |
---|
| 1991 | |
---|
| 1992 | goto reconsider_line; /* the line may have to be cut again */ |
---|
| 1993 | } |
---|
| 1994 | pge = age[(f->aidx + k1)%clen]; /* new beginning */ |
---|
| 1995 | good = 1; /* flag: no need do do a debugging dump */ |
---|
| 1996 | for(i=1; i<len; i++, pge = pge->frwd) |
---|
| 1997 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 1998 | if(X_FRAG(pge)->len[d]) { |
---|
| 1999 | fprintf(stderr, " removed %s frag at %p len=%d covered by line\n", |
---|
| 2000 | gxf_name[d], pge, X_FRAG(pge)->len[d], len); |
---|
| 2001 | good = 0; |
---|
| 2002 | } |
---|
| 2003 | X_FRAG(pge)->len[d] = 0; |
---|
| 2004 | } |
---|
| 2005 | pge = age[(f->aidx + k1 + 1)%clen]; /* next after new beginning */ |
---|
| 2006 | for(i=1; i<len; i++, pge = pge->frwd) |
---|
| 2007 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) |
---|
| 2008 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 2009 | if(!good) |
---|
| 2010 | gex_dump_contour(ge, clen); |
---|
| 2011 | |
---|
| 2012 | ge = ge->frwd; |
---|
| 2013 | } while(ge != cge->next); |
---|
| 2014 | |
---|
| 2015 | /* Resolve conflicts between curves */ |
---|
| 2016 | for(d = GEXFI_CONVEX; d<= GEXFI_CONCAVE; d++) { |
---|
| 2017 | dx = (GEXFI_CONVEX + GEXFI_CONCAVE) - d; /* the other type */ |
---|
| 2018 | ge = cge->next; |
---|
| 2019 | do { |
---|
| 2020 | GENTRY *sge; |
---|
| 2021 | |
---|
| 2022 | f = X_FRAG(ge); |
---|
| 2023 | len = f->len[d]; |
---|
| 2024 | if(len < 2) { |
---|
| 2025 | ge = ge->frwd; |
---|
| 2026 | continue; |
---|
| 2027 | } |
---|
| 2028 | sge = ge; /* the start of fragment */ |
---|
| 2029 | |
---|
| 2030 | i = f->len[dx]; |
---|
| 2031 | if(i != 0) { /* two curved frags starting here */ |
---|
| 2032 | /* should be i!=len because otherwise they would be |
---|
| 2033 | * covered by an exact line |
---|
| 2034 | */ |
---|
| 2035 | if(i > len) { |
---|
| 2036 | curve_completely_covered: |
---|
| 2037 | /* remove the convex frag */ |
---|
| 2038 | fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", |
---|
| 2039 | gxf_name[d], ge, len, gxf_name[dx]); |
---|
| 2040 | f->len[d] = 0; |
---|
| 2041 | for(pge = ge->frwd, j = 1; j < len; j++, pge = pge->frwd) |
---|
| 2042 | X_FRAG(pge)->lenback[d] = 0; |
---|
| 2043 | gex_dump_contour(ge, clen); |
---|
| 2044 | |
---|
| 2045 | ge = ge->frwd; /* the frag is gone, nothing more to do */ |
---|
| 2046 | continue; |
---|
| 2047 | } else { |
---|
| 2048 | /* remove the concave frag */ |
---|
| 2049 | fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", |
---|
| 2050 | gxf_name[dx], ge, i, gxf_name[d]); |
---|
| 2051 | f->len[dx] = 0; |
---|
| 2052 | for(pge = ge->frwd, j = 1; j < i; j++, pge = pge->frwd) |
---|
| 2053 | X_FRAG(pge)->lenback[dx] = 0; |
---|
| 2054 | gex_dump_contour(ge, clen); |
---|
| 2055 | } |
---|
| 2056 | } |
---|
| 2057 | |
---|
| 2058 | |
---|
| 2059 | k1 = X_FRAG(ge->frwd)->lenback[dx]; |
---|
| 2060 | if(k1 != 0) { /* conflict at the front */ |
---|
| 2061 | GENTRY *gels, *gele, *gei; |
---|
| 2062 | |
---|
| 2063 | pge = age[(f->aidx + clen - (k1-1))%clen]; /* first gentry of concave frag */ |
---|
| 2064 | k2 = X_FRAG(pge)->len[dx]; /* its length */ |
---|
| 2065 | |
---|
| 2066 | i = k2 - (k1-1); /* amount of overlap */ |
---|
| 2067 | if(i > len) |
---|
| 2068 | i = len; |
---|
| 2069 | /* i >= 2 by definition */ |
---|
| 2070 | if(i >= k2-1) { /* covers the other frag - maybe with 1 gentry showing */ |
---|
| 2071 | fprintf(stderr, " removed %s frag at %p len=%d covered by %s\n", |
---|
| 2072 | gxf_name[dx], pge, k2, gxf_name[d]); |
---|
| 2073 | X_FRAG(pge)->len[dx] = 0; |
---|
| 2074 | for(pge = pge->frwd, j = 1; j < k2; j++, pge = pge->frwd) |
---|
| 2075 | X_FRAG(pge)->lenback[dx] = 0; |
---|
| 2076 | if(i >= len-1) { /* covers our frag too - maybe with 1 gentry showing */ |
---|
| 2077 | /* our frag will be removed as well, prepare a line to replace it */ |
---|
| 2078 | gels = ge; |
---|
| 2079 | gele = age[(f->aidx + i - 1)%clen]; |
---|
| 2080 | fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i); |
---|
| 2081 | X_FRAG(gels)->len[GEXFI_LINE] = i; |
---|
| 2082 | for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++) |
---|
| 2083 | X_FRAG(gei)->lenback[GEXFI_LINE] = j; |
---|
| 2084 | } else { |
---|
| 2085 | gex_dump_contour(ge, clen); |
---|
| 2086 | ge = ge->frwd; |
---|
| 2087 | continue; |
---|
| 2088 | } |
---|
| 2089 | } |
---|
| 2090 | if(i >= len-1) /* covers our frag - maybe with 1 gentry showing */ |
---|
| 2091 | goto curve_completely_covered; |
---|
| 2092 | |
---|
| 2093 | /* XXX need to do something better for the case when a curve frag |
---|
| 2094 | * is actually nothing but an artifact of two other curves of |
---|
| 2095 | * the opposite type touching each other, like on the back of "3" |
---|
| 2096 | */ |
---|
| 2097 | |
---|
| 2098 | /* change the overlapping part to a line */ |
---|
| 2099 | gels = ge; |
---|
| 2100 | gele = age[(f->aidx + i - 1)%clen]; |
---|
| 2101 | /* give preference to local extremums */ |
---|
| 2102 | if(X_FRAG(gels)->flags & GEXFF_EXTR) { |
---|
| 2103 | gels = gels->frwd; |
---|
| 2104 | i--; |
---|
| 2105 | } |
---|
| 2106 | if(X_FRAG(gele)->flags & GEXFF_EXTR) { |
---|
| 2107 | gele = gele->bkwd; |
---|
| 2108 | i--; |
---|
| 2109 | } |
---|
| 2110 | if(gels->bkwd == gele) { |
---|
| 2111 | /* Oops the line became negative. Probably should |
---|
| 2112 | * never happen but I can't think of any formal reasoning |
---|
| 2113 | * leading to that, so check just in case. Restore |
---|
| 2114 | * the previous state. |
---|
| 2115 | */ |
---|
| 2116 | gels = gele; gele = gels->frwd; i = 2; |
---|
| 2117 | } |
---|
| 2118 | |
---|
| 2119 | j = X_FRAG(gels)->lenback[dx] + 1; /* new length */ |
---|
| 2120 | if(j != k2) { |
---|
| 2121 | X_FRAG(pge)->len[dx] = j; |
---|
| 2122 | fprintf(stderr, " cut %s frag at %p len=%d to %p len=%d end overlap with %s\n", |
---|
| 2123 | gxf_name[dx], pge, k2, gels, j, gxf_name[d]); |
---|
| 2124 | for(gei = gels->frwd; j < k2; gei = gei->frwd, j++) |
---|
| 2125 | X_FRAG(gei)->lenback[dx] = 0; |
---|
| 2126 | } |
---|
| 2127 | |
---|
| 2128 | if(gele != ge) { |
---|
| 2129 | sge = gele; |
---|
| 2130 | f->len[d] = 0; |
---|
| 2131 | fprintf(stderr, " cut %s frag at %p len=%d ", gxf_name[d], ge, len); |
---|
| 2132 | len--; |
---|
| 2133 | for(gei = ge->frwd; gei != gele; gei = gei->frwd, len--) |
---|
| 2134 | X_FRAG(gei)->lenback[d] = 0; |
---|
| 2135 | X_FRAG(gele)->len[d] = len; |
---|
| 2136 | X_FRAG(gele)->lenback[d] = 0; |
---|
| 2137 | fprintf(stderr, "to %p len=%d start overlap with %s\n", |
---|
| 2138 | sge, len, gxf_name[dx]); |
---|
| 2139 | for(gei = gei->frwd, j = 1; j < len; gei = gei->frwd, j++) |
---|
| 2140 | X_FRAG(gei)->lenback[d] = j; |
---|
| 2141 | |
---|
| 2142 | } |
---|
| 2143 | if(i > 1) { |
---|
| 2144 | fprintf(stderr, " new Line frag at %p-%p len=%d\n", gels, gele, i); |
---|
| 2145 | X_FRAG(gels)->len[GEXFI_LINE] = i; |
---|
| 2146 | for(gei = gels->frwd, j = 1; j < i; gei = gei->frwd, j++) |
---|
| 2147 | X_FRAG(gei)->lenback[GEXFI_LINE] = j; |
---|
| 2148 | } |
---|
| 2149 | gex_dump_contour(ge, clen); |
---|
| 2150 | } |
---|
| 2151 | |
---|
| 2152 | ge = ge->frwd; |
---|
| 2153 | } while(ge != cge->next); |
---|
| 2154 | } |
---|
| 2155 | |
---|
| 2156 | /* |
---|
| 2157 | * Assert that there are no conflicts any more and |
---|
| 2158 | * for each gentry find the fragment types that start |
---|
| 2159 | * and continue here. |
---|
| 2160 | */ |
---|
| 2161 | ge = cge->next; |
---|
| 2162 | do { |
---|
| 2163 | f = X_FRAG(ge); |
---|
| 2164 | dx = GEXFI_NONE; /* type that starts here */ |
---|
| 2165 | dy = GEXFI_NONE; /* type that goes through here */ |
---|
| 2166 | /* GEXFI_EXACTLINE and GEXFI_SERIF are auxiliary and don't |
---|
| 2167 | * generate any actual lines/curves in the result |
---|
| 2168 | */ |
---|
| 2169 | for(d = GEXFI_CONVEX; d<= GEXFI_LINE; d++) { |
---|
| 2170 | if(f->len[d]) { |
---|
| 2171 | if(dx >= 0) { |
---|
| 2172 | fprintf(stderr, "**** Internal error in vectorization\n"); |
---|
| 2173 | fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n", |
---|
| 2174 | g->name, ge, gxf_name[dx], gxf_name[d]); |
---|
| 2175 | dumppaths(g, cge->next, cge->next->bkwd); |
---|
| 2176 | gex_dump_contour(ge, clen); |
---|
| 2177 | exit(1); |
---|
| 2178 | } |
---|
| 2179 | dx = d; |
---|
| 2180 | } |
---|
| 2181 | if(f->lenback[d]) { |
---|
| 2182 | if(dy >= 0) { |
---|
| 2183 | fprintf(stderr, "**** Internal error in vectorization\n"); |
---|
| 2184 | fprintf(stderr, "CONFLICT in %s at %p between %s and %s\n", |
---|
| 2185 | g->name, ge, gxf_name[dy], gxf_name[d]); |
---|
| 2186 | dumppaths(g, cge->next, cge->next->bkwd); |
---|
| 2187 | gex_dump_contour(ge, clen); |
---|
| 2188 | exit(1); |
---|
| 2189 | } |
---|
| 2190 | dy = d; |
---|
| 2191 | } |
---|
| 2192 | } |
---|
| 2193 | f->ixstart = dx; |
---|
| 2194 | f->ixcont = dy; |
---|
| 2195 | ge = ge->frwd; |
---|
| 2196 | } while(ge != cge->next); |
---|
| 2197 | |
---|
| 2198 | /* |
---|
| 2199 | * make sure that the contour does not start in the |
---|
| 2200 | * middle of a fragment |
---|
| 2201 | */ |
---|
| 2202 | ge = cge->next; /* old start of the contour */ |
---|
| 2203 | f = X_FRAG(ge); |
---|
| 2204 | if(f->ixstart == GEXFI_NONE && f->ixcont != GEXFI_NONE) { |
---|
| 2205 | /* oops, it's mid-fragment, move the start */ |
---|
| 2206 | GENTRY *xge; |
---|
| 2207 | |
---|
| 2208 | xge = ge->bkwd->next; /* entry following the contour */ |
---|
| 2209 | |
---|
| 2210 | /* find the first gentry of this frag */ |
---|
| 2211 | pge = age[(f->aidx + clen - f->lenback[f->ixcont])%clen]; |
---|
| 2212 | |
---|
| 2213 | ge->prev = ge->bkwd; |
---|
| 2214 | ge->bkwd->next = ge; |
---|
| 2215 | |
---|
| 2216 | cge->next = pge; |
---|
| 2217 | pge->prev = cge; |
---|
| 2218 | |
---|
| 2219 | pge->bkwd->next = xge; |
---|
| 2220 | if(xge) |
---|
| 2221 | xge->prev = pge->bkwd; |
---|
| 2222 | |
---|
| 2223 | cge->ix3 = pge->bkwd->ix3; cge->iy3 = pge->bkwd->iy3; |
---|
| 2224 | } |
---|
| 2225 | |
---|
| 2226 | /* vectorize each fragment separately |
---|
| 2227 | * make 2 passes: first handle the straight lines, then |
---|
| 2228 | * the curves to allow the curver to be connected smoothly |
---|
| 2229 | * to the straights |
---|
| 2230 | */ |
---|
| 2231 | ge = cge->next; |
---|
| 2232 | do { /* pass 1 */ |
---|
| 2233 | f = X_FRAG(ge); |
---|
| 2234 | switch(f->ixstart) { |
---|
| 2235 | case GEXFI_LINE: |
---|
| 2236 | len = f->len[GEXFI_LINE]; |
---|
| 2237 | pge = age[(f->aidx + len - 1)%clen]; /* last gentry */ |
---|
| 2238 | |
---|
| 2239 | if(ge->iy3 == ge->bkwd->iy3) { /* frag starts and ends horizontally */ |
---|
| 2240 | k1 = 1/*Y*/ ; /* across the direction of start */ |
---|
| 2241 | k2 = 0/*X*/ ; /* along the direction of start */ |
---|
| 2242 | } else { /* frag starts and ends vertically */ |
---|
| 2243 | k1 = 0/*X*/ ; /* across the direction of start */ |
---|
| 2244 | k2 = 1/*Y*/ ; /* along the direction of start */ |
---|
| 2245 | } |
---|
| 2246 | |
---|
| 2247 | if(len % 2) { |
---|
| 2248 | /* odd number of entries in the frag */ |
---|
| 2249 | double halfstep, halfend; |
---|
| 2250 | |
---|
| 2251 | f->vect[0][k1] = fscale * ge->ipoints[k1][2]; |
---|
| 2252 | f->vect[3][k1] = fscale * pge->ipoints[k1][2]; |
---|
| 2253 | |
---|
| 2254 | halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) |
---|
| 2255 | * 0.5 / ((len+1)/2); |
---|
| 2256 | if(f->ixcont != GEXFI_NONE) { |
---|
| 2257 | halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5; |
---|
| 2258 | if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ |
---|
| 2259 | halfstep = halfend; |
---|
| 2260 | } |
---|
| 2261 | if(X_FRAG(pge)->ixstart != GEXFI_NONE) { |
---|
| 2262 | halfend = (pge->ipoints[k2][2] - pge->bkwd->ipoints[k2][2]) * 0.5; |
---|
| 2263 | if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ |
---|
| 2264 | halfstep = halfend; |
---|
| 2265 | } |
---|
| 2266 | f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep); |
---|
| 2267 | f->vect[3][k2] = fscale * (pge->ipoints[k2][2] - halfstep); |
---|
| 2268 | } else { |
---|
| 2269 | /* even number of entries */ |
---|
| 2270 | double halfstep, halfend; |
---|
| 2271 | |
---|
| 2272 | f->vect[0][k1] = fscale * ge->ipoints[k1][2]; |
---|
| 2273 | halfstep = (pge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) |
---|
| 2274 | * 0.5 / (len/2); |
---|
| 2275 | if(f->ixcont != GEXFI_NONE) { |
---|
| 2276 | halfend = (ge->ipoints[k2][2] - ge->bkwd->ipoints[k2][2]) * 0.5; |
---|
| 2277 | if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ |
---|
| 2278 | halfstep = halfend; |
---|
| 2279 | } |
---|
| 2280 | f->vect[0][k2] = fscale * (ge->bkwd->ipoints[k2][2] + halfstep); |
---|
| 2281 | |
---|
| 2282 | halfstep = (pge->ipoints[k1][2] - ge->bkwd->ipoints[k1][2]) |
---|
| 2283 | * 0.5 / (len/2); |
---|
| 2284 | if(X_FRAG(pge)->ixstart != GEXFI_NONE) { |
---|
| 2285 | halfend = (pge->ipoints[k1][2] - pge->bkwd->ipoints[k1][2]) * 0.5; |
---|
| 2286 | if(fabs(halfstep) < fabs(halfend)) /* must be at least half gentry away */ |
---|
| 2287 | halfstep = halfend; |
---|
| 2288 | } |
---|
| 2289 | f->vect[3][k1] = fscale * (pge->ipoints[k1][2] - halfstep); |
---|
| 2290 | f->vect[3][k2] = fscale * pge->ipoints[k2][2]; |
---|
| 2291 | } |
---|
| 2292 | f->vectlen = len; |
---|
| 2293 | f->flags |= GEXFF_DRAWLINE; |
---|
| 2294 | break; |
---|
| 2295 | } |
---|
| 2296 | } while((ge = ge->frwd) != cge->next); |
---|
| 2297 | |
---|
| 2298 | ge = cge->next; |
---|
| 2299 | do { /* pass 2 */ |
---|
| 2300 | /* data for curves */ |
---|
| 2301 | GENTRY *firstge, *lastge, *gef, *gel, *gei, *gex; |
---|
| 2302 | GENTRY *ordhd; /* head of the order list */ |
---|
| 2303 | GENTRY **ordlast; |
---|
| 2304 | int nsub; /* number of subfrags */ |
---|
| 2305 | GEX_FRAG *ff, *lf, *xf; |
---|
| 2306 | |
---|
| 2307 | f = X_FRAG(ge); |
---|
| 2308 | switch(f->ixstart) { |
---|
| 2309 | case GEXFI_CONVEX: |
---|
| 2310 | case GEXFI_CONCAVE: |
---|
| 2311 | len = f->len[f->ixstart]; |
---|
| 2312 | firstge = ge; |
---|
| 2313 | lastge = age[(f->aidx + len - 1)%clen]; /* last gentry */ |
---|
| 2314 | |
---|
| 2315 | nsub = 0; |
---|
| 2316 | gex = firstge; |
---|
| 2317 | xf = X_FRAG(gex); |
---|
| 2318 | xf->prevsub = 0; |
---|
| 2319 | xf->sublen = 1; |
---|
| 2320 | xf->flags &= ~GEXFF_DONE; |
---|
| 2321 | for(gei = firstge->frwd; gei != lastge; gei = gei->frwd) { |
---|
| 2322 | xf->sublen++; |
---|
| 2323 | if(X_FRAG(gei)->flags & GEXFF_EXTR) { |
---|
| 2324 | xf->nextsub = gei; |
---|
| 2325 | for(i=0; i<2; i++) |
---|
| 2326 | xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]); |
---|
| 2327 | nsub++; |
---|
| 2328 | xf = X_FRAG(gei); |
---|
| 2329 | xf->prevsub = gex; |
---|
| 2330 | xf->sublen = 1; |
---|
| 2331 | xf->flags &= ~GEXFF_DONE; |
---|
| 2332 | gex = gei; |
---|
| 2333 | } |
---|
| 2334 | } |
---|
| 2335 | xf->sublen++; |
---|
| 2336 | xf->nextsub = gei; |
---|
| 2337 | for(i=0; i<2; i++) |
---|
| 2338 | xf->bbox[i] = abs(gei->ipoints[i][2] - gex->bkwd->ipoints[i][2]); |
---|
| 2339 | nsub++; |
---|
| 2340 | ff = xf; /* remember the beginning of the last subfrag */ |
---|
| 2341 | xf = X_FRAG(gei); |
---|
| 2342 | xf->prevsub = gex; |
---|
| 2343 | if(firstge != lastge) { |
---|
| 2344 | xf->nextsub = 0; |
---|
| 2345 | xf->sublen = 0; |
---|
| 2346 | |
---|
| 2347 | /* correct the bounding box of the last and first subfrags for |
---|
| 2348 | * intersections with other fragments |
---|
| 2349 | */ |
---|
| 2350 | if(xf->ixstart != GEXFI_NONE) { |
---|
| 2351 | /* ff points to the beginning of the last subfrag */ |
---|
| 2352 | for(i=0; i<2; i++) |
---|
| 2353 | ff->bbox[i] -= 0.5 * abs(lastge->ipoints[i][2] - lastge->bkwd->ipoints[i][2]); |
---|
| 2354 | } |
---|
| 2355 | ff = X_FRAG(firstge); |
---|
| 2356 | if(ff->ixcont != GEXFI_NONE) { |
---|
| 2357 | for(i=0; i<2; i++) |
---|
| 2358 | ff->bbox[i] -= 0.5 * abs(firstge->ipoints[i][2] - firstge->bkwd->ipoints[i][2]); |
---|
| 2359 | } |
---|
| 2360 | } |
---|
| 2361 | |
---|
| 2362 | fprintf(stderr, " %s frag %p%s nsub=%d\n", gxf_name[f->ixstart], |
---|
| 2363 | ge, (f->flags&GEXFF_CIRC)?" circular":"", nsub); |
---|
| 2364 | |
---|
| 2365 | /* find the symmetry between the subfragments */ |
---|
| 2366 | for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { |
---|
| 2367 | ff = X_FRAG(gef); |
---|
| 2368 | gex = ff->nextsub; |
---|
| 2369 | xf = X_FRAG(gex); |
---|
| 2370 | gel = xf->nextsub; |
---|
| 2371 | if(gel == 0) { |
---|
| 2372 | ff->flags &= ~GEXFF_SYMNEXT; |
---|
| 2373 | break; /* not a circular frag */ |
---|
| 2374 | } |
---|
| 2375 | good = 1; /* assume that we have symmetry */ |
---|
| 2376 | /* gei goes backwards, gex goes forwards from the extremum */ |
---|
| 2377 | gei = gex; |
---|
| 2378 | /* i is the symmetry axis, j is the other axis (X=0 Y=1) */ |
---|
| 2379 | ff->symaxis = i = (gex->ix3 != gex->bkwd->ix3); |
---|
| 2380 | j = !i; |
---|
| 2381 | for( ; gei!=gef && gex!=gel; gei=gei->bkwd, gex=gex->frwd) { |
---|
| 2382 | if( gei->bkwd->ipoints[i][2] != gex->ipoints[i][2] |
---|
| 2383 | || gei->bkwd->ipoints[j][2] - gei->ipoints[j][2] |
---|
| 2384 | != gex->bkwd->ipoints[j][2] - gex->ipoints[j][2] |
---|
| 2385 | ) { |
---|
| 2386 | good = 0; /* no symmetry */ |
---|
| 2387 | break; |
---|
| 2388 | } |
---|
| 2389 | } |
---|
| 2390 | if(good) { |
---|
| 2391 | if( isign(gei->bkwd->ipoints[j][2] - gei->ipoints[j][2]) |
---|
| 2392 | != isign(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) { |
---|
| 2393 | good = 0; /* oops, goes into another direction */ |
---|
| 2394 | } |
---|
| 2395 | } |
---|
| 2396 | if(good) |
---|
| 2397 | ff->flags |= GEXFF_SYMNEXT; |
---|
| 2398 | else |
---|
| 2399 | ff->flags &= ~GEXFF_SYMNEXT; |
---|
| 2400 | } |
---|
| 2401 | |
---|
| 2402 | for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { |
---|
| 2403 | ff = X_FRAG(gef); |
---|
| 2404 | if((ff->flags & GEXFF_SYMNEXT)==0) { |
---|
| 2405 | ff->symxlen = 0; |
---|
| 2406 | continue; |
---|
| 2407 | } |
---|
| 2408 | gex = ff->prevsub; |
---|
| 2409 | if(gex == 0 || (X_FRAG(gex)->flags & GEXFF_SYMNEXT)==0) { |
---|
| 2410 | ff->symxlen = 0; |
---|
| 2411 | continue; |
---|
| 2412 | } |
---|
| 2413 | ff->symxlen = X_FRAG(gex)->sublen; |
---|
| 2414 | xf = X_FRAG(ff->nextsub); |
---|
| 2415 | if(xf->sublen < ff->symxlen) |
---|
| 2416 | ff->symxlen = xf->sublen; |
---|
| 2417 | } |
---|
| 2418 | |
---|
| 2419 | /* find the symmetry inside the subfragments */ |
---|
| 2420 | for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { |
---|
| 2421 | ff = X_FRAG(gef); |
---|
| 2422 | |
---|
| 2423 | if(ff->sublen % 2) { |
---|
| 2424 | /* we must have an even number of gentries for diagonal symmetry */ |
---|
| 2425 | ff->symge = 0; |
---|
| 2426 | continue; |
---|
| 2427 | } |
---|
| 2428 | |
---|
| 2429 | /* gei goes forwards from the front */ |
---|
| 2430 | gei = gef->frwd; |
---|
| 2431 | /* gex goes backwards from the back */ |
---|
| 2432 | gex = ff->nextsub->bkwd; |
---|
| 2433 | |
---|
| 2434 | /* i is the direction of gei, j is the direction of gex */ |
---|
| 2435 | i = (gei->iy3 != gei->bkwd->iy3); |
---|
| 2436 | j = !i; |
---|
| 2437 | for( ; gei->bkwd != gex; gei=gei->frwd, gex=gex->bkwd) { |
---|
| 2438 | if( abs(gei->bkwd->ipoints[i][2] - gei->ipoints[i][2]) |
---|
| 2439 | != abs(gex->bkwd->ipoints[j][2] - gex->ipoints[j][2]) ) |
---|
| 2440 | break; /* no symmetry */ |
---|
| 2441 | i = j; |
---|
| 2442 | j = !j; |
---|
| 2443 | } |
---|
| 2444 | if(gei->bkwd == gex) |
---|
| 2445 | ff->symge = gex; |
---|
| 2446 | else |
---|
| 2447 | ff->symge = 0; /* no symmetry */ |
---|
| 2448 | } |
---|
| 2449 | |
---|
| 2450 | /* find the order of calculation: |
---|
| 2451 | * prefer to start from long fragments that have the longest |
---|
| 2452 | * neighbours symmetric with them, with all being equal prefer |
---|
| 2453 | * the fragments that have smaller physical size |
---|
| 2454 | */ |
---|
| 2455 | ordhd = 0; |
---|
| 2456 | for(gef = firstge, count=0; count < nsub; gef = ff->nextsub, count++) { |
---|
| 2457 | ff = X_FRAG(gef); |
---|
| 2458 | |
---|
| 2459 | for(ordlast = &ordhd; *ordlast != 0; ordlast = &xf->ordersub) { |
---|
| 2460 | xf = X_FRAG(*ordlast); |
---|
| 2461 | if(ff->sublen > xf->sublen) |
---|
| 2462 | break; |
---|
| 2463 | if(ff->sublen < xf->sublen) |
---|
| 2464 | continue; |
---|
| 2465 | if(ff->symxlen > xf->symxlen) |
---|
| 2466 | break; |
---|
| 2467 | if(ff->symxlen < xf->symxlen) |
---|
| 2468 | continue; |
---|
| 2469 | if(ff->bbox[0] < xf->bbox[0] || ff->bbox[1] < xf->bbox[1]) |
---|
| 2470 | break; |
---|
| 2471 | } |
---|
| 2472 | |
---|
| 2473 | ff->ordersub = *ordlast; |
---|
| 2474 | *ordlast = gef; |
---|
| 2475 | } |
---|
| 2476 | |
---|
| 2477 | /* vectorize the subfragments */ |
---|
| 2478 | for(gef = ordhd; gef != 0; gef = ff->ordersub) { |
---|
| 2479 | |
---|
| 2480 | /* debugging stuff */ |
---|
| 2481 | ff = X_FRAG(gef); |
---|
| 2482 | fprintf(stderr, " %p-%p bbox[%g,%g] sym=%p %s len=%d xlen=%d\n", |
---|
| 2483 | gef, ff->nextsub, ff->bbox[0], ff->bbox[1], ff->symge, |
---|
| 2484 | (ff->flags & GEXFF_SYMNEXT) ? "symnext" : "", |
---|
| 2485 | ff->sublen, ff->symxlen); |
---|
| 2486 | |
---|
| 2487 | dosubfrag(g, f->ixstart, firstge, gef, fscale); |
---|
| 2488 | } |
---|
| 2489 | |
---|
| 2490 | break; |
---|
| 2491 | } |
---|
| 2492 | } while((ge = ge->frwd) != cge->next); |
---|
| 2493 | |
---|
| 2494 | free(age); |
---|
| 2495 | |
---|
| 2496 | } |
---|
| 2497 | |
---|
| 2498 | } |
---|
| 2499 | |
---|
| 2500 | /* all the fragments are found, extract the vectorization */ |
---|
| 2501 | pge = g->entries; |
---|
| 2502 | g->entries = g->lastentry = 0; |
---|
| 2503 | g->flags |= GF_FLOAT; |
---|
| 2504 | loopge = 0; |
---|
| 2505 | skip = 0; |
---|
| 2506 | |
---|
| 2507 | for(ge = pge; ge != 0; ge = ge->next) { |
---|
| 2508 | GEX_FRAG *f, *pf; |
---|
| 2509 | |
---|
| 2510 | switch(ge->type) { |
---|
| 2511 | case GE_LINE: |
---|
| 2512 | f = X_FRAG(ge); |
---|
| 2513 | if(skip == 0) { |
---|
| 2514 | if(f->flags & (GEXFF_DRAWLINE|GEXFF_DRAWCURVE)) { |
---|
| 2515 | /* draw a line to the start point */ |
---|
| 2516 | fg_rlineto(g, f->vect[0][0], f->vect[0][1]); |
---|
| 2517 | /* draw the fragment */ |
---|
| 2518 | if(f->flags & GEXFF_DRAWCURVE) |
---|
| 2519 | fg_rrcurveto(g, |
---|
| 2520 | f->vect[1][0], f->vect[1][1], |
---|
| 2521 | f->vect[2][0], f->vect[2][1], |
---|
| 2522 | f->vect[3][0], f->vect[3][1]); |
---|
| 2523 | else |
---|
| 2524 | fg_rlineto(g, f->vect[3][0], f->vect[3][1]); |
---|
| 2525 | skip = f->vectlen - 2; |
---|
| 2526 | } else { |
---|
| 2527 | fg_rlineto(g, fscale * ge->ix3, fscale * ge->iy3); |
---|
| 2528 | } |
---|
| 2529 | } else |
---|
| 2530 | skip--; |
---|
| 2531 | break; |
---|
| 2532 | case GE_MOVE: |
---|
| 2533 | fg_rmoveto(g, -1e6, -1e6); /* will be fixed by GE_PATH */ |
---|
| 2534 | skip = 0; |
---|
| 2535 | /* remember the reference to update it later */ |
---|
| 2536 | loopge = g->lastentry; |
---|
| 2537 | break; |
---|
| 2538 | case GE_PATH: |
---|
| 2539 | /* update the first MOVE of this contour */ |
---|
| 2540 | if(loopge) { |
---|
| 2541 | loopge->fx3 = g->lastentry->fx3; |
---|
| 2542 | loopge->fy3 = g->lastentry->fy3; |
---|
| 2543 | loopge = 0; |
---|
| 2544 | } |
---|
| 2545 | g_closepath(g); |
---|
| 2546 | break; |
---|
| 2547 | } |
---|
| 2548 | } |
---|
| 2549 | for(ge = pge; ge != 0; ge = cge) { |
---|
| 2550 | cge = ge->next; |
---|
| 2551 | free(ge->ext); |
---|
| 2552 | free(ge); |
---|
| 2553 | } |
---|
| 2554 | dumppaths(g, NULL, NULL); |
---|
| 2555 | |
---|
| 2556 | /* end of vectorization logic */ |
---|
| 2557 | } else { |
---|
| 2558 | /* convert the data to float */ |
---|
| 2559 | GENTRY *ge; |
---|
| 2560 | double x, y; |
---|
| 2561 | |
---|
| 2562 | for(ge = g->entries; ge != 0; ge = ge->next) { |
---|
| 2563 | ge->flags |= GEF_FLOAT; |
---|
| 2564 | if(ge->type != GE_MOVE && ge->type != GE_LINE) |
---|
| 2565 | continue; |
---|
| 2566 | |
---|
| 2567 | x = fscale * ge->ix3; |
---|
| 2568 | y = fscale * ge->iy3; |
---|
| 2569 | |
---|
| 2570 | ge->fx3 = x; |
---|
| 2571 | ge->fy3 = y; |
---|
| 2572 | } |
---|
| 2573 | g->flags |= GF_FLOAT; |
---|
| 2574 | } |
---|
| 2575 | |
---|
| 2576 | free(hlm); free(vlm); free(amp); |
---|
| 2577 | } |
---|
| 2578 | |
---|
| 2579 | #if 0 |
---|
| 2580 | /* print out the bitmap */ |
---|
| 2581 | printbmap(bmap, xsz, ysz, xoff, yoff) |
---|
| 2582 | char *bmap; |
---|
| 2583 | int xsz, ysz, xoff, yoff; |
---|
| 2584 | { |
---|
| 2585 | int x, y; |
---|
| 2586 | |
---|
| 2587 | for(y=ysz-1; y>=0; y--) { |
---|
| 2588 | putchar( (y%10==0) ? y/10+'0' : ' ' ); |
---|
| 2589 | putchar( y%10+'0' ); |
---|
| 2590 | for(x=0; x<xsz; x++) |
---|
| 2591 | putchar( bmap[y*xsz+x] ? 'X' : '.' ); |
---|
| 2592 | if(-yoff==y) |
---|
| 2593 | putchar('_'); /* mark the baseline */ |
---|
| 2594 | putchar('\n'); |
---|
| 2595 | } |
---|
| 2596 | putchar(' '); putchar(' '); |
---|
| 2597 | for(x=0; x<xsz; x++) |
---|
| 2598 | putchar( x%10+'0' ); |
---|
| 2599 | putchar('\n'); putchar(' '); putchar(' '); |
---|
| 2600 | for(x=0; x<xsz; x++) |
---|
| 2601 | putchar( (x%10==0) ? x/10+'0' : ' ' ); |
---|
| 2602 | putchar('\n'); |
---|
| 2603 | } |
---|
| 2604 | |
---|
| 2605 | /* print out the limits of outlines */ |
---|
| 2606 | printlimits(hlm, vlm, amp, xsz, ysz) |
---|
| 2607 | char *hlm, *vlm, *amp; |
---|
| 2608 | int xsz, ysz; |
---|
| 2609 | { |
---|
| 2610 | int x, y; |
---|
| 2611 | static char h_char[]={ ' ', '~', '^' }; |
---|
| 2612 | static char v_char[]={ ' ', '(', ')' }; |
---|
| 2613 | |
---|
| 2614 | for(y=ysz-1; y>=0; y--) { |
---|
| 2615 | for(x=0; x<xsz; x++) { |
---|
| 2616 | if(amp[y*xsz+x]) |
---|
| 2617 | putchar('!'); /* ambigouos point is always on a limit */ |
---|
| 2618 | else |
---|
| 2619 | putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]); |
---|
| 2620 | putchar(h_char[ hlm[(y+1)*xsz+x] & (L_ON|L_OFF) ]); |
---|
| 2621 | } |
---|
| 2622 | putchar(v_char[ vlm[y*(xsz+1)+x] & (L_ON|L_OFF) ]); |
---|
| 2623 | putchar('\n'); |
---|
| 2624 | } |
---|
| 2625 | /* last line */ |
---|
| 2626 | for(x=0; x<xsz; x++) { |
---|
| 2627 | putchar(' '); |
---|
| 2628 | putchar(h_char[ hlm[x] & (L_ON|L_OFF) ]); |
---|
| 2629 | } |
---|
| 2630 | putchar(' '); |
---|
| 2631 | putchar('\n'); |
---|
| 2632 | } |
---|
| 2633 | #endif /* 0 */ |
---|