From 26b7878c3b47f76e55195a8682fd7dce04d83ea8 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Sat, 21 Jun 2008 03:04:14 +0000 Subject: 2nd big refactoring. Goals are orthogonal components for: - OS Windowing system - NEWT, X11, Windows, MacOsX - GL Windowing GLUE - EGL, GLX, WGL, CGL - GL profiles - core and util packages - generate all Java components from any platform All above goals are achieved. TODO: - Native compilation fix and test - Check/Fix Win32, MacOSX and the mobile devices - .. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/branches/JOGL_2_SANDBOX@1670 232f8b59-042b-4e1e-8c03-345bb8c30851 --- src/classes/com/sun/opengl/impl/glue/Glue.java | 114 -- .../com/sun/opengl/impl/nurbs/CurveEvaluator.java | 86 ++ .../sun/opengl/impl/nurbs/SurfaceEvaluator.java | 111 ++ .../opengl/util/texture/awt/AWTTextureData.java | 764 ++++++++++++ .../opengl/util/texture/awt/AWTTextureReader.java | 968 +++++++++++++++ .../opengl/util/texture/awt/AWTTextureWriter.java | 500 ++++++++ src/classes/javax/media/opengl/GLContext.java | 6 + src/classes/javax/media/opengl/GLDrawable.java | 15 +- .../javax/media/opengl/GLDrawableFactory.java | 126 +- src/classes/javax/media/opengl/NativeWindow.java | 27 +- .../javax/media/opengl/NativeWindowFactory.java | 9 +- src/classes/javax/media/opengl/Threading.java | 2 +- src/classes/javax/media/opengl/awt/GLCanvas.java | 36 +- src/classes/javax/media/opengl/awt/GLJPanel.java | 1270 -------------------- src/classes/javax/media/opengl/glu/GLUquadric.java | 26 +- src/native/jogl/WindowsWindow.c | 58 +- src/native/jogl/X11Window.c | 163 ++- 17 files changed, 2788 insertions(+), 1493 deletions(-) delete mode 100644 src/classes/com/sun/opengl/impl/glue/Glue.java create mode 100755 src/classes/com/sun/opengl/impl/nurbs/CurveEvaluator.java create mode 100755 src/classes/com/sun/opengl/impl/nurbs/SurfaceEvaluator.java create mode 100755 src/classes/com/sun/opengl/util/texture/awt/AWTTextureData.java create mode 100755 src/classes/com/sun/opengl/util/texture/awt/AWTTextureReader.java create mode 100755 src/classes/com/sun/opengl/util/texture/awt/AWTTextureWriter.java delete mode 100644 src/classes/javax/media/opengl/awt/GLJPanel.java (limited to 'src') diff --git a/src/classes/com/sun/opengl/impl/glue/Glue.java b/src/classes/com/sun/opengl/impl/glue/Glue.java deleted file mode 100644 index 863c9417c..000000000 --- a/src/classes/com/sun/opengl/impl/glue/Glue.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * License Applicability. Except to the extent portions of this file are - * made subject to an alternative license as permitted in the SGI Free - * Software License B, Version 1.1 (the "License"), the contents of this - * file are subject only to the provisions of the License. You may not use - * this file except in compliance with the License. You may obtain a copy - * of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 - * Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: - * - * http://oss.sgi.com/projects/FreeB - * - * Note that, as provided in the License, the Software is distributed on an - * "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS - * DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND - * CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A - * PARTICULAR PURPOSE, AND NON-INFRINGEMENT. - * - * NOTE: The Original Code (as defined below) has been licensed to Sun - * Microsystems, Inc. ("Sun") under the SGI Free Software License B - * (Version 1.1), shown above ("SGI License"). Pursuant to Section - * 3.2(3) of the SGI License, Sun is distributing the Covered Code to - * you under an alternative license ("Alternative License"). This - * Alternative License includes all of the provisions of the SGI License - * except that Section 2.2 and 11 are omitted. Any differences between - * the Alternative License and the SGI License are offered solely by Sun - * and not by SGI. - * - * Original Code. The Original Code is: OpenGL Sample Implementation, - * Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, - * Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. - * Copyright in any portions created by third parties is as indicated - * elsewhere herein. All Rights Reserved. - * - * Additional Notice Provisions: The application programming interfaces - * established by SGI in conjunction with the Original Code are The - * OpenGL(R) Graphics System: A Specification (Version 1.2.1), released - * April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version - * 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X - * Window System(R) (Version 1.3), released October 19, 1998. This software - * was created using the OpenGL(R) version 1.2.1 Sample Implementation - * published by SGI, but has not been independently verified as being - * compliant with the OpenGL(R) version 1.2.1 Specification. - */ - -package com.sun.opengl.impl.glue; - -/** - * - * @author Administrator - */ -public class Glue { - private static String[] __gluNurbsErrors = { - " ", - "spline order un-supported", - "too few knots", - "valid knot range is empty", - "decreasing knot sequence knot", - "knot multiplicity greater than order of spline", - "gluEndCurve() must follow gluBeginCurve()", - "gluBeginCurve() must precede gluEndCurve()", - "missing or extra geometric data", - "can't draw piecewise linear trimming curves", - "missing or extra domain data", - "missing or extra domain data", - "gluEndTrim() must precede gluEndSurface()", - "gluBeginSurface() must precede gluEndSurface()", - "curve of improper type passed as trim curve", - "gluBeginSurface() must precede gluBeginTrim()", - "gluEndTrim() must follow gluBeginTrim()", - "gluBeginTrim() must follow gluEndTrim()", - "invalid or missing trim curve", - "gluBeginTrim() must precede gluPwlCurve()", - "piecewise linear trimming curve referenced twice", - "piecewise linear trimming curve and nurbs curve mixed", - "improper usage of trim data type", - "nurbs curve referenced twice", - "nurbs curve and piecewise linear trimming curve mixed", - "nurbs surface referenced twice", - "invalid property", - "gluEndSurface() must follow gluBeginSurface()", - "intersecting or misoriented trim curve", - "intersecting trim curves", - "UNUSED", - "inconnected trim curves", - "unknown knot error", - "negative vertex count encountered", - "negative byte-stride encountered", - "unknown type descriptor", - "null control point reference", - "duplicate point on piecewise linear trimming curve" - } ; - - /** Creates a new instance of Glue */ - public Glue() { - } - - public static String __gluNURBSErrorString( int errno ) { - return( __gluNurbsErrors[ errno ] ); - } - - private static String[] __gluTessErrors = { - " ", - "gluTessBeginPolygon() must precede a gluTessEndPolygon", - "gluTessBeginContour() must precede a gluTessEndContour()", - "gluTessEndPolygon() must follow a gluTessBeginPolygon()", - "gluTessEndContour() must follow a gluTessBeginContour()", - "a coordinate is too large", - "need combine callback" - }; - - public static String __gluTessErrorString( int errno ) { - return( __gluTessErrors[ errno ] ); - } -} diff --git a/src/classes/com/sun/opengl/impl/nurbs/CurveEvaluator.java b/src/classes/com/sun/opengl/impl/nurbs/CurveEvaluator.java new file mode 100755 index 000000000..a6393ddf4 --- /dev/null +++ b/src/classes/com/sun/opengl/impl/nurbs/CurveEvaluator.java @@ -0,0 +1,86 @@ +package com.sun.opengl.impl.glu.nurbs; + +/* + ** License Applicability. Except to the extent portions of this file are + ** made subject to an alternative license as permitted in the SGI Free + ** Software License B, Version 1.1 (the "License"), the contents of this + ** file are subject only to the provisions of the License. You may not use + ** this file except in compliance with the License. You may obtain a copy + ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 + ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: + ** + ** http://oss.sgi.com/projects/FreeB + ** + ** Note that, as provided in the License, the Software is distributed on an + ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS + ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND + ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A + ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + ** + ** Original Code. The Original Code is: OpenGL Sample Implementation, + ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, + ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. + ** Copyright in any portions created by third parties is as indicated + ** elsewhere herein. All Rights Reserved. + ** + ** Additional Notice Provisions: The application programming interfaces + ** established by SGI in conjunction with the Original Code are The + ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released + ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version + ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X + ** Window System(R) (Version 1.3), released October 19, 1998. This software + ** was created using the OpenGL(R) version 1.2.1 Sample Implementation + ** published by SGI, but has not been independently verified as being + ** compliant with the OpenGL(R) version 1.2.1 Specification. + */ + +/** + * Class rendering curves with OpenGL + * @author Tomáš Hráský + * + */ +public interface CurveEvaluator { + /** + * Pushes eval bit + */ + public void bgnmap1f(); + + /** + * Pops all OpenGL attributes + */ + public void endmap1f() ; + + /** + * Initializes opengl evaluator + * @param type curve type + * @param ulo lowest u + * @param uhi highest u + * @param stride control point coords + * @param order curve order + * @param ps control points + */ + public void map1f(int type, float ulo, float uhi, int stride, int order, + CArrayOfFloats ps) ; + + /** + * Calls opengl enable + * @param type what to enable + */ + public void enable(int type) ; + + /** + * Calls glMapGrid1f + * @param nu steps + * @param u1 low u + * @param u2 high u + */ + public void mapgrid1f(int nu, float u1, float u2) ; + + /** + * Evaluates a curve using glEvalMesh1f + * @param style Backend.N_MESHFILL/N_MESHLINE/N_MESHPOINT + * @param from lowest param + * @param to highest param + */ + public void mapmesh1f(int style, int from, int to) ; +} diff --git a/src/classes/com/sun/opengl/impl/nurbs/SurfaceEvaluator.java b/src/classes/com/sun/opengl/impl/nurbs/SurfaceEvaluator.java new file mode 100755 index 000000000..feb3bc9a7 --- /dev/null +++ b/src/classes/com/sun/opengl/impl/nurbs/SurfaceEvaluator.java @@ -0,0 +1,111 @@ +package com.sun.opengl.impl.glu.nurbs; + +/* + ** License Applicability. Except to the extent portions of this file are + ** made subject to an alternative license as permitted in the SGI Free + ** Software License B, Version 1.1 (the "License"), the contents of this + ** file are subject only to the provisions of the License. You may not use + ** this file except in compliance with the License. You may obtain a copy + ** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 + ** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: + ** + ** http://oss.sgi.com/projects/FreeB + ** + ** Note that, as provided in the License, the Software is distributed on an + ** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS + ** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND + ** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A + ** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. + ** + ** Original Code. The Original Code is: OpenGL Sample Implementation, + ** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, + ** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc. + ** Copyright in any portions created by third parties is as indicated + ** elsewhere herein. All Rights Reserved. + ** + ** Additional Notice Provisions: The application programming interfaces + ** established by SGI in conjunction with the Original Code are The + ** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released + ** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version + ** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X + ** Window System(R) (Version 1.3), released October 19, 1998. This software + ** was created using the OpenGL(R) version 1.2.1 Sample Implementation + ** published by SGI, but has not been independently verified as being + ** compliant with the OpenGL(R) version 1.2.1 Specification. + */ + +/** + * Class rendering surfaces with OpenGL + * @author Tomas Hrasky + * + */ +public interface SurfaceEvaluator { + + /** + * Pushes eval bit + */ + public void bgnmap2f() ; + + /** + * Sets glPolygonMode + * @param style polygon mode (N_MESHFILL/N_MESHLINE/N_MESHPOINT) + */ + public void polymode(int style) ; + + /** + * Pops all attributes + */ + public void endmap2f() ; + + /** + * Empty method + * @param ulo + * @param uhi + * @param vlo + * @param vhi + */ + public void domain2f(float ulo, float uhi, float vlo, float vhi) ; + + /** + * Defines 2D mesh + * @param nu number of steps in u direction + * @param u0 lowest u + * @param u1 highest u + * @param nv number of steps in v direction + * @param v0 lowest v + * @param v1 highest v + */ + public void mapgrid2f(int nu, float u0, float u1, int nv, float v0, float v1) ; + + /** + * Evaluates surface + * @param style surface style + * @param umin minimum U + * @param umax maximum U + * @param vmin minimum V + * @param vmax maximum V + */ + public void mapmesh2f(int style, int umin, int umax, int vmin, int vmax) ; + + /** + * Initializes evaluator + * @param type surface type + * @param ulo lowest u + * @param uhi highest u + * @param ustride number of objects between control points in u direction + * @param uorder surface order in u direction + * @param vlo lowest v + * @param vhi highest v + * @param vstride number of control points' coords + * @param vorder surface order in v direction + * @param pts control points + */ + public void map2f(int type, float ulo, float uhi, int ustride, int uorder, + float vlo, float vhi, int vstride, int vorder, CArrayOfFloats pts) ; + + /** + * Calls opengl enable + * @param type what to enable + */ + public void enable(int type) ; +} diff --git a/src/classes/com/sun/opengl/util/texture/awt/AWTTextureData.java b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureData.java new file mode 100755 index 000000000..41895cf1a --- /dev/null +++ b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureData.java @@ -0,0 +1,764 @@ +/* + * Copyright (c) 2005 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. + */ + +package com.sun.opengl.util.texture.awt; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Transparency; +import java.awt.color.*; +import java.awt.image.*; +import java.nio.*; + +import javax.media.opengl.*; +import javax.media.opengl.util.*; +import com.sun.opengl.util.texture.*; +import com.sun.opengl.util.texture.spi.*; + +/** + * Represents the data for an OpenGL texture. This is separated from + * the notion of a Texture to support things like streaming in of + * textures in a background thread without requiring an OpenGL context + * to be current on that thread. + * + * @author Chris Campbell + * @author Kenneth Russell + */ + +public class AWTTextureData implements TextureData { + private int width; + private int height; + private int border; + private int pixelFormat; + private int pixelType; + private int internalFormat; // perhaps inferred from pixelFormat? + private boolean mipmap; // indicates whether mipmaps should be generated + // (ignored if mipmaps are supplied from the file) + private boolean dataIsCompressed; + private boolean mustFlipVertically; // Must flip texture coordinates + // vertically to get OpenGL output + // to look correct + private Buffer buffer; // the actual data... + private Buffer[] mipmapData; // ...or a series of mipmaps + private Flusher flusher; + private int rowLength; + private int alignment; // 1, 2, or 4 bytes + private int estimatedMemorySize; + + // Mechanism for lazily converting input BufferedImages with custom + // ColorModels to standard ones for uploading to OpenGL, as well as + // backing off from the optimizations of hoping that either + // GL_EXT_abgr or OpenGL 1.2 are present + private BufferedImage imageForLazyCustomConversion; + private boolean expectingEXTABGR; + private boolean haveEXTABGR; + private boolean expectingGL12; + private boolean haveGL12; + + private static final ColorModel rgbaColorModel = + new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[] {8, 8, 8, 8}, true, true, + Transparency.TRANSLUCENT, + DataBuffer.TYPE_BYTE); + private static final ColorModel rgbColorModel = + new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + new int[] {8, 8, 8, 0}, false, false, + Transparency.OPAQUE, + DataBuffer.TYPE_BYTE); + + /** + * Constructs a new AWTTextureData object with the specified parameters + * and data contained in the given Buffer. The optional Flusher can + * be used to clean up native resources associated with this + * AWTTextureData when processing is complete; for example, closing of + * memory-mapped files that might otherwise require a garbage + * collection to reclaim and close. + * + * @param internalFormat the OpenGL internal format for the + * resulting texture; must be specified, may + * not be 0 + * @param width the width in pixels of the texture + * @param height the height in pixels of the texture + * @param border the number of pixels of border this texture + * data has (0 or 1) + * @param pixelFormat the OpenGL pixel format for the + * resulting texture; must be specified, may + * not be 0 + * @param pixelType the OpenGL type of the pixels of the texture + * @param mipmap indicates whether mipmaps should be + * autogenerated (using GLU) for the resulting + * texture. Currently if mipmap is true then + * dataIsCompressed may not be true. + * @param dataIsCompressed indicates whether the texture data is in + * compressed form + * (e.g. GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + * @param mustFlipVertically indicates whether the texture + * coordinates must be flipped vertically + * in order to properly display the + * texture + * @param buffer the buffer containing the texture data + * @param flusher optional flusher to perform cleanup tasks + * upon call to flush() + * + * @throws IllegalArgumentException if any parameters of the texture + * data were invalid, such as requesting mipmap generation for a + * compressed texture + */ + public AWTTextureData(int internalFormat, + int width, + int height, + int border, + int pixelFormat, + int pixelType, + boolean mipmap, + boolean dataIsCompressed, + boolean mustFlipVertically, + Buffer buffer, + Flusher flusher) throws IllegalArgumentException { + if (mipmap && dataIsCompressed) { + throw new IllegalArgumentException("Can not generate mipmaps for compressed textures"); + } + + this.width = width; + this.height = height; + this.border = border; + this.pixelFormat = pixelFormat; + this.pixelType = pixelType; + this.internalFormat = internalFormat; + this.mipmap = mipmap; + this.dataIsCompressed = dataIsCompressed; + this.mustFlipVertically = mustFlipVertically; + this.buffer = buffer; + this.flusher = flusher; + alignment = 1; // FIXME: is this correct enough in all situations? + estimatedMemorySize = estimatedMemorySize(buffer); + } + + /** + * Constructs a new AWTTextureData object with the specified parameters + * and data for multiple mipmap levels contained in the given array + * of Buffers. The optional Flusher can be used to clean up native + * resources associated with this AWTTextureData when processing is + * complete; for example, closing of memory-mapped files that might + * otherwise require a garbage collection to reclaim and close. + * + * @param internalFormat the OpenGL internal format for the + * resulting texture; must be specified, may + * not be 0 + * @param width the width in pixels of the topmost mipmap + * level of the texture + * @param height the height in pixels of the topmost mipmap + * level of the texture + * @param border the number of pixels of border this texture + * data has (0 or 1) + * @param pixelFormat the OpenGL pixel format for the + * resulting texture; must be specified, may + * not be 0 + * @param pixelType the OpenGL type of the pixels of the texture + * @param dataIsCompressed indicates whether the texture data is in + * compressed form + * (e.g. GL_COMPRESSED_RGB_S3TC_DXT1_EXT) + * @param mustFlipVertically indicates whether the texture + * coordinates must be flipped vertically + * in order to properly display the + * texture + * @param mipmapData the buffers containing all mipmap levels + * of the texture's data + * @param flusher optional flusher to perform cleanup tasks + * upon call to flush() + * + * @throws IllegalArgumentException if any parameters of the texture + * data were invalid, such as requesting mipmap generation for a + * compressed texture + */ + public AWTTextureData(int internalFormat, + int width, + int height, + int border, + int pixelFormat, + int pixelType, + boolean dataIsCompressed, + boolean mustFlipVertically, + Buffer[] mipmapData, + Flusher flusher) throws IllegalArgumentException { + this.width = width; + this.height = height; + this.border = border; + this.pixelFormat = pixelFormat; + this.pixelType = pixelType; + this.internalFormat = internalFormat; + this.dataIsCompressed = dataIsCompressed; + this.mustFlipVertically = mustFlipVertically; + this.mipmapData = (Buffer[]) mipmapData.clone(); + this.flusher = flusher; + alignment = 1; // FIXME: is this correct enough in all situations? + for (int i = 0; i < mipmapData.length; i++) { + estimatedMemorySize += estimatedMemorySize(mipmapData[i]); + } + } + + /** + * Constructs a new AWTTextureData object with the specified parameters + * and data contained in the given BufferedImage. The resulting + * AWTTextureData "wraps" the contents of the BufferedImage, so if a + * modification is made to the BufferedImage between the time the + * AWTTextureData is constructed and when a Texture is made from the + * AWTTextureData, that modification will be visible in the resulting + * Texture. + * + * @param internalFormat the OpenGL internal format for the + * resulting texture; may be 0, in which case + * it is inferred from the image's type + * @param pixelFormat the OpenGL internal format for the + * resulting texture; may be 0, in which case + * it is inferred from the image's type (note: + * this argument is currently always ignored) + * @param mipmap indicates whether mipmaps should be + * autogenerated (using GLU) for the resulting + * texture + * @param image the image containing the texture data + */ + public AWTTextureData(int internalFormat, + int pixelFormat, + boolean mipmap, + BufferedImage image) { + if (internalFormat == 0) { + this.internalFormat = image.getColorModel().hasAlpha() ? GL.GL_RGBA : GL.GL_RGB; + } else { + this.internalFormat = internalFormat; + } + createFromImage(image); + this.mipmap = mipmap; + if (buffer != null) { + estimatedMemorySize = estimatedMemorySize(buffer); + } else { + // In the lazy custom conversion case we don't yet have a buffer + if (imageForLazyCustomConversion != null) { + estimatedMemorySize = estimatedMemorySize(wrapImageDataBuffer(imageForLazyCustomConversion)); + } + } + } + + /** Returns the width in pixels of the texture data. */ + public int getWidth() { return width; } + /** Returns the height in pixels of the texture data. */ + public int getHeight() { return height; } + /** Returns the border in pixels of the texture data. */ + public int getBorder() { return border; } + /** Returns the intended OpenGL pixel format of the texture data. */ + public int getPixelFormat() { + if (imageForLazyCustomConversion != null) { + if (!((expectingEXTABGR && haveEXTABGR) || + (expectingGL12 && haveGL12))) { + revertPixelFormatAndType(); + } + } + return pixelFormat; + } + /** Returns the intended OpenGL pixel type of the texture data. */ + public int getPixelType() { + if (imageForLazyCustomConversion != null) { + if (!((expectingEXTABGR && haveEXTABGR) || + (expectingGL12 && haveGL12))) { + revertPixelFormatAndType(); + } + } + return pixelType; + } + /** Returns the intended OpenGL internal format of the texture data. */ + public int getInternalFormat() { return internalFormat; } + /** Returns whether mipmaps should be generated for the texture data. */ + public boolean getMipmap() { return mipmap; } + /** Indicates whether the texture data is in compressed form. */ + public boolean isDataCompressed() { return dataIsCompressed; } + /** Indicates whether the texture coordinates must be flipped + vertically for proper display. */ + public boolean getMustFlipVertically() { return mustFlipVertically; } + /** Returns the texture data, or null if it is specified as a set of mipmaps. */ + public Buffer getBuffer() { + if (imageForLazyCustomConversion != null) { + if (!((expectingEXTABGR && haveEXTABGR) || + (expectingGL12 && haveGL12))) { + revertPixelFormatAndType(); + // Must present the illusion to the end user that we are simply + // wrapping the input BufferedImage + createFromCustom(imageForLazyCustomConversion); + } + } + return buffer; + } + /** Returns all mipmap levels for the texture data, or null if it is + specified as a single image. */ + public Buffer[] getMipmapData() { return mipmapData; } + /** Returns the required byte alignment for the texture data. */ + public int getAlignment() { return alignment; } + /** Returns the row length needed for correct GL_UNPACK_ROW_LENGTH + specification. This is currently only supported for + non-mipmapped, non-compressed textures. */ + public int getRowLength() { return rowLength; } + + /** Sets the width in pixels of the texture data. */ + public void setWidth(int width) { this.width = width; } + /** Sets the height in pixels of the texture data. */ + public void setHeight(int height) { this.height = height; } + /** Sets the border in pixels of the texture data. */ + public void setBorder(int border) { this.border = border; } + /** Sets the intended OpenGL pixel format of the texture data. */ + public void setPixelFormat(int pixelFormat) { this.pixelFormat = pixelFormat; } + /** Sets the intended OpenGL pixel type of the texture data. */ + public void setPixelType(int pixelType) { this.pixelType = pixelType; } + /** Sets the intended OpenGL internal format of the texture data. */ + public void setInternalFormat(int internalFormat) { this.internalFormat = internalFormat; } + /** Sets whether mipmaps should be generated for the texture data. */ + public void setMipmap(boolean mipmap) { this.mipmap = mipmap; } + /** Sets whether the texture data is in compressed form. */ + public void setIsDataCompressed(boolean compressed) { this.dataIsCompressed = compressed; } + /** Sets whether the texture coordinates must be flipped vertically + for proper display. */ + public void setMustFlipVertically(boolean mustFlipVertically) { this.mustFlipVertically = mustFlipVertically; } + /** Sets the texture data. */ + public void setBuffer(Buffer buffer) { + this.buffer = buffer; + estimatedMemorySize = estimatedMemorySize(buffer); + } + /** Sets the required byte alignment for the texture data. */ + public void setAlignment(int alignment) { this.alignment = alignment; } + /** Sets the row length needed for correct GL_UNPACK_ROW_LENGTH + specification. This is currently only supported for + non-mipmapped, non-compressed textures. */ + public void setRowLength(int rowLength) { this.rowLength = rowLength; } + /** Indicates to this TextureData whether the GL_EXT_abgr extension + is available. Used for optimization along some code paths to + avoid data copies. */ + public void setHaveEXTABGR(boolean haveEXTABGR) { + this.haveEXTABGR = haveEXTABGR; + } + /** Indicates to this TextureData whether OpenGL version 1.2 is + available. If not, falls back to relatively inefficient code + paths for several input data types (several kinds of packed + pixel formats, in particular). */ + public void setHaveGL12(boolean haveGL12) { + this.haveGL12 = haveGL12; + } + + /** Returns an estimate of the amount of memory in bytes this + TextureData will consume once uploaded to the graphics card. It + should only be treated as an estimate; most applications should + not need to query this but instead let the OpenGL implementation + page textures in and out as necessary. */ + public int getEstimatedMemorySize() { + return estimatedMemorySize; + } + + /** Flushes resources associated with this TextureData by calling + Flusher.flush(). */ + public void flush() { + if (flusher != null) { + flusher.flush(); + flusher = null; + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private void createNIOBufferFromImage(BufferedImage image) { + buffer = wrapImageDataBuffer(image); + } + + private Buffer wrapImageDataBuffer(BufferedImage image) { + // + // Note: Grabbing the DataBuffer will defeat Java2D's image + // management mechanism (as of JDK 5/6, at least). This shouldn't + // be a problem for most JOGL apps, but those that try to upload + // the image into an OpenGL texture and then use the same image in + // Java2D rendering might find the 2D rendering is not as fast as + // it could be. + // + + DataBuffer data = image.getRaster().getDataBuffer(); + if (data instanceof DataBufferByte) { + return ByteBuffer.wrap(((DataBufferByte) data).getData()); + } else if (data instanceof DataBufferDouble) { + throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL"); + } else if (data instanceof DataBufferFloat) { + return FloatBuffer.wrap(((DataBufferFloat) data).getData()); + } else if (data instanceof DataBufferInt) { + return IntBuffer.wrap(((DataBufferInt) data).getData()); + } else if (data instanceof DataBufferShort) { + return ShortBuffer.wrap(((DataBufferShort) data).getData()); + } else if (data instanceof DataBufferUShort) { + return ShortBuffer.wrap(((DataBufferUShort) data).getData()); + } else { + throw new RuntimeException("Unexpected DataBuffer type?"); + } + } + + private void createFromImage(BufferedImage image) { + pixelType = 0; // Determine from image + mustFlipVertically = true; + + width = image.getWidth(); + height = image.getHeight(); + + int scanlineStride; + SampleModel sm = image.getRaster().getSampleModel(); + if (sm instanceof SinglePixelPackedSampleModel) { + scanlineStride = + ((SinglePixelPackedSampleModel)sm).getScanlineStride(); + } else if (sm instanceof MultiPixelPackedSampleModel) { + scanlineStride = + ((MultiPixelPackedSampleModel)sm).getScanlineStride(); + } else if (sm instanceof ComponentSampleModel) { + scanlineStride = + ((ComponentSampleModel)sm).getScanlineStride(); + } else { + // This will only happen for TYPE_CUSTOM anyway + setupLazyCustomConversion(image); + return; + } + + if(GLProfile.isGL2()) { + switch (image.getType()) { + case BufferedImage.TYPE_INT_RGB: + pixelFormat = GL2.GL_BGRA; + pixelType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + rowLength = scanlineStride; + alignment = 4; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_INT_ARGB_PRE: + pixelFormat = GL2.GL_BGRA; + pixelType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + rowLength = scanlineStride; + alignment = 4; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_INT_BGR: + pixelFormat = GL.GL_RGBA; + pixelType = GL2.GL_UNSIGNED_INT_8_8_8_8_REV; + rowLength = scanlineStride; + alignment = 4; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_3BYTE_BGR: + { + // we can pass the image data directly to OpenGL only if + // we have an integral number of pixels in each scanline + if ((scanlineStride % 3) == 0) { + pixelFormat = GL2.GL_BGR; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 3; + alignment = 1; + } else { + setupLazyCustomConversion(image); + return; + } + } + break; + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + { + // we can pass the image data directly to OpenGL only if + // we have an integral number of pixels in each scanline + // and only if the GL_EXT_abgr extension is present + + // NOTE: disabling this code path for now as it appears it's + // buggy at least on some NVidia drivers and doesn't perform + // the necessary byte swapping (FIXME: needs more + // investigation) + if ((scanlineStride % 4) == 0 && false) { + pixelFormat = GL2.GL_ABGR_EXT; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 4; + alignment = 4; + + // Store a reference to the original image for later in + // case it turns out that we don't have GL_EXT_abgr at the + // time we're going to do the texture upload to OpenGL + setupLazyCustomConversion(image); + expectingEXTABGR = true; + break; + } else { + setupLazyCustomConversion(image); + return; + } + } + case BufferedImage.TYPE_USHORT_565_RGB: + pixelFormat = GL.GL_RGB; + pixelType = GL.GL_UNSIGNED_SHORT_5_6_5; + rowLength = scanlineStride; + alignment = 2; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_USHORT_555_RGB: + pixelFormat = GL2.GL_BGRA; + pixelType = GL2.GL_UNSIGNED_SHORT_1_5_5_5_REV; + rowLength = scanlineStride; + alignment = 2; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_BYTE_GRAY: + pixelFormat = GL.GL_LUMINANCE; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride; + alignment = 1; + break; + case BufferedImage.TYPE_USHORT_GRAY: + pixelFormat = GL.GL_LUMINANCE; + pixelType = GL.GL_UNSIGNED_SHORT; + rowLength = scanlineStride; + alignment = 2; + break; + // Note: TYPE_INT_ARGB and TYPE_4BYTE_ABGR images go down the + // custom code path to satisfy the invariant that images with an + // alpha channel always go down with premultiplied alpha. + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_BYTE_INDEXED: + case BufferedImage.TYPE_CUSTOM: + default: + ColorModel cm = image.getColorModel(); + if (cm.equals(rgbColorModel)) { + pixelFormat = GL.GL_RGB; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 3; + alignment = 1; + } else if (cm.equals(rgbaColorModel)) { + pixelFormat = GL.GL_RGBA; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 4; // FIXME: correct? + alignment = 4; + } else { + setupLazyCustomConversion(image); + return; + } + break; + } + } else { + switch (image.getType()) { + case BufferedImage.TYPE_INT_RGB: + pixelFormat = GL.GL_RGB; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride; + alignment = 3; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_INT_ARGB_PRE: + throw new GLException("INT_ARGB_PRE n.a."); + case BufferedImage.TYPE_INT_BGR: + throw new GLException("INT_BGR n.a."); + case BufferedImage.TYPE_3BYTE_BGR: + throw new GLException("INT_BGR n.a."); + case BufferedImage.TYPE_4BYTE_ABGR_PRE: + throw new GLException("INT_BGR n.a."); + case BufferedImage.TYPE_USHORT_565_RGB: + pixelFormat = GL.GL_RGB; + pixelType = GL.GL_UNSIGNED_SHORT_5_6_5; + rowLength = scanlineStride; + alignment = 2; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_USHORT_555_RGB: + pixelFormat = GL.GL_RGBA; + pixelType = GL.GL_UNSIGNED_SHORT_5_5_5_1; + rowLength = scanlineStride; + alignment = 2; + expectingGL12 = true; + setupLazyCustomConversion(image); + break; + case BufferedImage.TYPE_BYTE_GRAY: + pixelFormat = GL.GL_LUMINANCE; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride; + alignment = 1; + break; + case BufferedImage.TYPE_USHORT_GRAY: + throw new GLException("USHORT_GRAY n.a."); + // Note: TYPE_INT_ARGB and TYPE_4BYTE_ABGR images go down the + // custom code path to satisfy the invariant that images with an + // alpha channel always go down with premultiplied alpha. + case BufferedImage.TYPE_INT_ARGB: + case BufferedImage.TYPE_4BYTE_ABGR: + case BufferedImage.TYPE_BYTE_BINARY: + case BufferedImage.TYPE_BYTE_INDEXED: + case BufferedImage.TYPE_CUSTOM: + default: + ColorModel cm = image.getColorModel(); + if (cm.equals(rgbColorModel)) { + pixelFormat = GL.GL_RGB; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 3; + alignment = 1; + } else if (cm.equals(rgbaColorModel)) { + pixelFormat = GL.GL_RGBA; + pixelType = GL.GL_UNSIGNED_BYTE; + rowLength = scanlineStride / 4; // FIXME: correct? + alignment = 4; + } else { + setupLazyCustomConversion(image); + return; + } + break; + } + } + + createNIOBufferFromImage(image); + } + + private void setupLazyCustomConversion(BufferedImage image) { + imageForLazyCustomConversion = image; + boolean hasAlpha = image.getColorModel().hasAlpha(); + if (pixelFormat == 0) { + pixelFormat = hasAlpha ? GL.GL_RGBA : GL.GL_RGB; + } + alignment = 1; // FIXME: do we need better? + rowLength = width; // FIXME: correct in all cases? + + // Allow previously-selected pixelType (if any) to override that + // we can infer from the DataBuffer + DataBuffer data = image.getRaster().getDataBuffer(); + if (data instanceof DataBufferByte || isPackedInt(image)) { + // Don't use GL_UNSIGNED_INT for BufferedImage packed int images + if (pixelType == 0) pixelType = GL.GL_UNSIGNED_BYTE; + } else if (data instanceof DataBufferDouble) { + throw new RuntimeException("DataBufferDouble rasters not supported by OpenGL"); + } else if (data instanceof DataBufferFloat) { + if (pixelType == 0) pixelType = GL.GL_FLOAT; + } else if ((data instanceof DataBufferInt) && GLProfile.isGL2ES2()) { + // FIXME: should we support signed ints? + if (pixelType == 0) pixelType = GL2.GL_UNSIGNED_INT; + } else if (data instanceof DataBufferShort) { + if (pixelType == 0) pixelType = GL.GL_SHORT; + } else if (data instanceof DataBufferUShort) { + if (pixelType == 0) pixelType = GL.GL_UNSIGNED_SHORT; + } else { + throw new RuntimeException("Unexpected DataBuffer type?"); + } + } + + private void createFromCustom(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + + // create a temporary image that is compatible with OpenGL + boolean hasAlpha = image.getColorModel().hasAlpha(); + ColorModel cm = null; + int dataBufferType = image.getRaster().getDataBuffer().getDataType(); + // Don't use integer components for packed int images + if (isPackedInt(image)) { + dataBufferType = DataBuffer.TYPE_BYTE; + } + if (dataBufferType == DataBuffer.TYPE_BYTE) { + cm = hasAlpha ? rgbaColorModel : rgbColorModel; + } else { + if (hasAlpha) { + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + null, true, true, + Transparency.TRANSLUCENT, + dataBufferType); + } else { + cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), + null, false, false, + Transparency.OPAQUE, + dataBufferType); + } + } + + boolean premult = cm.isAlphaPremultiplied(); + WritableRaster raster = + cm.createCompatibleWritableRaster(width, height); + BufferedImage texImage = new BufferedImage(cm, raster, premult, null); + + // copy the source image into the temporary image + Graphics2D g = texImage.createGraphics(); + g.setComposite(AlphaComposite.Src); + g.drawImage(image, 0, 0, null); + g.dispose(); + + // Wrap the buffer from the temporary image + createNIOBufferFromImage(texImage); + } + + private boolean isPackedInt(BufferedImage image) { + int imgType = image.getType(); + return (imgType == BufferedImage.TYPE_INT_RGB || + imgType == BufferedImage.TYPE_INT_BGR || + imgType == BufferedImage.TYPE_INT_ARGB || + imgType == BufferedImage.TYPE_INT_ARGB_PRE); + } + + private void revertPixelFormatAndType() { + // Knowing we don't have e.g. OpenGL 1.2 functionality available, + // and knowing we're in the process of doing the fallback code + // path, re-infer a vanilla pixel format and type compatible with + // OpenGL 1.1 + pixelFormat = 0; + pixelType = 0; + setupLazyCustomConversion(imageForLazyCustomConversion); + } + + private int estimatedMemorySize(Buffer buffer) { + if (buffer == null) { + return 0; + } + int capacity = buffer.capacity(); + if (buffer instanceof ByteBuffer) { + return capacity; + } else if (buffer instanceof IntBuffer) { + return capacity * BufferUtil.SIZEOF_INT; + } else if (buffer instanceof FloatBuffer) { + return capacity * BufferUtil.SIZEOF_FLOAT; + } else if (buffer instanceof ShortBuffer) { + return capacity * BufferUtil.SIZEOF_SHORT; + } else if (buffer instanceof LongBuffer) { + return capacity * BufferUtil.SIZEOF_LONG; + } else if (buffer instanceof DoubleBuffer) { + return capacity * BufferUtil.SIZEOF_DOUBLE; + } + throw new RuntimeException("Unexpected buffer type " + + buffer.getClass().getName()); + } +} diff --git a/src/classes/com/sun/opengl/util/texture/awt/AWTTextureReader.java b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureReader.java new file mode 100755 index 000000000..13d8dac0e --- /dev/null +++ b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureReader.java @@ -0,0 +1,968 @@ +/* + * Copyright (c) 2005 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.texture.awt; + +import java.awt.image.BufferedImage; + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.util.*; +import javax.imageio.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import com.sun.opengl.impl.Debug; +import com.sun.opengl.util.io.*; +import com.sun.opengl.util.texture.*; +import com.sun.opengl.util.texture.spi.*; + +/**

