Hi,
I've trying to load *.tga files, but my texture-class, that I had just copied from a tutorial(looks like this (http://nopaste.info/2174c412fd.html)) 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 (http://ttp://nopaste.info/11afa92c19.html)
is the image loading for the wiki tutorial?
http://lwjgl.org/wiki/doku.php/lwjgl/tutorials/devil/loadingtextures (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
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?)
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
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;
}
}