Bug 687721 - stroking: Dropouts in 'stroke'
Summary: stroking: Dropouts in 'stroke'
Status: RESOLVED FIXED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: Graphics Library (show other bugs)
Version: master
Hardware: PC Windows XP
: P3 normal
Assignee: Robin Watts
URL:
Keywords: bountiable
Depends on:
Blocks:
 
Reported: 2004-09-30 13:39 UTC by Igor Melichev
Modified: 2009-12-01 11:40 UTC (History)
0 users

See Also:
Customer:
Word Size: ---


Attachments
GPRS test (66.30 KB, text/plain)
2006-10-09 03:13 UTC, leonardo
Details
circle_ar7_zoom_3x.png (30.71 KB, image/png)
2007-06-05 07:13 UTC, Ray Johnston
Details
circle_gs_zoom_3x.png (30.84 KB, image/png)
2007-06-05 07:14 UTC, Ray Johnston
Details
Figures to help explain the problem. (50.20 KB, image/png)
2008-06-27 09:04 UTC, Glen Nakamura
Details
Proposed fix. (5.72 KB, patch)
2008-06-27 09:12 UTC, Glen Nakamura
Details | Diff
Diagram to try to explain problem/proposed solution (9.99 KB, image/png)
2009-10-05 09:06 UTC, Robin Watts
Details
Proposed patch (3.53 KB, patch)
2009-10-05 09:12 UTC, Robin Watts
Details | Diff
Various figures illustrating the problem. (11.32 KB, image/png)
2009-10-06 05:30 UTC, Robin Watts
Details
File showing problem (114 bytes, application/postscript)
2009-10-06 11:24 UTC, Robin Watts
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Igor Melichev 2004-09-30 13:39:31 UTC
A circle or curve isn't contiguous :
ppmraw -r300 012-09.ps
ppmraw -r300 013-09.ps
ppmraw -r300 083-09.ps
ppmraw -r300 093-01.ps
ppmraw -r300 123-09.ps

Reported by Julia as a regression from June 09, 2004.
Reproduced by Igor with today's HEAD
Comment 1 Alex Cherepanov 2004-10-01 14:09:25 UTC
Reproduced on the current CVS build:
gswin32c -r300 -c 0 setlinewidth 300 400 28 0 360 arc showpage

Comment 2 Alex Cherepanov 2004-10-01 14:12:16 UTC
Sorry, the correct command line is:
gswin32c -r300 -c 0 setlinewidth 300 400 28 0 360 arc stroke showpage
Comment 3 Igor Melichev 2005-01-26 05:50:44 UTC
Need to re-test after the patch
http://ghostscript.com/pipermail/gs-cvs/2005-January/005180.html
Some dropouts could dissappear with it.
Comment 4 Igor Melichev 2005-01-26 05:54:40 UTC
Still have dropouts in 012-09.ps, the box 012-1, 2nd row, at the "1.5 hours" 
direction.
Comment 5 leonardo 2006-10-09 03:13:08 UTC
Created attachment 2526 [details]
GPRS test

This attachment ia absolutely unrelated to the subject. I'm testing the http
file uploading via GPRS.
Comment 6 Ray Johnston 2007-06-05 06:51:08 UTC
Comment on attachment 2526 [details]
GPRS test

When I get a chance, I'll remove this incorrect upload from the database.
Comment 7 Ray Johnston 2007-06-05 07:12:59 UTC
The correct method to demonstrate the problem is given in Comment #2

I rendered this to TIFF at 300 dpi using Adobe Acrobat Professional 7 and
the comparison files are attached. While there are some minor shape variations,
the issue is the dropout at 45 degrees down from vertical on either side.

The attached screen snapshots are 3:1 zoom views of the data.
Comment 8 Ray Johnston 2007-06-05 07:13:52 UTC
Created attachment 2999 [details]
circle_ar7_zoom_3x.png

Output from AR7 (3:1 zoom)
Comment 9 Ray Johnston 2007-06-05 07:14:59 UTC
Created attachment 3000 [details]
circle_gs_zoom_3x.png

Output from Ghostscript 8.57 (and HEAD as of rev 8014) showing problem
Comment 10 Glen Nakamura 2008-06-27 09:04:31 UTC
Created attachment 4162 [details]
Figures to help explain the problem.

