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.
Created attachment 5463 [details] mismatch.ps: Cut down version of tiger.eps that shows the problem gswin32c.exe mismatch.ps
Created attachment 5464 [details] mismatch2.ps: Even more cutdown example gswin32c.exe mismatch2.ps
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.
Created attachment 5466 [details] FillStrokeLine.png: Diagram attempting to explain the mismatch Corrected image, sorry.
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.
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.
> 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.
Fixing assingment to Robin
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.
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.
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.
Created attachment 5871 [details] thinlineJaws.png Ken has kindly done me a rendering using jaws. Analysis to follow.
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.
Reassigning to new email address.
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