001: /*
002: * $RCSfile: Text2D.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.4 $
041: * $Date: 2007/02/09 17:20:21 $
042: * $State: Exp $
043: */
044:
045: package com.sun.j3d.utils.geometry;
046:
047: import java.awt.image.BufferedImage;
048: import java.awt.image.WritableRaster;
049: import java.awt.image.DataBufferInt;
050: import java.awt.Color;
051: import java.awt.Font;
052: import java.awt.FontMetrics;
053: import java.awt.Graphics;
054: import java.awt.Toolkit;
055: import java.util.Hashtable;
056:
057: import javax.media.j3d.*;
058: import javax.vecmath.*;
059:
060: /**
061: * A Text2D object is a representation of a string as a texture mapped
062: * rectangle. The texture for the rectangle shows the string as rendered in
063: * the specified color with a transparent background. The appearance of the
064: * characters is specified using the font indicated by the font name, size
065: * and style (see java.awt.Font). The approximate height of the rendered
066: * string will be the font size times the rectangle scale factor, which has a
067: * default value of 1/256. For example, a 12 point font will produce
068: * characters that are about 12/256 = 0.047 meters tall. The lower left
069: * corner of the rectangle is located at (0,0,0) with the height
070: * extending along the positive y-axis and the width extending along the
071: * positive x-axis.
072: */
073: public class Text2D extends Shape3D {
074:
075: // This table caches FontMetrics objects to avoid the huge cost
076: // of re-retrieving metrics for a font we've already seen.
077: private static Hashtable metricsTable = new Hashtable();
078: float rectangleScaleFactor = 1f / 256f;
079:
080: Color3f color = new Color3f();
081: String fontName;
082: int fontSize, fontStyle;
083: String text;
084:
085: /**
086: * Creates a Shape3D object which holds a
087: * rectangle that is texture-mapped with an image that has
088: * the specified text written with the specified font
089: * parameters.
090: *
091: * @param text The string to be written into the texture map.
092: * @param color The color of the text string.
093: * @param fontName The name of the Java font to be used for
094: * the text string.
095: * @param fontSize The size of the Java font to be used.
096: * @param fontStyle The style of the Java font to be used.
097: */
098: public Text2D(String text, Color3f color, String fontName,
099: int fontSize, int fontStyle) {
100:
101: this .color.set(color);
102: this .fontName = fontName;
103: this .fontSize = fontSize;
104: this .fontStyle = fontStyle;
105: this .text = text;
106:
107: updateText2D(text, color, fontName, fontSize, fontStyle);
108: }
109:
110: /*
111: * Changes text of this Text2D to 'text'. All other
112: * parameters (color, fontName, fontSize, fontStyle
113: * remain the same.
114: * @param text The string to be set.
115: */
116: public void setString(String text) {
117: this .text = text;
118:
119: Texture tex = getAppearance().getTexture();
120: int width = tex.getWidth();
121: int height = tex.getHeight();
122:
123: ImageComponent imageComponent = setupImage(text, color,
124: fontName, fontSize, fontStyle);
125: if ((imageComponent.getWidth() == width)
126: && (imageComponent.getHeight() == height)) {
127: tex.setImage(0, imageComponent);
128: } else {
129: Texture2D newTex = setupTexture(imageComponent);
130: // Copy texture attributes except those related to
131: // mipmap since Texture only set base imageComponent.
132:
133: newTex.setBoundaryModeS(tex.getBoundaryModeS());
134: newTex.setBoundaryModeT(tex.getBoundaryModeT());
135: newTex.setMinFilter(tex.getMinFilter());
136: newTex.setMagFilter(tex.getMagFilter());
137: newTex.setEnable(tex.getEnable());
138: newTex.setAnisotropicFilterMode(tex
139: .getAnisotropicFilterMode());
140: newTex.setAnisotropicFilterDegree(tex
141: .getAnisotropicFilterDegree());
142: int pcount = tex.getFilter4FuncPointsCount();
143: if (pcount > 0) {
144: float weights[] = new float[pcount];
145: tex.getFilter4Func(weights);
146: newTex.setFilter4Func(weights);
147: }
148: Color4f c = new Color4f();
149: tex.getBoundaryColor(c);
150: newTex.setBoundaryColor(c);
151: newTex.setUserData(tex.getUserData());
152: getAppearance().setTexture(newTex);
153: }
154: }
155:
156: private void updateText2D(String text, Color3f color,
157: String fontName, int fontSize, int fontStyle) {
158: ImageComponent imageComponent = setupImage(text, color,
159: fontName, fontSize, fontStyle);
160:
161: Texture2D t2d = setupTexture(imageComponent);
162:
163: QuadArray rect = setupGeometry(imageComponent.getWidth(),
164: imageComponent.getHeight());
165: setGeometry(rect);
166:
167: Appearance appearance = setupAppearance(t2d);
168: setAppearance(appearance);
169: }
170:
171: /**
172: * Sets the scale factor used in converting the image width/height
173: * to width/height values in 3D.
174: *
175: * @param newScaleFactor The new scale factor.
176: */
177: public void setRectangleScaleFactor(float newScaleFactor) {
178: rectangleScaleFactor = newScaleFactor;
179: updateText2D(text, color, fontName, fontSize, fontStyle);
180: }
181:
182: /**
183: * Gets the current scale factor being used in converting the image
184: * width/height to width/height values in 3D.
185: *
186: * @return The current scale factor.
187: */
188: public float getRectangleScaleFactor() {
189: return rectangleScaleFactor;
190: }
191:
192: /**
193: * Create the ImageComponent and Texture object.
194: */
195: private Texture2D setupTexture(ImageComponent imageComponent) {
196: Texture2D t2d = new Texture2D(Texture2D.BASE_LEVEL,
197: Texture.RGBA, imageComponent.getWidth(), imageComponent
198: .getHeight());
199: t2d.setMinFilter(t2d.BASE_LEVEL_LINEAR);
200: t2d.setMagFilter(t2d.BASE_LEVEL_LINEAR);
201: t2d.setImage(0, imageComponent);
202: t2d.setEnable(true);
203: t2d.setCapability(Texture.ALLOW_IMAGE_WRITE);
204: t2d.setCapability(Texture.ALLOW_SIZE_READ);
205: t2d.setCapability(Texture.ALLOW_ENABLE_READ);
206: t2d.setCapability(Texture.ALLOW_BOUNDARY_MODE_READ);
207: t2d.setCapability(Texture.ALLOW_FILTER_READ);
208: t2d.setCapability(Texture.ALLOW_BOUNDARY_COLOR_READ);
209: t2d.setCapability(Texture.ALLOW_ANISOTROPIC_FILTER_READ);
210: t2d.setCapability(Texture.ALLOW_FILTER4_READ);
211: return t2d;
212: }
213:
214: /**
215: * Creates a ImageComponent2D of the correct dimensions for the
216: * given font attributes. Draw the given text into the image in
217: * the given color. The background of the image is transparent
218: * (alpha = 0).
219: */
220: private ImageComponent setupImage(String text, Color3f color,
221: String fontName, int fontSize, int fontStyle) {
222: Toolkit toolkit = Toolkit.getDefaultToolkit();
223: Font font = new java.awt.Font(fontName, fontStyle, fontSize);
224:
225: FontMetrics metrics;
226: if ((metrics = (FontMetrics) metricsTable.get(font)) == null) {
227: metrics = toolkit.getFontMetrics(font);
228: metricsTable.put(font, metrics);
229: }
230: int width = metrics.stringWidth(text);
231: int descent = metrics.getMaxDescent();
232: int ascent = metrics.getMaxAscent();
233: int leading = metrics.getLeading();
234: int height = descent + ascent;
235:
236: // Need to make width/height powers of 2 because of Java3d texture
237: // size restrictions
238: int pow = 1;
239: for (int i = 1; i < 32; ++i) {
240: pow *= 2;
241: if (width <= pow)
242: break;
243: }
244: width = Math.max(width, pow);
245: pow = 1;
246: for (int i = 1; i < 32; ++i) {
247: pow *= 2;
248: if (height <= pow)
249: break;
250: }
251: height = Math.max(height, pow);
252:
253: // For now, jdk 1.2 only handles ARGB format, not the RGBA we want
254: BufferedImage bImage = new BufferedImage(width, height,
255: BufferedImage.TYPE_INT_ARGB);
256: Graphics offscreenGraphics = bImage.createGraphics();
257:
258: // First, erase the background to the text panel - set alpha to 0
259: Color myFill = new Color(0f, 0f, 0f, 0f);
260: offscreenGraphics.setColor(myFill);
261: offscreenGraphics.fillRect(0, 0, width, height);
262:
263: // Next, set desired text properties (font, color) and draw String
264: offscreenGraphics.setFont(font);
265: Color myTextColor = new Color(color.x, color.y, color.z, 1f);
266: offscreenGraphics.setColor(myTextColor);
267: offscreenGraphics.drawString(text, 0, height - descent);
268:
269: ImageComponent imageComponent = new ImageComponent2D(
270: ImageComponent.FORMAT_RGBA, bImage);
271:
272: imageComponent.setCapability(ImageComponent.ALLOW_SIZE_READ);
273:
274: return imageComponent;
275: }
276:
277: /**
278: * Creates a rectangle of the given width and height and sets up
279: * texture coordinates to map the text image onto the whole surface
280: * of the rectangle (the rectangle is the same size as the text image)
281: */
282: private QuadArray setupGeometry(int width, int height) {
283: float zPosition = 0f;
284: float rectWidth = (float) width * rectangleScaleFactor;
285: float rectHeight = (float) height * rectangleScaleFactor;
286: float[] verts1 = { rectWidth, 0f, zPosition, rectWidth,
287: rectHeight, zPosition, 0f, rectHeight, zPosition, 0f,
288: 0f, zPosition };
289: float[] texCoords = { 0f, -1f, 0f, 0f, (-1f), 0f, (-1f), -1f };
290:
291: QuadArray rect = new QuadArray(4, QuadArray.COORDINATES
292: | QuadArray.TEXTURE_COORDINATE_2);
293: rect.setCoordinates(0, verts1);
294: rect.setTextureCoordinates(0, 0, texCoords);
295:
296: return rect;
297: }
298:
299: /**
300: * Creates Appearance for this Shape3D. This sets transparency
301: * for the object (we want the text to be "floating" in space,
302: * so only the text itself should be non-transparent. Also, the
303: * appearance disables lighting for the object; the text will
304: * simply be colored, not lit.
305: */
306: private Appearance setupAppearance(Texture2D t2d) {
307: TransparencyAttributes transp = new TransparencyAttributes();
308: transp.setTransparencyMode(TransparencyAttributes.BLENDED);
309: transp.setTransparency(0f);
310: Appearance appearance = new Appearance();
311: appearance.setTransparencyAttributes(transp);
312: appearance.setTexture(t2d);
313:
314: Material m = new Material();
315: m.setLightingEnable(false);
316: appearance.setMaterial(m);
317:
318: return appearance;
319: }
320:
321: /**
322: * Returns the text string
323: *
324: * @since Java 3D 1.2.1
325: */
326: public String getString() {
327: return text;
328: }
329:
330: /**
331: * Returns the color of the text
332: *
333: * @since Java 3D 1.2.1
334: */
335: public Color3f getColor() {
336: return color;
337: }
338:
339: /**
340: * Returns the font
341: *
342: * @since Java 3D 1.2.1
343: */
344: public String getFontName() {
345: return fontName;
346: }
347:
348: /**
349: * Returns the font size
350: *
351: * @since Java 3D 1.2.1
352: */
353: public int getFontSize() {
354: return fontSize;
355: }
356:
357: /**
358: * Returns the font style
359: *
360: * @since Java 3D 1.2.1
361: */
362: public int getFontStyle() {
363: return fontStyle;
364: }
365:
366: }
|