This image illustrates a problem with the gx_default_draw_thin_line function. 
The current implementation uses a pixel center fill rule and a 1-pixel wide
parallelogram to draw the line along its major axis.  Figures A-1 and A-2 show
examples of when the pixel containing the end point of the line is not filled
resulting in the dropouts which can be seen in the testcase.  One solution is
to use a crosshair brush to draw the line, which would add a second
parallelogram to the area of the line.	Figures B-1 and B-2 show the additional
parallelogram in green along with the now filled end points.
Comment 11 Glen Nakamura 2008-06-27 09:12:20 UTC
Created attachment 4163 [details]
Proposed fix.

I am interested in the bounty for this bug.
Attached is a patch with fixes the bug by filling pixels whose centers fall in
the green triangles outside of the blue parallelograms as illustrated in
Figures B-1 and B-2 in the previous attachment.
Comment 12 Ray Johnston 2008-06-27 10:29:02 UTC
If the patch is accepted (by Igor), possibly with modifications he may want,
we will gladly pay the bounty. Once the patch is committed (Igor will post
that fact here), please email Miles Jones (miles.jones@artifex.com) and let
him know the bug number. He'll get the information from you about your
preferred method to receive payment.

Thanks for looking into this.
Comment 13 leonardo 2008-06-29 04:44:27 UTC
Before verifying the patch 4163, I'd like to know whether it can paint a pixel 
several times. 

Another possible problem here is a non-uniform visible line width. We need a 
solution, which doesn't paint extra pixels like this :

xxx
  xx
    x
    x

(use a monospaced font to view this diagram properly). The 1st x in the 2nd 
row is extra : it may be removed with no dropout.

Generally I'm pretty sceptical about creating a simple decomposition rule 
based on considering individual linear segments without analyzing angles 
between neighbour segments. While it is more or less easy for circles, with 
random beziers it is much more complex. Due to that IMO the problem to be 
addressed to another level of decomposition - to gxstroke.c .

My old idea is to apply the general dropout prevention code to narrow curves 
(gxfdrop.c). "Narrow" means here <= 1.5 pixels wide or so. I want to apply it 
together with a "static" adjustment of the width and dropping 
the .setfilladjust rudiments. But this is another story, which would need to 
restart the patch development for this bug. 

IMO the development should go in another direction. Nevertheless if someone 
proves that the constraints are satisfied, I can change my opinion.
Comment 14 Robin Watts 2009-10-05 09:06:04 UTC
Created attachment 5446 [details]
Diagram to try to explain problem/proposed solution

When ghostscript renders a 'thin' line, it actually renders a trapezoid
shape, best illustrated by looking at the attachment in comment #10.

In the case of 'mostly vertical' lines, 2 sides of the trapezoid are
horizontally aligned. In the case of 'mostly horizontal' lines, 2 sides of
the trapezoid are vertically aligned. (These 2 cases can be seen in figure
A2).

A 1 pixel wide line conceptually looks like the red area in Figure 1.
If we chose to fill according to pixel centres being covered, we'd end up
with the given shaded squares being filled.

Ghostscript renders a slightly different thing; rather than rendering a
rotated rectangle for each path, it instead renders a trapezoid of width 1,
with (in the "near vertical" case) horizontal edges. (See figure 2).

As Figure A2 in the attachment in comment #10 shows, pixels can be completely
omitted when lines are rendered in this way.

I propose to add a 'fudge' to ghostscripts 'thin line' rendering output,
whereby we extend each trapezoid slightly by adding a triangular "extension
region" to each of these trapezoids, equivalent to 1/4 of a pixel. If this
region happens to encompass the pixel centre, we additionally shade that
pixel. (See figure 3).

The net effect of this change is that 1/4 of the time, we will extend the
end of a thin line by 1 pixel. It captures both the cases A1 and A2 shown as
being problem.

This is not an exact fix (and cannot be, I believe, as thin lines are
inherently a fudge), but is relatively inexpensive.

Objections raised to the original patch were that 1) pixels might be painted
multiple times, and 2) it might cause non uniform visible line widths and
that.

