From ffb65d48fef3e7f18549c5bd33008cb271f7c1a4 Mon Sep 17 00:00:00 2001 From: draxdyn Date: Wed, 1 Jun 2016 16:58:10 +0200 Subject: [PATCH] Draw GlowText to a buffered image and cache it across instances Speeds up GUI performance. --- .../java/org/mage/card/arcane/GlowText.java | 150 ++++++++++++++++-- 1 file changed, 133 insertions(+), 17 deletions(-) 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 c5e2f40c0d0..5a32f6b8727 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,15 +1,30 @@ package org.mage.card.arcane; +import com.google.common.base.Function; +import com.google.common.collect.MapMaker; import javax.swing.*; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import mage.client.util.ImageCaches; +import org.jdesktop.swingx.graphics.GraphicsUtilities; public class GlowText extends JLabel { private static final long serialVersionUID = 1827677946939348001L; @@ -19,6 +34,109 @@ public class GlowText extends JLabel { private Color glowColor; private boolean wrap; private int lineCount = 0; + private static Map IMAGE_CACHE; + + private final static class Key + { + final int width; + final int height; + final String text; + final Map fontAttributes; + final Color color; + final int glowSize; + final float glowIntensity; + final Color glowColor; + final boolean wrap; + + // used to pass the native font to the create function so we don't waste performance recreating it, but without holding onto the native object + final transient WeakReference originalFont; + + Font getFont() { + Font res = this.originalFont.get(); + if(res == null) + res = Font.getFont(this.fontAttributes); + return res; + } + + public Key(int width, int height, String text, Font font, Color color, int glowSize, float glowIntensity, Color glowColor, boolean wrap) { + this.width = width; + this.height = height; + this.text = text; + this.originalFont = new WeakReference<>(font); + this.fontAttributes = font.getAttributes(); + this.color = color; + this.glowSize = glowSize; + this.glowIntensity = glowIntensity; + this.glowColor = glowColor; + this.wrap = wrap; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 23 * hash + this.width; + hash = 23 * hash + this.height; + hash = 23 * hash + Objects.hashCode(this.text); + hash = 23 * hash + Objects.hashCode(this.fontAttributes); + hash = 23 * hash + Objects.hashCode(this.color); + hash = 23 * hash + this.glowSize; + hash = 23 * hash + Float.floatToIntBits(this.glowIntensity); + hash = 23 * hash + Objects.hashCode(this.glowColor); + hash = 23 * hash + (this.wrap ? 1 : 0); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Key other = (Key) obj; + if (this.width != other.width) { + return false; + } + if (this.height != other.height) { + return false; + } + if (this.glowSize != other.glowSize) { + return false; + } + if (Float.floatToIntBits(this.glowIntensity) != Float.floatToIntBits(other.glowIntensity)) { + return false; + } + if (this.wrap != other.wrap) { + return false; + } + if (!Objects.equals(this.text, other.text)) { + return false; + } + if (!Objects.equals(this.fontAttributes, other.fontAttributes)) { + return false; + } + if (!Objects.equals(this.color, other.color)) { + return false; + } + if (!Objects.equals(this.glowColor, other.glowColor)) { + return false; + } + return true; + } + } + + static { + IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap(new Function() { + @Override + public BufferedImage apply(Key key) { + return createImage(key); + } + })); + } public void setGlow (Color glowColor, int size, float intensity) { this.glowColor = glowColor; @@ -38,32 +156,32 @@ public class GlowText extends JLabel { return size; } - @Override - public void setText (String text) { - super.setText(text); - } - @Override public void paint (Graphics g) { if (getText().length() == 0) { return; } - Graphics2D g2d = (Graphics2D)g; + g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null); + } + + private static BufferedImage createImage (Key key) { + Dimension size = new Dimension(key.width, key.height); + BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(size.width, size.height); + Graphics2D g2d = image.createGraphics(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - Dimension size = getSize(); int textX = 0, textY = 0; - int wrapWidth = Math.max(0, wrap ? size.width - glowSize : Integer.MAX_VALUE); + int wrapWidth = Math.max(0, key.wrap ? size.width - key.glowSize : Integer.MAX_VALUE); - AttributedString attributedString = new AttributedString(getText()); - attributedString.addAttribute(TextAttribute.FONT, getFont()); + AttributedString attributedString = new AttributedString(key.text); + attributedString.addAttribute(TextAttribute.FONT, key.getFont()); AttributedCharacterIterator charIterator = attributedString.getIterator(); FontRenderContext fontContext = g2d.getFontRenderContext(); LineBreakMeasurer measurer = new LineBreakMeasurer(charIterator, BreakIterator.getWordInstance(Locale.ENGLISH), fontContext); - lineCount = 0; + int lineCount = 0; while (measurer.getPosition() < charIterator.getEndIndex()) { //TextLayout textLayout = measurer.nextLayout(wrapWidth); lineCount++; @@ -83,23 +201,21 @@ public class GlowText extends JLabel { float ascent = textLayout.getAscent(); textY += ascent; // Move down to baseline. - g2d.setColor(glowColor); + g2d.setColor(key.glowColor); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f)); + int glowSize = key.glowSize; textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 + 1, textY + glowSize / 2 + 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 - 1); textLayout.draw(g2d, textX + glowSize / 2 - 1, textY + glowSize / 2 + 1); - g2d.setColor(getForeground()); + g2d.setColor(key.color); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); textLayout.draw(g2d, textX + glowSize / 2, textY + glowSize / 2); textY += textLayout.getDescent() + textLayout.getLeading(); // Move down to top of next line. } - } - - public int getLineCount() { - return this.lineCount; + return image; } public void setGlowColor(Color glowColor) {