From aff25f135af20ec89c7a68f2a0a0ede7eb1491a6 Mon Sep 17 00:00:00 2001 From: Phil Race Date: Wed, 3 Dec 2025 18:20:31 +0000 Subject: [PATCH] 4690476: NegativeArraySizeException from AffineTransformOp with shear Reviewed-by: psadhukhan, jdv --- .../java/awt/image/AffineTransformOp.java | 82 ++++++++++++++++--- .../AffineTransformOp/AffineTxOpSizeTest.java | 68 +++++++++++++++ 2 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 test/jdk/java/awt/image/AffineTransformOp/AffineTxOpSizeTest.java diff --git a/src/java.desktop/share/classes/java/awt/image/AffineTransformOp.java b/src/java.desktop/share/classes/java/awt/image/AffineTransformOp.java index 4c4179699f0..7a3ee8646a7 100644 --- a/src/java.desktop/share/classes/java/awt/image/AffineTransformOp.java +++ b/src/java.desktop/share/classes/java/awt/image/AffineTransformOp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -186,7 +186,13 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { * {@code getBounds2D(BufferedImage)} * are not necessarily the same as the coordinates of the * {@code BufferedImage} returned by this method. If the - * upper-left corner coordinates of the rectangle are + * application provides a {@code dst} that is always returned. + * If {@code dst} is {@code null} and a destination {code BufferedImage} + * with the transformed dimensions cannot be created, the {@code src} + * dimensions will be substituted. + * + *

+ * If the upper-left corner coordinates of the rectangle are * negative then this part of the rectangle is not drawn. If the * upper-left corner coordinates of the rectangle are positive * then the filtered image is drawn at that position in the @@ -224,7 +230,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { BufferedImage origDst = dst; if (dst == null) { - dst = createCompatibleDestImage(src, null); + dst = createCompatibleDestImageInt(src, null); dstCM = srcCM; origDst = dst; } @@ -272,7 +278,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { } else { needToConvert = true; - dst = createCompatibleDestImage(src, null); + dst = createCompatibleDestImageInt(src, null); } } @@ -320,7 +326,12 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { * {@code getBounds2D(Raster)} * are not necessarily the same as the coordinates of the * {@code WritableRaster} returned by this method. If the - * upper-left corner coordinates of rectangle are negative then + * application provides a {@code dst} that is always returned. + * If {@code dst} is {@code null} and a destination {code Raster} + * with the transformed dimensions cannot be created, the {@code src} + * dimensions will be substituted. + *

+ * If the upper-left corner coordinates of rectangle are negative then * this part of the rectangle is not drawn. If the coordinates * of the rectangle are positive then the filtered image is drawn at * that position in the destination {@code Raster}. @@ -342,7 +353,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { throw new NullPointerException("src image is null"); } if (dst == null) { - dst = createCompatibleDestRaster(src); + dst = createCompatibleDestRasterInt(src); } if (src == dst) { throw new IllegalArgumentException("src image cannot be the "+ @@ -422,7 +433,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { /** * Creates a zeroed destination image with the correct size and number of * bands. A {@code RasterFormatException} may be thrown if the - * transformed width or height is equal to 0. + * transformed width or height is less than or equal to 0, or too large. *

* If {@code destCM} is null, * an appropriate {@code ColorModel} is used; this @@ -437,9 +448,36 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { */ public BufferedImage createCompatibleDestImage (BufferedImage src, ColorModel destCM) { - BufferedImage image; Rectangle r = getBounds2D(src).getBounds(); + try { + return createCompatibleDestImage(src, destCM, r); + } catch (Exception e) { + if (e instanceof RasterFormatException) { + throw e; + } else { + RasterFormatException re = + new RasterFormatException("Could not create transformed image of size " + r); + re.initCause(e); + throw re; + } + } + } + private BufferedImage createCompatibleDestImageInt(BufferedImage src, + ColorModel destCM) { + + try { + return createCompatibleDestImage(src, destCM); + } catch (Exception e) { + return createCompatibleDestImage(src, destCM, src.getRaster().getBounds()); + } + } + + private BufferedImage createCompatibleDestImage(BufferedImage src, + ColorModel destCM, + Rectangle r) { + + BufferedImage image; // If r.x (or r.y) is < 0, then we want to only create an image // that is in the positive range. // If r.x (or r.y) is > 0, then we need to create an image that @@ -482,14 +520,38 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { /** * Creates a zeroed destination {@code Raster} with the correct size * and number of bands. A {@code RasterFormatException} may be thrown - * if the transformed width or height is equal to 0. + * if the transformed width or height is less than or equal to 0, or too large. * * @param src The {@code Raster} to be transformed. * * @return The zeroed destination {@code Raster}. */ public WritableRaster createCompatibleDestRaster (Raster src) { - Rectangle2D r = getBounds2D(src); + Rectangle r = getBounds2D(src).getBounds(); + try { + return createCompatibleDestRaster(src, r); + } catch (Exception e) { + if (e instanceof RasterFormatException) { + throw e; + } else { + RasterFormatException re = + new RasterFormatException("Could not create transformed raster of size " + r); + re.initCause(e); + throw re; + } + } + } + + private WritableRaster createCompatibleDestRasterInt(Raster src) { + try { + return createCompatibleDestRaster(src); + } catch (Exception e) { + Rectangle r = src.getBounds(); + return createCompatibleDestRaster(src, r); + } + } + + private WritableRaster createCompatibleDestRaster (Raster src, Rectangle r) { return src.createCompatibleWritableRaster((int)r.getX(), (int)r.getY(), diff --git a/test/jdk/java/awt/image/AffineTransformOp/AffineTxOpSizeTest.java b/test/jdk/java/awt/image/AffineTransformOp/AffineTxOpSizeTest.java new file mode 100644 index 00000000000..86f0c1030d6 --- /dev/null +++ b/test/jdk/java/awt/image/AffineTransformOp/AffineTxOpSizeTest.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 4690476 + * @summary Verify behaviour with transform which creates too large an image. + */ + +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import static java.awt.image.AffineTransformOp.*; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.awt.image.RasterFormatException; + +public class AffineTxOpSizeTest { + + static final int W = 2552, H = 3300; + // This transform will require an approx 60_000 x 60_000 raster which is too large + static final AffineTransform AT = new AffineTransform(0.2, 23, 18, 0.24, -70.0, -90.0); + + public static void main(String[] args) { + BufferedImage src = new BufferedImage(W, H, BufferedImage.TYPE_INT_RGB); + testAOP(src, TYPE_BICUBIC); + testAOP(src, TYPE_BILINEAR); + testAOP(src, TYPE_NEAREST_NEIGHBOR); + } + + static void testAOP(BufferedImage src, int iType) { + AffineTransformOp aop = new AffineTransformOp(AT, iType); + System.out.println("Bounds=" + aop.getBounds2D(src)); + + aop.filter(src, null); + aop.filter(src.getRaster(), null); + try { + aop.createCompatibleDestImage(src, src.getColorModel()); + throw new RuntimeException("No exception for image"); + } catch (RasterFormatException e) { + } + try { + aop.createCompatibleDestRaster(src.getRaster()); + throw new RuntimeException("No exception for raster"); + } catch (RasterFormatException e) { + } + } + +}