W.r.t 1) I believe the solution here is no worse that the existing code
(which can, I think, cause pixels to be repainted in the end pixels of
adjacent line segments). Certainly it never causes pixels within a given
line segment to be painted more than once.

I do not believe 2) is an issue either, as lines are only ever extended.

I do not currently have access to the test files to show this bug, but back
to back renderings of tiger.ps show improved renderings with the patch in.
Comment 15 Robin Watts 2009-10-05 09:12:13 UTC
Created attachment 5447 [details]
Proposed patch

Simple patch to add triangular 'extension regions' onto the end of thin lines
to cure dropouts.
Comment 16 Henry Stiles 2009-10-05 09:50:49 UTC
What does adobe acrobat 8.0 and 9.0 do?  We know the 7.0 rendering is broken and
we are no longer trying to emulate it.  The original problem should be rendered
in Acrobat (8.0 or higher) to a tiff format without antialiasing at different
resolutions to establish exactly which pixels are being painted.  I am skeptical
of adding hacks and workarounds to the path code since the underlying
adjustments (690620) are incorrect, that should be addressed first, but it does
seem you have dug into the code adequately to receive the bounty.  
Comment 17 Robin Watts 2009-10-06 05:30:04 UTC
Created attachment 5453 [details]
Various figures illustrating the problem.

> What does adobe acrobat 8.0 and 9.0 do?  We know the 7.0 rendering is
> broken and we are no longer trying to emulate it.  The original problem
> should be rendered in Acrobat (8.0 or higher) to a tiff format without
> antialiasing at different resolutions to establish exactly which pixels
> are being painted.

I do not have access to the original test files for this, but I have done
some quick experiments with a simple pdf file generated from the following
postscript (which shows up problems in bug 687721):

0 setlinewidth 288.2 288.3 10 0 360 arc stroke showpage

The top 2 bitmaps in the image show ghostscripts output at 72dpi, with/without
the bugfixes respectively. The bugfix for 687666 results in the 'extra' pixel
in the top left being removed, and the bugfix for 687721 results in the 'extra'
pixel in the top right being added.

I've drawn a scale diagram on paper with the exact coordinates used the for the
path in ghostscript to convince myself that the new pixel in the top right is
in fact correct :)

After fiddling Acrobats options to avoid thin lines being smoothed, and setting
it's resolution to 72dpi. I took various snapshots. The 100% rendering actually
results in a shape that's a pixel wider/higher than ghostscripts rendering.
Knocking the magnification down to 99%, I get a shape that is the same size,
and knocking it down to 98%, I get a shape that exhibits the same 'extra'
pixel, as we get, but in the bottom left "corner".

I think it's clear that we don't exactly match Acrobats rendering, but we are
unquestionably in the same spirit.

> I am skeptical of adding hacks and workarounds to the path code since the
> underlying adjustments (690620) are incorrect, that should be addressed
> first, but it does seem you have dug into the code adequately to receive
> the bounty.

I believe 690620 talks about fill adjustment, which (if I understand correctly)
does not apply to ghostscripts handling of thin lines. GS does not exactly
match acrobats output pixel for pixel, but I suspect it is unlikely
ever to achieve that.

One alternative approach might be to follow the model that acrobat uses with
its default options, and to allow 'thin' lines to be antialiased too. I believe
we'd simply assign a 1 pixel equivalent stroke widths to zero widthed lines and
render them in the same way as non thin lines. This is the approach Picsel
take.

Also, by default, antialiased lines are done with hardware assistance (on my
windows installation of Acrobat at least). As such I suspect this may mean that
the exact output will vary between graphics cards, making it impossible for us
to exactly match the output.

Finally, on the attached image, I include fragments of tiger.ps rendered
through ghostscript with/without the bugfixes, showing 2 dropouts that get
fixed. Regardless of how precisely our output matches acrobat, I think that
this is a definite improvement in rendering.
Comment 18 Henry Stiles 2009-10-06 08:57:19 UTC
As I said you will need to render the pdf or ps file to a tiff, you need Acrobat
8.0 or 9.0, we are aware you cannot emulate the display output.  My guess would
be that stroking is going to be similar to filling.  I really don't want to do
anything until we understand what adobe does (as done in 690620).  

