[OpenJDK 2D-Dev] getClip()/getClipBounds() bug fix (of 8004859) causes new bug

Jim Graham james.graham at oracle.com
Mon Oct 14 22:47:27 UTC 2013

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 


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, which
> is not present on  Take a look at the test case
> ClippingTest.java (attached), it draws clippingOn40.png (attached) when
> running the JRE, and clippingOn25.png (attached) when running
> JRE Notice that on 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
> ( and were nicely cut off by the previous ( when it
> rounded the result using Rectangle2D.Float.
> Please let me know if I can be of more help,
> Nicolas

More information about the 2d-dev mailing list