diff options
author | Kenneth Russel <[email protected]> | 2006-01-13 07:29:23 +0000 |
---|---|---|
committer | Kenneth Russel <[email protected]> | 2006-01-13 07:29:23 +0000 |
commit | 16e23f079b1e5fdc15b64bf1593cbda8e8dcffc0 (patch) | |
tree | 8cf430f395f2a2691d152c9a61ca212d7a6c1f55 /src/classes/com/sun/opengl/util/texture/Texture.java | |
parent | 688ffdc8cc190c51c3e7355a4893e3e475efa29f (diff) |
Renamed com.sun.opengl.utils to com.sun.opengl.util. Moved
TextureIO-related classes to com.sun.opengl.util.texture and
TextureProvider, TextureWriter and format-specific readers to
com.sun.opengl.util.texture.spi. Renamed BufferUtils to BufferUtil.
Added ImageUtil and FileUtil. Cleaned up javadoc. Updated demos.
Cleaned up some imports.
git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@538 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src/classes/com/sun/opengl/util/texture/Texture.java')
-rwxr-xr-x | src/classes/com/sun/opengl/util/texture/Texture.java | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/src/classes/com/sun/opengl/util/texture/Texture.java b/src/classes/com/sun/opengl/util/texture/Texture.java new file mode 100755 index 000000000..9987551e2 --- /dev/null +++ b/src/classes/com/sun/opengl/util/texture/Texture.java @@ -0,0 +1,536 @@ +/* + * 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; + +import java.nio.*; + +import javax.media.opengl.*; +import javax.media.opengl.glu.*; +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 coordinates corresponding to the entire image. */ + private TextureCoords coords; + + 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 = GLU.getCurrentGL(); + texID = createTextureID(gl); + + updateImage(data); + } + + // Constructor for use when creating e.g. cube maps, where there is + // no initial texture data + Texture(int target) throws GLException { + GL gl = GLU.getCurrentGL(); + texID = createTextureID(gl); + this.target = target; + } + + /** + * 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 { + GLU.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 { + GLU.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 { + GLU.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 { + GLU.getCurrentGL().glDeleteTextures(1, new int[] {texID}, 0); + 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 set of texture coordinates corresponding to the + * entire image. If the TextureData indicated that the texture + * coordinates must be flipped vertically, the returned + * TextureCoords will take that into account. + * + * @return the texture coordinates corresponding to the entire image + */ + public TextureCoords getImageTexCoords() { + return coords; + } + + /** + * Returns the set of texture coordinates corresponding to the + * specified sub-image. The (x1, y1) and (x2, y2) points are + * specified in terms of pixels starting from the lower-left of the + * image. (x1, y1) should specify the lower-left corner of the + * sub-image and (x2, y2) the upper-right corner of the sub-image. + * If the TextureData indicated that the texture coordinates must be + * flipped vertically, the returned TextureCoords will take that + * into account; this should not be handled by the end user in the + * specification of the y1 and y2 coordinates. + * + * @return the texture coordinates corresponding to the specified sub-image + */ + public TextureCoords getSubImageTexCoords(int x1, int y1, int x2, int y2) { + if (target == GL.GL_TEXTURE_RECTANGLE_ARB) { + if (mustFlipVertically) { + return new TextureCoords(x1, texHeight - y1, x2, texHeight - y2); + } else { + return new TextureCoords(x1, y1, x2, y2); + } + } else { + float tx1 = (float)x1 / (float)texWidth; + float ty1 = (float)y1 / (float)texHeight; + float tx2 = (float)x2 / (float)texWidth; + float ty2 = (float)y2 / (float)texHeight; + if (mustFlipVertically) { + return new TextureCoords(tx1, 1.0f - ty1, tx2, 1.0f - ty2); + } else { + return new TextureCoords(tx1, ty1, tx2, ty2); + } + } + } + + /** + * Updates the entire content area of this texture using the data in + * the given image. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void updateImage(TextureData data) throws GLException { + updateImage(data, 0); + } + + /** + * Indicates whether this texture's texture coordinates must be + * flipped vertically in order to properly display the texture. This + * is handled automatically by {@link #getImageTexCoords} and {@link + * #getSubImageTexCoords}, but applications may generate or + * otherwise produce texture coordinates which must be corrected. + */ + public boolean getMustFlipVertically() { + return mustFlipVertically; + } + + /** + * Updates the content area of the specified target of this texture + * using the data in the given image. In general this is intended + * for construction of cube maps. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void updateImage(TextureData data, int target) throws GLException { + GL gl = GLU.getCurrentGL(); + + imgWidth = data.getWidth(); + imgHeight = data.getHeight(); + mustFlipVertically = data.getMustFlipVertically(); + + int newTarget = 0; + + if (data.getMipmap()) { + // GLU always scales the texture's dimensions to be powers of + // two. It also doesn't really matter exactly what the texture + // width and height are because the texture coords are always + // between 0.0 and 1.0. + imgWidth = nextPowerOfTwo(imgWidth); + imgHeight = nextPowerOfTwo(imgHeight); + texWidth = imgWidth; + texHeight = imgHeight; + newTarget = GL.GL_TEXTURE_2D; + } else 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; + newTarget = 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; + newTarget = 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); + newTarget = GL.GL_TEXTURE_2D; + } + + setImageSize(imgWidth, imgHeight); + + if (target != 0) { + // Allow user to override auto detection and skip bind step (for + // cubemap construction) + newTarget = target; + if (this.target == 0) { + throw new GLException("Override of target failed; no target specified yet"); + } + gl.glBindTexture(this.target, texID); + } else { + gl.glBindTexture(newTarget, texID); + } + + // REMIND: let the user specify these, optionally + int minFilter = (data.getMipmap() ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR); + int magFilter = GL.GL_LINEAR; + int wrapMode = GL.GL_CLAMP_TO_EDGE; + + // REMIND: figure out what to do for GL_TEXTURE_RECTANGLE_ARB + if (newTarget != GL.GL_TEXTURE_RECTANGLE_ARB) { + gl.glTexParameteri(newTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter); + gl.glTexParameteri(newTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter); + gl.glTexParameteri(newTarget, GL.GL_TEXTURE_WRAP_S, wrapMode); + gl.glTexParameteri(newTarget, GL.GL_TEXTURE_WRAP_T, wrapMode); + if (newTarget == GL.GL_TEXTURE_CUBE_MAP) { + gl.glTexParameteri(newTarget, GL.GL_TEXTURE_WRAP_R, wrapMode); + } + } + + if (data.getMipmap()) { + GLU glu = new GLU(); + glu.gluBuild2DMipmaps(newTarget, data.getInternalFormat(), + data.getWidth(), data.getHeight(), + data.getPixelFormat(), data.getPixelType(), data.getBuffer()); + } else { + gl.glTexImage2D(newTarget, 0, data.getInternalFormat(), + texWidth, texHeight, data.getBorder(), + data.getPixelFormat(), data.getPixelType(), null); + Buffer[] mipmapData = data.getMipmapData(); + if (mipmapData != null) { + for (int i = 0; i < mipmapData.length; i++) { + updateSubImageImpl(data, newTarget, i, 0, 0); + } + } else { + updateSubImageImpl(data, newTarget, 0, 0, 0); + } + } + + // Don't overwrite target if we're loading e.g. faces of a cube + // map + if ((this.target == 0) || + (this.target == GL.GL_TEXTURE_2D) || + (this.target == GL.GL_TEXTURE_RECTANGLE_ARB)) { + this.target = newTarget; + } + } + + /** + * Updates a subregion of the content area of this texture using the + * data in the given image. Only updates the specified mipmap level + * and does not re-generate mipmaps if they were originally produced + * or loaded. + * + * @param data the image data to be uploaded to this texture + * @param mipmapLevel the mipmap level of the texture to set. If + * this is non-zero and the TextureData contains mipmap data, the + * appropriate mipmap level will be selected. + * @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 mipmapLevel, int x, int y) throws GLException { + updateSubImageImpl(data, target, mipmapLevel, x, y); + } + + /** + * Sets the OpenGL integer texture parameter for the texture's + * target. This gives control over parameters such as + * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T, which by default are set + * to GL_CLAMP_TO_EDGE. Causes this texture to be bound to the + * current texture state. + * + * @throws GLException if no OpenGL context was current or if any + * OpenGL-related errors occurred + */ + public void setTexParameteri(int parameterName, + int value) { + bind(); + GL gl = GLU.getCurrentGL(); + gl.glTexParameteri(target, parameterName, value); + } + + /** + * Returns the underlying OpenGL texture object for this texture. + * Most applications will not need to access this, since it is + * handled automatically by the bind() and dispose() APIs. + */ + public int getTextureObject() { + return texID; + } + + //---------------------------------------------------------------------- + // Internals only below this point + // + + /** + * 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) { + 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 x, int y) throws GLException { + GL gl = GLU.getCurrentGL(); + gl.glBindTexture(newTarget, texID); + int width = data.getWidth(); + int height = data.getHeight(); + Buffer buffer = data.getBuffer(); + if (data.getMipmapData() != null) { + // Compute the width and height at the specified mipmap level + for (int i = 0; i < mipmapLevel; i++) { + width /= 2; + height /= 2; + } + buffer = data.getMipmapData()[mipmapLevel]; + } + + 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: + // FIXME: should test availability of more texture + // compression extensions here + break; + } + + gl.glCompressedTexSubImage2D(newTarget, mipmapLevel, + x, y, width, height, + data.getInternalFormat(), + buffer.remaining(), buffer); + } 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(newTarget, mipmapLevel, + x, y, width, height, + data.getPixelFormat(), data.getPixelType(), + buffer); + gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore align + } + } + + /** + * 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]; + } +} |