diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index 6c7a30abc1..0623ac6d7d 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -26,6 +26,7 @@ import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -93,13 +94,13 @@ public enum Plugins implements MagePlugins { @Override public MageCard getMagePermanent(PermanentView card, BigCard bigCard, CardIconRenderSettings cardIconRenderSettings, Dimension dimension, UUID gameId, boolean loadImage, int renderMode, boolean needFullPermanentRender) { MageCard mageCard; - if (cardPlugin != null) { - mageActionCallback.refreshSession(); - mageActionCallback.setCardPreviewComponent(bigCard); - mageCard = cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); - } else { + if (cardPlugin == null) { throw new IllegalArgumentException("Card's plugin must be loaded"); } + mageActionCallback.refreshSession(); + mageActionCallback.setCardPreviewComponent(bigCard); + mageCard = cardPlugin.getMagePermanent(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); + return createLayeredCard(mageCard, dimension, cardIconRenderSettings); } @@ -108,15 +109,14 @@ public enum Plugins implements MagePlugins { // Card icons panels must be put outside of the card like MTG Arena. // So for compatibility purposes: keep free space for icons and change card dimention MageCard mageCard; - if (cardPlugin != null) { - if (previewable) { - mageActionCallback.refreshSession(); - mageActionCallback.setCardPreviewComponent(bigCard); - } - mageCard = cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); - } else { + if (cardPlugin == null) { throw new IllegalArgumentException("Card's plugin must be loaded"); } + if (previewable) { + mageActionCallback.refreshSession(); + mageActionCallback.setCardPreviewComponent(bigCard); + } + mageCard = cardPlugin.getMageCard(card, dimension, gameId, mageActionCallback, false, !MageFrame.isLite() && loadImage, renderMode, needFullPermanentRender); return createLayeredCard(mageCard, dimension, cardIconRenderSettings); } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java index 63f6189844..8b281ccb26 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeImage.java @@ -24,6 +24,7 @@ import org.mage.plugins.card.utils.impl.ImageManagerImpl; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; +import java.util.Objects; import java.util.StringTokenizer; import java.util.UUID; @@ -36,6 +37,8 @@ public class CardPanelRenderModeImage extends CardPanel { private static final long serialVersionUID = -3272134219262184411L; + private final static SoftValuesLoadingCache IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelRenderModeImage::createImage)); + private static final int WIDTH_LIMIT = 90; // card width limit to create smaller counter private static final float ROUNDED_CORNER_SIZE = 0.1f; @@ -79,33 +82,9 @@ public class CardPanelRenderModeImage extends CardPanel { private boolean displayTitleAnyway; private final boolean displayFullImagePath; - private final static SoftValuesLoadingCache IMAGE_CACHE; + private int updateArtImageStamp; - public ImagePanel getOverlayPanel() { - return overlayPanel; - } - - public void setOverlayPanel(ImagePanel overlayPanel) { - this.overlayPanel = overlayPanel; - } - - public JPanel getTypeIconPanel() { - return typeIconPanel; - } - - public void setTypeIconPanel(JPanel typeIconPanel) { - this.typeIconPanel = typeIconPanel; - } - - public JPanel getCounterPanel() { - return counterPanel; - } - - public void setCounterPanel(JPanel counterPanel) { - this.counterPanel = counterPanel; - } - - static class Key { + private static class Key { final Insets border; final int width; @@ -137,98 +116,34 @@ public class CardPanelRenderModeImage extends CardPanel { this.canBlock = canBlock; } - @Override - public int hashCode() { - int hash = 3; - hash = 19 * hash + this.border.left; - hash = 19 * hash + this.border.right; - hash = 19 * hash + this.border.top; - hash = 19 * hash + this.border.bottom; - hash = 19 * hash + this.width; - hash = 19 * hash + this.height; - hash = 19 * hash + this.cardWidth; - hash = 19 * hash + this.cardHeight; - hash = 19 * hash + this.cardXOffset; - hash = 19 * hash + this.cardYOffset; - hash = 19 * hash + (this.hasImage ? 1 : 0); - hash = 19 * hash + (this.isSelected ? 1 : 0); - hash = 19 * hash + (this.isChoosable ? 1 : 0); - hash = 19 * hash + (this.isPlayable ? 1 : 0); - hash = 19 * hash + (this.canAttack ? 1 : 0); - hash = 19 * hash + (this.canBlock ? 1 : 0); - return hash; - } - @Override public boolean equals(Object obj) { if (this == obj) { return true; } - if (obj == null) { + if (obj == null || this.getClass() != obj.getClass()) { return false; } - if (getClass() != obj.getClass()) { - return false; - } - final Key other = (Key) obj; - if (this.border.left != other.border.left) { - return false; - } - if (this.border.right != other.border.right) { - return false; - } - if (this.border.top != other.border.top) { - return false; - } - if (this.border.bottom != other.border.bottom) { - return false; - } - if (this.width != other.width) { - return false; - } - if (this.height != other.height) { - return false; - } - if (this.cardWidth != other.cardWidth) { - return false; - } - if (this.cardHeight != other.cardHeight) { - return false; - } - if (this.cardXOffset != other.cardXOffset) { - return false; - } - if (this.cardYOffset != other.cardYOffset) { - return false; - } - if (this.hasImage != other.hasImage) { - return false; - } - if (this.isSelected != other.isSelected) { - return false; - } - if (this.isChoosable != other.isChoosable) { - return false; - } - if (this.isPlayable != other.isPlayable) { - return false; - } - if (this.canAttack != other.canAttack) { - return false; - } - return this.canBlock == other.canBlock; + Key that = (Key) obj; + return Objects.equals(this.border, that.border) + && this.width == that.width + && this.height == that.height + && this.cardWidth == that.cardWidth + && this.cardHeight == that.cardHeight + && this.cardXOffset == that.cardXOffset + && this.cardYOffset == that.cardYOffset + && this.hasImage == that.hasImage + && this.isSelected == that.isSelected + && this.isChoosable == that.isChoosable + && this.isPlayable == that.isPlayable + && this.canAttack == that.canAttack + && this.canBlock == that.canBlock; } - } - static { - IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelRenderModeImage::createImage)); - } - - static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage) { - // cards without images show icons and text always - // TODO: apply "card names on card" setting to icon too? - // TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200) - return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage); + @Override + public int hashCode() { + return Objects.hash(border, width, height, cardWidth, cardHeight, cardXOffset, cardYOffset, hasImage, isSelected, isChoosable, isPlayable, canAttack, canBlock); + } } private static class CardSizes { @@ -254,195 +169,11 @@ public class CardPanelRenderModeImage extends CardPanel { } } - public CardPanelRenderModeImage(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { - // Call to super - super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender); - - // Counter panel - if (!newGameCard.isAbility()) { - // panel to show counters on the card - setCounterPanel(new JPanel()); - getCounterPanel().setLayout(null); - getCounterPanel().setOpaque(false); - add(getCounterPanel()); - - plusCounterLabel = new JLabel(""); - plusCounterLabel.setToolTipText("+1/+1"); - getCounterPanel().add(plusCounterLabel); - - minusCounterLabel = new JLabel(""); - minusCounterLabel.setToolTipText("-1/-1"); - getCounterPanel().add(minusCounterLabel); - - loyaltyCounterLabel = new JLabel(""); - loyaltyCounterLabel.setToolTipText("loyalty"); - getCounterPanel().add(loyaltyCounterLabel); - - otherCounterLabel = new JLabel(""); - getCounterPanel().add(otherCounterLabel); - - getCounterPanel().setVisible(false); - } - - // Ability icon - if (newGameCard.isAbility()) { - if (newGameCard.getAbilityType() == AbilityType.TRIGGERED) { - setTypeIcon(ImageManagerImpl.instance.getTriggeredAbilityImage(), "Triggered Ability"); - } else if (newGameCard.getAbilityType() == AbilityType.ACTIVATED) { - setTypeIcon(ImageManagerImpl.instance.getActivatedAbilityImage(), "Activated Ability"); - } - } - - // Token icon - if (this.getGameCard().isToken()) { - setTypeIcon(ImageManagerImpl.instance.getTokenIconImage(), "Token Permanent"); - } - - displayTitleAnyway = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_CARD_NAMES, "true").equals("true"); - displayFullImagePath = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_FULL_IMAGE_PATH, "false").equals("true"); - - // Title Text - titleText = new GlowText(); - setTitle(getGameCard()); - titleText.setForeground(Color.white); - titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); - titleText.setWrap(true); - add(titleText); - - // Full path to image text - fullImageText = new JLabel(); - fullImageText.setText(fullImagePath); - fullImageText.setForeground(Color.BLACK); - add(fullImageText); - - // PT Text - ptPanel = new JPanel(); - ptPanel.setOpaque(false); - ptPanel.setLayout(new BoxLayout(ptPanel, BoxLayout.X_AXIS)); - ptPanel.add(new Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(Integer.MAX_VALUE, 0))); - ptText1 = new GlowText(); - ptText2 = new GlowText(); - ptText3 = new GlowText(); - updatePTTexts(getGameCard()); - ptPanel.add(ptText1); - ptPanel.add(ptText2); - ptPanel.add(ptText3); - // - add(ptPanel); - - // Sickness overlay - BufferedImage sickness = ImageManagerImpl.instance.getSicknessImage(); - setOverlayPanel(new ImagePanel(sickness, ImagePanelStyle.SCALED)); - getOverlayPanel().setOpaque(false); - add(getOverlayPanel()); - - // Imagel panel - imagePanel = new ScaledImagePanel(); - if (DebugUtil.GUI_RENDER_IMAGE_DRAW_IMAGE_BORDER) { - imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); - } - add(imagePanel); - - // Do we need to load? - if (loadImage) { - initialDraw(); - } else { - // Nothing to do - } - } - - private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) { - setTypeIconPanel(new JPanel()); - getTypeIconPanel().setLayout(null); - getTypeIconPanel().setOpaque(false); - add(getTypeIconPanel()); - - typeIconButton = new JButton(""); - typeIconButton.setLocation(2, 2); - typeIconButton.setSize(25, 25); - - getTypeIconPanel().setVisible(true); - typeIconButton.setIcon(new ImageIcon(bufferedImage)); - if (toolTipText != null) { - typeIconButton.setToolTipText(toolTipText); - } - getTypeIconPanel().add(typeIconButton); - } - - @Override - public void cleanUp() { - super.cleanUp(); - this.setCounterPanel(null); - } - - private void setTitle(CardView card) { - titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getDisplayName()); - } - - private void setImage(BufferedImage srcImage) { - synchronized (imagePanel) { - if (srcImage != null) { - imagePanel.setImage(srcImage); - } else { - imagePanel.clearImage(); - } - repaint(); - } - doLayout(); - } - - private void setFullPath(String fullImagePath) { - this.fullImagePath = fullImagePath; - this.fullImagePath = this.fullImagePath.replaceAll("\\\\", "\\\\
"); - this.fullImagePath = this.fullImagePath.replaceAll("/", "/
"); - this.fullImagePath = "" + this.fullImagePath + ""; - fullImageText.setText(!displayFullImagePath ? "" : this.fullImagePath); - doLayout(); - } - - @Override - public void transferResources(final CardPanel panelAbstract) { - if (panelAbstract instanceof CardPanelRenderModeImage) { - CardPanelRenderModeImage panel = (CardPanelRenderModeImage) panelAbstract; - synchronized (panel.imagePanel) { - if (panel.imagePanel.hasImage()) { - setImage(panel.imagePanel.getSrcImage()); - } - } - } - } - - @Override - public void setSelected(boolean isSelected) { - super.setSelected(isSelected); - if (isSelected) { - this.titleText.setGlowColor(Color.green); - } else { - this.titleText.setGlowColor(Color.black); - } - } - - @Override - protected void paintCard(Graphics2D g2d) { - float alpha = getAlpha(); - if (alpha != 1.0f) { - AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha); - g2d.setComposite(composite); - } - - // draw background (selected/chooseable/playable) - MageCardLocation cardLocation = getCardLocation(); - g2d.drawImage( - IMAGE_CACHE.getOrThrow( - new Key(getInsets(), - cardLocation.getCardWidth(), cardLocation.getCardHeight(), - cardLocation.getCardWidth(), cardLocation.getCardHeight(), - 0, - 0, - hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack(), - getGameCard().isCanBlock())), - 0, 0, cardLocation.getCardWidth(), cardLocation.getCardHeight(), null); - g2d.dispose(); + static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage) { + // cards without images show icons and text always + // TODO: apply "card names on card" setting to icon too? + // TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200) + return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage); } private static BufferedImage createImage(Key key) { @@ -501,62 +232,125 @@ public class CardPanelRenderModeImage extends CardPanel { return image; } - @Override - protected void paintChildren(Graphics g) { - super.paintChildren(g); + private static ImageIcon getCounterImageWithAmount(int amount, BufferedImage image, int cardWidth) { + int factor = cardWidth > WIDTH_LIMIT ? 2 : 1; + int xOffset = amount > 9 ? 2 : 5; + int fontSize = factor == 1 ? amount < 10 ? 12 : amount < 100 ? 10 : amount < 1000 ? 7 : 6 + : amount < 10 ? 19 : amount < 100 ? 15 : amount < 1000 ? 12 : amount < 10000 ? 9 : 8; + BufferedImage newImage; + if (cardWidth > WIDTH_LIMIT) { + newImage = ImageManagerImpl.deepCopy(image); + } else { + newImage = ImageHelper.getResizedImage(image, 20, 20); + } + Graphics graphics = newImage.getGraphics(); + graphics.setColor(Color.BLACK); + graphics.setFont(new Font("Arial Black", amount > 100 ? Font.PLAIN : Font.BOLD, fontSize)); + graphics.drawString(Integer.toString(amount), xOffset * factor, 11 * factor); + return new ImageIcon(newImage); + } - /* - // debug draw recs + public CardPanelRenderModeImage(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { + // Call to super + super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender); - // full card - g.setColor(new Color(255, 0, 0)); - g.drawRect(realCard.rectFull.x, realCard.rectFull.y, realCard.rectFull.width, realCard.rectFull.height); + // Counter panel + if (!newGameCard.isAbility()) { + // panel to show counters on the card + counterPanel = new JPanel(); + counterPanel.setLayout(null); + counterPanel.setOpaque(false); + add(counterPanel); - // real card - image - g.setColor(new Color(0, 0, 255)); - g.drawRect(imagePanel.getX(), imagePanel.getY(), imagePanel.getBounds().width, imagePanel.getBounds().height); + plusCounterLabel = new JLabel(""); + plusCounterLabel.setToolTipText("+1/+1"); + counterPanel.add(plusCounterLabel); - // caption - g.setColor(new Color(0, 255, 255)); - g.drawRect(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); + minusCounterLabel = new JLabel(""); + minusCounterLabel.setToolTipText("-1/-1"); + counterPanel.add(minusCounterLabel); - // life points - g.setColor(new Color(120, 0, 120)); - g.drawRect(ptText.getX(), ptText.getY(), ptText.getBounds().width, ptText.getBounds().height); - //*/ + loyaltyCounterLabel = new JLabel(""); + loyaltyCounterLabel.setToolTipText("loyalty"); + counterPanel.add(loyaltyCounterLabel); - if (getShowCastingCost() && !isAnimationPanel() && canShowCardIcons(getCardWidth(), hasImage)) { + otherCounterLabel = new JLabel(""); + counterPanel.add(otherCounterLabel); - int symbolMarginX = 2; // 2 px between icons + counterPanel.setVisible(false); + } - String manaCost = ManaSymbols.getClearManaCost(getGameCard().getManaCostStr()); - int manaWidth = getManaWidth(manaCost, symbolMarginX); + // Ability icon + if (newGameCard.isAbility()) { + if (newGameCard.getAbilityType() == AbilityType.TRIGGERED) { + setTypeIcon(ImageManagerImpl.instance.getTriggeredAbilityImage(), "Triggered Ability"); + } else if (newGameCard.getAbilityType() == AbilityType.ACTIVATED) { + setTypeIcon(ImageManagerImpl.instance.getActivatedAbilityImage(), "Activated Ability"); + } + } - // right top corner with margin (sizes from any sample card, length from black border to mana icon) - int manaMarginRight = Math.round(22f / 672f * getCardWidth()); - int manaMarginTop = Math.round(24f / 936f * getCardHeight()); + // Token icon + if (this.getGameCard().isToken()) { + setTypeIcon(ImageManagerImpl.instance.getTokenIconImage(), "Token Permanent"); + } - int cardOffsetX = 0; - int cardOffsetY = 0; - int manaX = cardOffsetX + getCardWidth() - manaMarginRight - manaWidth; - int manaY = cardOffsetY + manaMarginTop; + displayTitleAnyway = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_CARD_NAMES, "true").equals("true"); + displayFullImagePath = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_FULL_IMAGE_PATH, "false").equals("true"); - ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), ModernCardRenderer.MANA_ICONS_TEXT_COLOR, symbolMarginX); + // Title Text + titleText = new GlowText(); + setTitle(getGameCard()); + titleText.setForeground(Color.white); + titleText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + titleText.setWrap(true); + add(titleText); + + // Full path to image text + fullImageText = new JLabel(); + fullImageText.setText(fullImagePath); + fullImageText.setForeground(Color.BLACK); + add(fullImageText); + + // PT Text + ptPanel = new JPanel(); + ptPanel.setOpaque(false); + ptPanel.setLayout(new BoxLayout(ptPanel, BoxLayout.X_AXIS)); + ptPanel.add(new Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(Integer.MAX_VALUE, 0))); + ptText1 = new GlowText(); + ptText2 = new GlowText(); + ptText3 = new GlowText(); + updatePTTexts(getGameCard()); + ptPanel.add(ptText1); + ptPanel.add(ptText2); + ptPanel.add(ptText3); + // + add(ptPanel); + + // Sickness overlay + BufferedImage sickness = ImageManagerImpl.instance.getSicknessImage(); + overlayPanel = new ImagePanel(sickness, ImagePanelStyle.SCALED); + overlayPanel.setOpaque(false); + add(overlayPanel); + + // Imagel panel + imagePanel = new ScaledImagePanel(); + if (DebugUtil.GUI_RENDER_IMAGE_DRAW_IMAGE_BORDER) { + imagePanel.setBorder(BorderFactory.createLineBorder(Color.white)); + } + add(imagePanel); + + // Do we need to load? + if (loadImage) { + initialDraw(); + } else { + // Nothing to do } } - private int getManaWidth(String manaCost, int symbolMarginX) { - int width = 0; - manaCost = manaCost.replace("\\", ""); - StringTokenizer tok = new StringTokenizer(manaCost, " "); - while (tok.hasMoreTokens()) { - tok.nextToken(); - if (width != 0) { - width += symbolMarginX; - } - width += getSymbolWidth(); - } - return width; + @Override + public void cleanUp() { + super.cleanUp(); + counterPanel = null; } @Override @@ -588,32 +382,32 @@ public class CardPanelRenderModeImage extends CardPanel { imagePanel.setSize(realCardSize.width, realCardSize.height); if (hasSickness() && getGameCard().isCreature() && isPermanent()) { - getOverlayPanel().setLocation(realCardSize.x, realCardSize.y); - getOverlayPanel().setSize(realCardSize.width, realCardSize.height); + overlayPanel.setLocation(realCardSize.x, realCardSize.y); + overlayPanel.setSize(realCardSize.width, realCardSize.height); } else { - getOverlayPanel().setVisible(false); + overlayPanel.setVisible(false); } - if (getTypeIconPanel() != null) { - getTypeIconPanel().setLocation(realCardSize.x, realCardSize.y); - getTypeIconPanel().setSize(realCardSize.width, realCardSize.height); + if (typeIconPanel != null) { + typeIconPanel.setLocation(realCardSize.x, realCardSize.y); + typeIconPanel.setSize(realCardSize.width, realCardSize.height); } - if (getCounterPanel() != null) { - getCounterPanel().setLocation(realCardSize.x, realCardSize.y); - getCounterPanel().setSize(realCardSize.width, realCardSize.height); + if (counterPanel != null) { + counterPanel.setLocation(realCardSize.x, realCardSize.y); + counterPanel.setSize(realCardSize.width, realCardSize.height); int size = cardWidth > WIDTH_LIMIT ? 40 : 20; - minusCounterLabel.setLocation(getCounterPanel().getWidth() - size, getCounterPanel().getHeight() - size * 2); + minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2); minusCounterLabel.setSize(size, size); - plusCounterLabel.setLocation(5, getCounterPanel().getHeight() - size * 2); + plusCounterLabel.setLocation(5, counterPanel.getHeight() - size * 2); plusCounterLabel.setSize(size, size); - loyaltyCounterLabel.setLocation(getCounterPanel().getWidth() - size, getCounterPanel().getHeight() - size); + loyaltyCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size); loyaltyCounterLabel.setSize(size, size); - otherCounterLabel.setLocation(5, getCounterPanel().getHeight() - size); + otherCounterLabel.setLocation(5, counterPanel.getHeight() - size); otherCounterLabel.setSize(size, size); } @@ -668,41 +462,83 @@ public class CardPanelRenderModeImage extends CardPanel { } } - private void prepareGlowFont(GlowText label, int fontSize, MageInt value, boolean drawAsDamaged) { - label.setFont(getFont().deriveFont(Font.BOLD, fontSize)); - label.setForeground(CardRendererUtils.getCardTextColor(value, drawAsDamaged, titleText.getForeground(), true)); - Dimension ptSize = label.getPreferredSize(); - label.setSize(ptSize.width, ptSize.height); - } - - private void updatePTTexts(CardView card) { - if (card.isCreature() || card.getSubTypes().contains(SubType.VEHICLE)) { - ptText1.setText(getGameCard().getPower()); - ptText2.setText("/"); - ptText3.setText(CardRendererUtils.getCardLifeWithDamage(getGameCard())); - } else if (card.isPlanesWalker()) { - ptText1.setText(""); - ptText2.setText(""); - ptText3.setText(getGameCard().getLoyalty()); - } else { - ptText1.setText(""); - ptText2.setText(""); - ptText3.setText(""); + @Override + public Image getImage() { + if (this.hasImage) { + if (getGameCard().isFaceDown()) { + return getFaceDownImage(); + } else { + return ImageCache.getImageOriginal(getGameCard()); + } } - - ptText1.setForeground(Color.white); - ptText1.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); - - ptText2.setForeground(Color.white); - ptText2.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); - - ptText3.setForeground(Color.white); - ptText3.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + return null; } @Override - public String toString() { - return getGameCard().toString(); + protected void paintCard(Graphics2D g2d) { + float alpha = getAlpha(); + if (alpha != 1.0f) { + AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha); + g2d.setComposite(composite); + } + + // draw background (selected/chooseable/playable) + MageCardLocation cardLocation = getCardLocation(); + g2d.drawImage( + IMAGE_CACHE.getOrThrow( + new Key(getInsets(), + cardLocation.getCardWidth(), cardLocation.getCardHeight(), + cardLocation.getCardWidth(), cardLocation.getCardHeight(), + 0, + 0, + hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack(), + getGameCard().isCanBlock())), + 0, 0, cardLocation.getCardWidth(), cardLocation.getCardHeight(), null); + g2d.dispose(); + } + + @Override + protected void paintChildren(Graphics g) { + super.paintChildren(g); + + /* + // debug draw recs + + // full card + g.setColor(new Color(255, 0, 0)); + g.drawRect(realCard.rectFull.x, realCard.rectFull.y, realCard.rectFull.width, realCard.rectFull.height); + + // real card - image + g.setColor(new Color(0, 0, 255)); + g.drawRect(imagePanel.getX(), imagePanel.getY(), imagePanel.getBounds().width, imagePanel.getBounds().height); + + // caption + g.setColor(new Color(0, 255, 255)); + g.drawRect(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); + + // life points + g.setColor(new Color(120, 0, 120)); + g.drawRect(ptText.getX(), ptText.getY(), ptText.getBounds().width, ptText.getBounds().height); + //*/ + + if (getShowCastingCost() && !isAnimationPanel() && canShowCardIcons(getCardWidth(), hasImage)) { + + int symbolMarginX = 2; // 2 px between icons + + String manaCost = ManaSymbols.getClearManaCost(getGameCard().getManaCostStr()); + int manaWidth = getManaWidth(manaCost, symbolMarginX); + + // right top corner with margin (sizes from any sample card, length from black border to mana icon) + int manaMarginRight = Math.round(22f / 672f * getCardWidth()); + int manaMarginTop = Math.round(24f / 936f * getCardHeight()); + + int cardOffsetX = 0; + int cardOffsetY = 0; + int manaX = cardOffsetX + getCardWidth() - manaMarginRight - manaWidth; + int manaY = cardOffsetY + manaMarginTop; + + ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth(), ModernCardRenderer.MANA_ICONS_TEXT_COLOR, symbolMarginX); + } } @Override @@ -734,9 +570,59 @@ public class CardPanelRenderModeImage extends CardPanel { } } - /////////////////////////////////////////////////////////// - // Image updating code - private int updateArtImageStamp; + @Override + public void setSelected(boolean isSelected) { + super.setSelected(isSelected); + if (isSelected) { + this.titleText.setGlowColor(Color.green); + } else { + this.titleText.setGlowColor(Color.black); + } + } + + @Override + public void showCardTitle() { + displayTitleAnyway = true; + setTitle(getGameCard()); + } + + @Override + public String toString() { + return getGameCard().toString(); + } + + @Override + public void transferResources(final CardPanel panelAbstract) { + if (panelAbstract instanceof CardPanelRenderModeImage) { + CardPanelRenderModeImage panel = (CardPanelRenderModeImage) panelAbstract; + synchronized (panel.imagePanel) { + if (panel.imagePanel.hasImage()) { + setImage(panel.imagePanel.getSrcImage()); + } + } + } + } + + @Override + public void update(CardView card) { + // Super + super.update(card); + + // real card to show stores in getGameCard (e.g. after user clicks on night icon -- night card must be rendered) + updatePTTexts(getGameCard()); + setTitle(getGameCard()); + + // Summoning Sickness overlay + overlayPanel.setVisible(hasSickness() && getGameCard().isCreature() && isPermanent()); + + // Update counters panel + if (counterPanel != null) { + updateCounters(card); + } + + // Finally, queue a repaint + repaint(); + } @Override public void updateArtImage() { @@ -787,31 +673,68 @@ public class CardPanelRenderModeImage extends CardPanel { } } - @Override - public void showCardTitle() { - displayTitleAnyway = true; - setTitle(getGameCard()); + private int getManaWidth(String manaCost, int symbolMarginX) { + int width = 0; + manaCost = manaCost.replace("\\", ""); + StringTokenizer tok = new StringTokenizer(manaCost, " "); + while (tok.hasMoreTokens()) { + tok.nextToken(); + if (width != 0) { + width += symbolMarginX; + } + width += getSymbolWidth(); + } + return width; } - @Override - public void update(CardView card) { - // Super - super.update(card); + private void prepareGlowFont(GlowText label, int fontSize, MageInt value, boolean drawAsDamaged) { + label.setFont(getFont().deriveFont(Font.BOLD, fontSize)); + label.setForeground(CardRendererUtils.getCardTextColor(value, drawAsDamaged, titleText.getForeground(), true)); + Dimension ptSize = label.getPreferredSize(); + label.setSize(ptSize.width, ptSize.height); + } - // real card to show stores in getGameCard (e.g. after user clicks on night icon -- night card must be rendered) - updatePTTexts(getGameCard()); - setTitle(getGameCard()); + private void setFullPath(String fullImagePath) { + this.fullImagePath = fullImagePath; + this.fullImagePath = this.fullImagePath.replaceAll("\\\\", "\\\\
"); + this.fullImagePath = this.fullImagePath.replaceAll("/", "/
"); + this.fullImagePath = "" + this.fullImagePath + ""; + fullImageText.setText(!displayFullImagePath ? "" : this.fullImagePath); + doLayout(); + } - // Summoning Sickness overlay - getOverlayPanel().setVisible(hasSickness() && getGameCard().isCreature() && isPermanent()); - - // Update counters panel - if (getCounterPanel() != null) { - updateCounters(card); + private void setImage(BufferedImage srcImage) { + synchronized (imagePanel) { + if (srcImage != null) { + imagePanel.setImage(srcImage); + } else { + imagePanel.clearImage(); + } + repaint(); } + doLayout(); + } - // Finally, queue a repaint - repaint(); + private void setTitle(CardView card) { + titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getDisplayName()); + } + + private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) { + typeIconPanel = new JPanel(); + typeIconPanel.setLayout(null); + typeIconPanel.setOpaque(false); + add(typeIconPanel); + + typeIconButton = new JButton(""); + typeIconButton.setLocation(2, 2); + typeIconButton.setSize(25, 25); + + typeIconPanel.setVisible(true); + typeIconButton.setIcon(new ImageIcon(bufferedImage)); + if (toolTipText != null) { + typeIconButton.setToolTipText(toolTipText); + } + typeIconPanel.add(typeIconButton); } private void updateCounters(CardView card) { @@ -865,43 +788,38 @@ public class CardPanelRenderModeImage extends CardPanel { } } - getCounterPanel().setVisible(true); + counterPanel.setVisible(true); } else { plusCounterLabel.setVisible(false); minusCounterLabel.setVisible(false); loyaltyCounterLabel.setVisible(false); otherCounterLabel.setVisible(false); - getCounterPanel().setVisible(false); + counterPanel.setVisible(false); } } - private static ImageIcon getCounterImageWithAmount(int amount, BufferedImage image, int cardWidth) { - int factor = cardWidth > WIDTH_LIMIT ? 2 : 1; - int xOffset = amount > 9 ? 2 : 5; - int fontSize = factor == 1 ? amount < 10 ? 12 : amount < 100 ? 10 : amount < 1000 ? 7 : 6 - : amount < 10 ? 19 : amount < 100 ? 15 : amount < 1000 ? 12 : amount < 10000 ? 9 : 8; - BufferedImage newImage; - if (cardWidth > WIDTH_LIMIT) { - newImage = ImageManagerImpl.deepCopy(image); + private void updatePTTexts(CardView card) { + if (card.isCreature() || card.getSubTypes().contains(SubType.VEHICLE)) { + ptText1.setText(getGameCard().getPower()); + ptText2.setText("/"); + ptText3.setText(CardRendererUtils.getCardLifeWithDamage(getGameCard())); + } else if (card.isPlanesWalker()) { + ptText1.setText(""); + ptText2.setText(""); + ptText3.setText(getGameCard().getLoyalty()); } else { - newImage = ImageHelper.getResizedImage(image, 20, 20); + ptText1.setText(""); + ptText2.setText(""); + ptText3.setText(""); } - Graphics graphics = newImage.getGraphics(); - graphics.setColor(Color.BLACK); - graphics.setFont(new Font("Arial Black", amount > 100 ? Font.PLAIN : Font.BOLD, fontSize)); - graphics.drawString(Integer.toString(amount), xOffset * factor, 11 * factor); - return new ImageIcon(newImage); - } - @Override - public Image getImage() { - if (this.hasImage) { - if (getGameCard().isFaceDown()) { - return getFaceDownImage(); - } else { - return ImageCache.getImageOriginal(getGameCard()); - } - } - return null; + ptText1.setForeground(Color.white); + ptText1.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + + ptText2.setForeground(Color.white); + ptText2.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); + + ptText3.setForeground(Color.white); + ptText3.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java index 5c9359a9cf..a5811a367a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderModeMTGO.java @@ -16,6 +16,7 @@ import org.mage.plugins.card.images.ImageCache; import java.awt.*; import java.awt.image.BufferedImage; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -25,94 +26,67 @@ import java.util.concurrent.TimeUnit; */ public class CardPanelRenderModeMTGO extends CardPanel { - private static boolean cardViewEquals(CardView a, CardView b) { + // Map of generated images + private final static Cache IMAGE_CACHE = CacheBuilder + .newBuilder() + .maximumSize(3000) + .expireAfterAccess(60, TimeUnit.MINUTES) + .softValues() + .build(); + + // The art image for the card, loaded in from the disk + private BufferedImage artImage; + + // The faceart image for the card, loaded in from the disk (based on artid from mtgo) + private BufferedImage faceArtImage; + + // Factory to generate card appropriate views + private final CardRendererFactory cardRendererFactory = new CardRendererFactory(); + + // The rendered card image, with or without the art image loaded yet + // = null while invalid + private BufferedImage cardImage; + private CardRenderer cardRenderer; + + private int updateArtImageStamp; + + private static boolean cardViewEquals(CardView a, CardView b) { // TODO: This belongs in CardView if (a == b) { return true; } - if (a.getClass() != b.getClass()) { + if (a == null || b == null || a.getClass() != b.getClass()) { return false; } - if (!a.getDisplayName().equals(b.getDisplayName())) { + + if (!(a.getDisplayName().equals(b.getDisplayName()) // TODO: Original code not checking everything. Why is it only checking these values? + && a.getPower().equals(b.getPower()) + && a.getToughness().equals(b.getToughness()) + && a.getLoyalty().equals(b.getLoyalty()) + && 0 == a.getColor().compareTo(b.getColor()) + && a.getCardTypes().equals(b.getCardTypes()) + && a.getSubTypes().equals(b.getSubTypes()) + && a.getSuperTypes().equals(b.getSuperTypes()) + && a.getManaCostStr().equals(b.getManaCostStr()) + && a.getRules().equals(b.getRules()) + && Objects.equals(a.getRarity(), b.getRarity()) + && Objects.equals(a.getCardNumber(), b.getCardNumber()) + && Objects.equals(a.getExpansionSetCode(), b.getExpansionSetCode()) + && a.getFrameStyle() == b.getFrameStyle() + && Objects.equals(a.getCounters(), b.getCounters()) + && a.isFaceDown() == b.isFaceDown())) { return false; } - if (!a.getPower().equals(b.getPower())) { - return false; + + if (!(a instanceof PermanentView)) { + return true; } - if (!a.getToughness().equals(b.getToughness())) { - return false; - } - if (!a.getLoyalty().equals(b.getLoyalty())) { - return false; - } - if (0 != a.getColor().compareTo(b.getColor())) { - return false; - } - if (!a.getCardTypes().equals(b.getCardTypes())) { - return false; - } - if (!a.getSubTypes().equals(b.getSubTypes())) { - return false; - } - if (!a.getSuperTypes().equals(b.getSuperTypes())) { - return false; - } - if (!a.getManaCostStr().equals(b.getManaCostStr())) { - return false; - } - if (!a.getRules().equals(b.getRules())) { - return false; - } - if (a.getRarity() == null || b.getRarity() == null) { - return false; - } - if (a.getRarity() != b.getRarity()) { - return false; - } - if (a.getCardNumber() != null && !a.getCardNumber().equals(b.getCardNumber())) { - return false; - } - // Expansion set code, with null checking: - // TODO: The null checks should not be necessary, but thanks to Issue #2260 - // some tokens / commandobjects will be missing expansion set codes. - String expA = a.getExpansionSetCode(); - if (expA == null) { - expA = ""; - } - String expB = b.getExpansionSetCode(); - if (expB == null) { - expB = ""; - } - if (!expA.equals(expB)) { - return false; - } - if (a.getFrameStyle() != b.getFrameStyle()) { - return false; - } - if (a.getCounters() == null) { - if (b.getCounters() != null) { - return false; - } - } else if (!a.getCounters().equals(b.getCounters())) { - return false; - } - if (a.isFaceDown() != b.isFaceDown()) { - return false; - } - if ((a instanceof PermanentView)) { - PermanentView aa = (PermanentView) a; - PermanentView bb = (PermanentView) b; - if (aa.hasSummoningSickness() != bb.hasSummoningSickness()) { - // Note: b must be a permanentview too as we aleady checked that classes - // are the same for a and b - return false; - } - return aa.getDamage() == bb.getDamage(); - } - return true; + PermanentView aa = (PermanentView) a; + PermanentView bb = (PermanentView) b; + return aa.hasSummoningSickness() == bb.hasSummoningSickness() + && aa.getDamage() == bb.getDamage(); } - static class ImageKey { - + private static class ImageKey { final BufferedImage artImage; final int width; final int height; @@ -133,7 +107,7 @@ public class CardPanelRenderModeMTGO extends CardPanel { this.hashCode = hashCodeImpl(); } - private int hashCodeImpl() { + private int hashCodeImpl() { // TODO: Why is this using a string builder??? StringBuilder sb = new StringBuilder(); sb.append((char) (artImage != null ? 1 : 0)); sb.append((char) width); @@ -185,60 +159,23 @@ public class CardPanelRenderModeMTGO extends CardPanel { @Override public boolean equals(Object object) { - // Initial checks if (this == object) { return true; } - if (object == null) { + if (object == null || this.getClass() != object.getClass()) { return false; } - if (!(object instanceof ImageKey)) { - return false; - } - final ImageKey other = (ImageKey) object; + final ImageKey that = (ImageKey) object; - // Compare - if ((artImage == null) == (other.artImage != null)) { - return false; - } - if (width != other.width) { - return false; - } - if (height != other.height) { - return false; - } - if (isChoosable != other.isChoosable) { - return false; - } - if (isSelected != other.isSelected) { - return false; - } - return cardViewEquals(view, other.view); + return (artImage == null) == (that.artImage == null) + && this.width == that.width + && this.height == that.height + && this.isChoosable == that.isChoosable + && this.isSelected == that.isSelected + && cardViewEquals(this.view, that.view); } } - // Map of generated images - private final static Cache IMAGE_CACHE = CacheBuilder - .newBuilder() - .maximumSize(3000) - .expireAfterAccess(60, TimeUnit.MINUTES) - .softValues() - .build(); - - // The art image for the card, loaded in from the disk - private BufferedImage artImage; - - // The faceart image for the card, loaded in from the disk (based on artid from mtgo) - private BufferedImage faceArtImage; - - // Factory to generate card appropriate views - private final CardRendererFactory cardRendererFactory = new CardRendererFactory(); - - // The rendered card image, with or without the art image loaded yet - // = null while invalid - private BufferedImage cardImage; - private CardRenderer cardRenderer; - public CardPanelRenderModeMTGO(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension, boolean needFullPermanentRender) { // Call to super @@ -252,16 +189,14 @@ public class CardPanelRenderModeMTGO extends CardPanel { } @Override - public void transferResources(CardPanel panel) { - if (panel instanceof CardPanelRenderModeMTGO) { - CardPanelRenderModeMTGO impl = (CardPanelRenderModeMTGO) panel; - - // Use the art image and current rendered image from the card - artImage = impl.artImage; - cardRenderer.setArtImage(artImage); - faceArtImage = impl.faceArtImage; - cardRenderer.setFaceArtImage(faceArtImage); - cardImage = impl.cardImage; + public Image getImage() { + if (artImage == null) { + return null; + } + if (getGameCard().isFaceDown()) { + return getFaceDownImage(); + } else { + return ImageCache.getImageOriginal(getGameCard()); } } @@ -288,38 +223,72 @@ public class CardPanelRenderModeMTGO extends CardPanel { g.drawImage(cardImage, cardOffsetX, cardOffsetY, null); } - /** - * Render the card to a new BufferedImage at it's current dimensions - * - * @return image - */ - private BufferedImage renderCard() { - int cardWidth = getCardWidth(); - int cardHeight = getCardHeight(); + @Override + public void setCardBounds(int x, int y, int cardWidth, int cardHeight) { + int oldCardWidth = getCardWidth(); + int oldCardHeight = getCardHeight(); - // Create image to render to - BufferedImage image - = GraphicsUtilities.createCompatibleTranslucentImage(cardWidth, cardHeight); - Graphics2D g2d = image.createGraphics(); + super.setCardBounds(x, y, cardWidth, cardHeight); - // Render with Antialialsing - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - - // Draw card itself - cardRenderer.draw(g2d, getAttributes(), image); - - // Done - g2d.dispose(); - return image; + // Rerender if card size changed + if (getCardWidth() != oldCardWidth || getCardHeight() != oldCardHeight) { + cardImage = null; + } } - private CardPanelAttributes getAttributes() { - return new CardPanelAttributes(getCardWidth(), getCardHeight(), isChoosable(), isSelected(), isTransformed()); + @Override + public void setChoosable(boolean choosable) { + if (choosable != isChoosable()) { + super.setChoosable(choosable); + // Invalidate our render and trigger a repaint + cardImage = null; + repaint(); + } } - private int updateArtImageStamp; + @Override + public void setSelected(boolean selected) { + if (selected != isSelected()) { + super.setSelected(selected); + // Invalidate our render and trigger a repaint + cardImage = null; + repaint(); + } + } + + @Override + public void showCardTitle() { + // Nothing to do, rendered cards always have a title + } + + @Override + public void transferResources(CardPanel panel) { + if (panel instanceof CardPanelRenderModeMTGO) { + CardPanelRenderModeMTGO impl = (CardPanelRenderModeMTGO) panel; + + // Use the art image and current rendered image from the card + artImage = impl.artImage; + cardRenderer.setArtImage(artImage); + faceArtImage = impl.faceArtImage; + cardRenderer.setFaceArtImage(faceArtImage); + cardImage = impl.cardImage; + } + } + + @Override + public void update(CardView card) { + // Update super + super.update(card); + + // Update renderer + cardImage = null; + cardRenderer = cardRendererFactory.create(getGameCard()); + cardRenderer.setArtImage(artImage); + cardRenderer.setFaceArtImage(faceArtImage); + + // Repaint + repaint(); + } @Override public void updateArtImage() { @@ -379,32 +348,8 @@ public class CardPanelRenderModeMTGO extends CardPanel { } } - @Override - public void update(CardView card) { - // Update super - super.update(card); - - // Update renderer - cardImage = null; - cardRenderer = cardRendererFactory.create(getGameCard()); - cardRenderer.setArtImage(artImage); - cardRenderer.setFaceArtImage(faceArtImage); - - // Repaint - repaint(); - } - - @Override - public void setCardBounds(int x, int y, int cardWidth, int cardHeight) { - int oldCardWidth = getCardWidth(); - int oldCardHeight = getCardHeight(); - - super.setCardBounds(x, y, cardWidth, cardHeight); - - // Rerender if card size changed - if (getCardWidth() != oldCardWidth || getCardHeight() != oldCardHeight) { - cardImage = null; - } + private CardPanelAttributes getAttributes() { + return new CardPanelAttributes(getCardWidth(), getCardHeight(), isChoosable(), isSelected(), isTransformed()); } private BufferedImage getFaceDownImage() { @@ -422,40 +367,30 @@ public class CardPanelRenderModeMTGO extends CardPanel { } } - @Override - public void setSelected(boolean selected) { - if (selected != isSelected()) { - super.setSelected(selected); - // Invalidate our render and trigger a repaint - cardImage = null; - repaint(); - } - } + /** + * Render the card to a new BufferedImage at it's current dimensions + * + * @return image + */ + private BufferedImage renderCard() { + int cardWidth = getCardWidth(); + int cardHeight = getCardHeight(); - @Override - public void setChoosable(boolean choosable) { - if (choosable != isChoosable()) { - super.setChoosable(choosable); - // Invalidate our render and trigger a repaint - cardImage = null; - repaint(); - } - } + // Create image to render to + BufferedImage image + = GraphicsUtilities.createCompatibleTranslucentImage(cardWidth, cardHeight); + Graphics2D g2d = image.createGraphics(); - @Override - public Image getImage() { - if (artImage != null) { - if (getGameCard().isFaceDown()) { - return getFaceDownImage(); - } else { - return ImageCache.getImageOriginal(getGameCard()); - } - } - return null; - } + // Render with Antialialsing + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - @Override - public void showCardTitle() { - // Nothing to do, rendered cards always have a title + // Draw card itself + cardRenderer.draw(g2d, getAttributes(), image); + + // Done + g2d.dispose(); + return image; } }