diff options
Diffstat (limited to 'src/classes/com/sun')
-rwxr-xr-x | src/classes/com/sun/opengl/utils/Texture.java | 338 | ||||
-rwxr-xr-x | src/classes/com/sun/opengl/utils/TextureIO.java | 95 |
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(); } } |