Proper tga.gz loading using own imagedata

This commit is contained in:
Song Minjae
2017-04-19 20:53:03 +09:00
parent fcd378c51a
commit 2895f4be6d
3 changed files with 416 additions and 31 deletions

View File

@@ -0,0 +1,328 @@
package net.torvald.slick.opengl;
/*
* A utility to load .tga.gz
*
* Created by SKYHi14 on 2017-04-19.
*/
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.GZIPInputStream;
import org.lwjgl.BufferUtils;
import org.newdawn.slick.opengl.LoadableImageData;
/**
* A utility to load TGAs. Note: NOT THREAD SAFE
*
* Fresh cut of code but largely influeneced by the TGA loading class
* provided as part of the Java Monkey Engine (JME). Why not check out
* what they're doing over at http://www.jmonkeyengine.com. kudos to
* Mark Powell.
*
* @author Kevin Glass
*/
public class TGAGzImageData implements LoadableImageData {
/** The width of the texture that needs to be generated */
private int texWidth;
/** The height of the texture that needs to be generated */
private int texHeight;
/** The width of the TGA image */
private int width;
/** The height of the TGA image */
private int height;
/** The bit depth of the image */
private short pixelDepth;
/**
* Create a new TGA Loader
*/
public TGAGzImageData() {
}
/**
* Flip the endian-ness of the short
*
* @param signedShort The short to flip
* @return The flipped short
*/
private short flipEndian(short signedShort) {
int input = signedShort & 0xFFFF;
return (short) (input << 8 | (input & 0xFF00) >>> 8);
}
/**
* @see org.newdawn.slick.opengl.ImageData#getDepth()
*/
public int getDepth() {
return pixelDepth;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getWidth()
*/
public int getWidth() {
return width;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getHeight()
*/
public int getHeight() {
return height;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexWidth()
*/
public int getTexWidth() {
return texWidth;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexHeight()
*/
public int getTexHeight() {
return texHeight;
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream)
*/
public ByteBuffer loadImage(InputStream fis) throws IOException {
return loadImage(fis,true, null);
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, int[])
*/
public ByteBuffer loadImage(InputStream fis, boolean flipped, int[] transparent) throws IOException {
return loadImage(fis, flipped, false, transparent);
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream, boolean, boolean, int[])
*/
public ByteBuffer loadImage(InputStream fis, boolean flipped, boolean forceAlpha, int[] transparent) throws IOException {
if (transparent != null) {
forceAlpha = true;
}
byte red = 0;
byte green = 0;
byte blue = 0;
byte alpha = 0;
BufferedInputStream bis = new BufferedInputStream(new GZIPInputStream(fis, 8192), 16384);
DataInputStream dis = new DataInputStream(bis);
// Read in the Header
short idLength = (short) dis.read();
short colorMapType = (short) dis.read();
short imageType = (short) dis.read();
short cMapStart = flipEndian(dis.readShort());
short cMapLength = flipEndian(dis.readShort());
short cMapDepth = (short) dis.read();
short xOffset = flipEndian(dis.readShort());
short yOffset = flipEndian(dis.readShort());
if (imageType != 2) {
throw new IOException("Slick only supports uncompressed RGB(A) TGA images");
}
width = flipEndian(dis.readShort());
height = flipEndian(dis.readShort());
pixelDepth = (short) dis.read();
if (pixelDepth == 32) {
forceAlpha = false;
}
texWidth = get2Fold(width);
texHeight = get2Fold(height);
short imageDescriptor = (short) dis.read();
if ((imageDescriptor & 0x0020) == 0) {
flipped = !flipped;
}
// Skip image ID
if (idLength > 0) {
bis.skip(idLength);
}
byte[] rawData = null;
if ((pixelDepth == 32) || (forceAlpha)) {
pixelDepth = 32;
rawData = new byte[texWidth * texHeight * 4];
} else if (pixelDepth == 24) {
rawData = new byte[texWidth * texHeight * 3];
} else {
throw new RuntimeException("Only 24 and 32 bit TGAs are supported");
}
if (pixelDepth == 24) {
if (flipped) {
for (int i = height-1; i >= 0; i--) {
for (int j = 0; j < width; j++) {
blue = dis.readByte();
green = dis.readByte();
red = dis.readByte();
int ofs = ((j + (i * texWidth)) * 3);
rawData[ofs] = red;
rawData[ofs + 1] = green;
rawData[ofs + 2] = blue;
}
}
} else {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
blue = dis.readByte();
green = dis.readByte();
red = dis.readByte();
int ofs = ((j + (i * texWidth)) * 3);
rawData[ofs] = red;
rawData[ofs + 1] = green;
rawData[ofs + 2] = blue;
}
}
}
} else if (pixelDepth == 32) {
if (flipped) {
for (int i = height-1; i >= 0; i--) {
for (int j = 0; j < width; j++) {
blue = dis.readByte();
green = dis.readByte();
red = dis.readByte();
if (forceAlpha) {
alpha = (byte) 255;
} else {
alpha = dis.readByte();
}
int ofs = ((j + (i * texWidth)) * 4);
rawData[ofs] = red;
rawData[ofs + 1] = green;
rawData[ofs + 2] = blue;
rawData[ofs + 3] = alpha;
if (alpha == 0) {
rawData[ofs + 2] = (byte) 0;
rawData[ofs + 1] = (byte) 0;
rawData[ofs] = (byte) 0;
}
}
}
} else {
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
blue = dis.readByte();
green = dis.readByte();
red = dis.readByte();
if (forceAlpha) {
alpha = (byte) 255;
} else {
alpha = dis.readByte();
}
int ofs = ((j + (i * texWidth)) * 4);
if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
rawData[ofs] = red;
rawData[ofs + 1] = green;
rawData[ofs + 2] = blue;
rawData[ofs + 3] = alpha;
} else {
rawData[ofs] = red;
rawData[ofs + 1] = green;
rawData[ofs + 2] = blue;
rawData[ofs + 3] = alpha;
}
if (alpha == 0) {
rawData[ofs + 2] = 0;
rawData[ofs + 1] = 0;
rawData[ofs] = 0;
}
}
}
}
}
fis.close();
if (transparent != null) {
for (int i=0;i<rawData.length;i+=4) {
boolean match = true;
for (int c=0;c<3;c++) {
if (rawData[i+c] != transparent[c]) {
match = false;
}
}
if (match) {
rawData[i+3] = 0;
}
}
}
// Get a pointer to the image memory
ByteBuffer scratch = BufferUtils.createByteBuffer(rawData.length);
scratch.put(rawData);
int perPixel = pixelDepth / 8;
if (height < texHeight-1) {
int topOffset = (texHeight-1) * (texWidth*perPixel);
int bottomOffset = (height-1) * (texWidth*perPixel);
for (int x=0;x<texWidth*perPixel;x++) {
scratch.put(topOffset+x, scratch.get(x));
scratch.put(bottomOffset+(texWidth*perPixel)+x, scratch.get((texWidth*perPixel)+x));
}
}
if (width < texWidth-1) {
for (int y=0;y<texHeight;y++) {
for (int i=0;i<perPixel;i++) {
scratch.put(((y+1)*(texWidth*perPixel))-perPixel+i, scratch.get(y*(texWidth*perPixel)+i));
scratch.put((y*(texWidth*perPixel))+(width*perPixel)+i, scratch.get((y*(texWidth*perPixel))+((width-1)*perPixel)+i));
}
}
}
scratch.flip();
return scratch;
}
/**
* Get the closest greater power of 2 to the fold number
*
* @param fold The target number
* @return The power of 2
*/
private int get2Fold(int fold) {
int ret = 2;
while (ret < fold) {
ret *= 2;
}
return ret;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
*/
public ByteBuffer getImageBufferData() {
throw new RuntimeException("TGAImageData doesn't store it's image.");
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean)
*/
public void configureEdging(boolean edging) {
}
}

