From f81489c37778afb985a1a954a83ccac284865f6a Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 18 Nov 2017 05:13:38 +0400 Subject: [PATCH] Add fallback card render (origin card render), see #4168 --- .gitignore | 2 +- .../src/main/java/mage/client/cards/Card.java | 2 +- .../main/java/mage/client/cards/CardArea.java | 17 +- .../java/mage/client/cards/DragCardGrid.java | 2 +- .../collection/viewer/MageBook.java | 3 + .../java/org/mage/card/arcane/CardPanel.java | 10 +- .../card/arcane/CardPanelComponentImpl.java | 181 ++++++++++++++---- .../mage/card/arcane/CardPanelRenderImpl.java | 2 +- .../mage/plugins/card/images/ImageCache.java | 77 +++++++- .../src/main/java/mage/cards/MageCard.java | 4 +- 10 files changed, 235 insertions(+), 65 deletions(-) diff --git a/.gitignore b/.gitignore index d084fe143d0..61d6b76f856 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ syntax: glob Mage.Client/*.dck Mage.Client/db Mage.Client/gamelogs -Mage.Client/mageclient.log +Mage.Client/*.log Mage.Client/plugins/images Mage.Client/plugins/plugin.data Mage.Client/target diff --git a/Mage.Client/src/main/java/mage/client/cards/Card.java b/Mage.Client/src/main/java/mage/client/cards/Card.java index e984b305920..cb90ad56f40 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Card.java +++ b/Mage.Client/src/main/java/mage/client/cards/Card.java @@ -562,7 +562,7 @@ public class Card extends MagePermanent implements MouseMotionListener, MouseLis } @Override - public void setTextOffset(int yOffset) { + public void setCardCaptionTopOffset(int yOffsetPercent) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } diff --git a/Mage.Client/src/main/java/mage/client/cards/CardArea.java b/Mage.Client/src/main/java/mage/client/cards/CardArea.java index 5fa3840253e..cb63d2e6ea9 100644 --- a/Mage.Client/src/main/java/mage/client/cards/CardArea.java +++ b/Mage.Client/src/main/java/mage/client/cards/CardArea.java @@ -53,7 +53,7 @@ public class CardArea extends JPanel implements MouseListener { private boolean reloaded = false; private final javax.swing.JLayeredPane cardArea; private final javax.swing.JScrollPane scrollPane; - private int yTextOffset; + private int yCardCaptionOffsetPercent = 0; // card caption offset (use for moving card caption view center, below mana icons -- for more good UI) private Dimension cardDimension; private int verticalCardOffset; @@ -68,8 +68,6 @@ public class CardArea extends JPanel implements MouseListener { setGUISize(); cardArea = new JLayeredPane(); scrollPane.setViewportView(cardArea); - yTextOffset = 10; - } public void cleanUp() { @@ -103,10 +101,10 @@ public class CardArea extends JPanel implements MouseListener { this.reloaded = true; cardArea.removeAll(); if (showCards != null && showCards.size() < 10) { - yTextOffset = 10; + yCardCaptionOffsetPercent = 8; // TODO: need to test loadCardsFew(showCards, bigCard, gameId); } else { - yTextOffset = 0; + yCardCaptionOffsetPercent = 0; loadCardsMany(showCards, bigCard, gameId); } cardArea.revalidate(); @@ -118,8 +116,10 @@ public class CardArea extends JPanel implements MouseListener { public void loadCardsNarrow(CardsView showCards, BigCard bigCard, UUID gameId) { this.reloaded = true; cardArea.removeAll(); - yTextOffset = 0; + + yCardCaptionOffsetPercent = 0; // TODO: need to test loadCardsMany(showCards, bigCard, gameId); + cardArea.revalidate(); this.revalidate(); @@ -152,7 +152,10 @@ public class CardArea extends JPanel implements MouseListener { cardArea.moveToFront(cardPanel); cardPanel.update(card); cardPanel.setCardBounds(rectangle.x, rectangle.y, cardDimension.width, cardDimension.height); - cardPanel.setTextOffset(yTextOffset); + + // new card have same settings as current view + cardPanel.setCardCaptionTopOffset(yCardCaptionOffsetPercent); + cardPanel.showCardTitle(); } diff --git a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java index c93f0fecb9b..f96fc2f863b 100644 --- a/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java +++ b/Mage.Client/src/main/java/mage/client/cards/DragCardGrid.java @@ -1726,7 +1726,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg // Create the card view final MageCard cardPanel = Plugins.instance.getMageCard(card, lastBigCard, new Dimension(getCardWidth(), getCardHeight()), null, true, true); cardPanel.update(card); - cardPanel.setTextOffset(0); + cardPanel.setCardCaptionTopOffset(0); // Remove mouse wheel listeners so that scrolling works // Scrolling works on all areas without cards or by using the scroll bar, that's enough diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java index 3e27b580ae5..2488110b58a 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/collection/viewer/MageBook.java @@ -236,6 +236,7 @@ public class MageBook extends JComponent { for (int i = 0; i < Math.min(conf.CARDS_PER_PAGE / 2, size); i++) { Card card = cards.get(i).getMockCard(); addCard(new CardView(card), bigCard, null, rectangle); + rectangle = CardPosition.translatePosition(i, rectangle, conf); } @@ -341,6 +342,8 @@ public class MageBook extends JComponent { cardImg.update(card); cardImg.setCardBounds(rectangle.x, rectangle.y, cardDimensions.frameWidth, cardDimensions.frameHeight); + cardImg.setCardCaptionTopOffset(8); // card caption below real card caption to see full name even with mana icons + boolean implemented = card.getRarity() != Rarity.NA; GlowText label = new GlowText(); diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java index 4bf8218156f..b05a03ea261 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java @@ -91,7 +91,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, private JPanel cardArea; - private int yTextOffset = 10; + private int yCardCaptionOffsetPercent = 0; // card caption offset (use for moving card caption view center, below mana icons -- for more good UI) // if this is set, it's opened if the user right clicks on the card panel private JPopupMenu popupMenu; @@ -819,12 +819,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener, } @Override - public void setTextOffset(int yOffset) { - yTextOffset = yOffset; + public void setCardCaptionTopOffset(int yOffsetPercent) { + yCardCaptionOffsetPercent = yOffsetPercent; } - public int getTextOffset() { - return yTextOffset; + public int getCardCaptionTopOffset() { + return yCardCaptionOffsetPercent; } @Override diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java index c57a41f0430..abe3770ffb3 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java @@ -34,7 +34,7 @@ import static org.mage.plugins.card.constants.Constants.THUMBNAIL_SIZE_FULL; * Class for drawing the mage card object by using a form based JComponent * approach * - * @author arcane, nantuko, noxx, stravant + * @author arcane, nantuko, noxx, stravant, JayDi85 */ @SuppressWarnings({"unchecked", "rawtypes"}) public class CardPanelComponentImpl extends CardPanel { @@ -47,9 +47,14 @@ public class CardPanelComponentImpl extends CardPanel { private static final float ROUNDED_CORNER_SIZE = 0.1f; private static final float BLACK_BORDER_SIZE = 0.03f; + private static final float SELECTION_BORDER_SIZE = 0.03f; private static final int TEXT_GLOW_SIZE = 6; private static final float TEXT_GLOW_INTENSITY = 3f; + // size to show icons and text (help to see full size card without text) + private static final int CARD_MIN_SIZE_FOR_ICONS = 60; + private static final int CARD_MAX_SIZE_FOR_ICONS = 200; + public final ScaledImagePanel imagePanel; public ImagePanel overlayPanel; @@ -177,6 +182,31 @@ public class CardPanelComponentImpl extends CardPanel { IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap((Function) key -> createImage(key))); } + static private boolean canShowCardIcons(int fullWidth){ + return (fullWidth > 60) && (fullWidth < 200); + } + + private static class CardSizes{ + Rectangle rectFull; + Rectangle rectSelection; + Rectangle rectBorder; + Rectangle rectCard; + + CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight){ + + int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE); + int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE); + int realSelectionSizeX = Math.round(fullWidth * SELECTION_BORDER_SIZE); + int realSelectionSizeY = Math.round(fullWidth * SELECTION_BORDER_SIZE); + + // card full size = select border + black border + real card + rectFull = new Rectangle(offsetX, offsetY, fullWidth, fullHeight); + rectSelection = new Rectangle(rectFull.x, rectFull.y, rectFull.width, rectFull.height); + rectBorder = new Rectangle(rectSelection.x + realSelectionSizeX, rectSelection.y + realSelectionSizeY, rectSelection.width - 2 * realSelectionSizeX, rectSelection.height - 2 * realSelectionSizeY); + rectCard = new Rectangle(rectBorder.x + realBorderSizeX, rectBorder.y + realBorderSizeY, rectBorder.width - 2 * realBorderSizeX, rectBorder.height - 2 * realBorderSizeY); + } + } + public CardPanelComponentImpl(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) { // Call to super super(newGameCard, gameId, loadImage, callback, foil, dimension); @@ -368,32 +398,45 @@ public class CardPanelComponentImpl extends CardPanel { Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - if (!key.hasImage) { - g2d.setColor(new Color(30, 200, 200, 120)); - } else { - g2d.setColor(new Color(0, 0, 0, 0)); - } + // card full size = select border + black border + real card + CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight); - int cornerSize = Math.max(4, Math.round(cardWidth * ROUNDED_CORNER_SIZE)); - g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); + // corners for selection and for border + int cornerSizeSelection = Math.max(4, Math.round(sizes.rectSelection.width * ROUNDED_CORNER_SIZE)); + int cornerSizeBorder = Math.max(4, Math.round(sizes.rectBorder.width * ROUNDED_CORNER_SIZE)); + // DRAW ORDER from big to small: select -> select info -> border -> card + + // draw selection if (key.isSelected) { g2d.setColor(Color.green); - g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); + g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection); } else if (key.isChoosable) { g2d.setColor(new Color(250, 250, 0, 230)); - g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); + g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection); } else if (key.isPlayable) { g2d.setColor(new Color(153, 102, 204, 200)); - //g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); - g2d.fillRoundRect(cardXOffset, cardYOffset, cardWidth, cardHeight, cornerSize, cornerSize); + g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection); } + // draw empty card with border + if (!key.hasImage) { + // gray 1 px border + g2d.setColor(new Color(125, 125, 125, 255)); + g2d.fillRoundRect(sizes.rectBorder.x, sizes.rectBorder.y, sizes.rectBorder.width, sizes.rectBorder.height, cornerSizeBorder, cornerSizeBorder); + // color plate + g2d.setColor(new Color(30, 200, 200, 200)); + g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder); + } + + // draw attack border (inner part of border, not selection -- must always show attack) if (key.canAttack) { g2d.setColor(new Color(0, 0, 255, 230)); - g2d.fillRoundRect(cardXOffset + 1, cardYOffset + 1, cardWidth - 2, cardHeight - 2, cornerSize, cornerSize); + g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder); } + // draw real card by component (see imagePanel and other layout's items) + //TODO:uncomment /* if (gameCard.isAttacking()) { @@ -409,18 +452,54 @@ public class CardPanelComponentImpl extends CardPanel { protected void paintChildren(Graphics g) { super.paintChildren(g); - if (getShowCastingCost() && !isAnimationPanel() && getCardWidth() < 200 && getCardWidth() > 60) { + CardSizes realCard = new CardSizes(getCardXOffset(), getCardYOffset(), getCardWidth(), getCardHeight()); + + /* + // draw recs for debug + + // 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())) { + String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost()); - int width = getWidth(manaCost); + int manaWidth = getManaWidth(manaCost); + + // 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 manaX = getCardXOffset() + getCardWidth() - manaMarginRight - manaWidth; + int manaY = getCardYOffset() + manaMarginTop; + if (hasImage) { - ManaSymbols.draw(g, manaCost, getCardXOffset() + getCardWidth() - width - 5, getCardYOffset() + 5, getSymbolWidth()); + // top right corner if have image like real card + ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth()); } else { - ManaSymbols.draw(g, manaCost, getCardXOffset() + 8, getCardHeight() - 9, getSymbolWidth()); + // old version - bottom left corner if haven't card + // ManaSymbols.draw(g, manaCost, getCardXOffset() + manaMarginRight, getCardYOffset() + getCardHeight() - manaMarginTop - getSymbolWidth(), getSymbolWidth()); + + // new version - like a normal image (it's best to view and construct decks) + ManaSymbols.draw(g, manaCost, manaX, manaY, getSymbolWidth()); } } } - private int getWidth(String manaCost) { + private int getManaWidth(String manaCost) { int width = 0; manaCost = manaCost.replace("\\", ""); StringTokenizer tok = new StringTokenizer(manaCost, " "); @@ -439,24 +518,28 @@ public class CardPanelComponentImpl extends CardPanel { int cardHeight = getCardHeight(); int cardXOffset = getCardXOffset(); int cardYOffset = getCardYOffset(); - int borderSize = Math.round(cardWidth * BLACK_BORDER_SIZE); - imagePanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); - imagePanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + + CardSizes sizes = new CardSizes(cardXOffset, cardYOffset, cardWidth, cardHeight); + + // origin card without selection + Rectangle realCardSize = sizes.rectBorder; + imagePanel.setLocation(realCardSize.x, realCardSize.y); + imagePanel.setSize(realCardSize.width, realCardSize.height); if (hasSickness() && gameCard.isCreature() && isPermanent()) { - overlayPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); - overlayPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + overlayPanel.setLocation(realCardSize.x, realCardSize.y); + overlayPanel.setSize(realCardSize.width, realCardSize.height); } else { overlayPanel.setVisible(false); } if (iconPanel != null) { - iconPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); - iconPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + iconPanel.setLocation(realCardSize.x, realCardSize.y); + iconPanel.setSize(realCardSize.width, realCardSize.height); } if (counterPanel != null) { - counterPanel.setLocation(cardXOffset + borderSize, cardYOffset + borderSize); - counterPanel.setSize(cardWidth - borderSize * 2, cardHeight - borderSize * 2); + counterPanel.setLocation(realCardSize.x, realCardSize.y); + counterPanel.setSize(realCardSize.width, realCardSize.height); int size = cardWidth > WIDTH_LIMIT ? 40 : 20; minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2); @@ -472,32 +555,52 @@ public class CardPanelComponentImpl extends CardPanel { otherCounterLabel.setSize(size, size); } - int fontHeight = Math.round(cardHeight * (27f / 680)); - boolean showText = (!isAnimationPanel() && fontHeight < 12); + + // TITLE + + //old version - text hide on small fonts, why? + //int fontHeight = Math.round(cardHeight * (26f / 672)); + //boolean showText = (!isAnimationPanel() && fontHeight < 12); + + boolean showText = !isAnimationPanel() && canShowCardIcons(cardWidth); titleText.setVisible(showText); ptText.setVisible(showText); fullImageText.setVisible(fullImagePath != null); if (showText) { - int fontSize = cardHeight / 11; + int fontSize = cardHeight / 13; // startup font size (it same size on all zoom levels) titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); - int titleX = Math.round(cardWidth * (20f / 480)); - int titleY = Math.round(cardHeight * (9f / 680)) + getTextOffset(); - titleText.setBounds(cardXOffset + titleX, cardYOffset + titleY, cardWidth - titleX, cardHeight - titleY); + // margins from card black border to text, not need? text show up good without margins + int titleMarginLeft = 0; //Math.round(28f / 672f * cardWidth); + int titleMarginRight = 0; + int titleMarginTop = 0 + Math.round(getCardCaptionTopOffset() / 100f * cardHeight);//Math.round(28f / 936f * cardHeight); + int titleMarginBottom = 0; + titleText.setBounds( + imagePanel.getX() + titleMarginLeft, + imagePanel.getY() + titleMarginTop, + imagePanel.getBounds().width - titleMarginLeft - titleMarginRight, + imagePanel.getBounds().height - titleMarginTop - titleMarginBottom + ); fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10)); - fullImageText.setBounds(cardXOffset, cardYOffset + titleY, cardWidth, cardHeight - titleY); + fullImageText.setBounds(titleText.getX(), titleText.getY(), titleText.getBounds().width, titleText.getBounds().height); + // life points location (font as title) ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); Dimension ptSize = ptText.getPreferredSize(); ptText.setSize(ptSize.width, ptSize.height); - int ptX = Math.round(cardWidth * (420f / 480)) - ptSize.width / 2; - int ptY = Math.round(cardHeight * (675f / 680)) - ptSize.height; - int offsetX = Math.round((CARD_SIZE_FULL.width - cardWidth) / 10.0f); + // right bottom corner with margin (sizes from any sample card) + int ptMarginRight = Math.round(64f / 672f * cardWidth); + int ptMarginBottom = Math.round(62f / 936f * cardHeight); - ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); + int ptX = cardXOffset + cardWidth - ptMarginRight - ptSize.width; + int ptY = cardYOffset + cardHeight - ptMarginBottom - ptSize.height; + ptText.setLocation(ptX, ptY); + + // old version was with TEXT_GLOW_SIZE + //ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); } } @@ -581,7 +684,7 @@ public class CardPanelComponentImpl extends CardPanel { } else if (this.gameCard instanceof StackAbilityView) { return ImageCache.getMorphImage(); } else { - return ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename)); + return ImageCache.getCardbackImage(); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java index b418cd22df5..c5c25824352 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java @@ -398,7 +398,7 @@ public class CardPanelRenderImpl extends CardPanel { } else if (this.gameCard instanceof StackAbilityView) { return ImageCache.getMorphImage(); } else { - return ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename)); + return ImageCache.getCardbackImage(); } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java index 97cbd4af822..86f22e06b2d 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java @@ -3,7 +3,9 @@ package org.mage.plugins.card.images; import com.google.common.base.Function; import com.google.common.collect.ComputationException; import com.google.common.collect.MapMaker; -import java.awt.Graphics2D; + +import java.awt.*; +import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; @@ -81,12 +83,14 @@ public final class ImageCache { CardDownloadData info = new CardDownloadData(name, set, collectorId, usesVariousArt, type, tokenSetCode, tokenDescriptor); + boolean cardback = false; String path; if (collectorId.isEmpty() || "0".equals(collectorId)) { info.setToken(true); path = CardImageUtils.generateTokenImagePath(info); if (path == null) { - path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; + cardback = true; + path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback } } else { path = CardImageUtils.generateImagePath(info); @@ -101,6 +105,7 @@ public final class ImageCache { } if (thumbnail && path.endsWith(".jpg")) { + // need thumbnail image String thumbnailPath = buildThumbnailPath(path); TFile thumbnailFile = null; try { @@ -118,19 +123,35 @@ public final class ImageCache { if (exists) { LOGGER.debug("loading thumbnail for " + key + ", path=" + thumbnailPath); BufferedImage thumbnailImage = loadImage(thumbnailFile); + if (thumbnailImage == null) { // thumbnail exists but broken for some reason LOGGER.warn("failed loading thumbnail for " + key + ", path=" + thumbnailPath + ", thumbnail file is probably broken, attempting to recreate it..."); thumbnailImage = makeThumbnailByFile(key, file, thumbnailPath); } + + if (cardback){ + // unknown tokens on opponent desk + thumbnailImage = getRoundCorner(thumbnailImage); + } + return thumbnailImage; } else { return makeThumbnailByFile(key, file, thumbnailPath); } } else { - BufferedImage image = loadImage(file); - image = getWizardsCard(image); - return image; + if (cardback){ + // need cardback image + BufferedImage image = loadImage(file); + image = getRoundCorner(image); + return image; + }else { + // need normal card image + BufferedImage image = loadImage(file); + image = getWizardsCard(image); + image = getRoundCorner(image); + return image; + } } } else { throw new RuntimeException( @@ -148,6 +169,7 @@ public final class ImageCache { public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) { BufferedImage image = loadImage(file); image = getWizardsCard(image); + image = getRoundCorner(image); if (image == null) { return null; } @@ -189,7 +211,7 @@ public final class ImageCache { info.setToken(true); path = CardImageUtils.generateFullTokenImagePath(info); if (path == null) { - path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; + path = DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename; // TODO: replace empty token by other default card, not cardback } } else { path = CardImageUtils.generateImagePath(info); @@ -207,6 +229,12 @@ public final class ImageCache { private ImageCache() { } + public static BufferedImage getCardbackImage() { + BufferedImage image = ImageCache.loadImage(new TFile(DirectLinksForDownload.outDir + File.separator + DirectLinksForDownload.cardbackFilename)); + image = getRoundCorner(image); + return image; + } + public static BufferedImage getMorphImage() { CardDownloadData info = new CardDownloadData("Morph", "KTK", "0", false, 0, "KTK", ""); info.setToken(true); @@ -215,7 +243,10 @@ public final class ImageCache { return null; } TFile file = getTFile(path); - return loadImage(file); + + BufferedImage image = loadImage(file); + image = getRoundCorner(image); + return image; } public static BufferedImage getManifestImage() { @@ -226,7 +257,10 @@ public final class ImageCache { return null; } TFile file = getTFile(path); - return loadImage(file); + + BufferedImage image = loadImage(file); + image = getRoundCorner(image); + return image; } private static String buildThumbnailPath(String path) { @@ -239,6 +273,32 @@ public final class ImageCache { return thumbnailPath; } + public static BufferedImage getRoundCorner(BufferedImage image){ + if (image != null) { + BufferedImage cornerImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + + // corner + float ROUNDED_CORNER_SIZE = 0.15f; // see CardPanelComponentImpl + int cornerSizeBorder = Math.max(4, Math.round(image.getWidth() * ROUNDED_CORNER_SIZE)); + + // corner mask + Graphics2D gg = cornerImage.createGraphics(); + gg.setComposite(AlphaComposite.Src); + gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + gg.setColor(Color.white); + gg.fill(new RoundRectangle2D.Float(0, 0, cornerImage.getWidth(), cornerImage.getHeight(), cornerSizeBorder, cornerSizeBorder)); + + // image draw to buffer + gg.setComposite(AlphaComposite.SrcAtop); + gg.drawImage(image, 0, 0, null); + //gg.dispose(); + + return cornerImage; + } else { + return image; + } + } + public static BufferedImage getWizardsCard(BufferedImage image) { if (image != null && image.getWidth() == 265 && image.getHeight() == 370) { BufferedImage crop = new BufferedImage(256, 360, BufferedImage.TYPE_INT_RGB); @@ -314,6 +374,7 @@ public final class ImageCache { // return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#" // + (card.getTokenSetCode() == null ? "":card.getTokenSetCode()); // } + /** * Load image from file * diff --git a/Mage.Common/src/main/java/mage/cards/MageCard.java b/Mage.Common/src/main/java/mage/cards/MageCard.java index 61908d4daef..baea1105f26 100644 --- a/Mage.Common/src/main/java/mage/cards/MageCard.java +++ b/Mage.Common/src/main/java/mage/cards/MageCard.java @@ -25,8 +25,8 @@ public abstract class MageCard extends JPanel { public abstract CardView getOriginal(); - // sets the vertical text offset for the card name on the image - public abstract void setTextOffset(int yOffset); + // sets the vertical text offset for the card name on the image, use to move caption to card center + public abstract void setCardCaptionTopOffset(int yOffsetPercent); public abstract void setCardBounds(int x, int y, int width, int height);