diff --git a/forge-gui-desktop/src/main/java/forge/ImageCache.java b/forge-gui-desktop/src/main/java/forge/ImageCache.java
index a1587ba2445..4c5c5182e33 100644
--- a/forge-gui-desktop/src/main/java/forge/ImageCache.java
+++ b/forge-gui-desktop/src/main/java/forge/ImageCache.java
@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* This program 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 for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
@@ -42,6 +42,7 @@ import forge.game.card.CardView;
import forge.game.player.PlayerView;
import forge.gui.FThreads;
import forge.item.InventoryItem;
+import forge.item.PaperCard;
import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgePreferences;
import forge.localinstance.properties.ForgePreferences.FPref;
@@ -49,6 +50,7 @@ import forge.localinstance.skin.FSkinProp;
import forge.model.FModel;
import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinIcon;
+import forge.toolbox.imaging.FCardImageRenderer;
import forge.util.ImageUtil;
/**
@@ -61,7 +63,7 @@ import forge.util.ImageUtil;
*
Keys start with the file name, extension is skipped
* The key without suffix belongs to the unmodified image from the file
*
- *
+ *
* @author Forge
* @version $Id: ImageCache.java 25093 2014-03-08 05:36:37Z drdev $
*/
@@ -81,7 +83,7 @@ public class ImageCache {
} catch (Exception ex) {
System.err.println("could not load default card image");
} finally {
- _defaultImage = (null == defImage) ? new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB) : defImage;
+ _defaultImage = (null == defImage) ? new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB) : defImage;
}
}
@@ -133,31 +135,33 @@ public class ImageCache {
}
return new FSkin.UnskinnedIcon(i);
}
-
+
/**
* This requests the original unscaled image from the cache for the given key.
* If the image does not exist then it can return a default image if desired.
*
* If the requested image is not present in the cache then it attempts to load
- * the image from file (slower) and then add it to the cache for fast future access.
+ * the image from file (slower) and then add it to the cache for fast future access.
*
*/
public static BufferedImage getOriginalImage(String imageKey, boolean useDefaultIfNotFound) {
- if (null == imageKey) {
+ if (null == imageKey) {
return null;
}
-
+
+ PaperCard pc = null;
boolean altState = imageKey.endsWith(ImageKeys.BACKFACE_POSTFIX);
if(altState)
imageKey = imageKey.substring(0, imageKey.length() - ImageKeys.BACKFACE_POSTFIX.length());
if (imageKey.startsWith(ImageKeys.CARD_PREFIX)) {
- imageKey = ImageUtil.getImageKey(ImageUtil.getPaperCardFromImageKey(imageKey), altState, true);
- if (StringUtils.isBlank(imageKey)) {
+ pc = ImageUtil.getPaperCardFromImageKey(imageKey);
+ imageKey = ImageUtil.getImageKey(pc, altState, true);
+ if (StringUtils.isBlank(imageKey)) {
return _defaultImage;
}
}
- // Load from file and add to cache if not found in cache initially.
+ // Load from file and add to cache if not found in cache initially.
BufferedImage original = getImage(imageKey);
// If the user has indicated that they prefer Forge NOT render a black border, round the image corners
@@ -197,8 +201,14 @@ public class ImageCache {
// No image file exists for the given key so optionally associate with
// a default "not available" image, however do not add it to the cache,
// as otherwise it's problematic to update if the real image gets fetched.
- if (original == null && useDefaultIfNotFound) {
- original = _defaultImage;
+ if (original == null && useDefaultIfNotFound) {
+ if (pc != null) {
+ original = new BufferedImage(480, 680, BufferedImage.TYPE_INT_ARGB);
+ FCardImageRenderer.drawCardImage(original.createGraphics(), pc, altState, 480, 680);
+ _CACHE.put(imageKey, original);
+ } else {
+ original = _defaultImage;
+ }
}
return original;
@@ -217,7 +227,7 @@ public class ImageCache {
//System.out.println("found cached image: " + resizedKey);
return cached;
}
-
+
BufferedImage original = getOriginalImage(key, useDefaultImage);
if (original == null) { return null; }
@@ -232,7 +242,7 @@ public class ImageCache {
return cachedDefault;
}
}
-
+
// Calculate the scale required to best fit the image into the requested
// (width x height) dimensions whilst retaining aspect ratio.
double scaleX = (-1 == width ? 1 : (double)width / original.getWidth());
@@ -243,17 +253,17 @@ public class ImageCache {
}
BufferedImage result;
- if (1 == bestFitScale) {
+ if (1 == bestFitScale) {
result = original;
} else {
-
+
int destWidth = (int)(original.getWidth() * bestFitScale);
int destHeight = (int)(original.getHeight() * bestFitScale);
-
+
ResampleOp resampler = new ResampleOp(destWidth, destHeight);
result = resampler.filter(original, null);
}
-
+
//System.out.println("caching image: " + resizedKey);
_CACHE.put(resizedKey, result);
return result;
diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ColorSetRenderer.java b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ColorSetRenderer.java
index db9576ebe98..6735e15f1ec 100644
--- a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ColorSetRenderer.java
+++ b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ColorSetRenderer.java
@@ -20,7 +20,7 @@ public class ColorSetRenderer extends ItemCellRenderer {
/*
* (non-Javadoc)
- *
+ *
* @see
* javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent
* (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
@@ -41,7 +41,7 @@ public class ColorSetRenderer extends ItemCellRenderer {
/*
* (non-Javadoc)
- *
+ *
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
@Override
@@ -58,7 +58,7 @@ public class ColorSetRenderer extends ItemCellRenderer {
final int dx = Math.min(elemtWidth + elemtGap, offsetIfNoSpace);
for (final ManaCostShard s : cs.getOrderedShards()) {
- CardFaceSymbols.drawSymbol(s.getImageKey(), g, x, y);
+ CardFaceSymbols.drawManaSymbol(s.getImageKey(), g, x, y);
x += dx;
}
}
diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ManaCostRenderer.java b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ManaCostRenderer.java
index b18787cebb2..30a3a3133c4 100644
--- a/forge-gui-desktop/src/main/java/forge/itemmanager/views/ManaCostRenderer.java
+++ b/forge-gui-desktop/src/main/java/forge/itemmanager/views/ManaCostRenderer.java
@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* This program 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 for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
@@ -44,7 +44,7 @@ public class ManaCostRenderer extends ItemCellRenderer {
/*
* (non-Javadoc)
- *
+ *
* @see
* javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent
* (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int)
@@ -68,7 +68,7 @@ public class ManaCostRenderer extends ItemCellRenderer {
/*
* (non-Javadoc)
- *
+ *
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
@Override
@@ -112,7 +112,7 @@ public class ManaCostRenderer extends ItemCellRenderer {
// Display X Mana before any other type of mana
if (xManaCosts > 0) {
for (int i = 0; i < xManaCosts; i++) {
- CardFaceSymbols.drawSymbol(ManaCostShard.X.getImageKey(), g, x, y);
+ CardFaceSymbols.drawManaSymbol(ManaCostShard.X.getImageKey(), g, x, y);
x += dx;
}
}
@@ -120,7 +120,7 @@ public class ManaCostRenderer extends ItemCellRenderer {
// Display generic mana before colored mana
if (hasGeneric) {
final String sGeneric = Integer.toString(genericManaCost);
- CardFaceSymbols.drawSymbol(sGeneric, g, x, y);
+ CardFaceSymbols.drawManaSymbol(sGeneric, g, x, y);
x += dx;
}
@@ -129,7 +129,7 @@ public class ManaCostRenderer extends ItemCellRenderer {
// X costs already drawn up above
continue;
}
- CardFaceSymbols.drawSymbol(s.getImageKey(), g, x, y);
+ CardFaceSymbols.drawManaSymbol(s.getImageKey(), g, x, y);
x += dx;
}
}
diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/CardFaceSymbols.java b/forge-gui-desktop/src/main/java/forge/toolbox/CardFaceSymbols.java
index 68ff467db99..ac7105b0a75 100644
--- a/forge-gui-desktop/src/main/java/forge/toolbox/CardFaceSymbols.java
+++ b/forge-gui-desktop/src/main/java/forge/toolbox/CardFaceSymbols.java
@@ -42,44 +42,44 @@ public class CardFaceSymbols {
DECK_COLORSET.put("W", FSkin.getImage(FSkinProp.IMG_MANA_W));
for (int i = 0; i <= 20; i++) {
- MANA_IMAGES.put(String.valueOf(i), FSkin.getImage(FSkinProp.valueOf("IMG_MANA_" + i), manaImageSize, manaImageSize));
+ MANA_IMAGES.put(String.valueOf(i), FSkin.getImage(FSkinProp.valueOf("IMG_MANA_" + i)));
}
- MANA_IMAGES.put("X", FSkin.getImage(FSkinProp.IMG_MANA_X, manaImageSize, manaImageSize));
- MANA_IMAGES.put("Y", FSkin.getImage(FSkinProp.IMG_MANA_Y, manaImageSize, manaImageSize));
- MANA_IMAGES.put("Z", FSkin.getImage(FSkinProp.IMG_MANA_Z, manaImageSize, manaImageSize));
- MANA_IMAGES.put("C", FSkin.getImage(FSkinProp.IMG_MANA_COLORLESS, manaImageSize, manaImageSize));
+ MANA_IMAGES.put("X", FSkin.getImage(FSkinProp.IMG_MANA_X));
+ MANA_IMAGES.put("Y", FSkin.getImage(FSkinProp.IMG_MANA_Y));
+ MANA_IMAGES.put("Z", FSkin.getImage(FSkinProp.IMG_MANA_Z));
+ MANA_IMAGES.put("C", FSkin.getImage(FSkinProp.IMG_MANA_COLORLESS));
- MANA_IMAGES.put("B", FSkin.getImage(FSkinProp.IMG_MANA_B, manaImageSize, manaImageSize));
- MANA_IMAGES.put("BG", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_BG, manaImageSize, manaImageSize));
- MANA_IMAGES.put("BR", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_BR, manaImageSize, manaImageSize));
- MANA_IMAGES.put("G", FSkin.getImage(FSkinProp.IMG_MANA_G, manaImageSize, manaImageSize));
- MANA_IMAGES.put("GU", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_GU, manaImageSize, manaImageSize));
- MANA_IMAGES.put("GW", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_GW, manaImageSize, manaImageSize));
- MANA_IMAGES.put("R", FSkin.getImage(FSkinProp.IMG_MANA_R, manaImageSize, manaImageSize));
- MANA_IMAGES.put("RG", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_RG, manaImageSize, manaImageSize));
- MANA_IMAGES.put("RW", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_RW, manaImageSize, manaImageSize));
- MANA_IMAGES.put("U", FSkin.getImage(FSkinProp.IMG_MANA_U, manaImageSize, manaImageSize));
- MANA_IMAGES.put("UB", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_UB, manaImageSize, manaImageSize));
- MANA_IMAGES.put("UR", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_UR, manaImageSize, manaImageSize));
- MANA_IMAGES.put("W", FSkin.getImage(FSkinProp.IMG_MANA_W, manaImageSize, manaImageSize));
- MANA_IMAGES.put("WB", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_WB, manaImageSize, manaImageSize));
- MANA_IMAGES.put("WU", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_WU, manaImageSize, manaImageSize));
- MANA_IMAGES.put("PW", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_W, manaImageSize, manaImageSize));
- MANA_IMAGES.put("PR", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_R, manaImageSize, manaImageSize));
- MANA_IMAGES.put("PU", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_U, manaImageSize, manaImageSize));
- MANA_IMAGES.put("PB", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_B, manaImageSize, manaImageSize));
- MANA_IMAGES.put("PG", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_G, manaImageSize, manaImageSize));
- MANA_IMAGES.put("2W", FSkin.getImage(FSkinProp.IMG_MANA_2W, manaImageSize, manaImageSize));
- MANA_IMAGES.put("2U", FSkin.getImage(FSkinProp.IMG_MANA_2U, manaImageSize, manaImageSize));
- MANA_IMAGES.put("2R", FSkin.getImage(FSkinProp.IMG_MANA_2R, manaImageSize, manaImageSize));
- MANA_IMAGES.put("2G", FSkin.getImage(FSkinProp.IMG_MANA_2G, manaImageSize, manaImageSize));
- MANA_IMAGES.put("2B", FSkin.getImage(FSkinProp.IMG_MANA_2B, manaImageSize, manaImageSize));
+ MANA_IMAGES.put("B", FSkin.getImage(FSkinProp.IMG_MANA_B));
+ MANA_IMAGES.put("BG", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_BG));
+ MANA_IMAGES.put("BR", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_BR));
+ MANA_IMAGES.put("G", FSkin.getImage(FSkinProp.IMG_MANA_G));
+ MANA_IMAGES.put("GU", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_GU));
+ MANA_IMAGES.put("GW", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_GW));
+ MANA_IMAGES.put("R", FSkin.getImage(FSkinProp.IMG_MANA_R));
+ MANA_IMAGES.put("RG", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_RG));
+ MANA_IMAGES.put("RW", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_RW));
+ MANA_IMAGES.put("U", FSkin.getImage(FSkinProp.IMG_MANA_U));
+ MANA_IMAGES.put("UB", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_UB));
+ MANA_IMAGES.put("UR", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_UR));
+ MANA_IMAGES.put("W", FSkin.getImage(FSkinProp.IMG_MANA_W));
+ MANA_IMAGES.put("WB", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_WB));
+ MANA_IMAGES.put("WU", FSkin.getImage(FSkinProp.IMG_MANA_HYBRID_WU));
+ MANA_IMAGES.put("PW", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_W));
+ MANA_IMAGES.put("PR", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_R));
+ MANA_IMAGES.put("PU", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_U));
+ MANA_IMAGES.put("PB", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_B));
+ MANA_IMAGES.put("PG", FSkin.getImage(FSkinProp.IMG_MANA_PHRYX_G));
+ MANA_IMAGES.put("2W", FSkin.getImage(FSkinProp.IMG_MANA_2W));
+ MANA_IMAGES.put("2U", FSkin.getImage(FSkinProp.IMG_MANA_2U));
+ MANA_IMAGES.put("2R", FSkin.getImage(FSkinProp.IMG_MANA_2R));
+ MANA_IMAGES.put("2G", FSkin.getImage(FSkinProp.IMG_MANA_2G));
+ MANA_IMAGES.put("2B", FSkin.getImage(FSkinProp.IMG_MANA_2B));
- MANA_IMAGES.put("S", FSkin.getImage(FSkinProp.IMG_MANA_SNOW, manaImageSize, manaImageSize));
- MANA_IMAGES.put("T", FSkin.getImage(FSkinProp.IMG_TAP, manaImageSize, manaImageSize));
+ MANA_IMAGES.put("S", FSkin.getImage(FSkinProp.IMG_MANA_SNOW));
+ MANA_IMAGES.put("T", FSkin.getImage(FSkinProp.IMG_TAP));
MANA_IMAGES.put("E", FSkin.getImage(FSkinProp.IMG_ENERGY, 40, 40));
MANA_IMAGES.put("EXPERIENCE", FSkin.getImage(FSkinProp.IMG_EXPERIENCE, 40, 30));
- MANA_IMAGES.put("slash", FSkin.getImage(FSkinProp.IMG_SLASH, manaImageSize, manaImageSize));
+ MANA_IMAGES.put("slash", FSkin.getImage(FSkinProp.IMG_SLASH));
MANA_IMAGES.put("attack", FSkin.getImage(FSkinProp.IMG_ATTACK, 32, 32));
MANA_IMAGES.put("defend", FSkin.getImage(FSkinProp.IMG_DEFEND, 32, 32));
MANA_IMAGES.put("summonsick", FSkin.getImage(FSkinProp.IMG_SUMMONSICK, 32, 32));
@@ -179,37 +179,40 @@ public class CardFaceSymbols {
* a int.
*/
public static void draw(final Graphics g, final ManaCost manaCost, final int x, final int y) {
+ draw(g, manaCost, x, y, manaImageSize);
+ }
+ public static void draw(final Graphics g, final ManaCost manaCost, final int x, final int y, final int size) {
if (manaCost.isNoCost()) {
return;
}
int xpos = x;
- final int offset = 14;
+ final int offset = size + 1;
final int genericManaCost = manaCost.getGenericCost();
final boolean hasGeneric = (genericManaCost > 0) || manaCost.isPureGeneric();
if (hasGeneric) {
for (final ManaCostShard s : manaCost) { //render X shards before generic
if (s == ManaCostShard.X) {
- CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y);
+ CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y, size);
xpos += offset;
}
}
final String sGeneric = Integer.toString(genericManaCost);
- CardFaceSymbols.drawSymbol(sGeneric, g, xpos, y);
+ CardFaceSymbols.drawSymbol(sGeneric, g, xpos, y, size);
xpos += offset;
for (final ManaCostShard s : manaCost) { //render non-X shards after generic
if (s != ManaCostShard.X) {
- CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y);
+ CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y, size);
xpos += offset;
}
}
}
else { //if no generic, just render shards in order
for (final ManaCostShard s : manaCost) {
- CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y);
+ CardFaceSymbols.drawSymbol(s.getImageKey(), g, xpos, y, size);
xpos += offset;
}
}
@@ -270,6 +273,12 @@ public class CardFaceSymbols {
public static void drawSymbol(final String imageName, final Graphics g, final int x, final int y) {
FSkin.drawImage(g, MANA_IMAGES.get(imageName), x, y);
}
+ public static void drawManaSymbol(final String imageName, final Graphics g, final int x, final int y) {
+ FSkin.drawImage(g, MANA_IMAGES.get(imageName).resize(manaImageSize, manaImageSize), x, y);
+ }
+ public static void drawSymbol(final String imageName, final Graphics g, final int x, final int y, final int size) {
+ FSkin.drawImage(g, MANA_IMAGES.get(imageName).resize(size, size), x, y);
+ }
public static void drawAbilitySymbol(final String imageName, final Graphics g, final int x, final int y, final int w, final int h) {
FSkin.drawImage(g, MANA_IMAGES.get(imageName), x, y, w, h);
}
@@ -284,7 +293,7 @@ public class CardFaceSymbols {
* @return a int.
*/
public static int getWidth(final ManaCost manaCost) {
- return manaCost.getGlyphCount() * 14;
+ return manaCost.getGlyphCount() * (manaImageSize + 1);
}
public static int getHeight() {
diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java b/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java
new file mode 100644
index 00000000000..d1527bee3f5
--- /dev/null
+++ b/forge-gui-desktop/src/main/java/forge/toolbox/imaging/FCardImageRenderer.java
@@ -0,0 +1,401 @@
+package forge.toolbox.imaging;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JEditorPane;
+
+import org.apache.commons.lang3.StringUtils;
+
+import forge.card.CardRarity;
+import forge.card.mana.ManaCost;
+import forge.game.card.Card;
+import forge.game.card.CardView;
+import forge.game.card.CardView.CardStateView;
+import forge.gui.card.CardDetailUtil;
+import forge.gui.card.CardDetailUtil.DetailColors;
+import forge.item.PaperCard;
+import forge.localinstance.skin.FSkinProp;
+import forge.toolbox.CardFaceSymbols;
+import forge.toolbox.FSkin;
+import forge.toolbox.FSkin.SkinIcon;
+import forge.util.CardTranslation;
+
+public class FCardImageRenderer {
+ private static final float BASE_IMAGE_WIDTH = 480;
+ private static final float BASE_IMAGE_HEIGHT = 680;
+ private static float MANA_SYMBOL_SIZE, PT_BOX_WIDTH, HEADER_PADDING, BORDER_THICKNESS;
+ private static Font NAME_FONT, TYPE_FONT, TEXT_FONT, PT_FONT;
+ private static FontMetrics NAME_METRICS, TYPE_METRICS, TEXT_METRICS, PT_METRICS;
+ private static float prevImageWidth, prevImageHeight;
+ private static final float BLACK_BORDER_THICKNESS_RATIO = 0.021f;
+ private static final float NAME_BOX_TINT = 0.2f;
+ private static final float TEXT_BOX_TINT = 0.1f;
+ private static final float PT_BOX_TINT = 0.2f;
+ private static final float CARD_ART_RATIO = 1.32f;
+
+ private static Color tintColor(Color source, Color tint, float alpha) {
+ float r = (tint.getRed() - source.getRed()) * alpha + source.getRed();
+ float g = (tint.getGreen() - source.getGreen()) * alpha + source.getGreen();
+ float b = (tint.getBlue() - source.getBlue()) * alpha + source.getBlue();
+ return new Color(r / 255f, g / 255f, b / 255f, 1f);
+ }
+
+ private static Color[] tintColors(Color source, Color[] tints, float alpha) {
+ Color[] tintedColors = new Color[tints.length];
+ for (int i = 0; i < tints.length; i++) {
+ tintedColors[i] = tintColor(source, tints[i], alpha);
+ }
+ return tintedColors;
+ }
+
+ private static Color fromDetailColor(DetailColors detailColor) {
+ return new Color(detailColor.r, detailColor.g, detailColor.b);
+ }
+ private static Color C_COMMON = fromDetailColor(DetailColors.COMMON);
+ private static Color C_UNCOMMON = fromDetailColor(DetailColors.UNCOMMON);
+ private static Color C_RARE = fromDetailColor(DetailColors.RARE);
+ private static Color C_MYTHIC = fromDetailColor(DetailColors.MYTHIC);
+ private static Color C_SPECIAL = fromDetailColor(DetailColors.SPECIAL);
+ private static Color getRarityColor(CardRarity rarity) {
+ if (rarity == null)// NPE from Rarity weird...
+ return Color.MAGENTA;
+ switch(rarity) {
+ case Uncommon:
+ return C_UNCOMMON;
+ case Rare:
+ return C_RARE;
+ case MythicRare:
+ return C_MYTHIC;
+ case Special: //"Timeshifted" or other Special Rarity Cards
+ return C_SPECIAL;
+ default: //case BasicLand: + case Common:
+ return C_COMMON;
+ }
+ }
+
+ private static void updateStaticFields(Graphics2D g, float w, float h) {
+ if (w == prevImageWidth && h == prevImageHeight) {
+ //for performance sake, only update static fields if card image size is different than previous rendered card
+ return;
+ }
+
+ float ratio = Math.min(w / BASE_IMAGE_WIDTH, h / BASE_IMAGE_HEIGHT);
+
+ MANA_SYMBOL_SIZE = 26 * ratio;
+ PT_BOX_WIDTH = 75 * ratio;
+ HEADER_PADDING = 7 * ratio;
+ NAME_FONT = new Font(Font.SERIF, Font.BOLD, Math.round(MANA_SYMBOL_SIZE));
+ TYPE_FONT = new Font(Font.SERIF, Font.BOLD, Math.round(MANA_SYMBOL_SIZE * 0.8f));
+ TEXT_FONT = new Font(Font.SERIF, Font.PLAIN, Math.round(MANA_SYMBOL_SIZE * 0.7f));
+ PT_FONT = NAME_FONT;
+ NAME_METRICS = g.getFontMetrics(NAME_FONT);
+ TYPE_METRICS = g.getFontMetrics(TYPE_FONT);
+ TEXT_METRICS = g.getFontMetrics(TEXT_FONT);
+ PT_METRICS = NAME_METRICS;
+ BORDER_THICKNESS = Math.max(2 * ratio, 1f); //don't let border go below 1
+
+ prevImageWidth = w;
+ prevImageHeight = h;
+ }
+
+ public static void drawCardImage(Graphics2D g, PaperCard pc, boolean altState, int width, int height) {
+ final CardView card = Card.getCardForUi(pc).getView();
+ float x = 0, y = 0, w = width, h = height;
+ updateStaticFields(g, w, h);
+ g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+ float blackBorderThickness = w * BLACK_BORDER_THICKNESS_RATIO;
+ g.setColor(Color.BLACK);
+ g.fillRect((int)x, (int)y, (int)w, (int)h);
+ x += blackBorderThickness;
+ y += blackBorderThickness;
+ w -= 2 * blackBorderThickness;
+ h -= 2 * blackBorderThickness;
+
+ final CardStateView state = card.getState(altState);
+ //determine colors for borders
+ final List borderColors = CardDetailUtil.getBorderColors(state, true);
+ Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
+
+ float artInset = blackBorderThickness * 0.8f;
+ float outerBorderThickness = 2 * blackBorderThickness - artInset;
+ x += outerBorderThickness;
+ y += outerBorderThickness;
+ w -= 2 * outerBorderThickness;
+ float headerHeight = MANA_SYMBOL_SIZE + 2 * HEADER_PADDING + 2;
+
+ float artWidth = w - 2 * artInset;
+ float artHeight = artWidth / CARD_ART_RATIO;
+ float typeBoxHeight = TYPE_METRICS.getHeight() + HEADER_PADDING + 2;
+ float ptBoxHeight = 0;
+ float textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - outerBorderThickness - artInset - PT_METRICS.getHeight() / 2f;
+ if (state.isCreature() || state.isPlaneswalker() || state.getType().hasSubtype("Vehicle")) {
+ //if P/T box needed, make room for it
+ ptBoxHeight = MANA_SYMBOL_SIZE + HEADER_PADDING;
+ }
+
+ float artY = y + headerHeight;
+ float typeY = artY + artHeight;
+ float textY = typeY + typeBoxHeight;
+ float ptY = textY + textBoxHeight;
+
+ //draw art box with Forge icon
+ Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
+ drawArt(g, artBoxColors, x + artInset, artY, artWidth, artHeight);
+
+ //draw text box
+ Color[] textBoxColors = tintColors(Color.WHITE, colors, TEXT_BOX_TINT);
+ drawTextBox(g, card, state, textBoxColors, x + artInset, textY, w - 2 * artInset, textBoxHeight);
+
+ //draw header containing name and mana cost
+ Color[] headerColors = tintColors(Color.WHITE, colors, NAME_BOX_TINT);
+ drawHeader(g, card, state, headerColors, x, y, w, headerHeight);
+
+ //draw type line
+ drawTypeLine(g, card, state, headerColors, x, typeY, w, typeBoxHeight);
+
+ //draw P/T box
+ if (ptBoxHeight > 0) {
+ //only needed if on top since otherwise P/T will be hidden
+ Color[] ptColors = tintColors(Color.WHITE, colors, PT_BOX_TINT);
+ drawPtBox(g, card, state, ptColors, x, ptY - ptBoxHeight / 2f, w, ptBoxHeight);
+ }
+ }
+
+ private static Color[] fillColorBackground(Graphics2D g, List backColors, float x, float y, float w, float h) {
+ Color[] colors = new Color[backColors.size()];
+ for (int i = 0; i < colors.length; i++) {
+ DetailColors dc = backColors.get(i);
+ colors[i] = new Color(dc.r, dc.g, dc.b);
+ }
+ fillColorBackground(g, colors, x, y, w, h);
+ return colors;
+ }
+ private static void fillColorBackground(Graphics2D g, Color[] colors, float x, float y, float w, float h) {
+ Paint oldPaint = g.getPaint();
+ switch (colors.length) {
+ case 1:
+ g.setColor(colors[0]);
+ g.fillRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+ break;
+ case 2:
+ GradientPaint gradient = new GradientPaint(x, y, colors[0], x + w, y, colors[1]);
+ g.setPaint(gradient);
+ g.fillRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+ break;
+ case 3:
+ float halfWidth = w / 2;
+ GradientPaint gradient1 = new GradientPaint(x, y, colors[0], x + halfWidth, y, colors[1]);
+ g.setPaint(gradient1);
+ g.fillRect(Math.round(x), Math.round(y), Math.round(halfWidth), Math.round(h));
+ GradientPaint gradient2 = new GradientPaint(x + halfWidth, y, colors[1], x + w, y, colors[2]);
+ g.setPaint(gradient2);
+ g.fillRect(Math.round(x + halfWidth), Math.round(y), Math.round(halfWidth), Math.round(h));
+ break;
+ }
+ g.setPaint(oldPaint);
+ }
+
+ private static void drawVerticallyCenteredString(Graphics2D g, String text, Rectangle area, Font font, final FontMetrics fontMetrics) {
+ Font oldFont = g.getFont();
+
+ int x = area.x;
+ int y = area.y + (area.height - fontMetrics.getHeight()) / 2 + fontMetrics.getAscent();
+
+ g.setFont(font);
+ g.drawString(text, x, y);
+ g.setFont(oldFont);
+
+ }
+
+ private static void drawHeader(Graphics2D g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
+ fillColorBackground(g, colors, x, y, w, h);
+ g.setStroke(new BasicStroke(BORDER_THICKNESS));
+ g.setColor(Color.BLACK);
+ g.drawRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+
+ float padding = h / 8;
+
+ //draw mana cost for card
+ float manaCostWidth = 0;
+ ManaCost mainManaCost = state.getManaCost();
+ if (card.isSplitCard() && card.getAlternateState() != null) {
+ //handle rendering both parts of split card
+ mainManaCost = card.getLeftSplitState().getManaCost();
+ ManaCost otherManaCost = card.getAlternateState().getManaCost();
+ manaCostWidth = otherManaCost.getGlyphCount() * MANA_SYMBOL_SIZE + HEADER_PADDING;
+ CardFaceSymbols.draw(g, otherManaCost, Math.round(x + w - manaCostWidth), Math.round(y + (h - MANA_SYMBOL_SIZE) / 2), Math.round(MANA_SYMBOL_SIZE));
+ //draw "//" between two parts of mana cost
+ manaCostWidth += NAME_METRICS.stringWidth("//") + HEADER_PADDING;
+ drawVerticallyCenteredString(g, "//",
+ new Rectangle(Math.round(x + w - manaCostWidth), Math.round(y), Math.round(w), Math.round(h)),
+ NAME_FONT, NAME_METRICS);
+ }
+ manaCostWidth += mainManaCost.getGlyphCount() * MANA_SYMBOL_SIZE + HEADER_PADDING;
+ CardFaceSymbols.draw(g, mainManaCost, Math.round(x + w - manaCostWidth), Math.round(y + (h - MANA_SYMBOL_SIZE) / 2), Math.round(MANA_SYMBOL_SIZE));
+
+ //draw name for card
+ x += padding;
+ w -= 2 * padding;
+ drawVerticallyCenteredString(g, CardTranslation.getTranslatedName(state.getName()),
+ new Rectangle(Math.round(x), Math.round(y), Math.round(w - manaCostWidth - padding), Math.round(h)),
+ NAME_FONT, NAME_METRICS);
+ }
+
+
+ private static void drawArt(Graphics2D g, Color[] colors, float x, float y, float w, float h) {
+ fillColorBackground(g, colors, x, y, w, h);
+ SkinIcon art = FSkin.getIcon(FSkinProp.ICO_LOGO);
+ float artWidth = (float)art.getSizeForPaint(g).getWidth();
+ float artHeight = (float)art.getSizeForPaint(g).getHeight();
+ if (artWidth / artHeight >= w / h) {
+ float newH = w * (artHeight / artWidth);
+ FSkin.drawImage(g, art, Math.round(x), Math.round(y + (h - newH) / 2), Math.round(w), Math.round(newH));
+ } else {
+ float newW = h * (artWidth / artHeight);
+ FSkin.drawImage(g, art, Math.round(x + (w - newW) / 2), Math.round(y), Math.round(newW), Math.round(h));
+ }
+ g.setStroke(new BasicStroke(BORDER_THICKNESS));
+ g.setColor(Color.BLACK);
+ g.drawRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+ }
+
+ private static void drawTypeLine(Graphics2D g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
+ fillColorBackground(g, colors, x, y, w, h);
+ g.setStroke(new BasicStroke(BORDER_THICKNESS));
+ g.setColor(Color.BLACK);
+ g.drawRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+
+ float padding = h / 8;
+
+ //draw square icon for rarity
+ float iconSize = h * 0.55f;
+ float iconPadding = (h - iconSize) / 2;
+ w -= iconSize + iconPadding * 2;
+ g.setColor(getRarityColor(state.getRarity()));
+ g.fillRect(Math.round(x + w + iconPadding), Math.round(y + (h - iconSize) / 2), Math.round(iconSize), Math.round(iconSize));
+
+ //draw type
+ x += padding;
+ g.setColor(Color.BLACK);
+ drawVerticallyCenteredString(g, CardDetailUtil.formatCardType(state, true),
+ new Rectangle(Math.round(x), Math.round(y), Math.round(w), Math.round(h)),
+ TYPE_FONT, TYPE_METRICS);
+ }
+
+ private static void drawTextBox(Graphics2D g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
+ fillColorBackground(g, colors, x, y, w, h);
+ g.setStroke(new BasicStroke(BORDER_THICKNESS));
+ g.setColor(Color.BLACK);
+ g.drawRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+
+ if (state.isBasicLand()) {
+ //draw icons for basic lands
+ String imageKey;
+ switch (state.getName().replaceFirst("^Snow-Covered ", "")) {
+ case "Plains":
+ imageKey = "W";
+ break;
+ case "Island":
+ imageKey = "U";
+ break;
+ case "Swamp":
+ imageKey = "B";
+ break;
+ case "Mountain":
+ imageKey = "R";
+ break;
+ case "Forest":
+ imageKey = "G";
+ break;
+ default:
+ imageKey = "C";
+ break;
+ }
+ int iconSize = Math.round(h * 0.75f);
+ CardFaceSymbols.drawSymbol(imageKey, g, Math.round(x + (w - iconSize) / 2), Math.round(y + (h - iconSize) / 2), iconSize);
+ } else {
+ boolean needTranslation = true;
+ if (card.isToken()) {
+ if (card.getCloneOrigin() == null)
+ needTranslation = false;
+ }
+ final String text = !card.isSplitCard() ?
+ card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null) :
+ card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(card.getLeftSplitState().getName(), card.getRightSplitState().getName()) : null );
+ if (StringUtils.isEmpty(text))
+ return;
+
+ float padding = TEXT_METRICS.getAscent() * 0.25f;
+ x += padding;
+ w -= 2 * padding;
+ String textShow = text.replaceAll("(\r\n\r\n)|(\n)", "
");
+ textShow = FSkin.encodeSymbols(textShow, true);
+ JEditorPane textArea = new JEditorPane("text/html", textShow);
+ textArea.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, Boolean.TRUE);
+ textArea.setOpaque(false);
+ textArea.setFont(TEXT_FONT);
+ textArea.setSize(Math.round(w), Math.round(h));
+ textArea.print(g.create(Math.round(x), Math.round(y), Math.round(w), Math.round(h)));
+ }
+ }
+
+ private static void drawPtBox(Graphics2D g, CardView card, CardStateView state, Color[] colors, float x, float y, float w, float h) {
+ List pieces = new ArrayList<>();
+ if (state.isCreature()) {
+ pieces.add(String.valueOf(state.getPower()));
+ pieces.add("/");
+ pieces.add(String.valueOf(state.getToughness()));
+ }
+ else if (state.isPlaneswalker()) {
+ pieces.add(String.valueOf(state.getLoyalty()));
+ }
+ else if (state.getType().hasSubtype("Vehicle")) {
+ pieces.add("[");
+ pieces.add(String.valueOf(state.getPower()));
+ pieces.add("/");
+ pieces.add(String.valueOf(state.getToughness()));
+ pieces.add("]");
+ }
+ else { return; }
+
+ float padding = PT_METRICS.getAscent() / 4f;
+ float totalPieceWidth = -padding;
+ float[] pieceWidths = new float[pieces.size()];
+ for (int i = 0; i < pieces.size(); i++) {
+ float pieceWidth = PT_METRICS.stringWidth(pieces.get(i)) + padding;
+ pieceWidths[i] = pieceWidth;
+ totalPieceWidth += pieceWidth;
+ }
+ float boxHeight = PT_METRICS.getMaxAscent() + padding;
+
+ float boxWidth = Math.max(PT_BOX_WIDTH, totalPieceWidth + 2 * padding);
+ x += w - boxWidth;
+ y += h - boxHeight;
+ w = boxWidth;
+ h = boxHeight;
+
+ fillColorBackground(g, colors, x, y, w, h);
+ g.setStroke(new BasicStroke(BORDER_THICKNESS));
+ g.setColor(Color.BLACK);
+ g.drawRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
+
+ x += (boxWidth - totalPieceWidth) / 2;
+ for (int i = 0; i < pieces.size(); i++) {
+ drawVerticallyCenteredString(g, pieces.get(i),
+ new Rectangle(Math.round(x), Math.round(y), Math.round(w), Math.round(h)),
+ PT_FONT, PT_METRICS);
+ x += pieceWidths[i];
+ }
+ }
+}