Merge remote-tracking branch 'upstream/master' into collector-number-in-card-list-and-card-db-refactoring

This commit is contained in:
leriomaggio
2021-08-04 08:51:56 +01:00
610 changed files with 49923 additions and 49745 deletions

View File

@@ -24,6 +24,7 @@ import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -32,12 +33,14 @@ import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
import com.google.common.cache.LoadingCache;
import com.mortennobel.imagescaling.ResampleOp;
import forge.card.CardSplitType;
import forge.game.card.Card;
import forge.game.card.CardView;
import forge.game.player.PlayerView;
@@ -54,6 +57,7 @@ import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinIcon;
import forge.toolbox.imaging.FCardImageRenderer;
import forge.util.ImageUtil;
import forge.util.TextUtil;
/**
* This class stores ALL card images in a cache with soft values. this means
@@ -150,8 +154,13 @@ public class ImageCache {
*
*/
public static BufferedImage getOriginalImage(String imageKey, boolean useDefaultIfNotFound, CardView cardView) {
return getOriginalImageInternal(imageKey, useDefaultIfNotFound, cardView).getLeft();
}
// return the pair of image and a flag to indicate if it is a placeholder image.
private static Pair<BufferedImage, Boolean> getOriginalImageInternal(String imageKey, boolean useDefaultIfNotFound, CardView cardView) {
if (null == imageKey) {
return null;
return Pair.of(null, false);
}
IPaperCard ipc = null;
@@ -163,20 +172,44 @@ public class ImageCache {
ipc = pc;
imageKey = ImageUtil.getImageKey(pc, altState, true);
if (StringUtils.isBlank(imageKey)) {
return _defaultImage;
return Pair.of(_defaultImage, true);
}
} else if (imageKey.startsWith(ImageKeys.TOKEN_PREFIX) && cardView != null && cardView.isToken()) {
ipc = ImageUtil.getPaperTokenFromImageKey(imageKey);
}
// Replace .full to .artcrop if art crop is preferred
// Only allow use art if the artist info is available
boolean useArtCrop = "Crop".equals(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_CARD_ART_FORMAT))
&& ipc != null && !ipc.getArtist().isEmpty();
String originalKey = imageKey;
if (useArtCrop) {
if (ipc != null && ipc.getRules().getSplitType() == CardSplitType.Flip) {
// Art crop will always use front face as image key for flip cards
imageKey = ImageUtil.getImageKey((PaperCard) ipc, false, true);
}
imageKey = TextUtil.fastReplace(imageKey, ".full", ".artcrop");
}
// Load from file and add to cache if not found in cache initially.
BufferedImage original = getImage(imageKey);
if (original == null && !useDefaultIfNotFound) {
return Pair.of(null, false);
}
// if art crop is exist, check also if the full card image is also cached.
if (useArtCrop && original != null) {
BufferedImage cached = _CACHE.getIfPresent(originalKey);
if (cached != null)
return Pair.of(cached, false);
}
boolean noBorder = !useArtCrop && !isPreferenceEnabled(ForgePreferences.FPref.UI_RENDER_BLACK_BORDERS);
boolean fetcherEnabled = isPreferenceEnabled(ForgePreferences.FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER);
boolean isPlaceholder = (original == null) && fetcherEnabled;
// If the user has indicated that they prefer Forge NOT render a black border, round the image corners
// to account for JPEG images that don't have a transparency.
boolean noBorder = !isPreferenceEnabled(ForgePreferences.FPref.UI_RENDER_BLACK_BORDERS);
if (original != null && noBorder) {
// use a quadratic equation to calculate the needed radius from an image dimension
int radius;
float width = original.getWidth();
@@ -209,19 +242,28 @@ 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) {
if (original == null || useArtCrop) {
if (ipc != null || cardView != null) {
int width = 488, height = 680;
BufferedImage art = original;
CardView card = ipc != null ? Card.getCardForUi(ipc).getView() : cardView;
original = new BufferedImage(480, 680, BufferedImage.TYPE_INT_ARGB);
FCardImageRenderer.drawCardImage(original.createGraphics(), card, altState, 480, 680);
if (!isPreferenceEnabled(ForgePreferences.FPref.UI_ENABLE_ONLINE_IMAGE_FETCHER))
_CACHE.put(imageKey, original);
String legalString = null;
original = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
if (art != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(StaticData.instance().getCardEdition(ipc.getEdition()).getDate());
int year = cal.get(Calendar.YEAR);
legalString = "Illus. " + ipc.getArtist() + " ©" + year + " WOTC";
}
FCardImageRenderer.drawCardImage(original.createGraphics(), card, altState, width, height, art, legalString);
if (art != null || !fetcherEnabled)
_CACHE.put(originalKey, original);
} else {
original = _defaultImage;
}
}
return original;
return Pair.of(original, isPlaceholder);
}
// cardView is for Emblem, since there is no paper card for them
@@ -238,7 +280,9 @@ public class ImageCache {
return cached;
}
BufferedImage original = getOriginalImage(key, useDefaultImage, cardView);
Pair<BufferedImage, Boolean> orgImgs = getOriginalImageInternal(key, useDefaultImage, cardView);
BufferedImage original = orgImgs.getLeft();
boolean isPlaceholder = orgImgs.getRight();
if (original == null) { return null; }
if (original == _defaultImage) {
@@ -274,7 +318,9 @@ public class ImageCache {
result = resampler.filter(original, null);
}
_CACHE.put(resizedKey, result);
if (!isPlaceholder) {
_CACHE.put(resizedKey, result);
}
return result;
}
/**

View File

@@ -541,7 +541,8 @@ public abstract class ACEditorBase<TItem extends InventoryItem, TModel extends D
existingCard.getEdition(),
existingCard.getRarity(),
existingCard.getArtIndex(),
true);
true,
existingCard.getArtist());
// remove *quantity* instances of existing card
CDeckEditorUI.SINGLETON_INSTANCE.removeSelectedCards(false, quantity);
// add *quantity* into the deck and set them as selected

View File

@@ -256,6 +256,7 @@ public enum CSubmenuPreferences implements ICDoc {
initializeGameLogVerbosityComboBox();
initializeCloseActionComboBox();
initializeDefaultFontSizeComboBox();
initializeCardArtFormatComboBox();
initializeCardArtPreference();
initializeAutoUpdaterComboBox();
initializeMulliganRuleComboBox();
@@ -447,6 +448,15 @@ public enum CSubmenuPreferences implements ICDoc {
panel.setComboBox(comboBox, selectedItem);
}
private void initializeCardArtFormatComboBox() {
final String [] choices = {"Full", "Crop"};
final FPref userSetting = FPref.UI_CARD_ART_FORMAT;
final FComboBoxPanel<String> panel = this.view.getCbpCardArtFormatComboBoxPanel();
final FComboBox<String> comboBox = createComboBox(choices, userSetting);
final String selectedItem = this.prefs.getPref(userSetting);
panel.setComboBox(comboBox, selectedItem);
}
private void initializeAiProfilesComboBox() {
final FPref userSetting = FPref.UI_CURRENT_AI_PROFILE;
final FComboBoxPanel<String> panel = this.view.getAiProfilesComboBoxPanel();

View File

@@ -124,6 +124,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final FComboBoxPanel<GameLogEntryType> cbpGameLogEntryType = new FComboBoxPanel<>(localizer.getMessage("cbpGameLogEntryType")+":");
private final FComboBoxPanel<CloseAction> cbpCloseAction = new FComboBoxPanel<>(localizer.getMessage("cbpCloseAction")+":");
private final FComboBoxPanel<String> cbpDefaultFontSize = new FComboBoxPanel<>(localizer.getMessage("cbpDefaultFontSize")+":");
private final FComboBoxPanel<String> cbpCardArtFormat = new FComboBoxPanel<>(localizer.getMessage("cbpCardArtFormat")+":");
private final FComboBoxPanel<String> cbpCardArtPreference = new FComboBoxPanel<>(localizer.getMessage("lblPreferredArt")+":");
private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":");
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":");
@@ -331,6 +332,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbpDefaultFontSize, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlDefaultFontSize")), descriptionConstraints);
pnlPrefs.add(cbpCardArtFormat, comboBoxConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlCardArtFormat")), descriptionConstraints);
pnlPrefs.add(cbImageFetcher, titleConstraints);
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlImageFetcher")), descriptionConstraints);
@@ -764,6 +768,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbpDefaultFontSize;
}
public FComboBoxPanel<String> getCbpCardArtFormatComboBoxPanel() {
return cbpCardArtFormat;
}
public FComboBoxPanel<String> getCbpDefaultLanguageComboBoxPanel() {
return cbpDefaultLanguage;
}

View File

@@ -10,6 +10,7 @@ import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.HashMap;
@@ -37,16 +38,17 @@ import forge.util.CardTranslation;
public class FCardImageRenderer {
private static boolean isInitialed = false;
private static final float BASE_IMAGE_WIDTH = 480;
private static final float BASE_IMAGE_WIDTH = 488;
private static final float BASE_IMAGE_HEIGHT = 680;
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 float CARD_ART_RATIO;
private static int PT_BOX_WIDTH, HEADER_PADDING, TYPE_PADDING, BLACK_BORDER_THICKNESS, BORDER_THICKNESS;
private static Font NAME_FONT, TYPE_FONT, TEXT_FONT, REMINDER_FONT, PT_FONT;
private static int NAME_SIZE, TYPE_SIZE, TEXT_SIZE, REMINDER_SIZE, PT_SIZE;
private static int PT_BOX_WIDTH, HEADER_PADDING, TYPE_PADDING, BLACK_BORDER_THICKNESS, BOX_LINE_THICKNESS;
private static int ART_INSET, OUTER_BORDER_THICKNESS;
private static Font NAME_FONT, TYPE_FONT, TEXT_FONT, REMINDER_FONT, PT_FONT, ARTIST_FONT;
private static int NAME_SIZE, TYPE_SIZE, TEXT_SIZE, REMINDER_SIZE, PT_SIZE, ARTIST_SIZE;
private static Map<Font, Font[]> cachedFonts;
private static Color TEXT_COLOR;
private static BreakIterator boundary;
@@ -54,7 +56,6 @@ public class FCardImageRenderer {
private static Pattern reminderPattern;
private static Pattern reminderHidePattern;
private static Pattern symbolPattern;
private static Map<Font, Font[]> shrinkFonts;
private static void initialize() {
Locale locale = new Locale(FModel.getPreferences().getPref(FPref.UI_LANGUAGE));
@@ -79,12 +80,14 @@ public class FCardImageRenderer {
}
}
PT_FONT = NAME_FONT;
ARTIST_FONT = new Font(Font.SERIF, Font.BOLD, 20);
shrinkFonts = new HashMap<>();
shrinkFonts.put(NAME_FONT, new Font[NAME_FONT.getSize()]);
shrinkFonts.put(TYPE_FONT, new Font[TYPE_FONT.getSize()]);
shrinkFonts.put(TEXT_FONT, new Font[TEXT_FONT.getSize()]);
shrinkFonts.put(REMINDER_FONT, new Font[REMINDER_FONT.getSize()]);
cachedFonts = new HashMap<>();
cachedFonts.put(NAME_FONT, new Font[NAME_FONT.getSize() * 2]);
cachedFonts.put(TYPE_FONT, new Font[TYPE_FONT.getSize() * 2]);
cachedFonts.put(TEXT_FONT, new Font[TEXT_FONT.getSize() * 2]);
cachedFonts.put(REMINDER_FONT, new Font[REMINDER_FONT.getSize() * 2]);
cachedFonts.put(ARTIST_FONT, new Font[ARTIST_FONT.getSize() * 2]);
isInitialed = true;
}
@@ -107,11 +110,11 @@ public class FCardImageRenderer {
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 final Color C_COMMON = fromDetailColor(DetailColors.COMMON);
private static final Color C_UNCOMMON = fromDetailColor(DetailColors.UNCOMMON);
private static final Color C_RARE = fromDetailColor(DetailColors.RARE);
private static final Color C_MYTHIC = fromDetailColor(DetailColors.MYTHIC);
private static final Color C_SPECIAL = fromDetailColor(DetailColors.SPECIAL);
private static Color getRarityColor(CardRarity rarity) {
if (rarity == null)// NPE from Rarity weird...
return Color.MAGENTA;
@@ -129,122 +132,172 @@ public class FCardImageRenderer {
}
}
private static Font getShrinkFont(Font orgFont, int newSize) {
private static Font getFontBySize(Font orgFont, int newSize) {
if (newSize == orgFont.getSize())
return orgFont;
Font font = shrinkFonts.get(orgFont)[newSize];
Font font = cachedFonts.get(orgFont)[newSize];
if (font == null) {
font = orgFont.deriveFont((float)newSize);
shrinkFonts.get(orgFont)[newSize] = font;
cachedFonts.get(orgFont)[newSize] = font;
}
return font;
}
public static void drawCardImage(Graphics2D g, CardView card, boolean altState, int width, int height) {
if (!isInitialed)
initialize();
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
float ratio = Math.min((float)width / BASE_IMAGE_WIDTH, (float)height / BASE_IMAGE_HEIGHT);
TEXT_COLOR = Color.BLACK;
if (card.isSplitCard()) {
boolean needTranslation = !"en-US".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE));
final CardStateView leftState = card.getLeftSplitState();
final String leftText = needTranslation ? CardTranslation.getTranslatedOracle(leftState.getName()) : leftState.getOracleText();
final CardStateView rightState = card.getRightSplitState();
String rightText = needTranslation ? CardTranslation.getTranslatedOracle(rightState.getName()) : rightState.getOracleText();
boolean isAftermath = (rightState.getKeywordKey().contains("Aftermath"));
if (isAftermath) {
int halfHeight = Math.round(380 * ratio);
int halfWidth = Math.round((halfHeight - 10) * ratio);
CARD_ART_RATIO = 2.68f;
updateAreaSizes(ratio, ratio);
drawCardStateImage(g, leftState, leftText, width, halfHeight);
CARD_ART_RATIO = 1.66f;
g.translate((double) width, (double)halfWidth);
g.rotate(Math.PI / 2.);
drawCardStateImage(g, rightState, rightText, height - halfWidth, width);
} else {
CARD_ART_RATIO = 1.36f;
updateAreaSizes(ratio, (float)height / 2f / (float)width);
AffineTransform tf = g.getTransform();
g.translate(0., (double)height);
g.rotate(-Math.PI / 2.);
drawCardStateImage(g, leftState, leftText, height / 2, width);
g.setTransform(tf);
g.translate(0., (double)height / 2);
g.rotate(-Math.PI / 2.);
drawCardStateImage(g, rightState, rightText, height / 2, width);
}
} else if (card.isFlipCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(altState);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
final CardStateView flipState = card.getState(!altState);
final String flipText = card.getText(flipState, needTranslation ? CardTranslation.getTranslationTexts(flipState.getName(), "") : null);
CARD_ART_RATIO = 1.72f;
updateAreaSizes(ratio, ratio);
drawFlipCardImage(g, state, text, flipState, flipText, width, height, altState);
} else if (card.isAdventureCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(false);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
final CardStateView advState = card.getState(true);
final String advText = card.getText(advState, needTranslation ? CardTranslation.getTranslationTexts(advState.getName(), "") : null);
CARD_ART_RATIO = 1.32f;
updateAreaSizes(ratio, ratio);
drawAdvCardImage(g, state, text, advState, advText, width, height);
} else {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(altState);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
CARD_ART_RATIO = 1.32f;
updateAreaSizes(ratio, ratio);
drawCardStateImage(g, state, text, width, height);
}
}
private static void updateAreaSizes(float mainRatio, float subRatio) {
NAME_SIZE = Math.round(NAME_FONT.getSize() * mainRatio);
TYPE_SIZE = Math.round(TYPE_FONT.getSize() * subRatio);
TEXT_SIZE = Math.round(TEXT_FONT.getSize() * mainRatio);
REMINDER_SIZE = Math.round(REMINDER_FONT.getSize() * mainRatio);
PT_SIZE = Math.round(PT_FONT.getSize() * mainRatio);
ARTIST_SIZE = Math.round(ARTIST_FONT.getSize() * mainRatio);
PT_BOX_WIDTH = Math.round(75 * mainRatio);
HEADER_PADDING = Math.round(7 * mainRatio);
TYPE_PADDING = Math.round(7 * subRatio) + (mainRatio == subRatio ? (NAME_SIZE - TYPE_SIZE) / 2 : 0);
BORDER_THICKNESS = Math.max(Math.round(2 * mainRatio), 1);
BOX_LINE_THICKNESS = Math.max(Math.round(2 * mainRatio), 1);
BLACK_BORDER_THICKNESS = Math.round(10 * mainRatio);
ART_INSET = Math.round(BLACK_BORDER_THICKNESS * 0.6f);
OUTER_BORDER_THICKNESS = Math.round(1.2f * BLACK_BORDER_THICKNESS) - ART_INSET;
}
private static void drawCardStateImage(Graphics2D g, CardStateView state, String text, int w, int h) {
int x = 0, y = 0;
public static void drawCardImage(Graphics2D g, CardView card, boolean altState, int width, int height, BufferedImage art, String legalString) {
if (!isInitialed) {
initialize();
}
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
float ratio = Math.min((float)width / BASE_IMAGE_WIDTH, (float)height / BASE_IMAGE_HEIGHT);
BLACK_BORDER_THICKNESS = Math.round(10 * ratio);
g.setColor(Color.BLACK);
g.fillRect(x, y, w, h);
x += BLACK_BORDER_THICKNESS;
y += BLACK_BORDER_THICKNESS;
w -= 2 * BLACK_BORDER_THICKNESS;
h -= 2 * BLACK_BORDER_THICKNESS;
g.fillRect(0, 0, width, height);
if (legalString != null) {
TEXT_COLOR = Color.LIGHT_GRAY;
int x = BLACK_BORDER_THICKNESS * 3;
int y = height - BLACK_BORDER_THICKNESS * 3;
int w = width;
boolean hasPTBox = false;
if (!card.isSplitCard() && !card.isFlipCard()) {
final CardStateView state = card.getState(card.isAdventureCard() ? false : altState);
if ((state.isCreature() && !state.getKeywordKey().contains("Level up"))
|| state.isPlaneswalker() || state.getType().hasSubtype("Vehicle"))
hasPTBox = true;
}
if (hasPTBox) {
w -= PT_BOX_WIDTH + BLACK_BORDER_THICKNESS * 5;
} else {
w -= BLACK_BORDER_THICKNESS * 6;
}
int h = BLACK_BORDER_THICKNESS * 3;
drawVerticallyCenteredString(g, legalString, new Rectangle(x, y, w, h), ARTIST_FONT, ARTIST_SIZE);
}
width -= 2 * BLACK_BORDER_THICKNESS;
height -= 2 * BLACK_BORDER_THICKNESS;
g.translate(BLACK_BORDER_THICKNESS, BLACK_BORDER_THICKNESS);
TEXT_COLOR = Color.BLACK;
if (card.isSplitCard()) {
boolean needTranslation = !"en-US".equals(FModel.getPreferences().getPref(FPref.UI_LANGUAGE));
final CardStateView leftState = card.getLeftSplitState();
final String leftText = needTranslation ? CardTranslation.getTranslatedOracle(leftState.getName()) : leftState.getOracleText();
final CardStateView rightState = card.getRightSplitState();
String rightText = needTranslation ? CardTranslation.getTranslatedOracle(rightState.getName()) : rightState.getOracleText();
boolean isAftermath = (rightState.getKeywordKey().contains("Aftermath"));
BufferedImage leftArt = null;
BufferedImage rightArt = null;
if (isAftermath) {
if (art != null) {
int leftWidth = Math.round(art.getWidth() * 0.61328125f);
leftArt = art.getSubimage(0, 0, leftWidth, art.getHeight());
rightArt = art.getSubimage(leftWidth, 0, art.getWidth() - leftWidth, art.getHeight());
}
int halfHeight = Math.round(370 * ratio);
int halfWidth = Math.round(360 * ratio);
CARD_ART_RATIO = 2.719f;
updateAreaSizes(ratio, ratio);
drawCardStateImage(g, leftState, leftText, width, halfHeight, leftArt);
CARD_ART_RATIO = 1.714f;
int widthAdjust = OUTER_BORDER_THICKNESS;
int heightAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2;
g.translate((double) width - widthAdjust, (double)halfWidth);
g.rotate(Math.PI / 2.);
drawCardStateImage(g, rightState, rightText, height - halfWidth - heightAdjust, width, rightArt);
} else {
if (art != null) {
leftArt = art.getSubimage(0, 0, art.getWidth() / 2, art.getHeight());
rightArt = art.getSubimage(art.getWidth() / 2, 0, art.getWidth() / 2, art.getHeight());
}
CARD_ART_RATIO = 1.443f;
updateAreaSizes(ratio, ((float)height / 2f / (float)width) * ratio);
int widthAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2;
int heightAdjust = height - widthAdjust - BLACK_BORDER_THICKNESS;
AffineTransform tf = g.getTransform();
g.translate(0., (double)(height - widthAdjust));
g.rotate(-Math.PI / 2.);
drawCardStateImage(g, leftState, leftText, heightAdjust / 2, width + widthAdjust, leftArt);
g.setTransform(tf);
g.translate(0., (double)(heightAdjust / 2));
g.rotate(-Math.PI / 2.);
drawCardStateImage(g, rightState, rightText, heightAdjust / 2, width + widthAdjust, rightArt);
}
} else if (card.isFlipCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(false);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
final CardStateView flipState = card.getState(true);
final String flipText = card.getText(flipState, needTranslation ? CardTranslation.getTranslationTexts(flipState.getName(), "") : null);
CARD_ART_RATIO = 1.728f;
updateAreaSizes(ratio, ratio);
int heightAdjust = OUTER_BORDER_THICKNESS + PT_SIZE / 2;
if (altState) {
g.translate(width, height);
g.rotate(Math.PI);
}
drawFlipCardImage(g, state, text, flipState, flipText, width, height - heightAdjust, art);
} else if (card.isAdventureCard()) {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(false);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
final CardStateView advState = card.getState(true);
final String advText = card.getText(advState, needTranslation ? CardTranslation.getTranslationTexts(advState.getName(), "") : null);
CARD_ART_RATIO = 1.37f;
updateAreaSizes(ratio, ratio);
drawAdvCardImage(g, state, text, advState, advText, width, height, art);
} else {
boolean needTranslation = !card.isToken() || !(card.getCloneOrigin() == null);
final CardStateView state = card.getState(altState);
final String text = card.getText(state, needTranslation ? CardTranslation.getTranslationTexts(state.getName(), "") : null);
CARD_ART_RATIO = 1.37f;
if (art != null && Math.abs((float)art.getWidth() / (float)art.getHeight() - CARD_ART_RATIO) > 0.1f) {
CARD_ART_RATIO = (float)art.getWidth() / (float)art.getHeight();
}
updateAreaSizes(ratio, ratio);
drawCardStateImage(g, state, text, width, height, art);
}
g.dispose();
}
private static void drawCardStateImage(Graphics2D g, CardStateView state, String text, int w, int h, BufferedImage art) {
int x = 0, y = 0;
//determine colors for borders
final List<DetailColors> borderColors = CardDetailUtil.getBorderColors(state, true);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h, BLACK_BORDER_THICKNESS);
int artInset = Math.round(BLACK_BORDER_THICKNESS * 0.8f);
int outerBorderThickness = 2 * BLACK_BORDER_THICKNESS - artInset;
x += outerBorderThickness;
y += outerBorderThickness;
w -= 2 * outerBorderThickness;
x += OUTER_BORDER_THICKNESS;
y += OUTER_BORDER_THICKNESS;
w -= 2 * OUTER_BORDER_THICKNESS;
int headerHeight = NAME_SIZE + 2 * HEADER_PADDING;
int typeBoxHeight = TYPE_SIZE + 2 * TYPE_PADDING;
int ptBoxHeight = 0;
if (state.isCreature() || state.isPlaneswalker() || state.getType().hasSubtype("Vehicle")) {
//if P/T box needed, make room for it
ptBoxHeight = NAME_SIZE + HEADER_PADDING;
ptBoxHeight = headerHeight;
}
int artWidth = w - 2 * artInset;
int artWidth = w - 2 * ART_INSET;
int artHeight = Math.round(artWidth / CARD_ART_RATIO);
int textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - outerBorderThickness - artInset - PT_FONT.getSize() / 2;
int textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - OUTER_BORDER_THICKNESS - ART_INSET - PT_SIZE / 2;
int artY = y + headerHeight;
int typeY = artY + artHeight;
@@ -257,7 +310,7 @@ public class FCardImageRenderer {
boolean isDungeon = state.getType().isDungeon();
if (isSaga || isClass || isDungeon) {
// Move type line to the bottom
typeY = ptY - typeBoxHeight;
typeY = ptY - Math.round(typeBoxHeight * 1.2f);
if (!isDungeon)
artWidth = artWidth / 2;
artHeight = typeY - artY;
@@ -268,8 +321,8 @@ public class FCardImageRenderer {
//draw art box with Forge icon
if (!isDungeon) {
Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
int artX = x + artInset + (isSaga ? artWidth : 0);
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight);
int artX = x + ART_INSET + (isSaga ? artWidth : 0);
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight, art);
}
//handle leveler cards
@@ -320,7 +373,7 @@ public class FCardImageRenderer {
}
}
int textX = x + artInset;
int textX = x + ART_INSET;
//draw text box
Color[] textBox1Colors = tintColors(Color.WHITE, colors, TEXT_BOX_TINT);
@@ -364,7 +417,7 @@ public class FCardImageRenderer {
} else {
//draw text box
Color[] textBoxColors = tintColors(Color.WHITE, colors, TEXT_BOX_TINT);
int textX = x + artInset + (isClass ? artWidth : 0);
int textX = x + ART_INSET + (isClass ? artWidth : 0);
drawTextBox(g, state, text, textBoxColors, textX, textY, artWidth, textBoxHeight, ptBoxHeight > 0 ? 1 : 0);
//draw P/T box
@@ -377,63 +430,53 @@ public class FCardImageRenderer {
//draw header containing name and mana cost
Color[] headerColors = tintColors(Color.WHITE, colors, NAME_BOX_TINT);
drawHeader(g, state, headerColors, x, y, w, headerHeight, true);
drawHeader(g, state, headerColors, x, y, w, headerHeight, true, true);
//draw type line
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, 0, true);
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, 0, true, true);
}
private static void drawFlipCardImage(Graphics2D g, CardStateView state, String text, CardStateView flipState, String flipText, int w, int h, boolean isFlipped) {
private static void drawFlipCardImage(Graphics2D g, CardStateView state, String text, CardStateView flipState, String flipText, int w, int h, BufferedImage art) {
int width = w, height = h;
int x = 0, y = 0;
g.setColor(Color.BLACK);
g.fillRect(x, y, w, h);
x += BLACK_BORDER_THICKNESS;
y += BLACK_BORDER_THICKNESS;
w -= 2 * BLACK_BORDER_THICKNESS;
h -= 2 * BLACK_BORDER_THICKNESS;
//determine colors for borders
final List<DetailColors> borderColors = CardDetailUtil.getBorderColors(state, true);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h, 0);
int artInset = Math.round(BLACK_BORDER_THICKNESS * 0.8f);
int outerBorderThickness = 2 * BLACK_BORDER_THICKNESS - artInset;
x += outerBorderThickness;
y += outerBorderThickness;
w -= 2 * outerBorderThickness;
h -= 2 * outerBorderThickness;
x += OUTER_BORDER_THICKNESS;
y += OUTER_BORDER_THICKNESS;
w -= 2 * OUTER_BORDER_THICKNESS;
h -= 2 * OUTER_BORDER_THICKNESS;
int headerHeight = NAME_SIZE + 2 * HEADER_PADDING;
int typeBoxHeight = TYPE_SIZE + 2 * TYPE_PADDING;
int artWidth = w - 2 * artInset;
int artWidth = w - 2 * ART_INSET;
int artHeight = Math.round(artWidth / CARD_ART_RATIO);
int textBoxHeight = (h - (headerHeight + typeBoxHeight) * 2 - artHeight) / 2;
int ptBoxHeight = NAME_SIZE + HEADER_PADDING;
int ptBoxHeight = headerHeight;
int textY = y + headerHeight;
int typeY = textY + textBoxHeight;
int artY = typeY + typeBoxHeight;
int ptY = typeY - 4;
int ptY = typeY + 1;
//draw art box with Forge icon
if (!isFlipped) {
Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
int artX = x + artInset;
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight);
}
Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
int artX = x + ART_INSET;
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight, art);
//draw text box
Color[] textBoxColors = tintColors(Color.WHITE, colors, TEXT_BOX_TINT);
int textX = x + artInset;
int textX = x + ART_INSET;
drawTextBox(g, state, text, textBoxColors, textX, textY, artWidth, textBoxHeight, 0);
//draw header containing name and mana cost
Color[] headerColors = tintColors(Color.WHITE, colors, NAME_BOX_TINT);
drawHeader(g, state, headerColors, x, y, w, headerHeight, !isFlipped);
drawHeader(g, state, headerColors, x, y, w, headerHeight, true, true);
//draw type line
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, state.isCreature() ? PT_BOX_WIDTH : 0, !isFlipped);
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, state.isCreature() ? PT_BOX_WIDTH : 0, true, true);
//draw P/T box
if (state.isCreature()) {
@@ -445,21 +488,14 @@ public class FCardImageRenderer {
g.translate(width, height);
g.rotate(Math.PI);
//draw art box with Forge icon
if (isFlipped) {
Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
int artX = x + artInset;
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight);
}
//draw text box
drawTextBox(g, flipState, flipText, textBoxColors, textX, textY, artWidth, textBoxHeight, 0);
//draw header containing name and mana cost
drawHeader(g, flipState, headerColors, x, y, w, headerHeight, isFlipped);
drawHeader(g, flipState, headerColors, x, y, w, headerHeight, false, true);
//draw type line
drawTypeLine(g, flipState, headerColors, x, typeY, w, typeBoxHeight, flipState.isCreature() ? PT_BOX_WIDTH : 0, isFlipped);
drawTypeLine(g, flipState, headerColors, x, typeY, w, typeBoxHeight, flipState.isCreature() ? PT_BOX_WIDTH : 0, false, true);
//draw P/T box
if (flipState.isCreature()) {
@@ -468,32 +504,24 @@ public class FCardImageRenderer {
}
}
private static void drawAdvCardImage(Graphics2D g, CardStateView state, String text, CardStateView advState, String advText, int w, int h) {
private static void drawAdvCardImage(Graphics2D g, CardStateView state, String text, CardStateView advState, String advText, int w, int h, BufferedImage art) {
int x = 0, y = 0;
g.setColor(Color.BLACK);
g.fillRect(x, y, w, h);
x += BLACK_BORDER_THICKNESS;
y += BLACK_BORDER_THICKNESS;
w -= 2 * BLACK_BORDER_THICKNESS;
h -= 2 * BLACK_BORDER_THICKNESS;
//determine colors for borders
final List<DetailColors> borderColors = CardDetailUtil.getBorderColors(state, true);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h);
Color[] colors = fillColorBackground(g, borderColors, x, y, w, h, BLACK_BORDER_THICKNESS);
int artInset = Math.round(BLACK_BORDER_THICKNESS * 0.8f);
int outerBorderThickness = 2 * BLACK_BORDER_THICKNESS - artInset;
x += outerBorderThickness;
y += outerBorderThickness;
w -= 2 * outerBorderThickness;
x += OUTER_BORDER_THICKNESS;
y += OUTER_BORDER_THICKNESS;
w -= 2 * OUTER_BORDER_THICKNESS;
int headerHeight = NAME_SIZE + 2 * HEADER_PADDING;
int typeBoxHeight = TYPE_SIZE + 2 * TYPE_PADDING;
int ptBoxHeight = NAME_SIZE + HEADER_PADDING;
int ptBoxHeight = headerHeight;
int artWidth = w - 2 * artInset;
int artWidth = w - 2 * ART_INSET;
int artHeight = Math.round(artWidth / CARD_ART_RATIO);
int textBoxWidth = artWidth / 2;
int textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - outerBorderThickness - artInset - PT_FONT.getSize() / 2;
int textBoxHeight = h - headerHeight - artHeight - typeBoxHeight - OUTER_BORDER_THICKNESS - ART_INSET - PT_SIZE / 2;
int artY = y + headerHeight;
int typeY = artY + artHeight;
@@ -502,20 +530,20 @@ public class FCardImageRenderer {
//draw art box with Forge icon
Color[] artBoxColors = tintColors(Color.DARK_GRAY, colors, NAME_BOX_TINT);
int artX = x + artInset;
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight);
int artX = x + ART_INSET;
drawArt(g, artBoxColors, artX, artY, artWidth, artHeight, art);
//draw text box
Color[] textBoxColors = tintColors(Color.WHITE, colors, TEXT_BOX_TINT);
int textX = x + artInset + textBoxWidth;
int textX = x + ART_INSET + textBoxWidth;
drawTextBox(g, state, text, textBoxColors, textX, textY, textBoxWidth, textBoxHeight, 1);
//draw header containing name and mana cost
Color[] headerColors = tintColors(Color.WHITE, colors, NAME_BOX_TINT);
drawHeader(g, state, headerColors, x, y, w, headerHeight, true);
drawHeader(g, state, headerColors, x, y, w, headerHeight, true, true);
//draw type line
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, 0, true);
drawTypeLine(g, state, headerColors, x, typeY, w, typeBoxHeight, 0, true, true);
//draw P/T box
Color[] ptColors = tintColors(Color.WHITE, colors, PT_BOX_TINT);
@@ -525,32 +553,31 @@ public class FCardImageRenderer {
int advTypeHeight = advHeaderHeight - 1;
NAME_SIZE = TYPE_SIZE - 2;
TYPE_SIZE = NAME_SIZE - 1;
textX = x + artInset;
textX = x + ART_INSET;
//draw header containing name and mana cost
Color[] advheaderColors = tintColors(Color.GRAY, colors, 0.6f);
TEXT_COLOR = Color.WHITE;
drawHeader(g, advState, advheaderColors, textX, textY, textBoxWidth, advHeaderHeight, true);
drawHeader(g, advState, advheaderColors, textX, textY, textBoxWidth, advHeaderHeight, true, false);
//draw type line
Color[] advTypeColors = tintColors(Color.DARK_GRAY, colors, 0.6f);
textY += advHeaderHeight;
drawTypeLine(g, advState, advTypeColors, textX, textY, textBoxWidth, advTypeHeight, 0, false);
drawTypeLine(g, advState, advTypeColors, textX, textY + advHeaderHeight, textBoxWidth, advTypeHeight, 0, false, false);
//draw text box
TEXT_COLOR = Color.BLACK;
textY += advTypeHeight;
textBoxHeight -= advHeaderHeight + advTypeHeight;
drawTextBox(g, advState, advText, textBoxColors, textX, textY, textBoxWidth, textBoxHeight, 0);
int yAdjust = advHeaderHeight + advTypeHeight;
drawTextBox(g, advState, advText, textBoxColors, textX, textY, textBoxWidth, textBoxHeight, (yAdjust << 16));
}
private static Color[] fillColorBackground(Graphics2D g, List<DetailColors> backColors, int x, int y, int w, int h) {
private static Color[] fillColorBackground(Graphics2D g, List<DetailColors> backColors, int x, int y, int w, int h, int borderThickness) {
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);
fillRoundColorBackground(g, colors, x, y, w, h - 2 * borderThickness, borderThickness * 12, borderThickness * 10);
fillColorBackground(g, colors, x, y, w, 10 * borderThickness);
return colors;
}
private static void fillColorBackground(Graphics2D g, Color[] colors, float x, float y, float w, float h) {
@@ -577,15 +604,39 @@ public class FCardImageRenderer {
}
g.setPaint(oldPaint);
}
private static void fillRoundColorBackground(Graphics2D g, Color[] colors, float x, float y, float w, float h, float arcWidth, float arcHeight) {
Paint oldPaint = g.getPaint();
switch (colors.length) {
case 1:
g.setColor(colors[0]);
g.fillRoundRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h), Math.round(arcWidth), Math.round(arcHeight));
break;
case 2:
GradientPaint gradient = new GradientPaint(x, y, colors[0], x + w, y, colors[1]);
g.setPaint(gradient);
g.fillRoundRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h), Math.round(arcWidth), Math.round(arcHeight));
break;
case 3:
float halfWidth = w / 2;
GradientPaint gradient1 = new GradientPaint(x, y, colors[0], x + halfWidth, y, colors[1]);
g.setPaint(gradient1);
g.fillRoundRect(Math.round(x), Math.round(y), Math.round(halfWidth + arcWidth), Math.round(h), Math.round(arcWidth), Math.round(arcHeight));
GradientPaint gradient2 = new GradientPaint(x + halfWidth, y, colors[1], x + w, y, colors[2]);
g.setPaint(gradient2);
g.fillRoundRect(Math.round(x + halfWidth - arcWidth), Math.round(y), Math.round(halfWidth + arcWidth), Math.round(h), Math.round(arcWidth), Math.round(arcHeight));
break;
}
g.setPaint(oldPaint);
}
private static void drawVerticallyCenteredString(Graphics2D g, String text, Rectangle area, Font originalFont, int size) {
Font font = getShrinkFont(originalFont, size);
Font font = getFontBySize(originalFont, size);
FontMetrics fontMetrics = g.getFontMetrics(font);
// Shrink font if the text is too long
while (fontMetrics.stringWidth(text) > area.width) {
--size;
font = getShrinkFont(originalFont, size);
font = getFontBySize(originalFont, size);
fontMetrics = g.getFontMetrics(font);
}
@@ -597,19 +648,20 @@ public class FCardImageRenderer {
g.drawString(text, x, y);
}
private static void drawHeader(Graphics2D g, CardStateView state, Color[] colors, int x, int y, int w, int h, boolean drawMana) {
fillColorBackground(g, colors, x, y, w, h);
g.setStroke(new BasicStroke(BORDER_THICKNESS));
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
int padding = h / 4;
private static void drawHeader(Graphics2D g, CardStateView state, Color[] colors, int x, int y, int w, int h, boolean drawMana, boolean drawRoundRect) {
int padding = h / 3;
fillRoundColorBackground(g, colors, x, y, w, h, drawRoundRect ? padding : 0, drawRoundRect ? h : 0);
if (drawRoundRect) {
g.setStroke(new BasicStroke(BOX_LINE_THICKNESS));
g.setColor(Color.BLACK);
g.drawRoundRect(x, y, w, h, padding, h);
}
//draw mana cost for card
if (drawMana) {
ManaCost manaCost = state.getManaCost();
int manaCostWidth = manaCost.getGlyphCount() * NAME_SIZE + HEADER_PADDING;
CardFaceSymbols.draw(g, manaCost, x + w - manaCostWidth, y + (h - NAME_SIZE) / 2 + 1, NAME_SIZE);
CardFaceSymbols.draw(g, manaCost, x + w - manaCostWidth, y + (h - NAME_SIZE) / 2 + 1, NAME_SIZE - 1);
w -= padding + manaCostWidth;
}
@@ -621,31 +673,47 @@ public class FCardImageRenderer {
}
private static void drawArt(Graphics2D g, Color[] colors, int x, int y, int w, int 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 >= (float)w / (float)h) {
int newH = Math.round(w * (artHeight / artWidth));
FSkin.drawImage(g, art, x, y + (h - newH) / 2, w, newH);
private static void drawArt(Graphics2D g, Color[] colors, int x, int y, int w, int h, BufferedImage art) {
if (art != null) {
int artWidth = art.getWidth();
int artHeight = art.getHeight();
if ((float)artWidth / (float)artHeight >= (float)w / (float)h) {
int newW = Math.round(artHeight * ((float)w / (float)h));
int newX = (artWidth - newW) / 2;
g.drawImage(art, x, y, x + w, y + h, newX, 0, newX + newW, art.getHeight(), null);
} else {
int newH = Math.round(artWidth * ((float)h / (float)w));
int newY = (artHeight - newH) / 2;
g.drawImage(art, x, y, x + w, y + h, 0, newY, art.getWidth(), newY + newH, null);
}
} else {
int newW = Math.round(h * (artWidth / artHeight));
FSkin.drawImage(g, art, x + (w - newW) / 2, y, newW, h);
fillColorBackground(g, colors, x, y, w, h);
SkinIcon icon = FSkin.getIcon(FSkinProp.ICO_LOGO);
float artWidth = (float)icon.getSizeForPaint(g).getWidth();
float artHeight = (float)icon.getSizeForPaint(g).getHeight();
if (artWidth / artHeight >= (float)w / (float)h) {
int newH = Math.round(w * (artHeight / artWidth));
FSkin.drawImage(g, icon, x, y + (h - newH) / 2, w, newH);
} else {
int newW = Math.round(h * (artWidth / artHeight));
FSkin.drawImage(g, icon, x + (w - newW) / 2, y, newW, h);
}
}
g.setStroke(new BasicStroke(BORDER_THICKNESS));
g.setStroke(new BasicStroke(BOX_LINE_THICKNESS));
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
}
private static void drawTypeLine(Graphics2D g, CardStateView state, Color[] colors, int x, int y, int w, int h, int adjust, boolean drawRarity) {
fillColorBackground(g, colors, x, y, w, h);
g.setStroke(new BasicStroke(BORDER_THICKNESS));
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
private static void drawTypeLine(Graphics2D g, CardStateView state, Color[] colors, int x, int y, int w, int h, int adjust, boolean drawRarity, boolean drawRoundRect) {
int padding = h / 3;
fillRoundColorBackground(g, colors, x, y, w, h, drawRoundRect ? padding : 0, drawRoundRect ? h : 0);
if (drawRoundRect) {
g.setStroke(new BasicStroke(BOX_LINE_THICKNESS));
g.setColor(Color.BLACK);
g.drawRoundRect(x, y, w, h, padding, h);
}
w -= adjust;
int padding = h / 4;
//draw square icon for rarity
if (drawRarity) {
@@ -653,7 +721,7 @@ public class FCardImageRenderer {
int iconPadding = (h - iconSize) / 2;
w -= iconSize + iconPadding * 2;
g.setColor(getRarityColor(state.getRarity()));
g.fillRect(x + w + iconPadding, y + (h - iconSize) / 2, iconSize, iconSize);
g.fillRect(x + w + iconPadding, y + (h - iconSize + 1) / 2, iconSize, iconSize);
}
//draw type
@@ -664,12 +732,13 @@ public class FCardImageRenderer {
}
/**
* @param flagPTBox [0] bit: has PT box, [1] bit: leveler PT box, [2] bit: leveler Level box
* @param textBoxFlags [0] bit: has PT box, [1] bit: leveler PT box, [2] bit: leveler Level box, [16~31] bit: y adjust
*/
private static void drawTextBox(Graphics2D g, CardStateView state, String text, Color[] colors,
int x, int y, int w, int h, int flagPTBox) {
fillColorBackground(g, colors, x, y, w, h);
g.setStroke(new BasicStroke(BORDER_THICKNESS));
int x, int y, int w, int h, int textBoxFlags) {
int yAdjust = (textBoxFlags >> 16);
fillColorBackground(g, colors, x, y + yAdjust, w, h - yAdjust);
g.setStroke(new BasicStroke(BOX_LINE_THICKNESS));
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
@@ -705,9 +774,9 @@ public class FCardImageRenderer {
int padding = TEXT_SIZE / 4;
x += padding;
w -= 2 * padding;
if ((flagPTBox & 2) == 2)
if ((textBoxFlags & 2) == 2)
w -= PT_BOX_WIDTH;
drawTextBoxText(g, text, x, y, w, h, flagPTBox);
drawTextBoxText(g, text, x, y + yAdjust, w, h - yAdjust, textBoxFlags);
}
}
@@ -726,18 +795,22 @@ public class FCardImageRenderer {
}
}
else if (state.isPlaneswalker()) {
Color [] pwColor = { Color.BLACK };
colors = pwColor;
TEXT_COLOR = Color.WHITE;
pieces.add(String.valueOf(state.getLoyalty()));
}
else if (state.getType().hasSubtype("Vehicle")) {
pieces.add("[");
Color [] vhColor = { new Color(128, 96, 64) };
colors = vhColor;
TEXT_COLOR = Color.WHITE;
pieces.add(String.valueOf(state.getPower()));
pieces.add("/");
pieces.add(String.valueOf(state.getToughness()));
pieces.add("]");
}
else { return; }
Font font = getShrinkFont(PT_FONT, PT_SIZE);
Font font = getFontBySize(PT_FONT, PT_SIZE);
FontMetrics metrics = g.getFontMetrics(font);
int padding = PT_SIZE / 4;
int totalPieceWidth = -padding;
@@ -747,24 +820,21 @@ public class FCardImageRenderer {
pieceWidths[i] = pieceWidth;
totalPieceWidth += pieceWidth;
}
int boxHeight = metrics.getMaxAscent() + padding;
x += w - PT_BOX_WIDTH;
w = PT_BOX_WIDTH;
int boxWidth = Math.max(PT_BOX_WIDTH, totalPieceWidth + 2 * padding);
x += w - boxWidth;
y += h - boxHeight;
w = boxWidth;
h = boxHeight;
int arcWidth = h / 3;
fillRoundColorBackground(g, colors, x, y, w, h, arcWidth, h);
g.setStroke(new BasicStroke(BOX_LINE_THICKNESS));
g.setColor(state.isPlaneswalker() ? Color.WHITE : Color.BLACK);
g.drawRoundRect(x, y, w, h, arcWidth, h);
fillColorBackground(g, colors, x, y, w, h);
g.setStroke(new BasicStroke(BORDER_THICKNESS));
g.setColor(Color.BLACK);
g.drawRect(x, y, w, h);
x += (boxWidth - totalPieceWidth) / 2;
x += (PT_BOX_WIDTH - totalPieceWidth) / 2;
for (int i = 0; i < pieces.size(); i++) {
drawVerticallyCenteredString(g, pieces.get(i), new Rectangle(x, y - 2, w, h), PT_FONT, PT_SIZE);
x += pieceWidths[i];
}
TEXT_COLOR = Color.BLACK;
}
private static abstract class Piece {
@@ -986,8 +1056,8 @@ public class FCardImageRenderer {
}
// Find font size that fit in the text box area
Font txFont = getShrinkFont(TEXT_FONT, TEXT_SIZE);
Font rmFont = getShrinkFont(REMINDER_FONT, REMINDER_SIZE);
Font txFont = getFontBySize(TEXT_FONT, TEXT_SIZE);
Font rmFont = getFontBySize(REMINDER_FONT, REMINDER_SIZE);
FontMetrics txMetrics = g.getFontMetrics(txFont);
FontMetrics rmMetrics = g.getFontMetrics(rmFont);
int txFontSize = txFont.getSize(), rmFontSize = rmFont.getSize();
@@ -1017,10 +1087,10 @@ public class FCardImageRenderer {
break;
//Shrink font and do again
--txFontSize;
txFont = getShrinkFont(TEXT_FONT, txFontSize);
txFont = getFontBySize(TEXT_FONT, txFontSize);
txMetrics = g.getFontMetrics(txFont);
--rmFontSize;
rmFont = getShrinkFont(REMINDER_FONT, rmFontSize);
rmFont = getFontBySize(REMINDER_FONT, rmFontSize);
rmMetrics = g.getFontMetrics(rmFont);
} while (txFontSize >= 8 && rmFontSize >= 8);
@@ -1037,7 +1107,7 @@ public class FCardImageRenderer {
y += pg.drawPieces(g, x + xoffset, y, w, lineSpacing + lineHeight, txFont, txMetrics, rmFont, rmMetrics);
y += paraSpacing - lineSpacing;
if (isLevelBox) {
txFont = getShrinkFont(TEXT_FONT, txFontSize + 10);
txFont = getFontBySize(TEXT_FONT, txFontSize + 10);
txMetrics = g.getFontMetrics(txFont);
y -= paraSpacing;
}