From 5c48803ef9dbd425929330773cf45b5739ffb2ef Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 10 May 2019 10:01:51 +0400 Subject: [PATCH] * UI: improved cards appearance: * added colorized PT values (boosted is green, unboosted is red); * added toughness with damage calcs (damaged is red); * image render: now title and PT texts are readable/big in small cards; * mtgo render: improved image quality (less pixelated now); * mtgo render: improved PT font (bold now); --- .../client/dialog/TestCardRenderDialog.form | 3 - .../client/dialog/TestCardRenderDialog.java | 30 +++-- .../mage/client/util/gui/GuiDisplayUtil.java | 8 +- .../card/arcane/CardPanelComponentImpl.java | 116 ++++++++++++------ .../mage/card/arcane/CardPanelRenderImpl.java | 2 + .../mage/card/arcane/CardRendererUtils.java | 72 +++++++++-- .../java/org/mage/card/arcane/GlowText.java | 27 ++-- .../mage/card/arcane/ModernCardRenderer.java | 95 +++++++++++--- .../main/java/mage/MageObjectReference.java | 4 + 9 files changed, 268 insertions(+), 89 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form index 8e62e1f98fe..db55a2d6c57 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.form @@ -107,9 +107,6 @@ - - - diff --git a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java index 710f4d26dc2..d52bb111729 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TestCardRenderDialog.java @@ -29,6 +29,8 @@ import org.apache.log4j.Logger; import javax.swing.*; import java.awt.*; import java.awt.event.KeyEvent; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; /** @@ -67,18 +69,25 @@ public class TestCardRenderDialog extends MageDialog { this.removeDialog(); } - private PermanentView createCard(Game game, UUID controllerId, String code, String cardNumber, int damage) { + private PermanentView createCard(Game game, UUID controllerId, String code, String cardNumber, int power, int toughness, int damage) { CardInfo cardInfo = CardRepository.instance.findCard(code, cardNumber); ExpansionInfo setInfo = ExpansionRepository.instance.getSetByCode(code); CardSetInfo testSet = new CardSetInfo(cardInfo.getName(), setInfo.getCode(), cardNumber, cardInfo.getRarity(), new CardGraphicInfo(cardInfo.getFrameStyle(), cardInfo.usesVariousArt())); Card card = CardImpl.createCard(cardInfo.getClassName(), testSet); + Set cardsList = new HashSet<>(); + cardsList.add(card); + game.loadCards(cardsList, controllerId); + PermanentCard perm = new PermanentCard(card, controllerId, game); - if (damage > 0) { - perm.damage(damage, null, game); - } + if (damage > 0) perm.damage(damage, controllerId, game); + if (power > 0) perm.getPower().setValue(power); + if (toughness > 0) perm.getToughness().setValue(toughness); perm.removeSummoningSickness(); + if (perm.isTransformable()) { + perm.setTransformed(true); + } PermanentView cardView = new PermanentView(perm, card, controllerId, game); cardView.setInViewerOnly(true); @@ -97,11 +106,17 @@ public class TestCardRenderDialog extends MageDialog { BigCard big = new BigCard(); CardsView view = new CardsView(); PermanentView card; - card = createCard(game, player.getId(), "RNA", "263", 0); // mountain + card = createCard(game, player.getId(), "RNA", "263", 0, 0, 0); // mountain view.put(card.getId(), card); - card = createCard(game, player.getId(), "RNA", "185", 0); // Judith, the Scourge Diva + card = createCard(game, player.getId(), "RNA", "185", 0, 0, 0); // Judith, the Scourge Diva view.put(card.getId(), card); - card = createCard(game, player.getId(), "RNA", "14", 1); // Knight of Sorrows + card = createCard(game, player.getId(), "RNA", "78", 125, 89, 0); // Noxious Groodion + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "14", 3, 5, 2); // Knight of Sorrows + view.put(card.getId(), card); + card = createCard(game, player.getId(), "DKA", "140", 5, 2, 2); // Huntmaster of the Fells, transforms + view.put(card.getId(), card); + card = createCard(game, player.getId(), "RNA", "221", 0, 0, 0); // Bedeck // Bedazzle view.put(card.getId(), card); cardsPanel.setCustomCardSize(new Dimension(getCardWidth(), getCardHeight())); @@ -162,7 +177,6 @@ public class TestCardRenderDialog extends MageDialog { } }); - sliderSize.setValue(25); sliderSize.addChangeListener(new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent evt) { sliderSizeStateChanged(evt); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index d075cefeeb3..aa1cce0b27a 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -181,10 +181,14 @@ public final class GuiDisplayUtil { public static TextLines getTextLinesfromCardView(CardView card) { TextLines textLines = new TextLines(); + + // rules textLines.setLines(new ArrayList<>(card.getRules())); for (String rule : card.getRules()) { textLines.setBasicTextLength(textLines.getBasicTextLength() + rule.length()); } + + // counters if (card.getMageObjectType().canHaveCounters()) { ArrayList counters = new ArrayList<>(); if (card instanceof PermanentView) { @@ -212,10 +216,12 @@ public final class GuiDisplayUtil { textLines.setBasicTextLength(textLines.getBasicTextLength() + 50); } } + + // damage if (card.getMageObjectType().isPermanent() && card instanceof PermanentView) { int damage = ((PermanentView) card).getDamage(); if (damage > 0) { - textLines.getLines().add("Damage dealt: " + damage + ""); + textLines.getLines().add("Damage dealt: " + damage + ""); // TODO textLines.setBasicTextLength(textLines.getBasicTextLength() + 50); } } 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 a237318a174..97d84f38138 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 @@ -1,5 +1,6 @@ package org.mage.card.arcane; +import mage.MageInt; import mage.cards.action.ActionCallback; import mage.client.constants.Constants; import mage.client.dialog.PreferencesDialog; @@ -49,11 +50,16 @@ public class CardPanelComponentImpl extends CardPanel { private static final int CARD_MIN_SIZE_FOR_ICONS = 60; private static final int CARD_MAX_SIZE_FOR_ICONS = 200; + // text min size for image render mode + private static final int CARD_TITLE_FONT_MIN_SIZE = 13; + private static final int CARD_PT_FONT_MIN_SIZE = 17; + public final ScaledImagePanel imagePanel; private ImagePanel overlayPanel; private JPanel iconPanel; private JButton typeButton; + private JPanel ptPanel; private JPanel counterPanel; private JLabel loyaltyCounterLabel; @@ -67,7 +73,9 @@ public class CardPanelComponentImpl extends CardPanel { private int lastCardWidth; private final GlowText titleText; - private final GlowText ptText; + private final GlowText ptText1; + private final GlowText ptText2; + private final GlowText ptText3; private final JLabel fullImageText; private String fullImagePath = null; @@ -280,7 +288,7 @@ public class CardPanelComponentImpl extends CardPanel { // Title Text titleText = new GlowText(); - setText(getGameCard()); + setTitle(getGameCard()); // int fontSize = (int) cardHeight / 11; // titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); titleText.setForeground(Color.white); @@ -295,16 +303,19 @@ public class CardPanelComponentImpl extends CardPanel { add(fullImageText); // PT Text - ptText = new GlowText(); - if (getGameCard().isCreature()) { - ptText.setText(getGameCard().getPower() + '/' + getGameCard().getToughness()); - } else if (getGameCard().isPlanesWalker()) { - ptText.setText(getGameCard().getLoyalty()); - } -// ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); - ptText.setForeground(Color.white); - ptText.setGlow(Color.black, TEXT_GLOW_SIZE, TEXT_GLOW_INTENSITY); - add(ptText); + 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(); @@ -349,7 +360,7 @@ public class CardPanelComponentImpl extends CardPanel { this.setCounterPanel(null); } - private void setText(CardView card) { + private void setTitle(CardView card) { titleText.setText(!displayTitleAnyway && hasImage ? "" : card.getName()); } @@ -585,12 +596,14 @@ public class CardPanelComponentImpl extends CardPanel { boolean showText = !isAnimationPanel() && canShowCardIcons(cardWidth, hasImage); titleText.setVisible(showText); - ptText.setVisible(showText); + ptText1.setVisible(showText && !ptText1.getText().isEmpty()); + ptText2.setVisible(showText && !ptText2.getText().isEmpty()); + ptText3.setVisible(showText && !ptText3.getText().isEmpty()); fullImageText.setVisible(fullImagePath != null); if (showText) { int fontSize = cardHeight / 13; // startup font size (it same size on all zoom levels) - titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize)); + titleText.setFont(getFont().deriveFont(Font.BOLD, Math.max(CARD_TITLE_FONT_MIN_SIZE, fontSize))); // margins from card black border to text, not need? text show up good without margins int titleMarginLeft = 0; //Math.round(28f / 672f * cardWidth); @@ -607,24 +620,58 @@ public class CardPanelComponentImpl extends CardPanel { fullImageText.setFont(getFont().deriveFont(Font.PLAIN, 10)); 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); + // PT (font as title) + prepareGlowFont(ptText1, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getPower(), false); + prepareGlowFont(ptText2, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), null, false); + prepareGlowFont(ptText3, Math.max(CARD_PT_FONT_MIN_SIZE, fontSize), getGameCard().getOriginalCard().getToughness(), CardRendererUtils.isCardWithDamage(getGameCard())); // right bottom corner with margin (sizes from any sample card) int ptMarginRight = Math.round(64f / 672f * cardWidth); int ptMarginBottom = Math.round(62f / 936f * cardHeight); - int ptX = cardXOffset + cardWidth - ptMarginRight - ptSize.width; - int ptY = cardYOffset + cardHeight - ptMarginBottom - ptSize.height; - ptText.setLocation(ptX, ptY); + int ptWidth = cardWidth - ptMarginRight * 2; + int ptHeight = ptText2.getHeight(); + int ptX = cardXOffset + ptMarginRight; + int ptY = cardYOffset + cardHeight - ptMarginBottom - ptHeight; + ptPanel.setBounds(ptX, ptY, ptWidth, ptHeight); // old version was with TEXT_GLOW_SIZE //ptText.setLocation(cardXOffset + ptX - TEXT_GLOW_SIZE / 2 - offsetX, cardYOffset + ptY - TEXT_GLOW_SIZE / 2); } } + 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()) { + 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(""); + } + + 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); + } + @Override public String toString() { return getGameCard().toString(); @@ -647,10 +694,14 @@ public class CardPanelComponentImpl extends CardPanel { // Update components if (alpha == 0) { - this.ptText.setVisible(false); + this.ptText1.setVisible(false); + this.ptText2.setVisible(false); + this.ptText3.setVisible(false); this.titleText.setVisible(false); } else if (alpha == 1.0f) { - this.ptText.setVisible(true); + this.ptText1.setVisible(true); + this.ptText2.setVisible(true); + this.ptText3.setVisible(true); this.titleText.setVisible(true); } } @@ -683,7 +734,7 @@ public class CardPanelComponentImpl extends CardPanel { UI.invokeLater(() -> { if (stamp == updateArtImageStamp) { hasImage = srcImage != null; - setText(getGameCard()); + setTitle(getGameCard()); setImage(srcImage); } }); @@ -712,7 +763,7 @@ public class CardPanelComponentImpl extends CardPanel { @Override public void showCardTitle() { displayTitleAnyway = true; - setText(getGameCard()); + setTitle(getGameCard()); } @Override @@ -720,17 +771,8 @@ public class CardPanelComponentImpl extends CardPanel { // Super super.update(card); - // Update card text - if (card.isCreature() && card.isPlanesWalker()) { - ptText.setText(card.getPower() + '/' + card.getToughness() + " (" + card.getLoyalty() + ')'); - } else if (card.isCreature()) { - ptText.setText(card.getPower() + '/' + card.getToughness()); - } else if (card.isPlanesWalker()) { - ptText.setText(card.getLoyalty()); - } else { - ptText.setText(""); - } - setText(card); + updatePTTexts(card); + setTitle(card); // Summoning Sickness overlay if (hasSickness() && card.isCreature() && isPermanent()) { 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 cf62e7cb439..6b7dbc22745 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 @@ -297,6 +297,8 @@ public class CardPanelRenderImpl extends CardPanel { // 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); // Attributes CardPanelAttributes attribs diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java index f32c08e972d..42caffce72b 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java @@ -1,5 +1,9 @@ package org.mage.card.arcane; +import mage.MageInt; +import mage.view.CardView; +import mage.view.PermanentView; + import java.awt.*; import java.awt.image.BufferedImage; import java.util.HashMap; @@ -10,12 +14,18 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * @author stravant@gmail.com + * @author stravant@gmail.com, JayDi85 *

* Various static utilities for use in the card renderer */ public final class CardRendererUtils { + // text colors for PT (mtgo and image render modes) + private static final Color CARD_TEXT_COLOR_GOOD_LIGHT = new Color(182, 235, 168); + private static final Color CARD_TEXT_COLOR_GOOD_DARK = new Color(52, 135, 88); + private static final Color CARD_TEXT_COLOR_BAD_LIGHT = new Color(234, 153, 153); + private static final Color CARD_TEXT_COLOR_BAD_DARK = new Color(200, 33, 33); + /** * Convert an abstract image, whose underlying implementation may or may not * be a BufferedImage into a BufferedImage by creating one and coping the @@ -53,9 +63,9 @@ public final class CardRendererUtils { int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) ((255 - r) / 2); - int plus_g = (int) ((255 - g) / 2); - int plus_b = (int) ((255 - b) / 2); + int plus_r = (255 - r) / 2; + int plus_g = (255 - g) / 2; + int plus_b = (255 - b) / 2; return new Color(r + plus_r, g + plus_g, @@ -69,9 +79,9 @@ public final class CardRendererUtils { int b = c.getBlue(); int alpha = c.getAlpha(); - int plus_r = (int) (Math.min(255 - r, r) / 2); - int plus_g = (int) (Math.min(255 - g, g) / 2); - int plus_b = (int) (Math.min(255 - b, b) / 2); + int plus_r = Math.min(255 - r, r) / 2; + int plus_g = Math.min(255 - g, g) / 2; + int plus_b = Math.min(255 - b, b) / 2; return new Color(r - plus_r, g - plus_g, @@ -195,4 +205,52 @@ public final class CardRendererUtils { return null; } } + + public static String getCardLifeWithDamage(CardView cardView) { + // life with damage + String originLife = cardView.getToughness(); + if (cardView instanceof PermanentView) { + int damage = ((PermanentView) cardView).getDamage(); + int life; + try { + life = Integer.parseInt(originLife); + originLife = String.valueOf(Math.max(0, life - damage)); + } catch (NumberFormatException e) { + // + } + } + return originLife; + } + + public static boolean isCardWithDamage(CardView cardView) { + boolean haveDamage = false; + if (cardView instanceof PermanentView) { + haveDamage = ((PermanentView) cardView).getDamage() > 0; + } + return haveDamage; + } + + public static Color getCardTextColor(MageInt value, boolean drawAsDamaged, Color defaultColor, boolean textLight) { + if (drawAsDamaged) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } + + // boost colorizing + if (value != null) { + int current = value.getValue(); + int origin = value.getBaseValue(); + if (origin != 0) { + if (current < origin) { + return textLight ? CARD_TEXT_COLOR_BAD_LIGHT : CARD_TEXT_COLOR_BAD_DARK; + } else if (current > origin) { + return textLight ? CARD_TEXT_COLOR_GOOD_LIGHT : CARD_TEXT_COLOR_GOOD_DARK; + } else { + return defaultColor; + } + } + } + + return defaultColor; + } + } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java index 8d95a11ac04..5600c59b812 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java @@ -1,5 +1,10 @@ package org.mage.card.arcane; +import mage.client.util.ImageCaches; +import mage.client.util.SoftValuesLoadingCache; +import org.jdesktop.swingx.graphics.GraphicsUtilities; + +import javax.swing.*; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; @@ -14,17 +19,6 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; -import javax.swing.*; - -import org.jdesktop.swingx.graphics.GraphicsUtilities; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - -import mage.client.util.ImageCaches; -import mage.client.util.SoftValuesLoadingCache; - public class GlowText extends JLabel { private static final long serialVersionUID = 1827677946939348001L; @@ -123,10 +117,7 @@ public class GlowText extends JLabel { if (!Objects.equals(this.color, other.color)) { return false; } - if (!Objects.equals(this.glowColor, other.glowColor)) { - return false; - } - return true; + return Objects.equals(this.glowColor, other.glowColor); } } @@ -158,7 +149,11 @@ public class GlowText extends JLabel { return; } - g.drawImage(IMAGE_CACHE.getOrThrow(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null); + g.drawImage(getGlowImage(), 0, 0, null); + } + + public BufferedImage getGlowImage() { + return IMAGE_CACHE.getOrThrow(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)); } private static BufferedImage createGlowImage(Key key) { diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java index fbdac1c3fa4..3bbcb3fc055 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java @@ -1,8 +1,3 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ package org.mage.card.arcane; import mage.ObjectColor; @@ -56,13 +51,14 @@ import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol; */ /** - * @author stravant@gmail.com + * @author stravant@gmail.com, JayDi85 *

* Base rendering class for new border cards */ public class ModernCardRenderer extends CardRenderer { private static final Logger LOGGER = Logger.getLogger(ModernCardRenderer.class); + private static final GlowText glowTextRenderer = new GlowText(); /////////////////////////////////////////////////////////////////////////// // Textures for modern frame cards @@ -261,7 +257,7 @@ public class ModernCardRenderer extends CardRenderer { ptTextHeight = getPTTextHeightForLineHeight(boxHeight); ptTextOffset = (boxHeight - ptTextHeight) / 2; // Beleren font does work well for numbers though - ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.PLAIN, ptTextHeight); + ptTextFont = BASE_BELEREN_FONT.deriveFont(Font.BOLD, ptTextHeight); } @Override @@ -933,6 +929,51 @@ public class ModernCardRenderer extends CardRenderer { } } + public void paintOutlineTextByGlow(Graphics2D g, String text, Color color, int x, int y) { + GlowText label = new GlowText(); + label.setGlow(Color.black, 6, 3); + label.setText(text); + label.setFont(g.getFont().deriveFont(Font.BOLD)); + label.setForeground(color); + Dimension ptSize = label.getPreferredSize(); + label.setSize(ptSize.width, ptSize.height); + g.drawImage(label.getGlowImage(), x, y, null); + } + + public void paintOutlineTextByStroke(Graphics2D g, String text, Color color, int x, int y) { + // https://stackoverflow.com/a/35222059/1276632 + Color outlineColor = Color.black; + Color fillColor = color; + BasicStroke outlineStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + + // remember original settings + Color originalColor = g.getColor(); + Stroke originalStroke = g.getStroke(); + RenderingHints originalHints = g.getRenderingHints(); + + // create a glyph vector from your text + GlyphVector glyphVector = g.getFont().createGlyphVector(g.getFontRenderContext(), text); + // get the shape object + Shape textShape = glyphVector.getOutline(x, y); + + // activate anti aliasing for text rendering (if you want it to look nice) + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + g.setColor(outlineColor); + g.setStroke(outlineStroke); + g.draw(textShape); // draw outline + + g.setColor(fillColor); + g.fill(textShape); // fill the shape + + // reset to original settings after painting + g.setColor(originalColor); + g.setStroke(originalStroke); + g.setRenderingHints(originalHints); + } + // Draw the P/T and/or Loyalty boxes protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) { // No bottom right for abilities @@ -966,24 +1007,44 @@ public class ModernCardRenderer extends CardRenderer { partWidth - 2 * contentInset, 1); // Draw text - Color textColor; + Color defaultTextColor; + boolean defaultTextLight; if (isVehicle) { boolean isAnimated = !(cardView instanceof PermanentView) || cardView.isCreature(); if (isAnimated) { - textColor = Color.white; + defaultTextColor = Color.white; } else { - textColor = new Color(180, 180, 180); + defaultTextColor = new Color(180, 180, 180); } - + defaultTextLight = true; } else { - textColor = getBoxTextColor(); + defaultTextColor = getBoxTextColor(); + defaultTextLight = !defaultTextColor.equals(Color.black); } - g.setColor(textColor); + g.setColor(defaultTextColor); g.setFont(ptTextFont); - String ptText = cardView.getPower() + '/' + cardView.getToughness(); - int ptTextWidth = g.getFontMetrics().stringWidth(ptText); - g.drawString(ptText, - x + (partWidth - ptTextWidth) / 2, curY - ptTextOffset - 1); + + // draws p/t by parts + String ptText1 = cardView.getPower(); + String ptText2 = "/"; + String ptText3 = CardRendererUtils.getCardLifeWithDamage(cardView); + + int ptTextWidth1 = g.getFontMetrics().stringWidth(ptText1); + int ptTextWidth2 = g.getFontMetrics().stringWidth(ptText2); + + // draws / by center, P and T from left/right sides of / + int ptCenterX = x + partWidth / 2; + // p + g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), false, defaultTextColor, defaultTextLight)); + g.drawString(ptText1, ptCenterX - ptTextWidth2 / 2 - ptTextWidth1, curY - ptTextOffset - 1); // left + // / + g.setColor(defaultTextColor); + g.drawString(ptText2, ptCenterX - ptTextWidth2 / 2, curY - ptTextOffset - 1); // center + // t + g.setColor(CardRendererUtils.getCardTextColor(cardView.getOriginalCard().getPower(), CardRendererUtils.isCardWithDamage(cardView), defaultTextColor, defaultTextLight)); + g.drawString(ptText3, ptCenterX + ptTextWidth2 / 2, curY - ptTextOffset - 1); // right + // + g.setColor(defaultTextColor); // Advance curY -= boxHeight; diff --git a/Mage/src/main/java/mage/MageObjectReference.java b/Mage/src/main/java/mage/MageObjectReference.java index 44f63bebc66..a4f2a81302f 100644 --- a/Mage/src/main/java/mage/MageObjectReference.java +++ b/Mage/src/main/java/mage/MageObjectReference.java @@ -53,6 +53,10 @@ public class MageObjectReference implements Comparable, Ser public MageObjectReference(UUID sourceId, Game game) { this.sourceId = sourceId; + if (sourceId == null) { + throw new IllegalArgumentException("MageObjectReference contains nullable sourceId"); + } + MageObject mageObject = game.getObject(sourceId); if (mageObject != null) { this.zoneChangeCounter = mageObject.getZoneChangeCounter(game);