summaryrefslogtreecommitdiffstats
path: root/src/classes/com/sun/opengl/utils/TextureIO.java
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2006-01-09 02:37:43 +0000
committerKenneth Russel <[email protected]>2006-01-09 02:37:43 +0000
commitb464faaae70ba12e5842b901a6c6d1601af0e1c7 (patch)
tree6411322c882c48dfe04870adf981c33609fabefa /src/classes/com/sun/opengl/utils/TextureIO.java
parentc1e4d8a8937ecce5bbc88c6de604ce35e793b9cc (diff)
Added output support to TGAImage, SGIImage and newly-renamed DDSImage
classes. Added support to TextureIO for writing textures back to disk via new TextureWriter plug-in interface. Added TextureConvert demo which shows how an application might convert between arbitrary file formats using these APIs, including automatic compression to DXT3 format when available. git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/jogl/trunk@525 232f8b59-042b-4e1e-8c03-345bb8c30851
Diffstat (limited to 'src/classes/com/sun/opengl/utils/TextureIO.java')
-rwxr-xr-xsrc/classes/com/sun/opengl/utils/TextureIO.java454
1 files changed, 420 insertions, 34 deletions
diff --git a/src/classes/com/sun/opengl/utils/TextureIO.java b/src/classes/com/sun/opengl/utils/TextureIO.java
index 07bb67ce4..d1f62cd5c 100755
--- a/src/classes/com/sun/opengl/utils/TextureIO.java
+++ b/src/classes/com/sun/opengl/utils/TextureIO.java
@@ -39,6 +39,7 @@
package com.sun.opengl.utils;
+import java.awt.Graphics;
import java.awt.image.*;
import java.io.*;
import java.net.*;
@@ -47,25 +48,28 @@ import java.util.*;
import javax.imageio.*;
import javax.media.opengl.*;
+import javax.media.opengl.glu.*;
/** <P> Provides input and output facilities for both loading OpenGL
textures from disk and streams as well as writing textures already
in memory back to disk. </P>
<P> The TextureIO class supports an arbitrary number of plug-in
- TextureProviders which know how to produce TextureData objects
- from files, InputStreams and URLs. The TextureData class
- represents the raw data of the texture before it has been
- converted to an OpenGL texture object. The Texture class
+ readers and writers via TextureProviders and TextureWriters.
+ TextureProviders know how to produce TextureData objects from
+ files, InputStreams and URLs. TextureWriters know how to write
+ TextureData objects to disk in various file formats. The
+ TextureData class represents the raw data of the texture before it
+ has been converted to an OpenGL texture object. The Texture class
represents the OpenGL texture object and provides easy facilities
for using the texture. </P>
- <P> There are several built-in TextureProviders supplied with the
- TextureIO implementation. The most basic provider uses the
- platform's Image I/O facilities to read in a BufferedImage and
- convert it to a texture. This is the baseline provider and is
- registered so that it is the last one consulted. All others are
- asked first to open a given file. </P>
+ <P> There are several built-in TextureProviders and TextureWriters
+ supplied with the TextureIO implementation. The most basic
+ provider uses the platform's Image I/O facilities to read in a
+ BufferedImage and convert it to a texture. This is the baseline
+ provider and is registered so that it is the last one consulted.
+ All others are asked first to open a given file. </P>
<P> There are three other providers registered by default as of
the time of this writing. One handles SGI RGB (".sgi", ".rgb")
@@ -84,6 +88,18 @@ import javax.media.opengl.*;
when probing for e.g. magic numbers at the head of the file to
make sure not to disturb the state of the InputStream for
downstream TextureProviders. </P>
+
+ <P> There are analogous TextureWriters provided for writing
+ textures back to disk if desired. As of this writing, there are
+ four TextureWriters registered by default: one for Targa files,
+ one for SGI RGB files, one for DirectDraw surface (.dds) files,
+ and one for ImageIO-supplied formats such as .jpg and .png. Some
+ of these writers have certain limitations such as only being able
+ to write out textures stored in GL_RGB or GL_RGBA format. The DDS
+ writer supports fetching and writing to disk of texture data in
+ DXTn compressed format. Whether this will occur is dependent on
+ whether the texture's internal format is one of the DXTn
+ compressed formats and whether the target file is .dds format.
*/
public class TextureIO {
@@ -396,10 +412,11 @@ public class TextureIO {
* @param data the texture data to turn into an OpenGL texture
* @throws GLException if no OpenGL context is current or if an
* OpenGL error occurred
+ * @throws IllegalArgumentException if the passed TextureData was null
*/
- public static Texture newTexture(TextureData data) throws GLException {
+ public static Texture newTexture(TextureData data) throws GLException, IllegalArgumentException {
if (data == null) {
- return null;
+ throw new IllegalArgumentException("Null TextureData");
}
return new Texture(data);
}
@@ -513,6 +530,120 @@ public class TextureIO {
return new Texture(target);
}
+ /**
+ * Writes the given texture to a file. The type of the file is
+ * inferred from its suffix. An OpenGL context must be current in
+ * order to fetch the texture data back from the OpenGL pipeline.
+ * This method causes the specified Texture to be bound to the
+ * GL_TEXTURE_2D state. If no suitable writer for the requested file
+ * format was found, throws an IOException. <P>
+ *
+ * Reasonable attempts are made to produce good results in the
+ * resulting images. The Targa, SGI and ImageIO writers produce
+ * results in the correct vertical orientation for those file
+ * formats. The DDS writer performs no vertical flip of the data,
+ * even in uncompressed mode. (It is impossible to perform such a
+ * vertical flip with compressed data.) Applications should keep
+ * this in mind when using this routine to save textures to disk for
+ * later re-loading. <P>
+ *
+ * Any mipmaps for the specified texture are currently discarded
+ * when it is written to disk, regardless of whether the underlying
+ * file format supports multiple mipmaps in a given file.
+ *
+ * @throws IOException if an error occurred during writing or no
+ * suitable writer was found
+ * @throws GLException if no OpenGL context was current or an
+ * OpenGL-related error occurred
+ */
+ public static void write(Texture texture, File file) throws IOException, GLException {
+ if (texture.getTarget() != GL.GL_TEXTURE_2D) {
+ throw new GLException("Only GL_TEXTURE_2D textures are supported");
+ }
+
+ // First fetch the texture data
+ GL gl = GLU.getCurrentGL();
+
+ texture.bind();
+ int internalFormat = glGetTexLevelParameteri(GL.GL_TEXTURE_2D, 0, GL.GL_TEXTURE_INTERNAL_FORMAT);
+ int width = glGetTexLevelParameteri(GL.GL_TEXTURE_2D, 0, GL.GL_TEXTURE_WIDTH);
+ int height = glGetTexLevelParameteri(GL.GL_TEXTURE_2D, 0, GL.GL_TEXTURE_HEIGHT);
+ int border = glGetTexLevelParameteri(GL.GL_TEXTURE_2D, 0, GL.GL_TEXTURE_BORDER);
+ TextureData data = null;
+ if (internalFormat == GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT ||
+ internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
+ internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
+ internalFormat == GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) {
+ // Fetch using glGetCompressedTexImage
+ int size = glGetTexLevelParameteri(GL.GL_TEXTURE_2D, 0, GL.GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
+ ByteBuffer res = ByteBuffer.allocate(size);
+ gl.glGetCompressedTexImage(GL.GL_TEXTURE_2D, 0, res);
+ data = new TextureData(internalFormat, width, height, border, internalFormat, GL.GL_UNSIGNED_BYTE,
+ false, true, true, res, null);
+ } else {
+ int bytesPerPixel = 0;
+ int fetchedFormat = 0;
+ switch (internalFormat) {
+ case GL.GL_RGB:
+ case GL.GL_BGR:
+ case GL.GL_RGB8:
+ bytesPerPixel = 3;
+ fetchedFormat = GL.GL_RGB;
+ break;
+ case GL.GL_RGBA:
+ case GL.GL_BGRA:
+ case GL.GL_ABGR_EXT:
+ case GL.GL_RGBA8:
+ bytesPerPixel = 4;
+ fetchedFormat = GL.GL_RGBA;
+ break;
+ default:
+ throw new IOException("Unsupported texture internal format 0x" + Integer.toHexString(internalFormat));
+ }
+
+ // Fetch using glGetTexImage
+ int packAlignment = glGetInteger(GL.GL_PACK_ALIGNMENT);
+ int packRowLength = glGetInteger(GL.GL_PACK_ROW_LENGTH);
+ int packSkipRows = glGetInteger(GL.GL_PACK_SKIP_ROWS);
+ int packSkipPixels = glGetInteger(GL.GL_PACK_SKIP_PIXELS);
+ int packSwapBytes = glGetInteger(GL.GL_PACK_SWAP_BYTES);
+
+ gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, 1);
+ gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, 0);
+ gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, 0);
+ gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, 0);
+ gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, 0);
+
+ ByteBuffer res = ByteBuffer.allocate((width + (2 * border)) *
+ (height + (2 * border)) *
+ bytesPerPixel);
+ System.err.println("Allocated buffer of size " + res.remaining() + " for fetched image (" +
+ ((fetchedFormat == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA") + ")");
+ gl.glGetTexImage(GL.GL_TEXTURE_2D, 0, fetchedFormat, GL.GL_UNSIGNED_BYTE, res);
+
+ gl.glPixelStorei(GL.GL_PACK_ALIGNMENT, packAlignment);
+ gl.glPixelStorei(GL.GL_PACK_ROW_LENGTH, packRowLength);
+ gl.glPixelStorei(GL.GL_PACK_SKIP_ROWS, packSkipRows);
+ gl.glPixelStorei(GL.GL_PACK_SKIP_PIXELS, packSkipPixels);
+ gl.glPixelStorei(GL.GL_PACK_SWAP_BYTES, packSwapBytes);
+
+ data = new TextureData(internalFormat, width, height, border, fetchedFormat, GL.GL_UNSIGNED_BYTE,
+ false, false, false, res, null);
+
+ System.out.println("data.getPixelFormat() = " +
+ ((data.getPixelFormat() == GL.GL_RGB) ? "GL_RGB" : "GL_RGBA"));
+ }
+
+ for (Iterator iter = textureWriters.iterator(); iter.hasNext(); ) {
+ TextureWriter writer = (TextureWriter) iter.next();
+ if (writer.write(file, data)) {
+ return;
+ }
+ }
+
+ throw new IOException("No suitable texture writer found");
+ }
+
//----------------------------------------------------------------------
// Helper function for above TextureProviders
/**
@@ -540,8 +671,25 @@ public class TextureIO {
return toLowerCase(filename.substring(lastDot + 1));
}
- // FIXME: add texture writing capabilities
- // public void writeTextureToFile(Texture texture, File file, boolean saveUncompressed) throws IOException, GLException;
+ //----------------------------------------------------------------------
+ // Helper function which may be more generally useful
+ //
+
+ /** Flips the supplied BufferedImage vertically. This is often a
+ necessary conversion step to display a Java2D image correctly
+ with OpenGL and vice versa. */
+ public static void flipImageVertically(BufferedImage image) {
+ WritableRaster raster = image.getRaster();
+ Object scanline1 = null;
+ Object scanline2 = null;
+
+ for (int i = 0; i < image.getHeight() / 2; i++) {
+ scanline1 = raster.getDataElements(0, i, image.getWidth(), 1, scanline1);
+ scanline2 = raster.getDataElements(0, image.getHeight() - i - 1, image.getWidth(), 1, scanline2);
+ raster.setDataElements(0, i, image.getWidth(), 1, scanline2);
+ raster.setDataElements(0, image.getHeight() - i - 1, image.getWidth(), 1, scanline1);
+ }
+ }
//----------------------------------------------------------------------
// SPI support
@@ -556,11 +704,21 @@ public class TextureIO {
textureProviders.add(0, provider);
}
+ /** Adds a TextureWriter to support writing of a new file
+ format. */
+ public static void addTextureWriter(TextureWriter writer) {
+ // Must always add at the front so the ImageIO writer is last,
+ // so we don't accidentally use it instead of a user's possibly
+ // more optimal writer
+ textureWriters.add(0, writer);
+ }
+
//----------------------------------------------------------------------
// Internals only below this point
//
private static List/*<TextureProvider>*/ textureProviders = new ArrayList/*<TextureProvider>*/();
+ private static List/*<TextureWriter>*/ textureWriters = new ArrayList/*<TextureWriter>*/();
static {
// ImageIO provider, the fall-back, must be the first one added
@@ -570,6 +728,14 @@ public class TextureIO {
addTextureProvider(new DDSTextureProvider());
addTextureProvider(new SGITextureProvider());
addTextureProvider(new TGATextureProvider());
+
+ // ImageIO writer, the fall-back, must be the first one added
+ textureWriters.add(new IIOTextureWriter());
+
+ // Other special-case writers
+ addTextureWriter(new DDSTextureWriter());
+ addTextureWriter(new SGITextureWriter());
+ addTextureWriter(new TGATextureWriter());
}
// Implementation methods
@@ -595,7 +761,8 @@ public class TextureIO {
return data;
}
}
- return null;
+
+ throw new IOException("No suitable reader for given file");
}
private static TextureData newTextureDataImpl(InputStream stream,
@@ -626,7 +793,7 @@ public class TextureIO {
}
}
- return null;
+ throw new IOException("No suitable reader for given stream");
}
private static TextureData newTextureDataImpl(URL url,
@@ -652,7 +819,7 @@ public class TextureIO {
}
}
- return null;
+ throw new IOException("No suitable reader for given URL");
}
private static TextureData newTextureDataImpl(BufferedImage image,
@@ -713,12 +880,11 @@ public class TextureIO {
String fileSuffix) throws IOException {
if (DDS.equals(fileSuffix) ||
DDS.equals(getFileSuffix(file))) {
- final DDSReader reader = new DDSReader();
- reader.loadFile(file);
- DDSReader.ImageInfo info = reader.getMipMap(0);
+ final DDSImage image = DDSImage.read(file);
+ DDSImage.ImageInfo info = image.getMipMap(0);
if (pixelFormat == 0) {
- switch (reader.getPixelFormat()) {
- case DDSReader.D3DFMT_R8G8B8:
+ switch (image.getPixelFormat()) {
+ case DDSImage.D3DFMT_R8G8B8:
pixelFormat = GL.GL_RGB;
break;
default:
@@ -728,23 +894,23 @@ public class TextureIO {
}
if (info.isCompressed()) {
switch (info.getCompressionFormat()) {
- case DDSReader.D3DFMT_DXT1:
+ case DDSImage.D3DFMT_DXT1:
internalFormat = GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
break;
- case DDSReader.D3DFMT_DXT3:
+ case DDSImage.D3DFMT_DXT3:
internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
- case DDSReader.D3DFMT_DXT5:
+ case DDSImage.D3DFMT_DXT5:
internalFormat = GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
throw new RuntimeException("Unsupported DDS compression format \"" +
- DDSReader.getCompressionFormatName(info.getCompressionFormat()) + "\"");
+ DDSImage.getCompressionFormatName(info.getCompressionFormat()) + "\"");
}
}
if (internalFormat == 0) {
- switch (reader.getPixelFormat()) {
- case DDSReader.D3DFMT_R8G8B8:
+ switch (image.getPixelFormat()) {
+ case DDSImage.D3DFMT_R8G8B8:
pixelFormat = GL.GL_RGB;
break;
default:
@@ -754,14 +920,14 @@ public class TextureIO {
}
TextureData.Flusher flusher = new TextureData.Flusher() {
public void flush() {
- reader.close();
+ image.close();
}
};
TextureData data;
- if (mipmap && reader.getNumMipMaps() > 0) {
- Buffer[] mipmapData = new Buffer[reader.getNumMipMaps()];
- for (int i = 0; i < reader.getNumMipMaps(); i++) {
- mipmapData[i] = reader.getMipMap(i).getData();
+ if (mipmap && image.getNumMipMaps() > 0) {
+ Buffer[] mipmapData = new Buffer[image.getNumMipMaps()];
+ for (int i = 0; i < image.getNumMipMaps(); i++) {
+ mipmapData[i] = image.getMipMap(i).getData();
}
data = new TextureData(internalFormat,
info.getWidth(),
@@ -909,7 +1075,7 @@ public class TextureIO {
mipmap,
false,
false,
- ByteBuffer.wrap(image.getData()),
+ image.getData(),
null);
}
@@ -918,9 +1084,229 @@ public class TextureIO {
}
//----------------------------------------------------------------------
+ // ImageIO texture writer
+ //
+ static class IIOTextureWriter implements TextureWriter {
+ public boolean write(File file,
+ TextureData data) throws IOException {
+ int pixelFormat = data.getPixelFormat();
+ int pixelType = data.getPixelType();
+ if ((pixelFormat == GL.GL_RGB ||
+ pixelFormat == GL.GL_RGBA) &&
+ (pixelType == GL.GL_BYTE ||
+ pixelType == GL.GL_UNSIGNED_BYTE)) {
+ // Convert TextureData to appropriate BufferedImage
+ // FIXME: almost certainly not obeying correct pixel order
+ BufferedImage image = new BufferedImage(data.getWidth(), data.getHeight(),
+ (pixelFormat == GL.GL_RGB) ?
+ BufferedImage.TYPE_3BYTE_BGR :
+ BufferedImage.TYPE_4BYTE_ABGR);
+ byte[] imageData = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
+ ByteBuffer buf = (ByteBuffer) data.getBuffer();
+ if (buf == null) {
+ buf = (ByteBuffer) data.getMipmapData()[0];
+ }
+ buf.rewind();
+ buf.get(imageData);
+ buf.rewind();
+
+ // Swizzle image components to be correct
+ if (pixelFormat == GL.GL_RGB) {
+ for (int i = 0; i < imageData.length; i += 3) {
+ byte red = imageData[i + 0];
+ byte blue = imageData[i + 2];
+ imageData[i + 0] = blue;
+ imageData[i + 2] = red;
+ }
+ } else {
+ for (int i = 0; i < imageData.length; i += 4) {
+ byte red = imageData[i + 0];
+ byte green = imageData[i + 1];
+ byte blue = imageData[i + 2];
+ byte alpha = imageData[i + 3];
+ imageData[i + 0] = alpha;
+ imageData[i + 1] = blue;
+ imageData[i + 2] = green;
+ imageData[i + 3] = red;
+ }
+ }
+
+ // Flip image vertically for the user's convenience
+ flipImageVertically(image);
+
+ // Happened to notice that writing RGBA images to JPEGS is broken
+ if (JPG.equals(getFileSuffix(file)) &&
+ image.getType() == BufferedImage.TYPE_4BYTE_ABGR) {
+ BufferedImage tmpImage = new BufferedImage(image.getWidth(), image.getHeight(),
+ BufferedImage.TYPE_3BYTE_BGR);
+ Graphics g = tmpImage.getGraphics();
+ g.drawImage(image, 0, 0, null);
+ g.dispose();
+ image = tmpImage;
+ }
+
+ return ImageIO.write(image, getFileSuffix(file), file);
+ }
+
+ throw new IOException("ImageIO writer doesn't support this pixel format / type (only GL_RGB/A + bytes)");
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // DDS texture writer
+ //
+ static class DDSTextureWriter implements TextureWriter {
+ public boolean write(File file,
+ TextureData data) throws IOException {
+ if (DDS.equals(getFileSuffix(file))) {
+ // See whether the DDS writer can handle this TextureData
+ int pixelFormat = data.getPixelFormat();
+ int pixelType = data.getPixelType();
+ if (pixelType != GL.GL_BYTE &&
+ pixelType != GL.GL_UNSIGNED_BYTE) {
+ throw new IOException("DDS writer only supports byte / unsigned byte textures");
+ }
+
+ int d3dFormat = 0;
+ // FIXME: some of these are probably not completely correct and would require swizzling
+ switch (pixelFormat) {
+ case GL.GL_RGB: d3dFormat = DDSImage.D3DFMT_R8G8B8; break;
+ case GL.GL_RGBA: d3dFormat = DDSImage.D3DFMT_A8R8G8B8; break;
+ case GL.GL_COMPRESSED_RGB_S3TC_DXT1_EXT: d3dFormat = DDSImage.D3DFMT_DXT1; break;
+ case GL.GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: throw new IOException("RGBA DXT1 not yet supported");
+ case GL.GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: d3dFormat = DDSImage.D3DFMT_DXT3; break;
+ case GL.GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: d3dFormat = DDSImage.D3DFMT_DXT5; break;
+ default: throw new IOException("Unsupported pixel format 0x" + Integer.toHexString(pixelFormat) + " by DDS writer");
+ }
+
+ ByteBuffer[] mipmaps = null;
+ if (data.getMipmapData() != null) {
+ mipmaps = new ByteBuffer[data.getMipmapData().length];
+ for (int i = 0; i < mipmaps.length; i++) {
+ mipmaps[i] = (ByteBuffer) data.getMipmapData()[i];
+ }
+ } else {
+ mipmaps = new ByteBuffer[] { (ByteBuffer) data.getBuffer() };
+ }
+
+ DDSImage image = DDSImage.createFromData(d3dFormat,
+ data.getWidth(),
+ data.getHeight(),
+ mipmaps);
+ image.write(file);
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // SGI (rgb) texture writer
+ //
+ static class SGITextureWriter implements TextureWriter {
+ public boolean write(File file,
+ TextureData data) throws IOException {
+ String fileSuffix = getFileSuffix(file);
+ if (SGI.equals(fileSuffix) ||
+ SGI_RGB.equals(fileSuffix)) {
+ // See whether the SGI writer can handle this TextureData
+ int pixelFormat = data.getPixelFormat();
+ int pixelType = data.getPixelType();
+ if ((pixelFormat == GL.GL_RGB ||
+ pixelFormat == GL.GL_RGBA) &&
+ (pixelType == GL.GL_BYTE ||
+ pixelType == GL.GL_UNSIGNED_BYTE)) {
+ ByteBuffer buf = ((data.getBuffer() != null) ?
+ (ByteBuffer) data.getBuffer() :
+ (ByteBuffer) data.getMipmapData()[0]);
+ byte[] bytes;
+ if (buf.hasArray()) {
+ bytes = buf.array();
+ } else {
+ buf.rewind();
+ bytes = new byte[buf.remaining()];
+ buf.get(bytes);
+ buf.rewind();
+ }
+
+ SGIImage image = SGIImage.createFromData(data.getWidth(),
+ data.getHeight(),
+ (pixelFormat == GL.GL_RGBA),
+ bytes);
+ image.write(file, false);
+ return true;
+ }
+
+ throw new IOException("SGI writer doesn't support this pixel format / type (only GL_RGB/A + bytes)");
+ }
+
+ return false;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // TGA (Targa) texture writer
+
+ static class TGATextureWriter implements TextureWriter {
+ public boolean write(File file,
+ TextureData data) throws IOException {
+ if (TGA.equals(getFileSuffix(file))) {
+ // See whether the TGA writer can handle this TextureData
+ int pixelFormat = data.getPixelFormat();
+ int pixelType = data.getPixelType();
+ if ((pixelFormat == GL.GL_RGB ||
+ pixelFormat == GL.GL_RGBA) &&
+ (pixelType == GL.GL_BYTE ||
+ pixelType == GL.GL_UNSIGNED_BYTE)) {
+ ByteBuffer buf = ((data.getBuffer() != null) ?
+ (ByteBuffer) data.getBuffer() :
+ (ByteBuffer) data.getMipmapData()[0]);
+ // Must reverse order of red and blue channels to get correct results
+ int skip = ((pixelFormat == GL.GL_RGB) ? 3 : 4);
+ for (int i = 0; i < buf.remaining(); i += skip) {
+ byte red = buf.get(i + 0);
+ byte blue = buf.get(i + 2);
+ buf.put(i + 0, blue);
+ buf.put(i + 2, red);
+ }
+
+ TGAImage image = TGAImage.createFromData(data.getWidth(),
+ data.getHeight(),
+ (pixelFormat == GL.GL_RGBA),
+ false,
+ ((data.getBuffer() != null) ?
+ (ByteBuffer) data.getBuffer() :
+ (ByteBuffer) data.getMipmapData()[0]));
+ image.write(file);
+ return true;
+ }
+
+ throw new IOException("TGA writer doesn't support this pixel format / type (only GL_RGB/A + bytes)");
+ }
+
+ return false;
+ }
+ }
+
+ //----------------------------------------------------------------------
// Helper routines
//
+ private static int glGetInteger(int pname) {
+ int[] tmp = new int[1];
+ GL gl = GLU.getCurrentGL();
+ gl.glGetIntegerv(pname, tmp, 0);
+ return tmp[0];
+ }
+
+ private static int glGetTexLevelParameteri(int target, int level, int pname) {
+ int[] tmp = new int[1];
+ GL gl = GLU.getCurrentGL();
+ gl.glGetTexLevelParameteriv(target, 0, pname, tmp, 0);
+ return tmp[0];
+ }
+
private static String toLowerCase(String arg) {
if (arg == null) {
return null;