From 41cd6c47b23975098cd155517790e018670785e7 Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Mon, 15 Jun 2009 23:12:27 +0000 Subject: Copied JOGL_2_SANDBOX r350 on to trunk; JOGL_2_SANDBOX branch is now closed git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/../svn-server-sync/jogl-demos/trunk@352 3298f667-5e0e-4b4a-8ed4-a3559d26a5f4 --- src/demos/j2d/CustomText.java | 447 ++++++++++++++++++++++++++++++ src/demos/j2d/FlyingText.java | 485 +++++++++++++++++++++++++++++++++ src/demos/j2d/TestOverlay.java | 218 +++++++++++++++ src/demos/j2d/TestTextRenderer.java | 168 ++++++++++++ src/demos/j2d/TestTextureRenderer.java | 222 +++++++++++++++ src/demos/j2d/TextCube.java | 222 +++++++++++++++ src/demos/j2d/TextFlow.java | 221 +++++++++++++++ 7 files changed, 1983 insertions(+) create mode 100755 src/demos/j2d/CustomText.java create mode 100755 src/demos/j2d/FlyingText.java create mode 100755 src/demos/j2d/TestOverlay.java create mode 100755 src/demos/j2d/TestTextRenderer.java create mode 100755 src/demos/j2d/TestTextureRenderer.java create mode 100755 src/demos/j2d/TextCube.java create mode 100755 src/demos/j2d/TextFlow.java (limited to 'src/demos/j2d') diff --git a/src/demos/j2d/CustomText.java b/src/demos/j2d/CustomText.java new file mode 100755 index 0000000..bfcbc79 --- /dev/null +++ b/src/demos/j2d/CustomText.java @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextRenderer; +import com.sun.opengl.util.texture.Texture; +import com.sun.opengl.util.texture.TextureCoords; +import com.sun.opengl.util.texture.TextureIO; +import com.sun.opengl.util.texture.awt.AWTTextureIO; +import demos.common.Demo; +import demos.util.FPSCounter; +import demos.util.SystemTime; +import demos.util.Time; +import gleem.linalg.Vec2f; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.DisplayMode; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.glu.GLU; +import com.sun.opengl.util.Animator; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; + + +/** Illustrates more advanced use of the TextRenderer class; shows how + to do text filled with a linear Java 2D gradient. */ + +public class CustomText extends Demo { + public static void main(String[] args) { + JFrame frame = new JFrame("Custom Text"); + frame.getContentPane().setLayout(new BorderLayout()); + + GLCanvas canvas = new GLCanvas(); + final CustomText demo = new CustomText(); + + canvas.addGLEventListener(demo); + frame.getContentPane().add(canvas, BorderLayout.CENTER); + frame.getContentPane().add(demo.buildGUI(), BorderLayout.NORTH); + + DisplayMode mode = + GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); + + frame.setSize((int) (0.75f * mode.getWidth()), + (int) (0.75f * mode.getHeight())); + + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + // Put a little physics on the text to make it look nicer + private static final float INIT_ANG_VEL_MAG = 0.3f; + private static final float INIT_VEL_MAG = 400.0f; + private static final int DEFAULT_DROP_SHADOW_DIST = 20; + + // Information about each piece of text + private static class TextInfo { + float angularVelocity; + Vec2f velocity; + + float angle; + Vec2f position; + + String text; + } + + private List/**/ textInfo = new ArrayList/**/(); + private Time time; + private Texture backgroundTexture; + private TextRenderer renderer; + private Random random = new Random(); + private GLU glu = new GLU(); + private int width; + private int height; + + private int maxTextWidth; + + private FPSCounter fps; + + public Container buildGUI() { + // Create gui + JPanel panel = new JPanel(); + JButton button = new JButton("Less Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + lessText(); + } + }); + panel.add(button); + button = new JButton("More Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + moreText(); + } + }); + panel.add(button); + return panel; + } + + public void moreText() { + int numToAdd = (int) (textInfo.size() * 0.5f); + if (numToAdd == 0) + numToAdd = 1; + for (int i = 0; i < numToAdd; i++) { + textInfo.add(randomTextInfo()); + } + } + + public void lessText() { + if (textInfo.size() == 1) + return; + int numToRemove = textInfo.size() / 3; + if (numToRemove == 0) + numToRemove = 1; + for (int i = 0; i < numToRemove; i++) { + textInfo.remove(textInfo.size() - 1); + } + } + + public void init(GLAutoDrawable drawable) { + // Create the background texture + BufferedImage bgImage = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = bgImage.createGraphics(); + g.setColor(new Color(0.3f, 0.3f, 0.3f)); + g.fillRect(0, 0, 2, 2); + g.setColor(new Color(0.7f, 0.7f, 0.7f)); + g.fillRect(0, 0, 1, 1); + g.fillRect(1, 1, 1, 1); + g.dispose(); + backgroundTexture = AWTTextureIO.newTexture(bgImage, false); + backgroundTexture.bind(); + backgroundTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); + backgroundTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); + backgroundTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); + backgroundTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); + + // Create the text renderer + renderer = new TextRenderer(new Font("Serif", Font.PLAIN, 72), true, true, + new CustomRenderDelegate(52, 10, Color.BLUE, Color.CYAN)); + + // Create the FPS counter + fps = new FPSCounter(drawable, 36); + + width = drawable.getWidth(); + height = drawable.getWidth(); + + // Compute maximum width of text we're going to draw to avoid + // popping in/out at edges + maxTextWidth = (int) renderer.getBounds("Java 2D").getWidth(); + maxTextWidth = Math.max(maxTextWidth, (int) renderer.getBounds("OpenGL").getWidth()); + + // Create random text + textInfo.clear(); + for (int i = 0; i < 100; i++) { + textInfo.add(randomTextInfo()); + } + + time = new SystemTime(); + ((SystemTime) time).rebase(); + + // Set up properties; note we don't need the depth buffer in this demo + GL gl = drawable.getGL(); + gl.glDisable(GL.GL_DEPTH_TEST); + // Turn off vsync if we can + gl.setSwapInterval(0); + } + + public void dispose(GLAutoDrawable drawable) { + } + + public void display(GLAutoDrawable drawable) { + time.update(); + + // Update velocities and positions of all text + float deltaT = (float) time.deltaT(); + Vec2f tmp = new Vec2f(); + for (Iterator iter = textInfo.iterator(); iter.hasNext(); ) { + TextInfo info = (TextInfo) iter.next(); + + // Randomize things a little bit at run time + if (random.nextInt(1000) == 0) { + info.angularVelocity = INIT_ANG_VEL_MAG * (randomAngle() - 180); + info.velocity = randomVelocityVec2f(INIT_VEL_MAG, INIT_VEL_MAG); + } + + // Now update angles and positions + info.angle += info.angularVelocity * deltaT; + tmp.set(info.velocity); + tmp.scale(deltaT); + info.position.add(tmp); + + // Wrap angles and positions + if (info.angle < 0) { + info.angle += 360; + } else if (info.angle > 360) { + info.angle -= 360; + } + // Use maxTextWidth to avoid popping in/out at edges + // Would be better to do oriented bounding rectangle computation + if (info.position.x() < -maxTextWidth) { + info.position.setX(info.position.x() + drawable.getWidth() + 2 * maxTextWidth); + } else if (info.position.x() > drawable.getWidth() + maxTextWidth) { + info.position.setX(info.position.x() - drawable.getWidth() - 2 * maxTextWidth); + } + if (info.position.y() < -maxTextWidth) { + info.position.setY(info.position.y() + drawable.getHeight() + 2 * maxTextWidth); + } else if (info.position.y() > drawable.getHeight() + maxTextWidth) { + info.position.setY(info.position.y() - drawable.getHeight() - 2 * maxTextWidth); + } + } + + GL2 gl = drawable.getGL().getGL2(); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL2ES1.GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluOrtho2D(0, drawable.getWidth(), 0, drawable.getHeight()); + gl.glMatrixMode(GL2ES1.GL_MODELVIEW); + gl.glLoadIdentity(); + + // Draw the background texture + backgroundTexture.enable(); + backgroundTexture.bind(); + TextureCoords coords = backgroundTexture.getImageTexCoords(); + int w = drawable.getWidth(); + int h = drawable.getHeight(); + float fw = w / 100.0f; + float fh = h / 100.0f; + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(fw * coords.left(), fh * coords.bottom()); + gl.glVertex3f(0, 0, 0); + gl.glTexCoord2f(fw * coords.right(), fh * coords.bottom()); + gl.glVertex3f(w, 0, 0); + gl.glTexCoord2f(fw * coords.right(), fh * coords.top()); + gl.glVertex3f(w, h, 0); + gl.glTexCoord2f(fw * coords.left(), fh * coords.top()); + gl.glVertex3f(0, h, 0); + gl.glEnd(); + backgroundTexture.disable(); + + // Render all text + renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + + // Note we're doing some slightly fancy stuff to position the text. + // We tell the text renderer to render the text at the origin, and + // manipulate the modelview matrix to put the text where we want. + + gl.glMatrixMode(GL2ES1.GL_MODELVIEW); + + for (Iterator iter = textInfo.iterator(); iter.hasNext(); ) { + TextInfo info = (TextInfo) iter.next(); + gl.glLoadIdentity(); + gl.glTranslatef(info.position.x(), + info.position.y(), + 0); + gl.glRotatef(info.angle, 0, 0, 1); + renderer.draw(info.text, 0, 0); + renderer.flush(); + } + + renderer.endRendering(); + + // Use the FPS renderer last to render the FPS + fps.draw(); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + this.width = width; + this.height = height; + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private TextInfo randomTextInfo() { + TextInfo info = new TextInfo(); + info.text = randomString(); + info.angle = randomAngle(); + info.position = randomVec2f(width, height); + + info.angularVelocity = INIT_ANG_VEL_MAG * (randomAngle() - 180); + info.velocity = randomVelocityVec2f(INIT_VEL_MAG, INIT_VEL_MAG); + + return info; + } + + private String randomString() { + switch (random.nextInt(3)) { + case 0: + return "OpenGL"; + case 1: + return "Java 2D"; + default: + return "Text"; + } + } + + private float randomAngle() { + return 360.0f * random.nextFloat(); + } + + private Vec2f randomVec2f(float x, float y) { + return new Vec2f(x * random.nextFloat(), + y * random.nextFloat()); + } + + private Vec2f randomVelocityVec2f(float x, float y) { + return new Vec2f(x * (random.nextFloat() - 0.5f), + y * (random.nextFloat() - 0.5f)); + } + + private static final Color DROP_SHADOW_COLOR = new Color(0, 0, 0, 0.5f); + + class CustomRenderDelegate implements TextRenderer.RenderDelegate { + private float gradientSize; + private int dropShadowDepth; + private Color color1; + private Color color2; + + + CustomRenderDelegate(float gradientSize, int dropShadowDepth, Color color1, Color color2) { + this.gradientSize = gradientSize; + this.dropShadowDepth = dropShadowDepth; + this.color1 = color1; + this.color2 = color2; + } + + public boolean intensityOnly() { + return false; + } + + public Rectangle2D getBounds(CharSequence str, + Font font, + FontRenderContext frc) { + return getBounds(str.toString(), font, frc); + } + + public Rectangle2D getBounds(String str, + Font font, + FontRenderContext frc) { + return getBounds(font.createGlyphVector(frc, str), frc); + } + + public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { + Rectangle2D stringBounds = gv.getPixelBounds(frc, 0, 0); + return new Rectangle2D.Double(stringBounds.getX(), + stringBounds.getY(), + stringBounds.getWidth() + dropShadowDepth, + stringBounds.getHeight() + dropShadowDepth); + } + + public void drawGlyphVector(Graphics2D graphics, GlyphVector str, int x, int y) { + graphics.setColor(DROP_SHADOW_COLOR); + graphics.drawGlyphVector(str, x + dropShadowDepth, y + dropShadowDepth); + graphics.setColor(Color.WHITE); + graphics.setPaint(new GradientPaint(x, y, color1, + x, y + gradientSize / 2, color2, + true)); + graphics.drawGlyphVector(str, x, y); + } + + public void draw(Graphics2D graphics, String str, int x, int y) { + graphics.setColor(DROP_SHADOW_COLOR); + graphics.drawString(str, x + dropShadowDepth, y + dropShadowDepth); + graphics.setColor(Color.WHITE); + graphics.setPaint(new GradientPaint(x, y, color1, + x, y + gradientSize / 2, color2, + true)); + graphics.drawString(str, x, y); + } + } +} diff --git a/src/demos/j2d/FlyingText.java b/src/demos/j2d/FlyingText.java new file mode 100755 index 0000000..f46dac2 --- /dev/null +++ b/src/demos/j2d/FlyingText.java @@ -0,0 +1,485 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextRenderer; +import com.sun.opengl.util.texture.Texture; +import com.sun.opengl.util.texture.TextureCoords; +import com.sun.opengl.util.texture.TextureIO; +import com.sun.opengl.util.texture.awt.AWTTextureIO; +import demos.common.Demo; +import demos.util.FPSCounter; +import demos.util.SystemTime; +import demos.util.Time; +import gleem.linalg.Vec2f; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.DisplayMode; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import javax.media.opengl.GL; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.glu.GLU; +import com.sun.opengl.util.Animator; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + + + +/** Illustrates more advanced use of the TextRenderer class; shows how + to do animated translated and rotated text as well as a drop + shadow effect. */ + +public class FlyingText extends Demo { + + public static void main(String[] args) { + JFrame frame = new JFrame("Flying Text"); + frame.getContentPane().setLayout(new BorderLayout()); + + GLCanvas canvas = new GLCanvas(); + final FlyingText demo = new FlyingText(); + + canvas.addGLEventListener(demo); + frame.getContentPane().add(canvas, BorderLayout.CENTER); + frame.getContentPane().add(demo.buildGUI(), BorderLayout.NORTH); + + DisplayMode mode = + GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode(); + + frame.setSize((int) (0.75f * mode.getWidth()), + (int) (0.75f * mode.getHeight())); + + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + // Put a little physics on the text to make it look nicer + private static final float INIT_ANG_VEL_MAG = 0.3f; + private static final float INIT_VEL_MAG = 400.0f; + private static final int DEFAULT_DROP_SHADOW_DIST = 20; + + // Information about each piece of text + private static class TextInfo { + float angularVelocity; + Vec2f velocity; + + float angle; + Vec2f position; + + float h; + float s; + float v; + + // Cycle the saturation + float curTime; + + // Cache of the RGB color + float r; + float g; + float b; + + String text; + } + + private List/**/ textInfo = new ArrayList/**/(); + private int dropShadowDistance = DEFAULT_DROP_SHADOW_DIST; + private Time time; + private Texture backgroundTexture; + private TextRenderer renderer; + private Random random = new Random(); + private GLU glu = new GLU(); + private int width; + private int height; + + private int maxTextWidth; + + private FPSCounter fps; + + public Container buildGUI() { + // Create gui + JPanel panel = new JPanel(); + JButton button = new JButton("Less Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + lessText(); + } + }); + panel.add(button); + final JSlider slider = new JSlider(JSlider.HORIZONTAL, + getMinDropShadowDistance(), + getMaxDropShadowDistance(), + getDropShadowDistance()); + slider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + setDropShadowDistance(slider.getValue()); + } + }); + panel.add(slider); + button = new JButton("More Text"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + moreText(); + } + }); + panel.add(button); + return panel; + } + + public void moreText() { + int numToAdd = (int) (textInfo.size() * 0.5f); + if (numToAdd == 0) + numToAdd = 1; + for (int i = 0; i < numToAdd; i++) { + textInfo.add(randomTextInfo()); + } + } + + public void lessText() { + if (textInfo.size() == 1) + return; + int numToRemove = textInfo.size() / 3; + if (numToRemove == 0) + numToRemove = 1; + for (int i = 0; i < numToRemove; i++) { + textInfo.remove(textInfo.size() - 1); + } + } + + public int getDropShadowDistance() { + return dropShadowDistance; + } + + public int getMinDropShadowDistance() { + return 1; + } + + public int getMaxDropShadowDistance() { + return 30; + } + + public void setDropShadowDistance(int dist) { + dropShadowDistance = dist; + } + + public void init(GLAutoDrawable drawable) { + // Create the background texture + BufferedImage bgImage = new BufferedImage(2, 2, BufferedImage.TYPE_BYTE_GRAY); + Graphics2D g = bgImage.createGraphics(); + g.setColor(new Color(0.3f, 0.3f, 0.3f)); + g.fillRect(0, 0, 2, 2); + g.setColor(new Color(0.7f, 0.7f, 0.7f)); + g.fillRect(0, 0, 1, 1); + g.fillRect(1, 1, 1, 1); + g.dispose(); + backgroundTexture = AWTTextureIO.newTexture(bgImage, false); + backgroundTexture.bind(); + backgroundTexture.setTexParameteri(GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST); + backgroundTexture.setTexParameteri(GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST); + backgroundTexture.setTexParameteri(GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT); + backgroundTexture.setTexParameteri(GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT); + + // Create the text renderer + renderer = new TextRenderer(new Font("Serif", Font.PLAIN, 72), true, true); + + // Create the FPS counter + fps = new FPSCounter(drawable, 36); + + width = drawable.getWidth(); + height = drawable.getWidth(); + + // Compute maximum width of text we're going to draw to avoid + // popping in/out at edges + maxTextWidth = (int) renderer.getBounds("Java 2D").getWidth(); + maxTextWidth = Math.max(maxTextWidth, (int) renderer.getBounds("OpenGL").getWidth()); + + // Create random text + textInfo.clear(); + for (int i = 0; i < 100; i++) { + textInfo.add(randomTextInfo()); + } + + time = new SystemTime(); + ((SystemTime) time).rebase(); + + // Set up properties; note we don't need the depth buffer in this demo + GL gl = drawable.getGL(); + gl.glDisable(GL2.GL_DEPTH_TEST); + // Turn off vsync if we can + gl.setSwapInterval(0); + } + + public void dispose(GLAutoDrawable drawable) { + backgroundTexture = null; + renderer = null; + fps = null; + time = null; + } + + public void display(GLAutoDrawable drawable) { + time.update(); + + // Update velocities and positions of all text + float deltaT = (float) time.deltaT(); + Vec2f tmp = new Vec2f(); + for (Iterator iter = textInfo.iterator(); iter.hasNext(); ) { + TextInfo info = (TextInfo) iter.next(); + + // Randomize things a little bit at run time + if (random.nextInt(1000) == 0) { + info.angularVelocity = INIT_ANG_VEL_MAG * (randomAngle() - 180); + info.velocity = randomVelocityVec2f(INIT_VEL_MAG, INIT_VEL_MAG); + } + + // Now update angles and positions + info.angle += info.angularVelocity * deltaT; + tmp.set(info.velocity); + tmp.scale(deltaT); + info.position.add(tmp); + + // Update color + info.curTime += deltaT; + if (info.curTime > 2 * Math.PI) { + info.curTime -= 2 * Math.PI; + } + int rgb = Color.HSBtoRGB(info.h, + (float) (0.5 * (1 + Math.sin(info.curTime)) * info.s), + info.v); + info.r = ((rgb >> 16) & 0xFF) / 255.0f; + info.g = ((rgb >> 8) & 0xFF) / 255.0f; + info.b = ( rgb & 0xFF) / 255.0f; + + // Wrap angles and positions + if (info.angle < 0) { + info.angle += 360; + } else if (info.angle > 360) { + info.angle -= 360; + } + // Use maxTextWidth to avoid popping in/out at edges + // Would be better to do oriented bounding rectangle computation + if (info.position.x() < -maxTextWidth) { + info.position.setX(info.position.x() + drawable.getWidth() + 2 * maxTextWidth); + } else if (info.position.x() > drawable.getWidth() + maxTextWidth) { + info.position.setX(info.position.x() - drawable.getWidth() - 2 * maxTextWidth); + } + if (info.position.y() < -maxTextWidth) { + info.position.setY(info.position.y() + drawable.getHeight() + 2 * maxTextWidth); + } else if (info.position.y() > drawable.getHeight() + maxTextWidth) { + info.position.setY(info.position.y() - drawable.getHeight() - 2 * maxTextWidth); + } + } + + GL2 gl = drawable.getGL().getGL2(); + gl.glClear(GL2.GL_COLOR_BUFFER_BIT); + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluOrtho2D(0, drawable.getWidth(), 0, drawable.getHeight()); + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + + // Draw the background texture + backgroundTexture.enable(); + backgroundTexture.bind(); + TextureCoords coords = backgroundTexture.getImageTexCoords(); + int w = drawable.getWidth(); + int h = drawable.getHeight(); + float fw = w / 100.0f; + float fh = h / 100.0f; + gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_REPLACE); + gl.glBegin(GL2.GL_QUADS); + gl.glTexCoord2f(fw * coords.left(), fh * coords.bottom()); + gl.glVertex3f(0, 0, 0); + gl.glTexCoord2f(fw * coords.right(), fh * coords.bottom()); + gl.glVertex3f(w, 0, 0); + gl.glTexCoord2f(fw * coords.right(), fh * coords.top()); + gl.glVertex3f(w, h, 0); + gl.glTexCoord2f(fw * coords.left(), fh * coords.top()); + gl.glVertex3f(0, h, 0); + gl.glEnd(); + backgroundTexture.disable(); + + // Render all text + renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + + // Note we're doing some slightly fancy stuff to position the text. + // We tell the text renderer to render the text at the origin, and + // manipulate the modelview matrix to put the text where we want. + + gl.glMatrixMode(GL2.GL_MODELVIEW); + + // First render drop shadows + renderer.setColor(0, 0, 0, 0.5f); + for (Iterator iter = textInfo.iterator(); iter.hasNext(); ) { + TextInfo info = (TextInfo) iter.next(); + gl.glLoadIdentity(); + gl.glTranslatef(info.position.x() + dropShadowDistance, + info.position.y() - dropShadowDistance, + 0); + gl.glRotatef(info.angle, 0, 0, 1); + renderer.draw(info.text, 0, 0); + // We need to call flush() only because we're modifying the modelview matrix + renderer.flush(); + } + + // Now render the actual text + for (Iterator iter = textInfo.iterator(); iter.hasNext(); ) { + TextInfo info = (TextInfo) iter.next(); + gl.glLoadIdentity(); + gl.glTranslatef(info.position.x(), + info.position.y(), + 0); + gl.glRotatef(info.angle, 0, 0, 1); + renderer.setColor(info.r, info.g, info.b, 1); + renderer.draw(info.text, 0, 0); + // We need to call flush() only because we're modifying the modelview matrix + renderer.flush(); + } + + renderer.endRendering(); + + // Use the FPS renderer last to render the FPS + fps.draw(); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + this.width = width; + this.height = height; + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private TextInfo randomTextInfo() { + TextInfo info = new TextInfo(); + info.text = randomString(); + info.angle = randomAngle(); + info.position = randomVec2f(width, height); + + info.angularVelocity = INIT_ANG_VEL_MAG * (randomAngle() - 180); + info.velocity = randomVelocityVec2f(INIT_VEL_MAG, INIT_VEL_MAG); + + Color c = randomColor(); + float[] hsb = Color.RGBtoHSB(c.getRed(), c.getGreen(), c.getBlue(), null); + info.h = hsb[0]; + info.s = hsb[1]; + info.v = hsb[2]; + info.curTime = (float) (2 * Math.PI * random.nextFloat()); + return info; + } + + private String randomString() { + switch (random.nextInt(3)) { + case 0: + return "OpenGL"; + case 1: + return "Java 2D"; + default: + return "Text"; + } + } + + private float randomAngle() { + return 360.0f * random.nextFloat(); + } + + private Vec2f randomVec2f(float x, float y) { + return new Vec2f(x * random.nextFloat(), + y * random.nextFloat()); + } + + private Vec2f randomVelocityVec2f(float x, float y) { + return new Vec2f(x * (random.nextFloat() - 0.5f), + y * (random.nextFloat() - 0.5f)); + } + + private Color randomColor() { + // Get a bright and saturated color + float r = 0; + float g = 0; + float b = 0; + float s = 0; + do { + r = random.nextFloat(); + g = random.nextFloat(); + b = random.nextFloat(); + + float[] hsb = Color.RGBtoHSB((int) (255.0f * r), + (int) (255.0f * g), + (int) (255.0f * b), null); + s = hsb[1]; + } while ((r < 0.8f && g < 0.8f && b < 0.8f) || + s < 0.8f); + return new Color(r, g, b); + } +} diff --git a/src/demos/j2d/TestOverlay.java b/src/demos/j2d/TestOverlay.java new file mode 100755 index 0000000..f2c248d --- /dev/null +++ b/src/demos/j2d/TestOverlay.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.Overlay; +import demos.gears.Gears; +import demos.util.*; +import gleem.linalg.*; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.text.DecimalFormat; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.awt.GLCanvas; +import com.sun.opengl.util.Animator; + +/** A simple test of the Overlay utility class. Draws gears underneath + with moving Java 2D-rendered text on top. */ + +public class TestOverlay implements GLEventListener { + public static void main(String[] args) { + Frame frame = new Frame("Java 2D Overlay Test"); + GLCapabilities caps = new GLCapabilities(null); + caps.setAlphaBits(8); + GLCanvas canvas = new GLCanvas(caps); + canvas.addGLEventListener(new Gears()); + canvas.addGLEventListener(new TestOverlay()); + frame.add(canvas); + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + private Overlay overlay; + private Time time; + private Font font; + private Color TRANSPARENT_BLACK = new Color(0.0f, 0.0f, 0.0f, 0.0f); + private FontRenderContext frc; + private GlyphVector gv; + private Vec2f velocity = new Vec2f(100.0f, 150.0f); + private Vec2f position; + private Rectangle textBounds; + private Rectangle lastTextBounds; + private String TEST_STRING = "Java 2D Text"; + private long startTime; + private int frameCount; + private DecimalFormat format = new DecimalFormat("####.00"); + + private Rectangle fpsBounds; + + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + gl.setSwapInterval(0); + + overlay = new Overlay(drawable); + time = new SystemTime(); + ((SystemTime) time).rebase(); + + // Start the text half way up the left side + position = new Vec2f(0.0f, drawable.getHeight() / 2); + + // Create the font, render context, and glyph vector + font = new Font("SansSerif", Font.BOLD, 36); + } + + public void dispose(GLAutoDrawable drawable) { + font = null; + overlay = null; + time = null; + position = null; + } + + public void display(GLAutoDrawable drawable) { + if (startTime == 0) { + startTime = System.currentTimeMillis(); + } + + Graphics2D g2d = overlay.createGraphics(); + + if (++frameCount == 30) { + long endTime = System.currentTimeMillis(); + float fps = 30.0f / (float) (endTime - startTime) * 1000; + frameCount = 0; + startTime = System.currentTimeMillis(); + + FontRenderContext frc = g2d.getFontRenderContext(); + String fpsString = "FPS: " + format.format(fps); + GlyphVector gv = font.createGlyphVector(frc, TEST_STRING); + fpsBounds = gv.getPixelBounds(frc, 0, 0); + int x = drawable.getWidth() - fpsBounds.width - 20; + int y = drawable.getHeight() - 20; + g2d.setFont(font); + g2d.setComposite(AlphaComposite.Src); + g2d.setColor(TRANSPARENT_BLACK); + g2d.fillRect(x + fpsBounds.x, y + fpsBounds.y, fpsBounds.width, fpsBounds.height); + g2d.setColor(Color.WHITE); + g2d.drawString(fpsString, x, y); + overlay.markDirty(x + fpsBounds.x, y + fpsBounds.y, fpsBounds.width, fpsBounds.height); + } + + time.update(); + + g2d.setFont(font); + g2d.setComposite(AlphaComposite.Src); + if (overlay.contentsLost()) { + frc = g2d.getFontRenderContext(); + gv = font.createGlyphVector(frc, TEST_STRING); + } + + // Compute the next position of the text + position = position.plus(velocity.times((float) time.deltaT())); + // Figure out whether we have to switch directions + textBounds = gv.getPixelBounds(frc, position.x(), position.y()); + if (textBounds.getMinX() < 0) { + velocity.setX(Math.abs(velocity.x())); + } else if (textBounds.getMaxX() > drawable.getWidth()) { + velocity.setX(-1.0f * Math.abs(velocity.x())); + } + if (textBounds.getMinY() < 0) { + velocity.setY(Math.abs(velocity.y())); + } else if (textBounds.getMaxY() > drawable.getHeight()) { + velocity.setY(-1.0f * Math.abs(velocity.y())); + } + + // Clear the last text (if any) and draw the current + if (lastTextBounds != null) { + g2d.setColor(TRANSPARENT_BLACK); + g2d.fillRect((int) lastTextBounds.getMinX(), (int) lastTextBounds.getMinY(), + (int) (lastTextBounds.getWidth() + 1), (int) (lastTextBounds.getHeight() + 1)); + } else if (overlay.contentsLost()) { + g2d.setColor(TRANSPARENT_BLACK); + g2d.fillRect(0, 0, drawable.getWidth(), drawable.getHeight()); + } + g2d.setColor(Color.WHITE); + g2d.drawString(TEST_STRING, position.x(), position.y()); + + // Compute the union of these rectangles to push an update to + // the overlay + Rectangle union = new Rectangle(textBounds); + if (lastTextBounds != null) { + union.add(lastTextBounds); + } + // Put a little slop around this text due to apparent rounding errors + overlay.markDirty(union.x, union.y, union.width + 10, union.height + 10); + + // Move down the text bounds + lastTextBounds = textBounds; + + // Draw the overlay + overlay.drawAll(); + g2d.dispose(); + } + + // Unused methods + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} +} diff --git a/src/demos/j2d/TestTextRenderer.java b/src/demos/j2d/TestTextRenderer.java new file mode 100755 index 0000000..2d88a2d --- /dev/null +++ b/src/demos/j2d/TestTextRenderer.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextRenderer; +import demos.gears.Gears; +import demos.util.FPSCounter; +import demos.util.SystemTime; +import demos.util.Time; +import gleem.linalg.Vec2f; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.geom.Rectangle2D; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.awt.GLCanvas; +import com.sun.opengl.util.Animator; + + + +/** A simple test of the TextRenderer class. Draws gears underneath + with moving Java 2D-rendered text on top. */ + +public class TestTextRenderer implements GLEventListener { + + public static void main(String[] args) { + Frame frame = new Frame("Text Renderer Test"); + GLCapabilities caps = new GLCapabilities(null); + caps.setAlphaBits(8); + GLCanvas canvas = new GLCanvas(caps); + canvas.addGLEventListener(new Gears()); + canvas.addGLEventListener(new TestTextRenderer()); + frame.add(canvas); + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + + frame.setVisible(true); + animator.start(); + } + + private TextRenderer renderer; + private Time time; + private Font font; + private Vec2f velocity = new Vec2f(100.0f, 150.0f); + private Vec2f position; + private String TEST_STRING = "Java 2D Text"; + private int textWidth; + private int textHeight; + private FPSCounter fps; + + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + + // Don't artificially slow us down, at least on platforms where we + // have control over this (note: on X11 platforms this may not + // have the effect of overriding the setSwapInterval(1) in the + // Gears demo) + gl.setSwapInterval(0); + + renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36)); + time = new SystemTime(); + ((SystemTime) time).rebase(); + + // Start the text half way up the left side + position = new Vec2f(0.0f, drawable.getHeight() / 2); + Rectangle2D textBounds = renderer.getBounds(TEST_STRING); + textWidth = (int) textBounds.getWidth(); + textHeight = (int) textBounds.getHeight(); + + fps = new FPSCounter(drawable, 36); + } + + public void dispose(GLAutoDrawable drawable) { + renderer = null; + position = null; + time = null; + } + + public void display(GLAutoDrawable drawable) { + time.update(); + + // Compute the next position of the text + position = position.plus(velocity.times((float) time.deltaT())); + // Figure out whether we have to switch directions + if (position.x() < 0) { + velocity.setX(Math.abs(velocity.x())); + } else if (position.x() + textWidth > drawable.getWidth()) { + velocity.setX(-1.0f * Math.abs(velocity.x())); + } + if (position.y() < 0) { + velocity.setY(Math.abs(velocity.y())); + } else if (position.y() + textHeight > drawable.getHeight()) { + velocity.setY(-1.0f * Math.abs(velocity.y())); + } + + GL gl = drawable.getGL(); + + // Prepare to draw text + renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + + // Draw text + renderer.draw(TEST_STRING, (int) position.x(), (int) position.y()); + + // Draw FPS + fps.draw(); + + // Clean up rendering + renderer.endRendering(); + } + + // Unused methods + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} +} diff --git a/src/demos/j2d/TestTextureRenderer.java b/src/demos/j2d/TestTextureRenderer.java new file mode 100755 index 0000000..f85cce4 --- /dev/null +++ b/src/demos/j2d/TestTextureRenderer.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextureRenderer; +import demos.gears.Gears; +import demos.util.SystemTime; +import demos.util.Time; +import gleem.linalg.Vec2f; +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.GlyphVector; +import java.text.DecimalFormat; +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.glu.GLU; +import com.sun.opengl.util.Animator; + + + +/** A simple test of the TextureRenderer utility class. Draws gears + underneath with moving Java 2D-rendered text on top. */ + +public class TestTextureRenderer implements GLEventListener { + + public static void main(String[] args) { + + Frame frame = new Frame("Java 2D Renderer Test"); + GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL2)); + caps.setAlphaBits(8); + + GLCanvas canvas = new GLCanvas(caps); + canvas.addGLEventListener(new Gears()); + canvas.addGLEventListener(new TestTextureRenderer()); + frame.add(canvas); + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + private TextureRenderer renderer; + private Time time; + private Font font; + private Color TRANSPARENT_BLACK = new Color(0.0f, 0.0f, 0.0f, 0.0f); + private Vec2f velocity = new Vec2f(100.0f, 150.0f); + private Vec2f position; + private Rectangle textBounds; + private Rectangle fpsBounds; + private String TEST_STRING = "Java 2D Text"; + private GLU glu = new GLU(); + private long startTime; + private int frameCount; + private DecimalFormat format = new DecimalFormat("####.00"); + + public void init(GLAutoDrawable drawable) { + GL gl = drawable.getGL(); + gl.setSwapInterval(0); + + renderer = new TextureRenderer(256, 256, true); + time = new SystemTime(); + ((SystemTime) time).rebase(); + + // Start the text half way up the left side + position = new Vec2f(0.0f, drawable.getHeight() / 2); + + // Create the font, render context, and glyph vector + font = new Font("SansSerif", Font.BOLD, 36); + Graphics2D g2d = renderer.createGraphics(); + g2d.setFont(font); + g2d.setComposite(AlphaComposite.Src); + FontRenderContext frc = g2d.getFontRenderContext(); + GlyphVector gv = font.createGlyphVector(frc, TEST_STRING); + g2d.setColor(TRANSPARENT_BLACK); + g2d.fillRect(0, 0, renderer.getWidth(), renderer.getHeight()); + g2d.setColor(Color.WHITE); + g2d.drawString(TEST_STRING, 10, 50); + textBounds = gv.getPixelBounds(frc, 10, 50); + renderer.markDirty(textBounds.x, textBounds.y, textBounds.width, textBounds.height); + } + + public void dispose(GLAutoDrawable drawable) { + renderer = null; + textBounds = null; + position = null; + time = null; + } + + public void display(GLAutoDrawable drawable) { + if (startTime == 0) { + startTime = System.currentTimeMillis(); + } + + if (++frameCount == 100) { + long endTime = System.currentTimeMillis(); + float fps = 100.0f / (float) (endTime - startTime) * 1000; + frameCount = 0; + startTime = System.currentTimeMillis(); + + Graphics2D g2d = renderer.createGraphics(); + g2d.setFont(font); + g2d.setComposite(AlphaComposite.Src); + FontRenderContext frc = g2d.getFontRenderContext(); + String fpsString = "FPS: " + format.format(fps); + GlyphVector gv = font.createGlyphVector(frc, TEST_STRING); + fpsBounds = gv.getPixelBounds(frc, 10, 100); + g2d.setColor(TRANSPARENT_BLACK); + g2d.fillRect(fpsBounds.x, fpsBounds.y, fpsBounds.width, fpsBounds.height); + g2d.setColor(Color.WHITE); + g2d.drawString(fpsString, 10, 100); + renderer.markDirty(fpsBounds.x, fpsBounds.y, fpsBounds.width, fpsBounds.height); + } + + time.update(); + + // Compute the next position of the text + position = position.plus(velocity.times((float) time.deltaT())); + // Figure out whether we have to switch directions + Rectangle tmpBounds = new Rectangle((int) position.x(), (int) position.y(), + textBounds.width, textBounds.height); + if (tmpBounds.getMinX() < 0) { + velocity.setX(Math.abs(velocity.x())); + } else if (tmpBounds.getMaxX() > drawable.getWidth()) { + velocity.setX(-1.0f * Math.abs(velocity.x())); + } + if (tmpBounds.getMinY() < 0) { + velocity.setY(Math.abs(velocity.y())); + } else if (tmpBounds.getMaxY() > drawable.getHeight()) { + velocity.setY(-1.0f * Math.abs(velocity.y())); + } + + GL gl = drawable.getGL(); + + // Prepare to draw from the renderer's texture + renderer.beginOrthoRendering(drawable.getWidth(), drawable.getHeight()); + + // Draw from the renderer's texture + renderer.drawOrthoRect((int) position.x(), (int) position.y(), + textBounds.x, + renderer.getHeight() - textBounds.y - textBounds.height, + textBounds.width, + textBounds.height); + + // If we have the FPS, draw it + if (fpsBounds != null) { + renderer.drawOrthoRect(drawable.getWidth() - fpsBounds.width, + 20, + fpsBounds.x, + renderer.getHeight() - fpsBounds.y - fpsBounds.height, + fpsBounds.width, + fpsBounds.height); + } + + // Clean up rendering + renderer.endOrthoRendering(); + } + + // Unused methods + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {} + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} +} diff --git a/src/demos/j2d/TextCube.java b/src/demos/j2d/TextCube.java new file mode 100755 index 0000000..0c97650 --- /dev/null +++ b/src/demos/j2d/TextCube.java @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextRenderer; +import demos.common.Demo; +import demos.util.FPSCounter; +import demos.util.SystemTime; +import demos.util.Time; +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.geom.Rectangle2D; +import javax.media.opengl.GL; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GL2; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import javax.media.opengl.glu.GLU; +import com.sun.opengl.util.Animator; + + + +/** Shows how to place 2D text in 3D using the TextRenderer. */ + +public class TextCube extends Demo { + private float xAng; + private float yAng; + private GLU glu = new GLU(); + private Time time; + private TextRenderer renderer; + private FPSCounter fps; + private float textScaleFactor; + + public static void main(String[] args) { + Frame frame = new Frame("Text Cube"); + frame.setLayout(new BorderLayout()); + + GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL2)); + GLCanvas canvas = new GLCanvas(caps); + final TextCube demo = new TextCube(); + + canvas.addGLEventListener(demo); + frame.add(canvas, BorderLayout.CENTER); + + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + public void init(GLAutoDrawable drawable) { + renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 72)); + GL2 gl = drawable.getGL().getGL2(); + gl.glEnable(GL2.GL_DEPTH_TEST); + + // Compute the scale factor of the largest string which will make + // them all fit on the faces of the cube + Rectangle2D bounds = renderer.getBounds("Bottom"); + float w = (float) bounds.getWidth(); + float h = (float) bounds.getHeight(); + textScaleFactor = 1.0f / (w * 1.1f); + fps = new FPSCounter(drawable, 36); + + time = new SystemTime(); + ((SystemTime) time).rebase(); +// gl.setSwapInterval(0); + } + + public void dispose(GLAutoDrawable drawable) { + } + + public void display(GLAutoDrawable drawable) { + GL2 gl = drawable.getGL().getGL2(); + gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT); + + gl.glMatrixMode(GL2.GL_MODELVIEW); + gl.glLoadIdentity(); + glu.gluLookAt(0, 0, 10, + 0, 0, 0, + 0, 1, 0); + + // Base rotation of cube + gl.glRotatef(xAng, 1, 0, 0); + gl.glRotatef(yAng, 0, 1, 0); + + // Six faces of cube + // Top face + gl.glPushMatrix(); + gl.glRotatef(-90, 1, 0, 0); + drawFace(gl, 1.0f, 0.2f, 0.2f, 0.8f, "Top"); + gl.glPopMatrix(); + // Front face + drawFace(gl, 1.0f, 0.8f, 0.2f, 0.2f, "Front"); + // Right face + gl.glPushMatrix(); + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.2f, 0.8f, 0.2f, "Right"); + // Back face + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.8f, 0.8f, 0.2f, "Back"); + // Left face + gl.glRotatef(90, 0, 1, 0); + drawFace(gl, 1.0f, 0.2f, 0.8f, 0.8f, "Left"); + gl.glPopMatrix(); + // Bottom face + gl.glPushMatrix(); + gl.glRotatef(90, 1, 0, 0); + drawFace(gl, 1.0f, 0.8f, 0.2f, 0.8f, "Bottom"); + gl.glPopMatrix(); + + fps.draw(); + + time.update(); + xAng += 200 * (float) time.deltaT(); + yAng += 150 * (float) time.deltaT(); + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + GL2 gl = drawable.getGL().getGL2(); + gl.glMatrixMode(GL2.GL_PROJECTION); + gl.glLoadIdentity(); + glu.gluPerspective(15, (float) width / (float) height, 5, 15); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + private void drawFace(GL2 gl, + float faceSize, + float r, float g, float b, + String text) { + float halfFaceSize = faceSize / 2; + // Face is centered around the local coordinate system's z axis, + // at a z depth of faceSize / 2 + gl.glColor3f(r, g, b); + gl.glBegin(GL2.GL_QUADS); + gl.glVertex3f(-halfFaceSize, -halfFaceSize, halfFaceSize); + gl.glVertex3f( halfFaceSize, -halfFaceSize, halfFaceSize); + gl.glVertex3f( halfFaceSize, halfFaceSize, halfFaceSize); + gl.glVertex3f(-halfFaceSize, halfFaceSize, halfFaceSize); + gl.glEnd(); + + // Now draw the overlaid text. In this setting, we don't want the + // text on the backward-facing faces to be visible, so we enable + // back-face culling; and since we're drawing the text over other + // geometry, to avoid z-fighting we disable the depth test. We + // could plausibly also use glPolygonOffset but this is simpler. + // Note that because the TextRenderer pushes the enable state + // internally we don't have to reset the depth test or cull face + // bits after we're done. + renderer.begin3DRendering(); + gl.glDisable(GL2.GL_DEPTH_TEST); + gl.glEnable(GL2.GL_CULL_FACE); + // Note that the defaults for glCullFace and glFrontFace are + // GL_BACK and GL_CCW, which match the TextRenderer's definition + // of front-facing text. + Rectangle2D bounds = renderer.getBounds(text); + float w = (float) bounds.getWidth(); + float h = (float) bounds.getHeight(); + renderer.draw3D(text, + w / -2.0f * textScaleFactor, + h / -2.0f * textScaleFactor, + halfFaceSize, + textScaleFactor); + renderer.end3DRendering(); + } +} diff --git a/src/demos/j2d/TextFlow.java b/src/demos/j2d/TextFlow.java new file mode 100755 index 0000000..f3f4524 --- /dev/null +++ b/src/demos/j2d/TextFlow.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package demos.j2d; + +import com.sun.opengl.util.awt.TextRenderer; +import demos.common.Demo; +import demos.util.SystemTime; +import demos.util.Time; +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.geom.Rectangle2D; +import java.text.AttributedString; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.media.opengl.GL; +import javax.media.opengl.GLCapabilities; +import javax.media.opengl.GLProfile; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLProfile; +import javax.media.opengl.awt.GLCanvas; +import com.sun.opengl.util.Animator; + + +/** Illustrates both the TextRenderer's capability for handling + relatively large amounts of text (more than drawn on the screen -- + showing the least recently used capabilities of its internal + cache) as well as using the Java 2D text layout mechanisms in + conjunction with the TextRenderer to flow text across the + screen. */ + +public class TextFlow extends Demo { + + public static void main(String[] args) { + + Frame frame = new Frame("Text Flow"); + frame.setLayout(new BorderLayout()); + + GLCapabilities caps = new GLCapabilities(GLProfile.get(GLProfile.GL2)); + GLCanvas canvas = new GLCanvas(caps); + final TextFlow demo = new TextFlow(); + + canvas.addGLEventListener(demo); + frame.add(canvas, BorderLayout.CENTER); + + frame.setSize(512, 512); + final Animator animator = new Animator(canvas); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + // Run this on another thread than the AWT event queue to + // make sure the call to Animator.stop() completes before + // exiting + new Thread(new Runnable() { + public void run() { + animator.stop(); + System.exit(0); + } + }).start(); + } + }); + frame.setVisible(true); + animator.start(); + } + + private List/**/ lines = new ArrayList(); + private Time time; + private TextRenderer renderer; + private int curParagraph; + private float x = 30; + private float y; + private float velocity = 100; // pixels/sec + private int lineSpacing; + private int EXTRA_LINE_SPACING = 5; + + private void reflow(float width) { + lines.clear(); + lineSpacing = 0; + int numLines = 0; + FontRenderContext frc = renderer.getFontRenderContext(); + for (int i = 0; i < text.length; i++) { + String paragraph = text[i]; + Map attrs = new HashMap(); + attrs.put(TextAttribute.FONT, renderer.getFont()); + AttributedString str = new AttributedString(paragraph, attrs); + LineBreakMeasurer measurer = new LineBreakMeasurer(str.getIterator(), frc); + int curPos = 0; + while (measurer.getPosition() < paragraph.length()) { + int nextPos = measurer.nextOffset(width); + String line = paragraph.substring(curPos, nextPos); + Rectangle2D bounds = renderer.getBounds(line); + lines.add(line); + lineSpacing += (int) bounds.getHeight(); + ++numLines; + curPos = nextPos; + measurer.setPosition(curPos); + } + // Indicate end of paragraph with a null LineInfo + lines.add(null); + } + lineSpacing = (int) ((float) lineSpacing / (float) numLines) + EXTRA_LINE_SPACING; + } + + public void init(GLAutoDrawable drawable) { + renderer = new TextRenderer(new Font("SansSerif", Font.PLAIN, 36), true, false); + time = new SystemTime(); + ((SystemTime) time).rebase(); + } + + public void dispose(GLAutoDrawable drawable) { + renderer = null; + time = null; + } + + public void display(GLAutoDrawable drawable) { + time.update(); + + GL gl = drawable.getGL(); + gl.glClear(GL.GL_COLOR_BUFFER_BIT); + + float deltaT = (float) time.deltaT(); + y += velocity * deltaT; + + // Draw text starting at the specified paragraph + int paragraph = 0; + float curY = y; + renderer.beginRendering(drawable.getWidth(), drawable.getHeight()); + boolean renderedOne = false; + for (int i = 0; i < lines.size(); i++) { + String line = (String) lines.get(i); + if (line == null) { + ++paragraph; + if (paragraph >= curParagraph) { + // If this paragraph has scrolled off the top of the screen, + // don't draw it the next frame + if (paragraph > curParagraph && curY > drawable.getHeight()) { + ++curParagraph; + y = curY; + } + curY -= 2 * lineSpacing; + } + } else { + if (paragraph >= curParagraph) { + curY -= lineSpacing; + if (curY < drawable.getHeight() + lineSpacing) { + renderer.draw(line, (int) x, (int) curY); + renderedOne = true; + } + if (curY < 0) { + // Done rendering all visible lines + break; + } + } + } + } + renderer.endRendering(); + if (!renderedOne) { + // Start over + curParagraph = 0; + y = 0; + } + } + + public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { + reflow(Math.max(100, width - 60)); + } + + public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {} + + // Use some nonsense Lorem Ipsum text generated from www.lipsum.com + private static final String[] text = { + "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nulla in mi ut augue laoreet gravida. Quisque sodales vehicula ligula. Donec posuere. Morbi aliquet, odio vitae tempus mattis, odio dolor vestibulum leo, congue laoreet risus felis vitae dolor. Nulla arcu. Morbi non quam. Vestibulum pretium dolor fermentum erat. Proin dictum volutpat nibh. Morbi egestas mauris a diam. Vestibulum mauris eros, porttitor at, fermentum a, varius eu, mauris. Cras rutrum felis ut diam. Aenean porttitor risus a nunc. Aliquam et ante eu dolor pretium adipiscing. Sed fermentum, eros in dapibus lacinia, augue nunc fermentum tellus, eu egestas justo elit at mauris. Sed leo nisl, fermentum in, pretium vitae, tincidunt at, lacus. Curabitur non diam.", + "Etiam varius sagittis lorem. Vivamus iaculis condimentum tortor. Nunc sollicitudin scelerisque dolor. Nunc condimentum fringilla nisl. Fusce purus mauris, blandit eu, lacinia eget, vestibulum nec, massa. Nulla vitae libero. Suspendisse potenti. Aliquam iaculis, lorem eu adipiscing tempor, ipsum dui aliquam sem, eu vehicula leo leo eu ipsum. Pellentesque faucibus. Nullam porttitor ligula eget nibh. Cras elementum mi ac libero. Praesent pellentesque pede vitae quam. Sed nec arcu id ante cursus mollis. Suspendisse quis ipsum. Maecenas feugiat interdum neque. Nullam dui diam, convallis at, condimentum vitae, mattis vitae, metus. Integer sollicitudin, diam id lacinia posuere, quam velit fringilla dolor, eu semper sapien felis ac elit.", + "Ut a magna vitae lectus euismod hendrerit. Quisque varius consectetuer sapien. Suspendisse ligula. Nullam feugiat venenatis mauris. In consequat lorem at neque. Pellentesque libero. In eget lectus in velit auctor facilisis. Donec nec metus. Aliquam facilisis eros vel dui. Integer a diam. Donec interdum, eros faucibus blandit venenatis, ante ante ornare enim, a gravida ante lectus id metus. Ut sem.", + "Duis consectetuer leo quis elit. Suspendisse pretium nunc ac dolor. Quisque eleifend fringilla nisl. Suspendisse potenti. Duis vel ipsum at enim tincidunt consectetuer. Aliquam tempor justo nec metus. Nunc ac velit id nibh consequat vulputate. Cras vel dolor eu massa lacinia volutpat. Curabitur nibh nisi, auctor et, tincidunt eget, molestie vel, neque. Sed semper viverra neque. Nullam rhoncus hendrerit libero. Nulla adipiscing. Fusce pede nibh, lacinia a, malesuada a, dictum nec, pede. Etiam ut lorem. Donec quis massa vitae est pharetra mattis.", + "Nullam dui. Morbi nulla quam, imperdiet iaculis, consectetuer a, porttitor eu, sem. Donec id ipsum vitae nisi viverra porta. In hac habitasse platea dictumst. In ligula libero, dapibus eleifend, eleifend vel, accumsan sit amet, felis. Morbi tortor. Donec mattis ultricies arcu. Ut eget leo. Sed vel quam at ipsum sodales semper. Curabitur tincidunt quam id odio. Quisque porta, magna vel nonummy pulvinar, ligula tellus fringilla tellus, ut pharetra turpis velit ac eros. Cras eu enim vel mi suscipit malesuada. Phasellus ut orci. Aenean vitae turpis vitae lectus malesuada aliquet." + }; +} -- cgit v1.2.3