From 506b634b780dcd23aa61015c2ceba3e687196abf Mon Sep 17 00:00:00 2001 From: Kenneth Russel Date: Mon, 15 Jun 2009 22:52:34 +0000 Subject: Deleted obsolete source code in preparation for copying JOGL_2_SANDBOX on to trunk git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@1958 232f8b59-042b-4e1e-8c03-345bb8c30851 --- src/classes/com/sun/opengl/util/j2d/Overlay.java | 214 --- .../com/sun/opengl/util/j2d/TextRenderer.java | 1931 -------------------- .../com/sun/opengl/util/j2d/TextureRenderer.java | 695 ------- 3 files changed, 2840 deletions(-) delete mode 100755 src/classes/com/sun/opengl/util/j2d/Overlay.java delete mode 100755 src/classes/com/sun/opengl/util/j2d/TextRenderer.java delete mode 100755 src/classes/com/sun/opengl/util/j2d/TextureRenderer.java (limited to 'src/classes/com/sun/opengl/util/j2d') diff --git a/src/classes/com/sun/opengl/util/j2d/Overlay.java b/src/classes/com/sun/opengl/util/j2d/Overlay.java deleted file mode 100755 index 6b7ac4b9b..000000000 --- a/src/classes/com/sun/opengl/util/j2d/Overlay.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * 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 com.sun.opengl.util.j2d; - -import java.awt.Graphics2D; - -import javax.media.opengl.*; -import com.sun.opengl.util.texture.*; - -/** Provides a Java 2D overlay on top of an arbitrary GLDrawable, - making it easier to do things like draw text and images on top of - an OpenGL scene while still maintaining reasonably good - efficiency. */ - -public class Overlay { - private GLDrawable drawable; - private TextureRenderer renderer; - private boolean contentsLost; - - /** Creates a new Java 2D overlay on top of the specified - GLDrawable. */ - public Overlay(GLDrawable drawable) { - this.drawable = drawable; - } - - /** Creates a {@link java.awt.Graphics2D Graphics2D} instance for - rendering into the overlay. The returned object should be - disposed of using the normal {@link java.awt.Graphics#dispose() - Graphics.dispose()} method once it is no longer being used. - - @return a new {@link java.awt.Graphics2D Graphics2D} object for - rendering into the backing store of this renderer - */ - public Graphics2D createGraphics() { - // Validate the size of the renderer against the current size of - // the drawable - validateRenderer(); - return renderer.createGraphics(); - } - - /** Indicates whether the Java 2D contents of the overlay were lost - since the last time {@link #createGraphics} was called. This - method should be called immediately after calling {@link - #createGraphics} to see whether the entire contents of the - overlay need to be redrawn or just the region the application is - interested in updating. - - @return whether the contents of the overlay were lost since the - last render - */ - public boolean contentsLost() { - return contentsLost; - } - - /** Marks the given region of the overlay as dirty. This region, and - any previously set dirty regions, will be automatically - synchronized with the underlying Texture during the next {@link - #draw draw} or {@link #drawAll drawAll} operation, at which - point the dirty region will be cleared. It is not necessary for - an OpenGL context to be current when this method is called. - - @param x the x coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param y the y coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param width the width of the region to update - @param height the height of the region to update - - @throws GLException If an OpenGL context is not current when this method is called */ - public void markDirty(int x, int y, int width, int height) { - renderer.markDirty(x, y, width, height); - } - - /** Draws the entire contents of the overlay on top of the OpenGL - drawable. This is a convenience method which encapsulates all - portions of the rendering process; if this method is used, - {@link #beginRendering}, {@link #endRendering}, etc. should not - be used. This method should be called while the OpenGL context - for the drawable is current, and after your OpenGL scene has - been rendered. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void drawAll() throws GLException { - beginRendering(); - draw(0, 0, drawable.getWidth(), drawable.getHeight()); - endRendering(); - } - - /** Begins the OpenGL rendering process for the overlay. This is - separated out so advanced applications can render independent - pieces of the overlay to different portions of the drawable. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void beginRendering() throws GLException { - renderer.beginOrthoRendering(drawable.getWidth(), drawable.getHeight()); - } - - /** Ends the OpenGL rendering process for the overlay. This is - separated out so advanced applications can render independent - pieces of the overlay to different portions of the drawable. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void endRendering() throws GLException { - renderer.endOrthoRendering(); - } - - /** Draws the specified sub-rectangle of the overlay on top of the - OpenGL drawable. {@link #beginRendering} and {@link - #endRendering} must be used in conjunction with this method to - achieve proper rendering results. This method should be called - while the OpenGL context for the drawable is current, and after - your OpenGL scene has been rendered. - - @param x the lower-left x coordinate (relative to the lower left - of the overlay) of the rectangle to draw - @param y the lower-left y coordinate (relative to the lower left - of the overlay) of the rectangle to draw - @param width the width of the rectangle to draw - @param height the height of the rectangle to draw - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw(int x, int y, int width, int height) throws GLException { - draw(x, y, x, y, width, height); - } - - /** Draws the specified sub-rectangle of the overlay at the - specified x and y coordinate on top of the OpenGL drawable. - {@link #beginRendering} and {@link #endRendering} must be used - in conjunction with this method to achieve proper rendering - results. This method should be called while the OpenGL context - for the drawable is current, and after your OpenGL scene has - been rendered. - - @param screenx the on-screen x coordinate at which to draw the rectangle - @param screeny the on-screen y coordinate (relative to lower left) at - which to draw the rectangle - @param overlayx the x coordinate of the pixel in the overlay of - the lower left portion of the rectangle to draw - @param overlayy the y coordinate of the pixel in the overlay - (relative to lower left) of the lower left portion of the - rectangle to draw - @param width the width of the rectangle to draw - @param height the height of the rectangle to draw - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw(int screenx, int screeny, - int overlayx, int overlayy, - int width, int height) throws GLException { - renderer.drawOrthoRect(screenx, screeny, - overlayx, overlayy, - width, height); - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private void validateRenderer() { - if (renderer == null) { - renderer = new TextureRenderer(drawable.getWidth(), - drawable.getHeight(), - true); - contentsLost = true; - } else if (renderer.getWidth() != drawable.getWidth() || - renderer.getHeight() != drawable.getHeight()) { - renderer.setSize(drawable.getWidth(), drawable.getHeight()); - contentsLost = true; - } else { - contentsLost = false; - } - } -} diff --git a/src/classes/com/sun/opengl/util/j2d/TextRenderer.java b/src/classes/com/sun/opengl/util/j2d/TextRenderer.java deleted file mode 100755 index c8493affa..000000000 --- a/src/classes/com/sun/opengl/util/j2d/TextRenderer.java +++ /dev/null @@ -1,1931 +0,0 @@ -/* - * 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 com.sun.opengl.util.j2d; - -import com.sun.opengl.impl.*; -import com.sun.opengl.impl.packrect.*; -import com.sun.opengl.util.*; -import com.sun.opengl.util.texture.*; - -import java.awt.AlphaComposite; -import java.awt.Color; -import java.awt.Composite; - -// For debugging purposes -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.Frame; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Point; -import java.awt.RenderingHints; -import java.awt.event.*; -import java.awt.font.*; -import java.awt.geom.*; - -import java.nio.*; - -import java.text.*; - -import java.util.*; - -import javax.media.opengl.*; -import javax.media.opengl.glu.*; - - -/** Renders bitmapped Java 2D text into an OpenGL window with high - performance, full Unicode support, and a simple API. Performs - appropriate caching of text rendering results in an OpenGL texture - internally to avoid repeated font rasterization. The caching is - completely automatic, does not require any user intervention, and - has no visible controls in the public API.

- - Using the {@link TextRenderer TextRenderer} is simple. Add a - "TextRenderer renderer;" field to your {@link - javax.media.opengl.GLEventListener GLEventListener}. In your {@link - javax.media.opengl.GLEventListener#init init} method, add: - -

-    renderer = new TextRenderer(new Font("SansSerif", Font.BOLD, 36));
-    
- -

In the {@link javax.media.opengl.GLEventListener#display display} method of your - {@link javax.media.opengl.GLEventListener GLEventListener}, add: -

-    renderer.beginRendering(drawable.getWidth(), drawable.getHeight());
-    // optionally set the color
-    renderer.setColor(1.0f, 0.2f, 0.2f, 0.8f);
-    renderer.draw("Text to draw", xPosition, yPosition);
-    // ... more draw commands, color changes, etc.
-    renderer.endRendering();
-    
- - Unless you are sharing textures and display lists between OpenGL - contexts, you do not need to call the {@link #dispose dispose} - method of the TextRenderer; the OpenGL resources it uses - internally will be cleaned up automatically when the OpenGL - context is destroyed.

- - Note that the TextRenderer may cause the vertex and texture - coordinate array buffer bindings to change, or to be unbound. This - is important to note if you are using Vertex Buffer Objects (VBOs) - in your application.

- - Internally, the renderer uses a rectangle packing algorithm to - pack both glyphs and full Strings' rendering results (which are - variable size) onto a larger OpenGL texture. The internal backing - store is maintained using a {@link - com.sun.opengl.util.j2d.TextureRenderer TextureRenderer}. A least - recently used (LRU) algorithm is used to discard previously - rendered strings; the specific algorithm is undefined, but is - currently implemented by flushing unused Strings' rendering - results every few hundred rendering cycles, where a rendering - cycle is defined as a pair of calls to {@link #beginRendering - beginRendering} / {@link #endRendering endRendering}. - - @author John Burkey - @author Kenneth Russell -*/ -public class TextRenderer { - private static final boolean DEBUG = Debug.debug("TextRenderer"); - - // These are occasionally useful for more in-depth debugging - private static final boolean DISABLE_GLYPH_CACHE = false; - private static final boolean DRAW_BBOXES = false; - - static final int kSize = 256; - - // Every certain number of render cycles, flush the strings which - // haven't been used recently - private static final int CYCLES_PER_FLUSH = 100; - - // The amount of vertical dead space on the backing store before we - // force a compaction - private static final float MAX_VERTICAL_FRAGMENTATION = 0.7f; - static final int kQuadsPerBuffer = 100; - static final int kCoordsPerVertVerts = 3; - static final int kCoordsPerVertTex = 2; - static final int kVertsPerQuad = 4; - static final int kTotalBufferSizeVerts = kQuadsPerBuffer * kVertsPerQuad; - static final int kTotalBufferSizeCoordsVerts = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertVerts; - static final int kTotalBufferSizeCoordsTex = kQuadsPerBuffer * kVertsPerQuad * kCoordsPerVertTex; - static final int kTotalBufferSizeBytesVerts = kTotalBufferSizeCoordsVerts * 4; - static final int kTotalBufferSizeBytesTex = kTotalBufferSizeCoordsTex * 4; - static final int kSizeInBytes_OneVertices_VertexData = kCoordsPerVertVerts * 4; - static final int kSizeInBytes_OneVertices_TexData = kCoordsPerVertTex * 4; - private Font font; - private boolean antialiased; - private boolean useFractionalMetrics; - - // Whether we're attempting to use automatic mipmap generation support - private boolean mipmap; - private RectanglePacker packer; - private boolean haveMaxSize; - private RenderDelegate renderDelegate; - private TextureRenderer cachedBackingStore; - private Graphics2D cachedGraphics; - private FontRenderContext cachedFontRenderContext; - private Map /**/ stringLocations = new HashMap /**/(); - private GlyphProducer mGlyphProducer; - - private int numRenderCycles; - - // Need to keep track of whether we're in a beginRendering() / - // endRendering() cycle so we can re-enter the exact same state if - // we have to reallocate the backing store - private boolean inBeginEndPair; - private boolean isOrthoMode; - private int beginRenderingWidth; - private int beginRenderingHeight; - private boolean beginRenderingDepthTestDisabled; - - // For resetting the color after disposal of the old backing store - private boolean haveCachedColor; - private float cachedR; - private float cachedG; - private float cachedB; - private float cachedA; - private Color cachedColor; - private boolean needToResetColor; - - // For debugging only - private Frame dbgFrame; - - // Debugging purposes only - private boolean debugged; - Pipelined_QuadRenderer mPipelinedQuadRenderer; - - //emzic: added boolean flag - private boolean useVertexArrays = true; - - //emzic: added boolean flag - private boolean isExtensionAvailable_GL_VERSION_1_5; - private boolean checkFor_isExtensionAvailable_GL_VERSION_1_5; - - // Whether GL_LINEAR filtering is enabled for the backing store - private boolean smoothing = true; - - /** Creates a new TextRenderer with the given font, using no - antialiasing or fractional metrics, and the default - RenderDelegate. Equivalent to TextRenderer(font, false, - false). - - @param font the font to render with - */ - public TextRenderer(Font font) { - this(font, false, false, null, false); - } - - /** Creates a new TextRenderer with the given font, using no - antialiasing or fractional metrics, and the default - RenderDelegate. If mipmap is true, attempts to use - OpenGL's automatic mipmap generation for better smoothing when - rendering the TextureRenderer's contents at a distance. - Equivalent to TextRenderer(font, false, false). - - @param font the font to render with - @param mipmap whether to attempt use of automatic mipmap generation - */ - public TextRenderer(Font font, boolean mipmap) { - this(font, false, false, null, mipmap); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and default RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. No mipmap support is requested. Equivalent to - TextRenderer(font, antialiased, useFractionalMetrics, - null). - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics) { - this(font, antialiased, useFractionalMetrics, null, false); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and given RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. The renderDelegate provides more control - over the text rendered. No mipmap support is requested. - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - @param renderDelegate the render delegate to use to draw the - text's bitmap, or null to use the default one - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate) { - this(font, antialiased, useFractionalMetrics, renderDelegate, false); - } - - /** Creates a new TextRenderer with the given Font, specified font - properties, and given RenderDelegate. The - antialiased and useFractionalMetrics - flags provide control over the same properties at the Java 2D - level. The renderDelegate provides more control - over the text rendered. If mipmap is true, attempts - to use OpenGL's automatic mipmap generation for better smoothing - when rendering the TextureRenderer's contents at a distance. - - @param font the font to render with - @param antialiased whether to use antialiased fonts - @param useFractionalMetrics whether to use fractional font - metrics at the Java 2D level - @param renderDelegate the render delegate to use to draw the - text's bitmap, or null to use the default one - @param mipmap whether to attempt use of automatic mipmap generation - */ - public TextRenderer(Font font, boolean antialiased, - boolean useFractionalMetrics, RenderDelegate renderDelegate, - boolean mipmap) { - this.font = font; - this.antialiased = antialiased; - this.useFractionalMetrics = useFractionalMetrics; - this.mipmap = mipmap; - - // FIXME: consider adjusting the size based on font size - // (it will already automatically resize if necessary) - packer = new RectanglePacker(new Manager(), kSize, kSize); - - if (renderDelegate == null) { - renderDelegate = new DefaultRenderDelegate(); - } - - this.renderDelegate = renderDelegate; - - mGlyphProducer = new GlyphProducer(font.getNumGlyphs()); - } - - /** Returns the bounding rectangle of the given String, assuming it - was rendered at the origin. See {@link #getBounds(CharSequence) - getBounds(CharSequence)}. */ - public Rectangle2D getBounds(String str) { - return getBounds((CharSequence) str); - } - - /** Returns the bounding rectangle of the given CharSequence, - assuming it was rendered at the origin. The coordinate system of - the returned rectangle is Java 2D's, with increasing Y - coordinates in the downward direction. The relative coordinate - (0, 0) in the returned rectangle corresponds to the baseline of - the leftmost character of the rendered string, in similar - fashion to the results returned by, for example, {@link - java.awt.font.GlyphVector#getVisualBounds}. Most applications - will use only the width and height of the returned Rectangle for - the purposes of centering or justifying the String. It is not - specified which Java 2D bounds ({@link - java.awt.font.GlyphVector#getVisualBounds getVisualBounds}, - {@link java.awt.font.GlyphVector#getPixelBounds getPixelBounds}, - etc.) the returned bounds correspond to, although every effort - is made to ensure an accurate bound. */ - public Rectangle2D getBounds(CharSequence str) { - // FIXME: this should be more optimized and use the glyph cache - Rect r = null; - - if ((r = (Rect) stringLocations.get(str)) != null) { - TextData data = (TextData) r.getUserData(); - - // Reconstitute the Java 2D results based on the cached values - return new Rectangle2D.Double(-data.origin().x, -data.origin().y, - r.w(), r.h()); - } - - // Must return a Rectangle compatible with the layout algorithm -- - // must be idempotent - return normalize(renderDelegate.getBounds(str, font, - getFontRenderContext())); - } - - /** Returns the Font this renderer is using. */ - public Font getFont() { - return font; - } - - /** Returns a FontRenderContext which can be used for external - text-related size computations. This object should be considered - transient and may become invalidated between {@link - #beginRendering beginRendering} / {@link #endRendering - endRendering} pairs. */ - public FontRenderContext getFontRenderContext() { - if (cachedFontRenderContext == null) { - cachedFontRenderContext = getGraphics2D().getFontRenderContext(); - } - - return cachedFontRenderContext; - } - - /** Begins rendering with this {@link TextRenderer TextRenderer} - into the current OpenGL drawable, pushing the projection and - modelview matrices and some state bits and setting up a - two-dimensional orthographic projection with (0, 0) as the - lower-left coordinate and (width, height) as the upper-right - coordinate. Binds and enables the internal OpenGL texture - object, sets the texture environment mode to GL_MODULATE, and - changes the current color to the last color set with this - TextRenderer via {@link #setColor setColor}. This method - disables the depth test and is equivalent to - beginRendering(width, height, true). - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - @throws javax.media.opengl.GLException If an OpenGL context is not current when this method is called - */ - public void beginRendering(int width, int height) throws GLException { - beginRendering(width, height, true); - } - - /** Begins rendering with this {@link TextRenderer TextRenderer} - into the current OpenGL drawable, pushing the projection and - modelview matrices and some state bits and setting up a - two-dimensional orthographic projection with (0, 0) as the - lower-left coordinate and (width, height) as the upper-right - coordinate. Binds and enables the internal OpenGL texture - object, sets the texture environment mode to GL_MODULATE, and - changes the current color to the last color set with this - TextRenderer via {@link #setColor setColor}. Disables the depth - test if the disableDepthTest argument is true. - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - @param disableDepthTest whether to disable the depth test - @throws GLException If an OpenGL context is not current when this method is called - */ - public void beginRendering(int width, int height, boolean disableDepthTest) - throws GLException { - beginRendering(true, width, height, disableDepthTest); - } - - /** Begins rendering of 2D text in 3D with this {@link TextRenderer - TextRenderer} into the current OpenGL drawable. Assumes the end - user is responsible for setting up the modelview and projection - matrices, and will render text using the {@link #draw3D draw3D} - method. This method pushes some OpenGL state bits, binds and - enables the internal OpenGL texture object, sets the texture - environment mode to GL_MODULATE, and changes the current color - to the last color set with this TextRenderer via {@link - #setColor setColor}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void begin3DRendering() throws GLException { - beginRendering(false, 0, 0, false); - } - - /** Changes the current color of this TextRenderer to the supplied - one. The default color is opaque white. - - @param color the new color to use for rendering text - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(Color color) throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor != null) && - color.equals(cachedColor)); - - if (!noNeedForFlush) { - flushGlyphPipeline(); - } - - getBackingStore().setColor(color); - haveCachedColor = true; - cachedColor = color; - } - - /** Changes the current color of this TextRenderer to the supplied - one, where each component ranges from 0.0f - 1.0f. The alpha - component, if used, does not need to be premultiplied into the - color channels as described in the documentation for {@link - com.sun.opengl.util.texture.Texture Texture}, although - premultiplied colors are used internally. The default color is - opaque white. - - @param r the red component of the new color - @param g the green component of the new color - @param b the blue component of the new color - @param a the alpha component of the new color, 0.0f = completely - transparent, 1.0f = completely opaque - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(float r, float g, float b, float a) - throws GLException { - boolean noNeedForFlush = (haveCachedColor && (cachedColor == null) && - (r == cachedR) && (g == cachedG) && (b == cachedB) && - (a == cachedA)); - - if (!noNeedForFlush) { - flushGlyphPipeline(); - } - - getBackingStore().setColor(r, g, b, a); - haveCachedColor = true; - cachedR = r; - cachedG = g; - cachedB = b; - cachedA = a; - cachedColor = null; - } - - /** Draws the supplied CharSequence at the desired location using - the renderer's current color. The baseline of the leftmost - character is at position (x, y) specified in OpenGL coordinates, - where the origin is at the lower-left of the drawable and the Y - coordinate increases in the upward direction. - - @param str the string to draw - @param x the x coordinate at which to draw - @param y the y coordinate at which to draw - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw(CharSequence str, int x, int y) throws GLException { - draw3D(str, x, y, 0, 1); - } - - /** Draws the supplied String at the desired location using the - renderer's current color. See {@link #draw(CharSequence, int, - int) draw(CharSequence, int, int)}. */ - public void draw(String str, int x, int y) throws GLException { - draw3D(str, x, y, 0, 1); - } - - /** Draws the supplied CharSequence at the desired 3D location using - the renderer's current color. The baseline of the leftmost - character is placed at position (x, y, z) in the current - coordinate system. - - @param str the string to draw - @param x the x coordinate at which to draw - @param y the y coordinate at which to draw - @param z the z coordinate at which to draw - @param scaleFactor a uniform scale factor applied to the width and height of the drawn rectangle - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { - internal_draw3D(str, x, y, z, scaleFactor); - } - - /** Draws the supplied String at the desired 3D location using the - renderer's current color. See {@link #draw3D(CharSequence, - float, float, float, float) draw3D(CharSequence, float, float, - float, float)}. */ - public void draw3D(String str, float x, float y, float z, float scaleFactor) { - internal_draw3D(str, x, y, z, scaleFactor); - } - - /** Returns the pixel width of the given character. */ - public float getCharWidth(char inChar) { - return mGlyphProducer.getGlyphPixelWidth(inChar); - } - - /** Causes the TextRenderer to flush any internal caches it may be - maintaining and draw its rendering results to the screen. This - should be called after each call to draw() if you are setting - OpenGL state such as the modelview matrix between calls to - draw(). */ - public void flush() { - flushGlyphPipeline(); - } - - /** Ends a render cycle with this {@link TextRenderer TextRenderer}. - Restores the projection and modelview matrices as well as - several OpenGL state bits. Should be paired with {@link - #beginRendering beginRendering}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void endRendering() throws GLException { - endRendering(true); - } - - /** Ends a 3D render cycle with this {@link TextRenderer TextRenderer}. - Restores several OpenGL state bits. Should be paired with {@link - #begin3DRendering begin3DRendering}. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void end3DRendering() throws GLException { - endRendering(false); - } - - /** Disposes of all resources this TextRenderer is using. It is not - valid to use the TextRenderer after this method is called. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void dispose() throws GLException { - packer.dispose(); - packer = null; - cachedBackingStore = null; - cachedGraphics = null; - cachedFontRenderContext = null; - - if (dbgFrame != null) { - dbgFrame.dispose(); - } - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private static Rectangle2D preNormalize(Rectangle2D src) { - // Need to round to integer coordinates - // Also give ourselves a little slop around the reported - // bounds of glyphs because it looks like neither the visual - // nor the pixel bounds works perfectly well - int minX = (int) Math.floor(src.getMinX()) - 1; - int minY = (int) Math.floor(src.getMinY()) - 1; - int maxX = (int) Math.ceil(src.getMaxX()) + 1; - int maxY = (int) Math.ceil(src.getMaxY()) + 1; - return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY); - } - - - private Rectangle2D normalize(Rectangle2D src) { - // Give ourselves a boundary around each entity on the backing - // store in order to prevent bleeding of nearby Strings due to - // the fact that we use linear filtering - - // NOTE that this boundary is quite heuristic and is related - // to how far away in 3D we may view the text -- - // heuristically, 1.5% of the font's height - int boundary = (int) Math.max(1, 0.015 * font.getSize()); - - return new Rectangle2D.Double((int) Math.floor(src.getMinX() - boundary), - (int) Math.floor(src.getMinY() - boundary), - (int) Math.ceil(src.getWidth() + 2 * boundary), - (int) Math.ceil(src.getHeight()) + 2 * boundary); - } - - private TextureRenderer getBackingStore() { - TextureRenderer renderer = (TextureRenderer) packer.getBackingStore(); - - if (renderer != cachedBackingStore) { - // Backing store changed since last time; discard any cached Graphics2D - if (cachedGraphics != null) { - cachedGraphics.dispose(); - cachedGraphics = null; - cachedFontRenderContext = null; - } - - cachedBackingStore = renderer; - } - - return cachedBackingStore; - } - - private Graphics2D getGraphics2D() { - TextureRenderer renderer = getBackingStore(); - - if (cachedGraphics == null) { - cachedGraphics = renderer.createGraphics(); - - // Set up composite, font and rendering hints - cachedGraphics.setComposite(AlphaComposite.Src); - cachedGraphics.setColor(Color.WHITE); - cachedGraphics.setFont(font); - cachedGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - (antialiased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON - : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF)); - cachedGraphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, - (useFractionalMetrics - ? RenderingHints.VALUE_FRACTIONALMETRICS_ON - : RenderingHints.VALUE_FRACTIONALMETRICS_OFF)); - } - - return cachedGraphics; - } - - private void beginRendering(boolean ortho, int width, int height, - boolean disableDepthTestForOrtho) { - if (DEBUG && !debugged) { - debug(); - } - - inBeginEndPair = true; - isOrthoMode = ortho; - beginRenderingWidth = width; - beginRenderingHeight = height; - beginRenderingDepthTestDisabled = disableDepthTestForOrtho; - - if (ortho) { - getBackingStore().beginOrthoRendering(width, height, - disableDepthTestForOrtho); - } else { - getBackingStore().begin3DRendering(); - } - - GL gl = GLU.getCurrentGL(); - - // Push client attrib bits used by the pipelined quad renderer - gl.glPushClientAttrib((int) GL.GL_ALL_CLIENT_ATTRIB_BITS); - - if (!haveMaxSize) { - // Query OpenGL for the maximum texture size and set it in the - // RectanglePacker to keep it from expanding too large - int[] sz = new int[1]; - gl.glGetIntegerv(GL.GL_MAX_TEXTURE_SIZE, sz, 0); - packer.setMaxSize(sz[0], sz[0]); - haveMaxSize = true; - } - - if (needToResetColor && haveCachedColor) { - if (cachedColor == null) { - getBackingStore().setColor(cachedR, cachedG, cachedB, cachedA); - } else { - getBackingStore().setColor(cachedColor); - } - - needToResetColor = false; - } - - // Disable future attempts to use mipmapping if TextureRenderer - // doesn't support it - if (mipmap && !getBackingStore().isUsingAutoMipmapGeneration()) { - if (DEBUG) { - System.err.println("Disabled mipmapping in TextRenderer"); - } - - mipmap = false; - } - } - - /** - * emzic: here the call to glBindBuffer crashes on certain graphicscard/driver combinations - * this is why the ugly try-catch block has been added, which falls back to the old textrenderer - * - * @param ortho - * @throws GLException - */ - private void endRendering(boolean ortho) throws GLException { - flushGlyphPipeline(); - - inBeginEndPair = false; - - GL gl = GLU.getCurrentGL(); - - // Pop client attrib bits used by the pipelined quad renderer - gl.glPopClientAttrib(); - - // The OpenGL spec is unclear about whether this changes the - // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER - // binding - if (is15Available(gl)) { - try { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - } - } - - if (ortho) { - getBackingStore().endOrthoRendering(); - } else { - getBackingStore().end3DRendering(); - } - - if (++numRenderCycles >= CYCLES_PER_FLUSH) { - numRenderCycles = 0; - - if (DEBUG) { - System.err.println("Clearing unused entries in endRendering()"); - } - - clearUnusedEntries(); - } - } - - private void clearUnusedEntries() { - final java.util.List deadRects = new ArrayList /**/(); - - // Iterate through the contents of the backing store, removing - // text strings that haven't been used recently - packer.visit(new RectVisitor() { - public void visit(Rect rect) { - TextData data = (TextData) rect.getUserData(); - - if (data.used()) { - data.clearUsed(); - } else { - deadRects.add(rect); - } - } - }); - - for (Iterator iter = deadRects.iterator(); iter.hasNext();) { - Rect r = (Rect) iter.next(); - packer.remove(r); - stringLocations.remove(((TextData) r.getUserData()).string()); - - int unicodeToClearFromCache = ((TextData) r.getUserData()).unicodeID; - - if (unicodeToClearFromCache > 0) { - mGlyphProducer.clearCacheEntry(unicodeToClearFromCache); - } - - // if (DEBUG) { - // Graphics2D g = getGraphics2D(); - // g.setComposite(AlphaComposite.Clear); - // g.fillRect(r.x(), r.y(), r.w(), r.h()); - // g.setComposite(AlphaComposite.Src); - // } - } - - // If we removed dead rectangles this cycle, try to do a compaction - float frag = packer.verticalFragmentationRatio(); - - if (!deadRects.isEmpty() && (frag > MAX_VERTICAL_FRAGMENTATION)) { - if (DEBUG) { - System.err.println( - "Compacting TextRenderer backing store due to vertical fragmentation " + - frag); - } - - packer.compact(); - } - - if (DEBUG) { - getBackingStore().markDirty(0, 0, getBackingStore().getWidth(), - getBackingStore().getHeight()); - } - } - - private void internal_draw3D(CharSequence str, float x, float y, float z, - float scaleFactor) { - List/**/ glyphs = mGlyphProducer.getGlyphs(str); - for (Iterator iter = glyphs.iterator(); iter.hasNext(); ) { - Glyph glyph = (Glyph) iter.next(); - float advance = glyph.draw3D(x, y, z, scaleFactor); - x += advance * scaleFactor; - } - } - - private void flushGlyphPipeline() { - if (mPipelinedQuadRenderer != null) { - mPipelinedQuadRenderer.draw(); - } - } - - private void draw3D_ROBUST(CharSequence str, float x, float y, float z, - float scaleFactor) { - String curStr; - if (str instanceof String) { - curStr = (String) str; - } else { - curStr = str.toString(); - } - - // Look up the string on the backing store - Rect rect = (Rect) stringLocations.get(curStr); - - if (rect == null) { - // Rasterize this string and place it on the backing store - Graphics2D g = getGraphics2D(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(curStr, font, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), - (int) -bbox.getMinY()); - rect = new Rect(0, 0, (int) bbox.getWidth(), - (int) bbox.getHeight(), - new TextData(curStr, origin, origBBox, -1)); - - packer.add(rect); - stringLocations.put(curStr, rect); - - // Re-fetch the Graphics2D in case the addition of the rectangle - // caused the old backing store to be thrown away - g = getGraphics2D(); - - // OK, should now have an (x, y) for this rectangle; rasterize - // the String - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; - - // Clear out the area we're going to draw into - g.setComposite(AlphaComposite.Clear); - g.fillRect(rect.x(), rect.y(), rect.w(), rect.h()); - g.setComposite(AlphaComposite.Src); - - // Draw the string - renderDelegate.draw(g, curStr, strx, stry); - - if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); - // Draw a bounding box on the backing store - g.drawRect(strx - data.origOriginX(), - stry - data.origOriginY(), - (int) data.origRect().getWidth(), - (int) data.origRect().getHeight()); - g.drawRect(strx - data.origin().x, - stry - data.origin().y, - rect.w(), - rect.h()); - } - - // Mark this region of the TextureRenderer as dirty - getBackingStore().markDirty(rect.x(), rect.y(), rect.w(), - rect.h()); - } - - // OK, now draw the portion of the backing store to the screen - TextureRenderer renderer = getBackingStore(); - - // NOTE that the rectangles managed by the packer have their - // origin at the upper-left but the TextureRenderer's origin is - // at its lower left!!! - TextData data = (TextData) rect.getUserData(); - data.markUsed(); - - Rectangle2D origRect = data.origRect(); - - // Align the leftmost point of the baseline to the (x, y, z) coordinate requested - renderer.draw3DRect(x - (scaleFactor * data.origOriginX()), - y - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())), z, - rect.x() + (data.origin().x - data.origOriginX()), - renderer.getHeight() - rect.y() - (int) origRect.getHeight() - - (data.origin().y - data.origOriginY()), - (int) origRect.getWidth(), (int) origRect.getHeight(), scaleFactor); - } - - //---------------------------------------------------------------------- - // Debugging functionality - // - private void debug() { - dbgFrame = new Frame("TextRenderer Debug Output"); - - GLCanvas dbgCanvas = new GLCanvas(new GLCapabilities(), null, - GLContext.getCurrent(), null); - dbgCanvas.addGLEventListener(new DebugListener(dbgFrame)); - dbgFrame.add(dbgCanvas); - - final FPSAnimator anim = new FPSAnimator(dbgCanvas, 10); - dbgFrame.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() { - anim.stop(); - } - }).start(); - } - }); - dbgFrame.setSize(kSize, kSize); - dbgFrame.setVisible(true); - anim.start(); - debugged = true; - } - - /** Class supporting more full control over the process of rendering - the bitmapped text. Allows customization of whether the backing - store text bitmap is full-color or intensity only, the size of - each individual rendered text rectangle, and the contents of - each individual rendered text string. The default implementation - of this interface uses an intensity-only texture, a - closely-cropped rectangle around the text, and renders text - using the color white, which is modulated by the set color - during the rendering process. */ - public static interface RenderDelegate { - /** Indicates whether the backing store of this TextRenderer - should be intensity-only (the default) or full-color. */ - public boolean intensityOnly(); - - /** Computes the bounds of the given String relative to the - origin. */ - public Rectangle2D getBounds(String str, Font font, - FontRenderContext frc); - - /** Computes the bounds of the given character sequence relative - to the origin. */ - public Rectangle2D getBounds(CharSequence str, Font font, - FontRenderContext frc); - - /** Computes the bounds of the given GlyphVector, already - assumed to have been created for a particular Font, - relative to the origin. */ - public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc); - - /** Render the passed character sequence at the designated - location using the supplied Graphics2D instance. The - surrounding region will already have been cleared to the RGB - color (0, 0, 0) with zero alpha. The initial drawing context - of the passed Graphics2D will be set to use - AlphaComposite.Src, the color white, the Font specified in the - TextRenderer's constructor, and the rendering hints specified - in the TextRenderer constructor. Changes made by the end user - may be visible in successive calls to this method, but are not - guaranteed to be preserved. Implementors of this method - should reset the Graphics2D's state to that desired each time - this method is called, in particular those states which are - not the defaults. */ - public void draw(Graphics2D graphics, String str, int x, int y); - - /** Render the passed GlyphVector at the designated location using - the supplied Graphics2D instance. The surrounding region will - already have been cleared to the RGB color (0, 0, 0) with zero - alpha. The initial drawing context of the passed Graphics2D - will be set to use AlphaComposite.Src, the color white, the - Font specified in the TextRenderer's constructor, and the - rendering hints specified in the TextRenderer constructor. - Changes made by the end user may be visible in successive - calls to this method, but are not guaranteed to be preserved. - Implementors of this method should reset the Graphics2D's - state to that desired each time this method is called, in - particular those states which are not the defaults. */ - public void drawGlyphVector(Graphics2D graphics, GlyphVector str, - int x, int y); - } - - private static class CharSequenceIterator implements CharacterIterator { - CharSequence mSequence; - int mLength; - int mCurrentIndex; - - CharSequenceIterator() { - } - - CharSequenceIterator(CharSequence sequence) { - initFromCharSequence(sequence); - } - - public void initFromCharSequence(CharSequence sequence) { - mSequence = sequence; - mLength = mSequence.length(); - mCurrentIndex = 0; - } - - public char last() { - mCurrentIndex = Math.max(0, mLength - 1); - - return current(); - } - - public char current() { - if ((mLength == 0) || (mCurrentIndex >= mLength)) { - return CharacterIterator.DONE; - } - - return mSequence.charAt(mCurrentIndex); - } - - public char next() { - mCurrentIndex++; - - return current(); - } - - public char previous() { - mCurrentIndex = Math.max(mCurrentIndex - 1, 0); - - return current(); - } - - public char setIndex(int position) { - mCurrentIndex = position; - - return current(); - } - - public int getBeginIndex() { - return 0; - } - - public int getEndIndex() { - return mLength; - } - - public int getIndex() { - return mCurrentIndex; - } - - public Object clone() { - CharSequenceIterator iter = new CharSequenceIterator(mSequence); - iter.mCurrentIndex = mCurrentIndex; - - return iter; - } - - public char first() { - if (mLength == 0) { - return CharacterIterator.DONE; - } - - mCurrentIndex = 0; - - return current(); - } - } - - // Data associated with each rectangle of text - static class TextData { - // Back-pointer to String this TextData describes, if it - // represents a String rather than a single glyph - private String str; - - // If this TextData represents a single glyph, this is its - // unicode ID - int unicodeID; - - // The following must be defined and used VERY precisely. This is - // the offset from the upper-left corner of this rectangle (Java - // 2D coordinate system) at which the string must be rasterized in - // order to fit within the rectangle -- the leftmost point of the - // baseline. - private Point origin; - - // This represents the pre-normalized rectangle, which fits - // within the rectangle on the backing store. We keep a - // one-pixel border around entries on the backing store to - // prevent bleeding of adjacent letters when using GL_LINEAR - // filtering for rendering. The origin of this rectangle is - // equivalent to the origin above. - private Rectangle2D origRect; - - private boolean used; // Whether this text was used recently - - TextData(String str, Point origin, Rectangle2D origRect, int unicodeID) { - this.str = str; - this.origin = origin; - this.origRect = origRect; - this.unicodeID = unicodeID; - } - - String string() { - return str; - } - - Point origin() { - return origin; - } - - // The following three methods are used to locate the glyph - // within the expanded rectangle coming from normalize() - int origOriginX() { - return (int) -origRect.getMinX(); - } - - int origOriginY() { - return (int) -origRect.getMinY(); - } - - Rectangle2D origRect() { - return origRect; - } - - boolean used() { - return used; - } - - void markUsed() { - used = true; - } - - void clearUsed() { - used = false; - } - } - - class Manager implements BackingStoreManager { - private Graphics2D g; - - public Object allocateBackingStore(int w, int h) { - // FIXME: should consider checking Font's attributes to see - // whether we're likely to need to support a full RGBA backing - // store (i.e., non-default Paint, foreground color, etc.), but - // for now, let's just be more efficient - TextureRenderer renderer; - - if (renderDelegate.intensityOnly()) { - renderer = TextureRenderer.createAlphaOnlyRenderer(w, h, mipmap); - } else { - renderer = new TextureRenderer(w, h, true, mipmap); - } - renderer.setSmoothing(smoothing); - - if (DEBUG) { - System.err.println(" TextRenderer allocating backing store " + - w + " x " + h); - } - - return renderer; - } - - public void deleteBackingStore(Object backingStore) { - ((TextureRenderer) backingStore).dispose(); - } - - public boolean preExpand(Rect cause, int attemptNumber) { - // Only try this one time; clear out potentially obsolete entries - // NOTE: this heuristic and the fact that it clears the used bit - // of all entries seems to cause cycling of entries in some - // situations, where the backing store becomes small compared to - // the amount of text on the screen (see the TextFlow demo) and - // the entries continually cycle in and out of the backing - // store, decreasing performance. If we added a little age - // information to the entries, and only cleared out entries - // above a certain age, this behavior would be eliminated. - // However, it seems the system usually stabilizes itself, so - // for now we'll just keep things simple. Note that if we don't - // clear the used bit here, the backing store tends to increase - // very quickly to its maximum size, at least with the TextFlow - // demo when the text is being continually re-laid out. - if (attemptNumber == 0) { - if (DEBUG) { - System.err.println( - "Clearing unused entries in preExpand(): attempt number " + - attemptNumber); - } - - if (inBeginEndPair) { - // Draw any outstanding glyphs - flush(); - } - - clearUnusedEntries(); - - return true; - } - - return false; - } - - public void additionFailed(Rect cause, int attemptNumber) { - // Heavy hammer -- might consider doing something different - packer.clear(); - stringLocations.clear(); - mGlyphProducer.clearAllCacheEntries(); - - if (DEBUG) { - System.err.println( - " *** Cleared all text because addition failed ***"); - } - } - - public void beginMovement(Object oldBackingStore, Object newBackingStore) { - // Exit the begin / end pair if necessary - if (inBeginEndPair) { - // Draw any outstanding glyphs - flush(); - - GL gl = GLU.getCurrentGL(); - - // Pop client attrib bits used by the pipelined quad renderer - gl.glPopClientAttrib(); - - // The OpenGL spec is unclear about whether this changes the - // buffer bindings, so preemptively zero out the GL_ARRAY_BUFFER - // binding - if (is15Available(gl)) { - try { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0); - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - } - } - - if (isOrthoMode) { - ((TextureRenderer) oldBackingStore).endOrthoRendering(); - } else { - ((TextureRenderer) oldBackingStore).end3DRendering(); - } - } - - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - g = newRenderer.createGraphics(); - } - - public void move(Object oldBackingStore, Rect oldLocation, - Object newBackingStore, Rect newLocation) { - TextureRenderer oldRenderer = (TextureRenderer) oldBackingStore; - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - - if (oldRenderer == newRenderer) { - // Movement on the same backing store -- easy case - g.copyArea(oldLocation.x(), oldLocation.y(), oldLocation.w(), - oldLocation.h(), newLocation.x() - oldLocation.x(), - newLocation.y() - oldLocation.y()); - } else { - // Need to draw from the old renderer's image into the new one - Image img = oldRenderer.getImage(); - g.drawImage(img, newLocation.x(), newLocation.y(), - newLocation.x() + newLocation.w(), - newLocation.y() + newLocation.h(), oldLocation.x(), - oldLocation.y(), oldLocation.x() + oldLocation.w(), - oldLocation.y() + oldLocation.h(), null); - } - } - - public void endMovement(Object oldBackingStore, Object newBackingStore) { - g.dispose(); - - // Sync the whole surface - TextureRenderer newRenderer = (TextureRenderer) newBackingStore; - newRenderer.markDirty(0, 0, newRenderer.getWidth(), - newRenderer.getHeight()); - - // Re-enter the begin / end pair if necessary - if (inBeginEndPair) { - if (isOrthoMode) { - ((TextureRenderer) newBackingStore).beginOrthoRendering(beginRenderingWidth, - beginRenderingHeight, beginRenderingDepthTestDisabled); - } else { - ((TextureRenderer) newBackingStore).begin3DRendering(); - } - - // Push client attrib bits used by the pipelined quad renderer - GL gl = GLU.getCurrentGL(); - gl.glPushClientAttrib((int) GL.GL_ALL_CLIENT_ATTRIB_BITS); - - if (haveCachedColor) { - if (cachedColor == null) { - ((TextureRenderer) newBackingStore).setColor(cachedR, - cachedG, cachedB, cachedA); - } else { - ((TextureRenderer) newBackingStore).setColor(cachedColor); - } - } - } else { - needToResetColor = true; - } - } - } - - public static class DefaultRenderDelegate implements RenderDelegate { - public boolean intensityOnly() { - return true; - } - - public Rectangle2D getBounds(CharSequence str, Font font, - FontRenderContext frc) { - return getBounds(font.createGlyphVector(frc, - new CharSequenceIterator(str)), - frc); - } - - public Rectangle2D getBounds(String str, Font font, - FontRenderContext frc) { - return getBounds(font.createGlyphVector(frc, str), frc); - } - - public Rectangle2D getBounds(GlyphVector gv, FontRenderContext frc) { - return gv.getVisualBounds(); - } - - public void drawGlyphVector(Graphics2D graphics, GlyphVector str, - int x, int y) { - graphics.drawGlyphVector(str, x, y); - } - - public void draw(Graphics2D graphics, String str, int x, int y) { - graphics.drawString(str, x, y); - } - } - - //---------------------------------------------------------------------- - // Glyph-by-glyph rendering support - // - - // A temporary to prevent excessive garbage creation - private char[] singleUnicode = new char[1]; - - /** A Glyph represents either a single unicode glyph or a - substring of characters to be drawn. The reason for the dual - behavior is so that we can take in a sequence of unicode - characters and partition them into runs of individual glyphs, - but if we encounter complex text and/or unicode sequences we - don't understand, we can render them using the - string-by-string method.

- - Glyphs need to be able to re-upload themselves to the backing - store on demand as we go along in the render sequence. - */ - - class Glyph { - // If this Glyph represents an individual unicode glyph, this - // is its unicode ID. If it represents a String, this is -1. - private int unicodeID; - // If the above field isn't -1, then these fields are used. - // The glyph code in the font - private int glyphCode; - // The GlyphProducer which created us - private GlyphProducer producer; - // The advance of this glyph - private float advance; - // The GlyphVector for this single character; this is passed - // in during construction but cleared during the upload - // process - private GlyphVector singleUnicodeGlyphVector; - // The rectangle of this glyph on the backing store, or null - // if it has been cleared due to space pressure - private Rect glyphRectForTextureMapping; - // If this Glyph represents a String, this is the sequence of - // characters - private String str; - // Whether we need a valid advance when rendering this string - // (i.e., whether it has other single glyphs coming after it) - private boolean needAdvance; - - // Creates a Glyph representing an individual Unicode character - public Glyph(int unicodeID, - int glyphCode, - float advance, - GlyphVector singleUnicodeGlyphVector, - GlyphProducer producer) { - this.unicodeID = unicodeID; - this.glyphCode = glyphCode; - this.advance = advance; - this.singleUnicodeGlyphVector = singleUnicodeGlyphVector; - this.producer = producer; - } - - // Creates a Glyph representing a sequence of characters, with - // an indication of whether additional single glyphs are being - // rendered after it - public Glyph(String str, boolean needAdvance) { - this.str = str; - this.needAdvance = needAdvance; - } - - /** Returns this glyph's unicode ID */ - public int getUnicodeID() { - return unicodeID; - } - - /** Returns this glyph's (font-specific) glyph code */ - public int getGlyphCode() { - return glyphCode; - } - - /** Returns the advance for this glyph */ - public float getAdvance() { - return advance; - } - - /** Draws this glyph and returns the (x) advance for this glyph */ - public float draw3D(float inX, float inY, float z, float scaleFactor) { - if (str != null) { - draw3D_ROBUST(str, inX, inY, z, scaleFactor); - if (!needAdvance) { - return 0; - } - // Compute and return the advance for this string - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), str); - float totalAdvance = 0; - for (int i = 0; i < gv.getNumGlyphs(); i++) { - totalAdvance += gv.getGlyphMetrics(i).getAdvance(); - } - return totalAdvance; - } - - // This is the code path taken for individual glyphs - if (glyphRectForTextureMapping == null) { - upload(); - } - - try { - if (mPipelinedQuadRenderer == null) { - mPipelinedQuadRenderer = new Pipelined_QuadRenderer(); - } - - TextureRenderer renderer = getBackingStore(); - // Handles case where NPOT texture is used for backing store - TextureCoords wholeImageTexCoords = renderer.getTexture().getImageTexCoords(); - float xScale = wholeImageTexCoords.right(); - float yScale = wholeImageTexCoords.bottom(); - - Rect rect = glyphRectForTextureMapping; - TextData data = (TextData) rect.getUserData(); - data.markUsed(); - - Rectangle2D origRect = data.origRect(); - - float x = inX - (scaleFactor * data.origOriginX()); - float y = inY - (scaleFactor * ((float) origRect.getHeight() - data.origOriginY())); - - int texturex = rect.x() + (data.origin().x - data.origOriginX()); - int texturey = renderer.getHeight() - rect.y() - (int) origRect.getHeight() - - (data.origin().y - data.origOriginY()); - int width = (int) origRect.getWidth(); - int height = (int) origRect.getHeight(); - - float tx1 = xScale * (float) texturex / (float) renderer.getWidth(); - float ty1 = yScale * (1.0f - - ((float) texturey / (float) renderer.getHeight())); - float tx2 = xScale * (float) (texturex + width) / (float) renderer.getWidth(); - float ty2 = yScale * (1.0f - - ((float) (texturey + height) / (float) renderer.getHeight())); - - mPipelinedQuadRenderer.glTexCoord2f(tx1, ty1); - mPipelinedQuadRenderer.glVertex3f(x, y, z); - mPipelinedQuadRenderer.glTexCoord2f(tx2, ty1); - mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), y, - z); - mPipelinedQuadRenderer.glTexCoord2f(tx2, ty2); - mPipelinedQuadRenderer.glVertex3f(x + (width * scaleFactor), - y + (height * scaleFactor), z); - mPipelinedQuadRenderer.glTexCoord2f(tx1, ty2); - mPipelinedQuadRenderer.glVertex3f(x, - y + (height * scaleFactor), z); - } catch (Exception e) { - e.printStackTrace(); - } - return advance; - } - - /** Notifies this glyph that it's been cleared out of the cache */ - public void clear() { - glyphRectForTextureMapping = null; - } - - private void upload() { - GlyphVector gv = getGlyphVector(); - Rectangle2D origBBox = preNormalize(renderDelegate.getBounds(gv, getFontRenderContext())); - Rectangle2D bbox = normalize(origBBox); - Point origin = new Point((int) -bbox.getMinX(), - (int) -bbox.getMinY()); - Rect rect = new Rect(0, 0, (int) bbox.getWidth(), - (int) bbox.getHeight(), - new TextData(null, origin, origBBox, unicodeID)); - packer.add(rect); - glyphRectForTextureMapping = rect; - Graphics2D g = getGraphics2D(); - // OK, should now have an (x, y) for this rectangle; rasterize - // the glyph - int strx = rect.x() + origin.x; - int stry = rect.y() + origin.y; - - // Clear out the area we're going to draw into - g.setComposite(AlphaComposite.Clear); - g.fillRect(rect.x(), rect.y(), rect.w(), rect.h()); - g.setComposite(AlphaComposite.Src); - - // Draw the string - renderDelegate.drawGlyphVector(g, gv, strx, stry); - - if (DRAW_BBOXES) { - TextData data = (TextData) rect.getUserData(); - // Draw a bounding box on the backing store - g.drawRect(strx - data.origOriginX(), - stry - data.origOriginY(), - (int) data.origRect().getWidth(), - (int) data.origRect().getHeight()); - g.drawRect(strx - data.origin().x, - stry - data.origin().y, - rect.w(), - rect.h()); - } - - // Mark this region of the TextureRenderer as dirty - getBackingStore().markDirty(rect.x(), rect.y(), rect.w(), - rect.h()); - // Re-register ourselves with our producer - producer.register(this); - } - - private GlyphVector getGlyphVector() { - GlyphVector gv = singleUnicodeGlyphVector; - if (gv != null) { - singleUnicodeGlyphVector = null; // Don't need this anymore - return gv; - } - singleUnicode[0] = (char) unicodeID; - return font.createGlyphVector(getFontRenderContext(), singleUnicode); - } - } - - class GlyphProducer { - final int undefined = -2; - FontRenderContext fontRenderContext; - List/**/ glyphsOutput = new ArrayList/**/(); - // The mapping from unicode character to font-specific glyph ID - int[] unicodes2Glyphs; - // The mapping from glyph ID to Glyph - Glyph[] glyphCache; - // We re-use this for each incoming string - CharSequenceIterator iter = new CharSequenceIterator(); - - GlyphProducer(int fontLengthInGlyphs) { - unicodes2Glyphs = new int[512]; - glyphCache = new Glyph[fontLengthInGlyphs]; - clearAllCacheEntries(); - } - - public List/**/ getGlyphs(CharSequence inString) { - glyphsOutput.clear(); - iter.initFromCharSequence(inString); - GlyphVector fullRunGlyphVector = font.createGlyphVector(getFontRenderContext(), - iter); - boolean complex = (fullRunGlyphVector.getLayoutFlags() != 0); - if (complex || DISABLE_GLYPH_CACHE) { - // Punt to the robust version of the renderer - glyphsOutput.add(new Glyph(inString.toString(), false)); - return glyphsOutput; - } - - int lengthInGlyphs = fullRunGlyphVector.getNumGlyphs(); - int i = 0; - while (i < lengthInGlyphs) { - Glyph glyph = getGlyph(inString, fullRunGlyphVector, i); - if (glyph != null) { - glyphsOutput.add(glyph); - i++; - } else { - // Assemble a run of characters that don't fit in - // the cache - StringBuffer buf = new StringBuffer(); - while (i < lengthInGlyphs && - getGlyph(inString, fullRunGlyphVector, i) == null) { - buf.append(inString.charAt(i++)); - } - glyphsOutput.add(new Glyph(buf.toString(), - // Any more glyphs after this run? - i < lengthInGlyphs)); - } - } - return glyphsOutput; - } - - public void clearCacheEntry(int unicodeID) { - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - Glyph glyph = glyphCache[glyphID]; - if (glyph != null) { - glyph.clear(); - } - glyphCache[glyphID] = null; - } - unicodes2Glyphs[unicodeID] = undefined; - } - - public void clearAllCacheEntries() { - for (int i = 0; i < unicodes2Glyphs.length; i++) { - clearCacheEntry(i); - } - } - - public void register(Glyph glyph) { - unicodes2Glyphs[glyph.getUnicodeID()] = glyph.getGlyphCode(); - glyphCache[glyph.getGlyphCode()] = glyph; - } - - public float getGlyphPixelWidth(char unicodeID) { - Glyph glyph = getGlyph(unicodeID); - if (glyph != null) { - return glyph.getAdvance(); - } - - // Have to do this the hard / uncached way - singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(fontRenderContext, - singleUnicode); - return gv.getGlyphMetrics(0).getAdvance(); - } - - // Returns a glyph object for this single glyph. Returns null - // if the unicode or glyph ID would be out of bounds of the - // glyph cache. - private Glyph getGlyph(CharSequence inString, - GlyphVector fullRunGlyphVector, - int index) { - char unicodeID = inString.charAt(index); - - if (unicodeID >= unicodes2Glyphs.length) { - return null; - } - - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - return glyphCache[glyphID]; - } - - // Must fabricate the glyph - singleUnicode[0] = unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); - return getGlyph(unicodeID, gv, fullRunGlyphVector.getGlyphMetrics(index)); - } - - // It's unclear whether this variant might produce less - // optimal results than if we can see the entire GlyphVector - // for the incoming string - private Glyph getGlyph(int unicodeID) { - if (unicodeID >= unicodes2Glyphs.length) { - return null; - } - - int glyphID = unicodes2Glyphs[unicodeID]; - if (glyphID != undefined) { - return glyphCache[glyphID]; - } - singleUnicode[0] = (char) unicodeID; - GlyphVector gv = font.createGlyphVector(getFontRenderContext(), singleUnicode); - return getGlyph(unicodeID, gv, gv.getGlyphMetrics(0)); - } - - private Glyph getGlyph(int unicodeID, - GlyphVector singleUnicodeGlyphVector, - GlyphMetrics metrics) { - int glyphCode = singleUnicodeGlyphVector.getGlyphCode(0); - // Have seen huge glyph codes (65536) coming out of some fonts in some Unicode situations - if (glyphCode >= glyphCache.length) { - return null; - } - Glyph glyph = new Glyph(unicodeID, - glyphCode, - metrics.getAdvance(), - singleUnicodeGlyphVector, - this); - register(glyph); - return glyph; - } - } - - class Pipelined_QuadRenderer { - int mOutstandingGlyphsVerticesPipeline = 0; - FloatBuffer mTexCoords; - FloatBuffer mVertCoords; - boolean usingVBOs; - int mVBO_For_ResuableTileVertices; - int mVBO_For_ResuableTileTexCoords; - - Pipelined_QuadRenderer() { - GL gl = GLU.getCurrentGL(); - mVertCoords = BufferUtil.newFloatBuffer(kTotalBufferSizeCoordsVerts); - mTexCoords = BufferUtil.newFloatBuffer(kTotalBufferSizeCoordsTex); - - usingVBOs = is15Available(gl); - - if (usingVBOs) { - try { - int[] vbos = new int[2]; - gl.glGenBuffers(2, IntBuffer.wrap(vbos)); - - mVBO_For_ResuableTileVertices = vbos[0]; - mVBO_For_ResuableTileTexCoords = vbos[1]; - - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileVertices); - gl.glBufferData(GL.GL_ARRAY_BUFFER, kTotalBufferSizeBytesVerts, - null, GL.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileTexCoords); - gl.glBufferData(GL.GL_ARRAY_BUFFER, kTotalBufferSizeBytesTex, - null, GL.GL_STREAM_DRAW); // stream draw because this is a single quad use pipeline - } catch (Exception e) { - isExtensionAvailable_GL_VERSION_1_5 = false; - usingVBOs = false; - } - } - } - - public void glTexCoord2f(float v, float v1) { - mTexCoords.put(v); - mTexCoords.put(v1); - } - - public void glVertex3f(float inX, float inY, float inZ) { - mVertCoords.put(inX); - mVertCoords.put(inY); - mVertCoords.put(inZ); - - mOutstandingGlyphsVerticesPipeline++; - - if (mOutstandingGlyphsVerticesPipeline >= kTotalBufferSizeVerts) { - this.draw(); - } - } - - private void draw() { - if (useVertexArrays) { - drawVertexArrays(); - } else { - drawIMMEDIATE(); - } - } - - private void drawVertexArrays() { - if (mOutstandingGlyphsVerticesPipeline > 0) { - GL gl = GLU.getCurrentGL(); - - TextureRenderer renderer = getBackingStore(); - Texture texture = renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? - - mVertCoords.rewind(); - mTexCoords.rewind(); - - gl.glEnableClientState(GL.GL_VERTEX_ARRAY); - - if (usingVBOs) { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileVertices); - gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, - mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_VertexData, - mVertCoords); // upload only the new stuff - gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0); - } else { - gl.glVertexPointer(3, GL.GL_FLOAT, 0, mVertCoords); - } - - gl.glEnableClientState(GL.GL_TEXTURE_COORD_ARRAY); - - if (usingVBOs) { - gl.glBindBuffer(GL.GL_ARRAY_BUFFER, - mVBO_For_ResuableTileTexCoords); - gl.glBufferSubData(GL.GL_ARRAY_BUFFER, 0, - mOutstandingGlyphsVerticesPipeline * kSizeInBytes_OneVertices_TexData, - mTexCoords); // upload only the new stuff - gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0); - } else { - gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, mTexCoords); - } - - gl.glDrawArrays(GL.GL_QUADS, 0, - mOutstandingGlyphsVerticesPipeline); - - mVertCoords.rewind(); - mTexCoords.rewind(); - mOutstandingGlyphsVerticesPipeline = 0; - } - } - - private void drawIMMEDIATE() { - if (mOutstandingGlyphsVerticesPipeline > 0) { - TextureRenderer renderer = getBackingStore(); - Texture texture = renderer.getTexture(); // triggers texture uploads. Maybe this should be more obvious? - - GL gl = GLU.getCurrentGL(); - gl.glBegin(GL.GL_QUADS); - - try { - int numberOfQuads = mOutstandingGlyphsVerticesPipeline / 4; - mVertCoords.rewind(); - mTexCoords.rewind(); - - for (int i = 0; i < numberOfQuads; i++) { - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - - gl.glTexCoord2f(mTexCoords.get(), mTexCoords.get()); - gl.glVertex3f(mVertCoords.get(), mVertCoords.get(), - mVertCoords.get()); - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - gl.glEnd(); - mVertCoords.rewind(); - mTexCoords.rewind(); - mOutstandingGlyphsVerticesPipeline = 0; - } - } - } - } - - class DebugListener implements GLEventListener { - private GLU glu = new GLU(); - private Frame frame; - - DebugListener(Frame frame) { - this.frame = frame; - } - - public void display(GLAutoDrawable drawable) { - GL gl = drawable.getGL(); - gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_COLOR_BUFFER_BIT); - - if (packer == null) { - return; - } - - TextureRenderer rend = getBackingStore(); - final int w = rend.getWidth(); - final int h = rend.getHeight(); - rend.beginOrthoRendering(w, h); - rend.drawOrthoRect(0, 0); - rend.endOrthoRendering(); - - if ((frame.getWidth() != w) || (frame.getHeight() != h)) { - EventQueue.invokeLater(new Runnable() { - public void run() { - frame.setSize(w, h); - } - }); - } - } - - // Unused methods - public void init(GLAutoDrawable drawable) { - } - - public void reshape(GLAutoDrawable drawable, int x, int y, int width, - int height) { - } - - public void displayChanged(GLAutoDrawable drawable, - boolean modeChanged, boolean deviceChanged) { - } - } - - /** - * Sets whether vertex arrays are being used internally for - * rendering, or whether text is rendered using the OpenGL - * immediate mode commands. This is provided as a concession for - * certain graphics cards which have poor vertex array - * performance. Defaults to true. - */ - public void setUseVertexArrays(boolean useVertexArrays) { - this.useVertexArrays = useVertexArrays; - } - - /** - * Indicates whether vertex arrays are being used internally for - * rendering, or whether text is rendered using the OpenGL - * immediate mode commands. Defaults to true. - */ - public boolean getUseVertexArrays() { - return useVertexArrays; - } - - /** - * Sets whether smoothing (i.e., GL_LINEAR filtering) is enabled - * in the backing TextureRenderer of this TextRenderer. A few - * graphics cards do not behave well when this is enabled, - * resulting in fuzzy text. Defaults to true. - */ - public void setSmoothing(boolean smoothing) { - this.smoothing = smoothing; - getBackingStore().setSmoothing(smoothing); - } - - /** - * Indicates whether smoothing is enabled in the backing - * TextureRenderer of this TextRenderer. A few graphics cards do - * not behave well when this is enabled, resulting in fuzzy text. - * Defaults to true. - */ - public boolean getSmoothing() { - return smoothing; - } - - private boolean is15Available(GL gl) { - if (!checkFor_isExtensionAvailable_GL_VERSION_1_5) { - isExtensionAvailable_GL_VERSION_1_5 = gl.isExtensionAvailable("GL_VERSION_1_5"); - checkFor_isExtensionAvailable_GL_VERSION_1_5 = true; - } - return isExtensionAvailable_GL_VERSION_1_5; - } -} diff --git a/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java b/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java deleted file mode 100755 index 4ca3d03e0..000000000 --- a/src/classes/com/sun/opengl/util/j2d/TextureRenderer.java +++ /dev/null @@ -1,695 +0,0 @@ -/* - * 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 com.sun.opengl.util.j2d; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics2D; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.image.*; - -import javax.media.opengl.*; -import javax.media.opengl.glu.*; -import com.sun.opengl.util.texture.*; - -/** Provides the ability to render into an OpenGL {@link - com.sun.opengl.util.texture.Texture Texture} using the Java 2D - APIs. This renderer class uses an internal Java 2D image (of - unspecified type) for its backing store and flushes portions of - that image to an OpenGL texture on demand. The resulting OpenGL - texture can then be mapped on to a polygon for display. */ - -public class TextureRenderer { - // For now, we supply only a BufferedImage back-end for this - // renderer. In theory we could use the Java 2D/JOGL bridge to fully - // accelerate the rendering paths, but there are restrictions on - // what work can be done where; for example, Graphics2D-related work - // must not be done on the Queue Flusher Thread, but JOGL's - // OpenGL-related work must be. This implies that the user's code - // would need to be split up into multiple callbacks run from the - // appropriate threads, which would be somewhat unfortunate. - - // Whether we have an alpha channel in the (RGB/A) backing store - private boolean alpha; - - // Whether we're using only a GL_INTENSITY backing store - private boolean intensity; - - // Whether we're attempting to use automatic mipmap generation support - private boolean mipmap; - - // Whether smoothing is enabled for the OpenGL texture (switching - // between GL_LINEAR and GL_NEAREST filtering) - private boolean smoothing = true; - private boolean smoothingChanged; - - // The backing store itself - private BufferedImage image; - - private Texture texture; - private TextureData textureData; - private boolean mustReallocateTexture; - private Rectangle dirtyRegion; - - private GLU glu = new GLU(); - - // Current color - private float r = 1.0f; - private float g = 1.0f; - private float b = 1.0f; - private float a = 1.0f; - - /** Creates a new renderer with backing store of the specified width - and height. If alpha is true, allocates an alpha - channel in the backing store image. No mipmap support is - requested. - - @param width the width of the texture to render into - @param height the height of the texture to render into - @param alpha whether to allocate an alpha channel for the texture - */ - public TextureRenderer(int width, int height, boolean alpha) { - this(width, height, alpha, false); - } - - /** Creates a new renderer with backing store of the specified width - and height. If alpha is true, allocates an alpha channel in the - backing store image. If mipmap is true, attempts to use OpenGL's - automatic mipmap generation for better smoothing when rendering - the TextureRenderer's contents at a distance. - - @param width the width of the texture to render into - @param height the height of the texture to render into - @param alpha whether to allocate an alpha channel for the texture - @param mipmap whether to attempt use of automatic mipmap generation - */ - public TextureRenderer(int width, int height, boolean alpha, boolean mipmap) { - this(width, height, alpha, false, mipmap); - } - - // Internal constructor to avoid confusion since alpha only makes - // sense when intensity is not set - private TextureRenderer(int width, int height, boolean alpha, boolean intensity, boolean mipmap) { - this.alpha = alpha; - this.intensity = intensity; - this.mipmap = mipmap; - init(width, height); - } - - /** Creates a new renderer with a special kind of backing store - which acts only as an alpha channel. No mipmap support is - requested. Internally, this associates a GL_INTENSITY OpenGL - texture with the backing store. */ - public static TextureRenderer createAlphaOnlyRenderer(int width, int height) { - return createAlphaOnlyRenderer(width, height, false); - } - - /** Creates a new renderer with a special kind of backing store - which acts only as an alpha channel. If mipmap is - true, attempts to use OpenGL's automatic mipmap generation for - better smoothing when rendering the TextureRenderer's contents - at a distance. Internally, this associates a GL_INTENSITY OpenGL - texture with the backing store. */ - public static TextureRenderer createAlphaOnlyRenderer(int width, int height, boolean mipmap) { - return new TextureRenderer(width, height, false, true, mipmap); - } - - /** Returns the width of the backing store of this renderer. - - @return the width of the backing store of this renderer - */ - public int getWidth() { - return image.getWidth(); - } - - /** Returns the height of the backing store of this renderer. - - @return the height of the backing store of this renderer - */ - public int getHeight() { - return image.getHeight(); - } - - /** Returns the size of the backing store of this renderer in a - newly-allocated {@link java.awt.Dimension Dimension} object. - - @return the size of the backing store of this renderer - */ - public Dimension getSize() { - return getSize(null); - } - - /** Returns the size of the backing store of this renderer. Uses the - {@link java.awt.Dimension Dimension} object if one is supplied, - or allocates a new one if null is passed. - - @param d a {@link java.awt.Dimension Dimension} object in which - to store the results, or null to allocate a new one - - @return the size of the backing store of this renderer - */ - public Dimension getSize(Dimension d) { - if (d == null) - d = new Dimension(); - d.setSize(image.getWidth(), image.getHeight()); - return d; - } - - /** Sets the size of the backing store of this renderer. This may - cause the OpenGL texture object associated with this renderer to - be invalidated; it is not recommended to cache this texture - object outside this class but to instead call {@link #getTexture - getTexture} when it is needed. - - @param width the new width of the backing store of this renderer - @param height the new height of the backing store of this renderer - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setSize(int width, int height) throws GLException { - init(width, height); - } - - /** Sets the size of the backing store of this renderer. This may - cause the OpenGL texture object associated with this renderer to - be invalidated. - - @param d the new size of the backing store of this renderer - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setSize(Dimension d) throws GLException { - setSize(d.width, d.height); - } - - /** Sets whether smoothing is enabled for the OpenGL texture; if so, - uses GL_LINEAR interpolation for the minification and - magnification filters. Defaults to true. Changes to this setting - will not take effect until the next call to {@link - #beginOrthoRendering beginOrthoRendering}. - - @param smoothing whether smoothing is enabled for the OpenGL texture - */ - public void setSmoothing(boolean smoothing) { - this.smoothing = smoothing; - smoothingChanged = true; - } - - /** Returns whether smoothing is enabled for the OpenGL texture; see - {@link #setSmoothing setSmoothing}. Defaults to true. - - @return whether smoothing is enabled for the OpenGL texture - */ - public boolean getSmoothing() { - return smoothing; - } - - /** Creates a {@link java.awt.Graphics2D Graphics2D} instance for - rendering to the backing store of this renderer. The returned - object should be disposed of using the normal {@link - java.awt.Graphics#dispose() Graphics.dispose()} method once it - is no longer being used. - - @return a new {@link java.awt.Graphics2D Graphics2D} object for - rendering into the backing store of this renderer - */ - public Graphics2D createGraphics() { - return image.createGraphics(); - } - - /** Returns the underlying Java 2D {@link java.awt.Image Image} - being rendered into. */ - public Image getImage() { - return image; - } - - /** Marks the given region of the TextureRenderer as dirty. This - region, and any previously set dirty regions, will be - automatically synchronized with the underlying Texture during - the next {@link #getTexture getTexture} operation, at which - point the dirty region will be cleared. It is not necessary for - an OpenGL context to be current when this method is called. - - @param x the x coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param y the y coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param width the width of the region to update - @param height the height of the region to update - */ - public void markDirty(int x, int y, int width, int height) { - Rectangle curRegion = new Rectangle(x, y, width, height); - if (dirtyRegion == null) { - dirtyRegion = curRegion; - } else { - dirtyRegion.add(curRegion); - } - } - - /** Returns the underlying OpenGL Texture object associated with - this renderer, synchronizing any dirty regions of the - TextureRenderer with the underlying OpenGL texture. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public Texture getTexture() throws GLException { - if (dirtyRegion != null) { - sync(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height); - dirtyRegion = null; - } - - ensureTexture(); - return texture; - } - - /** Disposes all resources associated with this renderer. It is not - valid to use this renderer after calling this method. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void dispose() throws GLException { - if (texture != null) { - texture.dispose(); - texture = null; - } - if (image != null) { - image.flush(); - image = null; - } - } - - /** Convenience method which assists in rendering portions of the - OpenGL texture to the screen, if the application intends to draw - them as a flat overlay on to the screen. Pushes OpenGL state - bits (GL_ENABLE_BIT, GL_DEPTH_BUFFER_BIT and GL_TRANSFORM_BIT); - disables the depth test, back-face culling, and lighting; - enables the texture in this renderer; and sets up the viewing - matrices for orthographic rendering where the coordinates go - from (0, 0) at the lower left to (width, height) at the upper - right. Equivalent to beginOrthoRendering(width, height, true). - {@link #endOrthoRendering} must be used in conjunction with this - method to restore all OpenGL states. - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void beginOrthoRendering(int width, int height) throws GLException { - beginOrthoRendering(width, height, true); - } - - /** Convenience method which assists in rendering portions of the - OpenGL texture to the screen, if the application intends to draw - them as a flat overlay on to the screen. Pushes OpenGL state - bits (GL_ENABLE_BIT, GL_DEPTH_BUFFER_BIT and GL_TRANSFORM_BIT); - disables the depth test (if the "disableDepthTest" argument is - true), back-face culling, and lighting; enables the texture in - this renderer; and sets up the viewing matrices for orthographic - rendering where the coordinates go from (0, 0) at the lower left - to (width, height) at the upper right. {@link - #endOrthoRendering} must be used in conjunction with this method - to restore all OpenGL states. - - @param width the width of the current on-screen OpenGL drawable - @param height the height of the current on-screen OpenGL drawable - @param disableDepthTest whether the depth test should be disabled - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void beginOrthoRendering(int width, int height, boolean disableDepthTest) throws GLException { - beginRendering(true, width, height, disableDepthTest); - } - - /** Convenience method which assists in rendering portions of the - OpenGL texture to the screen as 2D quads in 3D space. Pushes - OpenGL state (GL_ENABLE_BIT); disables lighting; and enables the - texture in this renderer. Unlike {@link #beginOrthoRendering - beginOrthoRendering}, does not modify the depth test, back-face - culling, lighting, or the modelview or projection matrices. The - user is responsible for setting up the view matrices for correct - results of {@link #draw3DRect draw3DRect}. {@link - #end3DRendering} must be used in conjunction with this method to - restore all OpenGL states. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void begin3DRendering() throws GLException { - beginRendering(false, 0, 0, false); - } - - /** Changes the color of the polygons, and therefore the drawn - images, this TextureRenderer produces. Use of this method is - optional. The TextureRenderer uses the GL_MODULATE texture - environment mode, which causes the portions of the rendered - texture to be multiplied by the color of the rendered - polygons. The polygon color can be varied to achieve effects - like tinting of the overall output or fading in and out by - changing the alpha of the color.

- - Each component ranges from 0.0f - 1.0f. The alpha component, if - used, does not need to be premultiplied into the color channels - as described in the documentation for {@link - com.sun.opengl.util.texture.Texture Texture}, although - premultiplied colors are used internally. The default color is - opaque white. - - @param r the red component of the new color - @param g the green component of the new color - @param b the blue component of the new color - @param a the alpha component of the new color, 0.0f = completely - transparent, 1.0f = completely opaque - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(float r, float g, float b, float a) throws GLException { - GL gl = GLU.getCurrentGL(); - this.r = r * a; - this.g = g * a; - this.b = b * a; - this.a = a; - - gl.glColor4f(this.r, this.g, this.b, this.a); - } - - private float[] compArray; - /** Changes the current color of this TextureRenderer to the - supplied one. The default color is opaque white. See {@link - #setColor(float,float,float,float) setColor} for more details. - - @param color the new color to use for rendering - @throws GLException If an OpenGL context is not current when this method is called - */ - public void setColor(Color color) throws GLException { - // Get color's RGBA components as floats in the range [0,1]. - if (compArray == null) { - compArray = new float[4]; - } - color.getRGBComponents(compArray); - setColor(compArray[0], compArray[1], compArray[2], compArray[3]); - } - - /** Draws an orthographically projected rectangle containing all of - the underlying texture to the specified location on the - screen. All (x, y) coordinates are specified relative to the - lower left corner of either the texture image or the current - OpenGL drawable. This method is equivalent to - drawOrthoRect(screenx, screeny, 0, 0, getWidth(), - getHeight());. - - @param screenx the on-screen x coordinate at which to draw the rectangle - @param screeny the on-screen y coordinate (relative to lower left) at - which to draw the rectangle - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void drawOrthoRect(int screenx, int screeny) throws GLException { - drawOrthoRect(screenx, screeny, 0, 0, getWidth(), getHeight()); - } - - /** Draws an orthographically projected rectangle of the underlying - texture to the specified location on the screen. All (x, y) - coordinates are specified relative to the lower left corner of - either the texture image or the current OpenGL drawable. - - @param screenx the on-screen x coordinate at which to draw the rectangle - @param screeny the on-screen y coordinate (relative to lower left) at - which to draw the rectangle - @param texturex the x coordinate of the pixel in the texture of - the lower left portion of the rectangle to draw - @param texturey the y coordinate of the pixel in the texture - (relative to lower left) of the lower left portion of the - rectangle to draw - @param width the width of the rectangle to draw - @param height the height of the rectangle to draw - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void drawOrthoRect(int screenx, int screeny, - int texturex, int texturey, - int width, int height) throws GLException { - draw3DRect(screenx, screeny, 0, texturex, texturey, width, height, 1); - } - - /** Draws a rectangle of the underlying texture to the specified 3D - location. In the current coordinate system, the lower left - corner of the rectangle is placed at (x, y, z), and the upper - right corner is placed at (x + width * scaleFactor, y + height * - scaleFactor, z). The lower left corner of the sub-rectangle of - the texture is (texturex, texturey) and the upper right corner - is (texturex + width, texturey + height). For back-face culling - purposes, the rectangle is drawn with counterclockwise - orientation of the vertices when viewed from the front. - - @param x the x coordinate at which to draw the rectangle - @param y the y coordinate at which to draw the rectangle - @param z the z coordinate at which to draw the rectangle - @param texturex the x coordinate of the pixel in the texture of - the lower left portion of the rectangle to draw - @param texturey the y coordinate of the pixel in the texture - (relative to lower left) of the lower left portion of the - rectangle to draw - @param width the width in texels of the rectangle to draw - @param height the height in texels of the rectangle to draw - @param scaleFactor the scale factor to apply (multiplicatively) - to the size of the drawn rectangle - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void draw3DRect(float x, float y, float z, - int texturex, int texturey, - int width, int height, - float scaleFactor) throws GLException { - GL gl = GLU.getCurrentGL(); - Texture texture = getTexture(); - TextureCoords coords = texture.getSubImageTexCoords(texturex, texturey, - texturex + width, - texturey + height); - gl.glBegin(GL.GL_QUADS); - gl.glTexCoord2f(coords.left(), coords.bottom()); - gl.glVertex3f(x, y, z); - gl.glTexCoord2f(coords.right(), coords.bottom()); - gl.glVertex3f(x + width * scaleFactor, y, z); - gl.glTexCoord2f(coords.right(), coords.top()); - gl.glVertex3f(x + width * scaleFactor, y + height * scaleFactor, z); - gl.glTexCoord2f(coords.left(), coords.top()); - gl.glVertex3f(x, y + height * scaleFactor, z); - gl.glEnd(); - } - - /** Convenience method which assists in rendering portions of the - OpenGL texture to the screen, if the application intends to draw - them as a flat overlay on to the screen. Must be used if {@link - #beginOrthoRendering} is used to set up the rendering stage for - this overlay. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void endOrthoRendering() throws GLException { - endRendering(true); - } - - /** Convenience method which assists in rendering portions of the - OpenGL texture to the screen as 2D quads in 3D space. Must be - used if {@link #begin3DRendering} is used to set up the - rendering stage for this overlay. - - @throws GLException If an OpenGL context is not current when this method is called - */ - public void end3DRendering() throws GLException { - endRendering(false); - } - - /** Indicates whether automatic mipmap generation is in use for this - TextureRenderer. The result of this method may change from true - to false if it is discovered during allocation of the - TextureRenderer's backing store that automatic mipmap generation - is not supported at the OpenGL level. */ - public boolean isUsingAutoMipmapGeneration() { - return mipmap; - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private void beginRendering(boolean ortho, int width, int height, boolean disableDepthTestForOrtho) { - GL gl = GLU.getCurrentGL(); - int attribBits = - GL.GL_ENABLE_BIT | GL.GL_TEXTURE_BIT | GL.GL_COLOR_BUFFER_BIT | - (ortho ? (GL.GL_DEPTH_BUFFER_BIT | GL.GL_TRANSFORM_BIT) : 0); - gl.glPushAttrib(attribBits); - gl.glDisable(GL.GL_LIGHTING); - if (ortho) { - if (disableDepthTestForOrtho) { - gl.glDisable(GL.GL_DEPTH_TEST); - } - gl.glDisable(GL.GL_CULL_FACE); - gl.glMatrixMode(GL.GL_PROJECTION); - gl.glPushMatrix(); - gl.glLoadIdentity(); - glu.gluOrtho2D(0, width, 0, height); - gl.glMatrixMode(GL.GL_MODELVIEW); - gl.glPushMatrix(); - gl.glLoadIdentity(); - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPushMatrix(); - gl.glLoadIdentity(); - } - gl.glEnable(GL.GL_BLEND); - gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA); - Texture texture = getTexture(); - texture.enable(); - texture.bind(); - gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE); - // Change polygon color to last saved - gl.glColor4f(r, g, b, a); - if (smoothingChanged) { - smoothingChanged = false; - if (smoothing) { - texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); - if (mipmap) { - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR); - } else { - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR); - } - } else { - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - } - } - } - - private void endRendering(boolean ortho) { - GL gl = GLU.getCurrentGL(); - Texture texture = getTexture(); - texture.disable(); - if (ortho) { - gl.glMatrixMode(GL.GL_PROJECTION); - gl.glPopMatrix(); - gl.glMatrixMode(GL.GL_MODELVIEW); - gl.glPopMatrix(); - gl.glMatrixMode(GL.GL_TEXTURE); - gl.glPopMatrix(); - } - gl.glPopAttrib(); - } - - private void init(int width, int height) { - // Discard previous BufferedImage if any - if (image != null) { - image.flush(); - image = null; - } - - // Infer the internal format if not an intensity texture - int internalFormat = (intensity ? GL.GL_INTENSITY : 0); - int imageType = - (intensity ? BufferedImage.TYPE_BYTE_GRAY : - (alpha ? BufferedImage.TYPE_INT_ARGB_PRE : BufferedImage.TYPE_INT_RGB)); - image = new BufferedImage(width, height, imageType); - // Always realllocate the TextureData associated with this - // BufferedImage; it's just a reference to the contents but we - // need it in order to update sub-regions of the underlying - // texture - textureData = new TextureData(internalFormat, 0, mipmap, image); - // For now, always reallocate the underlying OpenGL texture when - // the backing store size changes - mustReallocateTexture = true; - } - - /** Synchronizes the specified region of the backing store down to - the underlying OpenGL texture. If {@link #markDirty markDirty} - is used instead to indicate the regions that are out of sync, - this method does not need to be called. - - @param x the x coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param y the y coordinate (in Java 2D coordinates -- relative to - upper left) of the region to update - @param width the width of the region to update - @param height the height of the region to update - - @throws GLException If an OpenGL context is not current when this method is called - */ - private void sync(int x, int y, int width, int height) throws GLException { - // Force allocation if necessary - boolean canSkipUpdate = ensureTexture(); - - if (!canSkipUpdate) { - // Update specified region. - // NOTE that because BufferedImage-based TextureDatas now don't - // do anything to their contents, the coordinate systems for - // OpenGL and Java 2D actually line up correctly for - // updateSubImage calls, so we don't need to do any argument - // conversion here (i.e., flipping the Y coordinate). - texture.updateSubImage(textureData, 0, x, y, x, y, width, height); - } - } - - // Returns true if the texture was newly allocated, false if not - private boolean ensureTexture() { - if (mustReallocateTexture) { - if (texture != null) { - texture.dispose(); - texture = null; - } - mustReallocateTexture = false; - } - - if (texture == null) { - texture = TextureIO.newTexture(textureData); - if (mipmap && !texture.isUsingAutoMipmapGeneration()) { - // Only try this once - texture.dispose(); - mipmap = false; - textureData.setMipmap(false); - texture = TextureIO.newTexture(textureData); - } - - if (!smoothing) { - // The TextureIO classes default to GL_LINEAR filtering - texture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST); - texture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST); - } - return true; - } - - return false; - } -} -- cgit v1.2.3