This bug occasionaly detected while working on the bug 688363.With GraphicAlphaBits=1 -r72 on any raster device the following program gives a brush-like spot instead the right one : 0 setlinecap 0 setlinejoin 0.001 setflat 700 setlinewidth 300 350 50 90 95 arc stroke showpage The problem happens due to the gx_stroke_path_only is incomplete. It gives defects when the line width is 2+ times grater than the local curvity radius.
Created attachment 2160 [details] cur.ppm.zip A zipped raster from ppmraw.
Reduced example that shows the same problems: 0 setlinecap 0 setlinejoin 0.001 setflat 700 setlinewidth 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3242 lineto stroke showpage
Created attachment 5472 [details] Diagram of why the problem happens This diagram attempts to explain why the problem occurs. When stroking the 2 segment red line, we thicken each line segment to the cyan areas. We then adjust the first area we plot with a mitre join to the second area. In the line_join_points routine, we correctly determine which side of the curve is 'outside', and add the green region. Unfortunately, we ignore the possibility that the stroking is wide enough to expose the area between the lines at the bottom.
Created attachment 5473 [details] Possible (sub optimal) solution The simplest solution I can see to this bug would be to add another point to the join, which would result in the area shown in this attachment being filled. Clearly this is much closer to the 'correct' result, and does not require many changes to the current code. For 'normal' cases the extra area would be entirely hidden in the area being filled anyway. The only downsides of this is that a) more pixels might get plotted twice (don't know if this is true - I'd have to follow the code through a bit further to see whether this is the case or not), and b) the bottom edge of the curve isn't quite smooth.
Created attachment 5474 [details] Better solution The 'ideal' solution, if I can figure out how, would be to spot that a secondary join is required (possibly by intersecting the two 'outer' edges of the 'inner' region, and to add the indicated region to the shape. I have no idea how much effort this will actually be though. Will continue investigating.
Created attachment 5477 [details] Proposed patch to implement the "better" solution. This patch extends the line_join_points function to return points for a "secondary" join too. Normally this will be empty, but in cases such as shown here, it will be populated with the region needing to be filled under the curve. Callers to line_join_points have been modified to fill this area as required too.
Created attachment 5483 [details] jointest2.ps - an example that shows why this solution is no good. This attachment shows the effect of different joins with short lines. When rendered in ghostscript (patched/unpatched) and acrobat, we get different results. The proposed fix makes this significantly worse.
Created attachment 5484 [details] jointest2 rendered in acrobat This shows how acrobat renders the file. This version can be taken as definitive.
Created attachment 5485 [details] unpatched ghostscripts rendering Here we see that unpatched, ghostscripts rendering is pretty much on the money, with the exception of the 'round' join, which is a known bug (bug # 688269).
Created attachment 5486 [details] patched ghostscripts rendering Here we see that both sides of the join are filled in - clearly not the same as acrobat. This suggests that filling the opposite side of the stroke is the wrong solution. An alternative approach to fix the problem is therefore required - possibly by boosting the degree of flatness used for the initial stroke in cases where the stroke is wide.
> An alternative approach to fix the problem is therefore required - > possibly by boosting the degree of flatness used for the initial stroke in > cases where the stroke is wide. And no, the answer can't be found purely in flattening better, because, as the reduced example in comment 2 shows, it happens with already flattened things too.
Created attachment 5487 [details] cur.png Attaching a png version of Igor's original ppm.zip for easier browsing.
FWIW, the original example looks ok with x11alpha, but not with x11. Robin's flattened example from comment #2 shows the problem with both devices.
Some further observations: 1) Render the postscript in the original bug report in acrobat 9 pro and ghostscript, and both give the same 'bow tie' shape. (By which I mean, it looks like a line rotated by a few degrees around it's centre, so the ends are wider than the middle). The difference is that ghostscript leaves one end of the bow tie looking "ragged". 2) Render the cutdown example from comment #2, and we see more difference; ghostscript draws it as the same 'bow tie' shape as before (albeit less thick at the ends, as we only have 2 line segments). Acrobat draws it with even thickness along the length. This leads me to suspect that Acrobat is 'fudging' the stroke somehow to avoid the widening at the end. As a test, consider this modified example. ---->8----- 0 setlinecap 0 setlinejoin /DeviceRGB setcolorspace 700 setlinewidth 0 0 0 setcolor 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3242 lineto stroke 0 1 0 setcolor 400.0000 533.3320 moveto 399.6328 533.3281 lineto stroke 0 0 1 setcolor 399.6328 533.3281 moveto 399.2695 533.3242 lineto stroke showpage ---->8----- This renders the same shape as before in black, then renders the individual line segments in blue/green on top. View this in Acrobat 9, and black pixels can be seen outside the bottom half of the shape - indicating that Acrobat has adjusted the combine stroke region outwards there. Alternatively, render this in Acrobat 9: ---->8----- 0 setlinecap 0 setlinejoin /DeviceRGB setcolorspace 700 setlinewidth 0 1 0 setcolor 400.0000 533.3320 moveto 399.6328 533.3281 lineto stroke 0 0 1 setcolor 399.6328 533.3281 moveto 399.2695 533.3242 lineto stroke 0 0 0 setcolor 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3242 lineto stroke showpage ---->8----- and it shows the opposite adjustment being done in the top half of the shape (blue and green pixels can be seen poking out from the black shape that is now plotted on top). I suspect that Acrobat is using some undocumented heuristic to identify when the outside edge of line segments should be shortened. As such, we may be best to ignore the cutdown example and revert to the original one. This means that the earlier discarded idea of flattening more with wide curves might indeed be a solution here (leaving us with a separate problem of matching adobes 'fudge' later).
The following example: 0 setlinecap 0 setlinejoin 700 setlinewidth 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3236 lineto stroke showpage Shows the problem in gs, and appears to render in acrobat as a line of constant thickness. Tweak just one number very slightly, thus: 0 setlinecap 0 setlinejoin 700 setlinewidth 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3235 lineto stroke showpage and Acrobat stops rendering it as a line of constant thickness. Call this 'Change A'. Tweak that number a bit further, and as we change it from 533.3205 to 533.3204, thus: 0 setlinecap 0 setlinejoin 700 setlinewidth 400.0000 533.3320 moveto 399.6328 533.3281 lineto 399.2695 533.3204 lineto stroke showpage ... Acrobat starts to show the same 'void' in the bottom half of the shape as ghostscript does. Call this 'Change B'. Comparing the renderings given either side of change B it is clear that they do not differ just by the void starting to show up - something has deliberately gone to the trouble of moving the line ends and filling the void. So maybe the code in the patch above *is* required, but only if some obscure condition (perhaps relating to the difference in angle of the 2 line segments) is satisfied. Both Change A and B happen irrespective of the strokewidth (same changes occur at the same values with strokewidth set to 600 or 800). In this particular example at least, change A appears to happen as the difference in the angle of the two lines passes 0.1degrees. Change B occurs as the difference in the angle of the two lines passes 0.6degrees.
(Thanks to Ralph who suggested I look for what exactly triggers this behaviour). Testing another couple of examples suggests it's not purely the relative angle of the lines. Scaling an example up by a factor of 2 changes the angle at which the changes happen.
One solution that occurs to me; if we make use of the segment_notes, we can ensure that we always fill 'under' the curve where required, if the line segment comes from a flattened curve. This would solve the original problem. It would leave us behaving slightly differently to Acrobat in the case where Acrobat is adjusting the ends of strokes between the cutdown example, but this may be an acceptable step forwards. If we figure out exactly what Acrobat is doing in the future, we can extend the conidition to make it fill under the curve then too.
Created attachment 5497 [details] jointest3.ps: Show a range of joins between short strokes This shows a range of joins between short strokes: Column 1: Left to right Column 2: Right to left Column 3: Left to right, inverted Column 4: Right to left, inverted Row 0 (Bottom): Mitre join (0) Row 1: Round join (1) Row 2: Bevel join (2) Row 3: Very small curve, with mitre joins Row 4: Very small curve, with round joins Row 5 (Top): Very small curve, with bevel joins.
Created attachment 5498 [details] jointest3_ApplePreview.png
Created attachment 5499 [details] jointest3-evince.png attaching the results of running jointest3.ps through pdfwrite, and then rendered with evince (libpoppler + cairo). Looks substantially the same as Apple Preview.
Reassigning to me, as I have a plan to fix this.
Fixed in revision 10385.