Provides input and output facilities for both loading OpenGL + textures from disk and streams as well as writing textures already + in memory back to disk.

+ +

The TextureIO class supports an arbitrary number of plug-in + readers and writers via TextureProviders and TextureWriters. + TextureProviders know how to produce TextureData objects from + files, InputStreams and URLs. TextureWriters know how to write + TextureData objects to disk in various file formats. The + TextureData class represents the raw data of the texture before it + has been converted to an OpenGL texture object. The Texture class + represents the OpenGL texture object and provides easy facilities + for using the texture.

+ +

There are several built-in TextureProviders and TextureWriters + supplied with the TextureIO implementation. The most basic + provider uses the platform's Image I/O facilities to read in a + BufferedImage and convert it to a texture. This is the baseline + provider and is registered so that it is the last one consulted. + All others are asked first to open a given file.

+ +

There are three other providers registered by default as of + the time of this writing. One handles SGI RGB (".sgi", ".rgb") + images from both files and streams. One handles DirectDraw Surface + (".dds") images read from files, though can not read these images + from streams. One handles Targa (".tga") images read from both + files and streams. These providers are executed in an arbitrary + order. Some of these providers require the file's suffix to either + be specified via the newTextureData methods or for the file to be + named with the appropriate suffix. In general a file suffix should + be provided to the newTexture and newTextureData methods if at all + possible.

