I cannot load *.tga files :(

Started by Schnitter, October 02, 2007, 22:35:01

Previous topic - Next topic

Schnitter

Hi,

I've trying to load *.tga files, but my texture-class, that I had just copied from a tutorial(looks like this) didn't support it.
If I try to load such an file, I get the following output:
Quote
java.lang.IllegalArgumentException: Empty region!
   at javax.imageio.ImageReader.computeRegions(Unknown Source)
   at com.sun.imageio.plugins.wbmp.WBMPImageReader.read(Unknown Source)
   at javax.imageio.ImageIO.read(Unknown Source)
   at javax.imageio.ImageIO.read(Unknown Source)
   at TextureLoader.loadImage(TextureLoader.java:203)
   at TextureLoader.getTexture(TextureLoader.java:104)
   at TextureLoader.getTexture(TextureLoader.java:83)
   at Sprite.<init>(Sprite.java:19)
   at Start.<init>(Start.java:52)
   at Start.main(Start.java:94)
java.lang.NullPointerException
   at Sprite.render(Sprite.java:86)
   at Start.<init>(Start.java:67)
   at Start.main(Start.java:94)

Now I ask you, how can I implement the support of tga-files?


EDIT: Here's the Texure-Loader class: ttp://nopaste.info/11afa92c19.html


bobjob

is the image loading for the wiki tutorial?
http://lwjgl.org/wiki/doku.php/lwjgl/tutorials/devil/loadingtextures

if so make sure that you have downloaded the lwjgl_optional package, and that you include lwjgl_devil in with the rest of your jar's

Schnitter

No, it's just, because I read that *.tga files are better. And I want to have transparent sprites(Color-Keying/Color-picking).
(Is devil or java better for loading textures?)

Matthias

You try to lo0ad TGA files using ImageIO. But ImageIO does not know TGA files and tries to load them as WBMP files - as seen in the stack trace.

Try to download one of the many Java TGA loaders. Or better use PNG - it also supports transparency.

Ciao Matthias

Evil-Devil

Loading TGA without DevIL isn't that hard. Here is my TGA-Loader (handles 24/32bit/RLE compresses/uncompressed TGA images). The loader itself refers to some other classes that were posted below the TGA-Loader. It is not optimized in this version :P

TGATextureLoader.java
/* =============================================================================
 * TGATextureLoader.java Bubble EngineOld 
 * Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
 * All rights reserved
 * -----------------------------------------------------------------------------
 * powered by LWJGL - Copyright (c) LWJGL Team (http://www.lwjgl.org)
 * powered by JAVA - Copyright (c) Sun Microsystems (http://java.sun.com)
 * -------------------------------------------------------------------------- */
package com.evildevil.bubble.core.texture;

//JAVA Imports
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;

import com.evildevil.bubble.core.LogManager;
import com.evildevil.bubble.util.BufferUtils;

/**
 * @author Benjamin "Evil-Devil" Behrendt
 * @version 1.1, 25.03.2006 
 */
public final class TGATextureLoader extends TextureLoader {    
    private byte[] uncompressedTGAHeader = new byte[] {            
            0,0,2,0,0,0,0,0,0,0,0,0 };				// header of uncompressed tga image
    private byte[] compressedTGAHeader = new byte[] {            
            0,0,10,0,0,0,0,0,0,0,0,0 };				// header of compressed tga image
    private byte[] compareTGAHeader = new byte[12];	// to check if we really have a tga image
    private byte[] tgaHeader = new byte[6];			// only the first 6 bytes are usefull for us
    
    private byte[] colorBuffer;			// used for RLE compression
    private byte[] textureData;			// will store the TGA Image data    
    
    public TGATextureLoader() {             
    }
    
    public Texture loadTexture(String  textureName) throws TextureFormatException {
        // call TextureLoader::loadTexture to ensure everything went ok!
        super.loadTexture(textureName);
        
        // determine if it is an compressed or uncompressed tga image
        compareTGAHeader = getRawHeader(compareTGAHeader.length);        
        
        // load the image corresponding to be compressed or not        
        try {
            if (Arrays.equals(compareTGAHeader,uncompressedTGAHeader))	// load uncompressed TGA
                loadUncompressedTexture(textureName);
            else if (Arrays.equals(compareTGAHeader,compressedTGAHeader)) {	// load compressed TGA
                loadCompressedTexture(textureName);
            } else	// none TGA -> throw new TGA exception
                throw new TGAFormatException("texture \""+textureName+"\" is no tga image");
        } catch (TGAFormatException tgafe) {
            throw tgafe;		// rethrow the exception
        }                
        return texture;
    }
    
    /* 
     * @see com.evildevil.bubble.core.texture.ITextureLoad#loadUncompressedTexture(java.lang.String)
     * Fixed: 25.03.2006 got the RLE encoding working.
     */
    public int loadUncompressedTexture(String textureName) throws TGAFormatException {
    	LogManager.log("Attempting to load a uncompressed TGA Image.");
        texture = new Texture();
        textureFile = new File(textureName);
        try {
            // get the real header ^^
            tgaHeader = getHeader(tgaHeader.length);
            // get the texture format
            getTextureFormat();
        } catch (NullPointerException npe) {
            LogManager.logException(npe);
        } catch (TextureFormatException tfe) {
            LogManager.logException(tfe);
        }
        
        // load the binary texture data and close the stream
        textureData = loadTextureData(texture.width * texture.height * texture.bpp,true);
        
        // swap the r and g values
        /*for (int i=0; i<textureData.length; i+=texture.bpp) {
            textureData[i] ^= textureData[i+2] ^= textureData[i];
            textureData[i+2] ^= textureData[i];
        }*/
        // the image is mirrored vertical, but why?
        // one line
        //byte[] line = new byte[texture.width * texture.bpp];
        //for (int i=0; i<line.length)
        
        return createTexture(textureData);
    }
    
    public int loadCompressedTexture(String textureName) throws TGAFormatException {
        LogManager.log("Attempting to load a compressed TGA Image.");
        texture = new Texture();
        textureFile = new File(textureName);
        
        try {            
            // get the real header ^^
            tgaHeader = getHeader(tgaHeader.length);
            // get the texture format
            getTextureFormat();
        } catch (NullPointerException npe) {
            LogManager.logException(npe);
        } catch (TextureFormatException tfe) {
            LogManager.logException(tfe);
        }
        
        textureData = new byte[texture.width * texture.height * texture.bpp];
        
        final int pixelCount = texture.width * texture.height;	// pixels in this image
        int currPixel = 0;	// current pixel
        int currByte = 0;	// current byte                
        int chunkheader;
        
        do {
        	byte chunkHeader = 0;	// have to be unsigned -.-
        	// read
        	chunkheader = loadTextureData(1)[0] & 0xFF;
        	
        	// RAW Chunk
        	if (chunkheader < 128) {
        		chunkheader++;		// number of raw pixels
        		
        		// pixel reading
        		for (int counter = 0; counter < chunkheader; counter++) {
  			
        			writeTextureData(loadTextureData(texture.bpp),currByte);        			
        			
        			// increase the currByte counter
        			currByte+=texture.bpp;        			
        			currPixel++;
    			}
        	} else {	// RLE Header
        		chunkheader -= 127;
        		// read the data
        		colorBuffer = loadTextureData(texture.bpp);
        		
        		// pixel loop
        		for (int counter = 0; counter < chunkheader; counter++) {
        			
        			writeTextureData(colorBuffer,currByte);
        			
    				// increase the currByte counter
        			currByte+=texture.bpp;
        			currPixel++;
        		}
        	}
        } while (currPixel < pixelCount);
        
        return createTexture(textureData);
    }
    
    private int createTexture(byte[] data) {
    	// put the data in a bytebuffer
    	final ByteBuffer textureBuffer = BufferUtils.createByteBuffer(data);
        // create the glname for the new texture
    	final IntBuffer glName = generateGLName();
    	
    	generateTexture(glName);
    	bindTexture(glName.get(0));
    	
    	setLinearFilter();
    	generateTextureImage(textureBuffer);
    	texture.setGLName(glName.get(0));
    	
    	return glName.get(0);
    }
    
    private void writeTextureData(byte[] colorBuffer,int currentByte) {
    	textureData[currentByte] = colorBuffer[2];		// R Byte
		textureData[currentByte+1] = colorBuffer[1];	// G Byte
		textureData[currentByte+2] = colorBuffer[0];	// B Byte
		
		// 32bit (alpha) check
		if (texture.bpp == 4)
			textureData[currentByte+3] = colorBuffer[3];	// A Byte
    }
    
    public int getWidth(byte[] header) throws TextureFormatException {
        return Math.abs(header[1] * 256 + header[0]);
    }
    
    public int getHeight(byte[] header) throws TextureFormatException {        
        return Math.abs(header[3] * 256 + header[2]);        
    }
    
    public int getBitdepth(byte[] header) throws TextureFormatException {
        // we have to prove the correct bitdepth ourself, because the texture only checks
        // for zero values on width and height        
        if (header[4] != 24 && header[4] != 32)
            throw new TGAFormatException("bitdepth of the tga texture is not 24 or 32");
        return header[4];
    }
    
    public void getTextureFormat() throws TextureFormatException {        
        texture.setWidth(getWidth(tgaHeader));
        texture.setHeight(getHeight(tgaHeader));
        texture.setBitdepth(getBitdepth(tgaHeader));        
    }
}


TextureLoader.java
/* =============================================================================
 * TextureLoader.java Bubble EngineOld 
 * Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
 * All rights reserved
 * -----------------------------------------------------------------------------
 * powered by LWJGL - Copyright (c) LWJGL Team (http://www.lwjgl.org)
 * powered by JAVA - Copyright (c) Sun Microsystems (http://java.sun.com)
 * -------------------------------------------------------------------------- */
package com.evildevil.bubble.core.texture;

// JAVA Imports
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.EXTBgra;
import org.lwjgl.opengl.GL12;

// LWJGL Imports
import static org.lwjgl.opengl.GL11.*;

// Bubble Engine Imports
import com.evildevil.bubble.core.EngineOld;
import com.evildevil.bubble.core.LogManager;
import com.evildevil.bubble.util.BufferUtils;

/**
 * Textureloading implementation will be done in subclasses or within custom classes
 * which implements ITextureLoad interface.
 * 
 * @author Evil-Devil
 * @version 1.0, 08.02.2005 
 */
public abstract class TextureLoader implements ITextureLoad {
    protected Texture texture = null;				// the texture that will holds the relevant data
    protected File textureFile = null;				// the file that represents the binary texture
    private FileInputStream fis = null;				// used for reading the texturedata
    private BufferedInputStream br = null;       // for webstart sucking 
    protected static glFilter defaultFilter = glFilter.LINEAR;		// the default filter that will used on filtering the texture
    
    public static enum glFilter {
        LINEAR, NEAREST, MIPMAPPED 
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getRawHeader(byte[])
     */
    public byte[] getRawHeader(int headerSize) throws NullPointerException  {
        if (textureFile == null)
            throw new NullPointerException("texture can't be null");
        // load the raw header
        return loadTextureData(headerSize);
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getHeader(byte[])
     * @exception a NullPointerException will be thrown if the textureFile is null
     */
    public byte[] getHeader(int headerSize) {        
        if (textureFile == null)
            throw new NullPointerException("texture can't be null");
        // load the header
        return loadTextureData(headerSize);
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getWidth(byte[])
     */
    public int getWidth(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getHeight(byte[])
     */
    public int getHeight(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getBitDepth(byte[])
     */
    public int getBitdepth(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadTexture(java.lang.String)
     */
    public Texture loadTexture(String textureName) throws TextureFormatException {
        texture = new Texture();
        textureFile = new File(textureName);
        return null;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadCompressedTexture(java.lang.String)
     */
    public int loadCompressedTexture(String textureName) throws TextureFormatException {        
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadUncompressedTexture(java.lang.String)
     */
    public int loadUncompressedTexture(String textureName) throws TextureFormatException {
        return 0;
    }    
    
    /*
     * use this to handle all obtainable informations with one function :)
     * But you can instead calling themselve and do the error handling :p
     */
    public abstract void getTextureFormat() throws TextureFormatException;
    
    /**
     * obtain the binary data from the texture file and return it to you
     * @param size
     * @return the loaded texturedata
     */
    public byte[] loadTextureData(int size) {        
        final byte[] textureData = new byte[size];
        try {
            if (fis == null) {
                fis = new FileInputStream(textureFile);
                if (EngineOld.isDebugMode())
                    LogManager.logDebug("FIS textureFile is \""+textureFile.getName()+"\"");
            }            
            fis.read(textureData);
            /*if (br == null) {
                br = new BufferedInputStream(
                        getClass().getResourceAsStream(textureFile.toString()));
            }
            br.read(textureData);*/
        } catch (FileNotFoundException fnfe) {
           // LogManager.logException(fnfe);
        } catch (IOException ioe) {
            LogManager.logException(ioe);
        }
        return textureData;
    }
    
    public byte[] loadTextureData(int size, boolean closeStream) {
        final byte[] textureData = loadTextureData(size);
        // close the stream if needed
        if (closeStream) {
            try {
                fis.close();
                //br.close();
            } catch (IOException ioe) {
                LogManager.logException(ioe);
            } finally {
                fis = null;		// make sure with the next load there will be a new FIS created
                //br = null;
            }
        }
        return textureData;
    }
    
    protected IntBuffer generateGLName() {
        return BufferUtils.createIntBuffer(1);
    }
    
    protected void setLinearFilter() {
        // Linear Filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    
    protected void setNearestFilter() {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);        
    }
    
    protected void setMipMappedFilter() {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    }
    
    protected void setTextureFilter(glFilter filter) {
        if (filter == glFilter.LINEAR)
            setLinearFilter();
        else if (filter == glFilter.NEAREST)
            setNearestFilter();
        else if (filter == glFilter.MIPMAPPED)
            setMipMappedFilter();
    }
    
    protected void bindTexture(int glName) {
        glBindTexture(GL_TEXTURE_2D,glName);
    }
    
    protected void generateTexture(IntBuffer address) {
        glGenTextures(address);
    }
    
    protected void generateAndBindTexture(IntBuffer address) {
        generateTexture(address);
        bindTexture(address.get(0));
    }
    
    protected void generateTextureImage(ByteBuffer textureBuffer) {
        //int glColor = texture.bitdepth == 24 ? GL_RGB : GL_RGBA;
    	int glColor = texture.bitdepth == 24 ? GL12.GL_BGR : GL12.GL_BGRA;
        glTexImage2D(GL_TEXTURE_2D,0,glColor,texture.width,texture.height,0,glColor,GL_UNSIGNED_BYTE,textureBuffer);
        // i guess we can free the buffer now, it isn't needed anymore
        //textureBuffer.clear();
    }
    
    protected ByteBuffer generateTextureBuffer(byte[] textureData) {
        return BufferUtils.createByteBuffer(textureData);

    }
    
    /**
     * performs all nessessary steps to create a functional gl texture, excepting texturefiltering, by calling
     * generateTexture(IntBuffer address) 
     * @param address the address that is used to create a valid gl name
     * @param textureBuffer the bytebuffer that holds the binary informations about the texture file
     */
    protected void buildTexture(IntBuffer address, ByteBuffer textureBuffer) {
        generateTexture(address);
        bindTexture(address.get(0));
        generateTextureImage(textureBuffer);
    }
    
    protected void buildTexture(IntBuffer address, ByteBuffer textureBuffer, glFilter filter) {
        buildTexture(address,textureBuffer);
        setTextureFilter(filter);
    }
    
    protected void buildTexture(byte[] textureData) {
        final IntBuffer address = generateGLName();
        // assign the glName to the texture
        texture.setGLName(address.get(0));
        System.out.println(address.get());
        // call an overloaded version of buildTexture() to do the rest work
        buildTexture(address,generateTextureBuffer(textureData));
    }
    
    protected void setDefaultFilter(glFilter filter) {
        defaultFilter = filter;
    }
}