package org.anddev.andengine.opengl.util;
import static org.anddev.andengine.opengl.util.GLHelper.BYTES_PER_FLOAT;
import java.lang.ref.SoftReference;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
/**
* Convenient work-around for poor {@link FloatBuffer#put(float[])} performance.
* This should become unnecessary in gingerbread,
* @see <a href="http://code.google.com/p/android/issues/detail?id=11078">Issue 11078</a>
*
* @author ryanm
*/
public class FastFloatBuffer {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
/**
* Use a {@link SoftReference} so that the array can be collected if
* necessary
*/
private static SoftReference<int[]> sWeakIntArray = new SoftReference<int[]>(new int[0]);
/**
* Underlying data - give this to OpenGL
*/
public final ByteBuffer mByteBuffer;
private final FloatBuffer mFloatBuffer;
private final IntBuffer mIntBuffer;
// ===========================================================
// Constructors
// ===========================================================
/**
* Constructs a new direct native-ordered buffer
*/
public FastFloatBuffer(final int pCapacity) {
this.mByteBuffer = ByteBuffer.allocateDirect((pCapacity * BYTES_PER_FLOAT)).order(ByteOrder.nativeOrder());
this.mFloatBuffer = this.mByteBuffer.asFloatBuffer();
this.mIntBuffer = this.mByteBuffer.asIntBuffer();
}
// ===========================================================
// Getter & Setter
// ===========================================================
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
/**
* See {@link FloatBuffer#flip()}
*/
public void flip() {
this.mByteBuffer.flip();
this.mFloatBuffer.flip();
this.mIntBuffer.flip();
}
/**
* See {@link FloatBuffer#put(float)}
*/
public void put(final float f) {
final ByteBuffer byteBuffer = this.mByteBuffer;
final IntBuffer intBuffer = this.mIntBuffer;
byteBuffer.position(byteBuffer.position() + BYTES_PER_FLOAT);
this.mFloatBuffer.put(f);
intBuffer.position(intBuffer.position() + 1);
}
/**
* It'MAGIC_CONSTANT like {@link FloatBuffer#put(float[])}, but about 10 times faster
*/
public void put(final float[] data) {
final int length = data.length;
int[] ia = sWeakIntArray.get();
if(ia == null || ia.length < length) {
ia = new int[length];
sWeakIntArray = new SoftReference<int[]>(ia);
}
for(int i = 0; i < length; i++) {
ia[i] = Float.floatToRawIntBits(data[i]);
}
final ByteBuffer byteBuffer = this.mByteBuffer;
byteBuffer.position(byteBuffer.position() + BYTES_PER_FLOAT * length);
final FloatBuffer floatBuffer = this.mFloatBuffer;
floatBuffer.position(floatBuffer.position() + length);
this.mIntBuffer.put(ia, 0, length);
}
/**
* For use with pre-converted data. This is 50x faster than
* {@link #put(float[])}, and 500x faster than
* {@link FloatBuffer#put(float[])}, so if you've got float[] data that
* won't change, {@link #convert(float...)} it to an int[] once and use this
* method to put it in the buffer
*
* @param data floats that have been converted with {@link Float#floatToIntBits(float)}
*/
public void put(final int[] data) {
final ByteBuffer byteBuffer = this.mByteBuffer;
byteBuffer.position(byteBuffer.position() + BYTES_PER_FLOAT * data.length);
final FloatBuffer floatBuffer = this.mFloatBuffer;
floatBuffer.position(floatBuffer.position() + data.length);
this.mIntBuffer.put(data, 0, data.length);
}
/**
* Converts float data to a format that can be quickly added to the buffer
* with {@link #put(int[])}
*
* @param data
* @return the int-formatted data
*/
public static int[] convert(final float ... data) {
final int length = data.length;
final int[] id = new int[length];
for(int i = 0; i < length; i++) {
id[i] = Float.floatToRawIntBits(data[i]);
}
return id;
}
/**
* See {@link FloatBuffer#put(FloatBuffer)}
*/
public void put(final FastFloatBuffer b) {
final ByteBuffer byteBuffer = this.mByteBuffer;
byteBuffer.put(b.mByteBuffer);
this.mFloatBuffer.position(byteBuffer.position() >> 2);
this.mIntBuffer.position(byteBuffer.position() >> 2);
}
/**
* @return See {@link FloatBuffer#capacity()}
*/
public int capacity() {
return this.mFloatBuffer.capacity();
}
/**
* @return See {@link FloatBuffer#position()}
*/
public int position() {
return this.mFloatBuffer.position();
}
/**
* See {@link FloatBuffer#position(int)}
*/
public void position(final int p) {
this.mByteBuffer.position(p * BYTES_PER_FLOAT);
this.mFloatBuffer.position(p);
this.mIntBuffer.position(p);
}
/**
* @return See {@link FloatBuffer#slice()}
*/
public FloatBuffer slice() {
return this.mFloatBuffer.slice();
}
/**
* @return See {@link FloatBuffer#remaining()}
*/
public int remaining() {
return this.mFloatBuffer.remaining();
}
/**
* @return See {@link FloatBuffer#limit()}
*/
public int limit() {
return this.mFloatBuffer.limit();
}
/**
* See {@link FloatBuffer#clear()}
*/
public void clear() {
this.mByteBuffer.clear();
this.mFloatBuffer.clear();
this.mIntBuffer.clear();
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}