Bug 690810 - Fill/Stroke mismatches
Summary: Fill/Stroke mismatches
Status: CONFIRMED
Alias: None
Product: Ghostscript
Classification: Unclassified
Component: Graphics Library (show other bugs)
Version: master
Hardware: PC Windows XP
: P4 normal
Assignee: Robin Watts
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2009-10-08 09:42 UTC by Robin Watts
Modified: 2021-02-14 08:20 UTC (History)
2 users (show)

See Also:
Customer:
Word Size: ---


Attachments
mismatch.ps: Cut down version of tiger.eps that shows the problem (241 bytes, application/postscript)
2009-10-08 09:58 UTC, Robin Watts
Details
mismatch2.ps: Even more cutdown example (172 bytes, application/postscript)
2009-10-08 10:00 UTC, Robin Watts
Details
FillStrokeLine.png: Diagram attempting to explain the mismatch (1.99 KB, image/png)
2009-10-08 10:25 UTC, Robin Watts
Details
FillStrokeLine.png: Diagram attempting to explain the mismatch (8.98 KB, image/png)
2009-10-08 10:26 UTC, Robin Watts
Details
testpatch: patch to test my understanding of the bug is correct. (979 bytes, patch)
2009-10-08 11:20 UTC, Robin Watts
Details | Diff
ThinLines.png (9.08 KB, image/png)
2010-01-13 05:29 UTC, Robin Watts
Details
thinline.ps (5.24 KB, application/postscript)
2010-01-13 06:16 UTC, Robin Watts
Details
thinlineAcrobat.tiff (10.07 KB, image/tiff)
2010-01-13 06:33 UTC, Robin Watts
Details
thinlineJaws.png (5.71 KB, image/png)
2010-01-13 07:57 UTC, Robin Watts
Details
thicklineAcrobat.tiff (302.08 KB, image/tiff)
2010-01-13 09:44 UTC, Robin Watts
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Robin Watts 2009-10-08 09:42:10 UTC
When rendering tiger.eps using the default ghostscript settings (on the 
display device), various stray white pixels can be seen, particularly around 
the whiskers. These correspond to places where a path is first filled in 
white, then stroked in black. Contrary to our expectations the fill pixels 
extend further outwards in some areas than the stroked pixels, hence leaving 
white specs in the final image.
Comment 1 Robin Watts 2009-10-08 09:58:39 UTC
Created attachment 5463 [details]
mismatch.ps: Cut down version of tiger.eps that shows the problem

gswin32c.exe mismatch.ps
Comment 2 Robin Watts 2009-10-08 10:00:26 UTC
Created attachment 5464 [details]
mismatch2.ps: Even more cutdown example

gswin32c.exe mismatch2.ps
Comment 3 Robin Watts 2009-10-08 10:25:00 UTC
Created attachment 5465 [details]
FillStrokeLine.png: Diagram attempting to explain the mismatch

The line to fill/stroke is shown in red.

This is first "filled" by filling the cyan region (in 3 sections as indicated
by the dark blue lines).

Next, this is "stroked" by filling the green region (in 3 sections as indicated
by the dark green lines).

The short vertical end of the "stroke" parallelogram (filled in green) is 1
pixel high (for 'thin' lines, and others that do not use the fill adjust figure
at least).

The endpoints of the "filled" region (cyan) are displaced from the line
endpoint by the given 'fill adjust' figure.

If the fill adjust figure is more than 1/4 of a pixel (for a 45 degree line,
the worst case), then the fill will extend beyond the boundary of the stroke,
possibly resulting in the stray pixels we see.
Comment 4 Robin Watts 2009-10-08 10:26:48 UTC
Created attachment 5466 [details]
FillStrokeLine.png: Diagram attempting to explain the mismatch 

Corrected image, sorry.
Comment 5 Robin Watts 2009-10-08 11:20:38 UTC
Created attachment 5467 [details]
testpatch: patch to test my understanding of the bug is correct.

Simple patch that forces the minimum possible non-zero stroke adjustment to be
used when stroking if the fill adjustment would have been too large.

(Also disables the use of the special thin lines code which doesn't ever use
the stroke adjustment figure).

This has the side effect of slightly thickening the stroked lines (as we'd
expect), but does irradicate all the problems I can see in tiger.eps, except
for a) end points and b) one which is think might due to adjacent lines of a
stroke differing by more than 45 degrees.

I think this supports my analysis, but doesn't really give us a fix.
Comment 6 Masaki Ushizaka 2009-10-09 02:51:04 UTC
First of all, I haven't followed your discussions on IRC enough, so pardon me if I miss some 
perspectives already discussed.

