diff options
Diffstat (limited to 'src/classes/com/sun/opengl/utils/Texture.java')
-rwxr-xr-x | src/classes/com/sun/opengl/utils/Texture.java | 440 |
1 files changed, 440 insertions, 0 deletions
diff --git a/src/classes/com/sun/opengl/utils/Texture.java b/src/classes/com/sun/opengl/utils/Texture.java new file mode 100755 index 000000000..6c3fbd1c3 --- /dev/null +++ b/src/classes/com/sun/opengl/utils/Texture.java @@ -0,0 +1,440 @@ +/* + * 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.utils; + +import java.awt.geom.*; + +import javax.media.opengl.*; +import com.sun.opengl.impl.*; + +/** + * 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. + * + * <br> REMIND: document GL_TEXTURE_2D/GL_TEXTURE_RECTANGLE_ARB issues... + * <br> REMIND: translucent images will have premultiplied comps by default... + * + * @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; + /** Indicates whether the TextureData requires a vertical flip of + the texture coords. */ + private boolean mustFlipVertically; + + /** The texture coordinate corresponding to the lower left corner + of the texture when properly oriented. */ + private Point2D lowerLeftTexCoord = new Point2D.Float(); + + /** The texture coordinate corresponding to the upper right corner + of the texture when properly oriented. */ + private Point2D upperRightTexCoord = new Point2D.Float(); + + private static final boolean DEBUG = Debug.debug("Texture"); + + // For now make Texture constructor package-private to limit the + // number of public APIs we commit to + Texture(TextureData data) throws GLException { + GL gl = getCurrentGL(); + + imgWidth = data.getWidth(); + imgHeight = data.getHeight(); + mustFlipVertically = data.getMustFlipVertically(); + + if ((isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) || + gl.isExtensionAvailable("GL_ARB_texture_non_power_of_two")) { + if (DEBUG) { + if (isPowerOfTwo(imgWidth) && isPowerOfTwo(imgHeight)) { + System.err.println("Power-of-two texture"); + } else { + System.err.println("Using GL_ARB_texture_non_power_of_two"); + } + } + + texWidth = imgWidth; + texHeight = imgHeight; + target = GL.GL_TEXTURE_2D; + } else if (gl.isExtensionAvailable("GL_ARB_texture_rectangle")) { + if (DEBUG) { + System.err.println("Using GL_ARB_texture_rectangle"); + } + + texWidth = imgWidth; + texHeight = imgHeight; + target = GL.GL_TEXTURE_RECTANGLE_ARB; + } else { + if (DEBUG) { + System.err.println("Expanding texture to power-of-two dimensions"); + } + + if (data.getBorder() != 0) { + throw new RuntimeException("Scaling up a non-power-of-two texture which has a border won't work"); + } + texWidth = nextPowerOfTwo(imgWidth); + texHeight = nextPowerOfTwo(imgHeight); + target = GL.GL_TEXTURE_2D; + } + + // REMIND: let the user specify these, optionally + int minFilter = GL.GL_LINEAR; + int magFilter = GL.GL_LINEAR; + int wrapMode = GL.GL_CLAMP_TO_EDGE; + + texID = createTextureID(gl); + setImageSize(imgWidth, imgHeight); + gl.glBindTexture(target, texID); + gl.glTexImage2D(target, 0, data.getInternalFormat(), + texWidth, texHeight, data.getBorder(), + data.getPixelFormat(), data.getPixelType(), null); + + // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB + if (target == GL.GL_TEXTURE_2D) { + gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter); + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_S, wrapMode); + gl.glTexParameteri(target, GL.GL_TEXTURE_WRAP_T, wrapMode); + } + + updateSubImage(data, 0, 0); + } + + /** + * Enables this texture's target (e.g., GL_TEXTURE_2D) in the + * current GL context's state. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void enable() throws GLException { + getCurrentGL().glEnable(target); + } + + /** + * Disables this texture's target (e.g., GL_TEXTURE_2D) in the + * current GL context's state. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void disable() throws GLException { + getCurrentGL().glDisable(target); + } + + /** + * Binds this texture to the current GL context. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void bind() throws GLException { + 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 + */ + public void dispose() throws GLException { + getCurrentGL().glDeleteTextures(1, new int[] {texID}, 0); + } + + /** + * Returns the OpenGL "target" of this texture. + * + * @return the OpenGL target of this texture + * @see javax.media.opengl.GL#GL_TEXTURE_2D + * @see javax.media.opengl.GL#GL_TEXTURE_RECTANGLE_ARB + */ + public int getTarget() { + return target; + } + + /** + * Returns the width of the texture. Note that the texture width will + * be greater than or equal to the width of the image contained within. + * + * @return the width of the texture + */ + public int getWidth() { + return texWidth; + } + + /** + * Returns the height of the texture. Note that the texture height will + * be greater than or equal to the height of the image contained within. + * + * @return the height of the texture + */ + public int getHeight() { + return texHeight; + } + + /** + * Returns the width of the image contained within this texture. + * + * @return the width of the image + */ + public int getImageWidth() { + return imgWidth; + } + + /** + * Returns the height of the image contained within this texture. + * + * @return the height of the image + */ + public int getImageHeight() { + return imgHeight; + } + + /** + * Returns the texture coordinate corresponding to the lower-left + * point of this texture when oriented properly. If the TextureData + * indicated that the texture coordinates must be flipped + * vertically, the returned Point will take that into account. + * + * @return the texture coordinate of the lower-left point of the + * texture + */ + public Point2D getImageLowerLeftTexCoord() { + return (Point2D) lowerLeftTexCoord.clone(); + } + + /** + * Returns the texture coordinate corresponding to the upper-right + * point of this texture when oriented properly. If the TextureData + * indicated that the texture coordinates must be flipped + * vertically, the returned Point will take that into account. + * + * @return the texture coordinates of the upper-right point of the + * texture + */ + public Point2D getImageUpperRightTexCoord() { + return (Point2D) upperRightTexCoord.clone(); + } + + + /** + * Returns the texture coordinate corresponding to the lower-left + * point of the specified sub-image of this texture when oriented + * properly. If the TextureData indicated that the texture + * coordinates must be flipped vertically, the returned Point will + * take that into account. + * + * @return the texture coordinate of the lower-left point of the + * texture + */ + public Point2D getSubImageLowerLeftTexCoord(int x1, int y1, int x2, int y2) { + if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { + if (mustFlipVertically) { + return new Point2D.Float(x1, texHeight - y1); + } else { + return new Point2D.Float(x1, y1); + } + } else { + float tx = (float)x1 / (float)texWidth; + float ty = (float)y1 / (float)texHeight; + + if (mustFlipVertically) { + return new Point2D.Float(tx, 1.0f - ty); + } else { + return new Point2D.Float(tx, ty); + } + } + } + + /** + * Returns the texture coordinate corresponding to the upper-right + * point of the specified sub-image of this texture when oriented + * properly. If the TextureData indicated that the texture + * coordinates must be flipped vertically, the returned Point will + * take that into account. + * + * @return the texture coordinate of the upper-right point of the + * texture + */ + public Point2D getSubImageUpperRightTexCoord(int x1, int y1, int x2, int y2) { + if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { + if (mustFlipVertically) { + return new Point2D.Float(x2, texHeight - y2); + } else { + return new Point2D.Float(x2, y2); + } + } else { + float tx = (float)x2 / (float)texWidth; + float ty = (float)y2 / (float)texHeight; + + if (mustFlipVertically) { + return new Point2D.Float(tx, 1.0f - ty); + } else { + return new Point2D.Float(tx, ty); + } + } + } + + /** + * Updates a subregion of the content area of this texture using the + * data in the given image. + * + * @param data the image data to be uploaded to this texture + * @param x the x offset (in pixels) relative to the lower-left corner + * of this texture + * @param y the y offset (in pixels) relative to the lower-left corner + * of this texture + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void updateSubImage(TextureData data, int x, int y) throws GLException { + GL gl = getCurrentGL(); + bind(); + if (data.isDataCompressed()) { + // FIXME: should test availability of appropriate texture + // compression extension here + gl.glCompressedTexSubImage2D(target, data.getMipmapLevel(), + x, y, data.getWidth(), data.getHeight(), + data.getInternalFormat(), data.getBuffer().remaining(), + data.getBuffer()); + } else { + int[] align = new int[1]; + gl.glGetIntegerv(GL.GL_UNPACK_ALIGNMENT, align, 0); // save alignment + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, data.getAlignment()); + + gl.glTexSubImage2D(target, data.getMipmapLevel(), + x, y, data.getWidth(), data.getHeight(), + data.getPixelFormat(), data.getPixelType(), + data.getBuffer()); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore align + } + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + /** + * Returns the current GL object. Throws GLException if no OpenGL + * context was current. + */ + private static GL getCurrentGL() throws GLException { + GLContext context = GLContext.getCurrent(); + if (context == null) { + throw new GLException("No OpenGL context current on current thread"); + } + return context.getGL(); + } + + /** + * Returns true if the given value is a power of two. + * + * @return true if the given value is a power of two, false otherwise + */ + private static boolean isPowerOfTwo(int val) { + return ((val & (val - 1)) == 0); + } + + /** + * Returns the nearest power of two that is larger than the given value. + * If the given value is already a power of two, this method will simply + * return that value. + * + * @param val the value + * @return the next power of two + */ + private static int nextPowerOfTwo(int val) { + int ret = 1; + while (ret < val) { + ret <<= 1; + } + return ret; + } + + /** + * Updates the actual image dimensions; usually only called from + * <code>updateImage</code>. + */ + private void setImageSize(int width, int height) { + imgWidth = width; + imgHeight = height; + if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { + if (mustFlipVertically) { + lowerLeftTexCoord = new Point2D.Float(0, imgHeight); + upperRightTexCoord = new Point2D.Float(imgWidth, 0); + } else { + lowerLeftTexCoord = new Point2D.Float(0, 0); + upperRightTexCoord = new Point2D.Float(imgWidth, imgHeight); + } + } else { + if (mustFlipVertically) { + lowerLeftTexCoord = new Point2D.Float(0, (float) imgHeight / (float) texHeight); + upperRightTexCoord = new Point2D.Float((float) imgWidth / (float) texWidth, 0); + } else { + lowerLeftTexCoord = new Point2D.Float(0, 0); + upperRightTexCoord = new Point2D.Float((float) imgWidth / (float) texWidth, + (float) imgHeight / (float) texHeight); + } + } + } + + /** + * Creates a new texture ID. + * + * @param gl the GL object associated with the current OpenGL context + * @return a new texture ID + */ + private static int createTextureID(GL gl) { + int[] tmp = new int[1]; + gl.glGenTextures(1, tmp, 0); + return tmp[0]; + } +} |