Download
I modified both WindowsBitmapFile and TargaFile in a way that is now possible to load a media from a jar file.
I also added compressed TGA support.
Finally I wrote a function called getBufferedImage(String filename) which returns a bufferedImage with an alpha channel.
To use the new function, one would call WindowsBitmapFile.getBufferedImage(path), or TargaFile.getBufferedImage(path).
PS: To whoever wrote those two classes:
When I was making the changes, I tried reading the files using a DataInputStream, but for some reason when I attempted loading a file from a jar, it threw an exception about mark/reset, function that I used to compensate the lack of rewind().
Anyways I switched to regular InputStream and now everything is ok
PS2: The bitmap loader currently supports 24 bit images only, but I have some code that allows reading 256 color media as well.
Let me know if anyone want it.
Looks good
Are you able to generate a patch file of your changes?
Just run “cvs diff -u TargaFile.java WindowsBitmapFile.java” over your sources (if you’re using a GUI then there should be some way of doing that). it will give a list of all changes between your version and the CVS one.
That way your changes can get included into XIth3D easily
Cheers,
Will.
Know what? The code is now yours, do whatever you want with it…
well that rocks
[quote]Know what? The code is now yours, do whatever you want with it…
[/quote]
fine - I will.
Index: TargaFile.java
===================================================================
RCS file: /cvs/xith3d/src/com/xith3d/imaging/TargaFile.java,v
retrieving revision 1.1
diff -u -w -r1.1 TargaFile.java
--- TargaFile.java 19 Jul 2003 00:42:42 -0000 1.1
+++ TargaFile.java 9 Dec 2003 11:29:31 -0000
@@ -1,7 +1,9 @@
package com.xith3d.imaging;
-import java.io.*;
-
+import java.awt.image.BufferedImage;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
/**
* Handles dealing with targa image files.
* +--------------------------------------+
@@ -10,7 +12,7 @@
* | Bitmap Data |
* +--------------------------------------+
*/
-public class TargaFile implements ImageFile {
+public class TargaFile implements com.xith3d.imaging.ImageFile {
private byte FHimageIDLength = 0;
private byte FHcolorMapType = 0; // 0 = no pallete
@@ -24,6 +26,8 @@
private short FHheight = 0;
private byte FHbitCount = 0; // 16,24,32
private byte FHimageDescriptor = 0; // 24 bit = 0x00, 32-bit=0x08
+ private int filePointer = 0;
+ private byte fileContents[] = null;
private byte[] data = null;
@@ -50,6 +54,31 @@
return data.length;
}
+ public static BufferedImage getBufferedImage(String filename){
+ TargaFile loader = new TargaFile();
+ loader.load(filename);
+
+ int width = loader.getWidth(),
+ height = loader.getHeight(),
+ bytePerPixel = loader.getBPP()/8;
+
+ BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+ byte[] imageData = loader.getData();
+
+ for(int j = height - 1; j >= 0; j--)
+ for(int i = 0; i < width; i++) {
+ int index = ((height - 1 - j) * width + i) * bytePerPixel;
+ byte alpha = (bytePerPixel == 4) ? imageData[index + 3] : (byte)255;
+ int color = ( alpha & 0xFF) << 24|
+ (imageData[index + 2] & 0xFF) << 16|
+ (imageData[index + 1] & 0xFF) << 8|
+ (imageData[index + 0] & 0xFF);
+ bufferedImage.setRGB(i,j, color);
+ }
+ return bufferedImage;
+ }
+
public void printHeaders() {
System.out.println("-----------------------------------");
System.out.println("File Header");
@@ -82,31 +111,36 @@
FHheight = 0;
FHbitCount = 0;
FHimageDescriptor = 0;
+ filePointer = 0;
+
+ InputStream dis = ClassLoader.getSystemResourceAsStream(filename);
- File file = null;
- RandomAccessFile dis = null;
- try
- {
- file = new File(filename);
- dis = new RandomAccessFile(file,"r");
+ try{
+ if( dis == null)
+ dis = new FileInputStream(filename);
+
+ fileContents = new byte[dis.available()];
+ dis.read(fileContents);
+ try{dis.close();}catch(Exception x){}
// read the file header
- FHimageIDLength = (byte)dis.readUnsignedByte();
- FHcolorMapType = (byte)dis.readUnsignedByte();
- FHimageType = (byte)dis.readUnsignedByte();
- FHcolorMapOrigin = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHcolorMapLength = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHcolorMapDepth = (byte)dis.readUnsignedByte();
- FHimageXOrigin = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHimageYOrigin = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHwidth = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHheight = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHbitCount = (byte)dis.readUnsignedByte();
- FHimageDescriptor = (byte)dis.readUnsignedByte();
-
- if(FHimageType!=2 && FHimageType!=3) // only deal with these two types
- {
- dis.close();
+ FHimageIDLength = (byte)readUnsignedByte();
+ FHcolorMapType = (byte)readUnsignedByte();
+ FHimageType = (byte)readUnsignedByte();
+ FHcolorMapOrigin = readShort();
+ FHcolorMapLength = readShort();
+ FHcolorMapDepth = (byte)readUnsignedByte();
+ FHimageXOrigin = readShort();
+ FHimageYOrigin = readShort();
+ FHwidth = readShort();
+ FHheight = readShort();
+ FHbitCount = (byte)readUnsignedByte();
+ FHimageDescriptor = (byte)readUnsignedByte();
+
+ if(FHimageType!=2 && FHimageType!=3) { // only deal with these two types
+ if(FHimageType == 10)
+ loadCompressed();
+ fileContents = null;
return;
}
@@ -114,30 +148,100 @@
// allocate memory for the pixel data
data = new byte[FHwidth*FHheight*bytesPerPixel];
-
// read the pixel data
- dis.read(data,0,data.length);
+ System.arraycopy(fileContents, filePointer, data, 0, data.length);
- if(FHbitCount==24 || FHbitCount==32)
- {
- // swap the R and B values to get RGB, bitmap color format is BGR
- for(int loop=0;loop<data.length;loop+=bytesPerPixel)
- {
+ if(FHbitCount==24 || FHbitCount==32){
+ // swap the R and B values to get RGB, targa color format is BGR
+ for(int loop=0;loop<data.length;loop+=bytesPerPixel){
byte btemp = data[loop];
data[loop] = data[loop+2];
data[loop+2] = btemp;
}
}
+ fileContents = null;
}
- catch(Exception x)
- {
+ catch(Exception x){
x.printStackTrace();
System.out.println(x.getMessage());
}
- finally
- {
- try{dis.close();}catch(Exception x){}
+
+ }
+
+ public void loadCompressed() {
+ printHeaders();
+ int bytesPerPixel = (FHbitCount/8);
+ data = new byte[FHwidth*FHheight*bytesPerPixel];
+
+ int pixelcount = FHwidth*FHheight,
+ currentbyte = 0,
+ currentpixel = 0;
+
+ byte[] colorbuffer = new byte[bytesPerPixel];
+
+ try{
+ do{
+ int chunkheader = 0;
+ chunkheader = (int)readUnsignedByte();
+ System.out.println(chunkheader);
+ if(chunkheader < 128){
+ chunkheader++;
+ for(short counter = 0; counter < chunkheader; counter++){
+
+ readColorBuffer(colorbuffer);
+ data[currentbyte + 0] = colorbuffer[2];
+ data[currentbyte + 1] = colorbuffer[1];
+ data[currentbyte + 2] = colorbuffer[0];
+
+
+ if(bytesPerPixel == 4)
+ data[currentbyte + 3] = (byte)readUnsignedByte();
+
+ currentbyte += bytesPerPixel;
+ currentpixel++;
+ if(currentpixel > pixelcount)
+ throw new IOException("Too many pixels read");
+ }
+ }
+ else{
+ chunkheader -= 127;
+ readColorBuffer(colorbuffer);
+ for(short counter = 0; counter < chunkheader; counter++){ // by the header
+ data[currentbyte + 0] = colorbuffer[2];
+ data[currentbyte + 1] = colorbuffer[1];
+ data[currentbyte + 2] = colorbuffer[0];
+
+ if(bytesPerPixel == 4)
+ data[currentbyte + 3] = (byte)readUnsignedByte();
+
+ currentbyte += bytesPerPixel;
+ currentpixel++;
+ if(currentpixel > pixelcount)
+ throw new IOException("Too many pixels read");
+ }
+ }
+ } while (currentpixel < pixelcount);
}
+ catch(Exception x){
+ x.printStackTrace();
+ System.out.println(x.getMessage());
+ }
+ }
+
+ private void readColorBuffer(byte[] buffer){
+ buffer[0] = (byte)readUnsignedByte();
+ buffer[1] = (byte)readUnsignedByte();
+ buffer[2] = (byte)readUnsignedByte();
+ }
+
+ private int readUnsignedByte(){
+ return (int) fileContents[filePointer++] & 0xFF;
+ }
+
+ private short readShort(){
+ int s1 = (fileContents[filePointer++] & 0xFF),
+ s2 = (fileContents[filePointer++] & 0xFF) << 8;
+ return ((short)(s1 | s2));
}
public static void main(String[] args) {
Index: WindowsBitmapFile.java
===================================================================
RCS file: /cvs/xith3d/src/com/xith3d/imaging/WindowsBitmapFile.java,v
retrieving revision 1.1
diff -u -w -r1.1 WindowsBitmapFile.java
--- WindowsBitmapFile.java 19 Jul 2003 00:42:42 -0000 1.1
+++ WindowsBitmapFile.java 9 Dec 2003 11:29:32 -0000
@@ -1,6 +1,8 @@
package com.xith3d.imaging;
-import java.io.*;
+import java.awt.image.BufferedImage;
+import java.io.InputStream;
+import java.io.FileInputStream;
/**
* Handles dealing with windows bitmap files. This class doesn't handle palettized files.
@@ -14,9 +16,8 @@
* | Bitmap Data |
* +--------------------------------------+
*/
-public class WindowsBitmapFile implements ImageFile {
+public class WindowsBitmapFile implements com.xith3d.imaging.ImageFile {
- private byte[] FHtype = new byte[2];
private int FHsize = 0;
private short FHreserved1 = 0;
private short FHreserved2 = 0;
@@ -33,6 +34,8 @@
private long IHypelsPerMeter = 0;
private int IHcolorsUsed = 0;
private int IHcolorsImportant = 0;
+ private int filePointer = 0;
+ private byte[] fileContents = null;
private byte[] data = null;
@@ -59,6 +62,29 @@
return data.length;
}
+ public static BufferedImage getBufferedImage(String filename){
+ WindowsBitmapFile loader = new WindowsBitmapFile();
+ loader.load(filename);
+
+ int width = loader.getWidth(),
+
+ height = loader.getHeight();
+
+ BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+ byte[] imageData = loader.getData();
+ for(int j = height - 1; j >= 0; j--)
+ for(int i = 0; i < width; i++) {
+ int index = ((height - 1 - j)* width + i) * 3,
+ color= ( 255 & 0xFF) << 24|
+ (imageData[index + 2] & 0xFF) << 16|
+ (imageData[index + 1] & 0xFF) << 8|
+ (imageData[index + 0] & 0xFF);
+ bufferedImage.setRGB(i,j, color);
+ }
+ return bufferedImage;
+ }
+
public void printHeaders() {
System.out.println("-----------------------------------");
System.out.println("File Header");
@@ -100,50 +126,54 @@
IHypelsPerMeter = 0;
IHcolorsUsed = 0;
IHcolorsImportant = 0;
+ filePointer = 0;
+
+ InputStream dis = ClassLoader.getSystemResourceAsStream(filename);
- File file = null;
- RandomAccessFile dis = null;
try
{
- file = new File(filename);
- dis = new RandomAccessFile(file,"r");
+ if( dis == null)
+ dis = new FileInputStream(filename);
+
+
+ fileContents = new byte[dis.available()];
+ dis.read(fileContents);
+ try{dis.close();}catch(Exception x){}
- dis.read(FHtype,0,2);
+ short magicNumber = readShort();
+ // FHtype
// make sure it's windows bitmap file
- if(FHtype[0]!='B' || FHtype[1]!='M')
+ if(magicNumber != 19778)
{
- dis.close();
+ fileContents = null;
return;
}
// read the file header
- FHsize = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- FHreserved1 = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHreserved2 = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- FHoffsetBits = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
+ FHsize = readInt();
+ FHreserved1 = readShort();
+ FHreserved2 = readShort();
+ FHoffsetBits = readInt();
// read the info header
- IHsize = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHwidth = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHheight = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHplanes = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- IHbitCount = (short)(dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- IHcompression = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHsizeImage = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHxpelsPerMeter = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8);
- IHypelsPerMeter = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHcolorsUsed = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
- IHcolorsImportant = (dis.readUnsignedByte() | dis.readUnsignedByte()<<8 | dis.readUnsignedByte()<<16 | dis.readUnsignedByte()<<24);
+ IHsize = readInt();
+ IHwidth = readInt();
+ IHheight = readInt();
+ IHplanes = readShort();
+ IHbitCount = readShort();
+ IHcompression = readInt();
+ IHsizeImage = readInt();
+ IHxpelsPerMeter = readInt();
+ IHypelsPerMeter = readInt();
+ IHcolorsUsed = readInt();
+ IHcolorsImportant = readInt();
// allocate memory for the pixel data
data = new byte[IHsizeImage];
- // move to the pixel data
- dis.seek(FHoffsetBits);
-
- // read the pixel data
- dis.read(data,0,IHsizeImage);
+ System.arraycopy(fileContents, FHoffsetBits, data, 0, IHsizeImage);
+ fileContents = null;
// swap the R and B values to get RGB, bitmap color format is BGR
for(int loop=0;loop<IHsizeImage;loop+=3)
@@ -158,11 +188,21 @@
x.printStackTrace();
System.out.println(x.getMessage());
}
- finally
- {
- try{dis.close();}catch(Exception x){}
}
+
+ private short readShort(){
+ int s1 = (fileContents[filePointer++] & 0xFF),
+ s2 = (fileContents[filePointer++] & 0xFF) << 8;
+ return ((short)(s1 | s2));
}
+
+ private int readInt(){
+ return (fileContents[filePointer++] & 0xFF) |
+ (fileContents[filePointer++] & 0xFF) << 8|
+ (fileContents[filePointer++] & 0xFF) << 16|
+ (fileContents[filePointer++] & 0xFF) << 24;
+ }
+
public static void main(String[] args) {
WindowsBitmapFile bf = new WindowsBitmapFile();
I can’t test it but as I am only using JPG and PNG files
I’m guessing the code is David’s so he’ll have to commit it (or at least approve the changes and I could commit it from my local copy).
Will.
[quote]Download
PS: To whoever wrote those two classes:
When I was making the changes, I tried reading the files using a DataInputStream, but for some reason when I attempted loading a file from a jar, it threw an exception about mark/reset, function that I used to compensate the lack of rewind().
Anyways I switched to regular InputStream and now everything is ok
[/quote]
The classes were originally written by me, as well as the initial versions of the scene graph classes. I wasn’t getting any farther with my LWJGL implementation so I gave the code to David so he didn’t have to waste time implementing the basics of the graph.
I’m extreemly happy this happened, you all are making one heck of a good library, way better than I could have done.
EDIT: original thread http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=LWJGL;action=display;num=1049466445;start=0
The thread were it went to David http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=News;action=display;num=1056635395;start=32#32
Scott,
Thanks for the background info - we really should add a credit to you in the project and an @author tag in those files for your contribution!
Will.
Funny, when I put a demo together in a jar file and move it around on my pc 4 partitions, it works just fine meaning that it loads the media from within the jar parent.
However when I set up a JNLP file, it fails miserably to load my images through WindowsBitmapFile or TargaFile classes.
When I put the mentioned classes in the same directory than the main class, everything works.
Now that pisses me off to no extent >:(
JWS is a fickle thing. I had many problems getting images to work too, even though I had done that many times previously just with plain jar files.
???
Will.
[quote]Funny, when I put a demo together in a jar file and move it around on my pc 4 partitions, it works just fine meaning that it loads the media from within the jar parent.
However when I set up a JNLP file, it fails miserably to load my images through WindowsBitmapFile or TargaFile classes.
When I put the mentioned classes in the same directory than the main class, everything works.
[/quote]
I experienced the same problem. Thanks to Kev’s ClassLoader three-line-tutorial I replaced the following stream loading line of code (of a static class file) :
InputStream ist = Myklass.class.getClass().getResourceAsStream(file_url);
whith the following one:
InputStream ist = Myklass.class.getClassLoader().getResourceAsStream(file_url);
Any everything works fine now. There’s something with the security manager which the first line didn’t take into account. So while it worked fine on local JARs, it failed with JNLP because of security issues.
( As usual, the file_url is relative. Say, the directory hierarchy of your JAR would look like:
./com/name/your_application/...*.class
./pictures/portrait.png
Then file_url would be equal to “pictures/portrait.png” for example. )
Tried that, still fails when I attempt loading media through JWS :’(
:sad:
As you might guess, martian madness uses a fair bit of media… what error are you getting exactly? And what code are you using to reference the files?
It could be something in WindowsBitmapFile and TargerFile classes causing the problem. The class loader access to the jar file doesn’t seem to be identical in JWS as to standard ClassLoader.
Kev
[quote]As you might guess, martian madness uses a fair bit of media… what error are you getting exactly? And what code are you using to reference the files?
It could be something in WindowsBitmapFile and TargerFile classes causing the problem. The class loader access to the jar file doesn’t seem to be identical in JWS as to standard ClassLoader.
Kev
[/quote]
Umm I think somethings is wrong with my system as I can’t get beyond the settings screen in Martian Madness :
PS: It loads the music just fine though
Coo, do you have the Java Web Start console up?
Kev
Assuming that we’re talking about MM, nope there is no console up: Just plain sound and music.
Not even a window unless I select fullscreen to only be greeted by a blank frame.
PS: None of the tutorial demos that use textures work, for example crude tank only shows white meshes…
I’m positive my Xith3D.jar is up to date though
There’s a configuration option for Web Start that allows you to see the java console as the app is running…
Interesting tho, you see the setup window (which must be loading images?) but you can’t load anything else… bizaare!
The option is in File->Preferences, in the Advanced Tab.
Kev
[quote]PS: None of the tutorial demos that use textures work, for example crude tank only shows white meshes…
I’m positive my Xith3D.jar is up to date though
[/quote]
prey tell - is xith3d.jar in your jre/lib/ext directory?
Dunno if you’ve read this doco recently: http://xith.org/tutes/GettingStarted/html/deploying_xith3d_games_with.html
It now carries the warning: “Be warned that there may be conflicts if you have Xith3D installed (i.e. xith3d.jar) into your java classpath (i.e. jre/lib/ext/”)
And this doco: http://xith.org/installing.php
Now says:
Don’t ask me why JWS does this caus the only reason I can think of is because it’s lame.
I tore out my hair for the best part of an afternoon finding that out…
Trust me - the textures in the Xith3D JWS demos (official and GSG) work.
Will.
Score, yay, happy.png
After taking a look at the oggloader, I made few modifications and got everything working not only with Jar files, but also JWS.
Yay again
public static BufferedImage getBufferedImage(Object file){
WindowsBitmapFile loader = new WindowsBitmapFile();
if(file instanceof String)
loader.load((String)file);
else
if(file instanceof URL)
loader.load((URL)file);
else
if(file instanceof InputStream)
loader.load((InputStream)file);
else
return (BufferedImage)null;
int width = loader.getWidth(),
height = loader.getHeight();
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
byte[] imageData = loader.getData();
for(int j = height - 1; j >= 0; j--)
for(int i = 0; i < width; i++) {
int index = ((height - 1 - j)* width + i) * 3,
color= ( 255 & 0xFF) << 24|
(imageData[index + 2] & 0xFF) << 16|
(imageData[index + 1] & 0xFF) << 8|
(imageData[index + 0] & 0xFF);
bufferedImage.setRGB(i,j, color);
}
return bufferedImage;
}
public void load(URL url) {
try {
dis = new java.io.BufferedInputStream(url.openStream());
} catch(Exception ex) {
System.err.println("file " + url + " could not be loaded");
}
load();
}
/**
* Loads the file from an input stream.
*/
public void load(InputStream data) {
try{
dis = new java.io.BufferedInputStream(data);
}catch(Exception e){
}
load();
}
To use it with a jar or jws application
private BufferedImage loadBitmap(String textureName){
try{
return com.xith3d.imaging.WindowsBitmapFile.getBufferedImage(getClass().getClassLoader().getResource(textureName));
}catch(Exception e){
System.out.println(e);
}
return (BufferedImage)null;
}
Ah - so you’re not using the standard com.xith3d.loaders.texture.TextureLoader class?
It’s method to load the image is called “loadImageFast” and can handle loading images from a Jar (and JWS). That method calls DirectBufferedImage.loadImage. If that method could load Targa and Bitmap files as well that would be great.
No point having two different BufferedImage loading methods - any change we can merge them?
Will.