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/deckeditor/table/TableModel.java b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java index baa4cf5f85..65c5ebd027 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/table/TableModel.java @@ -409,8 +409,7 @@ public class TableModel extends AbstractTableModel implements ICardGrid { Image image = Plugins.getInstance().getOriginalImage(card); if (image != null && image instanceof BufferedImage) { // XXX: scaled to fit width - image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth()); - bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, new ArrayList()); + bigCard.setCard(card.getId(), EnlargeMode.NORMAL, image, new ArrayList(), false); } else { drawCardText(card); } diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index 109e3c9e91..e55588b743 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -228,7 +228,7 @@ public class DraftPanel extends javax.swing.JPanel { int height = left * 18; lblTableImage.setSize(new Dimension(lblTableImage.getWidth(), height)); Image tableImage = ImageHelper.getImageFromResources(draftView.getBoosterNum() == 2 ? "/draft/table_left.png" : "/draft/table_right.png"); - BufferedImage resizedTable = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(tableImage, BufferedImage.TYPE_INT_ARGB), lblTableImage.getWidth()); + BufferedImage resizedTable = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(tableImage, BufferedImage.TYPE_INT_ARGB), lblTableImage.getWidth(), lblTableImage.getHeight()); lblTableImage.setIcon(new ImageIcon(resizedTable)); int count = 0; diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 8a0bee2366..4c18ca7fb2 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -598,13 +598,7 @@ public class MageActionCallback implements ActionCallback { private void displayCardInfo(MageCard mageCard, Image image, BigCard bigCard) { if (image != null && image instanceof BufferedImage) { // XXX: scaled to fit width - if (mageCard.getOriginal().isToRotate() && bigCard.getWidth() > bigCard.getHeight()) { - image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getHeight()); - image = ImageHelper.rotate((BufferedImage) image, Math.toRadians(90)); - } else { - image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth()); - } - bigCard.setCard(mageCard.getOriginal().getId(), enlargeMode, image, mageCard.getOriginal().getRules()); + bigCard.setCard(mageCard.getOriginal().getId(), enlargeMode, image, mageCard.getOriginal().getRules(), mageCard.getOriginal().isToRotate()); // if it's an ability, show only the ability text as overlay if (mageCard.getOriginal().isAbility() && enlargeMode.equals(EnlargeMode.NORMAL)) { bigCard.showTextComponent(); 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/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 86f7b6c5d0..3234bcc028 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) {