RasterFormatException thrown when drawing a tiled image made of non-writable rasters

Martin Desruisseaux martin.desruisseaux at geomatys.com
Fri Oct 15 15:10:49 UTC 2021


Hello all

Call to Graphics2D.drawRenderedImage(RenderedImage, AffineTransform) 
fails if the image contains more than one tile (or a single tile not 
located at 0,0) and the tiles are not instances of WritableRaster (i.e. 
are instances of the read-only Raster parent class). A test case [1] 
reproduces this issue. The bug is demonstrated by drawing the same image 
twice: once with WritableRaster tiles (which succeed), then the same 
operation where the only change is the use of Raster tiles. The 
exception is:

Exception in thread "main" java.awt.image.RasterFormatException: (parentX + width) is outside raster
     at java.desktop/java.awt.image.WritableRaster.createWritableChild(WritableRaster.java:228)
     at java.desktop/sun.java2d.SunGraphics2D.drawTranslatedRenderedImage(SunGraphics2D.java:2852)
     at java.desktop/sun.java2d.SunGraphics2D.drawRenderedImage(SunGraphics2D.java:2711)

The cause is in the following code in SunGraphics2D:

// Create a WritableRaster containing the tile
WritableRaster wRaster = null;
if (raster instanceof WritableRaster) {
     wRaster = (WritableRaster)raster;
} else {
     // Create a WritableRaster in the same coordinate system
     // as the original raster.
     wRaster =
         Raster.createWritableRaster(raster.getSampleModel(),
                                     raster.getDataBuffer(),
                                     null);
}
// Translate wRaster to start at (0, 0) and to contain
// only the relevent portion of the tile
wRaster = wRaster.createWritableChild(tileRect.x, tileRect.y,
                                       tileRect.width,
                                       tileRect.height,
                                       0, 0,
                                       null);

If the tile is not an instance of WritableRaster, then the code wraps 
the tile DataBuffer in a new WritableRaster*but with a location set to 
(0,0)*, because the location argument in createWritableRaster(…) call is 
null. Then the call to createWritableChild(…) applies a translation for 
bringing the tile location to (0,0). But in the case where the raster 
has been converted from Raster to WritableRaster, that translation has 
already been applied, and the effect of current code is to apply the 
translation twice.

I think that this bug has been largely unnoticed because most users use 
BufferedImage, which has a single tile always located at (0,0), and the 
minority of developers using their own RenderedImage implementation uses 
WritableRaster instances, because Raster.create(…) methods provide 
optimizations for common cases which result in WritableRaster instances 
created even when the user asked only for a Raster. To make this bug 
apparent, it is necessary to invoke Raster.createRaster(…) with a sample 
model for which no optimization is provided.

The commit at [1] provides a test case and a suggested fix. From that 
point, I'm not sure how to proceed (I'm sure there is many steps, that 
the proposed commit needs to be modified, etc.). Can anyone can give me 
some hints?

     Regards,

         Martin

[1]https://github.com/Geomatys/jdk/commit/94242a05ff8b9c1af603a11133af7c6016c9e833



More information about the client-libs-dev mailing list