View File

@@ -33,40 +33,10 @@ object TilesDrawer {
private val TILE_SIZE = FeaturesDrawer.TILE_SIZE
private val TILE_SIZEF = FeaturesDrawer.TILE_SIZE.toFloat()
//val tilesTerrain = SpriteSheet(ModMgr.getPath("basegame", "tiles/terrain.tga"), TILE_SIZE, TILE_SIZE)
val tilesTerrain = SpriteSheet(ModMgr.getPath("basegame", "tiles/terrain.tga.gz"), TILE_SIZE, TILE_SIZE)
// Slick has some weird quirks with PNG's transparency. I'm using 32-bit targa here.
val tilesWire = SpriteSheet(ModMgr.getPath("basegame", "tiles/wire.tga"), TILE_SIZE, TILE_SIZE)
val tilesTerrain: SpriteSheet
init {
// read DEFLATEd terrain.tar
val tgaLoader = TGAImageData()
val terrainImageData = tgaLoader.loadImage(
GZIPInputStream(
FileInputStream(ModMgr.getFile("basegame", "tiles/terrain.tga.gz")),
8192
), false, null)
terrainImageData.rewind()
/*val terrainTex = InternalTextureLoader.get().getTexture(object : ImageData {
override fun getHeight(): Int = tgaLoader.height
override fun getTexWidth(): Int = tgaLoader.texWidth
override fun getDepth(): Int = tgaLoader.depth
override fun getImageBufferData(): ByteBuffer = terrainImageData
override fun getWidth(): Int = tgaLoader.width
override fun getTexHeight(): Int = tgaLoader.texHeight
}, Image.FILTER_NEAREST)*/
//// method 1
//val terrainImage = Image(terrainTex)
//tilesTerrain = SpriteSheet(terrainImage, TILE_SIZE, TILE_SIZE)
//// method 2
val terrainImgBuffer = ImageBuffer(tgaLoader.width, tgaLoader.height)
terrainImageData.get(terrainImgBuffer.rgba)
tilesTerrain = SpriteSheet(terrainImgBuffer.image, TILE_SIZE, TILE_SIZE)
}
val breakAnimSteps = 10

View File

@@ -0,0 +1,87 @@
package org.newdawn.slick.opengl;
/**
* Modifications: Added support for .tga.gz
*
* Created by SKYHi14 on 2017-04-19.
*/
import net.torvald.slick.opengl.TGAGzImageData;
import org.newdawn.slick.util.Log;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* A static utility to create the appropriate image data for a particular reference.
*
* @author kevin
*/
public class ImageDataFactory {
/** True if we're going to use the native PNG loader - cached so it doesn't have
* the security check repeatedly
*/
private static boolean usePngLoader = true;
/** True if the PNG loader property has been checked */
private static boolean pngLoaderPropertyChecked = false;
/** The name of the PNG loader configuration property */
private static final String PNG_LOADER = "org.newdawn.slick.pngloader";
/**
* Check PNG loader property. If set the native PNG loader will
* not be used.
*/
private static void checkProperty() {
if (!pngLoaderPropertyChecked) {
pngLoaderPropertyChecked = true;
try {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
String val = System.getProperty(PNG_LOADER);
if ("false".equalsIgnoreCase(val)) {
usePngLoader = false;
}
Log.info("Use Java PNG Loader = " + usePngLoader);
return null;
}
});
} catch (Throwable e) {
// ignore, security failure - probably an applet
}
}
}
/**
* Create an image data that is appropriate for the reference supplied
*
* @param ref The reference to the image to retrieve
* @return The image data that can be used to retrieve the data for that resource
*/
public static LoadableImageData getImageDataFor(String ref) {
LoadableImageData imageData;
checkProperty();
ref = ref.toLowerCase();
if (ref.endsWith(".tga")) {
return new TGAImageData();
}
if (ref.endsWith(".tga.gz")) {
return new TGAGzImageData();
}
if (ref.endsWith(".png")) {
CompositeImageData data = new CompositeImageData();
if (usePngLoader) {
data.add(new PNGImageData());
}
data.add(new ImageIOImageData());
return data;
}
return new ImageIOImageData();
}
}