From d38898b1dda98b526d903870a58c0fbda52d4ded Mon Sep 17 00:00:00 2001 From: spr Date: Wed, 25 Sep 2013 10:15:05 +0000 Subject: [PATCH] - #0000743: OutOfMemoryError caused by image ResampleOp in FImagePanel. (http://www.cardforge.org/bugz/view.php?id=743) --- .../gui/toolbox/imaging/FImagePanel.java | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/main/java/forge/gui/toolbox/imaging/FImagePanel.java b/src/main/java/forge/gui/toolbox/imaging/FImagePanel.java index ff807e97588..313882e3edf 100644 --- a/src/main/java/forge/gui/toolbox/imaging/FImagePanel.java +++ b/src/main/java/forge/gui/toolbox/imaging/FImagePanel.java @@ -41,7 +41,7 @@ import com.mortennobel.imagescaling.ResampleOp; *

* Options to scale and rotate the image are available if required. * - * @version $Id:$ + * @version $Id$ * */ @SuppressWarnings("serial") @@ -55,6 +55,11 @@ public class FImagePanel extends JPanel { // Remains the same regardless of any transformations that might be applied to it. private BufferedImage sourceImage = null; + // Resampling is an expensive operation so keep a copy of last resampled image and + // use this for repaints if image has not been resized or changed. + private BufferedImage scaledImage = null; + private boolean isResampleEnabled = true; + private double imageScale = 1; private int degreesOfRotation = 0; @@ -69,12 +74,6 @@ public class FImagePanel extends JPanel { setResizeListener(); }; - public void clearImage() { - this.sourceImage = null; - this.imageScale = 1; - repaint(); - } - /** * This timer is used to identify when resizing has finished. *

@@ -95,6 +94,7 @@ public class FImagePanel extends JPanel { private void doResizedFinished() { this.resizingTimer.stop(); this.isResizing = false; + this.isResampleEnabled = true; this.repaint(); } @@ -118,11 +118,11 @@ public class FImagePanel extends JPanel { * This means the image can only have either a vertical or horizontal orientation. */ public void setImage(BufferedImage image, int initialRotation, AutoSizeImageMode autoSizeMode) { - if (this.sourceImage != image) { + if (this.sourceImage != image || this.degreesOfRotation != initialRotation || this.autoSizeMode != autoSizeMode) { + isResampleEnabled = true; this.autoSizeMode = autoSizeMode; if (initialRotation > 0) { setRotation(initialRotation); } this.sourceImage = image; - setImageScale(); repaint(); } } @@ -165,9 +165,11 @@ public class FImagePanel extends JPanel { * This means the image can only have either a vertical or horizontal orientation. */ public void setRotation(int degrees) { - this.degreesOfRotation = ImageUtil.getRotationToNearest(degrees, 90); - setImageScale(); - repaint(); + if (this.degreesOfRotation != degrees) { + this.degreesOfRotation = ImageUtil.getRotationToNearest(degrees, 90); + isResampleEnabled = true; + repaint(); + } } /** @@ -217,19 +219,26 @@ public class FImagePanel extends JPanel { /** * Uses Morten Nobel's java-image-scaling library to resize image. *

- * This produces superior quality to affine scaling as image sizes - * are reduced but at the cost of performance. + * This produces superior quality to affine scaling especially as + * image sizes are reduced but at the cost of performance. + *

+ * You cannot legislate for when this will be called since it depends + * on how often paintComponent() is invoked and any number of external + * events can cause this to happen. But resampling is an expensive operation + * so use an existing copy if the image has not changed or been resized. */ private BufferedImage getResampledImage() { - BufferedImage scaledImage = null; if (this.imageScale != 1) { - DimensionConstrain constrain = DimensionConstrain.createRelativeDimension((float)this.imageScale); - ResampleOp resampler = new ResampleOp(constrain); - scaledImage = resampler.filter(sourceImage, null); + if (isResampleEnabled) { + isResampleEnabled = false; + DimensionConstrain constrain = DimensionConstrain.createRelativeDimension((float)this.imageScale); + ResampleOp resampler = new ResampleOp(constrain); + this.scaledImage = resampler.filter(sourceImage, null); + } } else { - scaledImage = sourceImage; + this.scaledImage = sourceImage; } - return scaledImage; + return this.scaledImage; } /** @@ -319,10 +328,14 @@ public class FImagePanel extends JPanel { private void setImageScale() { if (this.sourceImage != null) { if (this.autoSizeMode != AutoSizeImageMode.OFF) { - this.imageScale = ImageUtil.getBestFitScale(getSourceImageSize(), this.getSize()); - if (this.imageScale == 0) { this.imageScale = 1; }; - if (this.autoSizeMode == AutoSizeImageMode.SOURCE && this.imageScale > 1) { - this.imageScale = 1; + Double newScale = ImageUtil.getBestFitScale(getSourceImageSize(), this.getSize()); + if (newScale != this.imageScale) { + isResampleEnabled = true; + this.imageScale = newScale; + if (newScale == 0) { this.imageScale = 1; }; + if (this.autoSizeMode == AutoSizeImageMode.SOURCE && newScale > 1) { + this.imageScale = 1; + } } } }