internal_t*i = (internal_t*)dev->internal;
ArtSVP* svp = gfxfillToSVP(line, 1);
+ if (svp->n_segs > 500)
+ {
+ int lineParts = 0;
+ gfxline_t* lineCursor = line;
+ while(lineCursor != NULL)
+ {
+ if(lineCursor->type != gfx_moveTo) ++lineParts;
+ lineCursor = lineCursor->next;
+ }
+ fprintf(stderr, "arts_fill abandonning shape with %d segments (%d line parts)\n", svp->n_segs, lineParts);
+ art_svp_free(svp);
+ return;
+ }
+
svp = art_svp_rewind_uncrossed(art_svp_uncross(svp),ART_WIND_RULE_ODDEVEN); /*FIXME*/
if(i->clip) {
dbg("arts_fillbitmap");
internal_t*i = (internal_t*)dev->internal;
ArtSVP* svp = gfxfillToSVP(line, 1);
+
+ // We need to make sure that the SVP we now have bounds an area (i.e. the
+ // source line wound anticlockwise) rather than excludes an area (i.e. the
+ // line wound clockwise). It seems that PDF (or xpdf) is less strict about
+ // this for bitmaps than it is for fill areas.
+ //
+ // To check this, we'll sum the cross products of all pairs of adjacent
+ // lines. If the result is positive, the direction is correct; if not, we
+ // need to reverse the sense of the SVP generated. The easiest way to do
+ // this is to flip the up/down flags of all the segments.
+ //
+ // This is approximate; since the gfxline_t structure can contain any
+ // combination of moveTo, lineTo and splineTo in any order, not all pairs
+ // of lines in the shape that share a point need be described next to each
+ // other in the sequence. For ease, we'll consider only pairs of lines
+ // stored as lineTos and splineTos without intervening moveTos.
+ //
+ // TODO is this a valid algorithm? My vector maths is rusty.
+ //
+ // It may be more correct to instead reverse the line before we feed it
+ // into gfxfilltoSVP. However, this seems equivalent and is easier to
+ // implement!
+ double total_cross_product = 0.0;
+ gfxline_t* cursor = line;
+ if (cursor != NULL)
+ {
+ double x_last = cursor->x;
+ double y_last = cursor->y;
+ cursor = cursor->next;
+
+ while((cursor != NULL) && (cursor->next != NULL))
+ {
+ if (((cursor->type == gfx_lineTo) || (cursor->type == gfx_splineTo))
+ && ((cursor->next->type == gfx_lineTo) || (cursor->next->type == gfx_splineTo)))
+ {
+ // Process these lines
+ //
+ // In this space (x right, y down) the cross-product is
+ // (x1 * y0) - (x0 * y1)
+ double x0 = cursor->x - x_last;
+ double y0 = cursor->y - y_last;
+ double x1 = cursor->next->x - cursor->x;
+ double y1 = cursor->next->y - cursor->y;
+ total_cross_product += (x1 * y0) - (x0 * y1);
+ }
+
+ x_last = cursor->x;
+ y_last = cursor->y;
+ cursor = cursor->next;
+ }
+ }
+ if (total_cross_product < 0.0)
+ {
+ int i = 0;
+ for(; i < svp->n_segs; ++i)
+ {
+ if (svp->segs[i].dir != 0)
+ svp->segs[i].dir = 0;
+ else
+ svp->segs[i].dir = 1;
+ }
+ }
+
if(i->clip) {
ArtSVP*old = svp;
svp = art_svp_intersect(svp, i->clip->svp);
}
vec[pos].code = ART_END;
+ // Spot adjacent identical points
+ {
+ int j = 1;
+ while(j < pos)
+ {
+ double dx = vec[j].x - vec[j-1].x;
+ double dy = vec[j].y - vec[j-1].y;
+ double d = dx*dx + dy*dy;
+ if ((vec[j-1].x == vec[j].x)
+ && (vec[j-1].y == vec[j].y))
+ {
+ // adjacent identical points; remove one
+ memcpy(&(vec[j]), &(vec[j + 1]), sizeof(vec[j]) * (pos - j));
+ --pos;
+ }
+ else
+ {
+ // different
+ ++j;
+ }
+ }
+ }
+
+ // Check for further non-adjacent identical points. We don't want any
+ // points other than the first and last points to exactly match.
+ //
+ // If we do find matching points, move the second point slightly. This
+ // currently moves the duplicate 2% towards the midpoint of its neighbours
+ // (easier to calculate than 2% down the perpendicular to the line joining
+ // the neighbours) but limiting the change to 0.1 twips to avoid visual
+ // problems when the shapes are large. Note that there is no minimum
+ // change: if the neighbouring points are colinear and equally spaced,
+ // e.g. they were generated as part of a straight spline, then the
+ // duplicate point may not actually move.
+ //
+ // The scan for duplicates algorithm is quadratic in the number of points:
+ // there's probably a better method but the numbers of points is generally
+ // small so this will do for now.
+ {
+ int i = 1, j;
+ for(; i < (pos - 1); ++i)
+ {
+ for (j = 0; j < i; ++j)
+ {
+ if ((vec[i].x == vec[j].x)
+ && (vec[i].y == vec[j].y))
+ {
+ // points match; shuffle point
+ double dx = (vec[i-1].x + vec[i+1].x - (vec[i].x * 2.0)) / 100.0;
+ double dy = (vec[i-1].y + vec[i+1].y - (vec[i].y * 2.0)) / 100.0;
+ double dxxyy = (dx*dx) + (dy*dy);
+ if (dxxyy > 0.01)
+ {
+ // This is more than 0.1 twip's distance; scale down
+ double dscale = sqrt(dxxyy) * 10.0;
+ dx /= dscale;
+ dy /= dscale;
+ };
+ vec[i].x += dx;
+ vec[i].y += dy;
+ break;
+ }
+ }
+ }
+ }
+
return vec;
}