Next, the figure you provided in comment #4 doesn't make sense to me.  PostScript storke and fill do 
not have pens like GDI or QuickDraw.  (But maybe this is because you are trying to explain ghostscript's 
painting mechanism I don't understand.)

And, since the "zero-width" lines are device dependent (PSRM3 7.5.1 & 8 setlinewidth), it supposed to 
be having an difference to other fill/stroke graphics, and their use is not recommended.  "Tiger" is a 
very bad example of using it.  I think following code is more appropriate to demonstrate this problem.

---
%!
.2 setgray clippath fill
false setstrokeadjust
169 100 moveto
100 148 lineto
.001 setlinewidth
gsave 1 setgray fill grestore
gsave 0 setgray stroke grestore
showpage
---

But this code does not show same symptom on Mac OS X + x11.  On Mac OS X, the path needs to be 
this way to be problematic.

---
%!
.2 setgray clippath fill
false setstrokeadjust
100 100 moveto
168 148 lineto
.001 setlinewidth
gsave 1 setgray fill grestore
gsave 0 setgray stroke grestore
showpage
---

Windows' currentmatrix is [1.33333337 0.0 0.0 1.33333337 0.0 0.0], Mac is [0.930200815 0.0 0.0 -
0.930200815 0.0 736.0].  Maybe there's a connection. 



Then, I tried to replace "fill" with "strokepath fill", hoping "strokepath fill" and "stroke" paints exact 
same pixels.

---
%!
.2 setgray clippath fill
false setstrokeadjust
169 100 moveto
100 148 lineto
.001 setlinewidth
gsave 1 setgray strokepath fill grestore
gsave 0 setgray stroke grestore
showpage
---

