package org.newdawn.slick.opengl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.lwjgl.BufferUtils;
import org.newdawn.slick.util.Log;
/**
* The PNG imge data source that is pure java reading PNGs
*
* @author Matthias Mann (original code)
*/
public class PNGImageData implements LoadableImageData {
/** The width of the data loaded */
private int width;
/** The height of the data loaded */
private int height;
/** The texture height */
private int texHeight;
/** The texture width */
private int texWidth;
/** The decoder used to load the PNG */
private PNGDecoder decoder;
/** The data format of this PNG */
private Format format;
/** The scratch buffer storing the image data */
private ByteBuffer scratch;
/**
* @see org.newdawn.slick.opengl.ImageData#getFormat()
*/
public Format getFormat() {
return format;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
*/
public ByteBuffer getImageBufferData() {
return scratch;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexHeight()
*/
public int getTexHeight() {
return texHeight;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexWidth()
*/
public int getTexWidth() {
return texWidth;
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#loadImage(java.io.InputStream)
*/
public ByteBuffer loadImage(InputStream fis) throws IOException {
return loadImage(fis, false, 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;
}
PNGDecoder decoder = new PNGDecoder(fis);
width = decoder.getWidth();
height = decoder.getHeight();
texWidth = get2Fold(width);
texHeight = get2Fold(height);
final PNGDecoder.Format decoderFormat;
if (forceAlpha) {
if (decoder.isRGB()) {
decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.RGBA);
} else {
decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.LUMINANCE_ALPHA);
}
} else {
decoderFormat = decoder.decideTextureFormat(PNGDecoder.Format.LUMINANCE);
}
switch (decoderFormat) {
case RGB:
format = Format.RGB;
break;
case RGBA:
format = Format.RGBA;
break;
case BGRA:
format = Format.BGRA;
break;
case LUMINANCE:
format = Format.GRAY;
break;
case LUMINANCE_ALPHA:
format = Format.GRAYALPHA;
break;
default:
throw new IOException("Unsupported Image format.");
}
int perPixel = format.getColorComponents();
// Get a pointer to the image memory
scratch = BufferUtils.createByteBuffer(texWidth * texHeight * perPixel);
if (flipped) {
decoder.decodeFlipped(scratch, texWidth * perPixel, decoderFormat);
} else {
decoder.decode(scratch, texWidth * perPixel, decoderFormat);
}
if (height < texHeight-1) {
int topOffset = (texHeight-1) * (texWidth*perPixel);
int bottomOffset = (height-1) * (texWidth*perPixel);
for (int x=0;x<texWidth;x++) {
for (int i=0;i<perPixel;i++) {
scratch.put(topOffset+x+i, scratch.get(x+i));
scratch.put(bottomOffset+(texWidth*perPixel)+x+i, scratch.get(bottomOffset+x+i));
}
}
}
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.position(0);
if (transparent != null) {
// components will now be + 1
final int components = format.getColorComponents();
if (transparent.length != components - 1) {
Log.warn("The amount of color components of the transparent color does not fit the number of color components of the actual image.");
}
if (transparent.length < components - 1) {
Log.error("Failed to apply transparent color, not enough color values in color definition.");
} else {
final int size = texWidth * texHeight * components;
boolean match;
for (int i = 0; i < size; i += components) {
match = true;
for (int c = 0; c < components - 1; c++) {
if (toInt(scratch.get(i + c)) != transparent[c]) {
match = false;
break;
}
}
if (match) {
scratch.put(i + components - 1, (byte) 0);
}
}
}
}
scratch.position(0);
return scratch;
}
/**
* Safe convert byte to int
*
* @param b The byte to convert
* @return The converted byte
*/
private int toInt(byte b) {
if (b < 0) {
return 256+b;
}
return b;
}
/**
* 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.LoadableImageData#configureEdging(boolean)
*/
public void configureEdging(boolean edging) {
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}