+ +

Note that additional TextureProviders, if reading images from + InputStreams, must use the mark()/reset() methods on InputStream + when probing for e.g. magic numbers at the head of the file to + make sure not to disturb the state of the InputStream for + downstream TextureProviders.

+ +

There are analogous TextureWriters provided for writing + textures back to disk if desired. As of this writing, there are + four TextureWriters registered by default: one for Targa files, + one for SGI RGB files, one for DirectDraw surface (.dds) files, + and one for ImageIO-supplied formats such as .jpg and .png. Some + of these writers have certain limitations such as only being able + to write out textures stored in GL_RGB or GL_RGBA format. The DDS + writer supports fetching and writing to disk of texture data in + DXTn compressed format. Whether this will occur is dependent on + whether the texture's internal format is one of the DXTn + compressed formats and whether the target file is .dds format. +*/ + +public class AWTTextureReader { + private static final boolean DEBUG = Debug.debug("TextureReader"); + + // For manually disabling the use of the texture rectangle + // extensions so you know the texture target is GL_TEXTURE_2D; this + // is useful for shader writers (thanks to Chris Campbell for this + // observation) + private static boolean texRectEnabled = true; + + //---------------------------------------------------------------------- + // methods that *do not* require a current context + // These methods assume RGB or RGBA textures. + // Some texture providers may not recognize the file format unless + // the fileSuffix is specified, so it is strongly recommended to + // specify it wherever it is known. + // Some texture providers may also only support one kind of input, + // i.e., reading from a file as opposed to a stream. + + /** + * Creates a TextureData from the given file. Does no OpenGL work. + * + * @param file the file from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the file, or null if none of the + * registered texture providers could read the file + * @throws IOException if an error occurred while reading the file + */ + public static TextureData newTextureData(File file, + boolean mipmap, + String fileSuffix) throws IOException { + if (fileSuffix == null) { + fileSuffix = FileUtil.getFileSuffix(file); + } + return newTextureDataImpl(file, 0, 0, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given stream. Does no OpenGL work. + * + * @param stream the stream from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the stream, or null if none of the + * registered texture providers could read the stream + * @throws IOException if an error occurred while reading the stream + */ + public static TextureData newTextureData(InputStream stream, + boolean mipmap, + String fileSuffix) throws IOException { + return newTextureDataImpl(stream, 0, 0, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given URL. Does no OpenGL work. + * + * @param url the URL from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the URL, or null if none of the + * registered texture providers could read the URL + * @throws IOException if an error occurred while reading the URL + */ + public static TextureData newTextureData(URL url, + boolean mipmap, + String fileSuffix) throws IOException { + if (fileSuffix == null) { + fileSuffix = FileUtil.getFileSuffix(url.getPath()); + } + return newTextureDataImpl(url, 0, 0, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given BufferedImage. Does no + * OpenGL work. + * + * @param image the BufferedImage containing the texture data + * @param mipmap whether mipmaps should be produced for this + * texture by autogenerating them + * @return the texture data from the image + */ + public static TextureData newTextureData(BufferedImage image, + boolean mipmap) { + return newTextureDataImpl(image, 0, 0, mipmap); + } + + //---------------------------------------------------------------------- + // These methods make no assumption about the OpenGL internal format + // or pixel format of the texture; they must be specified by the + // user. It is not allowed to supply 0 (indicating no preference) + // for either the internalFormat or the pixelFormat; + // IllegalArgumentException will be thrown in this case. + + /** + * Creates a TextureData from the given file, using the specified + * OpenGL internal format and pixel format for the texture which + * will eventually result. The internalFormat and pixelFormat must + * be specified and may not be zero; to use default values, use the + * variant of this method which does not take these arguments. Does + * no OpenGL work. + * + * @param file the file from which to read the texture data + * @param internalFormat the OpenGL internal format of the texture + * which will eventually result from the TextureData + * @param pixelFormat the OpenGL pixel format of the texture + * which will eventually result from the TextureData + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the file, or null if none of the + * registered texture providers could read the file + * @throws IllegalArgumentException if either internalFormat or + * pixelFormat was 0 + * @throws IOException if an error occurred while reading the file + */ + public static TextureData newTextureData(File file, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException, IllegalArgumentException { + if ((internalFormat == 0) || (pixelFormat == 0)) { + throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); + } + + if (fileSuffix == null) { + fileSuffix = FileUtil.getFileSuffix(file); + } + + return newTextureDataImpl(file, internalFormat, pixelFormat, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given stream, using the specified + * OpenGL internal format and pixel format for the texture which + * will eventually result. The internalFormat and pixelFormat must + * be specified and may not be zero; to use default values, use the + * variant of this method which does not take these arguments. Does + * no OpenGL work. + * + * @param stream the stream from which to read the texture data + * @param internalFormat the OpenGL internal format of the texture + * which will eventually result from the TextureData + * @param pixelFormat the OpenGL pixel format of the texture + * which will eventually result from the TextureData + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the stream, or null if none of the + * registered texture providers could read the stream + * @throws IllegalArgumentException if either internalFormat or + * pixelFormat was 0 + * @throws IOException if an error occurred while reading the stream + */ + public static TextureData newTextureData(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException, IllegalArgumentException { + if ((internalFormat == 0) || (pixelFormat == 0)) { + throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); + } + + return newTextureDataImpl(stream, internalFormat, pixelFormat, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given URL, using the specified + * OpenGL internal format and pixel format for the texture which + * will eventually result. The internalFormat and pixelFormat must + * be specified and may not be zero; to use default values, use the + * variant of this method which does not take these arguments. Does + * no OpenGL work. + * + * @param url the URL from which to read the texture data + * @param internalFormat the OpenGL internal format of the texture + * which will eventually result from the TextureData + * @param pixelFormat the OpenGL pixel format of the texture + * which will eventually result from the TextureData + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @return the texture data from the URL, or null if none of the + * registered texture providers could read the URL + * @throws IllegalArgumentException if either internalFormat or + * pixelFormat was 0 + * @throws IOException if an error occurred while reading the URL + */ + public static TextureData newTextureData(URL url, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException, IllegalArgumentException { + if ((internalFormat == 0) || (pixelFormat == 0)) { + throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); + } + + if (fileSuffix == null) { + fileSuffix = FileUtil.getFileSuffix(url.getPath()); + } + + return newTextureDataImpl(url, internalFormat, pixelFormat, mipmap, fileSuffix); + } + + /** + * Creates a TextureData from the given BufferedImage, using the + * specified OpenGL internal format and pixel format for the texture + * which will eventually result. The internalFormat and pixelFormat + * must be specified and may not be zero; to use default values, use + * the variant of this method which does not take these + * arguments. Does no OpenGL work. + * + * @param image the BufferedImage containing the texture data + * @param internalFormat the OpenGL internal format of the texture + * which will eventually result from the TextureData + * @param pixelFormat the OpenGL pixel format of the texture + * which will eventually result from the TextureData + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @return the texture data from the image + * @throws IllegalArgumentException if either internalFormat or + * pixelFormat was 0 + */ + public static TextureData newTextureData(BufferedImage image, + int internalFormat, + int pixelFormat, + boolean mipmap) throws IllegalArgumentException { + if ((internalFormat == 0) || (pixelFormat == 0)) { + throw new IllegalArgumentException("internalFormat and pixelFormat must be non-zero"); + } + + return newTextureDataImpl(image, internalFormat, pixelFormat, mipmap); + } + + //---------------------------------------------------------------------- + // methods that *do* require a current context + // + + /** + * Creates an OpenGL texture object from the specified TextureData + * using the current OpenGL context. + * + * @param data the texture data to turn into an OpenGL texture + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + * @throws IllegalArgumentException if the passed TextureData was null + */ + public static Texture newTexture(TextureData data) throws GLException, IllegalArgumentException { + if (data == null) { + throw new IllegalArgumentException("Null TextureData"); + } + return new Texture(data); + } + + /** + * Creates an OpenGL texture object from the specified file using + * the current OpenGL context. + * + * @param file the file from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @throws IOException if an error occurred while reading the file + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + */ + public static Texture newTexture(File file, boolean mipmap) throws IOException, GLException { + TextureData data = newTextureData(file, mipmap, FileUtil.getFileSuffix(file)); + Texture texture = newTexture(data); + data.flush(); + return texture; + } + + /** + * Creates an OpenGL texture object from the specified stream using + * the current OpenGL context. + * + * @param stream the stream from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @throws IOException if an error occurred while reading the stream + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + */ + public static Texture newTexture(InputStream stream, boolean mipmap, String fileSuffix) throws IOException, GLException { + TextureData data = newTextureData(stream, mipmap, fileSuffix); + Texture texture = newTexture(data); + data.flush(); + return texture; + } + + /** + * Creates an OpenGL texture object from the specified URL using the + * current OpenGL context. + * + * @param url the URL from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture either by autogenerating them or + * reading them from the file. Some file formats + * support multiple mipmaps in a single file in + * which case those mipmaps will be used rather + * than generating them. + * @param fileSuffix the suffix of the file name to be used as a + * hint of the file format to the underlying + * texture provider, or null if none and should be + * auto-detected (some texture providers do not + * support this) + * @throws IOException if an error occurred while reading the URL + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + */ + public static Texture newTexture(URL url, boolean mipmap, String fileSuffix) throws IOException, GLException { + if (fileSuffix == null) { + fileSuffix = FileUtil.getFileSuffix(url.getPath()); + } + TextureData data = newTextureData(url, mipmap, fileSuffix); + Texture texture = newTexture(data); + data.flush(); + return texture; + } + + /** + * Creates an OpenGL texture object from the specified BufferedImage + * using the current OpenGL context. + * + * @param image the BufferedImage from which to read the texture data + * @param mipmap whether mipmaps should be produced for this + * texture by autogenerating them + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + */ + public static Texture newTexture(BufferedImage image, boolean mipmap) throws GLException { + TextureData data = newTextureData(image, mipmap); + Texture texture = newTexture(data); + data.flush(); + return texture; + } + + /** + * Creates an OpenGL texture object associated with the given OpenGL + * texture target using the current OpenGL context. The texture has + * no initial data. This is used, for example, to construct cube + * maps out of multiple TextureData objects. + * + * @throws GLException if no OpenGL context is current or if an + * OpenGL error occurred + */ + public static Texture newTexture(int target) throws GLException { + return new Texture(target); + } + + //---------------------------------------------------------------------- + // SPI support + // + + /** Adds a TextureProvider to support reading of a new file + format. */ + public static void addTextureProvider(TextureProvider provider) { + // Must always add at the front so the ImageIO provider is last, + // so we don't accidentally use it instead of a user's possibly + // more optimal provider + textureProviders.add(0, provider); + } + + //--------------------------------------------------------------------------- + // Global disabling of texture rectangle extension + // + + /** Toggles the use of the GL_ARB_texture_rectangle extension by the + TextureIO classes. By default, on hardware supporting this + extension, the TextureIO classes may use the + GL_ARB_texture_rectangle extension for non-power-of-two + textures. (If the hardware supports the + GL_ARB_texture_non_power_of_two extension, that one is + preferred.) In some situations, for example when writing + shaders, it is advantageous to force the texture target to + always be GL_TEXTURE_2D in order to have one version of the + shader, even at the expense of texture memory in the case where + NPOT textures are not supported. This method allows the use of + the GL_ARB_texture_rectangle extension to be turned off globally + for this purpose. The default is that the use of the extension + is enabled. */ + public static void setTexRectEnabled(boolean enabled) { + texRectEnabled = enabled; + } + + /** Indicates whether the GL_ARB_texture_rectangle extension is + allowed to be used for non-power-of-two textures; see {@link + #setTexRectEnabled setTexRectEnabled}. */ + public static boolean isTexRectEnabled() { + return texRectEnabled; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static List/**/ textureProviders = new ArrayList/**/(); + private static List/**/ textureWriters = new ArrayList/**/(); + + static { + // ImageIO provider, the fall-back, must be the first one added + addTextureProvider(new IIOTextureProvider()); + + // Other special-case providers + addTextureProvider(new DDSTextureProvider()); + addTextureProvider(new SGITextureProvider()); + addTextureProvider(new TGATextureProvider()); + } + + // Implementation methods + private static TextureData newTextureDataImpl(File file, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (file == null) { + throw new IOException("File was null"); + } + + fileSuffix = toLowerCase(fileSuffix); + + for (Iterator iter = textureProviders.iterator(); iter.hasNext(); ) { + TextureProvider provider = (TextureProvider) iter.next(); + TextureData data = provider.newTextureData(file, + internalFormat, + pixelFormat, + mipmap, + fileSuffix); + if (data != null) { + return data; + } + } + + throw new IOException("No suitable reader for given file"); + } + + private static TextureData newTextureDataImpl(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (stream == null) { + throw new IOException("Stream was null"); + } + + fileSuffix = toLowerCase(fileSuffix); + + // Note: use of BufferedInputStream works around 4764639/4892246 + if (!(stream instanceof BufferedInputStream)) { + stream = new BufferedInputStream(stream); + } + + for (Iterator iter = textureProviders.iterator(); iter.hasNext(); ) { + TextureProvider provider = (TextureProvider) iter.next(); + TextureData data = provider.newTextureData(stream, + internalFormat, + pixelFormat, + mipmap, + fileSuffix); + if (data != null) { + return data; + } + } + + throw new IOException("No suitable reader for given stream"); + } + + private static TextureData newTextureDataImpl(URL url, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (url == null) { + throw new IOException("URL was null"); + } + + fileSuffix = toLowerCase(fileSuffix); + + for (Iterator iter = textureProviders.iterator(); iter.hasNext(); ) { + TextureProvider provider = (TextureProvider) iter.next(); + TextureData data = provider.newTextureData(url, + internalFormat, + pixelFormat, + mipmap, + fileSuffix); + if (data != null) { + return data; + } + } + + throw new IOException("No suitable reader for given URL"); + } + + private static TextureData newTextureDataImpl(BufferedImage image, + int internalFormat, + int pixelFormat, + boolean mipmap) { + return new AWTTextureData(internalFormat, pixelFormat, mipmap, image); + } + + //---------------------------------------------------------------------- + // Base provider - used last + static class IIOTextureProvider implements TextureProvider { + public TextureData newTextureData(File file, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + BufferedImage img = ImageIO.read(file); + if (img == null) { + return null; + } + if (DEBUG) { + System.out.println("TextureIO.newTextureData(): BufferedImage type for " + file + " = " + + img.getType()); + } + return new AWTTextureData(internalFormat, pixelFormat, mipmap, img); + } + + public TextureData newTextureData(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + BufferedImage img = ImageIO.read(stream); + if (img == null) { + return null; + } + if (DEBUG) { + System.out.println("TextureIO.newTextureData(): BufferedImage type for stream = " + + img.getType()); + } + return new AWTTextureData(internalFormat, pixelFormat, mipmap, img); + } + + public TextureData newTextureData(URL url, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + InputStream stream = url.openStream(); + try { + return newTextureData(stream, internalFormat, pixelFormat, mipmap, fileSuffix); + } finally { + stream.close(); + } + } + } + + //---------------------------------------------------------------------- + // DDS provider -- supports files only for now + static class DDSTextureProvider implements TextureProvider { + public TextureData newTextureData(File file, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (TextureUtil.DDS.equals(fileSuffix) || + TextureUtil.DDS.equals(FileUtil.getFileSuffix(file))) { + DDSImage image = DDSImage.read(file); + return newTextureData(image, internalFormat, pixelFormat, mipmap); + } + + return null; + } + + public TextureData newTextureData(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (TextureUtil.DDS.equals(fileSuffix) || + DDSImage.isDDSImage(stream)) { + byte[] data = StreamUtil.readAll(stream); + ByteBuffer buf = ByteBuffer.wrap(data); + DDSImage image = DDSImage.read(buf); + return newTextureData(image, internalFormat, pixelFormat, mipmap); + } + + return null; + } + + public TextureData newTextureData(URL url, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + InputStream stream = new BufferedInputStream(url.openStream()); + try { + return newTextureData(stream, internalFormat, pixelFormat, mipmap, fileSuffix); + } finally { + stream.close(); + } + } + + private TextureData newTextureData(final DDSImage image, + int internalFormat, + int pixelFormat, + boolean mipmap) { + DDSImage.ImageInfo info = image.getMipMap(0); + if (pixelFormat == 0) { + switch (image.getPixelFormat()) { + case DDSImage.D3DFMT_R8G8B8: + pixelFormat = GL.GL_RGB; + break; + default: + pixelFormat = GL.GL_RGBA; + break; + } + } + if (info.isCompressed()) { + switch (info.getCompressionFormat()) { + case DDSImage.D3DFMT_DXT1: + internalFormat = GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + case DDSImage.D3DFMT_DXT3: + internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case DDSImage.D3DFMT_DXT5: + internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + default: + throw new RuntimeException("Unsupported DDS compression format \"" + + DDSImage.getCompressionFormatName(info.getCompressionFormat()) + "\""); + } + } + if (internalFormat == 0) { + switch (image.getPixelFormat()) { + case DDSImage.D3DFMT_R8G8B8: + pixelFormat = GL.GL_RGB; + break; + default: + pixelFormat = GL.GL_RGBA; + break; + } + } + TextureData.Flusher flusher = new TextureData.Flusher() { + public void flush() { + image.close(); + } + }; + TextureData data; + if (mipmap && image.getNumMipMaps() > 0) { + Buffer[] mipmapData = new Buffer[image.getNumMipMaps()]; + for (int i = 0; i < image.getNumMipMaps(); i++) { + mipmapData[i] = image.getMipMap(i).getData(); + } + data = new AWTTextureData(internalFormat, + info.getWidth(), + info.getHeight(), + 0, + pixelFormat, + GL.GL_UNSIGNED_BYTE, + info.isCompressed(), + true, + mipmapData, + flusher); + } else { + // Fix this up for the end user because we can't generate + // mipmaps for compressed textures + mipmap = false; + data = new AWTTextureData(internalFormat, + info.getWidth(), + info.getHeight(), + 0, + pixelFormat, + GL.GL_UNSIGNED_BYTE, + mipmap, + info.isCompressed(), + true, + info.getData(), + flusher); + } + return data; + } + } + + //---------------------------------------------------------------------- + // Base class for SGI RGB and TGA image providers + static abstract class StreamBasedTextureProvider implements TextureProvider { + public TextureData newTextureData(File file, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + InputStream inStream = new BufferedInputStream(new FileInputStream(file)); + try { + // The SGIImage and TGAImage implementations use InputStreams + // anyway so there isn't much point in having a separate code + // path for files + return newTextureData(inStream, + internalFormat, + pixelFormat, + mipmap, + ((fileSuffix != null) ? fileSuffix : FileUtil.getFileSuffix(file))); + } finally { + inStream.close(); + } + } + + public TextureData newTextureData(URL url, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + InputStream stream = new BufferedInputStream(url.openStream()); + try { + return newTextureData(stream, internalFormat, pixelFormat, mipmap, fileSuffix); + } finally { + stream.close(); + } + } + } + + //---------------------------------------------------------------------- + // SGI RGB image provider + static class SGITextureProvider extends StreamBasedTextureProvider { + public TextureData newTextureData(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (TextureUtil.SGI.equals(fileSuffix) || + TextureUtil.SGI_RGB.equals(fileSuffix) || + SGIImage.isSGIImage(stream)) { + SGIImage image = SGIImage.read(stream); + if (pixelFormat == 0) { + pixelFormat = image.getFormat(); + } + if (internalFormat == 0) { + internalFormat = image.getFormat(); + } + return new AWTTextureData(internalFormat, + image.getWidth(), + image.getHeight(), + 0, + pixelFormat, + GL.GL_UNSIGNED_BYTE, + mipmap, + false, + false, + ByteBuffer.wrap(image.getData()), + null); + } + + return null; + } + } + + //---------------------------------------------------------------------- + // TGA (Targa) image provider + static class TGATextureProvider extends StreamBasedTextureProvider { + public TextureData newTextureData(InputStream stream, + int internalFormat, + int pixelFormat, + boolean mipmap, + String fileSuffix) throws IOException { + if (TextureUtil.TGA.equals(fileSuffix)) { + TGAImage image = TGAImage.read(stream); + if (pixelFormat == 0) { + pixelFormat = image.getGLFormat(); + } + if (internalFormat == 0) { + internalFormat = GL.GL_RGBA8; + } + return new AWTTextureData(internalFormat, + image.getWidth(), + image.getHeight(), + 0, + pixelFormat, + GL.GL_UNSIGNED_BYTE, + mipmap, + false, + false, + image.getData(), + null); + } + + return null; + } + } + //---------------------------------------------------------------------- + // Helper routines + // + + private static String toLowerCase(String arg) { + if (arg == null) { + return null; + } + + return arg.toLowerCase(); + } +} diff --git a/src/classes/com/sun/opengl/util/texture/awt/AWTTextureWriter.java b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureWriter.java new file mode 100755 index 000000000..29082c27c --- /dev/null +++ b/src/classes/com/sun/opengl/util/texture/awt/AWTTextureWriter.java @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2005 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.texture.awt; + +import java.awt.Graphics; +import java.awt.image.*; +import java.io.*; +import java.net.*; +import java.nio.*; +import java.util.*; +import javax.imageio.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import com.sun.opengl.impl.Debug; +import com.sun.opengl.util.awt.*; +import com.sun.opengl.util.io.*; +import com.sun.opengl.util.texture.*; +import com.sun.opengl.util.texture.spi.*; + +/**

