package com.indyforge.twod.engine.graphics.rendering.scenegraph; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import com.indyforge.twod.engine.graphics.ImageDesc; import com.indyforge.twod.engine.resources.Resource; import com.indyforge.twod.engine.resources.TransientBufferedImage; /** * * @author Christopher Probst * */ public final class Text extends RenderedImage { /** * */ private static final long serialVersionUID = 1L; public enum Alignment { Left, Center, Right } /* * The text. */ private String text = ""; /* * The text, clear and border color. */ private Color textColor = Color.BLACK, clearColor = new Color(0, 0, 0, 0), borderColor = Color.LIGHT_GRAY; /* * The font asset. */ private Resource<? extends Font> fontResource; /* * Whether or not the text has changed. */ private boolean changed = false; /* * The text alignment. */ private Alignment alignment = Alignment.Left; /* * The border width. */ private float borderWidth = 0f; /* * (non-Javadoc) * * @see * com.indyforge.twod.engine.graphics.rendering.scenegraph.RenderedImage * #onRender(java.awt.Graphics2D, java.awt.Graphics2D) */ @Override protected void onRender(Graphics2D original, Graphics2D transformed) { // Has the text changed ?? if (changed && fontResource != null && imageResource() != null) { // Get the image Image im = imageResource().get(); // Get graphics 2d context Graphics2D graphics2d = (Graphics2D) im.getGraphics(); try { graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); // Read dimension int w = im.getWidth(null), h = im.getHeight(null); // Clear the image graphics2d.setBackground(clearColor); graphics2d.clearRect(0, 0, w, h); // Set font graphics2d.setFont(fontResource.get()); // Set the text color graphics2d.setColor(textColor); // Get the font metrics FontMetrics fm = graphics2d.getFontMetrics(); // Do some text calculations float textWidth = fm.stringWidth(text), x, y = (fm.getAscent() + (h - (fm .getAscent() + fm.getDescent())) * 0.5f); // Use the alignment switch (alignment) { case Left: x = borderWidth; break; case Center: x = (w - textWidth) * 0.5f; break; case Right: x = w - textWidth - borderWidth; break; default: throw new IllegalStateException("Unknown alignment: " + alignment); } // Offset needed for left alignment float off = 0; // Too much text ? if (textWidth > w && alignment == Alignment.Left) { off += textWidth - w; } // Draw the string graphics2d.drawString(text, x - off, y); // Draw border if necessary if (borderWidth > 0) { // Round the border width int bw = Math.round(borderWidth * 0.5f); graphics2d.setColor(borderColor); graphics2d.setStroke(new BasicStroke(borderWidth)); graphics2d.drawRoundRect(bw, bw, w - bw * 2, h - bw * 2, h / 2, h / 2); } // Disable change flag changed = false; } finally { graphics2d.dispose(); } } super.onRender(original, transformed); } /** * Creates an empty text. */ public Text() { /* * A text should always keep the ratio! */ keepRatio(true); } /** * Creates a text. * * @see Text#setup(int, int, int) */ public Text(ImageDesc imageDesc) { // Invoke default constructor! this(); setup(imageDesc); } /** * Creates an image resource using the given parameters. * * @param imageDesc * The image description. * @return this for chaining. */ public Text setup(ImageDesc imageDesc) { if (imageDesc == null) { throw new NullPointerException("imageDesc"); } imageResource(new TransientBufferedImage(imageDesc)); return this; } /** * @return the text. */ public String text() { return text; } /** * @return the border width. */ public float borderWidth() { return borderWidth; } /** * Sets the border width. * * @param borderWidth * The border width or 0 (no border!). * @return this for chaining. */ public Text borderWidth(float borderWidth) { if (borderWidth < 0) { throw new IllegalArgumentException("Border width must be >= 0"); } else if (borderWidth != this.borderWidth) { changed(); } this.borderWidth = borderWidth; return this; } /** * @return the text alignment. */ public Alignment alignment() { return alignment; } /** * * * @param alignment * @return */ public Text alignment(Alignment alignment) { if (alignment == null) { throw new NullPointerException("alignment"); } else if (this.alignment != alignment) { changed(); } this.alignment = alignment; return this; } /** * @return the font resource. */ public Resource<? extends Font> fontResource() { return fontResource; } /** * Sets the font resource. * * @param fontResource * The font resource. * @return this for chaining. */ public Text fontResource(Resource<? extends Font> fontResource) { if (fontResource == null || !fontResource.equals(this.fontResource)) { changed(); } this.fontResource = fontResource; return this; } /** * @return the text color. */ public Color textColor() { return textColor; } /** * Sets the text color. * * @param textColor * The text color. * @return this for chaining. */ public Text textColor(Color textColor) { if (textColor == null) { throw new NullPointerException("textColor"); } else if (!textColor.equals(this.textColor)) { changed(); } this.textColor = textColor; return this; } /** * @return the clear color. */ public Color clearColor() { return clearColor; } /** * Sets the clear color. * * @param clearColor * The clear color. * @return this for chaining. */ public Text clearColor(Color clearColor) { if (clearColor == null) { throw new NullPointerException("clearColor"); } else if (!clearColor.equals(this.clearColor)) { changed(); } this.clearColor = clearColor; return this; } /** * @return the border color. */ public Color borderColor() { return borderColor; } /** * Sets the border color. * * @param borderColor * The border color. * @return this for chaining. */ public Text borderColor(Color borderColor) { if (borderColor == null) { throw new NullPointerException("borderColor"); } else if (!borderColor.equals(this.borderColor)) { changed(); } this.borderColor = borderColor; return this; } /* * (non-Javadoc) * * @see * com.indyforge.twod.engine.graphics.rendering.scenegraph.RenderedImage * #imageResource(com.indyforge.twod.engine.resources.Resource) */ @Override public RenderedImage imageResource(Resource<? extends Image> imageResource) { if (imageResource == null || !imageResource.equals(imageResource())) { changed(); } return super.imageResource(imageResource); } /** * @return true if the rendered text is not updated yet, otherwise false. */ public boolean isChanged() { return changed; } /** * Requests a repaint. * * @return this for chaining. */ public Text changed() { changed = true; return this; } /** * Appends a backspace. * * @return this for chaining. */ public Text backspace() { if (text.length() > 1) { text(text.substring(0, text.length() - 1)); } else { text(""); } return this; } /** * Appends text. * * @param string * The string. * @return this for chaining. */ public Text append(String string) { if (string == null) { throw new NullPointerException("string"); } return text(text + string); } /** * Appends a char. * * @param c * The char. * @return this for chaining. */ public Text append(char c) { return text(text + c); } /** * Sets the text. * * @param text * The text. * @return this for chaining. */ public Text text(String text) { if (text == null) { throw new NullPointerException("text"); } else if (!this.text.equals(text)) { // Save this.text = text; // Text has changed changed(); } return this; } }