001: /*
002: * Copyright (c) 2001-2006 JGoodies Karsten Lentzsch. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JGoodies Karsten Lentzsch nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package com.jgoodies.animation.renderer;
032:
033: import java.awt.Color;
034: import java.awt.Font;
035: import java.awt.Graphics2D;
036: import java.awt.Shape;
037: import java.awt.font.FontRenderContext;
038: import java.awt.font.GlyphVector;
039: import java.awt.geom.Rectangle2D;
040:
041: import com.jgoodies.animation.*;
042:
043: /**
044: * An abstract superclass that helps implementing typographic animation renderers.
045: *
046: * @author Karsten Lentzsch
047: * @version $Revision: 1.1 $
048: */
049: public abstract class AbstractTextRenderer implements AnimationRenderer {
050:
051: private String text;
052: private Font font;
053: private Color color;
054: private HeightMode heightMode = HeightMode.CAPITAL_ASCENT;
055:
056: // Cached data -------------------------------------------
057: protected GlyphVector cachedGlyphVector;
058: protected Shape[] cachedGlyphShapes;
059: protected float cachedTextWidth;
060: protected float cachedTextAscent;
061: protected float cachedTextHeight;
062: protected float capitalMAscent = -1f; // Ascent of a capital M
063: private boolean cacheValid = false;
064:
065: // Instance Creation ******************************************************
066:
067: AbstractTextRenderer(String text) {
068: this (text, null);
069: }
070:
071: AbstractTextRenderer(String text, Font font) {
072: this .text = text == null ? "Karsten Lentzsch" : text;
073: this .font = font == null ? createDefaultFont() : font;
074: this .color = Color.BLACK;
075: }
076:
077: /**
078: * Creates and returns a default font object.
079: *
080: * @return a default font object
081: */
082: private static Font createDefaultFont() {
083: return new Font("dialog", Font.BOLD, 12);
084: }
085:
086: // Accessors **************************************************************
087:
088: public Color getColor() {
089: return color;
090: }
091:
092: public Font getFont() {
093: return font;
094: }
095:
096: public String getText() {
097: return text;
098: }
099:
100: public HeightMode getHeightMode() {
101: return heightMode;
102: }
103:
104: public void setColor(Color color) {
105: this .color = color;
106: }
107:
108: public void setHeightMode(HeightMode heightMode) {
109: this .heightMode = heightMode;
110: }
111:
112: /**
113: * Sets the renderer's font.
114: *
115: * @param newFont the font to be set
116: */
117: public void setFont(Font newFont) {
118: if (newFont == null)
119: throw new NullPointerException("The font must not be null.");
120:
121: if (newFont.equals(font))
122: return;
123:
124: font = newFont;
125: invalidateCache();
126: }
127:
128: /**
129: * Sets the renderer's text.
130: *
131: * @param newText the text to be set
132: */
133: public void setText(String newText) {
134: if (newText == null)
135: throw new NullPointerException("The text must not be null.");
136:
137: if (newText.equals(text))
138: return;
139:
140: text = newText;
141: invalidateCache();
142: }
143:
144: /**
145: * Computes and answers the text ascent using the current height mode.
146: *
147: * @return the ascent adjusted using the current height mode
148: * @see #getHeightMode()
149: */
150: protected float getAdjustedAscent() {
151: if (heightMode == HeightMode.CAPITAL_ASCENT)
152: return capitalMAscent;
153: else if (heightMode == HeightMode.TEXT_ASCENT)
154: return cachedTextAscent;
155: else
156: return cachedTextHeight;
157: }
158:
159: /**
160: * Computes and answers the text descent using the current height mode.
161: *
162: * @return the descent adjusted to the current height mode
163: * @see #getHeightMode()
164: */
165: protected float getAdjustedDescent() {
166: if (heightMode == HeightMode.CAPITAL_ASCENT)
167: return 0;
168: else if (heightMode == HeightMode.TEXT_ASCENT)
169: return 0;
170: else
171: return cachedTextHeight - cachedTextAscent;
172: }
173:
174: // Caching ****************************************************************
175:
176: protected boolean isCacheValid() {
177: return cacheValid;
178: }
179:
180: protected void setCacheValid(boolean b) {
181: cacheValid = b;
182: }
183:
184: protected void ensureValidCache(Graphics2D g2) {
185: if (!isCacheValid())
186: validateCache(g2);
187: }
188:
189: /**
190: * Validates the cache, here: creates a <code>GlyphVector</code>
191: * and computes and stores its size information.
192: *
193: * @param g2 the Graphics object used to get the font render context
194: */
195: protected void validateCache(Graphics2D g2) {
196: FontRenderContext frc = g2.getFontRenderContext();
197:
198: ensureCapitalMAscentComputed(frc);
199:
200: cachedGlyphVector = font.createGlyphVector(frc, text);
201: Rectangle2D bounds = cachedGlyphVector.getVisualBounds();
202: cachedTextWidth = (float) bounds.getWidth();
203: cachedTextAscent = (float) -bounds.getY();
204: cachedTextHeight = (float) bounds.getHeight();
205:
206: int glyphCount = cachedGlyphVector.getNumGlyphs();
207: cachedGlyphShapes = new Shape[glyphCount];
208: for (int i = 0; i < glyphCount; i++) {
209: cachedGlyphShapes[i] = cachedGlyphVector.getGlyphOutline(i);
210: }
211: setCacheValid(true);
212:
213: /* Debug lines
214: System.out.println("Text = " + text);
215: System.out.println("GV visual bounds = " + glyphVector.getVisualBounds());
216: */
217: }
218:
219: /**
220: * Ensures that the ascent of a capital M has been computed.
221: *
222: * @param frc the font render context used to create the glyph vector
223: */
224: private void ensureCapitalMAscentComputed(FontRenderContext frc) {
225: if (capitalMAscent > 0)
226: return;
227: GlyphVector mGlyphVector = font.createGlyphVector(frc, "M");
228: capitalMAscent = (float) -mGlyphVector.getVisualBounds().getY();
229: }
230:
231: /**
232: * Invalidates the cache.
233: */
234: protected void invalidateCache() {
235: setCacheValid(false);
236: cachedGlyphVector = null;
237: cachedGlyphShapes = null;
238: }
239:
240: }
|