added sources for Slick

Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054
Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
Song Minjae
2016-12-30 23:29:12 +09:00
parent d1f01a203d
commit d3080ffb78
329 changed files with 58400 additions and 7 deletions

View File

@@ -0,0 +1,42 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.util.ArrayList;
/**
* A collection of IOException that failed image data loading
*
* @author kevin
*/
public class CompositeIOException extends IOException {
/** The list of exceptions causing this one */
private ArrayList exceptions = new ArrayList();
/**
* Create a new composite IO Exception
*/
public CompositeIOException() {
super();
}
/**
* Add an exception that caused this exceptino
*
* @param e The exception
*/
public void addException(Exception e) {
exceptions.add(e);
}
/**
* @see java.lang.Throwable#getMessage()
*/
public String getMessage() {
String msg = "Composite Exception: \n";
for (int i=0;i<exceptions.size();i++) {
msg += "\t"+((IOException) exceptions.get(i)).getMessage()+"\n";
}
return msg;
}
}

View File

@@ -0,0 +1,151 @@
package org.newdawn.slick.opengl;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import org.newdawn.slick.util.Log;
/**
* A composite data source that checks multiple loaders in order of
* preference
*
* @author kevin
*/
public class CompositeImageData implements LoadableImageData {
/** The list of images sources in order of preference to try loading the data with */
private ArrayList sources = new ArrayList();
/** The data source that worked and was used - or null if no luck */
private LoadableImageData picked;
/**
* Add a potentional source of image data
*
* @param data The data source to try
*/
public void add(LoadableImageData data) {
sources.add(data);
}
/**
* @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 is, boolean flipped, boolean forceAlpha, int[] transparent) throws IOException {
CompositeIOException exception = new CompositeIOException();
ByteBuffer buffer = null;
BufferedInputStream in = new BufferedInputStream(is, is.available());
in.mark(is.available());
// cycle through our source until one of them works
for (int i=0;i<sources.size();i++) {
in.reset();
try {
LoadableImageData data = (LoadableImageData) sources.get(i);
buffer = data.loadImage(in, flipped, forceAlpha, transparent);
picked = data;
break;
} catch (Exception e) {
Log.warn(sources.get(i).getClass()+" failed to read the data", e);
exception.addException(e);
}
}
if (picked == null) {
throw exception;
}
return buffer;
}
/**
* Check the state of the image data and throw a
* runtime exception if theres a problem
*/
private void checkPicked() {
if (picked == null) {
throw new RuntimeException("Attempt to make use of uninitialised or invalid composite image data");
}
}
/**
* @see org.newdawn.slick.opengl.ImageData#getDepth()
*/
public int getDepth() {
checkPicked();
return picked.getDepth();
}
/**
* @see org.newdawn.slick.opengl.ImageData#getHeight()
*/
public int getHeight() {
checkPicked();
return picked.getHeight();
}
/**
* @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
*/
public ByteBuffer getImageBufferData() {
checkPicked();
return picked.getImageBufferData();
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexHeight()
*/
public int getTexHeight() {
checkPicked();
return picked.getTexHeight();
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexWidth()
*/
public int getTexWidth() {
checkPicked();
return picked.getTexWidth();
}
/**
* @see org.newdawn.slick.opengl.ImageData#getWidth()
*/
public int getWidth() {
checkPicked();
return picked.getWidth();
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean)
*/
public void configureEdging(boolean edging) {
for (int i=0;i<sources.size();i++) {
((LoadableImageData) sources.get(i)).configureEdging(edging);
}
}
}

View File

@@ -0,0 +1,183 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Cursor;
import org.newdawn.slick.util.Log;
import org.newdawn.slick.util.ResourceLoader;
/**
* A utility to load cursors (thanks go to Kappa for the animated cursor
* loader)
*
* @author Kevin Glass
* @author Kappa-One
*/
public class CursorLoader {
/** The single instnace of this loader to exist */
private static CursorLoader single = new CursorLoader();
/**
* Retrieve the single instance of this loader - convinient huh?
*
* @return The single instance of the cursor loader
*/
public static CursorLoader get() {
return single;
}
/**
* Create a new cursor loader
*/
private CursorLoader() {
}
/**
* Get a cursor based on a image reference on the classpath
*
* @param ref The reference to the image to be loaded
* @param x The x-coordinate of the cursor hotspot (left -> right)
* @param y The y-coordinate of the cursor hotspot (bottom -> top)
* @return The create cursor
* @throws IOException Indicates a failure to load the image
* @throws LWJGLException Indicates a failure to create the hardware cursor
*/
public Cursor getCursor(String ref,int x,int y) throws IOException, LWJGLException {
LoadableImageData imageData = null;
imageData = ImageDataFactory.getImageDataFor(ref);
imageData.configureEdging(false);
ByteBuffer buf = imageData.loadImage(ResourceLoader.getResourceAsStream(ref), true, true, null);
for (int i=0;i<buf.limit();i+=4) {
byte red = buf.get(i);
byte green = buf.get(i+1);
byte blue = buf.get(i+2);
byte alpha = buf.get(i+3);
buf.put(i+2, red);
buf.put(i+1, green);
buf.put(i, blue);
buf.put(i+3, alpha);
}
try {
int yspot = imageData.getHeight() - y - 1;
if (yspot < 0) {
yspot = 0;
}
return new Cursor(imageData.getTexWidth(), imageData.getTexHeight(), x, yspot, 1, buf.asIntBuffer(), null);
} catch (Throwable e) {
Log.info("Chances are you cursor is too small for this platform");
throw new LWJGLException(e);
}
}
/**
* Get a cursor based on a set of image data
*
* @param buf The image data (stored in RGBA) to load the cursor from
* @param x The x-coordinate of the cursor hotspot (left -> right)
* @param y The y-coordinate of the cursor hotspot (bottom -> top)
* @param width The width of the image data provided
* @param height The height of the image data provided
* @return The create cursor
* @throws IOException Indicates a failure to load the image
* @throws LWJGLException Indicates a failure to create the hardware cursor
*/
public Cursor getCursor(ByteBuffer buf,int x,int y,int width,int height) throws IOException, LWJGLException {
for (int i=0;i<buf.limit();i+=4) {
byte red = buf.get(i);
byte green = buf.get(i+1);
byte blue = buf.get(i+2);
byte alpha = buf.get(i+3);
buf.put(i+2, red);
buf.put(i+1, green);
buf.put(i, blue);
buf.put(i+3, alpha);
}
try {
int yspot = height - y - 1;
if (yspot < 0) {
yspot = 0;
}
return new Cursor(width,height, x, yspot, 1, buf.asIntBuffer(), null);
} catch (Throwable e) {
Log.info("Chances are you cursor is too small for this platform");
throw new LWJGLException(e);
}
}
/**
* Get a cursor based on a set of image data
*
* @param imageData The data from which the cursor can read it's contents
* @param x The x-coordinate of the cursor hotspot (left -> right)
* @param y The y-coordinate of the cursor hotspot (bottom -> top)
* @return The create cursor
* @throws IOException Indicates a failure to load the image
* @throws LWJGLException Indicates a failure to create the hardware cursor
*/
public Cursor getCursor(ImageData imageData,int x,int y) throws IOException, LWJGLException {
ByteBuffer buf = imageData.getImageBufferData();
for (int i=0;i<buf.limit();i+=4) {
byte red = buf.get(i);
byte green = buf.get(i+1);
byte blue = buf.get(i+2);
byte alpha = buf.get(i+3);
buf.put(i+2, red);
buf.put(i+1, green);
buf.put(i, blue);
buf.put(i+3, alpha);
}
try {
int yspot = imageData.getHeight() - y - 1;
if (yspot < 0) {
yspot = 0;
}
return new Cursor(imageData.getTexWidth(), imageData.getTexHeight(), x, yspot, 1, buf.asIntBuffer(), null);
} catch (Throwable e) {
Log.info("Chances are you cursor is too small for this platform");
throw new LWJGLException(e);
}
}
/**
* Get a cursor based on a image reference on the classpath. The image
* is assumed to be a set/strip of cursor animation frames running from top to
* bottom.
*
* @param ref The reference to the image to be loaded
* @param x The x-coordinate of the cursor hotspot (left -> right)
* @param y The y-coordinate of the cursor hotspot (bottom -> top)
* @param width The x width of the cursor
* @param height The y height of the cursor
* @param cursorDelays image delays between changing frames in animation
*
* @return The created cursor
* @throws IOException Indicates a failure to load the image
* @throws LWJGLException Indicates a failure to create the hardware cursor
*/
public Cursor getAnimatedCursor(String ref,int x,int y, int width, int height, int[] cursorDelays) throws IOException, LWJGLException {
IntBuffer cursorDelaysBuffer = ByteBuffer.allocateDirect(cursorDelays.length*4).order(ByteOrder.nativeOrder()).asIntBuffer();
for (int i=0;i<cursorDelays.length;i++) {
cursorDelaysBuffer.put(cursorDelays[i]);
}
cursorDelaysBuffer.flip();
LoadableImageData imageData = new TGAImageData();
ByteBuffer buf = imageData.loadImage(ResourceLoader.getResourceAsStream(ref), false, null);
return new Cursor(width, height, x, y, cursorDelays.length, buf.asIntBuffer(), cursorDelaysBuffer);
}
}

View File

@@ -0,0 +1,234 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.io.InputStream;
import org.newdawn.slick.loading.DeferredResource;
import org.newdawn.slick.loading.LoadingList;
import org.newdawn.slick.opengl.renderer.SGL;
/**
* A texture proxy that can be used to load a texture at a later date while still
* allowing elements to reference it
*
* @author kevin
*/
public class DeferredTexture extends TextureImpl implements DeferredResource {
/** The stream to read the texture from */
private InputStream in;
/** The name of the resource to load */
private String resourceName;
/** True if the image should be flipped */
private boolean flipped;
/** The filter to apply to the texture */
private int filter;
/** The texture we're proxying for */
private TextureImpl target;
/** The color to be transparent */
private int[] trans;
/**
* Create a new deferred texture
*
* @param in The input stream from which to read the texture
* @param resourceName The name to give the resource
* @param flipped True if the image should be flipped
* @param filter The filter to apply
* @param trans The colour to defined as transparent
*/
public DeferredTexture(InputStream in, String resourceName, boolean flipped, int filter, int[] trans) {
this.in = in;
this.resourceName = resourceName;
this.flipped = flipped;
this.filter = filter;
this.trans = trans;
LoadingList.get().add(this);
}
/**
* @see org.newdawn.slick.loading.DeferredResource#load()
*/
public void load() throws IOException {
boolean before = InternalTextureLoader.get().isDeferredLoading();
InternalTextureLoader.get().setDeferredLoading(false);
target = InternalTextureLoader.get().getTexture(in, resourceName, flipped, filter, trans);
InternalTextureLoader.get().setDeferredLoading(before);
}
/**
* Check if the target has been obtained already
*/
private void checkTarget() {
if (target == null) {
try {
load();
LoadingList.get().remove(this);
return;
} catch (IOException e) {
throw new RuntimeException("Attempt to use deferred texture before loading and resource not found: "+resourceName);
}
}
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#bind()
*/
public void bind() {
checkTarget();
target.bind();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getHeight()
*/
public float getHeight() {
checkTarget();
return target.getHeight();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getImageHeight()
*/
public int getImageHeight() {
checkTarget();
return target.getImageHeight();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getImageWidth()
*/
public int getImageWidth() {
checkTarget();
return target.getImageWidth();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getTextureHeight()
*/
public int getTextureHeight() {
checkTarget();
return target.getTextureHeight();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getTextureID()
*/
public int getTextureID() {
checkTarget();
return target.getTextureID();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getTextureRef()
*/
public String getTextureRef() {
checkTarget();
return target.getTextureRef();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getTextureWidth()
*/
public int getTextureWidth() {
checkTarget();
return target.getTextureWidth();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getWidth()
*/
public float getWidth() {
checkTarget();
return target.getWidth();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#release()
*/
public void release() {
checkTarget();
target.release();
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setAlpha(boolean)
*/
public void setAlpha(boolean alpha) {
checkTarget();
target.setAlpha(alpha);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setHeight(int)
*/
public void setHeight(int height) {
checkTarget();
target.setHeight(height);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setTextureHeight(int)
*/
public void setTextureHeight(int texHeight) {
checkTarget();
target.setTextureHeight(texHeight);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setTextureID(int)
*/
public void setTextureID(int textureID) {
checkTarget();
target.setTextureID(textureID);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setTextureWidth(int)
*/
public void setTextureWidth(int texWidth) {
checkTarget();
target.setTextureWidth(texWidth);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#setWidth(int)
*/
public void setWidth(int width) {
checkTarget();
target.setWidth(width);
}
/**
* @see org.newdawn.slick.opengl.TextureImpl#getTextureData()
*/
public byte[] getTextureData() {
checkTarget();
return target.getTextureData();
}
/**
* @see org.newdawn.slick.loading.DeferredResource#getDescription()
*/
public String getDescription() {
return resourceName;
}
/**
* @see org.newdawn.slick.opengl.Texture#hasAlpha()
*/
public boolean hasAlpha() {
checkTarget();
return target.hasAlpha();
}
/**
* @see org.newdawn.slick.opengl.Texture#setTextureFilter(int)
*/
public void setTextureFilter(int textureFilter) {
checkTarget();
target.setTextureFilter(textureFilter);
}
}

View File

@@ -0,0 +1,71 @@
package org.newdawn.slick.opengl;
import java.nio.ByteBuffer;
import org.lwjgl.BufferUtils;
/**
* An image data implementation which represents an empty texture
*
* @author kevin
*/
public class EmptyImageData implements ImageData {
/** The width of the data */
private int width;
/** The height of the data */
private int height;
/**
* Create an empty image data source
*
* @param width The width of the source
* @param height The height of the source
*/
public EmptyImageData(int width, int height) {
this.width = width;
this.height = height;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getDepth()
*/
public int getDepth() {
return 32;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getHeight()
*/
public int getHeight() {
return height;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
*/
public ByteBuffer getImageBufferData() {
return BufferUtils.createByteBuffer(getTexWidth() * getTexHeight() * 4);
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexHeight()
*/
public int getTexHeight() {
return InternalTextureLoader.get2Fold(height);
}
/**
* @see org.newdawn.slick.opengl.ImageData#getTexWidth()
*/
public int getTexWidth() {
return InternalTextureLoader.get2Fold(width);
}
/**
* @see org.newdawn.slick.opengl.ImageData#getWidth()
*/
public int getWidth() {
return width;
}
}

View File

@@ -0,0 +1,22 @@
package org.newdawn.slick.opengl;
import org.newdawn.slick.opengl.renderer.Renderer;
/**
* A collection of utilities to allow aid interaction with the GL provider
*
* @author kevin
*/
public final class GLUtils {
/**
* Check that we're in the right place to be doing GL operations
*/
public static void checkGLContext() {
try {
Renderer.get().glGetError();
} catch (NullPointerException e) {
throw new RuntimeException("OpenGL based resources (images, fonts, sprites etc) must be loaded as part of init() or the game loop. They cannot be loaded before initialisation.");
}
}
}

View File

@@ -0,0 +1,55 @@
package org.newdawn.slick.opengl;
import java.nio.ByteBuffer;
/**
* A description of any class providing ImageData in a form suitable for OpenGL texture
* creation.
*
* @author kevin
*/
public interface ImageData {
/**
* Get the last bit depth read from a TGA
*
* @return The last bit depth read
*/
public int getDepth();
/**
* Get the last width read from a TGA
*
* @return Get the last width in pixels fread from a TGA
*/
public int getWidth();
/**
* Get the last height read from a TGA
*
* @return Get the last height in pixels fread from a TGA
*/
public int getHeight();
/**
* Get the last required texture width for a loaded image
*
* @return Get the ast required texture width for a loaded image
*/
public int getTexWidth();
/**
* Get the ast required texture height for a loaded image
*
* @return Get the ast required texture height for a loaded image
*/
public int getTexHeight();
/**
* Get the store image
*
* @return The stored image
*/
public ByteBuffer getImageBufferData();
}

View File

@@ -0,0 +1,77 @@
package org.newdawn.slick.opengl;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.newdawn.slick.util.Log;
/**
* 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(".png")) {
CompositeImageData data = new CompositeImageData();
if (usePngLoader) {
data.add(new PNGImageData());
}
data.add(new ImageIOImageData());
return data;
}
return new ImageIOImageData();
}
}

View File

@@ -0,0 +1,244 @@
package org.newdawn.slick.opengl;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;
import javax.imageio.ImageIO;
/**
* An image data provider that uses ImageIO to retrieve image data in a format
* suitable for creating OpenGL textures. This implementation is used when
* formats not natively supported by the library are required.
*
* @author kevin
*/
public class ImageIOImageData implements LoadableImageData {
/** The colour model including alpha for the GL image */
private static final ColorModel glAlphaColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,8},
true,
false,
ComponentColorModel.TRANSLUCENT,
DataBuffer.TYPE_BYTE);
/** The colour model for the GL image */
private static final ColorModel glColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] {8,8,8,0},
false,
false,
ComponentColorModel.OPAQUE,
DataBuffer.TYPE_BYTE);
/** The bit depth of the image */
private int depth;
/** The height of the image */
private int height;
/** The width of the image */
private int width;
/** The width of the texture that should be created for the image */
private int texWidth;
/** The height of the texture that should be created for the image */
private int texHeight;
/** True if we should edge */
private boolean edging = true;
/**
* @see org.newdawn.slick.opengl.ImageData#getDepth()
*/
public int getDepth() {
return depth;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getHeight()
*/
public int getHeight() {
return height;
}
/**
* @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.ImageData#getWidth()
*/
public int getWidth() {
return width;
}
/**
* @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;
}
BufferedImage bufferedImage = ImageIO.read(fis);
return imageToByteBuffer(bufferedImage, flipped, forceAlpha, transparent);
}
public ByteBuffer imageToByteBuffer(BufferedImage image, boolean flipped, boolean forceAlpha, int[] transparent) {
ByteBuffer imageBuffer = null;
WritableRaster raster;
BufferedImage texImage;
int texWidth = 2;
int texHeight = 2;
// find the closest power of 2 for the width and height
// of the produced texture
while (texWidth < image.getWidth()) {
texWidth *= 2;
}
while (texHeight < image.getHeight()) {
texHeight *= 2;
}
this.width = image.getWidth();
this.height = image.getHeight();
this.texHeight = texHeight;
this.texWidth = texWidth;
// create a raster that can be used by OpenGL as a source
// for a texture
boolean useAlpha = image.getColorModel().hasAlpha() || forceAlpha;
if (useAlpha) {
depth = 32;
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,texWidth,texHeight,4,null);
texImage = new BufferedImage(glAlphaColorModel,raster,false,new Hashtable());
} else {
depth = 24;
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,texWidth,texHeight,3,null);
texImage = new BufferedImage(glColorModel,raster,false,new Hashtable());
}
// copy the source image into the produced image
Graphics2D g = (Graphics2D) texImage.getGraphics();
// only need to blank the image for mac compatibility if we're using alpha
if (useAlpha) {
g.setColor(new Color(0f,0f,0f,0f));
g.fillRect(0,0,texWidth,texHeight);
}
if (flipped) {
g.scale(1,-1);
g.drawImage(image,0,-height,null);
} else {
g.drawImage(image,0,0,null);
}
if (edging) {
if (height < texHeight - 1) {
copyArea(texImage, 0, 0, width, 1, 0, texHeight-1);
copyArea(texImage, 0, height-1, width, 1, 0, 1);
}
if (width < texWidth - 1) {
copyArea(texImage, 0,0,1,height,texWidth-1,0);
copyArea(texImage, width-1,0,1,height,1,0);
}
}
// build a byte buffer from the temporary image
// that be used by OpenGL to produce a texture.
byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData();
if (transparent != null) {
for (int i=0;i<data.length;i+=4) {
boolean match = true;
for (int c=0;c<3;c++) {
int value = data[i+c] < 0 ? 256 + data[i+c] : data[i+c];
if (value != transparent[c]) {
match = false;
}
}
if (match) {
data[i+3] = 0;
}
}
}
imageBuffer = ByteBuffer.allocateDirect(data.length);
imageBuffer.order(ByteOrder.nativeOrder());
imageBuffer.put(data, 0, data.length);
imageBuffer.flip();
g.dispose();
return imageBuffer;
}
/**
* @see org.newdawn.slick.opengl.ImageData#getImageBufferData()
*/
public ByteBuffer getImageBufferData() {
throw new RuntimeException("ImageIOImageData doesn't store it's image.");
}
/**
* Implement of transform copy area for 1.4
*
* @param image The image to copy
* @param x The x position to copy to
* @param y The y position to copy to
* @param width The width of the image
* @param height The height of the image
* @param dx The transform on the x axis
* @param dy The transform on the y axis
*/
private void copyArea(BufferedImage image, int x, int y, int width, int height, int dx, int dy) {
Graphics2D g = (Graphics2D) image.getGraphics();
g.drawImage(image.getSubimage(x, y, width, height),x+dx,y+dy,null);
}
/**
* @see org.newdawn.slick.opengl.LoadableImageData#configureEdging(boolean)
*/
public void configureEdging(boolean edging) {
this.edging = edging;
}
}

View File

@@ -0,0 +1,530 @@
package org.newdawn.slick.opengl;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Iterator;
import org.lwjgl.BufferUtils;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.ResourceLoader;
/**
* A texture loaded based on many old versions that will load image data from a file
* and produce OpenGL textures.
*
* @see ImageData
*
* @author kevin
*/
public class InternalTextureLoader {
/** The renderer to use for all GL operations */
protected static SGL GL = Renderer.get();
/** The standard texture loaded used everywhere */
private static final InternalTextureLoader loader = new InternalTextureLoader();
/**
* Get the single instance of this texture loader
*
* @return The single instance of the texture loader
*/
public static InternalTextureLoader get() {
return loader;
}
/** The table of textures that have been loaded in this loader */
private HashMap texturesLinear = new HashMap();
/** The table of textures that have been loaded in this loader */
private HashMap texturesNearest = new HashMap();
/** The destination pixel format */
private int dstPixelFormat = SGL.GL_RGBA8;
/** True if we're using deferred loading */
private boolean deferred;
/** True if we should hold texture data */
private boolean holdTextureData;
/**
* Create a new texture loader based on the game panel
*/
private InternalTextureLoader() {
}
/**
* Indicate where texture data should be held for reinitialising at a future
* point.
*
* @param holdTextureData True if we should hold texture data
*/
public void setHoldTextureData(boolean holdTextureData) {
this.holdTextureData = holdTextureData;
}
/**
* True if we should only record the request to load in the intention
* of loading the texture later
*
* @param deferred True if the we should load a token
*/
public void setDeferredLoading(boolean deferred) {
this.deferred = deferred;
}
/**
* Check if we're using deferred loading
*
* @return True if we're loading deferred textures
*/
public boolean isDeferredLoading() {
return deferred;
}
/**
* Remove a particular named image from the cache
*
* @param name The name of the image to be cleared
*/
public void clear(String name) {
texturesLinear.remove(name);
texturesNearest.remove(name);
}
/**
* Clear out the cached textures
*/
public void clear() {
texturesLinear.clear();
texturesNearest.clear();
}
/**
* Tell the loader to produce 16 bit textures
*/
public void set16BitMode() {
dstPixelFormat = SGL.GL_RGBA16;
}
/**
* Create a new texture ID
*
* @return A new texture ID
*/
public static int createTextureID()
{
IntBuffer tmp = createIntBuffer(1);
GL.glGenTextures(tmp);
return tmp.get(0);
}
/**
* Get a texture from a specific file
*
* @param source The file to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(File source, boolean flipped,int filter) throws IOException {
String resourceName = source.getAbsolutePath();
InputStream in = new FileInputStream(source);
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a specific file
*
* @param source The file to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(File source, boolean flipped,int filter, int[] transparent) throws IOException {
String resourceName = source.getAbsolutePath();
InputStream in = new FileInputStream(source);
return getTexture(in, resourceName, flipped, filter, transparent);
}
/**
* Get a texture from a resource location
*
* @param resourceName The location to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use when scaling the texture
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(String resourceName, boolean flipped, int filter) throws IOException {
InputStream in = ResourceLoader.getResourceAsStream(resourceName);
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a resource location
*
* @param resourceName The location to load the texture from
* @param flipped True if we should flip the texture on the y axis while loading
* @param filter The filter to use when scaling the texture
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(String resourceName, boolean flipped, int filter, int[] transparent) throws IOException {
InputStream in = ResourceLoader.getResourceAsStream(resourceName);
return getTexture(in, resourceName, flipped, filter, transparent);
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param filter The filter to use when scaling the texture
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public Texture getTexture(InputStream in, String resourceName, boolean flipped, int filter) throws IOException {
return getTexture(in, resourceName, flipped, filter, null);
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param filter The filter to use when scaling the texture
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
public TextureImpl getTexture(InputStream in, String resourceName, boolean flipped, int filter, int[] transparent) throws IOException {
if (deferred) {
return new DeferredTexture(in, resourceName, flipped, filter, transparent);
}
HashMap hash = texturesLinear;
if (filter == SGL.GL_NEAREST) {
hash = texturesNearest;
}
String resName = resourceName;
if (transparent != null) {
resName += ":"+transparent[0]+":"+transparent[1]+":"+transparent[2];
}
resName += ":"+flipped;
if (holdTextureData) {
TextureImpl tex = (TextureImpl) hash.get(resName);
if (tex != null) {
return tex;
}
} else {
SoftReference ref = (SoftReference) hash.get(resName);
if (ref != null) {
TextureImpl tex = (TextureImpl) ref.get();
if (tex != null) {
return tex;
} else {
hash.remove(resName);
}
}
}
// horrible test until I can find something more suitable
try {
GL.glGetError();
} catch (NullPointerException e) {
throw new RuntimeException("Image based resources must be loaded as part of init() or the game loop. They cannot be loaded before initialisation.");
}
TextureImpl tex = getTexture(in, resourceName,
SGL.GL_TEXTURE_2D,
filter,
filter, flipped, transparent);
tex.setCacheName(resName);
if (holdTextureData) {
hash.put(resName, tex);
} else {
hash.put(resName, new SoftReference(tex));
}
return tex;
}
/**
* Get a texture from a image file
*
* @param in The stream from which we can load the image
* @param resourceName The name to give this image in the internal cache
* @param flipped True if we should flip the image on the y-axis while loading
* @param target The texture target we're loading this texture into
* @param minFilter The scaling down filter
* @param magFilter The scaling up filter
* @param transparent The colour to interpret as transparent or null if none
* @return The texture loaded
* @throws IOException Indicates a failure to load the image
*/
private TextureImpl getTexture(InputStream in,
String resourceName,
int target,
int magFilter,
int minFilter, boolean flipped, int[] transparent) throws IOException
{
// create the texture ID for this texture
ByteBuffer textureBuffer;
LoadableImageData imageData = ImageDataFactory.getImageDataFor(resourceName);
textureBuffer = imageData.loadImage(new BufferedInputStream(in), flipped, transparent);
int textureID = createTextureID();
TextureImpl texture = new TextureImpl(resourceName, target, textureID);
// bind this texture
GL.glBindTexture(target, textureID);
int width;
int height;
int texWidth;
int texHeight;
boolean hasAlpha;
width = imageData.getWidth();
height = imageData.getHeight();
hasAlpha = imageData.getDepth() == 32;
texture.setTextureWidth(imageData.getTexWidth());
texture.setTextureHeight(imageData.getTexHeight());
texWidth = texture.getTextureWidth();
texHeight = texture.getTextureHeight();
IntBuffer temp = BufferUtils.createIntBuffer(16);
GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
int max = temp.get(0);
if ((texWidth > max) || (texHeight > max)) {
throw new IOException("Attempt to allocate a texture to big for the current hardware");
}
int srcPixelFormat = hasAlpha ? SGL.GL_RGBA : SGL.GL_RGB;
int componentCount = hasAlpha ? 4 : 3;
texture.setWidth(width);
texture.setHeight(height);
texture.setAlpha(hasAlpha);
if (holdTextureData) {
texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
}
GL.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, GL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
get2Fold(width),
get2Fold(height),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
/**
* Create an empty texture
*
* @param width The width of the new texture
* @param height The height of the new texture
* @return The created empty texture
* @throws IOException Indicates a failure to create the texture on the graphics hardware
*/
public Texture createTexture(final int width, final int height) throws IOException {
return createTexture(width, height, SGL.GL_NEAREST);
}
/**
* Create an empty texture
*
* @param width The width of the new texture
* @param height The height of the new texture
* @return The created empty texture
* @throws IOException Indicates a failure to create the texture on the graphics hardware
*/
public Texture createTexture(final int width, final int height, final int filter) throws IOException {
ImageData ds = new EmptyImageData(width, height);
return getTexture(ds, filter);
}
/**
* Get a texture from a image file
*
* @param dataSource The image data to generate the texture from
* @param filter The filter to use when scaling the texture
* @return The texture created
* @throws IOException Indicates the texture is too big for the hardware
*/
public Texture getTexture(ImageData dataSource, int filter) throws IOException
{
int target = SGL.GL_TEXTURE_2D;
ByteBuffer textureBuffer;
textureBuffer = dataSource.getImageBufferData();
// create the texture ID for this texture
int textureID = createTextureID();
TextureImpl texture = new TextureImpl("generated:"+dataSource, target ,textureID);
int minFilter = filter;
int magFilter = filter;
boolean flipped = false;
// bind this texture
GL.glBindTexture(target, textureID);
int width;
int height;
int texWidth;
int texHeight;
boolean hasAlpha;
width = dataSource.getWidth();
height = dataSource.getHeight();
hasAlpha = dataSource.getDepth() == 32;
texture.setTextureWidth(dataSource.getTexWidth());
texture.setTextureHeight(dataSource.getTexHeight());
texWidth = texture.getTextureWidth();
texHeight = texture.getTextureHeight();
int srcPixelFormat = hasAlpha ? SGL.GL_RGBA : SGL.GL_RGB;
int componentCount = hasAlpha ? 4 : 3;
texture.setWidth(width);
texture.setHeight(height);
texture.setAlpha(hasAlpha);
IntBuffer temp = BufferUtils.createIntBuffer(16);
GL.glGetInteger(SGL.GL_MAX_TEXTURE_SIZE, temp);
int max = temp.get(0);
if ((texWidth > max) || (texHeight > max)) {
throw new IOException("Attempt to allocate a texture to big for the current hardware");
}
if (holdTextureData) {
texture.setTextureData(srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
}
GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
get2Fold(width),
get2Fold(height),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
/**
* Get the closest greater power of 2 to the fold number
*
* @param fold The target number
* @return The power of 2
*/
public static int get2Fold(int fold) {
int ret = 2;
while (ret < fold) {
ret *= 2;
}
return ret;
}
/**
* Creates an integer buffer to hold specified ints
* - strictly a utility method
*
* @param size how many int to contain
* @return created IntBuffer
*/
public static IntBuffer createIntBuffer(int size) {
ByteBuffer temp = ByteBuffer.allocateDirect(4 * size);
temp.order(ByteOrder.nativeOrder());
return temp.asIntBuffer();
}
/**
* Reload all the textures loaded in this loader
*/
public void reload() {
Iterator texs = texturesLinear.values().iterator();
while (texs.hasNext()) {
((TextureImpl) texs.next()).reload();
}
texs = texturesNearest.values().iterator();
while (texs.hasNext()) {
((TextureImpl) texs.next()).reload();
}
}
/**
* Reload a given texture blob
*
* @param texture The texture being reloaded
* @param srcPixelFormat The source pixel format
* @param componentCount The component count
* @param minFilter The minification filter
* @param magFilter The magnification filter
* @param textureBuffer The pixel data
* @return The ID of the newly created texture
*/
public int reload(TextureImpl texture, int srcPixelFormat, int componentCount,
int minFilter, int magFilter, ByteBuffer textureBuffer) {
int target = SGL.GL_TEXTURE_2D;
int textureID = createTextureID();
GL.glBindTexture(target, textureID);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, minFilter);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, magFilter);
// produce a texture from the byte buffer
GL.glTexImage2D(target,
0,
dstPixelFormat,
texture.getTextureWidth(),
texture.getTextureHeight(),
0,
srcPixelFormat,
SGL.GL_UNSIGNED_BYTE,
textureBuffer);
return textureID;
}
}

View File

@@ -0,0 +1,54 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
* An image data source that can load images from a stream
*
* @author kevin
*/
public interface LoadableImageData extends ImageData {
/**
* Configure the edging that can be used to make texture edges
* loop more cleanly
*
* @param edging True if we should edge
*/
public void configureEdging(boolean edging);
/**
* Load a image from the specified stream
*
* @param fis The stream from which we'll load the TGA
* @throws IOException Indicates a failure to read the TGA
* @return The byte buffer containing texture data
*/
public ByteBuffer loadImage(InputStream fis) throws IOException;
/**
* Load a image from the specified stream
*
* @param fis The stream from which we'll load the TGA
* @param flipped True if we loading in flipped mode (used for cursors)
* @param transparent The colour to interpret as transparent or null if none
* @return The byte buffer containing texture data
* @throws IOException Indicates a failure to read the TGA
*/
public ByteBuffer loadImage(InputStream fis, boolean flipped, int[] transparent)
throws IOException;
/**
* Load a image from the specified stream
*
* @param fis The stream from which we'll load the TGA
* @param flipped True if we loading in flipped mode (used for cursors)
* @param forceAlpha Force the output to have an alpha channel
* @param transparent The colour to interpret as transparent or null if none
* @return The byte buffer containing texture data
* @throws IOException Indicates a failure to read the TGA
*/
public ByteBuffer loadImage(InputStream fis, boolean flipped, boolean forceAlpha, int[] transparent)
throws IOException;
}

View File

@@ -0,0 +1,762 @@
/*
* Copyright (c) 2008-2010, Matthias Mann
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Matthias Mann nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.newdawn.slick.opengl;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
/**
* A PNGDecoder. The slick PNG decoder is based on this class :)
*
* @author Matthias Mann
*/
public class PNGDecoder {
public static Format ALPHA = new Format(1, true);
public static Format LUMINANCE = new Format(1, false);
public static Format LUMINANCE_ALPHA = new Format(2, true);
public static Format RGB = new Format(3, false);
public static Format RGBA = new Format(4, true);
public static Format BGRA = new Format(4, true);
public static Format ABGR = new Format(4, true);
public static class Format {
final int numComponents;
final boolean hasAlpha;
private Format(int numComponents, boolean hasAlpha) {
this.numComponents = numComponents;
this.hasAlpha = hasAlpha;
}
public int getNumComponents() {
return numComponents;
}
public boolean isHasAlpha() {
return hasAlpha;
}
}
private static final byte[] SIGNATURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10};
private static final int IHDR = 0x49484452;
private static final int PLTE = 0x504C5445;
private static final int tRNS = 0x74524E53;
private static final int IDAT = 0x49444154;
private static final int IEND = 0x49454E44;
private static final byte COLOR_GREYSCALE = 0;
private static final byte COLOR_TRUECOLOR = 2;
private static final byte COLOR_INDEXED = 3;
private static final byte COLOR_GREYALPHA = 4;
private static final byte COLOR_TRUEALPHA = 6;
private final InputStream input;
private final CRC32 crc;
private final byte[] buffer;
private int chunkLength;
private int chunkType;
private int chunkRemaining;
private int width;
private int height;
private int bitdepth;
private int colorType;
private int bytesPerPixel;
private byte[] palette;
private byte[] paletteA;
private byte[] transPixel;
public PNGDecoder(InputStream input) throws IOException {
this.input = input;
this.crc = new CRC32();
this.buffer = new byte[4096];
readFully(buffer, 0, SIGNATURE.length);
if(!checkSignature(buffer)) {
throw new IOException("Not a valid PNG file");
}
openChunk(IHDR);
readIHDR();
closeChunk();
searchIDAT: for(;;) {
openChunk();
switch (chunkType) {
case IDAT:
break searchIDAT;
case PLTE:
readPLTE();
break;
case tRNS:
readtRNS();
break;
}
closeChunk();
}
if(colorType == COLOR_INDEXED && palette == null) {
throw new IOException("Missing PLTE chunk");
}
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public boolean hasAlpha() {
return colorType == COLOR_TRUEALPHA ||
paletteA != null || transPixel != null;
}
public boolean isRGB() {
return colorType == COLOR_TRUEALPHA ||
colorType == COLOR_TRUECOLOR ||
colorType == COLOR_INDEXED;
}
/**
* Computes the implemented format conversion for the desired format.
*
* @param fmt the desired format
* @return format which best matches the desired format
* @throws UnsupportedOperationException if this PNG file can't be decoded
*/
public Format decideTextureFormat(Format fmt) {
switch (colorType) {
case COLOR_TRUECOLOR:
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA) || (fmt == RGB)) {
return fmt;
}
return RGB;
case COLOR_TRUEALPHA:
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA) || (fmt == RGB)) {
return fmt;
}
return RGBA;
case COLOR_GREYSCALE:
if ((fmt == LUMINANCE) || (fmt == ALPHA)) {
return fmt;
}
return LUMINANCE;
case COLOR_GREYALPHA:
return LUMINANCE_ALPHA;
case COLOR_INDEXED:
if ((fmt == ABGR) || (fmt == RGBA) || (fmt == BGRA)) {
return fmt;
}
return RGBA;
default:
throw new UnsupportedOperationException("Not yet implemented");
}
}
public void decode(ByteBuffer buffer, int stride, Format fmt) throws IOException {
final int offset = buffer.position();
final int lineSize = ((width * bitdepth + 7) / 8) * bytesPerPixel;
byte[] curLine = new byte[lineSize+1];
byte[] prevLine = new byte[lineSize+1];
byte[] palLine = (bitdepth < 8) ? new byte[width+1] : null;
final Inflater inflater = new Inflater();
try {
for(int y=0 ; y<height ; y++) {
readChunkUnzip(inflater, curLine, 0, curLine.length);
unfilter(curLine, prevLine);
buffer.position(offset + y*stride);
switch (colorType) {
case COLOR_TRUECOLOR:
if (fmt == ABGR) {
copyRGBtoABGR(buffer, curLine);
}
else if (fmt == RGBA) {
copyRGBtoRGBA(buffer, curLine);
}
else if (fmt == BGRA) {
copyRGBtoBGRA(buffer, curLine);
}
else if (fmt == RGB) {
copy(buffer, curLine);
} else {
throw new UnsupportedOperationException("Unsupported format for this image");
}
break;
case COLOR_TRUEALPHA:
if (fmt == ABGR) {
copyRGBAtoABGR(buffer, curLine);
} else if (fmt == RGBA) {
copy(buffer, curLine);
} else if (fmt == BGRA) {
copyRGBAtoBGRA(buffer, curLine); break;
} else if (fmt == RGB) {
copyRGBAtoRGB(buffer, curLine); break;
} else {
throw new UnsupportedOperationException("Unsupported format for this image");
}
break;
case COLOR_GREYSCALE:
if ((fmt == LUMINANCE) || (fmt == ALPHA)) {
copy(buffer, curLine);
} else {
throw new UnsupportedOperationException("Unsupported format for this image");
}
break;
case COLOR_GREYALPHA:
if (fmt == LUMINANCE_ALPHA) {
copy(buffer, curLine);
} else {
throw new UnsupportedOperationException("Unsupported format for this image");
}
break;
case COLOR_INDEXED:
switch(bitdepth) {
case 8: palLine = curLine; break;
case 4: expand4(curLine, palLine); break;
case 2: expand2(curLine, palLine); break;
case 1: expand1(curLine, palLine); break;
default: throw new UnsupportedOperationException("Unsupported bitdepth for this image");
}
if (fmt == ABGR) {
copyPALtoABGR(buffer, palLine);
} else if (fmt == RGBA) {
copyPALtoRGBA(buffer, palLine);
} else if (fmt == BGRA) {
copyPALtoBGRA(buffer, palLine);
} else {
throw new UnsupportedOperationException("Unsupported format for this image");
}
break;
default:
throw new UnsupportedOperationException("Not yet implemented");
}
byte[] tmp = curLine;
curLine = prevLine;
prevLine = tmp;
}
} finally {
inflater.end();
}
}
private void copy(ByteBuffer buffer, byte[] curLine) {
buffer.put(curLine, 1, curLine.length-1);
}
private void copyRGBtoABGR(ByteBuffer buffer, byte[] curLine) {
if(transPixel != null) {
byte tr = transPixel[1];
byte tg = transPixel[3];
byte tb = transPixel[5];
for(int i=1,n=curLine.length ; i<n ; i+=3) {
byte r = curLine[i];
byte g = curLine[i+1];
byte b = curLine[i+2];
byte a = (byte)0xFF;
if(r==tr && g==tg && b==tb) {
a = 0;
}
buffer.put(a).put(b).put(g).put(r);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=3) {
buffer.put((byte)0xFF).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
}
}
}
private void copyRGBtoRGBA(ByteBuffer buffer, byte[] curLine) {
if(transPixel != null) {
byte tr = transPixel[1];
byte tg = transPixel[3];
byte tb = transPixel[5];
for(int i=1,n=curLine.length ; i<n ; i+=3) {
byte r = curLine[i];
byte g = curLine[i+1];
byte b = curLine[i+2];
byte a = (byte)0xFF;
if(r==tr && g==tg && b==tb) {
a = 0;
}
buffer.put(r).put(g).put(b).put(a);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=3) {
buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]).put((byte)0xFF);
}
}
}
private void copyRGBtoBGRA(ByteBuffer buffer, byte[] curLine) {
if(transPixel != null) {
byte tr = transPixel[1];
byte tg = transPixel[3];
byte tb = transPixel[5];
for(int i=1,n=curLine.length ; i<n ; i+=3) {
byte r = curLine[i];
byte g = curLine[i+1];
byte b = curLine[i+2];
byte a = (byte)0xFF;
if(r==tr && g==tg && b==tb) {
a = 0;
}
buffer.put(b).put(g).put(r).put(a);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=3) {
buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put((byte)0xFF);
}
}
}
private void copyRGBAtoABGR(ByteBuffer buffer, byte[] curLine) {
for(int i=1,n=curLine.length ; i<n ; i+=4) {
buffer.put(curLine[i+3]).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
}
}
private void copyRGBAtoBGRA(ByteBuffer buffer, byte[] curLine) {
for(int i=1,n=curLine.length ; i<n ; i+=4) {
buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i+0]).put(curLine[i+3]);
}
}
private void copyRGBAtoRGB(ByteBuffer buffer, byte[] curLine) {
for(int i=1,n=curLine.length ; i<n ; i+=4) {
buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]);
}
}
private void copyPALtoABGR(ByteBuffer buffer, byte[] curLine) {
if(paletteA != null) {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = paletteA[idx];
buffer.put(a).put(b).put(g).put(r);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = (byte)0xFF;
buffer.put(a).put(b).put(g).put(r);
}
}
}
private void copyPALtoRGBA(ByteBuffer buffer, byte[] curLine) {
if(paletteA != null) {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = paletteA[idx];
buffer.put(r).put(g).put(b).put(a);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = (byte)0xFF;
buffer.put(r).put(g).put(b).put(a);
}
}
}
private void copyPALtoBGRA(ByteBuffer buffer, byte[] curLine) {
if(paletteA != null) {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = paletteA[idx];
buffer.put(b).put(g).put(r).put(a);
}
} else {
for(int i=1,n=curLine.length ; i<n ; i+=1) {
int idx = curLine[i] & 255;
byte r = palette[idx*3 + 0];
byte g = palette[idx*3 + 1];
byte b = palette[idx*3 + 2];
byte a = (byte)0xFF;
buffer.put(b).put(g).put(r).put(a);
}
}
}
private void expand4(byte[] src, byte[] dst) {
for(int i=1,n=dst.length ; i<n ; i+=2) {
int val = src[1 + (i >> 1)] & 255;
switch(n-i) {
default: dst[i+1] = (byte)(val & 15);
case 1: dst[i ] = (byte)(val >> 4);
}
}
}
private void expand2(byte[] src, byte[] dst) {
for(int i=1,n=dst.length ; i<n ; i+=4) {
int val = src[1 + (i >> 2)] & 255;
switch(n-i) {
default: dst[i+3] = (byte)((val ) & 3);
case 3: dst[i+2] = (byte)((val >> 2) & 3);
case 2: dst[i+1] = (byte)((val >> 4) & 3);
case 1: dst[i ] = (byte)((val >> 6) );
}
}
}
private void expand1(byte[] src, byte[] dst) {
for(int i=1,n=dst.length ; i<n ; i+=8) {
int val = src[1 + (i >> 3)] & 255;
switch(n-i) {
default: dst[i+7] = (byte)((val ) & 1);
case 7: dst[i+6] = (byte)((val >> 1) & 1);
case 6: dst[i+5] = (byte)((val >> 2) & 1);
case 5: dst[i+4] = (byte)((val >> 3) & 1);
case 4: dst[i+3] = (byte)((val >> 4) & 1);
case 3: dst[i+2] = (byte)((val >> 5) & 1);
case 2: dst[i+1] = (byte)((val >> 6) & 1);
case 1: dst[i ] = (byte)((val >> 7) );
}
}
}
private void unfilter(byte[] curLine, byte[] prevLine) throws IOException {
switch (curLine[0]) {
case 0: // none
break;
case 1:
unfilterSub(curLine);
break;
case 2:
unfilterUp(curLine, prevLine);
break;
case 3:
unfilterAverage(curLine, prevLine);
break;
case 4:
unfilterPaeth(curLine, prevLine);
break;
default:
throw new IOException("invalide filter type in scanline: " + curLine[0]);
}
}
private void unfilterSub(byte[] curLine) {
final int bpp = this.bytesPerPixel;
for(int i=bpp+1,n=curLine.length ; i<n ; ++i) {
curLine[i] += curLine[i-bpp];
}
}
private void unfilterUp(byte[] curLine, byte[] prevLine) {
final int bpp = this.bytesPerPixel;
for(int i=1,n=curLine.length ; i<n ; ++i) {
curLine[i] += prevLine[i];
}
}
private void unfilterAverage(byte[] curLine, byte[] prevLine) {
final int bpp = this.bytesPerPixel;
int i;
for(i=1 ; i<=bpp ; ++i) {
curLine[i] += (byte)((prevLine[i] & 0xFF) >>> 1);
}
for(int n=curLine.length ; i<n ; ++i) {
curLine[i] += (byte)(((prevLine[i] & 0xFF) + (curLine[i - bpp] & 0xFF)) >>> 1);
}
}
private void unfilterPaeth(byte[] curLine, byte[] prevLine) {
final int bpp = this.bytesPerPixel;
int i;
for(i=1 ; i<=bpp ; ++i) {
curLine[i] += prevLine[i];
}
for(int n=curLine.length ; i<n ; ++i) {
int a = curLine[i - bpp] & 255;
int b = prevLine[i] & 255;
int c = prevLine[i - bpp] & 255;
int p = a + b - c;
int pa = p - a; if(pa < 0) pa = -pa;
int pb = p - b; if(pb < 0) pb = -pb;
int pc = p - c; if(pc < 0) pc = -pc;
if(pa<=pb && pa<=pc)
c = a;
else if(pb<=pc)
c = b;
curLine[i] += (byte)c;
}
}
private void readIHDR() throws IOException {
checkChunkLength(13);
readChunk(buffer, 0, 13);
width = readInt(buffer, 0);
height = readInt(buffer, 4);
bitdepth = buffer[8] & 255;
colorType = buffer[9] & 255;
switch (colorType) {
case COLOR_GREYSCALE:
if(bitdepth != 8) {
throw new IOException("Unsupported bit depth: " + bitdepth);
}
bytesPerPixel = 1;
break;
case COLOR_GREYALPHA:
if(bitdepth != 8) {
throw new IOException("Unsupported bit depth: " + bitdepth);
}
bytesPerPixel = 2;
break;
case COLOR_TRUECOLOR:
if(bitdepth != 8) {
throw new IOException("Unsupported bit depth: " + bitdepth);
}
bytesPerPixel = 3;
break;
case COLOR_TRUEALPHA:
if(bitdepth != 8) {
throw new IOException("Unsupported bit depth: " + bitdepth);
}
bytesPerPixel = 4;
break;
case COLOR_INDEXED:
switch(bitdepth) {
case 8:
case 4:
case 2:
case 1:
bytesPerPixel = 1;
break;
default:
throw new IOException("Unsupported bit depth: " + bitdepth);
}
break;
default:
throw new IOException("unsupported color format: " + colorType);
}
if(buffer[10] != 0) {
throw new IOException("unsupported compression method");
}
if(buffer[11] != 0) {
throw new IOException("unsupported filtering method");
}
if(buffer[12] != 0) {
throw new IOException("unsupported interlace method");
}
}
private void readPLTE() throws IOException {
int paletteEntries = chunkLength / 3;
if(paletteEntries < 1 || paletteEntries > 256 || (chunkLength % 3) != 0) {
throw new IOException("PLTE chunk has wrong length");
}
palette = new byte[paletteEntries*3];
readChunk(palette, 0, palette.length);
}
private void readtRNS() throws IOException {
switch (colorType) {
case COLOR_GREYSCALE:
checkChunkLength(2);
transPixel = new byte[2];
readChunk(transPixel, 0, 2);
break;
case COLOR_TRUECOLOR:
checkChunkLength(6);
transPixel = new byte[6];
readChunk(transPixel, 0, 6);
break;
case COLOR_INDEXED:
if(palette == null) {
throw new IOException("tRNS chunk without PLTE chunk");
}
paletteA = new byte[palette.length/3];
Arrays.fill(paletteA, (byte)0xFF);
readChunk(paletteA, 0, paletteA.length);
break;
default:
// just ignore it
}
}
private void closeChunk() throws IOException {
if(chunkRemaining > 0) {
// just skip the rest and the CRC
skip(chunkRemaining + 4);
} else {
readFully(buffer, 0, 4);
int expectedCrc = readInt(buffer, 0);
int computedCrc = (int)crc.getValue();
if(computedCrc != expectedCrc) {
throw new IOException("Invalid CRC");
}
}
chunkRemaining = 0;
chunkLength = 0;
chunkType = 0;
}
private void openChunk() throws IOException {
readFully(buffer, 0, 8);
chunkLength = readInt(buffer, 0);
chunkType = readInt(buffer, 4);
chunkRemaining = chunkLength;
crc.reset();
crc.update(buffer, 4, 4); // only chunkType
}
private void openChunk(int expected) throws IOException {
openChunk();
if(chunkType != expected) {
throw new IOException("Expected chunk: " + Integer.toHexString(expected));
}
}
private void checkChunkLength(int expected) throws IOException {
if(chunkLength != expected) {
throw new IOException("Chunk has wrong size");
}
}
private int readChunk(byte[] buffer, int offset, int length) throws IOException {
if(length > chunkRemaining) {
length = chunkRemaining;
}
readFully(buffer, offset, length);
crc.update(buffer, offset, length);
chunkRemaining -= length;
return length;
}
private void refillInflater(Inflater inflater) throws IOException {
while(chunkRemaining == 0) {
closeChunk();
openChunk(IDAT);
}
int read = readChunk(buffer, 0, buffer.length);
inflater.setInput(buffer, 0, read);
}
private void readChunkUnzip(Inflater inflater, byte[] buffer, int offset, int length) throws IOException {
try {
do {
int read = inflater.inflate(buffer, offset, length);
if(read <= 0) {
if(inflater.finished()) {
throw new EOFException();
}
if(inflater.needsInput()) {
refillInflater(inflater);
} else {
throw new IOException("Can't inflate " + length + " bytes");
}
} else {
offset += read;
length -= read;
}
} while(length > 0);
} catch (DataFormatException ex) {
throw (IOException)(new IOException("inflate error").initCause(ex));
}
}
private void readFully(byte[] buffer, int offset, int length) throws IOException {
do {
int read = input.read(buffer, offset, length);
if(read < 0) {
throw new EOFException();
}
offset += read;
length -= read;
} while(length > 0);
}
private int readInt(byte[] buffer, int offset) {
return
((buffer[offset ] ) << 24) |
((buffer[offset+1] & 255) << 16) |
((buffer[offset+2] & 255) << 8) |
((buffer[offset+3] & 255) );
}
private void skip(long amount) throws IOException {
while(amount > 0) {
long skipped = input.skip(amount);
if(skipped < 0) {
throw new EOFException();
}
amount -= skipped;
}
}
private static boolean checkSignature(byte[] buffer) {
for(int i=0 ; i<SIGNATURE.length ; i++) {
if(buffer[i] != SIGNATURE[i]) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,202 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.lwjgl.BufferUtils;
/**
* 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 bit depth of the image */
private int bitDepth;
/** The scratch buffer storing the image data */
private ByteBuffer scratch;
/**
* @see org.newdawn.slick.opengl.ImageData#getDepth()
*/
public int getDepth() {
return bitDepth;
}
/**
* @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;
throw new IOException("Transparent color not support in custom PNG Decoder");
}
PNGDecoder decoder = new PNGDecoder(fis);
if (!decoder.isRGB()) {
throw new IOException("Only RGB formatted images are supported by the PNGLoader");
}
width = decoder.getWidth();
height = decoder.getHeight();
texWidth = get2Fold(width);
texHeight = get2Fold(height);
int perPixel = decoder.hasAlpha() ? 4 : 3;
bitDepth = decoder.hasAlpha() ? 32 : 24;
// Get a pointer to the image memory
scratch = BufferUtils.createByteBuffer(texWidth * texHeight * perPixel);
decoder.decode(scratch, texWidth * perPixel, perPixel == 4 ? PNGDecoder.RGBA : PNGDecoder.RGB);
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));
}
}
}
if (!decoder.hasAlpha() && forceAlpha) {
ByteBuffer temp = BufferUtils.createByteBuffer(texWidth * texHeight * 4);
for (int x=0;x<texWidth;x++) {
for (int y=0;y<texHeight;y++) {
int srcOffset = (y*3)+(x*texHeight*3);
int dstOffset = (y*4)+(x*texHeight*4);
temp.put(dstOffset, scratch.get(srcOffset));
temp.put(dstOffset+1, scratch.get(srcOffset+1));
temp.put(dstOffset+2, scratch.get(srcOffset+2));
if ((x < getHeight()) && (y < getWidth())) {
temp.put(dstOffset+3, (byte) 255);
} else {
temp.put(dstOffset+3, (byte) 0);
}
}
}
bitDepth = 32;
scratch = temp;
}
if (transparent != null) {
for (int i=0;i<texWidth*texHeight*4;i+=4) {
boolean match = true;
for (int c=0;c<3;c++) {
if (toInt(scratch.get(i+c)) != transparent[c]) {
match = false;
}
}
if (match) {
scratch.put(i+3, (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;
}
}

View File

@@ -0,0 +1,122 @@
package org.newdawn.slick.opengl;
import org.lwjgl.opengl.GL11;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.renderer.Renderer;
/**
* A utility to allow performing GL operations without contaminating the
* Slick OpenGL state. Note this will not protect you from OpenGL programming errors
* like a glBegin() without a glEnd(), or glPush() without glPop() etc.
*
* Expected usage:
*
* <code>
* SlickCallable callable = new SlickCallable() {
* public performGLOperations() throws SlickException {
* GL.glTranslate(0,0,1);
* glBegin(GL.GL_POLYGONS);
* glVertex(..);
* ...
* glEnd();
* }
* }
* callable.call();
* </code>
*
* Alternatively you can use the static methods directly
*
* <code>
* SlickCallable.enterSafeBlock();
*
* GL.glTranslate(0,0,1);
* glBegin(GL.GL_POLYGONS);
* glVertex(..);
* ...
* glEnd();
*
* SlickCallable.leaveSafeBlock();
* </code>
*
* @author kevin
*/
public abstract class SlickCallable {
/** The last texture used */
private static Texture lastUsed;
/** True if we're in a safe block */
private static boolean inSafe = false;
/**
* Enter a safe block ensuring that all the OpenGL state that slick
* uses is safe before touching the GL state directly.
*/
public static void enterSafeBlock()
{
if (inSafe) {
return;
}
Renderer.get().flush();
lastUsed = TextureImpl.getLastBind();
TextureImpl.bindNone();
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPushMatrix();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
inSafe = true;
}
/**
* Leave a safe block ensuring that all of Slick's OpenGL state is
* restored since the last enter.
*/
public static void leaveSafeBlock()
{
if (!inSafe) {
return;
}
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPopMatrix();
GL11.glPopClientAttrib();
GL11.glPopAttrib();
if (lastUsed != null) {
lastUsed.bind();
} else {
TextureImpl.bindNone();
}
inSafe = false;
}
/**
* Cause this callable to perform it's GL operations (@see performGLOperations()). This
* method will block until the GL operations have been performed.
*
* @throws SlickException Indicates a failure while performing the GL operations or
* maintaing SlickState
*/
public final void call() throws SlickException {
enterSafeBlock();
performGLOperations();
leaveSafeBlock();
}
/**
* Perform the GL operations that this callable is intended to. This operations should
* not effect the slick OpenGL state.
*
* @throws SlickException Indicates a failure of some sort. This is user exception
*/
protected abstract void performGLOperations() throws SlickException;
}

View File

@@ -0,0 +1,319 @@
package org.newdawn.slick.opengl;
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 org.lwjgl.BufferUtils;
/**
* 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 TGAImageData 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 TGAImageData() {
}
/**
* 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(fis, 100000);
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

@@ -0,0 +1,97 @@
package org.newdawn.slick.opengl;
/**
* The description of a texture loaded by the TextureLoader utility
*
* @author kevin
*/
public interface Texture {
/**
* Check if the texture has alpha
*
* @return True if the texture has alpha
*/
public boolean hasAlpha();
/**
* Get the reference from which this texture was loaded
*
* @return The reference from which this texture was loaded
*/
public String getTextureRef();
/**
* Bind the GL context to a texture
*/
public void bind();
/**
* Get the height of the original image
*
* @return The height of the original image
*/
public int getImageHeight();
/**
* Get the width of the original image
*
* @return The width of the original image
*/
public int getImageWidth();
/**
* Get the height of the physical texture
*
* @return The height of physical texture
*/
public float getHeight();
/**
* Get the width of the physical texture
*
* @return The width of physical texture
*/
public float getWidth();
/**
* Get the height of the actual texture
*
* @return The height of the actual texture
*/
public int getTextureHeight();
/**
* Get the width of the actual texture
*
* @return The width of the actual texture
*/
public int getTextureWidth();
/**
* Destroy the texture reference
*/
public void release();
/**
* Get the OpenGL texture ID for this texture
*
* @return The OpenGL texture ID
*/
public int getTextureID();
/**
* Get the pixel data from the card for this texture
*
* @return The texture data from the card for this texture
*/
public byte[] getTextureData();
/**
* Apply a given texture filter to the texture
*
* @param textureFilter The texture filter to apply (GL_LINEAR, GL_NEAREST, etc..)
*/
public void setTextureFilter(int textureFilter);
}

View File

@@ -0,0 +1,377 @@
package org.newdawn.slick.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.newdawn.slick.opengl.renderer.Renderer;
import org.newdawn.slick.opengl.renderer.SGL;
import org.newdawn.slick.util.Log;
/**
* A texture to be bound within JOGL. This object is responsible for
* keeping track of a given OpenGL texture and for calculating the
* texturing mapping coordinates of the full image.
*
* Since textures need to be powers of 2 the actual texture may be
* considerably bigged that the source image and hence the texture
* mapping coordinates need to be adjusted to matchup drawing the
* sprite against the texture.
*
* @author Kevin Glass
* @author Brian Matzon
*/
public class TextureImpl implements Texture {
/** The renderer to use for all GL operations */
protected static SGL GL = Renderer.get();
/** The last texture that was bound to */
static Texture lastBind;
/**
* Retrieve the last texture bound through the texture interface
*
* @return The last texture bound
*/
public static Texture getLastBind() {
return lastBind;
}
/** The GL target type */
private int target;
/** The GL texture ID */
private int textureID;
/** The height of the image */
private int height;
/** The width of the image */
private int width;
/** The width of the texture */
private int texWidth;
/** The height of the texture */
private int texHeight;
/** The ratio of the width of the image to the texture */
private float widthRatio;
/** The ratio of the height of the image to the texture */
private float heightRatio;
/** If this texture has alpha */
private boolean alpha;
/** The reference this texture was loaded from */
private String ref;
/** The name the texture has in the cache */
private String cacheName;
/** Data used to reload this texture */
private ReloadData reloadData;
/**
* For subclasses to utilise
*/
protected TextureImpl() {
}
/**
* Create a new texture
*
* @param ref The reference this texture was loaded from
* @param target The GL target
* @param textureID The GL texture ID
*/
public TextureImpl(String ref, int target,int textureID) {
this.target = target;
this.ref = ref;
this.textureID = textureID;
lastBind = this;
}
/**
* Set the name this texture is stored against in the cache
*
* @param cacheName The name the texture is stored against in the cache
*/
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
/**
* @see org.newdawn.slick.opengl.Texture#hasAlpha()
*/
public boolean hasAlpha() {
return alpha;
}
/**
* @see org.newdawn.slick.opengl.Texture#getTextureRef()
*/
public String getTextureRef() {
return ref;
}
/**
* If this texture has alpha
*
* @param alpha True, If this texture has alpha
*/
public void setAlpha(boolean alpha) {
this.alpha = alpha;
}
/**
* Clear the binding of the texture
*/
public static void bindNone() {
lastBind = null;
GL.glDisable(SGL.GL_TEXTURE_2D);
}
/**
* Clear slick caching of the last bound texture so that an
* external texture binder can play with the context before returning
* control to slick.
*/
public static void unbind() {
lastBind = null;
}
/**
* @see org.newdawn.slick.opengl.Texture#bind()
*/
public void bind() {
if (lastBind != this) {
lastBind = this;
GL.glEnable(SGL.GL_TEXTURE_2D);
GL.glBindTexture(target, textureID);
}
}
/**
* Set the height of the image
*
* @param height The height of the image
*/
public void setHeight(int height) {
this.height = height;
setHeight();
}
/**
* Set the width of the image
*
* @param width The width of the image
*/
public void setWidth(int width) {
this.width = width;
setWidth();
}
/**
* @see org.newdawn.slick.opengl.Texture#getImageHeight()
*/
public int getImageHeight() {
return height;
}
/**
* @see org.newdawn.slick.opengl.Texture#getImageWidth()
*/
public int getImageWidth() {
return width;
}
/**
* @see org.newdawn.slick.opengl.Texture#getHeight()
*/
public float getHeight() {
return heightRatio;
}
/**
* @see org.newdawn.slick.opengl.Texture#getWidth()
*/
public float getWidth() {
return widthRatio;
}
/**
* @see org.newdawn.slick.opengl.Texture#getTextureHeight()
*/
public int getTextureHeight() {
return texHeight;
}
/**
* @see org.newdawn.slick.opengl.Texture#getTextureWidth()
*/
public int getTextureWidth() {
return texWidth;
}
/**
* Set the height of this texture
*
* @param texHeight The height of the texture
*/
public void setTextureHeight(int texHeight) {
this.texHeight = texHeight;
setHeight();
}
/**
* Set the width of this texture
*
* @param texWidth The width of the texture
*/
public void setTextureWidth(int texWidth) {
this.texWidth = texWidth;
setWidth();
}
/**
* Set the height of the texture. This will update the
* ratio also.
*/
private void setHeight() {
if (texHeight != 0) {
heightRatio = ((float) height)/texHeight;
}
}
/**
* Set the width of the texture. This will update the
* ratio also.
*/
private void setWidth() {
if (texWidth != 0) {
widthRatio = ((float) width)/texWidth;
}
}
/**
* @see org.newdawn.slick.opengl.Texture#release()
*/
public void release() {
IntBuffer texBuf = createIntBuffer(1);
texBuf.put(textureID);
texBuf.flip();
GL.glDeleteTextures(texBuf);
if (lastBind == this) {
bindNone();
}
if (cacheName != null) {
InternalTextureLoader.get().clear(cacheName);
} else {
InternalTextureLoader.get().clear(ref);
}
}
/**
* @see org.newdawn.slick.opengl.Texture#getTextureID()
*/
public int getTextureID() {
return textureID;
}
/**
* Set the OpenGL texture ID for this texture
*
* @param textureID The OpenGL texture ID
*/
public void setTextureID(int textureID) {
this.textureID = textureID;
}
/**
* Creates an integer buffer to hold specified ints
* - strictly a utility method
*
* @param size how many int to contain
* @return created IntBuffer
*/
protected IntBuffer createIntBuffer(int size) {
ByteBuffer temp = ByteBuffer.allocateDirect(4 * size);
temp.order(ByteOrder.nativeOrder());
return temp.asIntBuffer();
}
/**
* @see org.newdawn.slick.opengl.Texture#getTextureData()
*/
public byte[] getTextureData() {
ByteBuffer buffer = BufferUtils.createByteBuffer((hasAlpha() ? 4 : 3) * texWidth * texHeight);
bind();
GL.glGetTexImage(SGL.GL_TEXTURE_2D, 0, hasAlpha() ? SGL.GL_RGBA : SGL.GL_RGB, SGL.GL_UNSIGNED_BYTE,
buffer);
byte[] data = new byte[buffer.limit()];
buffer.get(data);
buffer.clear();
return data;
}
/**
* @see org.newdawn.slick.opengl.Texture#setTextureFilter(int)
*/
public void setTextureFilter(int textureFilter) {
bind();
GL.glTexParameteri(target, SGL.GL_TEXTURE_MIN_FILTER, textureFilter);
GL.glTexParameteri(target, SGL.GL_TEXTURE_MAG_FILTER, textureFilter);
}
/**
* Set the texture data that this texture can be reloaded from
*
* @param srcPixelFormat The pixel format
* @param componentCount The component count
* @param minFilter The OpenGL minification filter
* @param magFilter The OpenGL magnification filter
* @param textureBuffer The texture buffer containing the data for the texture
*/
public void setTextureData(int srcPixelFormat, int componentCount,
int minFilter, int magFilter, ByteBuffer textureBuffer) {
reloadData = new ReloadData();
reloadData.srcPixelFormat = srcPixelFormat;
reloadData.componentCount = componentCount;
reloadData.minFilter = minFilter;
reloadData.magFilter = magFilter;
reloadData.textureBuffer = textureBuffer;
}
/**
* Reload this texture
*/
public void reload() {
if (reloadData != null) {
textureID = reloadData.reload();
}
}
/**
* Reload this texture from it's original source data
*/
private class ReloadData {
/** The src pixel format */
private int srcPixelFormat;
/** The component count */
private int componentCount;
/** The OpenGL minification filter */
private int minFilter;
/** The OpenGL magnification filter */
private int magFilter;
/** The texture buffer of pixel data */
private ByteBuffer textureBuffer;
/**
* Reload this texture
*
* @return The new texture ID assigned to this texture
*/
public int reload() {
Log.error("Reloading texture: "+ref);
return InternalTextureLoader.get().reload(TextureImpl.this, srcPixelFormat, componentCount, minFilter, magFilter, textureBuffer);
}
}
}

View File

@@ -0,0 +1,66 @@
package org.newdawn.slick.opengl;
import java.io.IOException;
import java.io.InputStream;
import org.lwjgl.opengl.GL11;
/**
* A utility class to wrap the Slick internal texture loader and present a
* rational interface.
*
* @author kevin
*/
public class TextureLoader {
/**
* Load a texture with a given format from the supplied input stream
*
* @param format The format of the texture to be loaded (something like "PNG" or "TGA")
* @param in The input stream from which the image data will be read
* @return The newly created texture
* @throws IOException Indicates a failure to read the image data
*/
public static Texture getTexture(String format, InputStream in) throws IOException {
return getTexture(format, in, false, GL11.GL_LINEAR);
}
/**
* Load a texture with a given format from the supplied input stream
*
* @param format The format of the texture to be loaded (something like "PNG" or "TGA")
* @param in The input stream from which the image data will be read
* @param flipped True if the image should be flipped vertically on loading
* @return The newly created texture
* @throws IOException Indicates a failure to read the image data
*/
public static Texture getTexture(String format, InputStream in, boolean flipped) throws IOException {
return getTexture(format, in, flipped, GL11.GL_LINEAR);
}
/**
* Load a texture with a given format from the supplied input stream
*
* @param format The format of the texture to be loaded (something like "PNG" or "TGA")
* @param in The input stream from which the image data will be read
* @param filter The GL texture filter to use for scaling up and down
* @return The newly created texture
* @throws IOException Indicates a failure to read the image data
*/
public static Texture getTexture(String format, InputStream in, int filter) throws IOException {
return getTexture(format, in, false, filter);
}
/**
* Load a texture with a given format from the supplied input stream
*
* @param format The format of the texture to be loaded (something like "PNG" or "TGA")
* @param in The input stream from which the image data will be read
* @param flipped True if the image should be flipped vertically on loading
* @param filter The GL texture filter to use for scaling up and down
* @return The newly created texture
* @throws IOException Indicates a failure to read the image data
*/
public static Texture getTexture(String format, InputStream in, boolean flipped, int filter) throws IOException {
return InternalTextureLoader.get().getTexture(in, in.toString()+"."+format, flipped, filter);
}
}

View File

@@ -0,0 +1,4 @@
<BODY>
This package contains the nitty gritty image manipulation code for using OpenGL with standard image formats. As
a user you shouldn't need to access anything here directly.
</BODY>

View File

@@ -0,0 +1,228 @@
package org.newdawn.slick.opengl.pbuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.opengl.SlickCallable;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.util.Log;
/**
* A graphics implementation that renders to an FBO
*
* @author kevin
*/
public class FBOGraphics extends Graphics {
/** The image we're we're sort of rendering to */
private Image image;
/** The ID of the FBO in use */
private int FBO;
/** True if this context is valid */
private boolean valid = true;
/**
* Create a new graphics context around an FBO
*
* @param image The image we're rendering to
* @throws SlickException Indicates a failure to use pbuffers
*/
public FBOGraphics(Image image) throws SlickException {
super(image.getTexture().getTextureWidth(), image.getTexture().getTextureHeight());
this.image = image;
Log.debug("Creating FBO "+image.getWidth()+"x"+image.getHeight());
boolean FBOEnabled = GLContext.getCapabilities().GL_EXT_framebuffer_object;
if (!FBOEnabled) {
throw new SlickException("Your OpenGL card does not support FBO and hence can't handle the dynamic images required for this application.");
}
init();
}
/**
* Check the FBO for completeness as shown in the LWJGL tutorial
*
* @throws SlickException Indicates an incomplete FBO
*/
private void completeCheck() throws SlickException {
int framebuffer = EXTFramebufferObject.glCheckFramebufferStatusEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT);
switch ( framebuffer ) {
case EXTFramebufferObject.GL_FRAMEBUFFER_COMPLETE_EXT:
break;
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception" );
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception" );
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception" );
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception" );
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception" );
case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
throw new SlickException( "FrameBuffer: " + FBO
+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception" );
default:
throw new SlickException( "Unexpected reply from glCheckFramebufferStatusEXT: " + framebuffer);
}
}
/**
* Initialise the FBO that will be used to render to
*
* @throws SlickException
*/
private void init() throws SlickException {
IntBuffer buffer = BufferUtils.createIntBuffer(1);
EXTFramebufferObject.glGenFramebuffersEXT(buffer);
FBO = buffer.get();
// for some reason FBOs won't work on textures unless you've absolutely just
// created them.
try {
Texture tex = InternalTextureLoader.get().createTexture(image.getWidth(), image.getHeight(), image.getFilter());
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, FBO);
EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT,
GL11.GL_TEXTURE_2D, tex.getTextureID(), 0);
completeCheck();
unbind();
// Clear our destination area before using it
clear();
flush();
// keep hold of the original content
drawImage(image, 0, 0);
image.setTexture(tex);
} catch (Exception e) {
throw new SlickException("Failed to create new texture for FBO");
}
}
/**
* Bind to the FBO created
*/
private void bind() {
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, FBO);
GL11.glReadBuffer(EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT);
}
/**
* Unbind from the FBO created
*/
private void unbind() {
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
GL11.glReadBuffer(GL11.GL_BACK);
}
/**
* @see org.newdawn.slick.Graphics#disable()
*/
protected void disable() {
GL.flush();
unbind();
GL11.glPopClientAttrib();
GL11.glPopAttrib();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPopMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
SlickCallable.leaveSafeBlock();
}
/**
* @see org.newdawn.slick.Graphics#enable()
*/
protected void enable() {
if (!valid) {
throw new RuntimeException("Attempt to use a destroy()ed offscreen graphics context.");
}
SlickCallable.enterSafeBlock();
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GL11.glPushClientAttrib(GL11.GL_ALL_CLIENT_ATTRIB_BITS);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glPushMatrix();
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glPushMatrix();
bind();
initGL();
}
/**
* Initialise the GL context
*/
protected void initGL() {
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClearDepth(1);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glViewport(0,0,screenWidth,screenHeight);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
enterOrtho();
}
/**
* Enter the orthographic mode
*/
protected void enterOrtho() {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, screenWidth, 0, screenHeight, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
/**
* @see org.newdawn.slick.Graphics#destroy()
*/
public void destroy() {
super.destroy();
IntBuffer buffer = BufferUtils.createIntBuffer(1);
buffer.put(FBO);
buffer.flip();
EXTFramebufferObject.glDeleteFramebuffersEXT(buffer);
valid = false;
}
/**
* @see org.newdawn.slick.Graphics#flush()
*/
public void flush() {
super.flush();
image.flushPixelData();
}
}

View File

@@ -0,0 +1,141 @@
package org.newdawn.slick.opengl.pbuffer;
import java.util.HashMap;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.Pbuffer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.util.Log;
/**
* A factory to produce an appropriate render to texture graphics context based on current
* hardware
*
* @author kevin
*/
public class GraphicsFactory {
/** The graphics list of graphics contexts created */
private static HashMap graphics = new HashMap();
/** True if pbuffers are supported */
private static boolean pbuffer = true;
/** True if pbuffer render to texture are supported */
private static boolean pbufferRT = true;
/** True if fbo are supported */
private static boolean fbo = true;
/** True if we've initialised */
private static boolean init = false;
/**
* Initialise offscreen rendering by checking what buffers are supported
* by the card
*
* @throws SlickException Indicates no buffers are supported
*/
private static void init() throws SlickException {
init = true;
if (fbo) {
fbo = GLContext.getCapabilities().GL_EXT_framebuffer_object;
}
pbuffer = (Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) != 0;
pbufferRT = (Pbuffer.getCapabilities() & Pbuffer.RENDER_TEXTURE_SUPPORTED) != 0;
if (!fbo && !pbuffer && !pbufferRT) {
throw new SlickException("Your OpenGL card does not support offscreen buffers and hence can't handle the dynamic images required for this application.");
}
Log.info("Offscreen Buffers FBO="+fbo+" PBUFFER="+pbuffer+" PBUFFERRT="+pbufferRT);
}
/**
* Force FBO use on or off
*
* @param useFBO True if we should try and use FBO for offscreen images
*/
public static void setUseFBO(boolean useFBO) {
fbo = useFBO;
}
/**
* Check if we're using FBO for dynamic textures
*
* @return True if we're using FBOs
*/
public static boolean usingFBO() {
return fbo;
}
/**
* Check if we're using PBuffer for dynamic textures
*
* @return True if we're using PBuffer
*/
public static boolean usingPBuffer() {
return !fbo && pbuffer;
}
/**
* Get a graphics context for a particular image
*
* @param image The image for which to retrieve the graphics context
* @return The graphics context
* @throws SlickException Indicates it wasn't possible to create a graphics context
* given available hardware.
*/
public static Graphics getGraphicsForImage(Image image) throws SlickException {
Graphics g = (Graphics) graphics.get(image.getTexture());
if (g == null) {
g = createGraphics(image);
graphics.put(image.getTexture(), g);
}
return g;
}
/**
* Release any graphics context that is assocaited with the given image
*
* @param image The image to release
* @throws SlickException Indicates a failure to release the context
*/
public static void releaseGraphicsForImage(Image image) throws SlickException {
Graphics g = (Graphics) graphics.remove(image.getTexture());
if (g != null) {
g.destroy();
}
}
/**
* Create an underlying graphics context for the given image
*
* @param image The image we want to render to
* @return The graphics context created
* @throws SlickException
*/
private static Graphics createGraphics(Image image) throws SlickException {
init();
if (fbo) {
try {
return new FBOGraphics(image);
} catch (Exception e) {
fbo = false;
Log.warn("FBO failed in use, falling back to PBuffer");
}
}
if (pbuffer) {
if (pbufferRT) {
return new PBufferGraphics(image);
} else {
return new PBufferUniqueGraphics(image);
}
}
throw new SlickException("Failed to create offscreen buffer even though the card reports it's possible");
}
}

View File

@@ -0,0 +1,172 @@
package org.newdawn.slick.opengl.pbuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.opengl.RenderTexture;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.SlickCallable;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.util.Log;
/**
* A graphics implementation that renders to a PBuffer
*
* @author kevin
*/
public class PBufferGraphics extends Graphics {
/** The pbuffer we're going to render to */
private Pbuffer pbuffer;
/** The image we're we're sort of rendering to */
private Image image;
/**
* Create a new graphics context around a pbuffer
*
* @param image The image we're rendering to
* @throws SlickException Indicates a failure to use pbuffers
*/
public PBufferGraphics(Image image) throws SlickException {
super(image.getTexture().getTextureWidth(), image.getTexture().getTextureHeight());
this.image = image;
Log.debug("Creating pbuffer(rtt) "+image.getWidth()+"x"+image.getHeight());
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0) {
throw new SlickException("Your OpenGL card does not support PBuffers and hence can't handle the dynamic images required for this application.");
}
if ((Pbuffer.getCapabilities() & Pbuffer.RENDER_TEXTURE_SUPPORTED) == 0) {
throw new SlickException("Your OpenGL card does not support Render-To-Texture and hence can't handle the dynamic images required for this application.");
}
init();
}
/**
* Initialise the PBuffer that will be used to render to
*
* @throws SlickException
*/
private void init() throws SlickException {
try {
Texture tex = InternalTextureLoader.get().createTexture(image.getWidth(), image.getHeight(), image.getFilter());
final RenderTexture rt = new RenderTexture(false, true, false, false, RenderTexture.RENDER_TEXTURE_2D, 0);
pbuffer = new Pbuffer(screenWidth, screenHeight, new PixelFormat(8, 0, 0), rt, null);
// Initialise state of the pbuffer context.
pbuffer.makeCurrent();
initGL();
GL.glBindTexture(GL11.GL_TEXTURE_2D, tex.getTextureID());
pbuffer.releaseTexImage(Pbuffer.FRONT_LEFT_BUFFER);
image.draw(0,0);
image.setTexture(tex);
Display.makeCurrent();
} catch (Exception e) {
Log.error(e);
throw new SlickException("Failed to create PBuffer for dynamic image. OpenGL driver failure?");
}
}
/**
* @see org.newdawn.slick.Graphics#disable()
*/
protected void disable() {
GL.flush();
// Bind the texture after rendering.
GL.glBindTexture(GL11.GL_TEXTURE_2D, image.getTexture().getTextureID());
pbuffer.bindTexImage(Pbuffer.FRONT_LEFT_BUFFER);
try {
Display.makeCurrent();
} catch (LWJGLException e) {
Log.error(e);
}
SlickCallable.leaveSafeBlock();
}
/**
* @see org.newdawn.slick.Graphics#enable()
*/
protected void enable() {
SlickCallable.enterSafeBlock();
try {
if (pbuffer.isBufferLost()) {
pbuffer.destroy();
init();
}
pbuffer.makeCurrent();
} catch (Exception e) {
Log.error("Failed to recreate the PBuffer");
throw new RuntimeException(e);
}
// Put the renderer contents to the texture
GL.glBindTexture(GL11.GL_TEXTURE_2D, image.getTexture().getTextureID());
pbuffer.releaseTexImage(Pbuffer.FRONT_LEFT_BUFFER);
TextureImpl.unbind();
initGL();
}
/**
* Initialise the GL context
*/
protected void initGL() {
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClearDepth(1);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glViewport(0,0,screenWidth,screenHeight);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
enterOrtho();
}
/**
* Enter the orthographic mode
*/
protected void enterOrtho() {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, screenWidth, 0, screenHeight, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
/**
* @see org.newdawn.slick.Graphics#destroy()
*/
public void destroy() {
super.destroy();
pbuffer.destroy();
}
/**
* @see org.newdawn.slick.Graphics#flush()
*/
public void flush() {
super.flush();
image.flushPixelData();
}
}

View File

@@ -0,0 +1,167 @@
package org.newdawn.slick.opengl.pbuffer;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.opengl.SlickCallable;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.InternalTextureLoader;
import org.newdawn.slick.util.Log;
/**
* A graphics implementation that renders to a PBuffer using a unique context, i.e.
* without render to texture
*
* @author kevin
*/
public class PBufferUniqueGraphics extends Graphics {
/** The pbuffer we're going to render to */
private Pbuffer pbuffer;
/** The image we're we're sort of rendering to */
private Image image;
/**
* Create a new graphics context around a pbuffer
*
* @param image The image we're rendering to
* @throws SlickException Indicates a failure to use pbuffers
*/
public PBufferUniqueGraphics(Image image) throws SlickException {
super(image.getTexture().getTextureWidth(), image.getTexture().getTextureHeight());
this.image = image;
Log.debug("Creating pbuffer(unique) "+image.getWidth()+"x"+image.getHeight());
if ((Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0) {
throw new SlickException("Your OpenGL card does not support PBuffers and hence can't handle the dynamic images required for this application.");
}
init();
}
/**
* Initialise the PBuffer that will be used to render to
*
* @throws SlickException
*/
private void init() throws SlickException {
try {
Texture tex = InternalTextureLoader.get().createTexture(image.getWidth(), image.getHeight(), image.getFilter());
pbuffer = new Pbuffer(screenWidth, screenHeight, new PixelFormat(8, 0, 0), null, null);
// Initialise state of the pbuffer context.
pbuffer.makeCurrent();
initGL();
image.draw(0,0);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, tex.getTextureID());
GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 0, 0,
tex.getTextureWidth(),
tex.getTextureHeight(), 0);
image.setTexture(tex);
Display.makeCurrent();
} catch (Exception e) {
Log.error(e);
throw new SlickException("Failed to create PBuffer for dynamic image. OpenGL driver failure?");
}
}
/**
* @see org.newdawn.slick.Graphics#disable()
*/
protected void disable() {
// Bind the texture after rendering.
GL11.glBindTexture(GL11.GL_TEXTURE_2D, image.getTexture().getTextureID());
GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, 0, 0,
image.getTexture().getTextureWidth(),
image.getTexture().getTextureHeight(), 0);
try {
Display.makeCurrent();
} catch (LWJGLException e) {
Log.error(e);
}
SlickCallable.leaveSafeBlock();
}
/**
* @see org.newdawn.slick.Graphics#enable()
*/
protected void enable() {
SlickCallable.enterSafeBlock();
try {
if (pbuffer.isBufferLost()) {
pbuffer.destroy();
init();
}
pbuffer.makeCurrent();
} catch (Exception e) {
Log.error("Failed to recreate the PBuffer");
Log.error(e);
throw new RuntimeException(e);
}
// Put the renderer contents to the texture
TextureImpl.unbind();
initGL();
}
/**
* Initialise the GL context
*/
protected void initGL() {
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClearDepth(1);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glViewport(0,0,screenWidth,screenHeight);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
enterOrtho();
}
/**
* Enter the orthographic mode
*/
protected void enterOrtho() {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, screenWidth, 0, screenHeight, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
/**
* @see org.newdawn.slick.Graphics#destroy()
*/
public void destroy() {
super.destroy();
pbuffer.destroy();
}
/**
* @see org.newdawn.slick.Graphics#flush()
*/
public void flush() {
super.flush();
image.flushPixelData();
}
}

View File

@@ -0,0 +1,72 @@
package org.newdawn.slick.opengl.renderer;
/**
* The default version of the renderer relies of GL calls to do everything.
* Unfortunately this is driver dependent and often implemented inconsistantly
*
* @author kevin
*/
public class DefaultLineStripRenderer implements LineStripRenderer {
/** The access to OpenGL */
private SGL GL = Renderer.get();
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#end()
*/
public void end() {
GL.glEnd();
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setAntiAlias(boolean)
*/
public void setAntiAlias(boolean antialias) {
if (antialias) {
GL.glEnable(SGL.GL_LINE_SMOOTH);
} else {
GL.glDisable(SGL.GL_LINE_SMOOTH);
}
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setWidth(float)
*/
public void setWidth(float width) {
GL.glLineWidth(width);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#start()
*/
public void start() {
GL.glBegin(SGL.GL_LINE_STRIP);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#vertex(float, float)
*/
public void vertex(float x, float y) {
GL.glVertex2f(x,y);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#color(float, float, float, float)
*/
public void color(float r, float g, float b, float a) {
GL.glColor4f(r, g, b, a);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setLineCaps(boolean)
*/
public void setLineCaps(boolean caps) {
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#applyGLLineFixes()
*/
public boolean applyGLLineFixes() {
return true;
}
}

View File

@@ -0,0 +1,426 @@
package org.newdawn.slick.opengl.renderer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.opengl.EXTSecondaryColor;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
/**
* The default OpenGL renderer, uses immediate mode for everything
*
* @author kevin
*/
public class ImmediateModeOGLRenderer implements SGL {
/** The width of the display */
private int width;
/** The height of the display */
private int height;
/** The current colour */
private float[] current = new float[] {1,1,1,1};
/** The global colour scale */
protected float alphaScale = 1;
/**
* @see org.newdawn.slick.opengl.renderer.SGL#initDisplay(int, int)
*/
public void initDisplay(int width, int height) {
this.width = width;
this.height = height;
String extensions = GL11.glGetString(GL11.GL_EXTENSIONS);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glShadeModel(GL11.GL_SMOOTH);
GL11.glDisable(GL11.GL_DEPTH_TEST);
GL11.glDisable(GL11.GL_LIGHTING);
GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GL11.glClearDepth(1);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glViewport(0,0,width,height);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#enterOrtho(int, int)
*/
public void enterOrtho(int xsize, int ysize) {
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GL11.glOrtho(0, width, height, 0, 1, -1);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glTranslatef((width-xsize)/2,
(height-ysize)/2,0);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glBegin(int)
*/
public void glBegin(int geomType) {
GL11.glBegin(geomType);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glBindTexture(int, int)
*/
public void glBindTexture(int target, int id) {
GL11.glBindTexture(target, id);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glBlendFunc(int, int)
*/
public void glBlendFunc(int src, int dest) {
GL11.glBlendFunc(src, dest);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glCallList(int)
*/
public void glCallList(int id) {
GL11.glCallList(id);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glClear(int)
*/
public void glClear(int value) {
GL11.glClear(value);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glClearColor(float, float, float, float)
*/
public void glClearColor(float red, float green, float blue, float alpha) {
GL11.glClearColor(red, green, blue, alpha);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glClipPlane(int, java.nio.DoubleBuffer)
*/
public void glClipPlane(int plane, DoubleBuffer buffer) {
GL11.glClipPlane(plane, buffer);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glColor4f(float, float, float, float)
*/
public void glColor4f(float r, float g, float b, float a) {
a *= alphaScale;
current[0] = r;
current[1] = g;
current[2] = b;
current[3] = a;
GL11.glColor4f(r, g, b, a);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glColorMask(boolean, boolean, boolean, boolean)
*/
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
GL11.glColorMask(red, green, blue, alpha);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glCopyTexImage2D(int, int, int, int, int, int, int, int)
*/
public void glCopyTexImage2D(int target, int level, int internalFormat, int x, int y, int width, int height, int border) {
GL11.glCopyTexImage2D(target, level, internalFormat, x, y, width, height, border);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glDeleteTextures(java.nio.IntBuffer)
*/
public void glDeleteTextures(IntBuffer buffer) {
GL11.glDeleteTextures(buffer);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glDisable(int)
*/
public void glDisable(int item) {
GL11.glDisable(item);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glEnable(int)
*/
public void glEnable(int item) {
GL11.glEnable(item);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glEnd()
*/
public void glEnd() {
GL11.glEnd();
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glEndList()
*/
public void glEndList() {
GL11.glEndList();
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glGenLists(int)
*/
public int glGenLists(int count) {
return GL11.glGenLists(count);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glGetFloat(int, java.nio.FloatBuffer)
*/
public void glGetFloat(int id, FloatBuffer ret) {
GL11.glGetFloat(id, ret);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glGetInteger(int, java.nio.IntBuffer)
*/
public void glGetInteger(int id, IntBuffer ret) {
GL11.glGetInteger(id, ret);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glGetTexImage(int, int, int, int, java.nio.ByteBuffer)
*/
public void glGetTexImage(int target, int level, int format, int type, ByteBuffer pixels) {
GL11.glGetTexImage(target, level, format, type, pixels);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glLineWidth(float)
*/
public void glLineWidth(float width) {
GL11.glLineWidth(width);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glLoadIdentity()
*/
public void glLoadIdentity() {
GL11.glLoadIdentity();
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glNewList(int, int)
*/
public void glNewList(int id, int option) {
GL11.glNewList(id, option);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glPointSize(float)
*/
public void glPointSize(float size) {
GL11.glPointSize(size);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glPopMatrix()
*/
public void glPopMatrix() {
GL11.glPopMatrix();
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glPushMatrix()
*/
public void glPushMatrix() {
GL11.glPushMatrix();
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glReadPixels(int, int, int, int, int, int, java.nio.ByteBuffer)
*/
public void glReadPixels(int x, int y, int width, int height, int format, int type, ByteBuffer pixels) {
GL11.glReadPixels(x, y, width, height, format, type, pixels);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glRotatef(float, float, float, float)
*/
public void glRotatef(float angle, float x, float y, float z) {
GL11.glRotatef(angle, x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glScalef(float, float, float)
*/
public void glScalef(float x, float y, float z) {
GL11.glScalef(x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glScissor(int, int, int, int)
*/
public void glScissor(int x, int y, int width, int height) {
GL11.glScissor(x, y, width, height);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glTexCoord2f(float, float)
*/
public void glTexCoord2f(float u, float v) {
GL11.glTexCoord2f(u, v);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glTexEnvi(int, int, int)
*/
public void glTexEnvi(int target, int mode, int value) {
GL11.glTexEnvi(target, mode, value);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glTranslatef(float, float, float)
*/
public void glTranslatef(float x, float y, float z) {
GL11.glTranslatef(x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glVertex2f(float, float)
*/
public void glVertex2f(float x, float y) {
GL11.glVertex2f(x, y);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glVertex3f(float, float, float)
*/
public void glVertex3f(float x, float y, float z) {
GL11.glVertex3f(x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#flush()
*/
public void flush() {
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glTexParameteri(int, int, int)
*/
public void glTexParameteri(int target, int param, int value) {
GL11.glTexParameteri(target, param, value);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#getCurrentColor()
*/
public float[] getCurrentColor() {
return current;
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glDeleteLists(int, int)
*/
public void glDeleteLists(int list, int count) {
GL11.glDeleteLists(list, count);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glClearDepth(float)
*/
public void glClearDepth(float value) {
GL11.glClearDepth(value);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glDepthFunc(int)
*/
public void glDepthFunc(int func) {
GL11.glDepthFunc(func);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glDepthMask(boolean)
*/
public void glDepthMask(boolean mask) {
GL11.glDepthMask(mask);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#setGlobalAlphaScale(float)
*/
public void setGlobalAlphaScale(float alphaScale) {
this.alphaScale = alphaScale;
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glLoadMatrix(java.nio.FloatBuffer)
*/
public void glLoadMatrix(FloatBuffer buffer) {
GL11.glLoadMatrix(buffer);
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#glGenTextures(java.nio.IntBuffer)
*/
public void glGenTextures(IntBuffer ids) {
GL11.glGenTextures(ids);
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#glGetError()
*/
public void glGetError() {
GL11.glGetError();
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#glTexImage2D(int, int, int, int, int, int, int, int, java.nio.ByteBuffer)
*/
public void glTexImage2D(int target, int i, int dstPixelFormat,
int width, int height, int j, int srcPixelFormat,
int glUnsignedByte, ByteBuffer textureBuffer) {
GL11.glTexImage2D(target, i, dstPixelFormat, width, height, j, srcPixelFormat,glUnsignedByte,textureBuffer);
}
public void glTexSubImage2D(int glTexture2d, int i, int pageX, int pageY,
int width, int height, int glBgra, int glUnsignedByte,
ByteBuffer scratchByteBuffer) {
GL11.glTexSubImage2D(glTexture2d, i, pageX, pageY, width, height, glBgra, glUnsignedByte, scratchByteBuffer);
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#canTextureMirrorClamp()
*/
public boolean canTextureMirrorClamp() {
return GLContext.getCapabilities().GL_EXT_texture_mirror_clamp;
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#canSecondaryColor()
*/
public boolean canSecondaryColor() {
return GLContext.getCapabilities().GL_EXT_secondary_color;
}
/*
* (non-Javadoc)
* @see org.newdawn.slick.opengl.renderer.SGL#glSecondaryColor3ubEXT(byte, byte, byte)
*/
public void glSecondaryColor3ubEXT(byte b, byte c, byte d) {
EXTSecondaryColor.glSecondaryColor3ubEXT(b,c,d);
}
}

View File

@@ -0,0 +1,66 @@
package org.newdawn.slick.opengl.renderer;
/**
* The description of a class able to render line strips through
* OpenGL
*
* @author kevin
*/
public interface LineStripRenderer {
/**
* Check if we should apply default line fixes
*
* @return True if we should apply GL fixes
*/
public abstract boolean applyGLLineFixes();
/**
* Start the line strips
*/
public abstract void start();
/**
* End the line strips
*/
public abstract void end();
/**
* Add a vertex
*
* @param x The x coordinate of the vertex
* @param y The y coordinate of the vertex
*/
public abstract void vertex(float x, float y);
/**
* Apply a colour to the next vertex
*
* @param r The red component of the colour
* @param g The green component of the colour
* @param b The blue component of the colour
* @param a The alpha component of the colour
*/
public abstract void color(float r, float g, float b, float a);
/**
* Set the width of the lines to be drawn
*
* @param width The width of the lines to be drawn
*/
public abstract void setWidth(float width);
/**
* Indicate whether antialiasing should be applied
*
* @param antialias True if antialiasing should be applied
*/
public abstract void setAntiAlias(boolean antialias);
/**
* Indicate if we should render end caps
*
* @param caps True if we should render end caps
*/
public void setLineCaps(boolean caps);
}

View File

@@ -0,0 +1,293 @@
package org.newdawn.slick.opengl.renderer;
/**
* A line strip renderer that uses quads to generate lines
*
* @author kevin
*/
public class QuadBasedLineStripRenderer implements LineStripRenderer {
/** The renderer used to interact with GL */
private SGL GL = Renderer.get();
/** Maximum number of points allowed in a single strip */
public static int MAX_POINTS = 10000;
/** True if antialiasing is currently enabled */
private boolean antialias;
/** The width of the lines to draw */
private float width = 1;
/** The points to draw */
private float[] points;
/** The colours to draw */
private float[] colours;
/** The number of points to draw */
private int pts;
/** The number of colour points recorded */
private int cpt;
/** The default renderer used when width = 1 */
private DefaultLineStripRenderer def = new DefaultLineStripRenderer();
/** Indicates need to render half colour */
private boolean renderHalf;
/** True if we shoudl render end caps */
private boolean lineCaps = false;
/**
* Create a new strip renderer
*/
public QuadBasedLineStripRenderer() {
points = new float[MAX_POINTS * 2];
colours = new float[MAX_POINTS * 4];
}
/**
* Indicate if we should render end caps
*
* @param caps True if we should render end caps
*/
public void setLineCaps(boolean caps) {
this.lineCaps = caps;
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#start()
*/
public void start() {
if (width == 1) {
def.start();
return;
}
pts = 0;
cpt = 0;
GL.flush();
float[] col = GL.getCurrentColor();
color(col[0],col[1],col[2],col[3]);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#end()
*/
public void end() {
if (width == 1) {
def.end();
return;
}
renderLines(points, pts);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#vertex(float, float)
*/
public void vertex(float x, float y) {
if (width == 1) {
def.vertex(x,y);
return;
}
points[(pts*2)] = x;
points[(pts*2)+1] = y;
pts++;
int index = pts-1;
color(colours[(index*4)], colours[(index*4)+1], colours[(index*4)+2], colours[(index*4)+3]);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setWidth(float)
*/
public void setWidth(float width) {
this.width = width;
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setAntiAlias(boolean)
*/
public void setAntiAlias(boolean antialias) {
def.setAntiAlias(antialias);
this.antialias = antialias;
}
/**
* Render the lines applying antialiasing if required
*
* @param points The points to be rendered as lines
* @param count The number of points to render
*/
public void renderLines(float[] points, int count) {
if (antialias) {
GL.glEnable(SGL.GL_POLYGON_SMOOTH);
renderLinesImpl(points,count,width+1f);
}
GL.glDisable(SGL.GL_POLYGON_SMOOTH);
renderLinesImpl(points,count,width);
if (antialias) {
GL.glEnable(SGL.GL_POLYGON_SMOOTH);
}
}
/**
* Render the lines given
*
* @param points The points building up the lines
* @param count The number of points to render
* @param w The width to render at
*/
public void renderLinesImpl(float[] points, int count, float w) {
float width = w / 2;
float lastx1 = 0;
float lasty1 = 0;
float lastx2 = 0;
float lasty2 = 0;
GL.glBegin(SGL.GL_QUADS);
for (int i=0;i<count+1;i++) {
int current = i;
int next = i+1;
int prev = i-1;
if (prev < 0) {
prev += count;
}
if (next >= count) {
next -= count;
}
if (current >= count) {
current -= count;
}
float x1 = points[(current*2)];
float y1 = points[(current*2)+1];
float x2 = points[(next*2)];
float y2 = points[(next*2)+1];
// draw the next segment
float dx = x2 - x1;
float dy = y2 - y1;
if ((dx == 0) && (dy == 0)) {
continue;
}
float d2 = (dx*dx)+(dy*dy);
float d = (float) Math.sqrt(d2);
dx *= width;
dy *= width;
dx /= d;
dy /= d;
float tx = dy;
float ty = -dx;
if (i != 0) {
bindColor(prev);
GL.glVertex3f(lastx1,lasty1,0);
GL.glVertex3f(lastx2,lasty2,0);
bindColor(current);
GL.glVertex3f(x1+tx,y1+ty,0);
GL.glVertex3f(x1-tx,y1-ty,0);
}
lastx1 = x2-tx;
lasty1 = y2-ty;
lastx2 = x2+tx;
lasty2 = y2+ty;
if (i < count-1) {
bindColor(current);
GL.glVertex3f(x1+tx,y1+ty,0);
GL.glVertex3f(x1-tx,y1-ty,0);
bindColor(next);
GL.glVertex3f(x2-tx,y2-ty,0);
GL.glVertex3f(x2+tx,y2+ty,0);
}
}
GL.glEnd();
float step = width <= 12.5f ? 5 : 180 / (float)Math.ceil(width / 2.5);
// start cap
if (lineCaps) {
float dx = points[2] - points[0];
float dy = points[3] - points[1];
float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) + 90;
if ((dx != 0) || (dy != 0)) {
GL.glBegin(SGL.GL_TRIANGLE_FAN);
bindColor(0);
GL.glVertex2f(points[0], points[1]);
for (int i=0;i<180+step;i+=step) {
float ang = (float) Math.toRadians(fang+i);
GL.glVertex2f(points[0]+((float) (Math.cos(ang) * width)),
points[1]+((float) (Math.sin(ang) * width)));
}
GL.glEnd();
}
}
// end cap
if (lineCaps) {
float dx = points[(count*2)-2] - points[(count*2)-4];
float dy = points[(count*2)-1] - points[(count*2)-3];
float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) - 90;
if ((dx != 0) || (dy != 0)) {
GL.glBegin(SGL.GL_TRIANGLE_FAN);
bindColor(count-1);
GL.glVertex2f(points[(count*2)-2], points[(count*2)-1]);
for (int i=0;i<180+step;i+=step) {
float ang = (float) Math.toRadians(fang+i);
GL.glVertex2f(points[(count*2)-2]+((float) (Math.cos(ang) * width)),
points[(count*2)-1]+((float) (Math.sin(ang) * width)));
}
GL.glEnd();
}
}
}
/**
* Bind the colour at a given index in the array
*
* @param index The index of the colour to bind
*/
private void bindColor(int index) {
if (index < cpt) {
if (renderHalf) {
GL.glColor4f(colours[(index*4)]*0.5f, colours[(index*4)+1]*0.5f,
colours[(index*4)+2]*0.5f, colours[(index*4)+3]*0.5f);
} else {
GL.glColor4f(colours[(index*4)], colours[(index*4)+1],
colours[(index*4)+2], colours[(index*4)+3]);
}
}
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#color(float, float, float, float)
*/
public void color(float r, float g, float b, float a) {
if (width == 1) {
def.color(r,g,b,a);
return;
}
colours[(pts*4)] = r;
colours[(pts*4)+1] = g;
colours[(pts*4)+2] = b;
colours[(pts*4)+3] = a;
cpt++;
}
public boolean applyGLLineFixes() {
if (width == 1) {
return def.applyGLLineFixes();
}
return def.applyGLLineFixes();
}
}

View File

@@ -0,0 +1,99 @@
package org.newdawn.slick.opengl.renderer;
/**
* The static holder for the current GL implementation. Note that this
* renderer can only be set before the game has been started.
*
* @author kevin
*/
public class Renderer {
/** The indicator for immediate mode renderering (the default) */
public static final int IMMEDIATE_RENDERER = 1;
/** The indicator for vertex array based rendering */
public static final int VERTEX_ARRAY_RENDERER = 2;
/** The indicator for direct GL line renderer (the default) */
public static final int DEFAULT_LINE_STRIP_RENDERER = 3;
/** The indicator for consistant quad based lines */
public static final int QUAD_BASED_LINE_STRIP_RENDERER = 4;
/** The renderer in use */
private static SGL renderer = new ImmediateModeOGLRenderer();
/** The line strip renderer to use */
private static LineStripRenderer lineStripRenderer = new DefaultLineStripRenderer();
/**
* Set the renderer to one of the known types
*
* @param type The type of renderer to use
*/
public static void setRenderer(int type) {
switch (type) {
case IMMEDIATE_RENDERER:
setRenderer(new ImmediateModeOGLRenderer());
return;
case VERTEX_ARRAY_RENDERER:
setRenderer(new VAOGLRenderer());
return;
}
throw new RuntimeException("Unknown renderer type: "+type);
}
/**
* Set the line strip renderer to one of the known types
*
* @param type The type of renderer to use
*/
public static void setLineStripRenderer(int type) {
switch (type) {
case DEFAULT_LINE_STRIP_RENDERER:
setLineStripRenderer(new DefaultLineStripRenderer());
return;
case QUAD_BASED_LINE_STRIP_RENDERER:
setLineStripRenderer(new QuadBasedLineStripRenderer());
return;
}
throw new RuntimeException("Unknown line strip renderer type: "+type);
}
/**
* Set the line strip renderer to be used globally
*
* @param renderer The line strip renderer to be used
*/
public static void setLineStripRenderer(LineStripRenderer renderer) {
lineStripRenderer = renderer;
}
/**
* Set the renderer to be used
*
* @param r The renderer to be used
*/
public static void setRenderer(SGL r) {
renderer = r;
}
/**
* Get the renderer to be used when accessing GL
*
* @return The renderer to be used when accessing GL
*/
public static SGL get() {
return renderer;
}
/**
* Get the line strip renderer to use
*
* @return The line strip renderer to use
*/
public static LineStripRenderer getLineStripRenderer() {
return lineStripRenderer;
}
}

View File

@@ -0,0 +1,535 @@
package org.newdawn.slick.opengl.renderer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.opengl.EXTSecondaryColor;
import org.lwjgl.opengl.EXTTextureMirrorClamp;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
/**
* The description of the OpenGL functions used Slick. Any other rendering method will
* need to emulate these.
*
* @author kevin
*/
public interface SGL {
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_2D = GL11.GL_TEXTURE_2D;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_RGBA = GL11.GL_RGBA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_RGB = GL11.GL_RGB;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_UNSIGNED_BYTE = GL11.GL_UNSIGNED_BYTE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_LINEAR = GL11.GL_LINEAR;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_NEAREST = GL11.GL_NEAREST;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_MIN_FILTER = GL11.GL_TEXTURE_MIN_FILTER;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_MAG_FILTER = GL11.GL_TEXTURE_MAG_FILTER;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_POINT_SMOOTH = GL11.GL_POINT_SMOOTH;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_POLYGON_SMOOTH = GL11.GL_POLYGON_SMOOTH;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_LINE_SMOOTH = GL11.GL_LINE_SMOOTH;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_SCISSOR_TEST = GL11.GL_SCISSOR_TEST;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_MODULATE = GL11.GL_MODULATE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_ENV = GL11.GL_TEXTURE_ENV;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_ENV_MODE = GL11.GL_TEXTURE_ENV_MODE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_QUADS = GL11.GL_QUADS;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_POINTS = GL11.GL_POINTS;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_LINES = GL11.GL_LINES;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_LINE_STRIP = GL11.GL_LINE_STRIP;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TRIANGLES = GL11.GL_TRIANGLES;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TRIANGLE_FAN = GL11.GL_TRIANGLE_FAN;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_SRC_ALPHA = GL11.GL_SRC_ALPHA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_ONE = GL11.GL_ONE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_ONE_MINUS_DST_ALPHA = GL11.GL_ONE_MINUS_DST_ALPHA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_DST_ALPHA = GL11.GL_DST_ALPHA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_ONE_MINUS_SRC_ALPHA = GL11.GL_ONE_MINUS_SRC_ALPHA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_COMPILE = GL11.GL_COMPILE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_MAX_TEXTURE_SIZE = GL11.GL_MAX_TEXTURE_SIZE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_COLOR_BUFFER_BIT = GL11.GL_COLOR_BUFFER_BIT;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_DEPTH_BUFFER_BIT = GL11.GL_DEPTH_BUFFER_BIT;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_BLEND = GL11.GL_BLEND;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_COLOR_CLEAR_VALUE = GL11.GL_COLOR_CLEAR_VALUE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_LINE_WIDTH = GL11.GL_LINE_WIDTH;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_CLIP_PLANE0 = GL11.GL_CLIP_PLANE0;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_CLIP_PLANE1 = GL11.GL_CLIP_PLANE1;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_CLIP_PLANE2 = GL11.GL_CLIP_PLANE2;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_CLIP_PLANE3 = GL11.GL_CLIP_PLANE3;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_COMPILE_AND_EXECUTE = GL11.GL_COMPILE_AND_EXECUTE;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_RGBA8 = GL11.GL_RGBA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_RGBA16 = GL11.GL_RGBA16;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_BGRA = GL12.GL_BGRA;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_MIRROR_CLAMP_TO_EDGE_EXT = EXTTextureMirrorClamp.GL_MIRROR_CLAMP_TO_EDGE_EXT;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_WRAP_S = GL11.GL_TEXTURE_WRAP_S;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_TEXTURE_WRAP_T = GL11.GL_TEXTURE_WRAP_T;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_CLAMP = GL11.GL_CLAMP;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_COLOR_SUM_EXT = EXTSecondaryColor.GL_COLOR_SUM_EXT;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_ALWAYS = GL11.GL_ALWAYS;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_DEPTH_TEST = GL11.GL_DEPTH_TEST;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_NOTEQUAL = GL11.GL_NOTEQUAL;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_EQUAL = GL11.GL_EQUAL;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_SRC_COLOR = GL11.GL_SRC_COLOR;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_ONE_MINUS_SRC_COLOR = GL11.GL_ONE_MINUS_SRC_COLOR;
/** OpenGL Enum - @url http://www.opengl.org/documentation */
public static final int GL_MODELVIEW_MATRIX = GL11.GL_MODELVIEW_MATRIX;
/**
* Flush the current state of the renderer down to GL
*/
public void flush();
/**
* Initialise the display
*
* @param width The width of the display
* @param height The height of the display
*/
public void initDisplay(int width, int height);
/**
* Enter orthographic mode
*
* @param xsize The size of the ortho display
* @param ysize The size of the ortho display
*/
public void enterOrtho(int xsize, int ysize);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param red
* @param green
* @param blue
* @param alpha
*/
public void glClearColor(float red, float green, float blue, float alpha);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param plane
* @param buffer
*/
public void glClipPlane(int plane, DoubleBuffer buffer);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
* @param width
* @param height
*/
public void glScissor(int x, int y, int width, int height);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param width
*/
public void glLineWidth(float width);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param value
*/
public void glClear(int value);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param red
* @param green
* @param blue
* @param alpha
*/
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glLoadIdentity();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param id
* @param ret
*/
public void glGetInteger(int id, IntBuffer ret);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param id
* @param ret
*/
public void glGetFloat(int id, FloatBuffer ret);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param item
*/
public void glEnable(int item);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param item
*/
public void glDisable(int item);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param target
* @param id
*/
public void glBindTexture(int target, int id);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param target
* @param level
* @param format
* @param type
* @param pixels
*/
public void glGetTexImage(int target, int level, int format, int type, ByteBuffer pixels);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param buffer
*/
public void glDeleteTextures(IntBuffer buffer);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param r
* @param g
* @param b
* @param a
*/
public void glColor4f(float r, float g, float b, float a);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param u
* @param v
*/
public void glTexCoord2f(float u, float v);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
* @param z
*/
public void glVertex3f(float x, float y, float z);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
*/
public void glVertex2f(float x, float y);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param angle
* @param x
* @param y
* @param z
*/
public void glRotatef(float angle, float x, float y, float z);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
* @param z
*/
public void glTranslatef(float x, float y, float z);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param geomType
*/
public void glBegin(int geomType);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glEnd();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param target
* @param mode
* @param value
*/
public void glTexEnvi(int target, int mode, int value);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param size
*/
public void glPointSize(float size);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
* @param z
*/
public void glScalef(float x, float y, float z);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glPushMatrix();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glPopMatrix();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param src
* @param dest
*/
public void glBlendFunc(int src, int dest);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param count
* @return The index of the lists
*/
public int glGenLists(int count);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param id
* @param option
*/
public void glNewList(int id, int option);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glEndList();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param id
*/
public void glCallList(int id);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param target
* @param level
* @param internalFormat
* @param x
* @param y
* @param width
* @param height
* @param border
*/
public void glCopyTexImage2D(int target, int level, int internalFormat,
int x, int y, int width, int height, int border);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param x
* @param y
* @param width
* @param height
* @param format
* @param type
* @param pixels
*/
public void glReadPixels(int x, int y, int width, int height, int format, int type,
ByteBuffer pixels);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param target
* @param param
* @param value
*/
public void glTexParameteri(int target, int param, int value);
/**
* Get the current colour being rendered
*
* @return The current colour being rendered
*/
public float[] getCurrentColor();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param list
* @param count
*/
public void glDeleteLists(int list, int count);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param mask
*/
public void glDepthMask(boolean mask);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param value
*/
public void glClearDepth(float value);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param func
*/
public void glDepthFunc(int func);
/**
* Set the scaling we'll apply to any colour binds in this renderer
*
* @param alphaScale The scale to apply to any colour binds
*/
public void setGlobalAlphaScale(float alphaScale);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param buffer
*/
public void glLoadMatrix(FloatBuffer buffer);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*
* @param ids
*/
public void glGenTextures(IntBuffer ids);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glGetError();
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glTexImage2D(int target, int i, int dstPixelFormat,
int get2Fold, int get2Fold2, int j, int srcPixelFormat,
int glUnsignedByte, ByteBuffer textureBuffer);
/**
* OpenGL Method - @url http://www.opengl.org/documentation/
*/
public void glTexSubImage2D(int glTexture2d, int i, int pageX, int pageY,
int width, int height, int glBgra, int glUnsignedByte,
ByteBuffer scratchByteBuffer);
/**
* Check if the mirror clamp extension is available
*
* @return True if the mirro clamp extension is available
*/
public boolean canTextureMirrorClamp();
public boolean canSecondaryColor();
public void glSecondaryColor3ubEXT(byte b, byte c, byte d);
}

View File

@@ -0,0 +1,417 @@
package org.newdawn.slick.opengl.renderer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
/**
* A renderer that caches all operations into an array, creates an opengl vertex array when
* required and spits the data down to the card in batch mode
*
* @author kevin
*/
public class VAOGLRenderer extends ImmediateModeOGLRenderer {
/** The tolerance to rendering immediate */
private static final int TOLERANCE = 20;
/** Indicates there is no current geometry buffer */
public static final int NONE = -1;
/** The maximum number of vertices draw in one batch */
public static final int MAX_VERTS = 5000;
/** The type of the geometry array currently being built - i.e. GL_QUADS */
private int currentType = NONE;
/** The last colour applied */
private float[] color = new float[] {1f,1f,1f,1f};
/** The last texture applied */
private float[] tex = new float[] {0f,0f};
/** The index of the next vertex to be created */
private int vertIndex;
/** The vertex data cached */
private float[] verts = new float[MAX_VERTS*3];
/** The vertex colour data cached */
private float[] cols = new float[MAX_VERTS*4];
/** The vertex texture coordiante data cached */
private float[] texs = new float[MAX_VERTS*3];
/** The buffer used to pass the vertex data to the card */
private FloatBuffer vertices = BufferUtils.createFloatBuffer(MAX_VERTS * 3);
/** The buffer used to pass the vertex color data to the card */
private FloatBuffer colors = BufferUtils.createFloatBuffer(MAX_VERTS * 4);
/** The buffer used to pass the vertex texture coordinate data to the card */
private FloatBuffer textures = BufferUtils.createFloatBuffer(MAX_VERTS * 2);
/** The stack for entering list creation mode - when we're creating a list we can't use our VAs */
private int listMode = 0;
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#initDisplay(int, int)
*/
public void initDisplay(int width, int height) {
super.initDisplay(width, height);
startBuffer();
GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
}
/**
* Start a new buffer for a vertex array
*/
private void startBuffer() {
vertIndex = 0;
}
/**
* Flush the currently cached data down to the card
*/
private void flushBuffer() {
if (vertIndex == 0) {
return;
}
if (currentType == NONE) {
return;
}
if (vertIndex < TOLERANCE) {
GL11.glBegin(currentType);
for (int i=0;i<vertIndex;i++) {
GL11.glColor4f(cols[(i*4)+0], cols[(i*4)+1], cols[(i*4)+2], cols[(i*4)+3]);
GL11.glTexCoord2f(texs[(i*2)+0], texs[(i*2)+1]);
GL11.glVertex3f(verts[(i*3)+0], verts[(i*3)+1], verts[(i*3)+2]);
}
GL11.glEnd();
currentType = NONE;
return;
}
vertices.clear();
colors.clear();
textures.clear();
vertices.put(verts,0,vertIndex*3);
colors.put(cols,0,vertIndex*4);
textures.put(texs,0,vertIndex*2);
vertices.flip();
colors.flip();
textures.flip();
GL11.glVertexPointer(3,0,vertices);
GL11.glColorPointer(4,0,colors);
GL11.glTexCoordPointer(2,0,textures);
GL11.glDrawArrays(currentType, 0, vertIndex);
currentType = NONE;
}
/**
* Apply the current buffer and restart it
*/
private void applyBuffer() {
if (listMode > 0) {
return;
}
if (vertIndex != 0) {
flushBuffer();
startBuffer();
}
super.glColor4f(color[0], color[1], color[2], color[3]);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#flush()
*/
public void flush() {
super.flush();
applyBuffer();
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBegin(int)
*/
public void glBegin(int geomType) {
if (listMode > 0) {
super.glBegin(geomType);
return;
}
if (currentType != geomType) {
applyBuffer();
currentType = geomType;
}
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glColor4f(float, float, float, float)
*/
public void glColor4f(float r, float g, float b, float a) {
a *= alphaScale;
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
if (listMode > 0) {
super.glColor4f(r,g,b,a);
return;
}
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEnd()
*/
public void glEnd() {
if (listMode > 0) {
super.glEnd();
return;
}
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTexCoord2f(float, float)
*/
public void glTexCoord2f(float u, float v) {
if (listMode > 0) {
super.glTexCoord2f(u,v);
return;
}
tex[0] = u;
tex[1] = v;
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glVertex2f(float, float)
*/
public void glVertex2f(float x, float y) {
if (listMode > 0) {
super.glVertex2f(x,y);
return;
}
glVertex3f(x,y,0);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glVertex3f(float, float, float)
*/
public void glVertex3f(float x, float y, float z) {
if (listMode > 0) {
super.glVertex3f(x,y,z);
return;
}
verts[(vertIndex*3)+0] = x;
verts[(vertIndex*3)+1] = y;
verts[(vertIndex*3)+2] = z;
cols[(vertIndex*4)+0] = color[0];
cols[(vertIndex*4)+1] = color[1];
cols[(vertIndex*4)+2] = color[2];
cols[(vertIndex*4)+3] = color[3];
texs[(vertIndex*2)+0] = tex[0];
texs[(vertIndex*2)+1] = tex[1];
vertIndex++;
if (vertIndex > MAX_VERTS - 50) {
if (isSplittable(vertIndex, currentType)) {
int type = currentType;
applyBuffer();
currentType = type;
}
}
}
/**
* Check if the geometry being created can be split at the current index
*
* @param count The current index
* @param type The type of geometry being built
* @return True if the geometry can be split at the current index
*/
private boolean isSplittable(int count, int type) {
switch (type) {
case GL11.GL_QUADS:
return count % 4 == 0;
case GL11.GL_TRIANGLES:
return count % 3 == 0;
case GL11.GL_LINE:
return count % 2 == 0;
}
return false;
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBindTexture(int, int)
*/
public void glBindTexture(int target, int id) {
applyBuffer();
super.glBindTexture(target, id);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBlendFunc(int, int)
*/
public void glBlendFunc(int src, int dest) {
applyBuffer();
super.glBlendFunc(src, dest);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glCallList(int)
*/
public void glCallList(int id) {
applyBuffer();
super.glCallList(id);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glClear(int)
*/
public void glClear(int value) {
applyBuffer();
super.glClear(value);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glClipPlane(int, java.nio.DoubleBuffer)
*/
public void glClipPlane(int plane, DoubleBuffer buffer) {
applyBuffer();
super.glClipPlane(plane, buffer);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glColorMask(boolean, boolean, boolean, boolean)
*/
public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
applyBuffer();
super.glColorMask(red, green, blue, alpha);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glDisable(int)
*/
public void glDisable(int item) {
applyBuffer();
super.glDisable(item);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEnable(int)
*/
public void glEnable(int item) {
applyBuffer();
super.glEnable(item);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glLineWidth(float)
*/
public void glLineWidth(float width) {
applyBuffer();
super.glLineWidth(width);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPointSize(float)
*/
public void glPointSize(float size) {
applyBuffer();
super.glPointSize(size);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPopMatrix()
*/
public void glPopMatrix() {
applyBuffer();
super.glPopMatrix();
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPushMatrix()
*/
public void glPushMatrix() {
applyBuffer();
super.glPushMatrix();
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glRotatef(float, float, float, float)
*/
public void glRotatef(float angle, float x, float y, float z) {
applyBuffer();
super.glRotatef(angle, x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glScalef(float, float, float)
*/
public void glScalef(float x, float y, float z) {
applyBuffer();
super.glScalef(x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glScissor(int, int, int, int)
*/
public void glScissor(int x, int y, int width, int height) {
applyBuffer();
super.glScissor(x, y, width, height);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTexEnvi(int, int, int)
*/
public void glTexEnvi(int target, int mode, int value) {
applyBuffer();
super.glTexEnvi(target, mode, value);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTranslatef(float, float, float)
*/
public void glTranslatef(float x, float y, float z) {
applyBuffer();
super.glTranslatef(x, y, z);
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEndList()
*/
public void glEndList() {
listMode--;
super.glEndList();
}
/**
* @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glNewList(int, int)
*/
public void glNewList(int id, int option) {
listMode++;
super.glNewList(id, option);
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#getCurrentColor()
*/
public float[] getCurrentColor() {
return color;
}
/**
* @see org.newdawn.slick.opengl.renderer.SGL#glLoadMatrix(java.nio.FloatBuffer)
*/
public void glLoadMatrix(FloatBuffer buffer) {
flushBuffer();
super.glLoadMatrix(buffer);
}
}