001: package com.xoetrope.swing;
002:
003: import com.xoetrope.swing.animation.XAnimationContext;
004: import com.xoetrope.swing.animation.XAnimationSurface;
005: import java.text.AttributedString;
006: import java.awt.Font;
007: import java.awt.FontMetrics;
008: import java.awt.Graphics2D;
009: import java.awt.font.FontRenderContext;
010: import java.awt.font.LineBreakMeasurer;
011: import java.awt.font.TextLayout;
012: import java.awt.geom.Point2D;
013:
014: import net.xoetrope.xui.XAttributedComponent;
015:
016: import com.xoetrope.swing.animation.AnimationThread;
017: import com.xoetrope.util.XTextDefaults;
018: import java.awt.AlphaComposite;
019: import java.awt.Color;
020: import java.awt.GradientPaint;
021: import java.awt.Image;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.image.BufferedImage;
024:
025: /**
026: * A component that displays text that animates/scrolls up the screen like movie credits
027: *
028: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
029: * the GNU Public License (GPL), please see license.txt for more details. If
030: * you make commercial use of this software you must purchase a commercial
031: * license from Xoetrope.</p>
032: * <p> $Revision: 1.17 $</p>
033: */
034: public class XCreditsText extends XAnimationSurface //implements XAnimationContext, XAttributedComponent
035: {
036: private int fade;
037: private double lastY;
038: private Image bufferImage;
039: private int maxExtent;
040:
041: /**
042: * Create a new credits text animation
043: */
044: public XCreditsText() {
045: fade = 0;
046: maxExtent = 1000;
047: setDoubleBuffered(true);
048: }
049:
050: /**
051: * This method will receive all of the timing events from an Animator
052: * during an animation. The fraction is the percent elapsed (0 to 1)
053: * of the current animation cycle.
054: * @param fraction the fraction of completion between the start and
055: * end of the current cycle. Note that on reversing cycles
056: * ({@link Animator.Direction#BACKWARD}) the fraction decreases
057: * from 1.0 to 0 on backwards-running cycles. Note also that animations
058: * with a duration of {@link Animator#INFINITE INFINITE} will call
059: * timingEvent with an undefined value for fraction, since there is
060: * no fraction that makes sense if the animation has no defined length.
061: * @see Animator.Direction
062: */
063: public void timingEvent(float fraction) {
064: timingFraction = fraction;
065: repaint();
066: }
067:
068: //=========================================================================
069: // Rendering
070: //=========================================================================
071: /**
072: * Adjust the settings for the next step.
073: * @param w the width
074: * @param h the height
075: * @param at the animation thread on which this object is running
076: */
077: public void reset(AnimationThread at, int w, int h) {
078: position = oH;
079: bufferImage = null;
080: }
081:
082: /**
083: * Adjust the settings for the next step.
084: *
085: * @param w the width
086: * @param h the height
087: */
088: public void step(int w, int h) {
089: if (lastY < 0.1)
090: return;
091:
092: int numSteps = oH + (int) lastY + 25;
093: setSleepTime(maxExtent / numSteps);
094: }
095:
096: /**
097: * All classes that extend JAnimationSurface must implement this routine...
098: * @param w the width
099: * @param h the height
100: * @param g2 the graphics context
101: */
102: public void drawObjects(int w, int h, Graphics2D g2) {
103: //position = (int)((long)oH - animationThread.getStepPosition()) % maxExtent;
104: position = (int) ((long) oH - (int) (timingFraction * maxExtent + 0.5F));
105: Color bkColor = getBackground();
106: g2.setColor(bkColor);
107: g2.fillRect(0, 0, oW, oH);
108:
109: Point2D.Double pen = new Point2D.Double(0, 0);
110:
111: if (bufferImage == null) {
112: for (int i = 0; i < 2; i++) {
113: Graphics2D gbuffer;
114: if ((i == 1) && (lastY > 0)) {
115: bufferImage = new BufferedImage(oW, (int) lastY,
116: BufferedImage.TYPE_INT_ARGB);
117: gbuffer = (Graphics2D) bufferImage.getGraphics();
118: gbuffer.setColor(getForeground());
119: // gbuffer.drawRect( 0, 0, oW-1, (int)( lastY-1 )); // For debug purposes only
120: } else
121: gbuffer = g2;
122:
123: Font font = getFont();
124: FontMetrics fm = gbuffer.getFontMetrics(font);
125: FontRenderContext frc = gbuffer.getFontRenderContext();
126: ascent = fm.getAscent();
127:
128: lastY = 0;
129: if (label != null) {
130: int idx = label.indexOf(XTextDefaults.CRLF_PAIR);
131: String targetStr = label;
132: while (idx > -1) {
133: if (idx > 6)
134: lastY = wrapString(gbuffer, targetStr
135: .substring(0, idx), lastY, i == 1);
136: else
137: lastY += ascent;
138: targetStr = targetStr.substring(idx
139: + XTextDefaults.CRLF_PAIR.length());
140: idx = targetStr
141: .indexOf(XTextDefaults.CRLF_PAIR);
142: }
143: lastY = wrapString(gbuffer, targetStr, lastY, true);
144: }
145:
146: if ((i == 1) && (lastY > 0))
147: gbuffer.dispose();
148: }
149: }
150:
151: g2.drawImage(bufferImage, 0, position, null);
152:
153: maxExtent = Math.max(((int) lastY), oH) + 25;
154:
155: if (fade > 0) {
156: int fadeHeight = (oH * fade) / 100;
157:
158: // Fade out the top
159: if (position < fadeHeight) {
160: GradientPaint painter = new GradientPaint(0.0F, 0.0F,
161: new Color(bkColor.getRed(), bkColor.getGreen(),
162: bkColor.getBlue(), 255), 0.0F,
163: fadeHeight, new Color(bkColor.getRed(), bkColor
164: .getGreen(), bkColor.getBlue(), 0),
165: false);
166: g2.setPaint(painter);
167: g2.fill(new Rectangle2D.Double(0, 0, oW, fadeHeight));
168: }
169:
170: // Fade out the bottom
171: int fadeTop = oH - fadeHeight;
172: if ((position > fadeTop) || ((position + lastY) > fadeTop)) {
173: GradientPaint painter2 = new GradientPaint(0.0F,
174: fadeTop, new Color(bkColor.getRed(), bkColor
175: .getGreen(), bkColor.getBlue(), 0),
176: 0.0F, oH, new Color(bkColor.getRed(), bkColor
177: .getGreen(), bkColor.getBlue(), 255),
178: false);
179: g2.setPaint(painter2);
180: g2.fill(new Rectangle2D.Double(0, fadeTop, oW, oH));
181: }
182: }
183: }
184:
185: /**
186: * Render a text that wraps within the client area
187: * @param g2 the graphics context
188: * @param localCopy the string to render
189: * @param y the y offset into the client area
190: * @param doPaint true to paint, false to just calculate
191: * @return the latest y position
192: */
193: protected double wrapString(Graphics2D g2, String localCopy,
194: double y, boolean doPaint) {
195: Point2D.Double pen = new Point2D.Double(0, y);
196: float wrappingWidth = oW - 15;
197: int len = localCopy.length();
198: Font font = getFont();
199:
200: FontRenderContext frc = g2.getFontRenderContext();
201: LineBreakMeasurer measurer = new LineBreakMeasurer(
202: new AttributedString(localCopy, font.getAttributes())
203: .getIterator(), frc);
204: while (measurer.getPosition() < len) {
205: TextLayout layout = measurer.nextLayout(wrappingWidth);
206: pen.y += ascent;
207:
208: if (doPaint) {
209: double ddx = (wrappingWidth - layout.getAdvance()) / 2;
210: double yPos = /*position*/+pen.y;
211:
212: // Paint the translucent trail
213: Graphics2D gTrail = (Graphics2D) g2.create();
214: AlphaComposite trailComposite = AlphaComposite
215: .getInstance(AlphaComposite.SRC_OVER, 0.2F);
216: gTrail.setComposite(trailComposite);
217: gTrail.translate(0.0F, 1.0F);
218: layout.draw(gTrail, new Double(pen.x + ddx).intValue(),
219: new Double(yPos).intValue());
220: gTrail.dispose();
221:
222: // Paint the full/new version
223: layout.draw(g2, new Double(pen.x + ddx).intValue(),
224: new Double(yPos).intValue());
225: }
226: pen.y += layout.getDescent() + layout.getLeading();
227: }
228:
229: return pen.y;
230: }
231:
232: //=========================================================================
233: // Access Functions
234: //=========================================================================
235: /**
236: * Set the percentage of the width over which to fade the ends in and out. The
237: * fading usies the background colour to give a smooth transition by placing
238: * a gradient fading from full opacity at the extreme left and right to
239: * full transparency at the percentage of the width specified by the fade
240: * parameter. By default the fade is set to zero for no fade. The value is
241: * bound within the range 0-50.
242: * @param percentage the new fade percentage
243: */
244: public void setFade(int percentage) {
245: fade = Math.min(50, Math.max(0, percentage));
246: }
247:
248: /**
249: * Return the current amount of fading as a percentage of the component width.
250: */
251: public int getFade() {
252: return fade;
253: }
254:
255: /**
256: * Set one or more attributes of the component.
257: * @param attribName the name of the attribute
258: * <ul>
259: * <li>text - set the text</li>
260: * <li>content - set the text</li>
261: * <li>fade - the percentage of the width by which to fade each end of the marquee</li>
262: * </ul>
263: * @param attribValue the value of the attribute
264: * @return 0 for success, non zero otherwise
265: */
266: public int setAttribute(String attribName, Object attribValue) {
267: String attribNameLwr = attribName.toLowerCase();
268: String attribValueStr = (String) attribValue;
269: if (attribNameLwr.equals("fade"))
270: setFade(Integer.parseInt(attribValueStr));
271: else
272: super .setAttribute(attribName, attribValueStr);
273:
274: return 0;
275: }
276: }
|