summaryrefslogtreecommitdiffstats
path: root/src/classes/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/classes/com')
-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();
}
}