diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 7965c5e51c..c9d69ca85f 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -119,6 +119,7 @@ import mage.client.table.TablesPane; import mage.client.tournament.TournamentPane; import mage.client.util.EDTExceptionHandler; import mage.client.util.GUISizeHelper; +import mage.client.util.ImageCaches; import mage.client.util.SettingsManager; import mage.client.util.SystemUtil; import mage.client.util.audio.MusicPlayer; @@ -1470,6 +1471,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } public void changeGUISize() { + ImageCaches.flush(); setGUISize(); Plugins.getInstance().changeGUISize(); CountryUtil.changeGUISize(); diff --git a/Mage.Client/src/main/java/mage/client/cards/BigCard.java b/Mage.Client/src/main/java/mage/client/cards/BigCard.java index 75fa550878..cc3417c1b4 100644 --- a/Mage.Client/src/main/java/mage/client/cards/BigCard.java +++ b/Mage.Client/src/main/java/mage/client/cards/BigCard.java @@ -54,6 +54,7 @@ import mage.client.plugins.impl.Plugins; import mage.client.util.ImageHelper; import mage.constants.EnlargeMode; import org.jdesktop.swingx.JXPanel; +import mage.client.util.TransformedImageCache; /** * Class for displaying big image of the card @@ -103,7 +104,13 @@ public class BigCard extends JComponent { } - public void setCard(UUID cardId, EnlargeMode enlargeMode, Image image, List strings) { + public void setCard(UUID cardId, EnlargeMode enlargeMode, Image image, List strings, boolean rotate) { + if (rotate && getWidth() > getHeight()) { + image = TransformedImageCache.getRotatedResizedImage((BufferedImage)image, getHeight(), getWidth(), Math.toRadians(90.0)); + } else { + image = TransformedImageCache.getResizedImage((BufferedImage)image, getWidth(), getHeight()); + } + if (this.cardId == null || !enlargeMode.equals(this.enlargeMode) || !this.cardId.equals(cardId)) { if (this.panel != null) { remove(this.panel); diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index 8d6235a08a..1a43fca155 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -374,7 +374,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis @Override public void mouseMoved(MouseEvent arg0) { this.bigCard.showTextComponent(); - this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules()); + this.bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, getRules(), false); } @Override diff --git a/Mage.Client/src/main/java/mage/client/cards/Permanent.java b/Mage.Client/src/main/java/mage/client/cards/Permanent.java index a01e368641..0db10e5887 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Permanent.java +++ b/Mage.Client/src/main/java/mage/client/cards/Permanent.java @@ -56,6 +56,8 @@ import mage.client.util.ImageHelper; import mage.constants.CardType; import mage.view.CounterView; import mage.view.PermanentView; +import org.mage.plugins.card.images.ImageCache; +import mage.client.util.TransformedImageCache; /** * @@ -215,7 +217,7 @@ public class Permanent extends Card { Graphics2D g = (Graphics2D) tappedImage.getGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.drawImage(this.createImage(ImageHelper.rotate(small, dimension)), 0, 0, this); + g.drawImage(TransformedImageCache.getRotatedResizedImage(small, dimension.frameWidth, dimension.frameHeight, Math.toRadians(90.0)), 0, 0, this); g.dispose(); } diff --git a/Mage.Client/src/main/java/mage/client/components/ColorPane.java b/Mage.Client/src/main/java/mage/client/components/ColorPane.java index 240cc4380f..8fc39aa5be 100644 --- a/Mage.Client/src/main/java/mage/client/components/ColorPane.java +++ b/Mage.Client/src/main/java/mage/client/components/ColorPane.java @@ -139,9 +139,7 @@ public class ColorPane extends JEditorPane { if (hyperlinkEnabled) { text = text.replaceAll("(]*>([^<]*)) (\\[[0-9a-fA-F]*\\])", "$1 $3"); } - setEditable(true); kit.insertHTML(doc, doc.getLength(), text, 0, 0, null); - setEditable(false); int len = getDocument().getLength(); setCaretPosition(len); diff --git a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java index 1e3c9a5168..da4afed58e 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java +++ b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java @@ -1,12 +1,18 @@ package mage.client.components; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import java.awt.BasicStroke; import java.awt.Color; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.Objects; import javax.swing.JPanel; +import mage.client.util.ImageCaches; import org.jdesktop.swingx.graphics.GraphicsUtilities; import org.jdesktop.swingx.graphics.ShadowRenderer; @@ -21,23 +27,145 @@ public class MageRoundPane extends JPanel { private int X_OFFSET = 30; private int Y_OFFSET = 30; - private BufferedImage shadow = null; private final Color defaultBackgroundColor = new Color(255, 255, 255, 200); private Color backgroundColor = defaultBackgroundColor; private final int alpha = 0; + private static Map SHADOW_IMAGE_CACHE; + private static Map IMAGE_CACHE; + + static { + SHADOW_IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(ShadowKey key) { + return createShadowImage(key); + } + })); + + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } + + private final static class ShadowKey + { + final int width; + final int height; + + public ShadowKey(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 97 * hash + this.width; + hash = 97 * hash + this.height; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ShadowKey other = (ShadowKey) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + return true; + } + } + + private final static class Key + { + final int width; + final int height; + final int xOffset; + final int yOffset; + final Color backgroundColor; + + public Key(int width, int height, int xOffset, int yOffset, Color backgroundColor) { + this.width = width; + this.height = height; + this.xOffset = xOffset; + this.yOffset = yOffset; + this.backgroundColor = backgroundColor; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 59 * hash + this.width; + hash = 59 * hash + this.height; + hash = 59 * hash + this.xOffset; + hash = 59 * hash + this.yOffset; + hash = 59 * hash + Objects.hashCode(this.backgroundColor); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.xOffset != other.xOffset) { + return false; + } + if (this.yOffset != other.yOffset) { + return false; + } + if (!Objects.equals(this.backgroundColor, other.backgroundColor)) { + return false; + } + return true; + } + } @Override protected void paintComponent(Graphics g) { - int x = X_OFFSET; - int y = Y_OFFSET; - int w = getWidth() - 2 * X_OFFSET; - int h = getHeight() - 2 * Y_OFFSET; + g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), X_OFFSET, Y_OFFSET, backgroundColor)), 0, 0, null); + } + + private static BufferedImage createImage(Key key) { + int x = key.xOffset; + int y = key.yOffset; + int w = key.width - 2 * key.xOffset; + int h = key.height - 2 * key.yOffset; int arc = 10; - Graphics2D g2 = (Graphics2D) g.create(); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height); + Graphics2D g2 = image.createGraphics(); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - if (shadow != null) { + BufferedImage shadow = SHADOW_IMAGE_CACHE.get(new ShadowKey(w, h)); + + { int xOffset = (shadow.getWidth() - w) / 2; int yOffset = (shadow.getHeight() - h) / 2; g2.drawImage(shadow, x - xOffset, y - yOffset, null); @@ -54,7 +182,7 @@ public class MageRoundPane extends JPanel { g2.fillRoundRect(x, y, w, h, arc, arc); }*/ - g2.setColor(backgroundColor); + g2.setColor(key.backgroundColor); g2.fillRoundRect(x, y, w, h, arc, arc); ////////////////////////////////////////////////////////////////// @@ -66,6 +194,7 @@ public class MageRoundPane extends JPanel { // //////////////////////////////////////////////////////////////// g2.dispose(); + return image; } public void setXOffset(int x_offset) { @@ -76,24 +205,21 @@ public class MageRoundPane extends JPanel { Y_OFFSET = y_offset; } - @Override - public void setBounds(int x, int y, int width, int height) { - super.setBounds(x, y, width, height); - - int w = getWidth() - 2 * X_OFFSET; - int h = getHeight() - 2 * Y_OFFSET; + private static BufferedImage createShadowImage(ShadowKey key) { + int w = key.width; + int h = key.height; int arc = 10; int shadowSize = 50; - shadow = GraphicsUtilities.createCompatibleTranslucentImage(w, h); - Graphics2D g2 = shadow.createGraphics(); + BufferedImage base = GraphicsUtilities.createCompatibleTranslucentImage(w, h); + Graphics2D g2 = base.createGraphics(); g2.setColor(Color.WHITE); g2.fillRoundRect(0, 0, w, h, arc, arc); g2.dispose(); ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, Color.GRAY); - shadow = renderer.createShadow(shadow); + return renderer.createShadow(base); } public void showDialog(boolean bShow) { diff --git a/Mage.Client/src/main/java/mage/client/components/MageTextArea.java b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java index 49b0184eda..219e0f72d7 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageTextArea.java +++ b/Mage.Client/src/main/java/mage/client/components/MageTextArea.java @@ -13,6 +13,8 @@ import org.mage.card.arcane.UI; * @author nantuko */ public class MageTextArea extends JEditorPane { + private String currentText; + private int currentPanelWidth; public MageTextArea() { UI.setHTMLEditorKit(this); @@ -31,6 +33,12 @@ public class MageTextArea extends JEditorPane { return; } + if(text.equals(currentText) && panelWidth == currentPanelWidth) + return; + + currentText = text; + currentPanelWidth = panelWidth; + final StringBuilder buffer = new StringBuilder(512); // Dialog is a java logical font family, so it should work on all systems buffer.append(" IMAGE_CACHES; + + static { + IMAGE_CACHES = new Vector(); + } + + public static Map register(Map map) + { + IMAGE_CACHES.add(map); + return map; + } + + public static void flush() + { + for (Map map : IMAGE_CACHES) { + map.clear(); + } + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/ImageHelper.java b/Mage.Client/src/main/java/mage/client/util/ImageHelper.java index e4d55b0df4..1eddc28e0d 100644 --- a/Mage.Client/src/main/java/mage/client/util/ImageHelper.java +++ b/Mage.Client/src/main/java/mage/client/util/ImageHelper.java @@ -52,6 +52,7 @@ import static mage.client.constants.Constants.FRAME_MAX_WIDTH; import static mage.client.constants.Constants.SYMBOL_MAX_SPACE; import mage.view.CardView; import org.mage.card.arcane.UI; +import org.mage.plugins.card.images.ImageCache; /** * @@ -70,21 +71,6 @@ public class ImageHelper { return null; } - /** - * - * @param ref - image name - * @param height - height after scaling - * @return a scaled image that preserves the original aspect ratio, with a - * specified height - */ - public static BufferedImage loadImage(String ref, int height) { - BufferedImage image = loadImage(ref); - if (image != null) { - return scaleImage(image, height); - } - return null; - } - public static BufferedImage loadImage(String ref) { if (!images.containsKey(ref)) { try { @@ -107,67 +93,7 @@ public class ImageHelper { } public static BufferedImage scaleImage(BufferedImage image, int width, int height) { - BufferedImage scaledImage = image; - int w = image.getWidth(); - int h = image.getHeight(); - do { - w /= 2; - h /= 2; - if (w < width || h < height) { - w = width; - h = height; - } - BufferedImage newImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); - Graphics2D graphics2D = newImage.createGraphics(); - graphics2D.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - graphics2D.drawImage(scaledImage, 0, 0, w, h, null); - graphics2D.dispose(); - scaledImage = newImage; - } while (w != width || h != height); - return scaledImage; - } - - public static BufferedImage scaleImage(BufferedImage image, int height) { - double ratio = height / (double) image.getHeight(); - int width = (int) (image.getWidth() * ratio); - return scaleImage(image, width, height); - } - - public static MemoryImageSource rotate(Image image, CardDimensions dimensions) { - int buffer[] = new int[dimensions.frameWidth * dimensions.frameHeight]; - int rotate[] = new int[dimensions.frameHeight * dimensions.frameWidth]; - PixelGrabber grabber = new PixelGrabber(image, 0, 0, dimensions.frameWidth, dimensions.frameHeight, buffer, 0, dimensions.frameWidth); - try { - grabber.grabPixels(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - for (int y = 0; y < dimensions.frameHeight; y++) { - for (int x = 0; x < dimensions.frameWidth; x++) { - rotate[((dimensions.frameWidth - x - 1) * dimensions.frameHeight) + y] = buffer[(y * dimensions.frameWidth) + x]; - } - } - - return new MemoryImageSource(dimensions.frameHeight, dimensions.frameWidth, rotate, 0, dimensions.frameHeight); - - } - - public static BufferedImage rotate(BufferedImage image, double angle) { - double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle)); - int w = image.getWidth(), h = image.getHeight(); - int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math.floor(h * cos + w * sin); - - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice gs = ge.getDefaultScreenDevice(); - GraphicsConfiguration gc = gs.getDefaultConfiguration(); - - BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT); - Graphics2D g = result.createGraphics(); - g.translate((neww - w) / 2, (newh - h) / 2); - g.rotate(angle, w / 2, h / 2); - g.drawRenderedImage(image, null); - g.dispose(); - return result; + return TransformedImageCache.getResizedImage(image, width, height); } public static void drawCosts(List costs, Graphics2D g, int xOffset, int yOffset, ImageObserver o) { @@ -191,26 +117,7 @@ public class ImageHelper { * @return */ public static BufferedImage getResizedImage(BufferedImage original, int width, int height) { - ResampleOp resampleOp = new ResampleOp(width, height); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to fit width panel - * - * @param original - * @param width - * @return - */ - public static BufferedImage getResizedImage(BufferedImage original, int width) { - if (width != original.getWidth()) { - double ratio = width / (double) original.getWidth(); - int height = (int) (original.getHeight() * ratio); - return getResizedImage(original, width, height); - } else { - return original; - } + return TransformedImageCache.getResizedImage(original, width, height); } /** @@ -223,17 +130,7 @@ public class ImageHelper { * @return scaled image */ public static BufferedImage scale(BufferedImage sbi, int imageType, int dWidth, int dHeight) { - BufferedImage dbi = null; - if (sbi != null) { - double fWidth = dWidth / sbi.getWidth(); - double fHeight = dHeight / sbi.getHeight(); - dbi = new BufferedImage(dWidth, dHeight, imageType); - Graphics2D g = dbi.createGraphics(); - AffineTransform at = AffineTransform.getScaleInstance(fWidth, fHeight); - g.drawRenderedImage(sbi, at); - g.dispose(); - } - return dbi; + return TransformedImageCache.getResizedImage(sbi, dWidth, dHeight); } /** @@ -244,9 +141,7 @@ public class ImageHelper { * @return */ public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) { - ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height); - BufferedImage image = resampleOp.filter(original, null); - return image; + return TransformedImageCache.getResizedImage(original, sizeNeed.width, sizeNeed.height); } /** diff --git a/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java new file mode 100644 index 0000000000..9a2cf205dc --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/TransformedImageCache.java @@ -0,0 +1,153 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.client.util; + +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; +import com.mortennobel.imagescaling.ResampleOp; +import java.awt.Graphics2D; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Transparency; +import java.awt.image.BufferedImage; +import java.util.Map; +import mage.client.util.ImageHelper; + +/** + * + * @author user + */ +public class TransformedImageCache { + private final static class Key + { + final int width; + final int height; + final double angle; + + public Key(int width, int height, double angle) { + this.width = width; + this.height = height; + this.angle = angle; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 53 * hash + this.width; + hash = 53 * hash + this.height; + hash = 53 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(other.angle)) { + return false; + } + return true; + } + } + + static Map> IMAGE_CACHE; + + static + { + // TODO: can we use a single map? + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function>() { + @Override + public Map apply(final Key key) { + return new MapMaker().weakKeys().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(BufferedImage image) { + if(key.width != image.getWidth() || key.height != image.getHeight()) + image = resizeImage(image, key.width, key.height); + if(key.angle != 0.0) + image = rotateImage(image, key.angle); + return image; + } + }); + } + })); + } + + private static BufferedImage rotateImage(BufferedImage image, double angle) { + double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle)); + int w = image.getWidth(), h = image.getHeight(); + int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math.floor(h * cos + w * sin); + + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gs = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gs.getDefaultConfiguration(); + + BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT); + Graphics2D g = result.createGraphics(); + g.translate((neww - w) / 2, (newh - h) / 2); + g.rotate(angle, w / 2, h / 2); + g.drawRenderedImage(image, null); + g.dispose(); + return result; + } + + private static BufferedImage resizeImage(BufferedImage original, int width, int height) { + ResampleOp resampleOp = new ResampleOp(width, height); + BufferedImage image = resampleOp.filter(original, null); + return image; + } + + public static BufferedImage getResizedImage(BufferedImage image, int width, int height) + { + return getRotatedResizedImage(image, width, height, 0.0); + } + + public static BufferedImage getRotatedImage(BufferedImage image, double angle) + { + return getRotatedResizedImage(image, -1, -1, angle); + } + + public static BufferedImage getRotatedResizedImage(BufferedImage image, int width, int height, double angle) + { + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + + if(angle == 0.0 && (width < 0 || imageWidth == width) && (height < 0 || imageHeight == height)) + return image; + + int resWidth; + int resHeight; + if(width < 0 && height < 0) { + resWidth = imageWidth; + resHeight = imageHeight; + } else if((height < 0) || (width >= 0 && imageHeight * width <= imageWidth * height)) { + resWidth = width; + resHeight = imageHeight * width / imageWidth; + } else { + resWidth = imageWidth * height / imageHeight; + resHeight = height; + } + + if(angle == 0.0 && imageWidth == resWidth && imageHeight == resHeight) + return image; + + return IMAGE_CACHE.get(new Key(resWidth, resHeight, angle)).get(image); + } +} diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java index dec3764f70..8df10872f4 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java @@ -8,6 +8,8 @@ import java.util.Timer; import java.util.TimerTask; public abstract class Animation { + private static boolean ENABLED = true; + private static final long TARGET_MILLIS_PER_FRAME = 30; private static Timer timer = new Timer("Animation", true); @@ -25,6 +27,18 @@ public abstract class Animation { } public Animation (final long duration, long delay) { + if(!ENABLED) { + UI.invokeLater(new Runnable() { + @Override + public void run () { + start(); + //update(1.0f); + end(); + } + }); + return; + } + timerTask = new TimerTask() { @Override public void run () { @@ -171,6 +185,12 @@ public abstract class Animation { @Override protected void end () { + if (!state) { + parent.toggleTransformed(); + } + state = true; + panel.transformAngle = 0; + parent.onEndAnimation(); parent.repaint(); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index bae5624725..b02d697b97 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -1,5 +1,7 @@ package org.mage.card.arcane; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; @@ -23,6 +25,7 @@ import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.StringTokenizer; import java.util.UUID; import javax.swing.BorderFactory; @@ -38,6 +41,7 @@ import mage.cards.action.TransferData; import mage.client.dialog.PreferencesDialog; import mage.client.plugins.adapters.MageActionCallback; import mage.client.plugins.impl.Plugins; +import mage.client.util.ImageCaches; import mage.client.util.ImageHelper; import mage.client.util.audio.AudioManager; import mage.components.ImagePanel; @@ -52,8 +56,7 @@ import mage.view.PermanentView; import mage.view.StackAbilityView; import net.java.truevfs.access.TFile; import org.apache.log4j.Logger; -import org.mage.card.arcane.ScaledImagePanel.MultipassType; -import org.mage.card.arcane.ScaledImagePanel.ScalingType; +import org.jdesktop.swingx.graphics.GraphicsUtilities; import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL; import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.images.ImageCache; @@ -160,6 +163,111 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti // if this is set, it's opened if the user right clicks on the card panel private JPopupMenu popupMenu; + private static Map IMAGE_CACHE; + + private final static class Key + { + final int width; + final int height; + final int cardWidth; + final int cardHeight; + final int cardXOffset; + final int cardYOffset; + final boolean hasImage; + final boolean isSelected; + final boolean isChoosable; + final boolean isPlayable; + final boolean canAttack; + + public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack) { + this.width = width; + this.height = height; + this.cardWidth = cardWidth; + this.cardHeight = cardHeight; + this.cardXOffset = cardXOffset; + this.cardYOffset = cardYOffset; + this.hasImage = hasImage; + this.isSelected = isSelected; + this.isChoosable = isChoosable; + this.isPlayable = isPlayable; + this.canAttack = canAttack; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 19 * hash + this.width; + hash = 19 * hash + this.height; + hash = 19 * hash + this.cardWidth; + hash = 19 * hash + this.cardHeight; + hash = 19 * hash + this.cardXOffset; + hash = 19 * hash + this.cardYOffset; + hash = 19 * hash + (this.hasImage ? 1 : 0); + hash = 19 * hash + (this.isSelected ? 1 : 0); + hash = 19 * hash + (this.isChoosable ? 1 : 0); + hash = 19 * hash + (this.isPlayable ? 1 : 0); + hash = 19 * hash + (this.canAttack ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.cardWidth != other.cardWidth) { + return false; + } + if (this.cardHeight != other.cardHeight) { + return false; + } + if (this.cardXOffset != other.cardXOffset) { + return false; + } + if (this.cardYOffset != other.cardYOffset) { + return false; + } + if (this.hasImage != other.hasImage) { + return false; + } + if (this.isSelected != other.isSelected) { + return false; + } + if (this.isChoosable != other.isChoosable) { + return false; + } + if (this.isPlayable != other.isPlayable) { + return false; + } + if (this.canAttack != other.canAttack) { + return false; + } + return true; + } + } + + static { + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } + public CardPanel(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) { this.gameCard = newGameCard; this.callback = callback; @@ -304,9 +412,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti imagePanel = new ScaledImagePanel(); imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); add(imagePanel); - imagePanel.setScaleLarger(true); - imagePanel.setScalingType(ScalingType.nearestNeighbor); - imagePanel.setScalingMultiPassType(MultipassType.none); String cardType = getType(newGameCard); tooltipText.setText(getText(cardType, newGameCard)); @@ -387,7 +492,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getName()); } - private void setImage(Image srcImage) { + private void setImage(BufferedImage srcImage) { synchronized (imagePanel) { imagePanel.setImage(srcImage); repaint(); @@ -413,10 +518,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti return zone; } - public void setScalingType(ScalingType scalingType) { - imagePanel.setScalingType(scalingType); - } - public void setDisplayEnabled(boolean displayEnabled) { this.displayEnabled = displayEnabled; } @@ -484,15 +585,28 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override protected void paintComponent(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + Graphics2D g2d = (Graphics2D)(g.create()); if (alpha != 1.0f) { AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha); g2d.setComposite(composite); } - if (!hasImage) { + g2d.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), cardWidth, cardHeight, cardXOffset, cardYOffset, hasImage, isSelected, isChoosable, isPlayable, canAttack)), 0, 0, null); + g2d.dispose(); + } + + private static BufferedImage createImage(Key key) { + int cardWidth = key.cardWidth; + int cardHeight = key.cardHeight; + int cardXOffset = key.cardXOffset; + int cardYOffset = key.cardYOffset; + + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(key.width, key.height); + Graphics2D g2d = image.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (!key.hasImage) { g2d.setColor(new Color(30, 200, 200, 120)); } else { g2d.setColor(new Color(0, 0, 0, 0)); @@ -501,19 +615,19 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE)); g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); - if (isSelected) { + if (key.isSelected) { g2d.setColor(Color.green); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); - } else if (isChoosable) { + } else if (key.isChoosable) { g2d.setColor(new Color(250, 250, 0, 230)); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); - } else if (isPlayable) { + } else if (key.isPlayable) { g2d.setColor(new Color(153, 102, 204, 200)); //g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); } - if (canAttack) { + if (key.canAttack) { g2d.setColor(new Color(0, 0, 255, 230)); g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); } @@ -524,6 +638,9 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti g2d.setColor(new Color(200,10,10,200)); g2d.fillRoundRect(cardXOffset+1, cardYOffset+1, cardWidth-2, cardHeight-2, cornerSize, cornerSize); }*/ + g2d.dispose(); + + return image; } @Override @@ -619,12 +736,6 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); } - - if (isAnimationPanel || cardWidth < 200) { - imagePanel.setScalingType(ScalingType.nearestNeighbor); - } else { - imagePanel.setScalingType(ScalingType.bilinear); - } } @Override @@ -1063,7 +1174,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti if (gameCard.hideInfo()) { return; } - if (getMousePosition(true) != null) { + if (this.contains(e.getPoint())) { return; } if (tooltipShowing) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java index c5e2f40c0d..5a32f6b872 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java @@ -1,15 +1,30 @@ package org.mage.card.arcane; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import javax.swing.*; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import mage.client.util.ImageCaches; +import org.jdesktop.swingx.graphics.GraphicsUtilities; public class GlowText extends JLabel { private static final long serialVersionUID = 1827677946939348001L; @@ -19,6 +34,109 @@ public class GlowText extends JLabel { private Color glowColor; private boolean wrap; private int lineCount = 0; + private static Map IMAGE_CACHE; + + private final static class Key + { + final int width; + final int height; + final String text; + final Map fontAttributes; + final Color color; + final int glowSize; + final float glowIntensity; + final Color glowColor; + final boolean wrap; + + // used to pass the native font to the create function so we don't waste performance recreating it, but without holding onto the native object + final transient WeakReference originalFont; + + Font getFont() { + Font res = this.originalFont.get(); + if(res == null) + res = Font.getFont(this.fontAttributes); + return res; + } + + public Key(int width, int height, String text, Font font, Color color, int glowSize, float glowIntensity, Color glowColor, boolean wrap) { + this.width = width; + this.height = height; + this.text = text; + this.originalFont = new WeakReference<>(font); + this.fontAttributes = font.getAttributes(); + this.color = color; + this.glowSize = glowSize; + this.glowIntensity = glowIntensity; + this.glowColor = glowColor; + this.wrap = wrap; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 23 * hash + this.width; + hash = 23 * hash + this.height; + hash = 23 * hash + Objects.hashCode(this.text); + hash = 23 * hash + Objects.hashCode(this.fontAttributes); + hash = 23 * hash + Objects.hashCode(this.color); + hash = 23 * hash + this.glowSize; + hash = 23 * hash + Float.floatToIntBits(this.glowIntensity); + hash = 23 * hash + Objects.hashCode(this.glowColor); + hash = 23 * hash + (this.wrap ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.glowSize != other.glowSize) { + return false; + } + if (Float.floatToIntBits(this.glowIntensity) != Float.floatToIntBits(other.glowIntensity)) { + return false; + } + if (this.wrap != other.wrap) { + return false; + } + if (!Objects.equals(this.text, other.text)) { + return false; + } + if (!Objects.equals(this.fontAttributes, other.fontAttributes)) { + return false; + } + if (!Objects.equals(this.color, other.color)) { + return false; + } + if (!Objects.equals(this.glowColor, other.glowColor)) { + return false; + } + return true; + } + } + + static { + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } public void setGlow (Color glowColor, int size, float intensity) { this.glowColor = glowColor; @@ -38,32 +156,32 @@ public class GlowText extends JLabel { return size; } - @Override - public void setText (String text) { - super.setText(text); - } - @Override public void paint (Graphics g) { if (getText().length() == 0) { return; } - Graphics2D g2d = (Graphics2D)g; + g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null); + } + + private static BufferedImage createImage (Key key) { + Dimension size = new Dimension(key.width, key.height); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(size.width, size.height); + Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - Dimension size = getSize(); int textX = 0, textY = 0; - int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE); + int wrapWidth = Math.max(0, key.wrap ? size.width - key.glowSize : Integer.MAX_VALUE); - AttributedString attributedString = new AttributedString(getText()); - attributedString.addAttribute(TextAttribute.FONT, getFont()); + AttributedString attributedString = new AttributedString(key.text); + attributedString.addAttribute(TextAttribute.FONT, key.getFont()); AttributedCharacterIterator charIterator = attributedString.getIterator(); FontRenderContext fontContext = g2d.getFontRenderContext(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext); - lineCount = 0; + int lineCount = 0; while (measurer.getPosition() < charIterator.getEndIndex()) { //TextLayout textLayout = measurer.nextLayout(wrapWidth); lineCount++; @@ -83,23 +201,21 @@ public class GlowText extends JLabel { float ascent = textLayout.getAscent(); textY += ascent; // Move down to baseline. - g2d.setColor(glowColor); + g2d.setColor(key.glowColor); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + int glowSize = key.glowSize; textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1); - g2d.setColor(getForeground()); + g2d.setColor(key.color); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2); textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line. } - } - - public int getLineCount() { - return this.lineCount; + return image; } public void setGlowColor(Color glowColor) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java index 8a2edd3edb..9ad1d47fec 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ScaledImagePanel.java @@ -7,188 +7,35 @@ import java.awt.RenderingHints; import java.awt.image.BufferedImage; import javax.swing.JPanel; +import mage.client.util.TransformedImageCache; public class ScaledImagePanel extends JPanel { private static final long serialVersionUID = -1523279873208605664L; - private volatile Image srcImage; - - private ScalingType scalingType = ScalingType.bilinear; - private boolean scaleLarger; - private MultipassType multiPassType = MultipassType.bilinear; + private volatile BufferedImage srcImage; public ScaledImagePanel () { super(false); setOpaque(false); } - public void setImage(Image srcImage) { + public void setImage(BufferedImage srcImage) { this.srcImage = srcImage; } - public void clearImage () { - srcImage = null; - repaint(); - } - - public void setScalingMultiPassType (MultipassType multiPassType) { - this.multiPassType = multiPassType; - } - - public void setScalingType (ScalingType scalingType) { - this.scalingType = scalingType; - } - - public void setScaleLarger (boolean scaleLarger) { - this.scaleLarger = scaleLarger; - } - public boolean hasImage () { return srcImage != null; } - private ScalingInfo getScalingInfo () { - int panelWidth = getWidth(); - int panelHeight = getHeight(); - int srcWidth = srcImage.getWidth(null); - int srcHeight = srcImage.getHeight(null); - int targetWidth = srcWidth; - int targetHeight = srcHeight; - if (scaleLarger || srcWidth > panelWidth || srcHeight > panelHeight) { - targetWidth = Math.round(panelHeight * (srcWidth / (float)srcHeight)); - if (targetWidth > panelWidth) { - targetHeight = Math.round(panelWidth * (srcHeight / (float)srcWidth)); - targetWidth = panelWidth; - } else { - targetHeight = panelHeight; - } - } - ScalingInfo info = new ScalingInfo(); - info.targetWidth = targetWidth; - info.targetHeight = targetHeight; - info.srcWidth = srcWidth; - info.srcHeight = srcHeight; - info.x = panelWidth / 2 - targetWidth / 2; - info.y = panelHeight / 2 - targetHeight / 2; - return info; - } - @Override public void paint (Graphics g) { if (srcImage == null) { return; } - Graphics2D g2 = (Graphics2D)g.create(); - ScalingInfo info = getScalingInfo(); - - switch (scalingType) { - case nearestNeighbor: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - break; - case bilinear: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - break; - case bicubic: - scaleWithDrawImage(g2, info, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - break; - case areaAveraging: - scaleWithGetScaledInstance(g2, info, Image.SCALE_AREA_AVERAGING); - break; - case replicate: - scaleWithGetScaledInstance(g2, info, Image.SCALE_REPLICATE); - break; - } + g.drawImage(TransformedImageCache.getResizedImage(srcImage, getWidth(), getHeight()), 0, 0, null); } - private void scaleWithGetScaledInstance (Graphics2D g2, ScalingInfo info, int hints) { - Image scaledImage = srcImage.getScaledInstance(info.targetWidth, info.targetHeight, hints); - g2.drawImage(scaledImage, info.x, info.y, null); - } - - private void scaleWithDrawImage (Graphics2D g2, ScalingInfo info, Object hint) { - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); - - int tempDestWidth = info.srcWidth / 2, tempDestHeight = info.srcHeight / 2; - if (tempDestWidth < info.targetWidth) { - tempDestWidth = info.targetWidth; - } - if (tempDestHeight < info.targetHeight) { - tempDestHeight = info.targetHeight; - } - - // If not doing multipass or multipass only needs a single pass, just scale it once directly to the panel surface. - if (multiPassType == MultipassType.none || (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight)) { - g2.drawImage(srcImage, info.x, info.y, info.targetWidth, info.targetHeight, null); - return; - } - - BufferedImage tempImage = new BufferedImage(tempDestWidth, tempDestHeight, BufferedImage.TYPE_INT_RGB); - Graphics2D g2temp = tempImage.createGraphics(); - switch (multiPassType) { - case nearestNeighbor: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); - break; - case bilinear: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - break; - case bicubic: - g2temp.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - break; - } - // Render first pass from image to temp. - g2temp.drawImage(srcImage, 0, 0, tempDestWidth, tempDestHeight, null); - // Render passes between the first and last pass. - int tempSrcWidth = tempDestWidth; - int tempSrcHeight = tempDestHeight; - while (true) { - if (tempDestWidth > info.targetWidth) { - tempDestWidth = tempDestWidth / 2; - if (tempDestWidth < info.targetWidth) { - tempDestWidth = info.targetWidth; - } - } - - if (tempDestHeight > info.targetHeight) { - tempDestHeight = tempDestHeight / 2; - if (tempDestHeight < info.targetHeight) { - tempDestHeight = info.targetHeight; - } - } - - if (tempDestWidth == info.targetWidth && tempDestHeight == info.targetHeight) { - break; - } - - g2temp.drawImage(tempImage, 0, 0, tempDestWidth, tempDestHeight, 0, 0, tempSrcWidth, tempSrcHeight, null); - - tempSrcWidth = tempDestWidth; - tempSrcHeight = tempDestHeight; - } - g2temp.dispose(); - // Render last pass from temp to panel surface. - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint); - g2.drawImage(tempImage, info.x, info.y, info.x + info.targetWidth, info.y + info.targetHeight, 0, 0, tempSrcWidth, - tempSrcHeight, null); - } - - public Image getSrcImage() { + public BufferedImage getSrcImage() { return srcImage; } - - private static class ScalingInfo { - public int targetWidth; - public int targetHeight; - public int srcWidth; - public int srcHeight; - public int x; - public int y; - } - - public static enum MultipassType { - none, nearestNeighbor, bilinear, bicubic - } - - public static enum ScalingType { - nearestNeighbor, replicate, bilinear, bicubic, areaAveraging - } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 9292cb51e4..b5fdc3aea0 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -1,5 +1,6 @@ package org.mage.plugins.card.images; +import mage.client.util.TransformedImageCache; import com.google.common.base.Function; import com.google.common.collect.ComputationException; import com.google.common.collect.MapMaker; @@ -274,7 +275,7 @@ public class ImageCache { } public static BufferedImage makeThumbnail(BufferedImage original, String path) { - BufferedImage image = getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL); + BufferedImage image = TransformedImageCache.getResizedImage(original, Constants.THUMBNAIL_SIZE_FULL.width, Constants.THUMBNAIL_SIZE_FULL.height); TFile imageFile = getTFile(path); if (imageFile == null) { return null; @@ -312,36 +313,7 @@ public class ImageCache { return original; } - ResampleOp resampleOp = new ResampleOp(tgtWidth, tgtHeight); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to the size appropriate for the card picture - * panel For future use. - */ - private static BufferedImage getFullSizeImage(BufferedImage original, double scale) { - if (scale == 1) { - return original; - } - ResampleOp resampleOp = new ResampleOp((int) (original.getWidth() * scale), (int) (original.getHeight() * scale)); - BufferedImage image = resampleOp.filter(original, null); - return image; - } - - /** - * Returns an image scaled to the size appropriate for the card picture - * panel - * - * @param original - * @param sizeNeed - * @return - */ - public static BufferedImage getResizedImage(BufferedImage original, Rectangle sizeNeed) { - ResampleOp resampleOp = new ResampleOp(sizeNeed.width, sizeNeed.height); - BufferedImage image = resampleOp.filter(original, null); - return image; + return TransformedImageCache.getResizedImage(original, tgtWidth, tgtHeight); } /** @@ -364,11 +336,11 @@ public class ImageCache { } double scale = Math.min((double) width / original.getWidth(), (double) height / original.getHeight()); - if (scale > 1) { - scale = 1; + if (scale >= 1) { + return original; } - return getFullSizeImage(original, scale); + return TransformedImageCache.getResizedImage(original, (int)(original.getWidth() * scale), (int)(original.getHeight() * scale)); } public static TFile getTFile(String path) { diff --git a/Mage.Common/src/mage/cards/TextPopup.java b/Mage.Common/src/mage/cards/TextPopup.java index e2f1517a59..e02e9b124c 100644 --- a/Mage.Common/src/mage/cards/TextPopup.java +++ b/Mage.Common/src/mage/cards/TextPopup.java @@ -39,6 +39,8 @@ package mage.cards; * @author BetaSteward_at_googlemail.com */ public class TextPopup extends javax.swing.JPanel { + private String text; + private boolean needsUpdate; /** Creates new form TextPopup */ public TextPopup() { @@ -46,7 +48,17 @@ public class TextPopup extends javax.swing.JPanel { } public void setText(String text) { - popupText.setText(text); + if(!text.equals(this.text)) { + this.text = text; + this.needsUpdate = true; + } + } + + public void updateText() { + if(this.needsUpdate) { + popupText.setText(this.text); + this.needsUpdate = false; + } } /** This method is called from within the constructor to