Avoid arithmetic overflow in area calculation

This commit is contained in:
Eric Fischer
2015-12-09 15:02:59 -08:00
parent 4bde17f8ff
commit f04c5e153a
+17 -5
View File
@@ -357,16 +357,27 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
double area = 0; double area = 0;
for (unsigned k = i; k < j; k++) { for (unsigned k = i; k < j; k++) {
area += geom[k].x * geom[i + ((k - i + 1) % (j - i))].y; area += (long double) geom[k].x * (long double) geom[i + ((k - i + 1) % (j - i))].y;
area -= geom[k].y * geom[i + ((k - i + 1) % (j - i))].x; area -= (long double) geom[k].y * (long double) geom[i + ((k - i + 1) % (j - i))].x;
} }
area = area / 2; area = area / 2;
// XXX There is an ambiguity here: If the area of a ring is 0 and it is followed by holes,
// we don't know whether the area-0 ring was a hole too or whether it was the outer ring
// that these subsequent holes are somehow being subtracted from. I hope that if a polygon
// was simplified down to nothing, its holes also became nothing.
if (area != 0) {
// These are pixel coordinates, so area > 0 for the outer ring.
// If the outer ring of a polygon was reduced to a pixel, its
// inner rings must just have their area de-accumulated rather
// than being drawn since we don't really know where they are.
if (fabs(area) <= pixel * pixel || (area < 0 && !included_last_outer)) { if (fabs(area) <= pixel * pixel || (area < 0 && !included_last_outer)) {
// printf("area is only %f vs %lld so using square\n", area, pixel * pixel); // printf("area is only %f vs %lld so using square\n", area, pixel * pixel);
*accum_area += area; *accum_area += area;
if (*accum_area > pixel * pixel) { if (area > 0 && *accum_area > pixel * pixel) {
// XXX use centroid; // XXX use centroid;
out.push_back(draw(VT_MOVETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2)); out.push_back(draw(VT_MOVETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2));
@@ -378,7 +389,7 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
*accum_area -= pixel * pixel; *accum_area -= pixel * pixel;
} }
if (area >= 0) { if (area > 0) {
included_last_outer = false; included_last_outer = false;
} }
} else { } else {
@@ -390,10 +401,11 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
*reduced = false; *reduced = false;
if (area >= 0) { if (area > 0) {
included_last_outer = true; included_last_outer = true;
} }
} }
}
i = j - 1; i = j - 1;
} else { } else {