[gs-cvs] rev 8790 - trunk/gs/src
leonardo at ghostscript.com
leonardo at ghostscript.com
Mon Jun 9 00:33:59 PDT 2008
Author: leonardo
Date: 2008-06-09 00:33:57 -0700 (Mon, 09 Jun 2008)
New Revision: 8790
Modified:
trunk/gs/src/gdevbit.c
trunk/gs/src/gsimage.c
trunk/gs/src/gspaint.c
trunk/gs/src/gstext.c
trunk/gs/src/gxclip.c
trunk/gs/src/gxdevcli.h
trunk/gs/src/gxdevice.h
trunk/gs/src/gxstroke.c
Log:
Fix (stroking) : Prevent unpainted gaps between neighbour strokes that could appear due to stroke adjustment.
DETAILS :
Bug 687974 "pdf file has banding with ghostscript but not with acrobat".
The test case includes a gradient, which is represented
as a set of paralel strokes. Due to stroke widths equals to fractional pixels,
stroke adjustment shifts some of them so that
an unpainted gap appears between strokes and the gradient looks striped.
Note the test document is created with Quark Express 4.11 .
This patch implements a hewristic recognizer for parallel contacting strokes
and suppresses stroke adjustment for them. See comments in code
for more details.
Note that the recognizer data are associated with a device but they
are not a device property. We store them in a device structure because
we haven't got an associated structure in the graphic state,
and we don't want to introduce one now against extra complexity
with memory management. Also the recognizer code
Appears to be distributed along multiple functions and we would like
to apply a better incapsulation. Nevertheless the encapsulation
would cause CPU time expense for multiple small function calls
unless we create new macros. So commonly we're unhappy of
this code but we don't see a better solution.
Also note that the recognizer data is copied from/to clipper
device when the stroking algorithm installs and deinstalls a clipper.
So the data actually is not a property of a device.
Also they're not a part of graphic state because they must live
across gsave/grestore (the test case requires so).
EXPECTED DIFFERENCES :
Minor raster difference (a 1 pixel shift of some strokes) at 72 dpi :
"Altona-Testsuite_p2_S_x3.pdf"
"Altona_Visual_bb_1v1_x3.pdf"
"Altona_Visual_sb_1v1_x3.pdf"
Modified: trunk/gs/src/gdevbit.c
===================================================================
--- trunk/gs/src/gdevbit.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gdevbit.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -267,6 +267,7 @@
0 ,
0,
0,
+ {false},
{
gx_default_install,
gx_default_begin_page,
Modified: trunk/gs/src/gsimage.c
===================================================================
--- trunk/gs/src/gsimage.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gsimage.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -397,6 +397,7 @@
gs_image_enum_init(gs_image_enum * penum, gx_image_enum_common_t * pie,
const gs_data_image_t * pim, gs_state *pgs)
{
+ pgs->device->sgr.stroke_stored = false;
return gs_image_common_init(penum, pie, pim,
(pgs->in_charpath ? NULL :
gs_currentdevice_inline(pgs)));
Modified: trunk/gs/src/gspaint.c
===================================================================
--- trunk/gs/src/gspaint.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gspaint.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -342,12 +342,14 @@
int
gs_fill(gs_state * pgs)
{
+ pgs->device->sgr.stroke_stored = false;
return fill_with_rule(pgs, gx_rule_winding_number);
}
/* Fill using the even/odd rule */
int
gs_eofill(gs_state * pgs)
{
+ pgs->device->sgr.stroke_stored = false;
return fill_with_rule(pgs, gx_rule_even_odd);
}
@@ -494,6 +496,7 @@
gx_path_free(&spath, "gs_strokepath");
return code;
}
+ pgs->device->sgr.stroke_stored = false;
code = gx_path_assign_free(pgs->path, &spath);
if (code < 0)
return code;
Modified: trunk/gs/src/gstext.c
===================================================================
--- trunk/gs/src/gstext.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gstext.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -263,6 +263,7 @@
code = gs_state_color_load(pgs);
if (code < 0)
return code;
+ pgs->device->sgr.stroke_stored = false;
return gx_device_text_begin(pgs->device, (gs_imager_state *) pgs,
text, pgs->font, pgs->path, pgs->dev_color,
pcpath, mem, ppte);
Modified: trunk/gs/src/gxclip.c
===================================================================
--- trunk/gs/src/gxclip.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gxclip.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -116,6 +116,7 @@
dev->translation.y = 0;
dev->HWResolution[0] = target->HWResolution[0];
dev->HWResolution[1] = target->HWResolution[1];
+ dev->sgr = target->sgr;
dev->target = target;
(*dev_proc(dev, open_device)) ((gx_device *)dev);
}
@@ -129,6 +130,7 @@
dev->translation.y = 0;
dev->HWResolution[0] = target->HWResolution[0];
dev->HWResolution[1] = target->HWResolution[1];
+ dev->sgr = target->sgr;
gx_device_set_target((gx_device_forward *)dev, target);
gx_device_retain((gx_device *)dev, true); /* will free explicitly */
(*dev_proc(dev, open_device)) ((gx_device *)dev);
Modified: trunk/gs/src/gxdevcli.h
===================================================================
--- trunk/gs/src/gxdevcli.h 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gxdevcli.h 2008-06-09 07:33:57 UTC (rev 8790)
@@ -638,6 +638,21 @@
dev_page_proc_begin_page(gx_default_begin_page);
dev_page_proc_end_page(gx_default_end_page);
+/* ----------- A stroked gradient recognizer data ----------*/
+
+/* This structure is associated with a device for
+ internal needs of the graphics library.
+ The main purpose is to suppress stroke adjustment
+ when painting a gradient as a set of parallel strokes.
+ Such gradients still come from some obsolete 3d party software.
+ See bug 687974,
+ */
+
+typedef struct gx_stroked_gradient_recognizer_s {
+ bool stroke_stored;
+ gs_fixed_point orig[4], adjusted[4]; /* from, to, width, vector. */
+} gx_stroked_gradient_recognizer_t;
+
/* ---------------- Device structure ---------------- */
/*
@@ -712,6 +727,7 @@
bool LockSafetyParams; /* If true, prevent unsafe changes */\
long band_offset_x; /* offsets of clist band base to (mem device) buffer */\
long band_offset_y; /* for rendering that is phase sensitive (wtsimdi) */\
+ gx_stroked_gradient_recognizer_t sgr;\
gx_page_device_procs page_procs; /* must be last */\
/* end of std_device_body */\
gx_device_procs procs /* object procedures */
Modified: trunk/gs/src/gxdevice.h
===================================================================
--- trunk/gs/src/gxdevice.h 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gxdevice.h 2008-06-09 07:33:57 UTC (rev 8790)
@@ -102,7 +102,7 @@
#define std_device_part3_()\
0/*PageCount*/, 0/*ShowpageCount*/, 1/*NumCopies*/, 0/*NumCopies_set*/,\
0/*IgnoreNumCopies*/, 0/*UseCIEColor*/, 0/*LockSafetyParams*/,\
- 0/*band_offset_x*/, 0/*band_offset_y*/,\
+ 0/*band_offset_x*/, 0/*band_offset_y*/, {false}/* sgr */,\
{ gx_default_install, gx_default_begin_page, gx_default_end_page }
/*
* We need a number of different variants of the std_device_ macro simply
Modified: trunk/gs/src/gxstroke.c
===================================================================
--- trunk/gs/src/gxstroke.c 2008-06-05 21:52:58 UTC (rev 8789)
+++ trunk/gs/src/gxstroke.c 2008-06-09 07:33:57 UTC (rev 8790)
@@ -209,7 +209,7 @@
/* Other forward declarations */
static bool width_is_thin(pl_ptr);
-static void adjust_stroke(pl_ptr, const gs_imager_state *, bool, bool);
+static void adjust_stroke(gx_device *, pl_ptr, const gs_imager_state *, bool, bool);
static int line_join_points(const gx_line_params * pgs_lp,
pl_ptr plp, pl_ptr nplp,
gs_fixed_point * join_points,
@@ -738,7 +738,9 @@
pl.thin = width_is_thin(&pl);
}
if (!pl.thin) {
- adjust_stroke(&pl, pis, false,
+ if (index)
+ dev->sgr.stroke_stored = false;
+ adjust_stroke(dev, &pl, pis, false,
(pseg->prev == 0 || pseg->prev->type == s_start) &&
(pseg->next == 0 || pseg->next->type == s_start) &&
(zero_length || !is_closed));
@@ -800,6 +802,8 @@
psub = (const subpath *)pseg;
}
exit:
+ if (dev == (gx_device *)&cdev)
+ cdev.target->sgr = cdev.sgr;
if (to_path == &stroke_path_body)
gx_path_free(&stroke_path_body, "gx_stroke_path_only error"); /* (only needed if error) */
if (dash_count)
@@ -826,6 +830,8 @@
vd_erase(RGB(192, 192, 192));
}
}
+ if (vd_enabled)
+ vd_setcolor(pdevc->colors.pure);
code = gx_stroke_path_only_aux(ppath, to_path, pdev, pis, params, pdevc, pcpath);
if (vd_allowed('S'))
vd_release_dc;
@@ -943,6 +949,12 @@
fixed adj2 = (horiz ? STROKE_ADJUSTMENT(thin, pis, x)
: STROKE_ADJUSTMENT(thin, pis, y)) << 1;
+ /* fixme :
+ The best value for adjust_longitude is whether
+ the dash is isolated and doesn't cover entire segment.
+ The current data structure can't pass this info.
+ Therefore we restrict adjust_stroke_longitude with 1 pixel length.
+ */
if (length > fixed_1) /* comparefiles/file2.pdf */
return;
if (pis->line_params.cap == gs_cap_butt) {
@@ -975,22 +987,102 @@
/* to achieve more uniform rendering. */
/* Only o.p, e.p, e.cdelta, and width have been set. */
static void
-adjust_stroke(pl_ptr plp, const gs_imager_state * pis, bool thin, bool adjust_longitude)
+adjust_stroke(gx_device *dev, pl_ptr plp, const gs_imager_state * pis, bool thin, bool adjust_longitude)
{
- bool horiz;
+ bool horiz, adjust = true;
- if (!pis->stroke_adjust && plp->width.x != 0 && plp->width.y != 0)
+ if (!pis->stroke_adjust && (plp->width.x != 0 && plp->width.y != 0)) {
+ dev->sgr.stroke_stored = false;
return; /* don't adjust */
- horiz = (any_abs(plp->width.x) <= any_abs(plp->width.y));
- adjust_stroke_transversal(plp, pis, thin, horiz);
- if (adjust_longitude)
- adjust_stroke_longitude(plp, pis, thin, horiz);
- /* fixme :
- The best value for adjust_longitude is whether
- the dash is isolated and doesn't cover entire segment.
- The current data structure can't pass this info.
- Therefore we restrict adjust_stroke_longitude with 1 pixel length.
- */
+ }
+ /* Recognizing gradients, which some obsolete software
+ represent as a set of parallel strokes.
+ Such strokes must not be adjusted - bug 687974. */
+ if (dev->sgr.stroke_stored && pis->line_params.cap == gs_cap_butt &&
+ dev->sgr.orig[3].x == plp->vector.x && dev->sgr.orig[3].y == plp->vector.y) {
+ /* Parallel. */
+ if ((int64_t)(plp->o.p.x - dev->sgr.orig[0].x) * plp->vector.x ==
+ (int64_t)(plp->o.p.y - dev->sgr.orig[0].y) * plp->vector.y &&
+ (int64_t)(plp->e.p.x - dev->sgr.orig[1].x) * plp->vector.x ==
+ (int64_t)(plp->e.p.y - dev->sgr.orig[1].y) * plp->vector.y) {
+ /* Transversal shift. */
+ if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) &&
+ any_abs(plp->o.p.y - dev->sgr.orig[0].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y) &&
+ any_abs(plp->e.p.x - dev->sgr.orig[1].x) <= any_abs(plp->width.x + dev->sgr.orig[2].x) &&
+ any_abs(plp->e.p.y - dev->sgr.orig[1].y) <= any_abs(plp->width.y + dev->sgr.orig[2].y)) {
+ /* The strokes were contacting or overlapping. */
+ if (any_abs(plp->o.p.x - dev->sgr.orig[0].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 &&
+ any_abs(plp->o.p.y - dev->sgr.orig[0].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2 &&
+ any_abs(plp->e.p.x - dev->sgr.orig[1].x) >= any_abs(plp->width.x + dev->sgr.orig[2].x) / 2 &&
+ any_abs(plp->e.p.y - dev->sgr.orig[1].y) >= any_abs(plp->width.y + dev->sgr.orig[2].y) / 2) {
+ /* The strokes were not much overlapping. */
+ if (!(any_abs(plp->o.p.x - dev->sgr.adjusted[0].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) &&
+ any_abs(plp->o.p.y - dev->sgr.adjusted[0].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y) &&
+ any_abs(plp->e.p.x - dev->sgr.adjusted[1].x) <= any_abs(plp->width.x + dev->sgr.adjusted[2].x) &&
+ any_abs(plp->e.p.y - dev->sgr.adjusted[1].y) <= any_abs(plp->width.y + dev->sgr.adjusted[2].y))) {
+ /* they became not contacting.
+ We should not have adjusted the last stroke. Since if we did,
+ lets change the current one to restore the contact,
+ so that we don't leave gaps when rasterising. See bug 687974.
+ */
+ fixed delta_w_x = (dev->sgr.adjusted[2].x - dev->sgr.orig[2].x);
+ fixed delta_w_y = (dev->sgr.adjusted[2].y - dev->sgr.orig[2].y);
+ fixed shift_o_x = (dev->sgr.adjusted[0].x - dev->sgr.orig[0].x);
+ fixed shift_o_y = (dev->sgr.adjusted[0].y - dev->sgr.orig[0].y);
+ fixed shift_e_x = (dev->sgr.adjusted[1].x - dev->sgr.orig[1].x); /* Must be same, but we prefer clarity. */
+ fixed shift_e_y = (dev->sgr.adjusted[1].y - dev->sgr.orig[1].y);
+
+ if (plp->o.p.x < dev->sgr.orig[0].x ||
+ (plp->o.p.x == dev->sgr.orig[0].x && plp->o.p.y < dev->sgr.orig[0].y)) {
+ /* Left contact, adjust to keep the contact. */
+ if_debug4('O', "[O]don't adjust {{%f,%f},{%f,%f}}\n",
+ fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
+ fixed2float(plp->e.p.x), fixed2float(plp->e.p.y));
+ plp->width.x += (shift_o_x - delta_w_x) / 2;
+ plp->width.y += (shift_o_y - delta_w_y) / 2;
+ plp->o.p.x += (shift_o_x - delta_w_x) / 2;
+ plp->o.p.y += (shift_o_y - delta_w_y) / 2;
+ plp->e.p.x += (shift_e_x - delta_w_x) / 2;
+ plp->e.p.y += (shift_e_y - delta_w_y) / 2;
+ adjust = false;
+ } else {
+ /* Right contact, adjust to keep the contact. */
+ if_debug4('O', "[O]don't adjust {{%f,%f},{%f,%f}}\n",
+ fixed2float(plp->o.p.x), fixed2float(plp->o.p.y),
+ fixed2float(plp->e.p.x), fixed2float(plp->e.p.y));
+ plp->width.x -= (shift_o_x + delta_w_x) / 2;
+ plp->width.y -= (shift_o_y + delta_w_y) / 2;
+ plp->o.p.x += (shift_o_x + delta_w_x) / 2;
+ plp->o.p.y += (shift_o_y + delta_w_y) / 2;
+ plp->e.p.x += (shift_e_x + delta_w_x) / 2;
+ plp->e.p.y += (shift_e_y + delta_w_y) / 2;
+ adjust = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pis->line_params.cap == gs_cap_butt) {
+ dev->sgr.stroke_stored = true;
+ dev->sgr.orig[0] = plp->o.p;
+ dev->sgr.orig[1] = plp->e.p;
+ dev->sgr.orig[2] = plp->width;
+ dev->sgr.orig[3] = plp->vector;
+ } else
+ dev->sgr.stroke_stored = false;
+ if (adjust) {
+ horiz = (any_abs(plp->width.x) <= any_abs(plp->width.y));
+ adjust_stroke_transversal(plp, pis, thin, horiz);
+ if (adjust_longitude)
+ adjust_stroke_longitude(plp, pis, thin, horiz);
+ }
+ if (pis->line_params.cap == gs_cap_butt) {
+ dev->sgr.adjusted[0] = plp->o.p;
+ dev->sgr.adjusted[1] = plp->e.p;
+ dev->sgr.adjusted[2] = plp->width;
+ dev->sgr.adjusted[3] = plp->vector;
+ }
}
/* Compute the intersection of two lines. This is a messy algorithm */
@@ -1179,7 +1271,7 @@
/* We didn't set up the endpoint parameters before, */
/* because the line was thin. Do it now. */
set_thin_widths(plp);
- adjust_stroke(plp, pis, true, first == 0 && nplp == 0);
+ adjust_stroke(dev, plp, pis, true, first == 0 && nplp == 0);
compute_caps(plp);
}
/* Create an initial cap if desired. */
@@ -1251,7 +1343,7 @@
/* We didn't set up the endpoint parameters before, */
/* because the line was thin. Do it now. */
set_thin_widths(plp);
- adjust_stroke(plp, pis, true, first == 0 && nplp == 0);
+ adjust_stroke(dev, plp, pis, true, first == 0 && nplp == 0);
compute_caps(plp);
}
/* The segment itself : */
@@ -1347,7 +1439,7 @@
/* We didn't set up the endpoint parameters before, */
/* because the line was thin. Do it now. */
set_thin_widths(plp);
- adjust_stroke(plp, pis, true, adlust_longitude);
+ adjust_stroke(dev, plp, pis, true, adlust_longitude);
compute_caps(plp);
}
/* Create an initial cap if desired. */
@@ -1380,7 +1472,7 @@
{
int code;
- vd_setcolor(0);
+ /* vd_setcolor(0); */
vd_setlinewidth(0);
if (moveto_first) {
code = gx_path_add_point(ppath, points[0].x, points[0].y);
@@ -1466,7 +1558,7 @@
ASSIGN_POINT(&np2, nplp->o.p);
np = &np1;
}
- if_debug1('O', "[o]use %s\n", (ccw ? "co (ccw)" : "ce (cw)"));
+ if_debug1('O', "[O]use %s\n", (ccw ? "co (ccw)" : "ce (cw)"));
/* Handle triangular joins now. */
if (join == gs_join_triangle) {
More information about the gs-cvs
mailing list