Provides input and output facilities for both loading OpenGL + textures from disk and streams as well as writing textures already + in memory back to disk.

+ +

The TextureIO class supports an arbitrary number of plug-in + readers and writers via TextureProviders and TextureWriters. + TextureProviders know how to produce TextureData objects from + files, InputStreams and URLs. TextureWriters know how to write + TextureData objects to disk in various file formats. The + TextureData class represents the raw data of the texture before it + has been converted to an OpenGL texture object. The Texture class + represents the OpenGL texture object and provides easy facilities + for using the texture.

+ +

There are several built-in TextureProviders and TextureWriters + supplied with the TextureIO implementation. The most basic + provider uses the platform's Image I/O facilities to read in a + BufferedImage and convert it to a texture. This is the baseline + provider and is registered so that it is the last one consulted. + All others are asked first to open a given file.

+ +

There are three other providers registered by default as of + the time of this writing. One handles SGI RGB (".sgi", ".rgb") + images from both files and streams. One handles DirectDraw Surface + (".dds") images read from files, though can not read these images + from streams. One handles Targa (".tga") images read from both + files and streams. These providers are executed in an arbitrary + order. Some of these providers require the file's suffix to either + be specified via the newTextureData methods or for the file to be + named with the appropriate suffix. In general a file suffix should + be provided to the newTexture and newTextureData methods if at all + possible.

+ +

Note that additional TextureProviders, if reading images from + InputStreams, must use the mark()/reset() methods on InputStream + when probing for e.g. magic numbers at the head of the file to + make sure not to disturb the state of the InputStream for + downstream TextureProviders.

+ +

There are analogous TextureWriters provided for writing + textures back to disk if desired. As of this writing, there are + four TextureWriters registered by default: one for Targa files, + one for SGI RGB files, one for DirectDraw surface (.dds) files, + and one for ImageIO-supplied formats such as .jpg and .png. Some + of these writers have certain limitations such as only being able + to write out textures stored in GL_RGB or GL_RGBA format. The DDS + writer supports fetching and writing to disk of texture data in + DXTn compressed format. Whether this will occur is dependent on + whether the texture's internal format is one of the DXTn + compressed formats and whether the target file is .dds format. +*/ + +public class AWTTextureWriter { + private static final boolean DEBUG = Debug.debug("TextureReader"); + + //---------------------------------------------------------------------- + // methods that *do not* require a current context + // These methods assume RGB or RGBA textures. + // Some texture providers may not recognize the file format unless + // the fileSuffix is specified, so it is strongly recommended to + // specify it wherever it is known. + // Some texture providers may also only support one kind of input, + // i.e., reading from a file as opposed to a stream. + + /** + * Writes the given texture to a file. The type of the file is + * inferred from its suffix. An OpenGL context must be current in + * order to fetch the texture data back from the OpenGL pipeline. + * This method causes the specified Texture to be bound to the + * GL_TEXTURE_2D state. If no suitable writer for the requested file + * format was found, throws an IOException.

+ * + * Reasonable attempts are made to produce good results in the + * resulting images. The Targa, SGI and ImageIO writers produce + * results in the correct vertical orientation for those file + * formats. The DDS writer performs no vertical flip of the data, + * even in uncompressed mode. (It is impossible to perform such a + * vertical flip with compressed data.) Applications should keep + * this in mind when using this routine to save textures to disk for + * later re-loading.

