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 be9c04cd461..e995f4d8174 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 @@ -21,8 +21,8 @@ import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.awt.font.TextMeasurer; import java.awt.geom.Rectangle2D; -import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; +import java.awt.image.RasterFormatException; import java.io.IOException; import java.net.URL; import java.text.AttributedCharacterIterator; @@ -33,7 +33,6 @@ import java.util.Collection; import java.util.List; import javax.swing.ImageIcon; import mage.ObjectColor; -import mage.cards.FrameStyle; import mage.client.dialog.PreferencesDialog; import mage.constants.CardType; import mage.view.CardView; @@ -164,6 +163,9 @@ public class ModernCardRenderer extends CardRenderer { protected static float TYPE_LINE_Y_FRAC_FULL_ART = 0.74f; protected int typeLineY; + // Possible sizes of rules text font + protected static int[] RULES_TEXT_FONT_SIZES = {24, 18, 15, 12, 9}; + // How large is the box text, and how far is it down the boxes protected int boxTextHeight; protected int boxTextOffset; @@ -206,7 +208,6 @@ public class ModernCardRenderer extends CardRenderer { BOX_HEIGHT_FRAC * cardHeight); // Type line at - LOGGER.info("Draw, " + cardView.getFrameStyle() + ", " + cardView.getFrameStyle().isFullArt()); typeLineY = (int)(getTypeLineYFrac() * cardHeight); // Box text height @@ -337,14 +338,18 @@ public class ModernCardRenderer extends CardRenderer { // Trim off some height artHeight = artWidth / targetAspect; } - BufferedImage subImg - = artImage.getSubimage( - (int)(artRect.getX() * fullCardImgWidth), (int)(artRect.getY() * fullCardImgHeight), - (int)artWidth, (int)artHeight); - g.drawImage(subImg, - totalContentInset + 1, totalContentInset + boxHeight, - (int)targetWidth, (int)targetHeight, - null); + try { + BufferedImage subImg + = artImage.getSubimage( + (int) (artRect.getX() * fullCardImgWidth), (int) (artRect.getY() * fullCardImgHeight), + (int) artWidth, (int) artHeight); + g.drawImage(subImg, + totalContentInset + 1, totalContentInset + boxHeight, + (int) targetWidth, (int) targetHeight, + null); + } catch (RasterFormatException e) { + // At very small card sizes we may encounter a problem with rounding error making the rect not fit + } } } @@ -463,19 +468,23 @@ public class ModernCardRenderer extends CardRenderer { } else { nameStr = cardView.getName(); } - AttributedString str = new AttributedString(nameStr); - str.addAttribute(TextAttribute.FONT, boxTextFont); - TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); - int breakIndex = measure.getLineBreakIndex(0, availableWidth); - if (breakIndex < nameStr.length()) { - str = new AttributedString(nameStr); - str.addAttribute(TextAttribute.FONT, boxTextFontNarrow); - measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); - breakIndex = measure.getLineBreakIndex(0, availableWidth); + if (!nameStr.isEmpty()) { + AttributedString str = new AttributedString(nameStr); + str.addAttribute(TextAttribute.FONT, boxTextFont); + TextMeasurer measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + int breakIndex = measure.getLineBreakIndex(0, availableWidth); + if (breakIndex < nameStr.length()) { + str = new AttributedString(nameStr); + str.addAttribute(TextAttribute.FONT, boxTextFontNarrow); + measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); + breakIndex = measure.getLineBreakIndex(0, availableWidth); + } + if (breakIndex > 0) { + TextLayout layout = measure.getLayout(0, breakIndex); + g.setColor(getBoxTextColor()); + layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1); + } } - TextLayout layout = measure.getLayout(0, breakIndex); - g.setColor(getBoxTextColor()); - layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1); // Draw the mana symbols if (!cardView.isAbility() && !cardView.isFaceDown()) { @@ -518,9 +527,11 @@ public class ModernCardRenderer extends CardRenderer { measure = new TextMeasurer(str.getIterator(), g.getFontRenderContext()); breakIndex = measure.getLineBreakIndex(0, availableWidth); } - TextLayout layout = measure.getLayout(0, breakIndex); - g.setColor(getBoxTextColor()); - layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1); + if (breakIndex > 0) { + TextLayout layout = measure.getLayout(0, breakIndex); + g.setColor(getBoxTextColor()); + layout.draw(g, x, y + boxTextOffset + boxTextHeight - 1); + } } } @@ -538,7 +549,7 @@ public class ModernCardRenderer extends CardRenderer { int partWidth = (int) Math.max(30, 0.20f * cardWidth); // Is it a creature? - if (cardView.getCardTypes().contains(CardType.CREATURE)) { + if (cardView.getCardTypes().contains(CardType.CREATURE) || cardView.getSubTypes().contains("Vehicle")) { int x = cardWidth - borderWidth - partWidth; // Draw PT box @@ -563,12 +574,7 @@ public class ModernCardRenderer extends CardRenderer { g.drawString(ptText, x + (partWidth - ptTextWidth) / 2, curY - ptTextOffset - 1); - // Does it have damage on it? - if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) { - // Show marked damage - - } - + // Advance curY -= boxHeight; } @@ -646,94 +652,92 @@ public class ModernCardRenderer extends CardRenderer { // Draw the card's textbox in a given rect protected boolean loyaltyAbilityColorToggle = false; - protected void drawRulesText(Graphics2D g, int x, int y, int w, int h) { - // Initial font size to try to render at - Font font = new Font("Arial", Font.PLAIN, 12); - Font fontItalic = new Font("Arial", Font.ITALIC, 12); + private class RuleLayout { + public List attributedRules; + public int remainingHeight; + public boolean fits; + public Font font; + public Font fontItalic; + } - // Handle the keyword rules - boolean hasKeywords = !textboxKeywords.isEmpty(); - String keywordRulesString = getKeywordRulesString(); - AttributedString keywordRulesAttributed = new AttributedString(keywordRulesString); - if (hasKeywords) { - keywordRulesAttributed.addAttribute(TextAttribute.FONT, font); - } + /** + * Figure out if a given text size will work for laying out the rules in a card textbox + */ + protected RuleLayout layoutRules(Graphics2D g, List rules, int w, int h, int fontSize) { + // The fonts to try + Font font = new Font("Arial", Font.PLAIN, fontSize); + Font fontItalic = new Font("Arial", Font.ITALIC, fontSize); - // Get the total height + // Get the total height of the rules List attributedRules = new ArrayList<>(); - boolean useSmallFont = false; + boolean fits = true; int remaining = h; - { - if (hasKeywords) { - remaining -= drawSingleRule(g, keywordRulesAttributed, null, 0, 0, w, remaining, false); - } - for (TextboxRule rule : textboxRules) { - AttributedString attributed = rule.generateAttributedString(font, fontItalic); - attributedRules.add(attributed); - remaining -= drawSingleRule(g, attributed, rule, 0, 0, w, remaining, false); - if (remaining < 0) { - useSmallFont = true; - break; - } + for (TextboxRule rule : rules) { + AttributedString attributed = rule.generateAttributedString(font, fontItalic); + attributedRules.add(attributed); + remaining -= drawSingleRule(g, attributed, rule, 0, 0, w, remaining, /*doDraw=*/false); + if (remaining < 0) { + fits = false; + break; } } - // If there wasn't enough room, try using a smaller font - if (useSmallFont) { - font = new Font("Arial", Font.PLAIN, 9); - fontItalic = new Font("Arial", Font.ITALIC, 9); - if (hasKeywords) { - keywordRulesAttributed = new AttributedString(keywordRulesString); - keywordRulesAttributed.addAttribute(TextAttribute.FONT, font); - } + // Return the information + RuleLayout layout = new RuleLayout(); + layout.attributedRules = attributedRules; + layout.remainingHeight = remaining; + layout.fits = fits; + layout.font = font; + layout.fontItalic = fontItalic; + return layout; + } - // Clear out the attributed rules and reatribute them with the new font size - attributedRules.clear(); - for (TextboxRule rule : textboxRules) { - AttributedString attributed = rule.generateAttributedString(font, fontItalic); - attributedRules.add(attributed); - } + protected void drawRulesText(Graphics2D g, int x, int y, int w, int h) { + // Gather all rules to render + List allRules = new ArrayList<>(textboxRules); - // Get the new spacing for the small text - remaining = h; - if (hasKeywords) { - remaining -= drawSingleRule(g, keywordRulesAttributed, null, 0, 0, w, remaining, false); - } - for (TextboxRule rule : textboxRules) { - AttributedString attributed = rule.generateAttributedString(font, fontItalic); - attributedRules.add(attributed); - remaining -= drawSingleRule(g, attributed, rule, 0, 0, w, remaining, false); - if (remaining < 0) { - break; - } + // Add the keyword rule if there are any keywords + if (!textboxKeywords.isEmpty()) { + String keywordRulesString = getKeywordRulesString(); + TextboxRule keywordsRule = new TextboxRule(keywordRulesString, new ArrayList()); + allRules.add(keywordsRule); + } + + // Go through possible font sizes in descending order to find the best fit + RuleLayout bestLayout = null; + for (int fontSize: RULES_TEXT_FONT_SIZES) { + bestLayout = layoutRules(g, allRules, w, h, fontSize); + + // Stop, we found a good fit + if (bestLayout.fits) { + break; } } - // Do we have room for additional spacing between the parts of text? - // If so, calculate the spacing based on how much space was left over - int spacing; - if (remaining <= 0) { - spacing = 0; + // Nothing to draw + if (bestLayout == null) { + return; + } + + // Do we have room for additional padding between the parts of text? + // If so, calculate the padding based on how much space was left over + int padding; + if (bestLayout.fits) { + padding = (int) (((float)bestLayout.remainingHeight) / (1 + allRules.size())); } else { - spacing = (int) (((float)remaining) / (hasKeywords - ? (textboxRules.size() + 2) - : (textboxRules.size() + 1))); + // When the text doesn't fit to begin with there's no room for padding + padding = 0; } // Do the actual draw loyaltyAbilityColorToggle = false; g.setColor(Color.black); - int curY = y + spacing; - if (hasKeywords) { - int adv = drawSingleRule(g, keywordRulesAttributed, null, x, curY, w, h, true); - curY += adv + spacing; - h -= adv; - } - for (int i = 0; i < textboxRules.size(); ++i) { - TextboxRule rule = textboxRules.get(i); - AttributedString attributedRule = attributedRules.get(i); + int curY = y + padding; + for (int i = 0; i < bestLayout.attributedRules.size(); ++i) { + AttributedString attributedRule = bestLayout.attributedRules.get(i); + TextboxRule rule = allRules.get(i); int adv = drawSingleRule(g, attributedRule, rule, x, curY, w, h, true); - curY += adv + spacing; + curY += adv + padding; h -= adv; if (h < 0) { break; @@ -772,7 +776,8 @@ public class ModernCardRenderer extends CardRenderer { AttributedCharacterIterator newLineCheck = text.getIterator(); while (measure.getPosition() < textIter.getEndIndex()) { // Advance iterator to next line break - char ch = newLineCheck.setIndex(measure.getPosition()); + newLineCheck.setIndex(measure.getPosition()); + char ch; while ((ch = newLineCheck.next()) != CharacterIterator.DONE) { if (ch == '\n') { break;