There is a bug here because we should choose non-interfering sets of pixels for abutting shapes, like we do for rendered shapes (and clipping should use the same rules as non-AA rendered shapes). Adjacent shapes that share a common border will divide the pixels along that border between them such that any given pixel is in exactly one and only one of the 2 shapes. The same rule should hold for clipping, but it isn't holding here.

The fact that this particular test case works "at float precision" is only incidental and simply lowering our calculation resolution isn't fixing the real bug, which is that we are using the wrong calculations to turn a transformed rectangle into a rectangular integer pixel-oriented device region.

I think the problem here is the floor/ceil processing. The place for "floor/ceil" would be in "dirty region calculations". In those cases you compute the exact bounds of everything that changed which may be floating point bounds. You then typically floor/ceil those dirty bounds so that you redraw every pixel affected in its entirety - i.e. you are making sure that you draw the full content of any pixel that intersected the dirty region even if it only intersected a tiny bit.

But, for clipping, we "rasterize the shape to an integer region" and that process should use pixel inclusion rules which are:

- if the center of a pixel is inside the transformed shape, then that pixel is included in the rendering (clipping) process - if the center of a pixel is on the infinitely thin boundary of a shape, then it is included iff the space to its right is inside the txshape - or if it is on a horizontal edge, then iff the space below it is inside the txshape

That rule typically matches what happens when you simply round the results "down", i.e. "ceil(x-0.5), ceil(y-0.5)". (Those calculations will return floor(v) for a value that ends in 0.5, but ceil(v) for a value that ends above 0.5.) Note that the round() function performs a "round-up" operation which maps 0.5 values to the next largest integer, but we want to map them to the next smallest integer so we would use this modified "rounding formula". That same exact rounding operation is applied to both the min and max values of the transformed bounds so that if the floating point "max" values for one rectangle are equal to the "min" values of the next, then the resulting integer boundary between the two device-clip-regions would be rounded the same way and have the same value, and since the regions are considered "half open", then that means that the pixels will be included in only one of those resulting regions...

                        ...jim


On 10/13/13 7:11 PM, Nicolas wrote:
The getClip()/getClipBounds bug fix
(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8004859) changed
sun.java2d.SunGraphics2D.transformShape(AffineTransform tx, Shape clip)
to use a Rectangle2D.Double instead of Float and this change causes the
rounding (done by casting from double to float) to be lost. The lack of
rounding causes a bug on my program when using the jre 1.7.0.40, which
is not present on 1.7.0.25.  Take a look at the test case
ClippingTest.java (attached), it draws clippingOn40.png (attached) when
running the JRE 1.7.0.40, and clippingOn25.png (attached) when running
JRE 1.7.0.25. Notice that on 1.7.0.25 the drawing does not show the
center darker line... the clipping regions do not overlap.

A fix to this new bug is to change:
----
return new Rectangle2D.Double(matrix[0], matrix[1],
     matrix[2] - matrix[0],
     matrix[3] - matrix[1]);
----
on line 1947 of sun.java2d.SunGraphics2D to:
----
new Rectangle2D.Float((float)matrix[0], (float)matrix[1],
     (float)(matrix[2] - matrix[0]),
     (float)(matrix[3] - matrix[1])
     );
----

My application calculates the clipping regions on screen using doubles
and a procedure slightly different (but equivalent if we take away the
insignificant digits of the calculation) compared to the one used by
SunGraphics2D, so when SunGraphics2D transform them (calculates the
usrClip) there are small differences (due to insignificant digits) that
in some cases end up being magnified to a full pixel [as the clipRegion
is calculated using floor() and ceil() inside the getBounds() called by
SunGraphics2D.validateCompClip()] by the current implementation
(1.7.0.40) and were nicely cut off by the previous (1.7.0.25) when it
rounded the result using Rectangle2D.Float.

Please let me know if I can be of more help,
Nicolas

Reply via email to