Comment 30 for bug 398244

Revision history for this message
In , Carl Worth (cworth) wrote :

Created an attachment (id=29196)
Test program to demonstrate differing zero-width-line algorithms

(In reply to comment #13)
> Created an attachment (id=29156) [details]
> Test program
>
> Hi Carl. This is the program i used to get the xtrace. It just create a message
> window with a button that show the bug in the right bottom corner. Anything
> else just ask for it.

Hi Jaime,

Thanks for the test program.

I'm now attaching a test program that is as minimal as possible and
that shows different behavior with either the vesa or xf86-video-intel
drivers.

However, this isn't actually a driver bug.

Instead, the test program (and wine) are drawing lines with width of
0, (known as "thin lines" in the X protocol description). And such
lines can be implemented with a device-specific algorithm that can
differ from driver to driver[*].

So it's not actually surprising that there are differences
here---that's perfectly allowable. What is a bit surprising is that
the results with the intel driver are equivalent to the results that
would be obtained by using a line width of 1, (where the protocol
specifies an algorithm that defines precisely which pixels must be
affected). I would have expected the vesa driver to match that
behavior.

The "wide lines" (such as line_width=1) specification requires that
drawing a segment (x1,y1)-(x2,y2) be equivalent to drawing from
(x2,y2)-(x1,y1). The protocol specification recommends, (but does not
require), that "thin lines" also meet this same
constraint. Interestingly, the intel driver appears to meet this
constraint, but the vesa driver does not.

So, according to what I can read of the X protocol specification,
neither the vesa nor intel drivers have a bug here. They are allowed
to differ in these details of which pixels get lit. (And if anything,
vesa is the odd one for not matching the algorithm of line_width=1.)

So, to fix the bug, wine is going to have to change its code to not
rely on the pixelization of zero-width lines, (which can differ from
one driver to the next), and should instead use lines with
width=1. Making this change will require making some adjustments to
how the segment endpoint coordinates are computed to still achieve the
desired result.

I hope this helps, (and that my test program is useful for anyone
looking into this problem).

And of course, another option is to stop using things like
XDrawSegment entirely and instead use something like cairo
(http://cairographics.org) which always provides consistent
pixelization regardless of drivers.

Good luck!

[*] Here are some of the details from the specification:

Wide lines are drawn centered on the path described by the graphics
request. Unless otherwise specified by the join-style or cap-style,
the bounding box of a wide line with endpoints [x1, y1], [x2, y2] and
width w is a rectangle with vertices at the following real
coordinates:

[x1-(w*sn/2), y1+(w*cs/2)], [x1+(w*sn/2), y1-(w*cs/2)],
[x2-(w*sn/2), y2+(w*cs/2)], [x2+(w*sn/2), y2-(w*cs/2)]

Here sn is the sine of the angle of the line, and cs is the cosine of
the angle of the line. A pixel is part of the line and so is drawn if
the center of the pixel is fully inside the bounding box (which is
viewed as having infinitely thin edges). If the center of the pixel is
exactly on the bounding box, it is part of the line if and only if the
interior is immediately to its right (x increasing direction). Pixels
with centers on a horizontal edge are a special case and are part of
the line if and only if the interior or the boundary is immediately
below (y increasing direction) and the interior or the boundary is
immediately to the right (x increasing direction).

Thin lines (zero line-width) are one-pixel-wide lines drawn using an
unspecified, device-dependent algorithm. There are only two
constraints on this algorithm.

   1. If a line is drawn unclipped from [x1,y1] to [x2,y2] and if
   another line is drawn unclipped from [x1+dx,y1+dy] to
   [x2+dx,y2+dy], a point [x,y] is touched by drawing the first line
   if and only if the point [x+dx,y+dy] is touched by drawing the
   second line.

   2. The effective set of points comprising a line cannot be affected
   by clipping. That is, a point is touched in a clipped line if and
   only if the point lies inside the clipping region and the point
   would be touched by the line when drawn unclipped.

A wide line drawn from [x1,y1] to [x2,y2] always draws the same pixels
as a wide line drawn from [x2,y2] to [x1,y1], not counting cap-style
and join-style. It is recommended that this property be true for thin
lines, but this is not required. A line-width of zero may differ from
a line-width of one in which pixels are drawn. This permits the use of
many manufacturers' line drawing hardware, which may run many times
faster than the more precisely specified wide lines.

http://tronche.com/gui/x/xlib/GC/manipulating.html