//======================================================================== // // SplashXPath.cc // //======================================================================== #include #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "SplashMath.h" #include "SplashPath.h" #include "SplashXPath.h" //------------------------------------------------------------------------ #define maxCurveSplits (1 << 10) //------------------------------------------------------------------------ // SplashXPath //------------------------------------------------------------------------ SplashXPath::SplashXPath() { segs = NULL; length = size = 0; } SplashXPath::SplashXPath(SplashPath *path, SplashCoord flatness, GBool closeSubpaths) { SplashCoord xc, yc, dx, dy, r, x0, y0, x1, y1; int quad0, quad1, quad; int i, curSubpath; GBool last; segs = NULL; length = size = 0; i = 0; curSubpath = 0; while (i < path->length) { // first point in subpath - skip it if (path->flags[i] & splashPathFirst) { curSubpath = i; ++i; } else { // curve segment if (path->flags[i] & splashPathCurve) { addCurve(path->pts[i-1].x, path->pts[i-1].y, path->pts[i ].x, path->pts[i ].y, path->pts[i+1].x, path->pts[i+1].y, path->pts[i+2].x, path->pts[i+2].y, flatness, (path->flags[i-1] & splashPathFirst), (path->flags[i+2] & splashPathLast), !closeSubpaths && (path->flags[i-1] & splashPathFirst) && !(path->flags[i-1] & splashPathClosed), !closeSubpaths && (path->flags[i+2] & splashPathLast) && !(path->flags[i+2] & splashPathClosed)); i += 3; // clockwise circular arc } else if (path->flags[i] & splashPathArcCW) { xc = path->pts[i].x; yc = path->pts[i].y; dx = path->pts[i+1].x - xc; dy = path->pts[i+1].y - yc; r = splashSqrt(dx * dx + dy * dy); if (path->pts[i-1].x < xc && path->pts[i-1].y <= yc) { quad0 = 0; } else if (path->pts[i-1].x >= xc && path->pts[i-1].y < yc) { quad0 = 1; } else if (path->pts[i-1].x > xc && path->pts[i-1].y >= yc) { quad0 = 2; } else { quad0 = 3; } if (path->pts[i+1].x <= xc && path->pts[i+1].y < yc) { quad1 = 0; } else if (path->pts[i+1].x > xc && path->pts[i+1].y <= yc) { quad1 = 1; } else if (path->pts[i+1].x >= xc && path->pts[i+1].y > yc) { quad1 = 2; } else { quad1 = 3; } x0 = path->pts[i-1].x; y0 = path->pts[i-1].y; x1 = y1 = 0; // make gcc happy quad = quad0; while (1) { switch (quad) { case 0: x1 = xc; y1 = yc - r; break; case 1: x1 = xc + r; y1 = yc; break; case 2: x1 = xc; y1 = yc + r; break; case 3: x1 = xc - r; y1 = yc; break; } last = gFalse; if (quad == quad1) { switch (quad) { case 0: case 1: last = path->pts[i+1].x > x0; break; case 2: case 3: last = path->pts[i+1].x < x0; break; } } if (last) { addArc(x0, y0, path->pts[i+1].x, path->pts[i+1].y, xc, yc, r, quad, flatness, quad == quad0 && (path->flags[i-1] & splashPathFirst), (path->flags[i+1] & splashPathLast), quad == quad0 && !closeSubpaths && (path->flags[i-1] & splashPathFirst) && !(path->flags[i-1] & splashPathClosed), !closeSubpaths && (path->flags[i+1] & splashPathLast) && !(path->flags[i+1] & splashPathClosed)); break; } else { addArc(x0, y0, x1, y1, xc, yc, r, quad, flatness, quad == quad0 && (path->flags[i-1] & splashPathFirst), gFalse, quad == quad0 && !closeSubpaths && (path->flags[i-1] & splashPathFirst) && !(path->flags[i-1] & splashPathClosed), gFalse); x0 = x1; y0 = y1; quad = (quad + 1) & 3; } } i += 2; // line segment } else { addSegment(path->pts[i-1].x, path->pts[i-1].y, path->pts[i].x, path->pts[i].y, path->flags[i-1] & splashPathFirst, path->flags[i] & splashPathLast, !closeSubpaths && (path->flags[i-1] & splashPathFirst) && !(path->flags[i-1] & splashPathClosed), !closeSubpaths && (path->flags[i] & splashPathLast) && !(path->flags[i] & splashPathClosed)); ++i; } // close a subpath if (closeSubpaths && (path->flags[i-1] & splashPathLast) && (path->pts[i-1].x != path->pts[curSubpath].x || path->pts[i-1].y != path->pts[curSubpath]. y)) { addSegment(path->pts[i-1].x, path->pts[i-1].y, path->pts[curSubpath].x, path->pts[curSubpath].y, gFalse, gTrue, gFalse, gFalse); } } } } SplashXPath::SplashXPath(SplashXPath *xPath) { length = xPath->length; size = xPath->size; segs = (SplashXPathSeg *)gmalloc(size * sizeof(SplashXPathSeg)); memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg)); } SplashXPath::~SplashXPath() { gfree(segs); } // Add space for more segments void SplashXPath::grow(int nSegs) { if (length + nSegs > size) { if (size == 0) { size = 32; } while (size < length + nSegs) { size *= 2; } segs = (SplashXPathSeg *)grealloc(segs, size * sizeof(SplashXPathSeg)); } } void SplashXPath::addCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1) { SplashCoord cx[maxCurveSplits + 1][3]; SplashCoord cy[maxCurveSplits + 1][3]; int cNext[maxCurveSplits + 1]; SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh; SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh; SplashCoord dx, dy, mx, my, d1, d2, flatness2; int p1, p2, p3; flatness2 = flatness * flatness; // initial segment p1 = 0; p2 = maxCurveSplits; cx[p1][0] = x0; cy[p1][0] = y0; cx[p1][1] = x1; cy[p1][1] = y1; cx[p1][2] = x2; cy[p1][2] = y2; cx[p2][0] = x3; cy[p2][0] = y3; cNext[p1] = p2; while (p1 < maxCurveSplits) { // get the next segment xl0 = cx[p1][0]; yl0 = cy[p1][0]; xx1 = cx[p1][1]; yy1 = cy[p1][1]; xx2 = cx[p1][2]; yy2 = cy[p1][2]; p2 = cNext[p1]; xr3 = cx[p2][0]; yr3 = cy[p2][0]; // compute the distances from the control points to the // midpoint of the straight line (this is a bit of a hack, but // it's much faster than computing the actual distances to the // line) mx = (xl0 + xr3) * 0.5; my = (yl0 + yr3) * 0.5; dx = xx1 - mx; dy = yy1 - my; d1 = dx*dx + dy*dy; dx = xx2 - mx; dy = yy2 - my; d2 = dx*dx + dy*dy; // if the curve is flat enough, or no more subdivisions are // allowed, add the straight line segment if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) { addSegment(xl0, yl0, xr3, yr3, p1 == 0 && first, p2 == maxCurveSplits && last, p1 == 0 && end0, p2 == maxCurveSplits && end1); p1 = p2; // otherwise, subdivide the curve } else { xl1 = (xl0 + xx1) * 0.5; yl1 = (yl0 + yy1) * 0.5; xh = (xx1 + xx2) * 0.5; yh = (yy1 + yy2) * 0.5; xl2 = (xl1 + xh) * 0.5; yl2 = (yl1 + yh) * 0.5; xr2 = (xx2 + xr3) * 0.5; yr2 = (yy2 + yr3) * 0.5; xr1 = (xh + xr2) * 0.5; yr1 = (yh + yr2) * 0.5; xr0 = (xl2 + xr1) * 0.5; yr0 = (yl2 + yr1) * 0.5; // add the new subdivision points p3 = (p1 + p2) / 2; cx[p1][1] = xl1; cy[p1][1] = yl1; cx[p1][2] = xl2; cy[p1][2] = yl2; cNext[p1] = p3; cx[p3][0] = xr0; cy[p3][0] = yr0; cx[p3][1] = xr1; cy[p3][1] = yr1; cx[p3][2] = xr2; cy[p3][2] = yr2; cNext[p3] = p2; } } } void SplashXPath::addArc(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord xc, SplashCoord yc, SplashCoord r, int quad, SplashCoord flatness, GBool first, GBool last, GBool end0, GBool end1) { SplashCoord px[maxCurveSplits + 1]; SplashCoord py[maxCurveSplits + 1]; int pNext[maxCurveSplits + 1]; SplashCoord r2, flatness2; SplashCoord xx0, yy0, xx1, yy1, xm, ym, t, dx, dy; int p1, p2, p3; r2 = r * r; flatness2 = flatness * flatness; // initial segment p1 = 0; p2 = maxCurveSplits; px[p1] = x0; py[p1] = y0; px[p2] = x1; py[p2] = y1; pNext[p1] = p2; while (p1 < maxCurveSplits) { // get the next segment xx0 = px[p1]; yy0 = py[p1]; p2 = pNext[p1]; xx1 = px[p2]; yy1 = py[p2]; // compute the arc midpoint t = (xx0 - xc) * (xx1 - xc) - (yy0 - yc) * (yy1 - yc); xm = splashSqrt(0.5 * (r2 + t)); ym = splashSqrt(0.5 * (r2 - t)); switch (quad) { case 0: xm = xc - xm; ym = yc - ym; break; case 1: xm = xc + xm; ym = yc - ym; break; case 2: xm = xc + xm; ym = yc + ym; break; case 3: xm = xc - xm; ym = yc + ym; break; } // compute distance from midpoint of straight segment to midpoint // of arc dx = 0.5 * (xx0 + xx1) - xm; dy = 0.5 * (yy0 + yy1) - ym; // if the arc is flat enough, or no more subdivisions are allowed, // add the straight line segment if (p2 - p1 == 1 || dx * dx + dy * dy <= flatness2) { addSegment(xx0, yy0, xx1, yy1, p1 == 0 && first, p2 == maxCurveSplits && last, p1 == 0 && end0, p2 == maxCurveSplits && end1); p1 = p2; // otherwise, subdivide the arc } else { p3 = (p1 + p2) / 2; px[p3] = xm; py[p3] = ym; pNext[p1] = p3; pNext[p3] = p2; } } } void SplashXPath::addSegment(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, GBool first, GBool last, GBool end0, GBool end1) { grow(1); segs[length].x0 = x0; segs[length].y0 = y0; segs[length].x1 = x1; segs[length].y1 = y1; segs[length].flags = 0; if (first) { segs[length].flags |= splashXPathFirst; } if (last) { segs[length].flags |= splashXPathLast; } if (end0) { segs[length].flags |= splashXPathEnd0; } if (end1) { segs[length].flags |= splashXPathEnd1; } if (y1 == y0) { segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathHoriz; if (x1 == x0) { segs[length].flags |= splashXPathVert; } } else if (x1 == x0) { segs[length].dxdy = segs[length].dydx = 0; segs[length].flags |= splashXPathVert; } else { segs[length].dxdy = (x1 - x0) / (y1 - y0); segs[length].dydx = 1 / segs[length].dxdy; } if (y0 > y1) { segs[length].flags |= splashXPathFlip; } ++length; } static int cmpXPathSegs(const void *arg0, const void *arg1) { SplashXPathSeg *seg0 = (SplashXPathSeg *)arg0; SplashXPathSeg *seg1 = (SplashXPathSeg *)arg1; SplashCoord x0, y0, x1, y1; if (seg0->flags & splashXPathFlip) { x0 = seg0->x1; y0 = seg0->y1; } else { x0 = seg0->x0; y0 = seg0->y0; } if (seg1->flags & splashXPathFlip) { x1 = seg1->x1; y1 = seg1->y1; } else { x1 = seg1->x0; y1 = seg1->y0; } if (y0 != y1) { return (y0 > y1) ? 1 : -1; } if (x0 != x1) { return (x0 > x1) ? 1 : -1; } return 0; } void SplashXPath::sort() { qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs); }