+ * + * Any mipmaps for the specified texture are currently discarded + * when it is written to disk, regardless of whether the underlying + * file format supports multiple mipmaps in a given file. + * + * @throws IOException if an error occurred during writing or no + * suitable writer was found + * @throws GLException if no OpenGL context was current or an + * OpenGL-related error occurred + */ + public static void write(Texture texture, File file) throws IOException, GLException { + if (texture.getTarget() != GL.GL_TEXTURE_2D) { + throw new GLException("Only GL_TEXTURE_2D textures are supported"); + } + + // First fetch the texture data + GL _gl = GLU.getCurrentGL(); + if(!_gl.isGL2()) { + throw new GLException("Only GL2 supports fetching compressed images, GL: "+_gl); + } + GL2 gl = _gl.getGL2(); + + texture.bind(); + int internalFormat = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_INTERNAL_FORMAT); + int width = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_WIDTH); + int height = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_HEIGHT); + int border = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_BORDER); + TextureData data = null; + if (internalFormat == GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT || + internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT || + internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT || + internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) { + // Fetch using glGetCompressedTexImage + int size = glGetTexLevelParameteri(gl, GL.GL_TEXTURE_2D, 0, GL2.GL_TEXTURE_COMPRESSED_IMAGE_SIZE); + ByteBuffer res = ByteBuffer.allocate(size); + gl.glGetCompressedTexImage(GL.GL_TEXTURE_2D, 0, res); + data = new AWTTextureData(internalFormat, width, height, border, internalFormat, GL.GL_UNSIGNED_BYTE, + false, true, true, res, null); + } else { + int bytesPerPixel = 0; + int fetchedFormat = 0; + switch (internalFormat) { + case GL.GL_RGB: + case GL2.GL_BGR: + case GL.GL_RGB8: + bytesPerPixel = 3; + fetchedFormat = GL.GL_RGB; + break; + case GL.GL_RGBA: + case GL2.GL_BGRA: + case GL2.GL_ABGR_EXT: + case GL.GL_RGBA8: + bytesPerPixel = 4; + fetchedFormat = GL.GL_RGBA; + break; + default: + throw new IOException("Unsupported texture internal format 0x" + Integer.toHexString(internalFormat)); + } + + // Fetch using glGetTexImage + int packAlignment = glGetInteger(GL.GL_PACK_ALIGNMENT); + int packRowLength = glGetInteger(GL2.GL_PACK_ROW_LENGTH); + int packSkipRows = glGetInteger(GL2.GL_PACK_SKIP_ROWS); + int packSkipPixels = glGetInteger(GL2.GL_PACK_SKIP_PIXELS); + int packSwapBytes = glGetInteger(GL2.GL_PACK_SWAP_BYTES); + + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, 0); + gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, 0); + gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, 0); + gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, 0); + + ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) * + (height + (2 * border)) * + bytesPerPixel); + if (DEBUG) { + System.out.println("Allocated buffer of size " + res.remaining() + " for fetched image (" + + ((fetchedFormat == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA") + ")"); + } + gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res); + + gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment); + gl.glPixelStorei(GL2.GL_PACK_ROW_LENGTH, packRowLength); + gl.glPixelStorei(GL2.GL_PACK_SKIP_ROWS, packSkipRows); + gl.glPixelStorei(GL2.GL_PACK_SKIP_PIXELS, packSkipPixels); + gl.glPixelStorei(GL2.GL_PACK_SWAP_BYTES, packSwapBytes); + + data = new AWTTextureData(internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE, + false, false, false, res, null); + + if (DEBUG) { + System.out.println("data.getPixelFormat() = " + + ((data.getPixelFormat() == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA")); + } + } + + for (Iterator iter = textureWriters.iterator(); iter.hasNext(); ) { + TextureWriter writer = (TextureWriter) iter.next(); + if (writer.write(file, data)) { + return; + } + } + + throw new IOException("No suitable texture writer found"); + } + + //---------------------------------------------------------------------- + // SPI support + // + + /** Adds a TextureWriter to support writing of a new file + format. */ + public static void addTextureWriter(TextureWriter writer) { + // Must always add at the front so the ImageIO writer is last, + // so we don't accidentally use it instead of a user's possibly + // more optimal writer + textureWriters.add(0, writer); + } + + //--------------------------------------------------------------------------- + // Global disabling of texture rectangle extension + // + + //---------------------------------------------------------------------- + // Internals only below this point + // + + private static List/**/ textureWriters = new ArrayList/**/(); + + static { + // ImageIO writer, the fall-back, must be the first one added + textureWriters.add(new IIOTextureWriter()); + + // Other special-case writers + addTextureWriter(new DDSTextureWriter()); + addTextureWriter(new SGITextureWriter()); + addTextureWriter(new TGATextureWriter()); + } + + //---------------------------------------------------------------------- + // ImageIO texture writer + // + static class IIOTextureWriter implements TextureWriter { + public boolean write(File file, + TextureData data) throws IOException { + int pixelFormat = data.getPixelFormat(); + int pixelType = data.getPixelType(); + if ((pixelFormat == GL.GL_RGB || + pixelFormat == GL.GL_RGBA) && + (pixelType == GL.GL_BYTE || + pixelType == GL.GL_UNSIGNED_BYTE)) { + // Convert TextureData to appropriate BufferedImage + // FIXME: almost certainly not obeying correct pixel order + BufferedImage image = new BufferedImage(data.getWidth(), data.getHeight(), + (pixelFormat == GL.GL_RGB) ? + BufferedImage.TYPE_3BYTE_BGR : + BufferedImage.TYPE_4BYTE_ABGR); + byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); + ByteBuffer buf = (ByteBuffer) data.getBuffer(); + if (buf == null) { + buf = (ByteBuffer) data.getMipmapData()[0]; + } + buf.rewind(); + buf.get(imageData); + buf.rewind(); + + // Swizzle image components to be correct + if (pixelFormat == GL.GL_RGB) { + for (int i = 0; i < imageData.length; i += 3) { + byte red = imageData[i + 0]; + byte blue = imageData[i + 2]; + imageData[i + 0] = blue; + imageData[i + 2] = red; + } + } else { + for (int i = 0; i < imageData.length; i += 4) { + byte red = imageData[i + 0]; + byte green = imageData[i + 1]; + byte blue = imageData[i + 2]; + byte alpha = imageData[i + 3]; + imageData[i + 0] = alpha; + imageData[i + 1] = blue; + imageData[i + 2] = green; + imageData[i + 3] = red; + } + } + + // Flip image vertically for the user's convenience + ImageUtil.flipImageVertically(image); + + // Happened to notice that writing RGBA images to JPEGS is broken + if (TextureUtil.JPG.equals(FileUtil.getFileSuffix(file)) && + image.getType() == BufferedImage.TYPE_4BYTE_ABGR) { + BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(), + BufferedImage.TYPE_3BYTE_BGR); + Graphics g = tmpImage.getGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + image = tmpImage; + } + + return ImageIO.write(image, FileUtil.getFileSuffix(file), file); + } + + throw new IOException("ImageIO writer doesn't support this pixel format / type (only GL_RGB/A + bytes)"); + } + } + + //---------------------------------------------------------------------- + // DDS texture writer + // + static class DDSTextureWriter implements TextureWriter { + public boolean write(File file, + TextureData data) throws IOException { + if (TextureUtil.DDS.equals(FileUtil.getFileSuffix(file))) { + // See whether the DDS writer can handle this TextureData + int pixelFormat = data.getPixelFormat(); + int pixelType = data.getPixelType(); + if (pixelType != GL.GL_BYTE && + pixelType != GL.GL_UNSIGNED_BYTE) { + throw new IOException("DDS writer only supports byte / unsigned byte textures"); + } + + int d3dFormat = 0; + // FIXME: some of these are probably not completely correct and would require swizzling + switch (pixelFormat) { + case GL.GL_RGB: d3dFormat = DDSImage.D3DFMT_R8G8B8; break; + case GL.GL_RGBA: d3dFormat = DDSImage.D3DFMT_A8R8G8B8; break; + case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: d3dFormat = DDSImage.D3DFMT_DXT1; break; + case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: throw new IOException("RGBA DXT1 not yet supported"); + case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: d3dFormat = DDSImage.D3DFMT_DXT3; break; + case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: d3dFormat = DDSImage.D3DFMT_DXT5; break; + default: throw new IOException("Unsupported pixel format 0x" + Integer.toHexString(pixelFormat) + " by DDS writer"); + } + + ByteBuffer[] mipmaps = null; + if (data.getMipmapData() != null) { + mipmaps = new ByteBuffer[data.getMipmapData().length]; + for (int i = 0; i < mipmaps.length; i++) { + mipmaps[i] = (ByteBuffer) data.getMipmapData()[i]; + } + } else { + mipmaps = new ByteBuffer[] { (ByteBuffer) data.getBuffer() }; + } + + DDSImage image = DDSImage.createFromData(d3dFormat, + data.getWidth(), + data.getHeight(), + mipmaps); + image.write(file); + return true; + } + + return false; + } + } + + //---------------------------------------------------------------------- + // SGI (rgb) texture writer + // + static class SGITextureWriter implements TextureWriter { + public boolean write(File file, + TextureData data) throws IOException { + String fileSuffix = FileUtil.getFileSuffix(file); + if (TextureUtil.SGI.equals(fileSuffix) || + TextureUtil.SGI_RGB.equals(fileSuffix)) { + // See whether the SGI writer can handle this TextureData + int pixelFormat = data.getPixelFormat(); + int pixelType = data.getPixelType(); + if ((pixelFormat == GL.GL_RGB || + pixelFormat == GL.GL_RGBA) && + (pixelType == GL.GL_BYTE || + pixelType == GL.GL_UNSIGNED_BYTE)) { + ByteBuffer buf = ((data.getBuffer() != null) ? + (ByteBuffer) data.getBuffer() : + (ByteBuffer) data.getMipmapData()[0]); + byte[] bytes; + if (buf.hasArray()) { + bytes = buf.array(); + } else { + buf.rewind(); + bytes = new byte[buf.remaining()]; + buf.get(bytes); + buf.rewind(); + } + + SGIImage image = SGIImage.createFromData(data.getWidth(), + data.getHeight(), + (pixelFormat == GL.GL_RGBA), + bytes); + image.write(file, false); + return true; + } + + throw new IOException("SGI writer doesn't support this pixel format / type (only GL_RGB/A + bytes)"); + } + + return false; + } + } + + //---------------------------------------------------------------------- + // TGA (Targa) texture writer + + static class TGATextureWriter implements TextureWriter { + public boolean write(File file, + TextureData data) throws IOException { + if (TextureUtil.TGA.equals(FileUtil.getFileSuffix(file))) { + // See whether the TGA writer can handle this TextureData + int pixelFormat = data.getPixelFormat(); + int pixelType = data.getPixelType(); + if ((pixelFormat == GL.GL_RGB || + pixelFormat == GL.GL_RGBA) && + (pixelType == GL.GL_BYTE || + pixelType == GL.GL_UNSIGNED_BYTE)) { + ByteBuffer buf = ((data.getBuffer() != null) ? + (ByteBuffer) data.getBuffer() : + (ByteBuffer) data.getMipmapData()[0]); + // Must reverse order of red and blue channels to get correct results + int skip = ((pixelFormat == GL.GL_RGB) ? 3 : 4); + for (int i = 0; i < buf.remaining(); i += skip) { + byte red = buf.get(i + 0); + byte blue = buf.get(i + 2); + buf.put(i + 0, blue); + buf.put(i + 2, red); + } + + TGAImage image = TGAImage.createFromData(data.getWidth(), + data.getHeight(), + (pixelFormat == GL.GL_RGBA), + false, + ((data.getBuffer() != null) ? + (ByteBuffer) data.getBuffer() : + (ByteBuffer) data.getMipmapData()[0])); + image.write(file); + return true; + } + + throw new IOException("TGA writer doesn't support this pixel format / type (only GL_RGB/A + bytes)"); + } + + return false; + } + } + + //---------------------------------------------------------------------- + // Helper routines + // + + private static int glGetInteger(int pname) { + int[] tmp = new int[1]; + GL gl = GLU.getCurrentGL(); + gl.glGetIntegerv(pname, tmp, 0); + return tmp[0]; + } + + private static int glGetTexLevelParameteri(GL2 gl, int target, int level, int pname) { + int[] tmp = new int[1]; + gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0); + return tmp[0]; + } + +} diff --git a/src/classes/javax/media/opengl/GLContext.java b/src/classes/javax/media/opengl/GLContext.java index a70e131c8..02345ef04 100644 --- a/src/classes/javax/media/opengl/GLContext.java +++ b/src/classes/javax/media/opengl/GLContext.java @@ -187,4 +187,10 @@ public abstract class GLContext { * Sets the GL pipeline object for this GLContext. */ public abstract void setGL(GL gl); + + public final String toString() { + return "GLContext: "+getClass().getName()+ + "(GL: "+getGL().getClass().getName()+","+ + " Factory: "+ getGLDrawable().getFactory().getClass().getName()+")"; + } } diff --git a/src/classes/javax/media/opengl/GLDrawable.java b/src/classes/javax/media/opengl/GLDrawable.java index c7f0ef803..e99764af7 100644 --- a/src/classes/javax/media/opengl/GLDrawable.java +++ b/src/classes/javax/media/opengl/GLDrawable.java @@ -134,12 +134,21 @@ public interface GLDrawable { currently created or if its pixel format has not been set yet. On some platforms, the pixel format is not directly associated with the drawable; a best attempt is made to return a reasonable - value in this case. */ + value in this case. + Returns a copy of the passed object. + */ public GLCapabilities getChosenGLCapabilities(); - public NativeWindow getNativeWindow(); + /** + * stores a copy of the passed object + */ + public void setChosenGLCapabilities(GLCapabilities caps); - public String getProfile(); + public NativeWindow getNativeWindow(); public GLDrawableFactory getFactory(); + + public int lockSurface() throws GLException; + public void unlockSurface(); + public boolean isSurfaceLocked(); } diff --git a/src/classes/javax/media/opengl/GLDrawableFactory.java b/src/classes/javax/media/opengl/GLDrawableFactory.java index 2c48e3ceb..8177f2924 100644 --- a/src/classes/javax/media/opengl/GLDrawableFactory.java +++ b/src/classes/javax/media/opengl/GLDrawableFactory.java @@ -80,38 +80,30 @@ public abstract class GLDrawableFactory { private static GLDrawableFactory awtFactory; private static GLDrawableFactory nwFactory; - /** The desktop (OpenGL 2.0) profile */ - public static final String PROFILE_GL_20 = "GL20"; - - /** The OpenGL ES 1 (really, 1.1) profile */ - public static final String PROFILE_GLES1 = "GLES1"; - - /** The OpenGL ES 2 (really, 2.0) profile */ - public static final String PROFILE_GLES2 = "GLES2"; - - private String profile; - /** Initializes the sole GLDrawableFactory instance for the given profile. */ - public static void initializeAWTFactory(String profile) throws GLException { + private static void initializeAWTFactory() throws GLException { if (awtFactory != null) { return; } // See if the user is requesting one of the embedded profiles, // and if so, try to instantiate the EGLDrawableFactory - if (PROFILE_GLES1.equals(profile) || - PROFILE_GLES2.equals(profile)) { + if (GLProfile.isGLES()) { try { Class clazz = Class.forName("com.sun.opengl.impl.egl.awt.EGLDrawableFactory"); + /** Constructor c = clazz.getDeclaredConstructor(new Class[] { String.class }); - awtFactory = (GLDrawableFactory) c.newInstance(new Object[] { profile }); + awtFactory = (GLDrawableFactory) c.newInstance(new Object[] { GLProfile.getProfile() }); + */ + Constructor c = clazz.getDeclaredConstructor(new Class[0]); + awtFactory = (GLDrawableFactory) c.newInstance(null); return; } catch (Exception e) { e.printStackTrace(); } - } else if (!PROFILE_GL_20.equals(profile)) { + } else if (!GLProfile.isGL2()) { // We require that the user passes in one of the known profiles - throw new GLException("Unknown or unsupported profile \"" + profile + "\""); + throw new GLException("Unknown or unsupported profile \"" + GLProfile.getProfile() + "\""); } // Use the desktop OpenGL as the fallback always @@ -135,46 +127,53 @@ public abstract class GLDrawableFactory { if (factoryClassName != null) { factoryClass = Class.forName(factoryClassName); } else if (osNameLowerCase.startsWith("wind")) { - factoryClass = Class.forName("com.sun.opengl.impl.windows.awt.WindowsAWTGLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.windows.wgl.WindowsWGLDrawableFactory"); } else if (osNameLowerCase.startsWith("mac os x")) { - factoryClass = Class.forName("com.sun.opengl.impl.macosx.awt.MacOSXAWTGLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.macosx.cgl.awt.MacOSXAWTCGLDrawableFactory"); } else { // Assume Linux, Solaris, etc. Should probably test for these explicitly. - factoryClass = Class.forName("com.sun.opengl.impl.x11.awt.X11AWTGLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.x11.glx.awt.X11AWTGLXDrawableFactory"); } if (factoryClass == null) { throw new GLException("OS " + osName + " not yet supported"); } + /** Constructor c = factoryClass.getDeclaredConstructor(new Class[] { String.class }); - awtFactory = (GLDrawableFactory) c.newInstance(new Object[] { profile }); + awtFactory = (GLDrawableFactory) c.newInstance(new Object[] { GLProfile.getProfile() }); + */ + Constructor c = factoryClass.getDeclaredConstructor(new Class[0]); + awtFactory = (GLDrawableFactory) c.newInstance(null); } catch (Exception e) { throw new GLException(e); } } /** Initializes the sole GLDrawableFactory instance for the given profile. */ - public static void initializeNWFactory(String profile) throws GLException { + private static void initializeNWFactory() throws GLException { if (nwFactory != null) { return; } // See if the user is requesting one of the embedded profiles, // and if so, try to instantiate the EGLDrawableFactory - if (PROFILE_GLES1.equals(profile) || - PROFILE_GLES2.equals(profile)) { + if (GLProfile.isGLES()) { try { Class clazz = Class.forName("com.sun.opengl.impl.egl.EGLDrawableFactory"); + /** Constructor c = clazz.getDeclaredConstructor(new Class[] { String.class }); - nwFactory = (GLDrawableFactory) c.newInstance(new Object[] { profile }); + nwFactory = (GLDrawableFactory) c.newInstance(new Object[] { GLProfile.getProfile() }); + */ + Constructor c = clazz.getDeclaredConstructor(new Class[0]); + nwFactory = (GLDrawableFactory) c.newInstance(null); return; } catch (Exception e) { e.printStackTrace(); } - } else if (!PROFILE_GL_20.equals(profile)) { + } else if (!GLProfile.isGL2()) { // We require that the user passes in one of the known profiles - throw new GLException("Unknown or unsupported profile \"" + profile + "\""); + throw new GLException("Unknown or unsupported profile \"" + GLProfile.getProfile() + "\""); } // Use the desktop OpenGL as the fallback always @@ -192,20 +191,24 @@ public abstract class GLDrawableFactory { if (factoryClassName != null) { factoryClass = Class.forName(factoryClassName); } else if (osNameLowerCase.startsWith("wind")) { - factoryClass = Class.forName("com.sun.opengl.impl.windows.WindowsGLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.windows.wgl.WindowsWGLDrawableFactory"); } else if (osNameLowerCase.startsWith("mac os x")) { - factoryClass = Class.forName("com.sun.opengl.impl.macosx.MacOSXGLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.macosx.cgl.MacOSXCGLDrawableFactory"); } else { // Assume Linux, Solaris, etc. Should probably test for these explicitly. - factoryClass = Class.forName("com.sun.opengl.impl.x11.X11GLDrawableFactory"); + factoryClass = Class.forName("com.sun.opengl.impl.x11.glx.X11GLXDrawableFactory"); } if (factoryClass == null) { throw new GLException("OS " + osName + " not yet supported"); } + /** Constructor c = factoryClass.getDeclaredConstructor(new Class[] { String.class }); - nwFactory = (GLDrawableFactory) c.newInstance(new Object[] { profile }); + nwFactory = (GLDrawableFactory) c.newInstance(new Object[] { GLProfile.getProfile() }); + */ + Constructor c = factoryClass.getDeclaredConstructor(new Class[0]); + nwFactory = (GLDrawableFactory) c.newInstance(null); return; } catch (Exception e) { throw new GLException(e); @@ -214,34 +217,54 @@ public abstract class GLDrawableFactory { /** Creates a new GLDrawableFactory instance. End users do not need to call this method. */ - protected GLDrawableFactory(String profile) { - this.profile = profile; + protected GLDrawableFactory() { } /** Returns the sole GLDrawableFactory instance for the specified profile. */ - public static GLDrawableFactory getFactory(String profile, Object target) { + public static GLDrawableFactory getFactory(String profile, Object target) + throws GLException + { + GLProfile.setProfile(profile); + return getFactory(target); + } + + public static GLDrawableFactory getFactory(Object target) + throws GLException + { if(null==target) { throw new IllegalArgumentException("target is null"); } if(target instanceof NativeWindow) { - return getFactory(profile, false); + return getFactory(false); } else if (NativeWindowFactory.isAWTComponent(target)) { - return getFactory(profile, true); + return getFactory(true); } throw new IllegalArgumentException("Target type is unsupported. Currently supported: \n"+ "\tjavax.media.opengl.NativeWindow\n"+ "\tjava.awt.Component\n"); } - public static GLDrawableFactory getFactory(String profile, boolean awt) { + public static GLDrawableFactory getFactory(String profile, boolean awt) + throws GLException + { + GLProfile.setProfile(profile); + return getFactory(awt); + } + + public static GLDrawableFactory getFactory(boolean awt) + throws GLException + { + if(null==GLProfile.getProfile()) { + throw new GLException("No choosen/preset profile"); + } if(awt) { - initializeAWTFactory(profile); + initializeAWTFactory(); if(awtFactory == null) { throw new GLException("Could not determine the AWT-GLDrawableFactory"); } return awtFactory; } else { - initializeNWFactory(profile); + initializeNWFactory(); if(nwFactory == null) { throw new GLException("Could not determine the NativeWindow-GLDrawableFactory"); } @@ -249,11 +272,6 @@ public abstract class GLDrawableFactory { } } - /** Indicates which profile this GLDrawableFactory was created for. */ - public String getProfile() { - return profile; - } - /** Shuts down this GLDrawableFactory, releasing resources associated with it. Before calling this method you should first destroy any GLContexts and GLDrawables that have been created @@ -262,6 +280,25 @@ public abstract class GLDrawableFactory { public void shutdown() { } + public void lockToolkit() throws GLException { + if(lockedToolkit) { + throw new GLException("Toolkit already locked"); + } + lockedToolkit=true; + } + + public void unlockToolkit() { + if(lockedToolkit) { + lockedToolkit=false; + } + } + + public boolean isToolkitLocked() { + return lockedToolkit; + } + + protected static boolean lockedToolkit = false; + /** *

Selects a graphics configuration on the specified graphics * device compatible with the supplied GLCapabilities. This method @@ -411,4 +448,5 @@ public abstract class GLDrawableFactory { */ public abstract GLDrawable createExternalGLDrawable() throws GLException; + } diff --git a/src/classes/javax/media/opengl/NativeWindow.java b/src/classes/javax/media/opengl/NativeWindow.java index a93037bee..688c4c374 100644 --- a/src/classes/javax/media/opengl/NativeWindow.java +++ b/src/classes/javax/media/opengl/NativeWindow.java @@ -40,19 +40,40 @@ package javax.media.opengl; public interface NativeWindow { + public static final int LOCK_NOT_SUPPORTED = 0; public static final int LOCK_SURFACE_NOT_READY = 1; public static final int LOCK_SURFACE_CHANGED = 2; public static final int LOCK_SUCCESS = 3; - public boolean lockSurface() throws NativeWindowException ; + /** + * Lock this surface + */ + public int lockSurface() throws NativeWindowException ; + + /** + * Unlock this surface + */ public void unlockSurface(); public boolean isSurfaceLocked(); + /** + * render all native window information invalid, + * as if the native window was destroyed + */ + public void invalidate(); + + /** + * Lifetime: locked state + */ public long getDisplayHandle(); public long getScreenHandle(); - public int getScreenIndex(); public long getWindowHandle(); - public int getVisualID(); + + /** + * Lifetime: after 1st lock, until invalidation + */ + public long getVisualID(); + public int getScreenIndex(); public void setSize(int width, int height); public void setPosition(int x, int y); diff --git a/src/classes/javax/media/opengl/NativeWindowFactory.java b/src/classes/javax/media/opengl/NativeWindowFactory.java index c206adb78..cdd850322 100644 --- a/src/classes/javax/media/opengl/NativeWindowFactory.java +++ b/src/classes/javax/media/opengl/NativeWindowFactory.java @@ -95,14 +95,7 @@ public class NativeWindowFactory { * hence the independency to the java.awt.* package. */ public static boolean isAWTComponent(Object target) { - Class clazz = target.getClass(); - do { - if(clazz.getName().equals("java.awt.Component")) { - return true; - } - clazz = clazz.getSuperclass(); - } while (clazz!=null); - return false; + return GLProfile.instanceOf(target, "java.awt.Component"); } /** diff --git a/src/classes/javax/media/opengl/Threading.java b/src/classes/javax/media/opengl/Threading.java index f14912a3c..763389aca 100755 --- a/src/classes/javax/media/opengl/Threading.java +++ b/src/classes/javax/media/opengl/Threading.java @@ -313,7 +313,7 @@ public class Threading { /** This is a workaround for AWT-related deadlocks which only seem to show up in the context of applets */ - static boolean isAWTMode() { + public static boolean isAWTMode() { return (mode == AWT); } diff --git a/src/classes/javax/media/opengl/awt/GLCanvas.java b/src/classes/javax/media/opengl/awt/GLCanvas.java index 8b52c6da4..d002b58de 100644 --- a/src/classes/javax/media/opengl/awt/GLCanvas.java +++ b/src/classes/javax/media/opengl/awt/GLCanvas.java @@ -107,8 +107,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { which to create the GLCanvas; the GLDrawableFactory uses the default screen device of the local GraphicsEnvironment if null is passed for this argument. */ - public GLCanvas(String profile, - GLCapabilities capabilities, + public GLCanvas(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith, GraphicsDevice device) { @@ -142,8 +141,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { this.glCaps = capabilities; } if (!Beans.isDesignTime()) { - drawable = GLDrawableFactory.getFactory(profile, true).createGLDrawable(profile, - NativeWindowFactory.getNativeWindow(this), + drawable = GLDrawableFactory.getFactory(true).createGLDrawable(NativeWindowFactory.getNativeWindow(this), capabilities, chooser); context = (GLContextImpl) drawable.createContext(shareWith); context.setSynchronized(true); @@ -404,6 +402,34 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { return drawable.getChosenGLCapabilities(); } + public void setChosenGLCapabilities(GLCapabilities caps) { + drawable.setChosenGLCapabilities(caps); + } + + public NativeWindow getNativeWindow() { + return drawable.getNativeWindow(); + } + + public GLDrawableFactory getFactory() { + return drawable.getFactory(); + } + + public int lockSurface() throws GLException { + return drawable.lockSurface(); + } + + public void unlockSurface() { + drawable.unlockSurface(); + } + + public boolean isSurfaceLocked() { + return drawable.isSurfaceLocked(); + } + + public void destroy() { + drawable.destroy(); + } + //---------------------------------------------------------------------- // Internals only below this point // @@ -523,7 +549,7 @@ public class GLCanvas extends Canvas implements AWTGLAutoDrawable { } AWTGraphicsConfiguration config = (AWTGraphicsConfiguration) - GLDrawableFactory.getFactory().chooseGraphicsConfiguration(capabilities, + GLDrawableFactory.getFactory(true).chooseGraphicsConfiguration(capabilities, chooser, new AWTGraphicsDevice(device)); if (config == null) { diff --git a/src/classes/javax/media/opengl/awt/GLJPanel.java b/src/classes/javax/media/opengl/awt/GLJPanel.java deleted file mode 100644 index 4c1df74da..000000000 --- a/src/classes/javax/media/opengl/awt/GLJPanel.java +++ /dev/null @@ -1,1270 +0,0 @@ -/* - * Copyright (c) 2003 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 javax.media.opengl.awt; - -import javax.media.opengl.*; - -import java.awt.*; -import java.awt.geom.*; -import java.awt.image.*; -import java.beans.*; -import javax.swing.*; -import java.nio.*; -import java.security.*; -import javax.swing.JComponent; -import javax.swing.JPanel; -import com.sun.opengl.impl.*; - -// FIXME: Subclasses need to call resetGLFunctionAvailability() on their -// context whenever the displayChanged() function is called on their -// GLEventListeners - -/** A lightweight Swing component which provides OpenGL rendering - support. Provided for compatibility with Swing user interfaces - when adding a heavyweight doesn't work either because of - Z-ordering or LayoutManager problems.

- - The GLJPanel can be made transparent by creating it with a - GLCapabilities object with alpha bits specified and calling {@link - #setOpaque}(false). Pixels with resulting OpenGL alpha values less - than 1.0 will be overlaid on any underlying Swing rendering.

- - Notes specific to the Reference Implementation: This component - attempts to use hardware-accelerated rendering via pbuffers and - falls back on to software rendering if problems occur. - Note that because this component attempts to use pbuffers for - rendering, and because pbuffers can not be resized, somewhat - surprising behavior may occur during resize operations; the {@link - GLEventListener#init} method may be called multiple times as the - pbuffer is resized to be able to cover the size of the GLJPanel. - This behavior is correct, as the textures and display lists for - the GLJPanel will have been lost during the resize operation. The - application should attempt to make its GLEventListener.init() - methods as side-effect-free as possible.

- -*/ - -public class GLJPanel extends JPanel implements AWTGLAutoDrawable { - private static final boolean DEBUG = Debug.debug("GLJPanel"); - private static final boolean VERBOSE = Debug.verbose(); - - private GLDrawableHelper drawableHelper = new GLDrawableHelper(); - private volatile boolean isInitialized; - private volatile boolean shouldInitialize = false; - - // Data used for either pbuffers or pixmap-based offscreen surfaces - private GLCapabilities offscreenCaps; - private GLCapabilitiesChooser chooser; - private GLContext shareWith; - // This image is exactly the correct size to render into the panel - private BufferedImage offscreenImage; - // One of these is used to store the read back pixels before storing - // in the BufferedImage - private ByteBuffer readBackBytes; - private IntBuffer readBackInts; - private int readBackWidthInPixels; - private int readBackHeightInPixels; - // Width of the actual GLJPanel - private int panelWidth = 0; - private int panelHeight = 0; - private Updater updater; - private int awtFormat; - private int glFormat; - private int glType; - // Lazy reshape notification - private boolean handleReshape = false; - private boolean sendReshape = true; - - // Implementation using pbuffers - private static boolean hardwareAccelerationDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nohw"); - private static boolean softwareRenderingDisabled = - Debug.isPropertyDefined("jogl.gljpanel.nosw"); - private GLPbuffer pbuffer; - private int pbufferWidth = 256; - private int pbufferHeight = 256; - - // Implementation using software rendering - private GLDrawableImpl offscreenDrawable; - private GLContextImpl offscreenContext; - - // For handling reshape events lazily - private int reshapeX; - private int reshapeY; - private int reshapeWidth; - private int reshapeHeight; - - // For saving/restoring of OpenGL state during ReadPixels - private int[] swapbytes = new int[1]; - private int[] rowlength = new int[1]; - private int[] skiprows = new int[1]; - private int[] skippixels = new int[1]; - private int[] alignment = new int[1]; - - // Implementation using Java2D OpenGL pipeline's back buffer - private boolean oglPipelineEnabled = - Java2D.isOGLPipelineActive() && - !Debug.isPropertyDefined("jogl.gljpanel.noogl"); - // Opaque Object identifier representing the Java2D surface we are - // drawing to; used to determine when to destroy and recreate JOGL - // context - private Object j2dSurface; - // Graphics object being used during Java2D update action - // (absolutely essential to cache this) - private Graphics cached2DGraphics; - // No-op context representing the Java2D OpenGL context - private GLContext j2dContext; - // Context associated with no-op drawable representing the JOGL - // OpenGL context - private GLDrawable joglDrawable; - // The real OpenGL context JOGL uses to render - private GLContext joglContext; - // State captured from Java2D OpenGL context necessary in order to - // properly render into Java2D back buffer - private int[] drawBuffer = new int[1]; - private int[] readBuffer = new int[1]; - // This is required when the FBO option of the Java2D / OpenGL - // pipeline is active - private int[] frameBuffer = new int[1]; - // Current (as of this writing) NVidia drivers have a couple of bugs - // relating to the sharing of framebuffer and renderbuffer objects - // between contexts. It appears we have to (a) reattach the color - // attachment and (b) actually create new depth buffer storage and - // attach it in order for the FBO to behave properly in our context. - private boolean checkedForFBObjectWorkarounds; - private boolean fbObjectWorkarounds; - private int[] frameBufferDepthBuffer; - private int[] frameBufferTexture; - private boolean createNewDepthBuffer; - // Current (as of this writing) ATI drivers have problems when the - // same FBO is bound in two different contexts. Here we check for - // this case and explicitly release the FBO from Java2D's context - // before switching to ours. Java2D will re-bind the FBO when it - // makes its context current the next time. Interestingly, if we run - // this code path on NVidia hardware, it breaks the rendering - // results -- no output is generated. This doesn't appear to be an - // interaction with the abovementioned NVidia-specific workarounds, - // as even if we disable that code the FBO is still reported as - // incomplete in our context. - private boolean checkedGLVendor; - private boolean vendorIsATI; - - // Holding on to this GraphicsConfiguration is a workaround for a - // problem in the Java 2D / JOGL bridge when FBOs are enabled; see - // comment related to Issue 274 below - private GraphicsConfiguration workaroundConfig; - - // These are always set to (0, 0) except when the Java2D / OpenGL - // pipeline is active - private int viewportX; - private int viewportY; - - static { - // Force eager initialization of part of the Java2D class since - // otherwise it's likely it will try to be initialized while on - // the Queue Flusher Thread, which is not allowed - if (Java2D.isOGLPipelineActive() && Java2D.isFBOEnabled()) { - Java2D.getShareContext(GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice(). - getDefaultConfiguration()); - } - } - - /** Creates a new GLJPanel component with a default set of OpenGL - capabilities and using the default OpenGL capabilities selection - mechanism. */ - public GLJPanel() { - this(null); - } - - /** Creates a new GLJPanel component with the requested set of - OpenGL capabilities, using the default OpenGL capabilities - selection mechanism. */ - public GLJPanel(GLCapabilities capabilities) { - this(capabilities, null, null); - } - - /** Creates a new GLJPanel component. The passed GLCapabilities - specifies the OpenGL capabilities for the component; if null, a - default set of capabilities is used. The GLCapabilitiesChooser - specifies the algorithm for selecting one of the available - GLCapabilities for the component; a DefaultGLCapabilitesChooser - is used if null is passed for this argument. The passed - GLContext specifies an OpenGL context with which to share - textures, display lists and other OpenGL state, and may be null - if sharing is not desired. See the note in the overview documentation on - context sharing. - */ - public GLJPanel(GLCapabilities capabilities, GLCapabilitiesChooser chooser, GLContext shareWith) { - super(); - - // Works around problems on many vendors' cards; we don't need a - // back buffer for the offscreen surface anyway - if (capabilities != null) { - offscreenCaps = (GLCapabilities) capabilities.clone(); - } else { - offscreenCaps = new GLCapabilities(); - } - offscreenCaps.setDoubleBuffered(false); - this.chooser = ((chooser != null) ? chooser : new DefaultGLCapabilitiesChooser()); - this.shareWith = shareWith; - } - - public void display() { - if (EventQueue.isDispatchThread()) { - // Want display() to be synchronous, so call paintImmediately() - paintImmediately(0, 0, getWidth(), getHeight()); - } else { - // Multithreaded redrawing of Swing components is not allowed, - // so do everything on the event dispatch thread - try { - EventQueue.invokeAndWait(paintImmediatelyAction); - } catch (Exception e) { - throw new GLException(e); - } - } - } - - private void captureJ2DState(GL gl, Graphics g) { - gl.glGetIntegerv(GL.GL_DRAW_BUFFER, drawBuffer, 0); - gl.glGetIntegerv(GL.GL_READ_BUFFER, readBuffer, 0); - if (Java2D.isFBOEnabled() && - Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: Fetching GL_FRAMEBUFFER_BINDING_EXT"); - } - gl.glGetIntegerv(GL.GL_FRAMEBUFFER_BINDING_EXT, frameBuffer, 0); - - if (fbObjectWorkarounds || - !checkedForFBObjectWorkarounds) { - // See above for description of what we are doing here - if (frameBufferTexture == null) - frameBufferTexture = new int[1]; - - // Query the framebuffer for its color buffer so we can hook - // it back up in our context (should not be necessary) - gl.glGetFramebufferAttachmentParameterivEXT(GL.GL_FRAMEBUFFER_EXT, - GL.GL_COLOR_ATTACHMENT0_EXT, - GL.GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT, - frameBufferTexture, 0); - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: FBO COLOR_ATTACHMENT0: " + frameBufferTexture[0]); - } - } - - if (!checkedGLVendor) { - checkedGLVendor = true; - String vendor = gl.glGetString(GL.GL_VENDOR); - - if ((vendor != null) && - vendor.startsWith("ATI")) { - vendorIsATI = true; - } - } - - if (vendorIsATI) { - // Unbind the FBO from Java2D's context as it appears that - // driver bugs on ATI's side are causing problems if the FBO is - // simultaneously bound to more than one context. Java2D will - // re-bind the FBO during the next validation of its context. - // Note: this breaks rendering at least on NVidia hardware - gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0); - } - } - } - - private boolean preGL(Graphics g) { - GL gl = joglContext.getGL(); - // Set up needed state in JOGL context from Java2D context - gl.glEnable(GL.GL_SCISSOR_TEST); - Rectangle r = Java2D.getOGLScissorBox(g); - - if (r == null) { - if (DEBUG && VERBOSE) { - System.err.println("Java2D.getOGLScissorBox() returned null"); - } - return false; - } - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: gl.glScissor(" + r.x + ", " + r.y + ", " + r.width + ", " + r.height + ")"); - } - - gl.glScissor(r.x, r.y, r.width, r.height); - Rectangle oglViewport = Java2D.getOGLViewport(g, panelWidth, panelHeight); - // If the viewport X or Y changes, in addition to the panel's - // width or height, we need to send a reshape operation to the - // client - if ((viewportX != oglViewport.x) || - (viewportY != oglViewport.y)) { - sendReshape = true; - if (DEBUG) { - System.err.println("Sending reshape because viewport changed"); - System.err.println(" viewportX (" + viewportX + ") ?= oglViewport.x (" + oglViewport.x + ")"); - System.err.println(" viewportY (" + viewportY + ") ?= oglViewport.y (" + oglViewport.y + ")"); - } - } - viewportX = oglViewport.x; - viewportY = oglViewport.y; - - // If the FBO option is active, bind to the FBO from the Java2D - // context. - // Note that all of the plumbing in the context sharing stuff will - // allow us to bind to this object since it's in our namespace. - if (Java2D.isFBOEnabled() && - Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: Binding to framebuffer object " + frameBuffer[0]); - } - - // The texture target for Java2D's OpenGL pipeline when using FBOs - // -- either GL_TEXTURE_2D or GL_TEXTURE_RECTANGLE_ARB - int fboTextureTarget = Java2D.getOGLTextureType(g); - - if (!checkedForFBObjectWorkarounds) { - checkedForFBObjectWorkarounds = true; - gl.glBindTexture(fboTextureTarget, 0); - gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, frameBuffer[0]); - if (gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT) != - GL.GL_FRAMEBUFFER_COMPLETE_EXT) { - // Need to do workarounds - fbObjectWorkarounds = true; - createNewDepthBuffer = true; - if (DEBUG) { - System.err.println("-- GLJPanel: discovered frame_buffer_object workarounds to be necessary"); - } - } else { - // Don't need the frameBufferTexture temporary any more - frameBufferTexture = null; - } - } - - if (fbObjectWorkarounds && createNewDepthBuffer) { - if (frameBufferDepthBuffer == null) - frameBufferDepthBuffer = new int[1]; - - // Create our own depth renderbuffer and associated storage - // If we have an old one, delete it - if (frameBufferDepthBuffer[0] != 0) { - gl.glDeleteRenderbuffersEXT(1, frameBufferDepthBuffer, 0); - frameBufferDepthBuffer[0] = 0; - } - - gl.glBindTexture(fboTextureTarget, frameBufferTexture[0]); - int[] width = new int[1]; - int[] height = new int[1]; - gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL.GL_TEXTURE_WIDTH, width, 0); - gl.glGetTexLevelParameteriv(fboTextureTarget, 0, GL.GL_TEXTURE_HEIGHT, height, 0); - - gl.glGenRenderbuffersEXT(1, frameBufferDepthBuffer, 0); - if (DEBUG) { - System.err.println("GLJPanel: Generated frameBufferDepthBuffer " + frameBufferDepthBuffer[0] + - " with width " + width[0] + ", height " + height[0]); - } - - gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, frameBufferDepthBuffer[0]); - // FIXME: may need a loop here like in Java2D - gl.glRenderbufferStorageEXT(GL.GL_RENDERBUFFER_EXT, GL.GL_DEPTH_COMPONENT24, width[0], height[0]); - - gl.glBindRenderbufferEXT(GL.GL_RENDERBUFFER_EXT, 0); - createNewDepthBuffer = false; - } - - gl.glBindTexture(fboTextureTarget, 0); - gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, frameBuffer[0]); - - if (fbObjectWorkarounds) { - // Hook up the color and depth buffer attachment points for this framebuffer - gl.glFramebufferTexture2DEXT(GL.GL_FRAMEBUFFER_EXT, - GL.GL_COLOR_ATTACHMENT0_EXT, - fboTextureTarget, - frameBufferTexture[0], - 0); - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: frameBufferDepthBuffer: " + frameBufferDepthBuffer[0]); - } - gl.glFramebufferRenderbufferEXT(GL.GL_FRAMEBUFFER_EXT, - GL.GL_DEPTH_ATTACHMENT_EXT, - GL.GL_RENDERBUFFER_EXT, - frameBufferDepthBuffer[0]); - } - - if (DEBUG) { - int status = gl.glCheckFramebufferStatusEXT(GL.GL_FRAMEBUFFER_EXT); - if (status != GL.GL_FRAMEBUFFER_COMPLETE_EXT) { - throw new GLException("Error: framebuffer was incomplete: status = 0x" + - Integer.toHexString(status)); - } - } - } else { - if (DEBUG && VERBOSE) { - System.err.println("GLJPanel: Setting up drawBuffer " + drawBuffer[0] + - " and readBuffer " + readBuffer[0]); - } - - gl.glDrawBuffer(drawBuffer[0]); - gl.glReadBuffer(readBuffer[0]); - } - - return true; - } - - private void postGL(Graphics g) { - if (Java2D.isFBOEnabled() && - Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT) { - // Unbind the framebuffer from our context to work around - // apparent driver bugs or at least unspecified behavior causing - // OpenGL to run out of memory with certain cards and drivers - GL gl = joglContext.getGL(); - gl.glBindFramebufferEXT(GL.GL_FRAMEBUFFER_EXT, 0); - } - } - - /** Overridden to cause OpenGL rendering to be performed during - repaint cycles. Subclasses which override this method must call - super.paintComponent() in their paintComponent() method in order - to function properly.

- - Overrides: -

paintComponent in class javax.swing.JComponent
*/ - protected void paintComponent(final Graphics g) { - if (Beans.isDesignTime()) { - // Make GLJPanel behave better in NetBeans GUI builder - g.setColor(Color.BLACK); - g.fillRect(0, 0, getWidth(), getHeight()); - FontMetrics fm = g.getFontMetrics(); - String name = getName(); - if (name == null) { - name = getClass().getName(); - int idx = name.lastIndexOf('.'); - if (idx >= 0) { - name = name.substring(idx + 1); - } - } - Rectangle2D bounds = fm.getStringBounds(name, g); - g.setColor(Color.WHITE); - g.drawString(name, - (int) ((getWidth() - bounds.getWidth()) / 2), - (int) ((getHeight() + bounds.getHeight()) / 2)); - return; - } - - if (shouldInitialize) { - initialize(); - } - - if (!isInitialized) { - return; - } - - // NOTE: must do this when the context is not current as it may - // involve destroying the pbuffer (current context) and - // re-creating it -- tricky to do properly while the context is - // current - if (handleReshape) { - handleReshape(); - } - - updater.setGraphics(g); - - if (oglPipelineEnabled) { - - // This is a workaround for an issue in the Java 2D / JOGL - // bridge (reported by an end user as JOGL Issue 274) where Java - // 2D can occasionally leave its internal OpenGL context current - // to the on-screen window rather than its internal "scratch" - // pbuffer surface to which the FBO is attached. JOGL expects to - // find a stable OpenGL drawable (on Windows, an HDC) upon which - // it can create another OpenGL context. It turns out that, on - // Windows, when Java 2D makes its internal OpenGL context - // current against the window in order to put pixels on the - // screen, it gets the device context for the window, makes its - // context current, and releases the device context. This means - // that when JOGL's Runnable gets to run below, the HDC is - // already invalid. The workaround for this is to force Java 2D - // to make its context current to the scratch surface, which we - // can do by executing an empty Runnable with the "shared" - // context current. This will be fixed in a Java SE 6 update - // release, hopefully 6u2. - if (Java2D.isFBOEnabled()) { - if (workaroundConfig == null) { - workaroundConfig = GraphicsEnvironment. - getLocalGraphicsEnvironment(). - getDefaultScreenDevice(). - getDefaultConfiguration(); - } - Java2D.invokeWithOGLSharedContextCurrent(workaroundConfig, new Runnable() { public void run() {}}); - } - - Java2D.invokeWithOGLContextCurrent(g, new Runnable() { - public void run() { - if (DEBUG && VERBOSE) { - System.err.println("-- In invokeWithOGLContextCurrent"); - } - - // Create no-op context representing Java2D context - if (j2dContext == null) { - j2dContext = GLDrawableFactory.getFactory().createExternalGLContext(); - if (DEBUG) { - j2dContext.setGL(new DebugGL(j2dContext.getGL())); - } - - // Check to see whether we can support the requested - // capabilities or need to fall back to a pbuffer - // FIXME: add more checks? - - j2dContext.makeCurrent(); - GL gl = j2dContext.getGL(); - if ((getGLInteger(gl, GL.GL_RED_BITS) < offscreenCaps.getRedBits()) || - (getGLInteger(gl, GL.GL_GREEN_BITS) < offscreenCaps.getGreenBits()) || - (getGLInteger(gl, GL.GL_BLUE_BITS) < offscreenCaps.getBlueBits()) || - // (getGLInteger(gl, GL.GL_ALPHA_BITS) < offscreenCaps.getAlphaBits()) || - (getGLInteger(gl, GL.GL_ACCUM_RED_BITS) < offscreenCaps.getAccumRedBits()) || - (getGLInteger(gl, GL.GL_ACCUM_GREEN_BITS) < offscreenCaps.getAccumGreenBits()) || - (getGLInteger(gl, GL.GL_ACCUM_BLUE_BITS) < offscreenCaps.getAccumBlueBits()) || - (getGLInteger(gl, GL.GL_ACCUM_ALPHA_BITS) < offscreenCaps.getAccumAlphaBits()) || - // (getGLInteger(gl, GL.GL_DEPTH_BITS) < offscreenCaps.getDepthBits()) || - (getGLInteger(gl, GL.GL_STENCIL_BITS) < offscreenCaps.getStencilBits())) { - if (DEBUG) { - System.err.println("GLJPanel: Falling back to pbuffer-based support because Java2D context insufficient"); - System.err.println(" Available Required"); - System.err.println("GL_RED_BITS " + getGLInteger(gl, GL.GL_RED_BITS) + " " + offscreenCaps.getRedBits()); - System.err.println("GL_GREEN_BITS " + getGLInteger(gl, GL.GL_GREEN_BITS) + " " + offscreenCaps.getGreenBits()); - System.err.println("GL_BLUE_BITS " + getGLInteger(gl, GL.GL_BLUE_BITS) + " " + offscreenCaps.getBlueBits()); - System.err.println("GL_ALPHA_BITS " + getGLInteger(gl, GL.GL_ALPHA_BITS) + " " + offscreenCaps.getAlphaBits()); - System.err.println("GL_ACCUM_RED_BITS " + getGLInteger(gl, GL.GL_ACCUM_RED_BITS) + " " + offscreenCaps.getAccumRedBits()); - System.err.println("GL_ACCUM_GREEN_BITS " + getGLInteger(gl, GL.GL_ACCUM_GREEN_BITS) + " " + offscreenCaps.getAccumGreenBits()); - System.err.println("GL_ACCUM_BLUE_BITS " + getGLInteger(gl, GL.GL_ACCUM_BLUE_BITS) + " " + offscreenCaps.getAccumBlueBits()); - System.err.println("GL_ACCUM_ALPHA_BITS " + getGLInteger(gl, GL.GL_ACCUM_ALPHA_BITS) + " " + offscreenCaps.getAccumAlphaBits()); - System.err.println("GL_DEPTH_BITS " + getGLInteger(gl, GL.GL_DEPTH_BITS) + " " + offscreenCaps.getDepthBits()); - System.err.println("GL_STENCIL_BITS " + getGLInteger(gl, GL.GL_STENCIL_BITS) + " " + offscreenCaps.getStencilBits()); - } - isInitialized = false; - shouldInitialize = true; - oglPipelineEnabled = false; - handleReshape = true; - j2dContext.release(); - j2dContext.destroy(); - j2dContext = null; - return; - } - j2dContext.release(); - } - - j2dContext.makeCurrent(); - try { - captureJ2DState(j2dContext.getGL(), g); - Object curSurface = Java2D.getOGLSurfaceIdentifier(g); - if (curSurface != null) { - if (j2dSurface != curSurface) { - if (joglContext != null) { - joglContext.destroy(); - joglContext = null; - joglDrawable = null; - sendReshape = true; - if (DEBUG) { - System.err.println("Sending reshape because surface changed"); - System.err.println("New surface = " + curSurface); - } - } - j2dSurface = curSurface; - } - if (joglContext == null) { - if (GLDrawableFactory.getFactory().canCreateExternalGLDrawable()) { - joglDrawable = GLDrawableFactory.getFactory().createExternalGLDrawable(); - joglContext = joglDrawable.createContext(shareWith); - } else if (GLDrawableFactoryImpl.getFactoryImpl().canCreateContextOnJava2DSurface()) { - // Mac OS X code path - joglContext = GLDrawableFactoryImpl.getFactoryImpl().createContextOnJava2DSurface(g, shareWith); - } - if (DEBUG) { - joglContext.setGL(new DebugGL(joglContext.getGL())); - } - - if (Java2D.isFBOEnabled() && - Java2D.getOGLSurfaceType(g) == Java2D.FBOBJECT && - fbObjectWorkarounds) { - createNewDepthBuffer = true; - } - } - if (joglContext instanceof Java2DGLContext) { - // Mac OS X code path - ((Java2DGLContext) joglContext).setGraphics(g); - } - - if (DEBUG && VERBOSE && Java2D.isFBOEnabled()) { - System.err.print("-- Surface type: "); - int surfaceType = Java2D.getOGLSurfaceType(g); - if (surfaceType == Java2D.UNDEFINED) { - System.err.println("UNDEFINED"); - } else if (surfaceType == Java2D.WINDOW) { - System.err.println("WINDOW"); - } else if (surfaceType == Java2D.PBUFFER) { - System.err.println("PBUFFER"); - } else if (surfaceType == Java2D.TEXTURE) { - System.err.println("TEXTURE"); - } else if (surfaceType == Java2D.FLIP_BACKBUFFER) { - System.err.println("FLIP_BACKBUFFER"); - } else if (surfaceType == Java2D.FBOBJECT) { - System.err.println("FBOBJECT"); - } else { - System.err.println("(Unknown surface type " + surfaceType + ")"); - } - } - - drawableHelper.invokeGL(joglDrawable, joglContext, displayAction, initAction); - } - } finally { - j2dContext.release(); - } - } - }); - } else { - if (!hardwareAccelerationDisabled) { - pbuffer.display(); - } else { - drawableHelper.invokeGL(offscreenDrawable, offscreenContext, displayAction, initAction); - } - - if (offscreenImage != null) { - // Draw resulting image in one shot - g.drawImage(offscreenImage, 0, 0, offscreenImage.getWidth(), offscreenImage.getHeight(), this); - } - } - } - - /** Overridden to track when this component is added to a container. - Subclasses which override this method must call - super.addNotify() in their addNotify() method in order to - function properly.

- - Overrides: -

addNotify in class java.awt.Component
*/ - public void addNotify() { - super.addNotify(); - shouldInitialize = true; - if (DEBUG) { - System.err.println("GLJPanel.addNotify()"); - } - } - - /** Overridden to track when this component is removed from a - container. Subclasses which override this method must call - super.removeNotify() in their removeNotify() method in order to - function properly.

- - Overrides: -

removeNotify in class java.awt.Component
*/ - public void removeNotify() { - if (DEBUG) { - System.err.println("GLJPanel.removeNotify()"); - } - if (oglPipelineEnabled) { - Java2D.invokeWithOGLContextCurrent(null, new Runnable() { - public void run() { - if (joglContext != null) { - joglContext.destroy(); - joglContext = null; - } - joglDrawable = null; - if (j2dContext != null) { - j2dContext.destroy(); - j2dContext = null; - } - } - }); - } else { - if (!hardwareAccelerationDisabled) { - if (pbuffer != null) { - pbuffer.destroy(); - pbuffer = null; - } - } else { - if (offscreenContext != null) { - offscreenContext.destroy(); - offscreenContext = null; - } - if (offscreenDrawable != null) { - offscreenDrawable.destroy(); - offscreenDrawable = null; - } - } - } - isInitialized = false; - super.removeNotify(); - } - - /** Overridden to cause {@link GLDrawableHelper#reshape} to be - called on all registered {@link GLEventListener}s. Subclasses - which override this method must call super.reshape() in - their reshape() method in order to function properly.

- - Overrides: -

reshape in class java.awt.Component
*/ - public void reshape(int x, int y, int width, int height) { - super.reshape(x, y, width, height); - - reshapeX = x; - reshapeY = y; - reshapeWidth = width; - reshapeHeight = height; - handleReshape = true; - } - - public void setOpaque(boolean opaque) { - if (opaque != isOpaque()) { - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } - super.setOpaque(opaque); - } - - public void addGLEventListener(GLEventListener listener) { - drawableHelper.addGLEventListener(listener); - } - - public void removeGLEventListener(GLEventListener listener) { - drawableHelper.removeGLEventListener(listener); - } - - public GLContext createContext(GLContext shareWith) { - if (!hardwareAccelerationDisabled) { - return pbuffer.createContext(shareWith); - } else { - return offscreenDrawable.createContext(shareWith); - } - } - - public void setRealized(boolean realized) { - } - - public GLContext getContext() { - if (oglPipelineEnabled) { - return joglContext; - } else { - if (!hardwareAccelerationDisabled) { - // Workaround for crashes in NetBeans GUI builder - if (pbuffer == null && Beans.isDesignTime()) { - return null; - } - return pbuffer.getContext(); - } else { - return offscreenContext; - } - } - } - - public GL getGL() { - GLContext context = getContext(); - return (context == null) ? null : context.getGL(); - } - - public void setGL(GL gl) { - GLContext context = getContext(); - if (context != null) { - context.setGL(gl); - } - } - - public void setAutoSwapBufferMode(boolean onOrOff) { - if (!hardwareAccelerationDisabled) { - // Workaround for crashes in NetBeans GUI builder - if (pbuffer == null && Beans.isDesignTime()) { - return; - } - pbuffer.setAutoSwapBufferMode(onOrOff); - } else { - drawableHelper.setAutoSwapBufferMode(onOrOff); - } - } - - public boolean getAutoSwapBufferMode() { - if (!hardwareAccelerationDisabled && !oglPipelineEnabled) { - return pbuffer.getAutoSwapBufferMode(); - } else { - return drawableHelper.getAutoSwapBufferMode(); - } - } - - public void swapBuffers() { - // In the current implementation this is basically a no-op. Both - // the pbuffer and pixmap based rendering paths use a single- - // buffered surface so swapping the buffers doesn't do anything. - // We also don't currently have the provision to skip copying the - // data to the Swing portion of the GLJPanel in any of the - // rendering paths. - if (oglPipelineEnabled) { - // Do nothing - } else if (!hardwareAccelerationDisabled) { - pbuffer.swapBuffers(); - } else { - drawableHelper.invokeGL(offscreenDrawable, offscreenContext, swapBuffersAction, initAction); - } - } - - /** For a translucent GLJPanel (one for which {@link #setOpaque - setOpaque}(false) has been called), indicates whether the - application should preserve the OpenGL color buffer - (GL_COLOR_BUFFER_BIT) for correct rendering of the GLJPanel and - underlying widgets which may show through portions of the - GLJPanel with alpha values less than 1. Most Swing - implementations currently expect the GLJPanel to be completely - cleared (e.g., by glClear(GL_COLOR_BUFFER_BIT | - GL_DEPTH_BUFFER_BIT)), but for certain optimized Swing - implementations which use OpenGL internally, it may be possible - to perform OpenGL rendering using the GLJPanel into the same - OpenGL drawable as the Swing implementation uses. */ - public boolean shouldPreserveColorBufferIfTranslucent() { - return oglPipelineEnabled; - } - - public GLCapabilities getChosenGLCapabilities() { - if (oglPipelineEnabled) { - // FIXME: should do better than this; is it possible to using only platform-independent code? - return new GLCapabilities(); - } - - if (hardwareAccelerationDisabled) { - if (offscreenDrawable != null) - return offscreenDrawable.getChosenGLCapabilities(); - } else { - if (pbuffer != null) - return pbuffer.getChosenGLCapabilities(); - } - - return null; - } - - //---------------------------------------------------------------------- - // Internals only below this point - // - - private void initialize() { - if (panelWidth == 0 || - panelHeight == 0) { - // See whether we have a non-zero size yet and can go ahead with - // initialization - if (reshapeWidth == 0 || - reshapeHeight == 0) { - return; - } - - // Pull down reshapeWidth and reshapeHeight into panelWidth and - // panelHeight eagerly in order to complete initialization, and - // force a reshape later - panelWidth = reshapeWidth; - panelHeight = reshapeHeight; - } - - if (!oglPipelineEnabled) { - // Initialize either the hardware-accelerated rendering path or - // the lightweight rendering path - if (!hardwareAccelerationDisabled) { - if (GLDrawableFactory.getFactory().canCreateGLPbuffer()) { - if (pbuffer != null) { - throw new InternalError("Creating pbuffer twice without destroying it (memory leak / correctness bug)"); - } - try { - pbuffer = GLDrawableFactory.getFactory().createGLPbuffer(offscreenCaps, - null, - pbufferWidth, - pbufferHeight, - shareWith); - updater = new Updater(); - pbuffer.addGLEventListener(updater); - shouldInitialize = false; - isInitialized = true; - return; - } catch (GLException e) { - if (DEBUG) { - e.printStackTrace(); - System.err.println("GLJPanel: Falling back on software rendering because of problems creating pbuffer"); - } - hardwareAccelerationDisabled = true; - } - } else { - if (DEBUG) { - System.err.println("GLJPanel: Falling back on software rendering because no pbuffer support"); - } - - // If the factory reports that it can't create a pbuffer, - // don't try again the next time, and fall through to the - // software rendering path - hardwareAccelerationDisabled = true; - } - } - - if (softwareRenderingDisabled) { - throw new GLException("Fallback to software rendering disabled by user"); - } - - // Fall-through path: create an offscreen context instead - offscreenDrawable = GLDrawableFactoryImpl.getFactoryImpl().createOffscreenDrawable(offscreenCaps, chooser); - offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight)); - offscreenContext = (GLContextImpl) offscreenDrawable.createContext(shareWith); - offscreenContext.setSynchronized(true); - } - updater = new Updater(); - shouldInitialize = false; - isInitialized = true; - } - - private void handleReshape() { - readBackWidthInPixels = 0; - readBackHeightInPixels = 0; - - panelWidth = reshapeWidth; - panelHeight = reshapeHeight; - - if (DEBUG) { - System.err.println("GLJPanel.handleReshape: (w,h) = (" + - panelWidth + "," + panelHeight + ")"); - } - - sendReshape = true; - - if (!oglPipelineEnabled) { - if (!hardwareAccelerationDisabled) { - // Use factor larger than 2 during shrinks for some hysteresis - float shrinkFactor = 2.5f; - if ((panelWidth > pbufferWidth ) || (panelHeight > pbufferHeight) || - (panelWidth < (pbufferWidth / shrinkFactor)) || (panelHeight < (pbufferHeight / shrinkFactor))) { - if (DEBUG) { - System.err.println("Resizing pbuffer from (" + pbufferWidth + ", " + pbufferHeight + ") " + - " to fit (" + panelWidth + ", " + panelHeight + ")"); - } - // Must destroy and recreate pbuffer to fit - if (pbuffer != null) { - // Watch for errors during pbuffer destruction (due to - // buggy / bad OpenGL drivers, in particular SiS) and fall - // back to software rendering - try { - pbuffer.destroy(); - } catch (GLException e) { - hardwareAccelerationDisabled = true; - if (DEBUG) { - System.err.println("WARNING: falling back to software rendering due to bugs in OpenGL drivers"); - e.printStackTrace(); - } - } - } - pbuffer = null; - isInitialized = false; - pbufferWidth = getNextPowerOf2(panelWidth); - pbufferHeight = getNextPowerOf2(panelHeight); - if (DEBUG && !hardwareAccelerationDisabled) { - System.err.println("New pbuffer size is (" + pbufferWidth + ", " + pbufferHeight + ")"); - } - initialize(); - } - - // It looks like NVidia's drivers (at least the ones on my - // notebook) are buggy and don't allow a rectangle of less than - // the pbuffer's width to be read...this doesn't really matter - // because it's the Graphics.drawImage() calls that are the - // bottleneck. Should probably make the size of the offscreen - // image be the exact size of the pbuffer to save some work on - // resize operations... - if (!hardwareAccelerationDisabled) { - readBackWidthInPixels = pbufferWidth; - readBackHeightInPixels = panelHeight; - } else { - // Just disabled hardware acceleration during this resize operation; do a fixup - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); - } - } else { - offscreenContext.destroy(); - offscreenDrawable.setSize(Math.max(1, panelWidth), Math.max(1, panelHeight)); - readBackWidthInPixels = Math.max(1, panelWidth); - readBackHeightInPixels = Math.max(1, panelHeight); - } - - if (offscreenImage != null) { - offscreenImage.flush(); - offscreenImage = null; - } - } - - handleReshape = false; - } - - // FIXME: it isn't clear whether this works any more given that - // we're accessing the GLDrawable inside of the GLPbuffer directly - // up in reshape() -- need to rethink and clean this up - class Updater implements GLEventListener { - private Graphics g; - - public void setGraphics(Graphics g) { - this.g = g; - } - - public void init(GLAutoDrawable drawable) { - if (oglPipelineEnabled) { - if (!preGL(g)) { - return; - } - } - drawableHelper.init(GLJPanel.this); - if (oglPipelineEnabled) { - postGL(g); - } - } - - public void display(GLAutoDrawable drawable) { - if (oglPipelineEnabled) { - if (!preGL(g)) { - return; - } - } - - if (sendReshape) { - if (DEBUG) { - System.err.println("glViewport(" + viewportX + ", " + viewportY + ", " + panelWidth + ", " + panelHeight + ")"); - } - getGL().glViewport(viewportX, viewportY, panelWidth, panelHeight); - drawableHelper.reshape(GLJPanel.this, viewportX, viewportY, panelWidth, panelHeight); - sendReshape = false; - } - - drawableHelper.display(GLJPanel.this); - - if (!oglPipelineEnabled) { - // Must now copy pixels from offscreen context into surface - if (offscreenImage == null) { - if (panelWidth > 0 && panelHeight > 0) { - // It looks like NVidia's drivers (at least the ones on my - // notebook) are buggy and don't allow a sub-rectangle to be - // read from a pbuffer...this doesn't really matter because - // it's the Graphics.drawImage() calls that are the - // bottleneck - - int awtFormat = 0; - int hwGLFormat = 0; - if (!hardwareAccelerationDisabled) { - // This seems to be a good choice on all platforms - hwGLFormat = GL.GL_UNSIGNED_INT_8_8_8_8_REV; - } - - // Should be more flexible in these BufferedImage formats; - // perhaps see what the preferred image types are on the - // given platform - if (isOpaque()) { - awtFormat = BufferedImage.TYPE_INT_RGB; - } else { - awtFormat = BufferedImage.TYPE_INT_ARGB; - } - - offscreenImage = new BufferedImage(panelWidth, - panelHeight, - awtFormat); - switch (awtFormat) { - case BufferedImage.TYPE_3BYTE_BGR: - glFormat = GL.GL_BGR; - glType = GL.GL_UNSIGNED_BYTE; - readBackBytes = ByteBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels * 3); - break; - - case BufferedImage.TYPE_INT_RGB: - case BufferedImage.TYPE_INT_ARGB: - glFormat = GL.GL_BGRA; - glType = (hardwareAccelerationDisabled - ? offscreenContext.getOffscreenContextPixelDataType() - : hwGLFormat); - readBackInts = IntBuffer.allocate(readBackWidthInPixels * readBackHeightInPixels); - break; - - default: - // FIXME: Support more off-screen image types (current - // offscreen context implementations don't use others, and - // some of the OpenGL formats aren't supported in the 1.1 - // headers, which we're currently using) - throw new GLException("Unsupported offscreen image type " + awtFormat); - } - } - } - - if (offscreenImage != null) { - GL gl = getGL(); - // Save current modes - gl.glGetIntegerv(GL.GL_PACK_SWAP_BYTES, swapbytes, 0); - gl.glGetIntegerv(GL.GL_PACK_ROW_LENGTH, rowlength, 0); - gl.glGetIntegerv(GL.GL_PACK_SKIP_ROWS, skiprows, 0); - gl.glGetIntegerv(GL.GL_PACK_SKIP_PIXELS, skippixels, 0); - gl.glGetIntegerv(GL.GL_PACK_ALIGNMENT, alignment, 0); - - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, GL.GL_FALSE); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, readBackWidthInPixels); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1); - - // Actually read the pixels. - gl.glReadBuffer(GL.GL_FRONT); - if (readBackBytes != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackBytes); - } else if (readBackInts != null) { - gl.glReadPixels(0, 0, readBackWidthInPixels, readBackHeightInPixels, glFormat, glType, readBackInts); - } - - // Restore saved modes. - gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, swapbytes[0]); - gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, rowlength[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, skiprows[0]); - gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, skippixels[0]); - gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, alignment[0]); - - if (readBackBytes != null || readBackInts != null) { - // Copy temporary data into raster of BufferedImage for faster - // blitting Note that we could avoid this copy in the cases - // where !offscreenContext.offscreenImageNeedsVerticalFlip(), - // but that's the software rendering path which is very slow - // anyway - Object src = null; - Object dest = null; - int srcIncr = 0; - int destIncr = 0; - - if (readBackBytes != null) { - src = readBackBytes.array(); - dest = ((DataBufferByte) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels * 3; - destIncr = offscreenImage.getWidth() * 3; - } else { - src = readBackInts.array(); - dest = ((DataBufferInt) offscreenImage.getRaster().getDataBuffer()).getData(); - srcIncr = readBackWidthInPixels; - destIncr = offscreenImage.getWidth(); - } - - if (!hardwareAccelerationDisabled || - offscreenContext.offscreenImageNeedsVerticalFlip()) { - int srcPos = 0; - int destPos = (offscreenImage.getHeight() - 1) * destIncr; - for (; destPos >= 0; srcPos += srcIncr, destPos -= destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); - } - } else { - int srcPos = 0; - int destEnd = destIncr * offscreenImage.getHeight(); - for (int destPos = 0; destPos < destEnd; srcPos += srcIncr, destPos += destIncr) { - System.arraycopy(src, srcPos, dest, destPos, destIncr); - } - } - - // Note: image will be drawn back in paintComponent() for - // correctness on all platforms - } - } - } else { - // Cause OpenGL pipeline to flush its results because - // otherwise it's possible we will buffer up multiple frames' - // rendering results, resulting in apparent mouse lag - GL gl = getGL(); - gl.glFinish(); - - postGL(g); - } - } - - public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) { - // This is handled above and dispatched directly to the appropriate context - } - - public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) { - } - } - - class InitAction implements Runnable { - public void run() { - updater.init(GLJPanel.this); - } - } - private InitAction initAction = new InitAction(); - - class DisplayAction implements Runnable { - public void run() { - updater.display(GLJPanel.this); - } - } - private DisplayAction displayAction = new DisplayAction(); - - // This one is used exclusively in the non-hardware-accelerated case - class SwapBuffersAction implements Runnable { - public void run() { - offscreenDrawable.swapBuffers(); - } - } - private SwapBuffersAction swapBuffersAction = new SwapBuffersAction(); - - class PaintImmediatelyAction implements Runnable { - public void run() { - paintImmediately(0, 0, getWidth(), getHeight()); - } - } - private PaintImmediatelyAction paintImmediatelyAction = new PaintImmediatelyAction(); - - private int getNextPowerOf2(int number) { - // Workaround for problems where 0 width or height are transiently - // seen during layout - if (number == 0) { - return 2; - } - - if (((number-1) & number) == 0) { - //ex: 8 -> 0b1000; 8-1=7 -> 0b0111; 0b1000&0b0111 == 0 - return number; - } - int power = 0; - while (number > 0) { - number = number>>1; - power++; - } - return (1<0 ) { @@ -338,45 +383,93 @@ JNIEXPORT void JNICALL Java_com_sun_javafx_newt_x11_X11Window_DispatchMessages switch(evt.type) { case ButtonPress: - (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_PRESSED, - (jint) evt.xbutton.state, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button); + case ButtonRelease: + case MotionNotify: + if( ! ( eventMask & EVENT_MOUSE ) ) { + continue; + } + break; + case KeyPress: + case KeyRelease: + if( ! ( eventMask & EVENT_KEY ) ) { + continue; + } + break; + case FocusIn: + case FocusOut: + case DestroyNotify: + case CreateNotify: + case VisibilityNotify: + if( ! ( eventMask & EVENT_WINDOW ) ) { + continue; + } + break; + } + + switch(evt.type) { + case ButtonPress: + if(evt.xbutton.window==w) { + (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_PRESSED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button); + } break; case ButtonRelease: - (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_RELEASED, - (jint) evt.xbutton.state, - (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button); + if(evt.xbutton.window==w) { + (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_RELEASED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button); + } break; case MotionNotify: - (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, - (jint) evt.xmotion.state, - (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0); + if(evt.xmotion.window==w) { + (*env)->CallVoidMethod(env, obj, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + (jint) evt.xmotion.state, + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0); + } break; case KeyPress: - if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { - keyChar=text[0]; - } else { - keyChar=0; + if(evt.xkey.window==w) { + if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { + keyChar=text[0]; + } else { + keyChar=0; + } + (*env)->CallVoidMethod(env, obj, sendKeyEventID, (jint) EVENT_KEY_PRESSED, + (jint) evt.xkey.state, + (jint) keySym, (jchar) keyChar); } - (*env)->CallVoidMethod(env, obj, sendKeyEventID, (jint) EVENT_KEY_PRESSED, - (jint) evt.xkey.state, - (jint) keySym, (jchar) keyChar); break; case KeyRelease: - if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { - keyChar=text[0]; - } else { - keyChar=0; + if(evt.xkey.window==w) { + if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { + keyChar=text[0]; + } else { + keyChar=0; + } + (*env)->CallVoidMethod(env, obj, sendKeyEventID, (jint) EVENT_KEY_RELEASED, + (jint) evt.xkey.state, + (jint) keySym, (jchar) keyChar); } - (*env)->CallVoidMethod(env, obj, sendKeyEventID, (jint) EVENT_KEY_RELEASED, - (jint) evt.xkey.state, - (jint) keySym, (jchar) keyChar); break; case FocusIn: - // evt.xfocus - break; case FocusOut: - // evt.xfocus + if(evt.xfocus.window==w) { + } + break; + case DestroyNotify: + if(evt.xdestroywindow.window==w) { + (*env)->CallVoidMethod(env, obj, windowDestroyedID); + } + break; + case CreateNotify: + if(evt.xcreatewindow.window==w) { + (*env)->CallVoidMethod(env, obj, windowCreatedID); + } + break; + case VisibilityNotify: + if(evt.xvisibility.window==w) { + } break; } } -- cgit v1.2.3