RGBA, RGB, SGI, ATTR, INT image loader

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; // ysize
zsize*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]
3 Likes

and addition to desktop.cfg:



LOADER com.jme3.texture.plugins.RGBImageLoader : rgb, attr, rgba, int