summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2018-11-07 00:32:29 +0000
committerChris Liddell <chris.liddell@artifex.com>2018-11-07 15:51:32 +0000
commitd658d3103dcf7622d236cb3f9e3d2e1150f0afb2 (patch)
tree0a7b5d26f5664fd7a5eea6422d4a884d058fd5a2
parent0dc7a00acc71ab14d32e8cbcd06e155d5322f38d (diff)
downloadghostpdl-gs925_scanconvfixes.tar.gz
Add nasty workaround for PXL stroking.ghostpdl-9.25_scanconvfixesgs925_scanconvfixes
Filter paths to be filled to remove 1 dimensional sections.
-rw-r--r--base/gxpath.h2
-rw-r--r--base/gxpcopy.c193
-rw-r--r--pcl/pxl/pxpaint.c6
3 files changed, 200 insertions, 1 deletions
diff --git a/base/gxpath.h b/base/gxpath.h
index bf869ec8f..832173df5 100644
--- a/base/gxpath.h
+++ b/base/gxpath.h
@@ -287,6 +287,8 @@ int gx_path_add_dash_expansion(const gx_path * /*old*/, gx_path * /*new*/,
void gx_point_scale_exp2(gs_fixed_point *, int, int),
gx_rect_scale_exp2(gs_fixed_rect *, int, int);
+int gx_path_elide_1d(gx_path *ppath);
+
/* Path enumerator */
/* This interface does not make a copy of the path. */
diff --git a/base/gxpcopy.c b/base/gxpcopy.c
index d72c12b1f..8977a3b61 100644
--- a/base/gxpcopy.c
+++ b/base/gxpcopy.c
@@ -851,3 +851,196 @@ gx_path_merge_contacting_contours(gx_path *ppath)
}
return 0;
}
+
+static int
+is_colinear(gs_fixed_rect *rect, fixed x, fixed y)
+{
+ fixed x0 = rect->p.x;
+ fixed y0 = rect->p.y;
+ fixed x1 = rect->q.x;
+ fixed y1 = rect->q.y;
+
+ if (x0 == x1) {
+ if (y0 == y1) {
+ /* Initial case */
+ /* Still counts as colinear */
+ } else if (x == x0) {
+ /* OK! */
+ } else {
+ return 0; /* Not colinear */
+ }
+ } else if (rect->p.y == rect->q.y) {
+ if (y == rect->p.y) {
+ /* OK */
+ } else {
+ return 0; /* Not colinear */
+ }
+ } else {
+ /* Need to do hairy maths */
+ /* The distance of a point (x,y) from the line passing through
+ * (x0,y0) and (x1,y1) is:
+ * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2)
+ *
+ * We want d <= epsilon to count as colinear.
+ *
+ * d = |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| / SQR((y1-y0)^2 + (x1-x0)^2) <= epsilon
+ *
+ * |(y1-y0)x - (x1-x0)y + x1y0 - y1x0| <= epsilon * SQR((y1-y0)^2 + (x1-x0)^2)
+ *
+ * ((y1-y0)x - (x1-x0)y + x1y0 - y1x0)^2 <= epsilon^2 * ((y1-y0)^2 + (x1-x0)^2)
+ */
+ int64_t ix1 = ((int64_t)x1);
+ int64_t iy1 = ((int64_t)y1);
+ int64_t dx = ix1 - x0;
+ int64_t dy = iy1 - y0;
+ int64_t num = dy*x - dx*y + ix1*y0 - iy1*x0;
+ int64_t den = dx*dx + dy*dy;
+ int epsilon_squared = 2;
+
+ if (num < 0)
+ num = -num;
+ while (num > (1<<30)) {
+ num >>= 2;
+ den >>= 1;
+ if (den == 0)
+ return 0; /* Not colinear */
+ }
+ num *= num;
+ if (num > epsilon_squared * den)
+ return 0;
+ }
+ /* rect is not really a rect. It's just a pair of points. We guarantee that x0 <= x1. */
+ if (x == x0) {
+ if (y < y0)
+ rect->p.y = y;
+ else if (y > y1)
+ rect->q.y = y;
+ } else if (x < x0) {
+ rect->p.x = x;
+ rect->p.y = y;
+ } else {
+ rect->q.x = x;
+ rect->q.y = y;
+ }
+
+ return 1;
+}
+
+static int
+gx_path_copy_eliding_1d(const gx_path *ppath_old, gx_path *ppath)
+{
+ const segment *pseg;
+ /*
+ * Since we're going to be adding to the path, unshare it
+ * before we start.
+ */
+ int code = gx_path_unshare(ppath);
+
+ if (code < 0)
+ return code;
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath_old, "before eliding_1d");
+#endif
+
+ pseg = (const segment *)(ppath_old->first_subpath);
+ while (pseg != NULL) {
+ const segment *look = pseg;
+ gs_fixed_rect rect;
+
+ rect.p.x = rect.q.x = look->pt.x;
+ rect.p.y = rect.q.y = look->pt.y;
+
+ if (look->type != s_start) {
+ dlprintf("Unlikely?");
+ }
+
+ look = look->next;
+ while (look != NULL && look->type != s_start) {
+ if (look->type == s_curve) {
+ const curve_segment *pc = (const curve_segment *)look;
+ if (!is_colinear(&rect, pc->p1.x, pc->p1.y) ||
+ !is_colinear(&rect, pc->p2.x, pc->p2.y) ||
+ !is_colinear(&rect, pc->pt.x, pc->pt.y))
+ goto not_colinear;
+ } else if (!is_colinear(&rect, look->pt.x, look->pt.y)) {
+ goto not_colinear;
+ }
+ look = look->next;
+ }
+ pseg = look;
+ if (0)
+ {
+not_colinear:
+ /* Not colinear. We want to keep this section. */
+ while (look != NULL && look->type != s_start)
+ look = look->next;
+ while (pseg != look && code >= 0) {
+ /* Copy */
+ switch (pseg->type) {
+ case s_start:
+ code = gx_path_add_point(ppath,
+ pseg->pt.x, pseg->pt.y);
+ break;
+ case s_curve:
+ {
+ const curve_segment *pc = (const curve_segment *)pseg;
+
+ code = gx_path_add_curve_notes(ppath,
+ pc->p1.x, pc->p1.y, pc->p2.x, pc->p2.y,
+ pc->pt.x, pc->pt.y, pseg->notes);
+ break;
+ }
+ case s_line:
+ code = gx_path_add_line_notes(ppath,
+ pseg->pt.x, pseg->pt.y, pseg->notes);
+ break;
+ case s_gap:
+ code = gx_path_add_gap_notes(ppath,
+ pseg->pt.x, pseg->pt.y, pseg->notes);
+ break;
+ case s_dash:
+ {
+ const dash_segment *pd = (const dash_segment *)pseg;
+
+ code = gx_path_add_dash_notes(ppath,
+ pd->pt.x, pd->pt.y, pd->tangent.x, pd->tangent.y, pseg->notes);
+ break;
+ }
+ case s_line_close:
+ code = gx_path_close_subpath(ppath);
+ break;
+ default: /* can't happen */
+ code = gs_note_error(gs_error_unregistered);
+ }
+ pseg = pseg->next;
+ }
+ if (code < 0) {
+ gx_path_new(ppath);
+ return code;
+ }
+ }
+ }
+ ppath->bbox_set = false;
+#ifdef DEBUG
+ if (gs_debug_c('P'))
+ gx_dump_path(ppath, "after eliding_1d");
+#endif
+ return 0;
+}
+
+int
+gx_path_elide_1d(gx_path *ppath)
+{
+ int code;
+ gx_path path;
+
+ gx_path_init_local(&path, ppath->memory);
+ code = gx_path_copy_eliding_1d(ppath, &path);
+ if (code < 0)
+ return code;
+ gx_path_assign_free(ppath, &path);
+ gx_path_free(&path, "gx_path_elide_1d");
+
+ return 0;
+}
diff --git a/pcl/pxl/pxpaint.c b/pcl/pxl/pxpaint.c
index 30d92a6b9..dba89d55e 100644
--- a/pcl/pxl/pxpaint.c
+++ b/pcl/pxl/pxpaint.c
@@ -371,10 +371,14 @@ paint_path(px_state_t * pxs)
gx_path_assign_preserve(stroke_path, ppath);
}
+ /* Make a reduced version of the path, and put that back. */
+ code = gx_path_elide_1d(ppath);
+ if (code < 0)
+ return code;
+
/* exit here if no stroke or the fill failed. */
code = (*fill_proc) (pgs);
if (code < 0 || !will_stroke) {
- /* if there is a stroke path free it */
if (stroke_path)
gx_path_free(stroke_path, "paint_path(error_with_fill)");
return code;