I am also concerned an intermediate step was left out of this analysis.  The
curve was flattened to individual line segments before rendering.  Those line
segments can be used to create a new postscript file.  A breakpoint on
gxstroke.c:376 and

(gdb) print gx_path_print(&fpath)
   state_flags=7 subpaths=1, curves=0, point=(338.398438,404.316406)
   box=(93289.007812,-4194326.468750),(0.000000,0.000000) last=0x0
   segments=0xbfffe33c (refct=1, first=0x14db554, current=0x14db554)
   0x14db554<0x0,0x14db598>:0: 338.3984 404.3164 moveto	% #curves=0 last=0x14db978
   0x14db598<0x14db554,0x14db5b8>:2: 337.8086 398.4922 lineto
   0x14db5b8<0x14db598,0x14db5d8>:3: 336.1250 393.0703 lineto
   0x14db5d8<0x14db5b8,0x14db5f8>:3: 333.4648 388.1641 lineto
   0x14db5f8<0x14db5d8,0x14db618>:3: 329.9375 383.8906 lineto
   0x14db618<0x14db5f8,0x14db638>:3: 325.6602 380.3594 lineto
   0x14db638<0x14db618,0x14db658>:3: 320.7539 377.6992 lineto
   0x14db658<0x14db638,0x14db678>:3: 315.3320 376.0156 lineto
   0x14db678<0x14db658,0x14db698>:3: 309.5117 375.4297 lineto
   0x14db698<0x14db678,0x14db6b8>:3: 303.6875 376.0156 lineto
   0x14db6b8<0x14db698,0x14db6d8>:3: 298.2656 377.6992 lineto
   0x14db6d8<0x14db6b8,0x14db6f8>:3: 293.3594 380.3594 lineto
   0x14db6f8<0x14db6d8,0x14db718>:3: 289.0859 383.8906 lineto
   0x14db718<0x14db6f8,0x14db738>:3: 285.5547 388.1641 lineto
   0x14db738<0x14db718,0x14db758>:3: 282.8945 393.0703 lineto
   0x14db758<0x14db738,0x14db778>:3: 281.2109 398.4922 lineto
   0x14db778<0x14db758,0x14db798>:3: 280.6250 404.3164 lineto
   0x14db798<0x14db778,0x14db7b8>:3: 281.2109 410.1367 lineto
   0x14db7b8<0x14db798,0x14db7d8>:3: 282.8945 415.5586 lineto
   0x14db7d8<0x14db7b8,0x14db7f8>:3: 285.5547 420.4648 lineto
   0x14db7f8<0x14db7d8,0x14db818>:3: 289.0859 424.7422 lineto
   0x14db818<0x14db7f8,0x14db838>:3: 293.3594 428.2695 lineto
   0x14db838<0x14db818,0x14db858>:3: 298.2656 430.9297 lineto
   0x14db858<0x14db838,0x14db878>:3: 303.6875 432.6133 lineto
   0x14db878<0x14db858,0x14db898>:3: 309.5117 433.2031 lineto
   0x14db898<0x14db878,0x14db8b8>:3: 315.3320 432.6133 lineto
   0x14db8b8<0x14db898,0x14db8d8>:3: 320.7539 430.9297 lineto
   0x14db8d8<0x14db8b8,0x14db8f8>:3: 325.6602 428.2695 lineto
   0x14db8f8<0x14db8d8,0x14db918>:3: 329.9375 424.7422 lineto
   0x14db918<0x14db8f8,0x14db938>:3: 333.4648 420.4648 lineto
   0x14db938<0x14db918,0x14db958>:3: 336.1250 415.5586 lineto
   0x14db958<0x14db938,0x14db978>:3: 337.8086 410.1367 lineto
   0x14db978<0x14db958,0x0>:3: 338.3984 404.3164 lineto


reveals the flattened path, now you can construct a postscript file from this
data and I assume lines will be missing.  Exactly which ones?  Wouldn't it make
more sense to recast this problem to a single line segment missing?

Another issue is the scanline algorithm (gxfill.c), does that work properly?  
Various conditions will cause it to be used but it is straightforward to force
its use in the debugger.

It is much easier to quickly make postscript files and feed those to Acrobat
8.0.  It will distill them automatically.

