From 70c85e8a814e75b29883fc347a65f83af370249b Mon Sep 17 00:00:00 2001 From: kramm Date: Sun, 18 Mar 2007 18:22:55 +0000 Subject: [PATCH] applied patches by Rupert Wood --- lib/devices/arts.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ lib/devices/artsutils.c | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/lib/devices/arts.c b/lib/devices/arts.c index 68593c6..510df3e 100644 --- a/lib/devices/arts.c +++ b/lib/devices/arts.c @@ -145,6 +145,20 @@ void arts_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color) 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) { @@ -164,6 +178,69 @@ void arts_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxm 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); diff --git a/lib/devices/artsutils.c b/lib/devices/artsutils.c index 8271ffd..ce005f5 100644 --- a/lib/devices/artsutils.c +++ b/lib/devices/artsutils.c @@ -65,6 +65,72 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line) } 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; } -- 1.7.10.4