public class

FastFloatBuffer

extends Object
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
	// ===========================================================
}