Sorry to be a nit about these changes but my plan was to step back from the mess
that we have and try to start fresh, right now we have a collection of
"rendering improvements" which has obscured the original intent of the design
and lost track of the original goal: emulate Adobe.  I started this with 690620
and got sidetracked with other issues.
Comment 19 Robin Watts 2009-10-06 10:06:17 UTC
> I am also concerned an intermediate step was left out of this analysis.
> The curve was flattened to individual line segments before rendering.

I have debug in my local copy of ghostscript here that prints out the flattened
line segments, yes.

> Those line segments can be used to create a new postscript file.

I have not yet tried that, but I will do.

> now you can construct a postscript file from this data and I assume lines
> will be missing.

No. The problem is not that complete line segments are missing, just that when 
gs renders these 'thin lines', it misses out end pixels in some cases.

Most of the time this does not matter as, in general, if an end pixel is 
omitted from the end of one line segment, it will be rendered by the next 
segment anyway. In some cases though (as demonstrated by figures A1 and A2 in 
the attachment in comment #10) neither line segment will cause the pixel to be 
written, causing dropouts.

This can happen either where one line segment 'reverses direction' (figure A1) 
or where we move between one line segment being 'mostly vertical' and the next 
one being 'mostly horizontal' (figure A2).

> Exactly which ones?  Wouldn't it make more sense to recast this problem to a
> single line segment missing?

I will attempt to construct an example file with just 2 line segments in it, 
showing a dropout at the join.

> Another issue is the scanline algorithm (gxfill.c), does that work
> properly?

Clearly, there is a mismatch between the (somewhat wierd and wacky) algorithm 
used by ghostscripts 'thin line' stroking, and that of its shape filling 
algorithm.

If you look at tiger.eps rendered in ghostscript, you can clearly see the 
problem with the whiskers. These are constructed by filling a path in white, 
and then stroking around the edge 'thinly' in black. It is a reasonable 
expectation that no white sections should protrude outside the black stroked 
area, but the outermost 'jaggies' of the white fill do.

This mismatch may be best avoided by simply ditching ghostscripts use of thin 
line plotting, and instead rendering thin lines as conventional antialiased 
lines with a 1 pixel wide strokewidth. (This sounds simple, and it is what 
I've seen done elsewhere, but I don't know how hard it is to do in the gs 
codebase).

If you convert tiger.eps to tiger.pdf and render it in Adobe Reader, 2 things 
become apparent. Firstly, thin lines in Adobe Reader are antialiased by 
default (so we'd be "emulating adobe" better by switching away from thin 
lines). Secondly, if you disable hardware acceleration/line smoothing etc, so 
that it does 'pixel' thin lines, then their 'thin lines' appear somewhat 
heavier than ghostscripts, and they do not suffer from the fill extending 
beyond the stroke.

> Sorry to be a nit about these changes but my plan was to step back from the
> mess that we have and try to start fresh, right now we have a collection of
> "rendering improvements" which has obscured the original intent of the design
> and lost track of the original goal: emulate Adobe.  I started this with
> 690620 and got sidetracked with other issues.

To successfully emulate Adobe, we either need to 1) abandon gs's current thin 
line code and switch to feeding thin lines through the normal stroking code, 
or 2) figure out why gs's thin line code differs from Adobe when Adobe 
is 'nobbled' back into that way of working.

1) would be my personal preference; the rendering output looks *much* prettier 
like this, and it should hopefully eliminate the problem of stroke/fill 
mismatches.

2) requires more than the simple band aid I offer here - I suspect the 
whole "thin line stroking using trapezoids" approach needs to be rethought.
Comment 20 Robin Watts 2009-10-06 11:24:01 UTC
Created attachment 5457 [details]
File showing problem

Simple file containing 2 lines that shows the problem.

gswin32c.exe -r72 dropout.ps
Comment 21 Henry Stiles 2009-11-19 07:50:35 UTC
Reassigning path and fill problems to Robin Watts.
Comment 22 Robin Watts 2009-12-01 11:40:32 UTC
Fixed in revision 10429 using the method described in comment #14. This 
resolves the dropout issues in tiger.eps and the other files mentioned here.