aboutsummaryrefslogtreecommitdiffstats
path: root/src/classes/com/sun/opengl/util/texture/spi
diff options
context:
space:
mode:
authorKenneth Russel <[email protected]>2006-01-13 07:29:23 +0000
committerKenneth Russel <[email protected]>2006-01-13 07:29:23 +0000
commit16e23f079b1e5fdc15b64bf1593cbda8e8dcffc0 (patch)
tree8cf430f395f2a2691d152c9a61ca212d7a6c1f55 /src/classes/com/sun/opengl/util/texture/spi
parent688ffdc8cc190c51c3e7355a4893e3e475efa29f (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/spi')
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/DDSImage.java722
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java223
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/SGIImage.java671
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/TGAImage.java386
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/TextureProvider.java165
-rwxr-xr-xsrc/classes/com/sun/opengl/util/texture/spi/TextureWriter.java57
6 files changed, 2224 insertions, 0 deletions
diff --git a/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java
new file mode 100755
index 000000000..486515628
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/DDSImage.java
@@ -0,0 +1,722 @@
+/*
+ * 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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+
+/** A reader and writer for DirectDraw Surface (.dds) files, which are
+ used to describe textures. These files can contain multiple mipmap
+ levels in one file. This class is currently minimal and does not
+ support all of the possible file formats. */
+
+public class DDSImage {
+
+ /** Simple class describing images and data; does not encapsulate
+ image format information. User is responsible for transmitting
+ that information in another way. */
+
+ public static class ImageInfo {
+ private ByteBuffer data;
+ private int width;
+ private int height;
+ private boolean isCompressed;
+ private int compressionFormat;
+
+ public ImageInfo(ByteBuffer data, int width, int height, boolean compressed, int compressionFormat) {
+ this.data = data; this.width = width; this.height = height;
+ this.isCompressed = compressed; this.compressionFormat = compressionFormat;
+ }
+ public int getWidth() { return width; }
+ public int getHeight() { return height; }
+ public ByteBuffer getData() { return data; }
+ public boolean isCompressed() { return isCompressed; }
+ public int getCompressionFormat() {
+ if (!isCompressed())
+ throw new RuntimeException("Should not call unless compressed");
+ return compressionFormat;
+ }
+ }
+
+ private FileInputStream fis;
+ private FileChannel chan;
+ private ByteBuffer buf;
+ private Header header;
+
+ //
+ // Selected bits in header flags
+ //
+
+ public static final int DDSD_CAPS = 0x00000001; // Capacities are valid
+ public static final int DDSD_HEIGHT = 0x00000002; // Height is valid
+ public static final int DDSD_WIDTH = 0x00000004; // Width is valid
+ public static final int DDSD_PITCH = 0x00000008; // Pitch is valid
+ public static final int DDSD_BACKBUFFERCOUNT = 0x00000020; // Back buffer count is valid
+ public static final int DDSD_ZBUFFERBITDEPTH = 0x00000040; // Z-buffer bit depth is valid (shouldn't be used in DDSURFACEDESC2)
+ public static final int DDSD_ALPHABITDEPTH = 0x00000080; // Alpha bit depth is valid
+ public static final int DDSD_LPSURFACE = 0x00000800; // lpSurface is valid
+ public static final int DDSD_PIXELFORMAT = 0x00001000; // ddpfPixelFormat is valid
+ public static final int DDSD_MIPMAPCOUNT = 0x00020000; // Mip map count is valid
+ public static final int DDSD_LINEARSIZE = 0x00080000; // dwLinearSize is valid
+ public static final int DDSD_DEPTH = 0x00800000; // dwDepth is valid
+
+ public static final int DDPF_ALPHAPIXELS = 0x00000001; // Alpha channel is present
+ public static final int DDPF_ALPHA = 0x00000002; // Only contains alpha information
+ public static final int DDPF_FOURCC = 0x00000004; // FourCC code is valid
+ public static final int DDPF_PALETTEINDEXED4 = 0x00000008; // Surface is 4-bit color indexed
+ public static final int DDPF_PALETTEINDEXEDTO8 = 0x00000010; // Surface is indexed into a palette which stores indices
+ // into the destination surface's 8-bit palette
+ public static final int DDPF_PALETTEINDEXED8 = 0x00000020; // Surface is 8-bit color indexed
+ public static final int DDPF_RGB = 0x00000040; // RGB data is present
+ public static final int DDPF_COMPRESSED = 0x00000080; // Surface will accept pixel data in the format specified
+ // and compress it during the write
+ public static final int DDPF_RGBTOYUV = 0x00000100; // Surface will accept RGB data and translate it during
+ // the write to YUV data. The format of the data to be written
+ // will be contained in the pixel format structure. The DDPF_RGB
+ // flag will be set.
+ public static final int DDPF_YUV = 0x00000200; // Pixel format is YUV - YUV data in pixel format struct is valid
+ public static final int DDPF_ZBUFFER = 0x00000400; // Pixel format is a z buffer only surface
+ public static final int DDPF_PALETTEINDEXED1 = 0x00000800; // Surface is 1-bit color indexed
+ public static final int DDPF_PALETTEINDEXED2 = 0x00001000; // Surface is 2-bit color indexed
+ public static final int DDPF_ZPIXELS = 0x00002000; // Surface contains Z information in the pixels
+
+ // Selected bits in DDS capabilities flags
+ public static final int DDSCAPS_TEXTURE = 0x00001000; // Can be used as a texture
+ public static final int DDSCAPS_MIPMAP = 0x00400000; // Is one level of a mip-map
+
+ // Known pixel formats
+ public static final int D3DFMT_UNKNOWN = 0;
+ public static final int D3DFMT_R8G8B8 = 20;
+ public static final int D3DFMT_A8R8G8B8 = 21;
+ public static final int D3DFMT_X8R8G8B8 = 22;
+ // The following are also valid FourCC codes
+ public static final int D3DFMT_DXT1 = 0x31545844;
+ public static final int D3DFMT_DXT2 = 0x32545844;
+ public static final int D3DFMT_DXT3 = 0x33545844;
+ public static final int D3DFMT_DXT4 = 0x34545844;
+ public static final int D3DFMT_DXT5 = 0x35545844;
+
+ /** Reads a DirectDraw surface from the specified file name,
+ returning the resulting DDSImage. */
+ public static DDSImage read(String filename) throws IOException {
+ return read(new File(filename));
+ }
+
+ /** Reads a DirectDraw surface from the specified file, returning
+ the resulting DDSImage. */
+ public static DDSImage read(File file) throws IOException {
+ DDSImage image = new DDSImage();
+ image.readFromFile(file);
+ return image;
+ }
+
+ /** Closes open files and resources associated with the open
+ DDSImage. No other methods may be called on this object once
+ this is called. */
+ public void close() {
+ try {
+ if (chan != null) {
+ chan.close();
+ chan = null;
+ }
+ if (fis != null) {
+ fis.close();
+ fis = null;
+ }
+ buf = null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Creates a new DDSImage from data supplied by the user. The
+ * resulting DDSImage can be written to disk using the write()
+ * method.
+ *
+ * @param d3dFormat the D3DFMT_ constant describing the data; it is
+ * assumed that it is packed tightly
+ * @param width the width in pixels of the topmost mipmap image
+ * @param height the height in pixels of the topmost mipmap image
+ * @param mipmapData the data for each mipmap level of the resulting
+ * DDSImage; either only one mipmap level should
+ * be specified, or they all must be
+ * @throws IllegalArgumentException if the data does not match the
+ * specified arguments
+ */
+ public static DDSImage createFromData(int d3dFormat,
+ int width,
+ int height,
+ ByteBuffer[] mipmapData) throws IllegalArgumentException {
+ DDSImage image = new DDSImage();
+ image.initFromData(d3dFormat, width, height, mipmapData);
+ return image;
+ }
+
+ /**
+ * Writes this DDSImage to the specified file name.
+ */
+ public void write(String filename) throws IOException {
+ write(new File(filename));
+ }
+
+ /**
+ * Writes this DDSImage to the specified file name.
+ */
+ public void write(File file) throws IOException {
+ FileOutputStream stream = new FileOutputStream(file);
+ FileChannel chan = stream.getChannel();
+ // Create ByteBuffer for header in case the start of our
+ // ByteBuffer isn't actually memory-mapped
+ ByteBuffer hdr = ByteBuffer.allocate(Header.writtenSize());
+ hdr.order(ByteOrder.LITTLE_ENDIAN);
+ header.write(hdr);
+ hdr.rewind();
+ chan.write(hdr);
+ buf.position(Header.writtenSize());
+ chan.write(buf);
+ chan.force(true);
+ chan.close();
+ stream.close();
+ }
+
+ /** Test for presence/absence of surface description flags (DDSD_*) */
+ public boolean isSurfaceDescFlagSet(int flag) {
+ return ((header.flags & flag) != 0);
+ }
+
+ /** Test for presence/absence of pixel format flags (DDPF_*) */
+ public boolean isPixelFormatFlagSet(int flag) {
+ return ((header.pfFlags & flag) != 0);
+ }
+
+ /** Gets the pixel format of this texture (D3DFMT_*) based on some
+ heuristics. Returns D3DFMT_UNKNOWN if could not recognize the
+ pixel format. */
+ public int getPixelFormat() {
+ if (isCompressed()) {
+ return getCompressionFormat();
+ } else if (isPixelFormatFlagSet(DDPF_RGB)) {
+ if (isPixelFormatFlagSet(DDPF_ALPHAPIXELS)) {
+ if (getDepth() == 32 &&
+ header.pfRBitMask == 0x00FF0000 &&
+ header.pfGBitMask == 0x0000FF00 &&
+ header.pfBBitMask == 0x000000FF &&
+ header.pfABitMask == 0xFF000000) {
+ return D3DFMT_A8R8G8B8;
+ }
+ } else {
+ if (getDepth() == 24 &&
+ header.pfRBitMask == 0x00FF0000 &&
+ header.pfGBitMask == 0x0000FF00 &&
+ header.pfBBitMask == 0x000000FF) {
+ return D3DFMT_R8G8B8;
+ } else if (getDepth() == 32 &&
+ header.pfRBitMask == 0x00FF0000 &&
+ header.pfGBitMask == 0x0000FF00 &&
+ header.pfBBitMask == 0x000000FF) {
+ return D3DFMT_X8R8G8B8;
+ }
+ }
+ }
+
+ return D3DFMT_UNKNOWN;
+ }
+
+ /** Indicates whether this texture is compressed. */
+ public boolean isCompressed() {
+ return (isPixelFormatFlagSet(DDPF_FOURCC));
+ }
+
+ /** If this surface is compressed, returns the kind of compression
+ used (DXT1..DXT5). */
+ public int getCompressionFormat() {
+ return header.pfFourCC;
+ }
+
+ /** Width of the texture (or the top-most mipmap if mipmaps are
+ present) */
+ public int getWidth() {
+ return header.width;
+ }
+
+ /** Height of the texture (or the top-most mipmap if mipmaps are
+ present) */
+ public int getHeight() {
+ return header.height;
+ }
+
+ /** Total number of bits per pixel. Only valid if DDPF_RGB is
+ present. For A8R8G8B8, would be 32. */
+ public int getDepth() {
+ return header.pfRGBBitCount;
+ }
+
+ /** Number of mip maps in the texture */
+ public int getNumMipMaps() {
+ if (!isSurfaceDescFlagSet(DDSD_MIPMAPCOUNT)) {
+ return 0;
+ }
+ return header.mipMapCountOrAux;
+ }
+
+ /** Gets the <i>i</i>th mipmap data (0..getNumMipMaps() - 1) */
+ public ImageInfo getMipMap(int map) {
+ if (getNumMipMaps() > 0 &&
+ ((map < 0) || (map >= getNumMipMaps()))) {
+ throw new RuntimeException("Illegal mipmap number " + map + " (0.." + (getNumMipMaps() - 1) + ")");
+ }
+
+ // Figure out how far to seek
+ int seek = Header.writtenSize();
+ for (int i = 0; i < map; i++) {
+ seek += mipMapSizeInBytes(i);
+ }
+ buf.limit(seek + mipMapSizeInBytes(map));
+ buf.position(seek);
+ ByteBuffer next = buf.slice();
+ buf.position(0);
+ buf.limit(buf.capacity());
+ return new ImageInfo(next, mipMapWidth(map), mipMapHeight(map), isCompressed(), getCompressionFormat());
+ }
+
+ /** Returns an array of ImageInfos corresponding to all mipmap
+ levels of this DDS file. */
+ public ImageInfo[] getAllMipMaps() {
+ int numLevels = getNumMipMaps();
+ if (numLevels == 0) {
+ numLevels = 1;
+ }
+ ImageInfo[] result = new ImageInfo[numLevels];
+ for (int i = 0; i < numLevels; i++) {
+ result[i] = getMipMap(i);
+ }
+ return result;
+ }
+
+ /** Converts e.g. DXT1 compression format constant (see {@link
+ #getCompressionFormat}) into "DXT1". */
+ public static String getCompressionFormatName(int compressionFormat) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < 4; i++) {
+ char c = (char) (compressionFormat & 0xFF);
+ buf.append(c);
+ compressionFormat = compressionFormat >> 8;
+ }
+ return buf.toString();
+ }
+
+ public void debugPrint() {
+ PrintStream tty = System.err;
+ tty.println("Compressed texture: " + isCompressed());
+ if (isCompressed()) {
+ int fmt = getCompressionFormat();
+ String name = getCompressionFormatName(fmt);
+ tty.println("Compression format: 0x" + Integer.toHexString(fmt) + " (" + name + ")");
+ }
+ tty.println("Width: " + header.width + " Height: " + header.height);
+ tty.println("header.pitchOrLinearSize: " + header.pitchOrLinearSize);
+ tty.println("header.pfRBitMask: 0x" + Integer.toHexString(header.pfRBitMask));
+ tty.println("header.pfGBitMask: 0x" + Integer.toHexString(header.pfGBitMask));
+ tty.println("header.pfBBitMask: 0x" + Integer.toHexString(header.pfBBitMask));
+ tty.println("SurfaceDesc flags:");
+ boolean recognizedAny = false;
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_CAPS, "DDSD_CAPS");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_HEIGHT, "DDSD_HEIGHT");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_WIDTH, "DDSD_WIDTH");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PITCH, "DDSD_PITCH");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_BACKBUFFERCOUNT, "DDSD_BACKBUFFERCOUNT");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ZBUFFERBITDEPTH, "DDSD_ZBUFFERBITDEPTH");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_ALPHABITDEPTH, "DDSD_ALPHABITDEPTH");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LPSURFACE, "DDSD_LPSURFACE");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_PIXELFORMAT, "DDSD_PIXELFORMAT");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_MIPMAPCOUNT, "DDSD_MIPMAPCOUNT");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_LINEARSIZE, "DDSD_LINEARSIZE");
+ recognizedAny |= printIfRecognized(tty, header.flags, DDSD_DEPTH, "DDSD_DEPTH");
+ if (!recognizedAny) {
+ tty.println("(none)");
+ }
+ tty.println("Raw SurfaceDesc flags: 0x" + Integer.toHexString(header.flags));
+ tty.println("Pixel format flags:");
+ recognizedAny = false;
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHAPIXELS, "DDPF_ALPHAPIXELS");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ALPHA, "DDPF_ALPHA");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_FOURCC, "DDPF_FOURCC");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED4, "DDPF_PALETTEINDEXED4");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXEDTO8, "DDPF_PALETTEINDEXEDTO8");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED8, "DDPF_PALETTEINDEXED8");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGB, "DDPF_RGB");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_COMPRESSED, "DDPF_COMPRESSED");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_RGBTOYUV, "DDPF_RGBTOYUV");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_YUV, "DDPF_YUV");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZBUFFER, "DDPF_ZBUFFER");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED1, "DDPF_PALETTEINDEXED1");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_PALETTEINDEXED2, "DDPF_PALETTEINDEXED2");
+ recognizedAny |= printIfRecognized(tty, header.pfFlags, DDPF_ZPIXELS, "DDPF_ZPIXELS");
+ if (!recognizedAny) {
+ tty.println("(none)");
+ }
+ tty.println("Raw pixel format flags: 0x" + Integer.toHexString(header.pfFlags));
+ tty.println("Depth: " + getDepth());
+ tty.println("Number of mip maps: " + getNumMipMaps());
+ int fmt = getPixelFormat();
+ tty.print("Pixel format: ");
+ switch (fmt) {
+ case D3DFMT_R8G8B8: tty.println("D3DFMT_R8G8B8"); break;
+ case D3DFMT_A8R8G8B8: tty.println("D3DFMT_A8R8G8B8"); break;
+ case D3DFMT_X8R8G8B8: tty.println("D3DFMT_X8R8G8B8"); break;
+ case D3DFMT_DXT1: tty.println("D3DFMT_DXT1"); break;
+ case D3DFMT_DXT2: tty.println("D3DFMT_DXT2"); break;
+ case D3DFMT_DXT3: tty.println("D3DFMT_DXT3"); break;
+ case D3DFMT_DXT4: tty.println("D3DFMT_DXT4"); break;
+ case D3DFMT_DXT5: tty.println("D3DFMT_DXT5"); break;
+ case D3DFMT_UNKNOWN: tty.println("D3DFMT_UNKNOWN"); break;
+ default: tty.println("(unknown pixel format " + fmt + ")"); break;
+ }
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private static final int MAGIC = 0x20534444;
+
+ static class Header {
+ int size; // size of the DDSURFACEDESC structure
+ int flags; // determines what fields are valid
+ int height; // height of surface to be created
+ int width; // width of input surface
+ int pitchOrLinearSize;
+ int backBufferCountOrDepth;
+ int mipMapCountOrAux; // number of mip-map levels requested (in this context)
+ int alphaBitDepth; // depth of alpha buffer requested
+ int reserved1; // reserved
+ int surface; // pointer to the associated surface memory
+ // NOTE: following two entries are from DDCOLORKEY data structure
+ // Are overlaid with color for empty cubemap faces (unused in this reader)
+ int colorSpaceLowValue;
+ int colorSpaceHighValue;
+ int destBltColorSpaceLowValue;
+ int destBltColorSpaceHighValue;
+ int srcOverlayColorSpaceLowValue;
+ int srcOverlayColorSpaceHighValue;
+ int srcBltColorSpaceLowValue;
+ int srcBltColorSpaceHighValue;
+ // NOTE: following entries are from DDPIXELFORMAT data structure
+ // Are overlaid with flexible vertex format description of vertex
+ // buffers (unused in this reader)
+ int pfSize; // size of DDPIXELFORMAT structure
+ int pfFlags; // pixel format flags
+ int pfFourCC; // (FOURCC code)
+ // Following five entries have multiple interpretations, not just
+ // RGBA (but that's all we support right now)
+ int pfRGBBitCount; // how many bits per pixel
+ int pfRBitMask; // mask for red bits
+ int pfGBitMask; // mask for green bits
+ int pfBBitMask; // mask for blue bits
+ int pfABitMask; // mask for alpha channel
+ int ddsCaps1; // Texture and mip-map flags
+ int ddsCaps2; // Advanced capabilities, not yet used
+ int ddsCapsReserved1;
+ int ddsCapsReserved2;
+ int textureStage; // stage in multitexture cascade
+
+ void read(ByteBuffer buf) throws IOException {
+ int magic = buf.getInt();
+ if (magic != MAGIC) {
+ throw new IOException("Incorrect magic number 0x" +
+ Integer.toHexString(magic) +
+ " (expected " + MAGIC + ")");
+ }
+
+ size = buf.getInt();
+ flags = buf.getInt();
+ height = buf.getInt();
+ width = buf.getInt();
+ pitchOrLinearSize = buf.getInt();
+ backBufferCountOrDepth = buf.getInt();
+ mipMapCountOrAux = buf.getInt();
+ alphaBitDepth = buf.getInt();
+ reserved1 = buf.getInt();
+ surface = buf.getInt();
+ colorSpaceLowValue = buf.getInt();
+ colorSpaceHighValue = buf.getInt();
+ destBltColorSpaceLowValue = buf.getInt();
+ destBltColorSpaceHighValue = buf.getInt();
+ srcOverlayColorSpaceLowValue = buf.getInt();
+ srcOverlayColorSpaceHighValue = buf.getInt();
+ srcBltColorSpaceLowValue = buf.getInt();
+ srcBltColorSpaceHighValue = buf.getInt();
+ pfSize = buf.getInt();
+ pfFlags = buf.getInt();
+ pfFourCC = buf.getInt();
+ pfRGBBitCount = buf.getInt();
+ pfRBitMask = buf.getInt();
+ pfGBitMask = buf.getInt();
+ pfBBitMask = buf.getInt();
+ pfABitMask = buf.getInt();
+ ddsCaps1 = buf.getInt();
+ ddsCaps2 = buf.getInt();
+ ddsCapsReserved1 = buf.getInt();
+ ddsCapsReserved2 = buf.getInt();
+ textureStage = buf.getInt();
+ }
+
+ // buf must be in little-endian byte order
+ void write(ByteBuffer buf) {
+ buf.putInt(MAGIC);
+ buf.putInt(size);
+ buf.putInt(flags);
+ buf.putInt(height);
+ buf.putInt(width);
+ buf.putInt(pitchOrLinearSize);
+ buf.putInt(backBufferCountOrDepth);
+ buf.putInt(mipMapCountOrAux);
+ buf.putInt(alphaBitDepth);
+ buf.putInt(reserved1);
+ buf.putInt(surface);
+ buf.putInt(colorSpaceLowValue);
+ buf.putInt(colorSpaceHighValue);
+ buf.putInt(destBltColorSpaceLowValue);
+ buf.putInt(destBltColorSpaceHighValue);
+ buf.putInt(srcOverlayColorSpaceLowValue);
+ buf.putInt(srcOverlayColorSpaceHighValue);
+ buf.putInt(srcBltColorSpaceLowValue);
+ buf.putInt(srcBltColorSpaceHighValue);
+ buf.putInt(pfSize);
+ buf.putInt(pfFlags);
+ buf.putInt(pfFourCC);
+ buf.putInt(pfRGBBitCount);
+ buf.putInt(pfRBitMask);
+ buf.putInt(pfGBitMask);
+ buf.putInt(pfBBitMask);
+ buf.putInt(pfABitMask);
+ buf.putInt(ddsCaps1);
+ buf.putInt(ddsCaps2);
+ buf.putInt(ddsCapsReserved1);
+ buf.putInt(ddsCapsReserved2);
+ buf.putInt(textureStage);
+ }
+
+ private static final int size() {
+ return 124;
+ }
+
+ private static final int pfSize() {
+ return 32;
+ }
+
+ private static final int writtenSize() {
+ return 128;
+ }
+ }
+
+ private DDSImage() {
+ }
+
+ private void readFromFile(File file) throws IOException {
+ fis = new FileInputStream(file);
+ chan = fis.getChannel();
+ buf = chan.map(FileChannel.MapMode.READ_ONLY,
+ 0, (int) file.length());
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ header = new Header();
+ header.read(buf);
+ fixupHeader();
+ }
+
+ private void initFromData(int d3dFormat,
+ int width,
+ int height,
+ ByteBuffer[] mipmapData) throws IllegalArgumentException {
+ // Check size of mipmap data compared against format, width and
+ // height
+ int topmostMipmapSize = width * height;
+ int pitchOrLinearSize = width;
+ boolean isCompressed = false;
+ switch (d3dFormat) {
+ case D3DFMT_R8G8B8: topmostMipmapSize *= 3; pitchOrLinearSize *= 3; break;
+ case D3DFMT_A8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4; break;
+ case D3DFMT_X8R8G8B8: topmostMipmapSize *= 4; pitchOrLinearSize *= 4; break;
+ case D3DFMT_DXT1:
+ case D3DFMT_DXT2:
+ case D3DFMT_DXT3:
+ case D3DFMT_DXT4:
+ case D3DFMT_DXT5:
+ topmostMipmapSize = computeCompressedBlockSize(width, height, 1, d3dFormat);
+ pitchOrLinearSize = topmostMipmapSize;
+ isCompressed = true;
+ break;
+ default:
+ throw new IllegalArgumentException("d3dFormat must be one of the known formats");
+ }
+
+ // Now check the mipmaps against this size
+ int curSize = topmostMipmapSize;
+ int totalSize = 0;
+ for (int i = 0; i < mipmapData.length; i++) {
+ if (mipmapData[i].remaining() != curSize) {
+ throw new IllegalArgumentException("Mipmap level " + i +
+ " didn't match expected data size (expected " + curSize + ", got " +
+ mipmapData[i].remaining() + ")");
+ }
+ curSize /= 4;
+ totalSize += mipmapData[i].remaining();
+ }
+
+ // OK, create one large ByteBuffer to hold all of the mipmap data
+ totalSize += Header.writtenSize();
+ ByteBuffer buf = ByteBuffer.allocate(totalSize);
+ buf.position(Header.writtenSize());
+ for (int i = 0; i < mipmapData.length; i++) {
+ buf.put(mipmapData[i]);
+ }
+ this.buf = buf;
+
+ // Allocate and initialize a Header
+ header = new Header();
+ header.size = Header.size();
+ header.flags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
+ if (mipmapData.length > 1) {
+ header.flags |= DDSD_MIPMAPCOUNT;
+ header.mipMapCountOrAux = mipmapData.length;
+ }
+ header.width = width;
+ header.height = height;
+ if (isCompressed) {
+ header.flags |= DDSD_LINEARSIZE;
+ header.pfFlags |= DDPF_FOURCC;
+ header.pfFourCC = d3dFormat;
+ } else {
+ header.flags |= DDSD_PITCH;
+ // Figure out the various settings from the pixel format
+ header.pfFlags |= DDPF_RGB;
+ switch (d3dFormat) {
+ case D3DFMT_R8G8B8: header.pfRGBBitCount = 24; break;
+ case D3DFMT_A8R8G8B8: header.pfRGBBitCount = 32; header.pfFlags |= DDPF_ALPHAPIXELS; break;
+ case D3DFMT_X8R8G8B8: header.pfRGBBitCount = 32; break;
+ }
+ header.pfRBitMask = 0x00FF0000;
+ header.pfGBitMask = 0x0000FF00;
+ header.pfBBitMask = 0x000000FF;
+ if (d3dFormat == D3DFMT_A8R8G8B8) {
+ header.pfABitMask = 0xFF000000;
+ }
+ }
+ header.pitchOrLinearSize = pitchOrLinearSize;
+ header.pfSize = Header.pfSize();
+ // Not sure whether we can get away with leaving the rest of the
+ // header blank
+ }
+
+ // Microsoft doesn't follow their own specifications and the
+ // simplest conversion using the DxTex tool to e.g. a DXT3 texture
+ // results in an illegal .dds file without either DDSD_PITCH or
+ // DDSD_LINEARSIZE set in the header's flags. This code, adapted
+ // from the DevIL library, fixes up the header in these situations.
+ private void fixupHeader() {
+ if (isCompressed() && !isSurfaceDescFlagSet(DDSD_LINEARSIZE)) {
+ // Figure out how big the linear size should be
+ int depth = header.backBufferCountOrDepth;
+ if (depth == 0) {
+ depth = 1;
+ }
+
+ int blockSize = computeCompressedBlockSize(getWidth(), getHeight(), depth, getCompressionFormat());
+
+ header.pitchOrLinearSize = blockSize;
+ header.flags |= DDSD_LINEARSIZE;
+ }
+ }
+
+ private static int computeCompressedBlockSize(int width,
+ int height,
+ int depth,
+ int compressionFormat) {
+ int blockSize = ((width + 3)/4) * ((height + 3)/4) * ((depth + 3)/4);
+ switch (compressionFormat) {
+ case D3DFMT_DXT1: blockSize *= 8; break;
+ default: blockSize *= 16; break;
+ }
+ return blockSize;
+ }
+
+ private int mipMapWidth(int map) {
+ int width = getWidth();
+ for (int i = 0; i < map; i++) {
+ width >>= 1;
+ }
+ return width;
+ }
+
+ private int mipMapHeight(int map) {
+ int height = getHeight();
+ for (int i = 0; i < map; i++) {
+ height >>= 1;
+ }
+ return height;
+ }
+
+ private int mipMapSizeInBytes(int map) {
+ if (isCompressed()) {
+ if (!isSurfaceDescFlagSet(DDSD_LINEARSIZE)) {
+ throw new RuntimeException("Illegal compressed texture: DDSD_LINEARSIZE not specified in texture header");
+ }
+ int bytes = header.pitchOrLinearSize;
+ for (int i = 0; i < map; i++) {
+ bytes >>= 2;
+ }
+ return bytes;
+ } else {
+ int width = mipMapWidth(map);
+ int height = mipMapHeight(map);
+ return width * height * (getDepth() / 8);
+ }
+ }
+
+ private boolean printIfRecognized(PrintStream tty, int flags, int flag, String what) {
+ if ((flags & flag) != 0) {
+ tty.println(what);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java
new file mode 100755
index 000000000..edbb6e35e
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/LEDataInputStream.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2003 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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+
+/**
+ * Little Endian Data Input Stream.
+ *
+ * This class implements an input stream filter to allow reading
+ * of java native datatypes from an input stream which has those
+ * native datatypes stored in a little endian byte order.<p>
+ *
+ * This is the sister class of the DataInputStream which allows
+ * for reading of java native datatypes from an input stream with
+ * the datatypes stored in big endian byte order.<p>
+ *
+ * This class implements the minimum required and calls DataInputStream
+ * for some of the required methods for DataInput.<p>
+ *
+ * Not all methods are implemented due to lack of immediatte requirement
+ * for that functionality. It is not clear if it is ever going to be
+ * functionally required to be able to read UTF data in a LittleEndianManner<p>
+ *
+ * @author Robin Luiten
+ * @version 1.1 15/Dec/1997
+ */
+class LEDataInputStream extends FilterInputStream implements DataInput
+{
+ /**
+ * To reuse some of the non endian dependent methods from
+ * DataInputStreams methods.
+ */
+ DataInputStream dataIn;
+
+ public LEDataInputStream(InputStream in)
+ {
+ super(in);
+ dataIn = new DataInputStream(in);
+ }
+
+ public void close() throws IOException
+ {
+ dataIn.close(); // better close as we create it.
+ // this will close underlying as well.
+ }
+
+ public synchronized final int read(byte b[]) throws IOException
+ {
+ return dataIn.read(b, 0, b.length);
+ }
+
+ public synchronized final int read(byte b[], int off, int len) throws IOException
+ {
+ int rl = dataIn.read(b, off, len);
+ return rl;
+ }
+
+ public final void readFully(byte b[]) throws IOException
+ {
+ dataIn.readFully(b, 0, b.length);
+ }
+
+ public final void readFully(byte b[], int off, int len) throws IOException
+ {
+ dataIn.readFully(b, off, len);
+ }
+
+ public final int skipBytes(int n) throws IOException
+ {
+ return dataIn.skipBytes(n);
+ }
+
+ public final boolean readBoolean() throws IOException
+ {
+ int ch = dataIn.read();
+ if (ch < 0)
+ throw new EOFException();
+ return (ch != 0);
+ }
+
+ public final byte readByte() throws IOException
+ {
+ int ch = dataIn.read();
+ if (ch < 0)
+ throw new EOFException();
+ return (byte)(ch);
+ }
+
+ public final int readUnsignedByte() throws IOException
+ {
+ int ch = dataIn.read();
+ if (ch < 0)
+ throw new EOFException();
+ return ch;
+ }
+
+ public final short readShort() throws IOException
+ {
+ int ch1 = dataIn.read();
+ int ch2 = dataIn.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (short)((ch1 << 0) + (ch2 << 8));
+ }
+
+ public final int readUnsignedShort() throws IOException
+ {
+ int ch1 = dataIn.read();
+ int ch2 = dataIn.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (ch1 << 0) + (ch2 << 8);
+ }
+
+ public final char readChar() throws IOException
+ {
+ int ch1 = dataIn.read();
+ int ch2 = dataIn.read();
+ if ((ch1 | ch2) < 0)
+ throw new EOFException();
+ return (char)((ch1 << 0) + (ch2 << 8));
+ }
+
+ public final int readInt() throws IOException
+ {
+ int ch1 = dataIn.read();
+ int ch2 = dataIn.read();
+ int ch3 = dataIn.read();
+ int ch4 = dataIn.read();
+ if ((ch1 | ch2 | ch3 | ch4) < 0)
+ throw new EOFException();
+ return ((ch1 << 0) + (ch2 << 8) + (ch3 << 16) + (ch4 << 24));
+ }
+
+ public final long readLong() throws IOException
+ {
+ int i1 = readInt();
+ int i2 = readInt();
+ return ((long)(i1) & 0xFFFFFFFFL) + (i2 << 32);
+ }
+
+ public final float readFloat() throws IOException
+ {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ public final double readDouble() throws IOException
+ {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ /**
+ * dont call this it is not implemented.
+ * @return empty new string
+ **/
+ public final String readLine() throws IOException
+ {
+ return new String();
+ }
+
+ /**
+ * dont call this it is not implemented
+ * @return empty new string
+ **/
+ public final String readUTF() throws IOException
+ {
+ return new String();
+ }
+
+ /**
+ * dont call this it is not implemented
+ * @return empty new string
+ **/
+ public final static String readUTF(DataInput in) throws IOException
+ {
+ return new String();
+ }
+}
+
diff --git a/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java
new file mode 100755
index 000000000..12523eb18
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/SGIImage.java
@@ -0,0 +1,671 @@
+/*
+ * Portions 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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.*;
+import javax.media.opengl.*;
+import com.sun.opengl.util.*;
+
+// Test harness
+import java.awt.image.*;
+import javax.swing.*;
+
+/** <p> Reads and writes SGI RGB/RGBA images. </p>
+
+ <p> Written from <a href =
+ "http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/">Paul
+ Bourke's adaptation</a> of the <a href =
+ "http://astronomy.swin.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html">SGI
+ specification</a>. </p>
+*/
+
+public class SGIImage {
+ private Header header;
+ private int format;
+ private byte[] data;
+ // Used for decoding RLE-compressed images
+ private int[] rowStart;
+ private int[] rowSize;
+ private int rleEnd;
+ private byte[] tmpData;
+ private byte[] tmpRead;
+
+ private static final int MAGIC = 474;
+
+ static class Header {
+ short magic; // IRIS image file magic number
+ // This should be decimal 474
+ byte storage; // Storage format
+ // 0 for uncompressed
+ // 1 for RLE compression
+ byte bpc; // Number of bytes per pixel channel
+ // Legally 1 or 2
+ short dimension; // Number of dimensions
+ // Legally 1, 2, or 3
+ // 1 means a single row, XSIZE long
+ // 2 means a single 2D image
+ // 3 means multiple 2D images
+ short xsize; // X size in pixels
+ short ysize; // Y size in pixels
+ short zsize; // Number of channels
+ // 1 indicates greyscale
+ // 3 indicates RGB
+ // 4 indicates RGB and Alpha
+ int pixmin; // Minimum pixel value
+ // This is the lowest pixel value in the image
+ int pixmax; // Maximum pixel value
+ // This is the highest pixel value in the image
+ int dummy; // Ignored
+ // Normally set to 0
+ String imagename; // Image name; 80 bytes long
+ // Must be null terminated, therefore at most 79 bytes
+ int colormap; // Colormap ID
+ // 0 - normal mode
+ // 1 - dithered, 3 mits for red and green, 2 for blue, obsolete
+ // 2 - index colour, obsolete
+ // 3 - not an image but a colourmap
+ // 404 bytes char DUMMY Ignored
+ // Should be set to 0, makes the header 512 bytes.
+
+ Header() {
+ magic = MAGIC;
+ }
+
+ Header(DataInputStream in) throws IOException {
+ magic = in.readShort();
+ storage = in.readByte();
+ bpc = in.readByte();
+ dimension = in.readShort();
+ xsize = in.readShort();
+ ysize = in.readShort();
+ zsize = in.readShort();
+ pixmin = in.readInt();
+ pixmax = in.readInt();
+ dummy = in.readInt();
+ byte[] tmpname = new byte[80];
+ in.read(tmpname);
+ int numChars = 0;
+ while (tmpname[numChars++] != 0);
+ imagename = new String(tmpname, 0, numChars);
+ colormap = in.readInt();
+ byte[] tmp = new byte[404];
+ in.read(tmp);
+ }
+
+ public String toString() {
+ return ("magic: " + magic +
+ " storage: " + (int) storage +
+ " bpc: " + (int) bpc +
+ " dimension: " + dimension +
+ " xsize: " + xsize +
+ " ysize: " + ysize +
+ " zsize: " + zsize +
+ " pixmin: " + pixmin +
+ " pixmax: " + pixmax +
+ " imagename: " + imagename +
+ " colormap: " + colormap);
+ }
+ }
+
+ private SGIImage(Header header) {
+ this.header = header;
+ }
+
+ /** Reads an SGI image from the specified file. */
+ public static SGIImage read(String filename) throws IOException {
+ return read(new FileInputStream(filename));
+ }
+
+ /** Reads an SGI image from the specified InputStream. */
+ public static SGIImage read(InputStream in) throws IOException {
+ DataInputStream dIn = new DataInputStream(new BufferedInputStream(in));
+
+ Header header = new Header(dIn);
+ SGIImage res = new SGIImage(header);
+ res.decodeImage(dIn);
+ return res;
+ }
+
+ /** Writes this SGIImage to the specified file name. If
+ flipVertically is set, outputs the scanlines from top to bottom
+ rather than the default bottom to top order. */
+ public void write(String filename, boolean flipVertically) throws IOException {
+ write(new File(filename), flipVertically);
+ }
+
+ /** Writes this SGIImage to the specified file. If flipVertically is
+ set, outputs the scanlines from top to bottom rather than the
+ default bottom to top order. */
+ public void write(File file, boolean flipVertically) throws IOException {
+ writeImage(file, data, header.xsize, header.ysize, header.zsize, flipVertically);
+ }
+
+ /** Creates an SGIImage from the specified data in either RGB or
+ RGBA format. */
+ public static SGIImage createFromData(int width,
+ int height,
+ boolean hasAlpha,
+ byte[] data) {
+ Header header = new Header();
+ header.xsize = (short) width;
+ header.ysize = (short) height;
+ header.zsize = (short) (hasAlpha ? 4 : 3);
+ SGIImage image = new SGIImage(header);
+ image.data = data;
+ return image;
+ }
+
+ /** Determines from the magic number whether the given InputStream
+ points to an SGI RGB image. The given InputStream must return
+ true from markSupported() and support a minimum of two bytes
+ of read-ahead. */
+ public static boolean isSGIImage(InputStream in) throws IOException {
+ if (!(in instanceof BufferedInputStream)) {
+ in = new BufferedInputStream(in);
+ }
+ if (!in.markSupported()) {
+ throw new IOException("Can not test non-destructively whether given InputStream is an SGI RGB image");
+ }
+ DataInputStream dIn = new DataInputStream(in);
+ dIn.mark(4);
+ short magic = dIn.readShort();
+ dIn.reset();
+ return (magic == MAGIC);
+ }
+
+ /** Returns the width of the image. */
+ public int getWidth() {
+ return header.xsize;
+ }
+
+ /** Returns the height of the image. */
+ public int getHeight() {
+ return header.ysize;
+ }
+
+ /** Returns the OpenGL format for this texture; e.g. GL.GL_RGB or GL.GL_RGBA. */
+ public int getFormat() {
+ return format;
+ }
+
+ /** Returns the raw data for this texture in the correct
+ (bottom-to-top) order for calls to glTexImage2D. */
+ public byte[] getData() { return data; }
+
+ public String toString() {
+ return header.toString();
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void decodeImage(DataInputStream in) throws IOException {
+ if (header.storage == 1) {
+ // Read RLE compression data; row starts and sizes
+ int x = header.ysize * header.zsize;
+ rowStart = new int[x];
+ rowSize = new int[x];
+ rleEnd = 4 * 2 * x + 512;
+ for (int i = 0; i < x; i++) {
+ rowStart[i] = in.readInt();
+ }
+ for (int i = 0; i < x; i++) {
+ rowSize[i] = in.readInt();
+ }
+ tmpRead = new byte[header.xsize * 256];
+ }
+ tmpData = readAll(in);
+
+ int xsize = header.xsize;
+ int ysize = header.ysize;
+ int zsize = header.zsize;
+ int lptr = 0;
+
+ data = new byte[xsize * ysize * 4];
+ byte[] rbuf = new byte[xsize];
+ byte[] gbuf = new byte[xsize];
+ byte[] bbuf = new byte[xsize];
+ byte[] abuf = new byte[xsize];
+ for (int y = 0; y < ysize; y++) {
+ if (zsize >= 4) {
+ getRow(rbuf, y, 0);
+ getRow(gbuf, y, 1);
+ getRow(bbuf, y, 2);
+ getRow(abuf, y, 3);
+ rgbatorgba(rbuf, gbuf, bbuf, abuf, data, lptr);
+ } else if (zsize == 3) {
+ getRow(rbuf, y, 0);
+ getRow(gbuf, y, 1);
+ getRow(bbuf, y, 2);
+ rgbtorgba(rbuf, gbuf, bbuf, data, lptr);
+ } else if (zsize == 2) {
+ getRow(rbuf, y, 0);
+ getRow(abuf, y, 1);
+ latorgba(rbuf, abuf, data, lptr);
+ } else {
+ getRow(rbuf, y, 0);
+ bwtorgba(rbuf, data, lptr);
+ }
+ lptr += 4 * xsize;
+ }
+ rowStart = null;
+ rowSize = null;
+ tmpData = null;
+ tmpRead = null;
+ format = GL.GL_RGBA;
+ header.zsize = 4;
+ }
+
+ private void getRow(byte[] buf, int y, int z) {
+ if (header.storage == 1) {
+ int offs = rowStart[y + z * header.ysize] - rleEnd;
+ System.arraycopy(tmpData, offs, tmpRead, 0, rowSize[y + z * header.ysize]);
+ int iPtr = 0;
+ int oPtr = 0;
+ for (;;) {
+ byte pixel = tmpRead[iPtr++];
+ int count = (int) (pixel & 0x7F);
+ if (count == 0) {
+ return;
+ }
+ if ((pixel & 0x80) != 0) {
+ while ((count--) > 0) {
+ buf[oPtr++] = tmpRead[iPtr++];
+ }
+ } else {
+ pixel = tmpRead[iPtr++];
+ while ((count--) > 0) {
+ buf[oPtr++] = pixel;
+ }
+ }
+ }
+ } else {
+ int offs = (y * header.xsize) + (z * header.xsize * header.ysize);
+ System.arraycopy(tmpData, offs, buf, 0, header.xsize);
+ }
+ }
+
+ private void bwtorgba(byte[] b, byte[] dest, int lptr) {
+ for (int i = 0; i < b.length; i++) {
+ dest[4 * i + lptr + 0] = b[i];
+ dest[4 * i + lptr + 1] = b[i];
+ dest[4 * i + lptr + 2] = b[i];
+ dest[4 * i + lptr + 3] = (byte) 0xFF;
+ }
+ }
+
+ private void latorgba(byte[] b, byte[] a, byte[] dest, int lptr) {
+ for (int i = 0; i < b.length; i++) {
+ dest[4 * i + lptr + 0] = b[i];
+ dest[4 * i + lptr + 1] = b[i];
+ dest[4 * i + lptr + 2] = b[i];
+ dest[4 * i + lptr + 3] = a[i];
+ }
+ }
+
+ private void rgbtorgba(byte[] r, byte[] g, byte[] b, byte[] dest, int lptr) {
+ for (int i = 0; i < b.length; i++) {
+ dest[4 * i + lptr + 0] = r[i];
+ dest[4 * i + lptr + 1] = g[i];
+ dest[4 * i + lptr + 2] = b[i];
+ dest[4 * i + lptr + 3] = (byte) 0xFF;
+ }
+ }
+
+ private void rgbatorgba(byte[] r, byte[] g, byte[] b, byte[] a, byte[] dest, int lptr) {
+ for (int i = 0; i < b.length; i++) {
+ dest[4 * i + lptr + 0] = r[i];
+ dest[4 * i + lptr + 1] = g[i];
+ dest[4 * i + lptr + 2] = b[i];
+ dest[4 * i + lptr + 3] = a[i];
+ }
+ }
+
+ private static byte imgref(byte[] i,
+ int x,
+ int y,
+ int z,
+ int xs,
+ int ys,
+ int zs) {
+ return i[(xs*ys*z)+(xs*y)+x];
+ }
+
+
+ private void writeHeader(DataOutputStream stream,
+ int xsize, int ysize, int zsize, boolean rle) throws IOException {
+ // effects: outputs the 512-byte IRIS RGB header to STREAM, using xsize,
+ // ysize, and depth as the dimensions of the image. NOTE that
+ // the following defaults are used:
+ // STORAGE = 1 (storage format = RLE)
+ // BPC = 1 (# bytes/channel)
+ // DIMENSION = 3
+ // PIXMIN = 0
+ // PIXMAX = 255
+ // IMAGENAME = <80 nulls>
+ // COLORMAP = 0
+ // See ftp://ftp.sgi.com/pub/sgi/SGIIMAGESPEC for more details.
+
+ // write out MAGIC, STORAGE, BPC
+ stream.writeShort(474);
+ stream.write((rle ? 1 : 0));
+ stream.write(1);
+
+ // write out DIMENSION
+ stream.writeShort(3);
+
+ // write XSIZE, YSIZE, ZSIZE
+ stream.writeShort(xsize);
+ stream.writeShort(ysize);
+ stream.writeShort(zsize);
+
+ // write PIXMIN, PIXMAX
+ stream.writeInt(0);
+ stream.writeInt(255);
+
+ // write DUMMY
+ stream.writeInt(0);
+
+ // write IMAGENAME
+ for (int i = 0; i < 80; i++)
+ stream.write(0);
+
+ // write COLORMAP
+ stream.writeInt(0);
+
+ // write DUMMY (404 bytes)
+ for (int i = 0; i < 404; i++)
+ stream.write(0);
+ }
+
+ private void writeImage(File file,
+ byte[] data,
+ int xsize,
+ int ysize,
+ int zsize,
+ boolean yflip) throws IOException {
+ // Input data is in RGBRGBRGB or RGBARGBARGBA format; first unswizzle it
+ byte[] tmpData = new byte[xsize * ysize * zsize];
+ int dest = 0;
+ for (int i = 0; i < zsize; i++) {
+ for (int j = i; j < (xsize * ysize * zsize); j += zsize) {
+ tmpData[dest++] = data[j];
+ }
+ }
+ data = tmpData;
+
+ // requires: DATA must be an array of size XSIZE * YSIZE * ZSIZE,
+ // indexed in the following manner:
+ // data[0] ...data[xsize-1] == first row of first channel
+ // data[xsize]...data[2*xsize-1] == second row of first channel
+ // ... data[(ysize - 1) * xsize]...data[(ysize * xsize) - 1] ==
+ // last row of first channel
+ // Later channels follow the same format.
+ // *** NOTE that "first row" is defined by the BOTTOM ROW of
+ // the image. That is, the origin is in the lower left corner.
+ // effects: writes out an SGI image to FILE, RLE-compressed, INCLUDING
+ // header, of dimensions (xsize, ysize, zsize), and containing
+ // the data in DATA. If YFLIP is set, outputs the data in DATA
+ // in reverse order vertically (equivalent to a flip about the
+ // x axis).
+
+ // Build the offset tables
+ int[] starttab = new int[ysize * zsize];
+ int[] lengthtab = new int[ysize * zsize];
+
+ // Temporary buffer for holding RLE data.
+ // Note that this makes the assumption that RLE-compressed data will
+ // never exceed twice the size of the input data.
+ // There are surely formal proofs about how big the RLE buffer should
+ // be, as well as what the optimal look-ahead size is (i.e. don't switch
+ // copy/repeat modes for less than N repeats). However, I'm going from
+ // empirical evidence here; the break-even point seems to be a look-
+ // ahead of 3. (That is, if the three values following this one are all
+ // the same as the current value, switch to repeat mode.)
+ int lookahead = 3;
+ byte[] rlebuf = new byte[2 * xsize * ysize * zsize];
+
+ int cur_loc = 0; // current offset location.
+ int ptr = 0;
+ int total_size = 0;
+ int ystart = 0;
+ int yincr = 1;
+ int yend = ysize;
+
+ if (yflip) {
+ ystart = ysize - 1;
+ yend = -1;
+ yincr = -1;
+ }
+
+ boolean DEBUG = false;
+
+ for (int z = 0; z < zsize; z++) {
+ for (int y = ystart; y != yend; y += yincr) {
+ // RLE-compress each row.
+
+ int x = 0;
+ byte count = 0;
+ boolean repeat_mode = false;
+ boolean should_switch = false;
+ int start_ptr = ptr;
+ int num_ptr = ptr++;
+ byte repeat_val = 0;
+
+ while (x < xsize) {
+ // see if we should switch modes
+ should_switch = false;
+ if (repeat_mode) {
+ if (imgref(data, x, y, z, xsize, ysize, zsize) != repeat_val) {
+ should_switch = true;
+ }
+ } else {
+ // look ahead to see if we should switch to repeat mode.
+ // stay within the scanline for the lookahead
+ if ((x + lookahead) < xsize) {
+ should_switch = true;
+ for (int i = 1; i <= lookahead; i++) {
+ if (DEBUG)
+ System.err.println("left side was " + ((int) imgref(data, x, y, z, xsize, ysize, zsize)) +
+ ", right side was " + (int)imgref(data, x+i, y, z, xsize, ysize, zsize));
+
+ if (imgref(data, x, y, z, xsize, ysize, zsize) !=
+ imgref(data, x+i, y, z, xsize, ysize, zsize))
+ should_switch = false;
+ }
+ }
+ }
+
+ if (should_switch || (count == 127)) {
+ // update the number of elements we repeated/copied
+ if (x > 0) {
+ if (repeat_mode)
+ rlebuf[num_ptr] = count;
+ else
+ rlebuf[num_ptr] = (byte) (count | 0x80);
+ }
+ // perform mode switch if necessary; output repeat_val if
+ // switching FROM repeat mode, and set it if switching
+ // TO repeat mode.
+ if (repeat_mode) {
+ if (should_switch)
+ repeat_mode = false;
+ rlebuf[ptr++] = repeat_val;
+ } else {
+ if (should_switch)
+ repeat_mode = true;
+ repeat_val = imgref(data, x, y, z, xsize, ysize, zsize);
+ }
+
+ if (x > 0) {
+ // reset the number pointer
+ num_ptr = ptr++;
+ // reset number of bytes copied
+ count = 0;
+ }
+ }
+
+ // if not in repeat mode, copy element to ptr
+ if (!repeat_mode) {
+ rlebuf[ptr++] = imgref(data, x, y, z, xsize, ysize, zsize);
+ }
+ count++;
+
+ if (x == xsize - 1) {
+ // Need to store the number of pixels we copied/repeated.
+ if (repeat_mode) {
+ rlebuf[num_ptr] = count;
+ // If we ended the row in repeat mode, store the
+ // repeated value
+ rlebuf[ptr++] = repeat_val;
+ }
+ else
+ rlebuf[num_ptr] = (byte) (count | 0x80);
+
+ // output zero counter for the last value in the row
+ rlebuf[ptr++] = 0;
+ }
+
+ x++;
+ }
+ // output this row's length into the length table
+ int rowlen = ptr - start_ptr;
+ if (yflip)
+ lengthtab[ysize*z+(ysize-y-1)] = rowlen;
+ else
+ lengthtab[ysize*z+y] = rowlen;
+ // add to the start table, and update the current offset
+ if (yflip)
+ starttab[ysize*z+(ysize-y-1)] = cur_loc;
+ else
+ starttab[ysize*z+y] = cur_loc;
+ cur_loc += rowlen;
+ }
+ }
+
+ // Now we have the offset tables computed, as well as the RLE data.
+ // Output this information to the file.
+ total_size = ptr;
+
+ if (DEBUG)
+ System.err.println("total_size was " + total_size);
+
+ DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+
+ writeHeader(stream, xsize, ysize, zsize, true);
+
+ int SIZEOF_INT = 4;
+ for (int i = 0; i < (ysize * zsize); i++)
+ stream.writeInt(starttab[i] + 512 + (2 * ysize * zsize * SIZEOF_INT));
+ for (int i = 0; i < (ysize * zsize); i++)
+ stream.writeInt(lengthtab[i]);
+ for (int i = 0; i < total_size; i++)
+ stream.write(rlebuf[i]);
+
+ stream.close();
+ }
+
+ private byte[] readAll(DataInputStream in) throws IOException {
+ byte[] dest = new byte[16384];
+ int pos = 0;
+ int numRead = 0;
+
+ boolean done = false;
+
+ do {
+ numRead = in.read(dest, pos, dest.length - pos);
+ if (pos == dest.length) {
+ // Resize destination buffer
+ byte[] newDest = new byte[2 * dest.length];
+ System.arraycopy(dest, 0, newDest, 0, pos);
+ dest = newDest;
+ }
+ if (numRead > 0) {
+ pos += numRead;
+ }
+
+ done = ((numRead == -1) || (in.available() == 0));
+ } while (!done);
+
+ // Trim destination buffer
+ if (pos != dest.length) {
+ byte[] finalDest = new byte[pos];
+ System.arraycopy(dest, 0, finalDest, 0, pos);
+ dest = finalDest;
+ }
+
+ return dest;
+ }
+
+ // Test case
+ /*
+ public static void main(String[] args) {
+ for (int i = 0; i < args.length; i++) {
+ try {
+ System.out.println(args[i] + ":");
+ SGIImage image = SGIImage.read(args[i]);
+ System.out.println(image);
+ BufferedImage img = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
+ WritableRaster raster = img.getRaster();
+ DataBufferByte db = (DataBufferByte) raster.getDataBuffer();
+ byte[] src = image.getData();
+ byte[] dest = db.getData();
+ for (int j = 0; j < src.length; j += 4) {
+ dest[j + 0] = src[j + 3];
+ dest[j + 1] = src[j + 2];
+ dest[j + 2] = src[j + 1];
+ dest[j + 3] = src[j + 0];
+ }
+ // System.arraycopy(src, 0, dest, 0, src.length);
+ ImageIcon icon = new ImageIcon(img);
+ JLabel label = new JLabel();
+ label.setIcon(icon);
+ JFrame frame = new JFrame(args[i]);
+ frame.getContentPane().add(label);
+ frame.pack();
+ frame.show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ */
+}
diff --git a/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java b/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java
new file mode 100755
index 000000000..8ab095643
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/TGAImage.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2003-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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import javax.media.opengl.*;
+import com.sun.opengl.util.*;
+
+/**
+ * Targa image reader and writer adapted from sources of the <a href =
+ * "http://java.sun.com/products/jimi/">Jimi</a> image I/O class library.
+ *
+ * <P>
+ *
+ * Image decoder for image data stored in TGA file format.
+ * Currently only the original TGA file format is supported. This is
+ * because the new TGA format has data at the end of the file, getting
+ * to the end of a file in an InputStream orient environment presents
+ * several difficulties which are avoided at the moment.
+ *
+ * <P>
+ *
+ * This is a simple decoder and is only setup to load a single image
+ * from the input stream
+ *
+ * <P>
+ *
+ * @author Robin Luiten
+ * @author Kenneth Russell
+ * @version $Revision$
+ */
+
+public class TGAImage {
+ private Header header;
+ private int format;
+ private ByteBuffer data;
+
+ private TGAImage(Header header) {
+ this.header = header;
+ }
+
+ /**
+ * This class reads in all of the TGA image header in addition it also
+ * reads in the imageID field as it is convenient to handle that here.
+ *
+ * @author Robin Luiten
+ * @version 1.1
+ */
+ public static class Header {
+ /** Set of possible file format TGA types */
+ public final static int TYPE_NEW = 0;
+ public final static int TYPE_OLD = 1;
+ public final static int TYPE_UNK = 2; // cant rewind stream so unknown for now.
+
+ /** Set of possible image types in TGA file */
+ public final static int NO_IMAGE = 0; // no image data
+ public final static int UCOLORMAPPED = 1; // uncompressed color mapped image
+ public final static int UTRUECOLOR = 2; // uncompressed true color image
+ public final static int UBLACKWHITE = 3; // uncompressed black and white image
+ public final static int COLORMAPPED = 9; // compressed color mapped image
+ public final static int TRUECOLOR = 10; // compressed true color image
+ public final static int BLACKWHITE = 11; // compressed black and white image
+
+ /** Field image descriptor bitfield values definitions */
+ public final static int ID_ATTRIBPERPIXEL = 0xF;
+ public final static int ID_RIGHTTOLEFT = 0x10;
+ public final static int ID_TOPTOBOTTOM = 0x20;
+ public final static int ID_INTERLEAVE = 0xC0;
+
+ /** Field image descriptor / interleave values */
+ public final static int I_NOTINTERLEAVED = 0;
+ public final static int I_TWOWAY = 1;
+ public final static int I_FOURWAY = 2;
+
+ /** Type of this TGA file format */
+ private int tgaType;
+
+ /** initial TGA image data fields */
+ private int idLength; // byte value
+ private int colorMapType; // byte value
+ private int imageType; // byte value
+
+ /** TGA image colour map fields */
+ private int firstEntryIndex;
+ private int colorMapLength;
+ private byte colorMapEntrySize;
+
+ /** TGA image specification fields */
+ private int xOrigin;
+ private int yOrigin;
+ private int width;
+ private int height;
+ private byte pixelDepth;
+ private byte imageDescriptor;
+
+ private byte[] imageIDbuf;
+ private String imageID;
+
+ // For construction from user data
+ Header() {
+ tgaType = TYPE_OLD; // dont try and get footer.
+ }
+
+ Header(LEDataInputStream in) throws IOException {
+ int ret;
+
+ tgaType = TYPE_OLD; // dont try and get footer.
+
+ // initial header fields
+ idLength = in.readUnsignedByte();
+ colorMapType = in.readUnsignedByte();
+ imageType = in.readUnsignedByte();
+
+ // color map header fields
+ firstEntryIndex = in.readUnsignedShort();
+ colorMapLength = in.readUnsignedShort();
+ colorMapEntrySize = in.readByte();
+
+ // TGA image specification fields
+ xOrigin = in.readUnsignedShort();
+ yOrigin = in.readUnsignedShort();
+ width = in.readUnsignedShort();
+ height = in.readUnsignedShort();
+ pixelDepth = in.readByte();
+ imageDescriptor = in.readByte();
+
+ if (idLength > 0) {
+ imageIDbuf = new byte[idLength];
+ in.read(imageIDbuf, 0, idLength);
+ imageID = new String(imageIDbuf, "US-ASCII");
+ }
+ }
+
+ public int tgaType() { return tgaType; }
+
+ /** initial TGA image data fields */
+ public int idLength() { return idLength; }
+ public int colorMapType() { return colorMapType; }
+ public int imageType() { return imageType; }
+
+ /** TGA image colour map fields */
+ public int firstEntryIndex() { return firstEntryIndex; }
+ public int colorMapLength() { return colorMapLength; }
+ public byte colorMapEntrySize() { return colorMapEntrySize; }
+
+ /** TGA image specification fields */
+ public int xOrigin() { return xOrigin; }
+ public int yOrigin() { return yOrigin; }
+ public int width() { return width; }
+ public int height() { return height; }
+ public byte pixelDepth() { return pixelDepth; }
+ public byte imageDescriptor() { return imageDescriptor; }
+
+ /** bitfields in imageDescriptor */
+ public byte attribPerPixel() { return (byte)(imageDescriptor & ID_ATTRIBPERPIXEL); }
+ public boolean rightToLeft() { return ((imageDescriptor & ID_RIGHTTOLEFT) != 0); }
+ public boolean topToBottom() { return ((imageDescriptor & ID_TOPTOBOTTOM) != 0); }
+ public byte interleave() { return (byte)((imageDescriptor & ID_INTERLEAVE) >> 6); }
+
+ public byte[] imageIDbuf() { return imageIDbuf; }
+ public String imageID() { return imageID; }
+
+ public String toString() {
+ return "TGA Header " +
+ " id length: " + idLength +
+ " color map type: "+ colorMapType +
+ " image type: "+ imageType +
+ " first entry index: " + firstEntryIndex +
+ " color map length: " + colorMapLength +
+ " color map entry size: " + colorMapEntrySize +
+ " x Origin: " + xOrigin +
+ " y Origin: " + yOrigin +
+ " width: "+ width +
+ " height: "+ height +
+ " pixel depth: "+ pixelDepth +
+ " image descriptor: "+ imageDescriptor +
+ (imageIDbuf == null ? "" : (" ID String: " + imageID));
+ }
+
+ public int size() { return 18 + idLength; }
+
+ // buf must be in little-endian byte order
+ private void write(ByteBuffer buf) {
+ buf.put((byte) idLength);
+ buf.put((byte) colorMapType);
+ buf.put((byte) imageType);
+ buf.putShort((short) firstEntryIndex);
+ buf.putShort((short) colorMapLength);
+ buf.put((byte) colorMapEntrySize);
+ buf.putShort((short) xOrigin);
+ buf.putShort((short) yOrigin);
+ buf.putShort((short) width);
+ buf.putShort((short) height);
+ buf.put((byte) pixelDepth);
+ buf.put((byte) imageDescriptor);
+ if (idLength > 0) {
+ try {
+ byte[] chars = imageID.getBytes("US-ASCII");
+ buf.put(chars);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Identifies the image type of the tga image data and loads
+ * it into the JimiImage structure. This was taken from the
+ * prototype and modified for the new Jimi structure
+ */
+ private void decodeImage(LEDataInputStream dIn) throws IOException {
+ switch (header.imageType()) {
+ case Header.UCOLORMAPPED:
+ throw new IOException("TGADecoder Uncompressed Colormapped images not supported");
+
+ case Header.UTRUECOLOR: // pixelDepth 15, 16, 24 and 32
+ switch (header.pixelDepth) {
+ case 16:
+ throw new IOException("TGADecoder Compressed 16-bit True Color images not supported");
+
+ case 24:
+ case 32:
+ decodeRGBImageU24_32(dIn);
+ break;
+ }
+ break;
+
+ case Header.UBLACKWHITE:
+ throw new IOException("TGADecoder Uncompressed Grayscale images not supported");
+
+ case Header.COLORMAPPED:
+ throw new IOException("TGADecoder Compressed Colormapped images not supported");
+
+ case Header.TRUECOLOR:
+ throw new IOException("TGADecoder Compressed True Color images not supported");
+
+ case Header.BLACKWHITE:
+ throw new IOException("TGADecoder Compressed Grayscale images not supported");
+ }
+ }
+
+ /**
+ * This assumes that the body is for a 24 bit or 32 bit for a
+ * RGB or ARGB image respectively.
+ */
+ private void decodeRGBImageU24_32(LEDataInputStream dIn) throws IOException {
+ int i; // row index
+ int j; // column index
+ int y; // output row index
+ int raw; // index through the raw input buffer
+ int rawWidth = header.width() * (header.pixelDepth() / 8);
+ byte[] rawBuf = new byte[rawWidth];
+ byte[] tmpData = new byte[rawWidth * header.height()];
+
+ if (header.pixelDepth() == 24) {
+ format = GL.GL_BGR;
+ } else {
+ assert header.pixelDepth() == 32;
+ format = GL.GL_BGRA;
+ }
+
+ for (i = 0; i < header.height(); ++i) {
+ dIn.readFully(rawBuf, 0, rawWidth);
+
+ if (header.topToBottom())
+ y = header.height - i - 1; // range 0 to (header.height - 1)
+ else
+ y = i;
+
+ System.arraycopy(rawBuf, 0, tmpData, y * rawWidth, rawBuf.length);
+ }
+
+ data = ByteBuffer.wrap(tmpData);
+ }
+
+ /** Returns the width of the image. */
+ public int getWidth() { return header.width(); }
+
+ /** Returns the height of the image. */
+ public int getHeight() { return header.height(); }
+
+ /** Returns the OpenGL format for this texture; e.g. GL.GL_BGR or GL.GL_BGRA. */
+ public int getGLFormat() { return format; }
+
+ /** Returns the raw data for this texture in the correct
+ (bottom-to-top) order for calls to glTexImage2D. */
+ public ByteBuffer getData() { return data; }
+
+ /** Reads a Targa image from the specified file. */
+ public static TGAImage read(String filename) throws IOException {
+ return read(new FileInputStream(filename));
+ }
+
+ /** Reads a Targa image from the specified InputStream. */
+ public static TGAImage read(InputStream in) throws IOException {
+ LEDataInputStream dIn = new LEDataInputStream(new BufferedInputStream(in));
+
+ Header header = new Header(dIn);
+ TGAImage res = new TGAImage(header);
+ res.decodeImage(dIn);
+ return res;
+ }
+
+ /** Writes the image in Targa format to the specified file name. */
+ public void write(String filename) throws IOException {
+ write(new File(filename));
+ }
+
+ /** Writes the image in Targa format to the specified file. */
+ public void write(File file) throws IOException {
+ FileOutputStream stream = new FileOutputStream(file);
+ FileChannel chan = stream.getChannel();
+ ByteBuffer buf = ByteBuffer.allocate(header.size());
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ header.write(buf);
+ buf.rewind();
+ chan.write(buf);
+ chan.write(data);
+ data.rewind();
+ chan.force(true);
+ chan.close();
+ stream.close();
+ }
+
+ /** Creates a TGAImage from data supplied by the end user. Shares
+ data with the passed ByteBuffer. Assumes the data is already in
+ the correct byte order for writing to disk, i.e., BGR or
+ BGRA. */
+ public static TGAImage createFromData(int width,
+ int height,
+ boolean hasAlpha,
+ boolean topToBottom,
+ ByteBuffer data) {
+ Header header = new Header();
+ header.imageType = Header.UTRUECOLOR;
+ header.width = width;
+ header.height = height;
+ header.pixelDepth = (byte) (hasAlpha ? 32 : 24);
+ header.imageDescriptor = (byte) (topToBottom ? Header.ID_TOPTOBOTTOM : 0);
+ // Note ID not supported
+ TGAImage ret = new TGAImage(header);
+ ret.data = data;
+ return ret;
+ }
+}
diff --git a/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java b/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java
new file mode 100755
index 000000000..1c822ef02
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/TextureProvider.java
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.*;
+import java.net.*;
+
+import com.sun.opengl.util.texture.*;
+
+/** Plug-in interface to TextureIO to support reading OpenGL textures
+ from new file formats. For all methods, either internalFormat or
+ pixelFormat may be 0 in which case they must be inferred as
+ e.g. RGB or RGBA depending on the file contents.
+*/
+
+public interface TextureProvider {
+
+ /**
+ * Produces a TextureData object from a file, or returns null if the
+ * file format was not supported by this TextureProvider. Does not
+ * do any OpenGL-related work. The resulting TextureData can be
+ * converted into an OpenGL texture in a later step.
+ *
+ * @param file the file from which to read the texture data
+ *
+ * @param internalFormat the OpenGL internal format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param pixelFormat the OpenGL pixel format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param mipmap whether mipmaps should be produced for this
+ * texture either by autogenerating them or
+ * reading them from the file. Some file formats
+ * support multiple mipmaps in a single file in
+ * which case those mipmaps will be used rather
+ * than generating them.
+ *
+ * @param fileSuffix the file suffix to be used as a hint to the
+ * provider to more quickly decide whether it
+ * can handle the file, or null if the
+ * provider should infer the type from the
+ * file's contents
+ *
+ * @throws IOException if an error occurred while reading the file
+ */
+ public TextureData newTextureData(File file,
+ int internalFormat,
+ int pixelFormat,
+ boolean mipmap,
+ String fileSuffix) throws IOException;
+
+ /**
+ * Produces a TextureData object from a stream, or returns null if
+ * the file format was not supported by this TextureProvider. Does
+ * not do any OpenGL-related work. The resulting TextureData can be
+ * converted into an OpenGL texture in a later step.
+ *
+ * @param stream the stream from which to read the texture data
+ *
+ * @param internalFormat the OpenGL internal format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param pixelFormat the OpenGL pixel format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param mipmap whether mipmaps should be produced for this
+ * texture either by autogenerating them or
+ * reading them from the file. Some file formats
+ * support multiple mipmaps in a single file in
+ * which case those mipmaps will be used rather
+ * than generating them.
+ *
+ * @param fileSuffix the file suffix to be used as a hint to the
+ * provider to more quickly decide whether it
+ * can handle the file, or null if the
+ * provider should infer the type from the
+ * file's contents
+ *
+ * @throws IOException if an error occurred while reading the stream
+ */
+ public TextureData newTextureData(InputStream stream,
+ int internalFormat,
+ int pixelFormat,
+ boolean mipmap,
+ String fileSuffix) throws IOException;
+
+ /**
+ * Produces a TextureData object from a URL, or returns null if the
+ * file format was not supported by this TextureProvider. Does not
+ * do any OpenGL-related work. The resulting TextureData can be
+ * converted into an OpenGL texture in a later step.
+ *
+ * @param url the URL from which to read the texture data
+ *
+ * @param internalFormat the OpenGL internal format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param pixelFormat the OpenGL pixel format to be used for
+ * the texture, or 0 if it should be inferred
+ * from the file's contents
+ *
+ * @param mipmap whether mipmaps should be produced for this
+ * texture either by autogenerating them or
+ * reading them from the file. Some file formats
+ * support multiple mipmaps in a single file in
+ * which case those mipmaps will be used rather
+ * than generating them.
+ *
+ * @param fileSuffix the file suffix to be used as a hint to the
+ * provider to more quickly decide whether it
+ * can handle the file, or null if the
+ * provider should infer the type from the
+ * file's contents
+ *
+ * @throws IOException if an error occurred while reading the URL
+ */
+ public TextureData newTextureData(URL url,
+ int internalFormat,
+ int pixelFormat,
+ boolean mipmap,
+ String fileSuffix) throws IOException;
+}
diff --git a/src/classes/com/sun/opengl/util/texture/spi/TextureWriter.java b/src/classes/com/sun/opengl/util/texture/spi/TextureWriter.java
new file mode 100755
index 000000000..423a3b3e5
--- /dev/null
+++ b/src/classes/com/sun/opengl/util/texture/spi/TextureWriter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package com.sun.opengl.util.texture.spi;
+
+import java.io.*;
+
+import com.sun.opengl.util.texture.*;
+
+/** Plug-in interface to TextureIO to support writing OpenGL textures
+ to new file formats. */
+
+public interface TextureWriter {
+ /** Writes the given TextureData to the passed file. Returns true if
+ this TextureWriter successfully handled the writing of the file,
+ otherwise false. May throw IOException if either this writer did
+ not support certain parameters of the TextureData or if an I/O
+ error occurred. */
+ public boolean write(File file,
+ TextureData data) throws IOException;
+}