summaryrefslogtreecommitdiffstats
path: root/src/classes
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2006-01-07 23:47:47 +0000
committerKenneth Russel <[email protected]>2006-01-07 23:47:47 +0000
commit31c51bc71190ba7bbb28f3706e31b8a3c17a1f00 (patch)
tree56e1eda26b28b1526dccc37027e6dc587993a494 /src/classes
parent986e62cfaf516ac2b03c5c7c39c68d6900482c00 (diff)
Reorganized and added code in TextureIO and Texture classes to support
cubemap generation as well as complete replacement of the texture's image, not just a sub-image. Added setTexParameteri to provide control over things like the GL_TEXTURE_WRAP_ modes. Ported JOGL demos to use new TextureIO class instead of TGAImage and ImageIO directly. Fixed longstanding bug in ExaminerViewer's setting up of inverse rotation causing errors in loading of uffizi cubemap. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@523 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src/classes')
-rwxr-xr-xsrc/classes/com/sun/opengl/utils/Texture.java338
-rwxr-xr-xsrc/classes/com/sun/opengl/utils/TextureIO.java95
2 files changed, 297 insertions, 136 deletions
diff --git a/src/classes/com/sun/opengl/utils/Texture.java b/src/classes/com/sun/opengl/utils/Texture.java
index fc3b0d604..efecdffea 100755
--- a/src/classes/com/sun/opengl/utils/Texture.java
+++ b/src/classes/com/sun/opengl/utils/Texture.java
@@ -80,89 +80,17 @@ public class Texture {
// number of public APIs we commit to
Texture(TextureData data) throws GLException {
GL gl = getCurrentGL();
+ texID = createTextureID(gl);
- imgWidth = data.getWidth();
- imgHeight = data.getHeight();
- mustFlipVertically = data.getMustFlipVertically();
-
- if (data.getMipmap()) {
- // GLU always targets GL_TEXTURE_2D and scales the texture to be
- // a power 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;
- target = 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;
- 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;
+ 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 = getCurrentGL();
texID = createTextureID(gl);
- setImageSize(imgWidth, imgHeight);
- gl.glBindTexture(target, texID);
- // 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);
- }
-
- if (data.getMipmap()) {
- GLU glu = new GLU();
- glu.gluBuild2DMipmaps(target, data.getInternalFormat(),
- data.getWidth(), data.getHeight(),
- data.getPixelFormat(), data.getPixelType(), data.getBuffer());
- } else {
- gl.glTexImage2D(target, 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++) {
- updateSubImage(data, i, 0, 0);
- }
- } else {
- updateSubImage(data, 0, 0, 0);
- }
- }
+ this.target = target;
}
/**
@@ -173,7 +101,7 @@ public class Texture {
* OpenGL-related errors occurred
*/
public void enable() throws GLException {
- getCurrentGL().glEnable(target);
+ getCurrentGL().glEnable(target);
}
/**
@@ -205,6 +133,7 @@ public class Texture {
*/
public void dispose() throws GLException {
getCurrentGL().glDeleteTextures(1, new int[] {texID}, 0);
+ texID = 0;
}
/**
@@ -301,6 +230,135 @@ public class Texture {
}
}
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 = 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
@@ -321,52 +379,33 @@ public class Texture {
* OpenGL-related errors occurred
*/
public void updateSubImage(TextureData data, int mipmapLevel, int x, int y) throws GLException {
- GL gl = getCurrentGL();
- bind();
- 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;
- }
+ updateSubImageImpl(data, target, mipmapLevel, x, y);
+ }
- gl.glCompressedTexSubImage2D(target, 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());
+ /**
+ * 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 = getCurrentGL();
+ gl.glTexParameteri(target, parameterName, value);
+ }
- gl.glTexSubImage2D(target, mipmapLevel,
- x, y, width, height,
- data.getPixelFormat(), data.getPixelType(),
- buffer);
- gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, align[0]); // restore align
- }
+ /**
+ * 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;
}
//----------------------------------------------------------------------
@@ -435,6 +474,55 @@ public class Texture {
}
}
+ private void updateSubImageImpl(TextureData data, int newTarget, int mipmapLevel, int x, int y) throws GLException {
+ GL gl = 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.
*
diff --git a/src/classes/com/sun/opengl/utils/TextureIO.java b/src/classes/com/sun/opengl/utils/TextureIO.java
index aa2e3c0ac..07bb67ce4 100755
--- a/src/classes/com/sun/opengl/utils/TextureIO.java
+++ b/src/classes/com/sun/opengl/utils/TextureIO.java
@@ -439,12 +439,17 @@ public class TextureIO {
* 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) throws IOException, GLException {
- TextureData data = newTextureData(stream, mipmap, null);
+ 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;
@@ -462,12 +467,17 @@ public class TextureIO {
* 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) throws IOException, GLException {
- TextureData data = newTextureData(url, mipmap, null);
+ public static Texture newTexture(URL url, boolean mipmap, String fileSuffix) throws IOException, GLException {
+ TextureData data = newTextureData(url, mipmap, fileSuffix);
Texture texture = newTexture(data);
data.flush();
return texture;
@@ -490,6 +500,46 @@ public class TextureIO {
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);
+ }
+
+ //----------------------------------------------------------------------
+ // Helper function for above TextureProviders
+ /**
+ * Returns the suffix of the given file name for identifying the
+ * file to the configured TextureProviders.
+ *
+ * @param file name of the file
+ */
+
+ public static String getFileSuffix(File file) {
+ return getFileSuffix(file.getName());
+ }
+
+ /**
+ * Returns the suffix of the given file name for identifying the
+ * file to the configured TextureProviders.
+ *
+ * @param filename name of the file
+ */
+ public static String getFileSuffix(String filename) {
+ int lastDot = filename.lastIndexOf('.');
+ if (lastDot < 0) {
+ return null;
+ }
+ return toLowerCase(filename.substring(lastDot + 1));
+ }
+
// FIXME: add texture writing capabilities
// public void writeTextureToFile(Texture texture, File file, boolean saveUncompressed) throws IOException, GLException;
@@ -528,6 +578,12 @@ public class TextureIO {
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,
@@ -547,6 +603,17 @@ public class TextureIO {
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,
@@ -567,6 +634,12 @@ public class TextureIO {
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,
@@ -767,7 +840,7 @@ public class TextureIO {
int pixelFormat,
boolean mipmap,
String fileSuffix) throws IOException {
- InputStream stream = url.openStream();
+ InputStream stream = new BufferedInputStream(url.openStream());
try {
return newTextureData(stream, internalFormat, pixelFormat, mipmap, fileSuffix);
} finally {
@@ -845,14 +918,14 @@ public class TextureIO {
}
//----------------------------------------------------------------------
- // Helper function for above TextureProviders
- private static String getFileSuffix(File file) {
- String name = file.getName().toLowerCase();
+ // Helper routines
+ //
- int lastDot = name.lastIndexOf('.');
- if (lastDot < 0) {
+ private static String toLowerCase(String arg) {
+ if (arg == null) {
return null;
}
- return name.substring(lastDot + 1);
+
+ return arg.toLowerCase();
}
}