mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
Port Card Image Renderer to Desktop
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -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;
|
||||
* <li>Keys start with the file name, extension is skipped</li>
|
||||
* <li>The key without suffix belongs to the unmodified image from the file</li>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<DetailColors> 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<DetailColors> 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)", "<br>");
|
||||
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<String> 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user