RGBImageLoader.java
[java]
package com.jme3.texture.plugins;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetLoader;
import com.jme3.texture.Image;
import com.jme3.texture.Image.Format;
public class RGBImageLoader implements AssetLoader {
// constants:
private static final short MAGIC_NUMBER = 474;
private static final byte RLE = 1;
/** Reference to the Logger object. /
private static Logger logger = Logger.getLogger("RGBImageLoader");
// Extensions: rgb, rgba, attr, int
static class RLETable {
// read the offset tables
int sy, sz, rleByteSize;
int[] rleLengths, rleStarts; // ysizezsize*4 bytes
RLETable(SGIFileReader sfr, int sy, int sz) throws IOException {
this.sz = sz;
this.sy = sy;
int numScanlines = sy * sz; // table length
// read in the starts and sizes
// [1…numScanlines] of start table
// [1…numScanlines] of length table
rleStarts = new int[numScanlines];
for (int i = 0; i < numScanlines; i++) {
sfr.setOffset(512 + i * 4);
rleStarts = sfr.readInt();
}
rleLengths = new int[numScanlines];
for (int i = 0; i < numScanlines; i++) {
sfr.setOffset(512 + numScanlines * 4 + i * 4);
rleLengths = sfr.readInt();
}
rleByteSize = rleStarts.length + rleLengths.length;
}
public String toString() {
String sizes = "n";
for (int z = 0; z < sz; z++) {
for (int y = 0; y < sy; y++) {
int offset = z * sy + y;
sizes += "(" + z + "," + y + ")" + rleStarts[offset] + ":"
- rleLengths[offset] + "n";
}
}
return sizes;
}
};
public static final Image load(InputStream stream, String name) {
SGIFileReader sfr = null;
int magic; // IRIS image file magic number
int storageFormat; // storage format this really a char
int bytesPerChannel; // number of bits per channel this really a char
int dimension; // number of dimensions
int xSize; // x size in pixels
int ySize; // y size in pixels
int zSize; // number of channels
try {
sfr = new SGIFileReader(stream);
sfr.setOffset(0);
magic = sfr.readShort();
// if number is not 474 then its not a SGI RGB file
if (magic != MAGIC_NUMBER) {
logger.log(Level.SEVERE,"File is not SGI RGB format! ");
throw new IOException("Unrecognized file format.");
}
storageFormat = sfr.readByte(); // storageFormat is 1 RLE is used
bytesPerChannel = sfr.readByte(); // could be 1 or 2 nothing more
if (bytesPerChannel != 1) {
logger.log(Level.SEVERE,"Reader supports only 1 byte per channel – given "
- bytesPerChannel + " in file:" + name);
return null;
}
dimension = sfr.readShort(); // could be 1,2, or 3
if (dimension != 2 && dimension != 3) { // if 1 only one channel
// used from xsize
logger.log(Level.SEVERE,"File " + name + " only has given dimensions=" + dimension
- " only 2 or 3 supported");
}
xSize = sfr.readShort(); // The width of the image in pixels
// //length of the scanline
ySize = sfr.readShort(); // The height of the image in pixels
zSize = sfr.readShort(); // The number of channels in the image.
// Could be 1,3, or 4
// 1 is Black & White 3 is Color RGB and 4 is Color RGB with Alpha
sfr.readInt();
sfr.readInt();
sfr.skipBytes(4);
sfr.readString(80); //imageName
sfr.readInt();
sfr.skipBytes(404);
// allocate pixel buffer:
int nPixels = ySize * xSize;
if (nPixels > 1024 * 1024) {
logger.log(Level.SEVERE,"File " + name + " size out of bounds:" + ySize + "," + xSize);
throw new IOException("Size out of bounds");
}
Format imageType;
int newSize = zSize;
if (zSize == 1) {
// Black and White image
// JME does not have an image format for black and white
// using the same as zSize = 2
imageType = Format.Alpha16;
newSize = 2;
} else if (zSize == 2) {
// Black and White image with alpha
imageType = Format.Alpha16;
} else if (zSize == 3) {
// RGB Image
imageType = Format.RGB8;
} else if (zSize == 4) {
// RGBA Image
imageType = Format.RGBA8;
} else {
throw new IOException("Unsupported options in file");
}
byte image[] = new byte[nPixels * newSize];
byte[] rowBytes = new byte[xSize]; // num bytes per uncompressed row
byte[] rleBytes = new byte[xSize * 2];
RLETable rleTable = null;
if (storageFormat == RLE) {
image = new byte[nPixels * 4];
// if alpha not included in file we need to set the alpha of
// each pixel to 0xff or the image will be invisible:
if (zSize<4) {
for(int n=3; n+4<image.length; n+=4) image[n] = (byte)0xff;
}
rleTable = new RLETable(sfr, ySize, zSize);
imageType = Format.RGBA8;
}
/if (zSize == 1) {
for(int n=1; n+2<image.length; n+=2) image[n] = (byte)0xff;
}/
int t;
for (short z = 0; z < zSize; z++) {
boolean blackAndWhite = false;
int writeToByte = 0;
// read bit plane z
// ZSIZE == 4 – r,g,b,a
// ZSIZE == 3 – r,g,b,0xff
// ZSIZE == 2 – g,g,g,a (B&W)
// ZSIZE == 1 – g,g,g,0xff //this should be B&W
switch (zSize) {
case 1: blackAndWhite = true; break;
case 2: writeToByte = (z == 0 ? 0 : 3); break;
case 3:
case 4: writeToByte = z; break;
}
for (int y = 0; y < ySize; y++) {
// Just for RLE
if (storageFormat == RLE) {
// System.out.println("Has RLE");
int rleOffset = z * ySize + y;
int readLength = rleTable.rleLengths[rleOffset], readOffset = rleTable.rleStarts[rleOffset];
sfr.readBytes(rleBytes, readLength, sfr
.getFileContents(), readOffset);
rleBytes[readLength] = 0; // add sentinel
int count = -1, outputPos = 0, currRLEPos = 0;
while (outputPos < rowBytes.length) {
byte pixel = rleBytes[currRLEPos++];
count = (int) (pixel & 0x7F);
if (count == 0)
break;
if ((pixel & 0x80) != 0) {
// no RLE for this part
for (; count > 0; count–) {
rowBytes[outputPos++] = rleBytes[currRLEPos++];
}
} else {
// RLE for this part
for (; count > 0; count–) {
rowBytes[outputPos++] = rleBytes[currRLEPos];
}
currRLEPos++;
}
}
if (outputPos < rowBytes.length) {
System.out.println("RLE error!" + outputPos + ":"
- rowBytes.length);
}
int offset = yxSize4; // beg of pixel
// merge in the uncompressed row data
for(int i=0; i<rowBytes.length; i++,offset+=4) {
byte b = rowBytes;
if (blackAndWhite && writeToByte < 3) {
image[offset+0] = b;
image[offset+1] = b;
image[offset+2] = b;
}
else {
image[offset+writeToByte] = b;
}
}
}else{
//if it doesn’t have RLE format
for (short x = 0; x < xSize; x++) {
t = sfr.readByte();
//black & white with 1 channel
if(zSize == 1){
image[(y * (xSize * zSize) + (x * zSize) + z)] = (byte) t;
image[(y * (xSize * zSize) + (x * zSize) + z) + (image.length/2)] = (byte) t;
}else{
image[y * (xSize * zSize) + (x * zSize) + z] = (byte) t;
}
}
}
}
}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(image.length);
byteBuffer.put(image);
Image newImage = new Image(imageType, xSize, ySize, byteBuffer);
return newImage;
} catch (Throwable e) {
logger.log(Level.SEVERE,“File " + name + " cannot be read”);
}
return null;
}
public Object load(AssetInfo assetInfo) throws IOException {
Image image = load(assetInfo.openStream(),assetInfo.getKey().getName());
return image;
}
}
[/java]
SGIFileReader.java
[java]
package com.jme3.texture.plugins;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SGIFileReader {
private byte[] fileContents;
private int fileIndex = 0;
private int markedPos = 0;
/** Reference to the Logger object. */
private static Logger logger = Logger.getLogger(“SGIFileReader”);
/**
- Constructor creates a new <code>SGIFileReader</code> class.
*/
public SGIFileReader(InputStream md2) {
try {
DataInputStream bis=new DataInputStream(md2);
fileContents = new byte[bis.available()];
bis.readFully(fileContents);
bis.close();
} catch (IOException e) {
logger.log(Level.SEVERE,"Could not read InputStream ");
}
}
/**
*
- <code>readByte</code> reads a single byte from the array and
- returns this. The file index is then increased by one.
-
@return the byte at the current index.
*/
public int readByte() {
int b1 = (fileContents[fileIndex] );
fileIndex += 1;
return (b1);
}
/**
*
- <code>readShort</code> reads two bytes from the array, generating
- a short. The file index is then increased by two. The short is then
- inserted into an integer for convienience.
-
@return the short at the current index.
*/
public int readShort() {
int s1 = (fileContents[fileIndex + 1] & 0xFF) << 0;
int s2 = (fileContents[fileIndex] & 0xFF) << 8;
fileIndex += 2;
return (s1 | s2);
}
/**
*
- <code>readInt</code> reads four bytes from the array, generating
- an int. The file index is then increased by four.
-
@return the int at the currrent index.
*/
public int readInt() {
int i1 = (fileContents[fileIndex] & 0xFF) << 24;
int i2 = (fileContents[fileIndex + 1] & 0xFF) << 16;
int i3 = (fileContents[fileIndex + 2] & 0xFF) << 8;
int i4 = (fileContents[fileIndex + 3] & 0xFF) << 0;
fileIndex += 4;
return (i1 | i2 | i3 | i4);
}
/**
*
- <code>readFloat</code> reads four bytes from the array, generating
- a float. The file index is then increased by four.
-
@return the float at the current index.
*/
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
/**
*
- <code>readString</code> reads a specified number of bytes to
- form a string. The length of the string (number of characters)
- is required to notify when reading should stop. The index is
- increased the number of characters read.
-
@param size the length of the string to read.
-
@return the string read.
*/
public String readString(int size) {
//Look for zero terminated string from byte array
for (int i = fileIndex; i < fileIndex + size; i++) {
if (fileContents == (byte) 0) {
String s = new String(fileContents, fileIndex, i - fileIndex);
fileIndex += size;
return s;
}
}
String s = new String(fileContents, fileIndex, size);
fileIndex += size;
return s;
}
/**
*
- <code>getCurrentOffset</code> sets the index of the file data.
-
@param offset the new index of the file pointer.
*/
public int getCurrentOffset() {
return fileIndex;
}
/**
*
- <code>setOffset</code> sets the index of the file data.
-
@param offset the new index of the file pointer.
*/
public void setOffset(int offset) {
if (offset < 0 || offset > fileContents.length) {
logger.log(Level.SEVERE,"Illegal offset value. " + offset);
}
fileIndex = offset;
}
/**
- Sets a mark for a later seekMarkOffset call.
*/
public void markPos(){
markedPos=fileIndex;
}
/**
- Seeks to the position of the last mark + offset.
-
@param offset The Offset relative to mark.
*/
public void seekMarkOffset(int offset){
fileIndex=markedPos+offset;
if (fileIndex < 0 || fileIndex > fileContents.length){
logger.log(Level.SEVERE,"Illegal offset value. " + offset);
}
}
/**
- Reads a signed short value.
-
@return The signed short.
*/
public short readSignedShort() {
return (short) readShort();
}
public void skipBytes(int n) {
fileIndex += n;
}
/**
- Return the byte array associated with this input stream.
*/
public byte[] getFileContents() {
return fileContents;
}
public void readBytes(byte[] bytIn, int nBytes, byte[] buf, int offset) {
System.arraycopy(buf, offset, bytIn, 0, nBytes);
}
}
[/java]