This resulted a lot more difference than "fill vs stroke" on both Mac and Windows.  I surprised about the 
fact that "strokepath fill" and "stroke" has differences.  This does sound like a breach to the PostScript 
imaging model to me.  Or, this may have connection with this problem.
Comment 7 Robin Watts 2009-10-09 04:07:11 UTC
> Next, the figure you provided in comment #4 doesn't make sense to me.
> PostScript storke and fill do not have pens like GDI or QuickDraw.
> (But maybe this is because you are trying to explain ghostscript's
> painting mechanism I don't understand.)

Exactly. I'm coming to ghostscript anew, with no experience of how the code 
works. Therefore I drew the diagram to help myself understand how ghostscript
actually goes about performing fills/strokes.

As such the figure is designed to represent what ghostscript actually plots, 
not what the postscript imaging model says should be plotted.

Thank you for your other comments/examples. I will look at them later.
Comment 8 Ray Johnston 2009-10-22 10:58:21 UTC
Fixing assingment to Robin
Comment 9 Robin Watts 2010-01-13 05:29:08 UTC
Created attachment 5868 [details]
ThinLines.png

A simple diagram to explain why Ghostscripts thin line handling is wrong.

The top left figure in the diagram shows part of a path describing a shape (the
blue and red line segments), and the pixels that would be filled for this path
using the 'any part of a pixel' rule.

The top right figure in the diagram shows part of another path describing
another shape (again, blue and red line segments), and the pixels that would be
filled for this path using the 'any part of a pixel' rule.

The key observation here is that the line segment shown in red is common
between the two shapes.

Now consider what would happen when we stroke that red line segment with a thin
line. The bottom left figure shows which pixels would need to be filled
according to our 'any part of a pixel rule' (in brown). Observation shows that
these pixels would be sufficient to correctly ensure that no grey pixels were
showing 'outside' the stroke for both of the top 2 shapes.

Ghostscript does NOT fill these pixels. Instead, in thin line mode, it fills
the pixels shown in the bottom right figure using a unit high trapezium. This
is clearly not correct for this shape, as we have "external pixels" from the
fill showing beyond the stroke.

A few moments thought is sufficient to realise that there can be no set of
pixels given by the unit high trapezium method that will correctly fill the
required pixels on both shapes. If we are not to fail in our expectation of
having "no external fill pixels showing" then either we need to use the 'any
part of a pixel' rule pixels, or the set of pixels we choose to plot must
differ between the two shapes. Acrobat chooses the latter approach, as I will
demonstrate with my next example file.
Comment 10 Robin Watts 2010-01-13 06:16:24 UTC
Created attachment 5869 [details]
thinline.ps

thinline.ps is a testfile to demonstrate properties of stroking/filling.

Essentially, it repeatedly renders the same 2 filled shapes many times, with
different stroked paths on top. The shapes in question are essentially those in
the earlier attachment ThinLines.png.

There are 4 sets of 2 lines of 9 columns of shapes. The bottom 2 sets use a
strokewidth of 0.1, the top 2 a stroke width of 0. The bottom of each pair of
sets has strokeadjust disabled, the top has it enabled.

Within each set, the columns are as follows:

Column 1: The shapes, filled in grey.
Column 2: The shapes, filled in grey, then stroked in black.
Column 3: The shapes, filled in grey, with just the shared line segment stroked
in black.
Column 4: The shapes, filled in grey, then with another (identical) path
constructed, and stroked in black.
Column 5: The shapes, filled in grey, then with another (identical, but not
closed) path constructed and stroked in black.
Column 6: The shapes, filled in grey, then with a cutdown (2 segment) version
of the path constructed and stroked in black.
Column 7: The same as Column 6, except that the stroked path is extended by a
segment moving in the opposite direction (actually the segment that would have
been next in the other shapes path).
Column 8: The same as column 3, but with a the line segment preceded by a line
segment moving vertically downwards by 0.1 pixels to join the initial point.
Column 9: The same as column 3, but with a the line segment preceded by a line
segment moving vertically upwards by 0.1 pixels to join the initial point.
Comment 11 Robin Watts 2010-01-13 06:33:48 UTC
Created attachment 5870 [details]
thinlineAcrobat.tiff

This is a rendering of thinline.ps from Acrobat 9.3 Pro as a TIFF at 72dpi.

The first observation is that there is no difference in rendering between
strokeadjust being on or off, and firther, there is no change according to the
strokewidth being 0.1 or 0.

Column 1 shows us the filled shapes, exactly as we'd expect, according to the
"any part of pixel" rule.

Column 2 shows us the stroked shapes; the interesting point here is that pixels
filled for the 'shared linesegment' are different in each case.

Column 3 shows us that stroking just the shared linesegment fills exactly the
"any part of pixel" pixels we'd expect (i.e. the same for both shapes).

Column 4 shows us that Acrobat is not 'remembering' that it has just filled a
path and then stroking it to match (as the path is deliberately reconstructed).
It gives identical results to Column 1.

Column 5 shows us that Acrobat is not choosing to stroke the path differently
purely because it is closed - the results (other than the missing line segment)
are identical to Columns 1 and 3.

Column 6 shows us that just 2 line segments in each path is sufficient to
ensure that the stroked pixels remain different in the 2 caes.

Column 7 shows us that introducing a new line segment changes the rendering for
the existing line segment - i.e. despite having the same path as in Column 6,
but with an extra line segment appended, there is at least one pixel that is
stroked in 6 but not 7.

The best theory I can come up with to explain this is that acrobat adjusts the
pixels painted during stroking a line segment according to the direction that
the preceding/succeeding line segments run in relative to the segment in
question. For instance, if a line segment moves clockwise from the preceding
one (i.e. it 'turns right'), then the pixels plotted will 'tend to the left'
(i.e. towards the outside of what the same path would be filled as).

Column 8 attempts to test this; it shows that a subpixel line segment added
before the start of the shared segment is enough to cause differences along the
length of the rendered segment. An anticlockwise (left) turn causes the line to
tend to the right (downwards).

Column 9 shows the converse - a clockwise (right) turn causes the line to tend
to the left (upwards).

More tests would be required to exactly ascertain exactly what Acrobat is doing
in every condition, but we need to decide if we really want to follow Acrobat
slavishly here.
Comment 12 Robin Watts 2010-01-13 07:57:25 UTC
Created attachment 5871 [details]
thinlineJaws.png

Ken has kindly done me a rendering using jaws. Analysis to follow.
Comment 13 Robin Watts 2010-01-13 09:44:06 UTC
Created attachment 5872 [details]
thicklineAcrobat.tiff

A tiff saved from Acrobat of a variant of the test file scaled up so that the
lines aren't "thin" any more.

Visual inspection shows that the pixels covered by the stroke (in particular
those in column 8/9) are identical, hence as expected it seems that the
wierdness is restricted to thin lines. This makes sense as thin lines are
allowed to be handled device dependently according to the spec.
Comment 14 Robin Watts 2010-01-28 17:23:26 UTC
Reassigning to new email address.
Comment 15 Peter Cherepanov 2021-02-14 08:20:00 UTC
This problem still occurs in the current development version. The number of stray white pixels has increases compared to earlier versions since v. 9.22.

gs -sDEVICE=tiff34nc -r72 -o out.tiff mismatch.ps