From 2b954ff1fe88f35b59da6c6f6b82fde70274a6ef Mon Sep 17 00:00:00 2001 From: Michael Bien Date: Sat, 27 Mar 2010 23:24:13 +0100 Subject: refactoring: renamed com.sun.opengl -> com.jogamp.opengl. --- .../com/jogamp/opengl/util/texture/Texture.java | 1120 ++++++++++++++++++++ 1 file changed, 1120 insertions(+) create mode 100755 src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java (limited to 'src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java') diff --git a/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java b/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java new file mode 100755 index 000000000..5ef96eeaf --- /dev/null +++ b/src/jogl/classes/com/jogamp/opengl/util/texture/Texture.java @@ -0,0 +1,1120 @@ +/* + * 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.jogamp.opengl.util.texture; + +import java.nio.*; +import java.security.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +import javax.media.nativewindow.NativeWindowFactory; +import com.jogamp.opengl.impl.*; +import com.jogamp.opengl.util.texture.*; +import com.jogamp.opengl.util.texture.spi.*; + +/** + * Represents an OpenGL texture object. Contains convenience routines + * for enabling/disabling OpenGL texture state, binding this texture, + * and computing texture coordinates for both the entire image as well + * as a sub-image. + * + *

Non-power-of-two restrictions + *
When creating an OpenGL texture object, the Texture class will + * attempt to leverage the GL_ARB_texture_non_power_of_two + * and GL_ARB_texture_rectangle + * extensions (in that order) whenever possible. If neither extension + * is available, the Texture class will simply upload a non-pow2-sized + * image into a standard pow2-sized texture (without any special + * scaling). Since the choice of extension (or whether one is used at + * all) depends on the user's machine configuration, developers are + * recommended to use {@link #getImageTexCoords} and {@link + * #getSubImageTexCoords}, as those methods will calculate the + * appropriate texture coordinates for the situation. + * + *

One caveat in this approach is that certain texture wrap modes + * (e.g. GL_REPEAT) are not legal when the GL_ARB_texture_rectangle + * extension is in use. Another issue to be aware of is that in the + * default pow2 scenario, if the original image does not have pow2 + * dimensions, then wrapping may not work as one might expect since + * the image does not extend to the edges of the pow2 texture. If + * texture wrapping is important, it is recommended to use only + * pow2-sized images with the Texture class. + * + *

Performance Tips + *
For best performance, try to avoid calling {@link #enable} / + * {@link #bind} / {@link #disable} any more than necessary. For + * example, applications using many Texture objects in the same scene + * may want to reduce the number of calls to both {@link #enable} and + * {@link #disable}. To do this it is necessary to call {@link + * #getTarget} to make sure the OpenGL texture target is the same for + * all of the Texture objects in use; non-power-of-two textures using + * the GL_ARB_texture_rectangle extension use a different target than + * power-of-two textures using the GL_TEXTURE_2D target. Note that + * when switching between textures it is necessary to call {@link + * #bind}, but when drawing many triangles all using the same texture, + * for best performance only one call to {@link #bind} should be made. + * + *

Alpha premultiplication and blending + *
The mathematically correct way to perform blending in OpenGL + * (with the SrcOver "source over destination" mode, or any other + * Porter-Duff rule) is to use "premultiplied color components", which + * means the R/G/ B color components have already been multiplied by + * the alpha value. To make things easier for developers, the Texture + * class will automatically convert non-premultiplied image data into + * premultiplied data when storing it into an OpenGL texture. As a + * result, it is important to use the correct blending function; for + * example, the SrcOver rule is expressed as: +

+    gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);
+
+ * Also, when using a texture function like GL_MODULATE where + * the current color plays a role, it is important to remember to make + * sure that the color is specified in a premultiplied form, for + * example: +
+    float a = ...;
+    float r = r * a;
+    float g = g * a;
+    float b = b * a;
+    gl.glColor4f(r, g, b, a);
+
+ * + * For reference, here is a list of the Porter-Duff compositing rules + * and the associated OpenGL blend functions (source and destination + * factors) to use in the face of premultiplied alpha: + * +
+ +
Rule Source Dest +
Clear GL_ZERO GL_ZERO +
Src GL_ONE GL_ZERO +
SrcOver GL_ONE GL_ONE_MINUS_SRC_ALPHA +
DstOver GL_ONE_MINUS_DST_ALPHA GL_ONE +
SrcIn GL_DST_ALPHA GL_ZERO +
DstIn GL_ZERO GL_SRC_ALPHA +
SrcOut GL_ONE_MINUS_DST_ALPHA GL_ZERO +
DstOut GL_ZERO GL_ONE_MINUS_SRC_ALPHA +
Dst GL_ZERO GL_ONE +
SrcAtop GL_DST_ALPHA GL_ONE_MINUS_SRC_ALPHA +
DstAtop GL_ONE_MINUS_DST_ALPHA GL_SRC_ALPHA +
AlphaXor GL_ONE_MINUS_DST_ALPHA GL_ONE_MINUS_SRC_ALPHA +
+
+ * + * @author Chris Campbell + * @author Kenneth Russell + */ +public class Texture { + /** The GL target type. */ + private int target; + /** The GL texture ID. */ + private int texID; + /** The width of the texture. */ + private int texWidth; + /** The height of the texture. */ + private int texHeight; + /** The width of the image. */ + private int imgWidth; + /** The height of the image. */ + private int imgHeight; + /** The original aspect ratio of the image, before any rescaling + that might have occurred due to using the GLU mipmap routines. */ + private float aspectRatio; + /** Indicates whether the TextureData requires a vertical flip of + the texture coords. */ + private boolean mustFlipVertically; + /** Indicates whether we're using automatic mipmap generation + support (GL_GENERATE_MIPMAP). */ + private boolean usingAutoMipmapGeneration; + + /** The texture coordinates corresponding to the entire image. */ + private TextureCoords coords; + + /** An estimate of the amount of texture memory this texture consumes. */ + private int estimatedMemorySize; + + private static final AccessControlContext localACC = AccessController.getContext(); + + private static final boolean DEBUG = Debug.debug("Texture"); + private static final boolean VERBOSE = Debug.verbose(); + + // For testing alternate code paths on more capable hardware + private static final boolean disableNPOT = Debug.isPropertyDefined("jogl.texture.nonpot", true, localACC); + private static final boolean disableTexRect = Debug.isPropertyDefined("jogl.texture.notexrect", true, localACC); + + public Texture(TextureData data) throws GLException { + texID = 0; + updateImage(data); + } + + // Constructor for use when creating e.g. cube maps, where there is + // no initial texture data + public Texture(int target) throws GLException { + texID = 0; + this.target = target; + } + + // Package-private constructor for creating a texture object which wraps + // an existing texture ID from another package + Texture(int textureID, + int target, + int texWidth, + int texHeight, + int imgWidth, + int imgHeight, + boolean mustFlipVertically) { + this.texID = textureID; + this.target = target; + this.mustFlipVertically = mustFlipVertically; + this.texWidth = texWidth; + this.texHeight = texHeight; + setImageSize(imgWidth, imgHeight, target); + } + + /** + * Enables this texture's target (e.g., GL_TEXTURE_2D) in the + * current GL context's state. This method is a shorthand equivalent + * of the following OpenGL code: +
+     gl.glEnable(texture.getTarget());
+     
+ * + * See the performance tips above for hints + * on how to maximize performance when using many Texture objects. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void enable() throws GLException { + GLContext.getCurrentGL().glEnable(target); + } + + /** + * Disables this texture's target (e.g., GL_TEXTURE_2D) in the + * current GL context's state. This method is a shorthand equivalent + * of the following OpenGL code: +
+     gl.glDisable(texture.getTarget());
+     
+ * + * See the performance tips above for hints + * on how to maximize performance when using many Texture objects. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void disable() throws GLException { + GLContext.getCurrentGL().glDisable(target); + } + + /** + * Binds this texture to the current GL context. This method is a + * shorthand equivalent of the following OpenGL code: +
+     gl.glBindTexture(texture.getTarget(), texture.getTextureObject());
+     
+ * + * See the performance tips above for hints + * on how to maximize performance when using many Texture objects. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void bind() throws GLException { + validateTexID(null, true); + GLContext.getCurrentGL().glBindTexture(target, texID); + } + + /** + * Disposes the native resources used by this texture object. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + * @deprecated use destroy(GL) + */ + public void dispose() throws GLException { + destroy(GLContext.getCurrentGL()); + } + + /** + * Disposes the native resources used by this texture object. + * + * @throws GLException if any OpenGL-related errors occurred + * @deprecated use destroy(GL) + */ + public void dispose(GL gl) throws GLException { + destroy(gl); + } + + /** + * Destroys the native resources used by this texture object. + * + * @throws GLException if any OpenGL-related errors occurred + */ + public void destroy(GL gl) throws GLException { + if(0updateImage. + */ + private void setImageSize(int width, int height, int target) { + imgWidth = width; + imgHeight = height; + if (target == GL2.GL_TEXTURE_RECTANGLE_ARB) { + if (mustFlipVertically) { + coords = new TextureCoords(0, imgHeight, imgWidth, 0); + } else { + coords = new TextureCoords(0, 0, imgWidth, imgHeight); + } + } else { + if (mustFlipVertically) { + coords = new TextureCoords(0, (float) imgHeight / (float) texHeight, + (float) imgWidth / (float) texWidth, 0); + } else { + coords = new TextureCoords(0, 0, + (float) imgWidth / (float) texWidth, + (float) imgHeight / (float) texHeight); + } + } + } + + private void updateSubImageImpl(TextureData data, int newTarget, int mipmapLevel, + int dstx, int dsty, + int srcx, int srcy, int width, int height) throws GLException { + GL gl = GLContext.getCurrentGL(); + data.setHaveEXTABGR(gl.isExtensionAvailable("GL_EXT_abgr")); + data.setHaveGL12(gl.isExtensionAvailable("GL_VERSION_1_2")); + + Buffer buffer = data.getBuffer(); + if (buffer == null && data.getMipmapData() == null) { + // Assume user just wanted to get the Texture object allocated + return; + } + + int rowlen = data.getRowLength(); + int dataWidth = data.getWidth(); + int dataHeight = data.getHeight(); + if (data.getMipmapData() != null) { + // Compute the width, height and row length at the specified mipmap level + // Note we do not support specification of the row length for + // mipmapped textures at this point + for (int i = 0; i < mipmapLevel; i++) { + width = Math.max(width / 2, 1); + height = Math.max(height / 2, 1); + + dataWidth = Math.max(dataWidth / 2, 1); + dataHeight = Math.max(dataHeight / 2, 1); + } + rowlen = 0; + buffer = data.getMipmapData()[mipmapLevel]; + } + + // Clip incoming rectangles to what is available both on this + // texture and in the incoming TextureData + if (srcx < 0) { + width += srcx; + srcx = 0; + } + if (srcy < 0) { + height += srcy; + srcy = 0; + } + // NOTE: not sure whether the following two are the correct thing to do + if (dstx < 0) { + width += dstx; + dstx = 0; + } + if (dsty < 0) { + height += dsty; + dsty = 0; + } + + if (srcx + width > dataWidth) { + width = dataWidth - srcx; + } + if (srcy + height > dataHeight) { + height = dataHeight - srcy; + } + if (dstx + width > texWidth) { + width = texWidth - dstx; + } + if (dsty + height > texHeight) { + height = texHeight - dsty; + } + + checkCompressedTextureExtensions(data); + + if (data.isDataCompressed()) { + gl.glCompressedTexSubImage2D(newTarget, mipmapLevel, + dstx, dsty, width, height, + data.getInternalFormat(), + buffer.remaining(), buffer); + } else { + int[] align = { 0 }; + int[] rowLength = { 0 }; + int[] skipRows = { 0 }; + int[] skipPixels = { 0 }; + gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment + if(gl.isGL2()) { + gl.glGetIntegerv(GL2.GL_UNPACK_ROW_LENGTH, rowLength, 0); // save row length + gl.glGetIntegerv(GL2.GL_UNPACK_SKIP_ROWS, skipRows, 0); // save skipped rows + gl.glGetIntegerv(GL2.GL_UNPACK_SKIP_PIXELS, skipPixels, 0); // save skipped pixels + } + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); + if (DEBUG && VERBOSE) { + System.out.println("Row length = " + rowlen); + System.out.println("skip pixels = " + srcx); + System.out.println("skip rows = " + srcy); + System.out.println("dstx = " + dstx); + System.out.println("dsty = " + dsty); + System.out.println("width = " + width); + System.out.println("height = " + height); + } + if(gl.isGL2()) { + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, rowlen); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, srcy); + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, srcx); + } else { + if ( rowlen!=0 && rowlen!=width && + srcy!=0 && srcx!=0 ) { + throw new GLException("rowlen and/or x/y offset only available for GL2"); + } + } + + gl.glTexSubImage2D(newTarget, mipmapLevel, + dstx, dsty, width, height, + data.getPixelFormat(), data.getPixelType(), + buffer); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore alignment + if(gl.isGL2()) { + gl.glPixelStorei(GL2.GL_UNPACK_ROW_LENGTH, rowLength[0]); // restore row length + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_ROWS, skipRows[0]); // restore skipped rows + gl.glPixelStorei(GL2.GL_UNPACK_SKIP_PIXELS, skipPixels[0]); // restore skipped pixels + } + } + } + + private void checkCompressedTextureExtensions(TextureData data) { + GL gl = GLContext.getCurrentGL(); + if (data.isDataCompressed()) { + switch (data.getInternalFormat()) { + case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + if (!gl.isExtensionAvailable("GL_EXT_texture_compression_s3tc") && + !gl.isExtensionAvailable("GL_NV_texture_compression_vtc")) { + throw new GLException("DXTn compressed textures not supported by this graphics card"); + } + break; + default: + // FI1027GXME: should test availability of more texture + // compression extensions here + break; + } + } + } + + private void validateTexID(GL gl, boolean throwException) { + if( 0 < texID ) return; + if(null==gl) { + GLContext ctx = GLContext.getCurrent(); + if(null!=ctx) { + gl = ctx.getGL(); + } else if(throwException) { + throw new GLException("No context current, can't create texture ID"); + } + } + + if(null!=gl) { + int[] tmp = new int[1]; + gl.glGenTextures(1, tmp, 0); + texID = tmp[0]; + } + } + + // Helper routines for disabling certain codepaths + private static boolean haveNPOT(GL gl) { + return (!disableNPOT && + ( gl.isGLES2() || + gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two") ) ); + } + + private static boolean haveTexRect(GL gl) { + return (!disableTexRect && + TextureIO.isTexRectEnabled() && + gl.isExtensionAvailable("GL_ARB_texture_rectangle")); + } + + private static boolean preferTexRect(GL gl) { + // Prefer GL_ARB_texture_rectangle on ATI hardware on Mac OS X + // due to software fallbacks + + if (NativeWindowFactory.TYPE_MACOSX.equals(NativeWindowFactory.getNativeWindowType(false))) { + String vendor = gl.glGetString(GL.GL_VENDOR); + if (vendor != null && vendor.startsWith("ATI")) { + return true; + } + } + + return false; + } +} -- cgit v1.2.3