/**
 * This package provides classes to facilitate the handling of opengl textures, glsl shaders and 
 * off-screen rendering in Processing.
 * @author Andres Colubri
 * @version 0.9.3.2
 *
 * Copyright (c) 2008 Andres Colubri
 *
 * This source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This code is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License is available on the World
 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also
 * obtain it by writing to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
package codeanticode.glgraphics;

import processing.core.*;
import processing.opengl.*;

import javax.media.opengl.*;

import com.sun.opengl.util.*;
import java.nio.*;

// TODO:
// add texture name property.
// Fix the setBuffer methods to make them into setTexChannel or something like that.
// Overload the loadPixels and updatePixels so that texture info is copies to data[] array instead to
// pixels[] when the texture is floating point.
// Properly implement putBuffer method to copy ALPHA, RGB and RGBA buffers to the texture. Now it has been
// quickly hacked to properly use alpha textures needed by GLFonts.

/**
 * This class adds an opengl texture to a PImage object. The texture is handled in a similar way to the
 * pixels property: image data can be copied to and from the texture using loadTexture and updateTexture methods.
 * However, bringing the texture down to image or pixels data can slow down the application considerably (since
 * involves copying texture data from GPU to CPU), especially when handling large textures. So it is recommended 
 * to do all the texture handling without calling updateTexture, and doing so only at the end if the texture
 * is needed as a regular image.
 */
public class GLTexture extends PImage implements PConstants, GLConstants 
{
    /**
     * Creates an instance of GLTexture with size 1x1. The texture is not initialized.
     * @param parent PApplet
     */
    public GLTexture(PApplet parent)
    {
        super(1, 1, ARGB);
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(new GLTextureParameters());
    }  

    /**
     * Creates an instance of GLTexture with size width x height. The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param width int 
     * @param height int 
     */	 
    public GLTexture(PApplet parent, int width, int height)
    {
        super(width, height, ARGB);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(new GLTextureParameters());
       
        initTexture(width, height);
    }
    
    /**
     * Creates an instance of GLTexture with size width x height and with the specified parameters.
     *  The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param width int 
     * @param height int 
     * @param params GLTextureParameters 			
     */	 
    public GLTexture(PApplet parent, int width, int height, GLTextureParameters params)
    {
        super(width, height, params.format);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(params);
       
        initTexture(width, height);
    }	
	
    /**
     * Creates an instance of GLTexture with size width x height and with the specified format.
     *  The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param width int 
     * @param height int 
     * @param format int  			
     */	 
    public GLTexture(PApplet parent, int width, int height, int format)
    {
        super(width, height, format);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(new GLTextureParameters(format));
       
        initTexture(width, height);
    }	

    /**
     * Creates an instance of GLTexture with size width x height and with the specified format and filtering.
     *  The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param width int 
     * @param height int 
     * @param format int 
     * @param filter int 
     */	 
    public GLTexture(PApplet parent, int width, int height, int format, int filter)
    {
        super(width, height, format);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(new GLTextureParameters(format, filter));
       
        initTexture(width, height);
    }	

    /**
     * Creates an instance of GLTexture using image file filename as source.
     * @param parent PApplet
     * @param filename String
     */	
    public GLTexture(PApplet parent, String filename)
    {
        super(1, 1, ARGB);  
        this.parent = parent;
	   
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);		
        
        loadTexture(filename);
    }

    public GLTexture(PApplet parent, int width, int height, GLTextureParameters params, int id)
    {
        super(width, height, params.format);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
	    setTextureParams(params);
       
        initTexture(width, height, id);
    }	
    
    /**
     * Creates an instance of GLTexture using image file filename as source and the specified texture parameters.
     * @param parent PApplet
     * @param filename String
     * @param params GLTextureParameters
     */	
    public GLTexture(PApplet parent, String filename, GLTextureParameters params)
    {
        super(1, 1, params.format);  
        this.parent = parent;
	   
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);		
        
        loadTexture(filename, params);
    }

    /**
     * Creates an instance of GLTexture using image file filename as source and the specified format.
     * @param parent PApplet
     * @param filename String
     * @param format int 
     */	
    public GLTexture(PApplet parent, String filename, int format)
    {
        super(1, 1, format);  
        this.parent = parent;
	   
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);		
        
        loadTexture(filename, format);
    }

    /**
     * Creates an instance of GLTexture using image file filename as source and the specified format and filtering.
     * @param parent PApplet
     * @param filename String
     * @param format int 
     * @param filter int 
     */	
    public GLTexture(PApplet parent, String filename, int format, int filter)
    {
        super(1, 1, format);  
        this.parent = parent;
	   
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);		
        
        loadTexture(filename, format, filter);
    }

    /**
     * Creates an instance of GLTexture with power-of-two width and height that such that width * height is the closest to size. 
     * The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param size int 
     */	
    public GLTexture(PApplet parent, int size)
    {
        super(1, 1, ARGB);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);
        
        calculateWidthHeight(size);
        init(width, height);
    }

    /**
     * Creates an instance of GLTexture with power-of-two width and height that such that width 
     * height is the closest to size, and with the specified parameters.
     * The texture is initialized (empty) to that size.
     * @param parent PApplet
     * @param size int 
     * @param params GLTextureParameters
     */	 
    public GLTexture(PApplet parent, int size, GLTextureParameters params)
    {
        super(1, 1, params.format);  
        this.parent = parent;
       
        pgl = (PGraphicsOpenGL)parent.g;
        gl = pgl.gl;
        glstate = new GLState(gl);

        calculateWidthHeight(size);
        init(width, height, params);
    }

    protected void finalize()  throws Throwable 
    {
        if (tex[0] != 0)
        {
            //releaseTexture();
        }
        super.finalize();    
    }    
    
    /**
     * Sets the size of the image and texture to width x height. If the texture is already initialized,
     * it first destroys the current opengl texture object and then creates a new one with the specified
     * size.
     * @param width int
     * @param height int
     */
    public void init(int width, int height)
    {
	    init(width, height, new GLTextureParameters());
    }

    /**
     * Sets the size of the image and texture to width x height, and the parameters of the texture to params.
     * If the texture is already  initialized, it first destroys the current opengl texture object and then creates 
     * a new one with the specified size.
     * @param width int
     * @param height int
     * @param params GLTextureParameters 
     */
    public void init(int width, int height, GLTextureParameters params)
    {
        super.init(width, height, params.format);
		    setTextureParams(params);
        initTexture(width, height);
    }	
	
    /**
     * Returns true if the texture has been initialized.
     * @return boolean
     */  
    public boolean available()
    {
        return 0 < tex[0];
    }
    
    /**
     * Provides the ID of the opegl texture object.
     * @return int
     */	
    public int getTextureID()
    {
        return tex[0];
    }

    /**
     * Returns the texture target.
     * @return int
     */	
    public int getTextureTarget()
    {
        return texTarget;
    }    

    /**
     * Returns the texture internal format.
     * @return int
     */	
    public int getTextureInternalFormat()
    {
        return texInternalFormat;
    }

    /**
     * Returns the texture minimization filter.
     * @return int
     */	
    public int getTextureMinFilter()
    {
        return minFilter;
    }

    /**
     * Returns the texture magnification filter.
     * @return int
     */	
    public int getTextureMagFilter()
    {
        return magFilter;
    }
	
    /**
     * Returns true or false whether or not the texture is using mipmaps.
     * @return boolean
     */	
    public boolean usingMipmaps()
    {
        return usingMipmaps;
    }
	
    /**
     * Returns the maximum possible value for the texture coordinate S.
     * @return float
     */	
    public float getMaxTextureCoordS()
    {
        return maxTexCoordS;
    }
	
    /**
     * Returns the maximum possible value for the texture coordinate T.
     * @return float
     */	
    public float getMaxTextureCoordT()
    {
        return maxTexCoordT;
    }
	
    /**
     * Returns true if the texture is flipped along the horizontal direction.
     * @return boolean;
     */	
    public boolean isFlippedX()
    {
        return flippedX;
    }

    /**
     * Sets the texture as flipped or not flipped on the horizontal direction.
     * @param v boolean;
     */	
    public void setFlippedX(boolean v)
    {
        flippedX = v;
    }	
	
    /**
     * Returns true if the texture is flipped along the vertical direction.
     * @return boolean;
     */	
    public boolean isFlippedY()
    {
        return flippedY;
    }

    /**
     * Sets the texture as flipped or not flipped on the vertical direction.
     * @param v boolean;
     */	
    public void setFlippedY(boolean v)
    {
        flippedY = v;
    }
	
    /**
     * Puts img into texture, pixels and image.
     * @param img PImage
     */
    public void putImage(PImage img)
    {
        putImage(img, new GLTextureParameters());
    }

    /**
     * Puts img into texture, pixels and image.
     * @param img PImage
     * @param format int
     */
    public void putImage(PImage img, int format)
    {
        putImage(img, new GLTextureParameters(format));
    }

    /**
     * Puts img into texture, pixels and image.
     * @param img PImage
     * @param format int
     * @param filter int
     */
    public void putImage(PImage img, int format, int filter)
    {
        putImage(img, new GLTextureParameters(format, filter));
    }

    /**
     * Puts img into texture, pixels and image.
     * @param img PImage
     * @param params GLTextureParameters
     */
    public void putImage(PImage img, GLTextureParameters params)
    {
        img.loadPixels();
        
        if ((img.width != width) || (img.height != height))
        {
            init(img.width, img.height, params);
        }

        // Putting img into pixels...
        PApplet.arrayCopy(img.pixels, pixels);
        
        // ...into texture...
        loadTexture();
        
        // ...and into image.
        updatePixels();
    }

    /**
     * Puts pixels of img into texture only.
     * @param img PImage
     */
    public void putPixelsIntoTexture(PImage img)
    {	
        if ((img.width != width) || (img.height != height))
        {
            init(img.width, img.height, new GLTextureParameters());
        }
   
        // Putting into texture.
        if (texInternalFormat == GL.GL_RGB) 
        {
            putBuffer(img.pixels, RGB);
        } 
        if (texInternalFormat == GL.GL_RGBA) 
        {
            putBuffer(img.pixels, ARGB);
        } 
        if (texInternalFormat == GL.GL_ALPHA) 
        {
            putBuffer(img.pixels, ALPHA);
        }            
    }
	
    /**
     * Puts the pixels of img inside the rectangle (x, y, x+w, y+h) into texture only.
     * @param img PImage
     * @param x int
     * @param y int
     * @param w int
     * @param h int
     */	
    public void putPixelsIntoTexture(PImage img, int x, int y, int w, int h)
    {	
        x = PApplet.constrain(x, 0, img.width);
        y = PApplet.constrain(y, 0, img.height);
		
        w = PApplet.constrain(w, 0, img.width - x);
        h = PApplet.constrain(h, 0, img.height - y);
		
        if ((w != width) || (h != height))
        {
            init(w, h, new GLTextureParameters());
        }
		
        int p0;
        int dest[] = new int[w * h];
        for (int j = 0; j < h; j++)
	    {
	        p0 = y * img.width + x + (img.width - w) * j;
	        PApplet.arrayCopy(img.pixels, p0 + w * j, dest, w * j, w);
	    }
   
        // Putting into texture.
        if (texInternalFormat == GL.GL_RGB) 
        {
            putBuffer(dest, RGB);
        } 
        if (texInternalFormat == GL.GL_RGBA) 
        {
            putBuffer(dest, ARGB);
        } 
        if (texInternalFormat == GL.GL_ALPHA) 
        {
            putBuffer(dest, ALPHA);
        }	    
    }
	
    /**
     * Copies texture to img.
     * @param img PImage
     */	
    public void getImage(PImage img)
    {
        int w = width;
        int h = height;
        
        if ((img.width != w) || (img.height != h))
        {
            img.init(w, h, ARGB);
        }
     
        int size = w * h;
        IntBuffer buffer = BufferUtil.newIntBuffer(size);
        gl.glBindTexture(texTarget, tex[0]);
        gl.glGetTexImage(texTarget, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, buffer);
        gl.glBindTexture(texTarget, 0);       
       
        buffer.get(img.pixels);
        
        int[] pixelsARGB = convertToARGB(img.pixels);
        PApplet.arrayCopy(pixelsARGB, img.pixels);        
        if (flippedX) flipArrayOnX(img.pixels, 1);
        if (flippedY) flipArrayOnY(img.pixels, 1);
        img.updatePixels();       
    }

    /**
     * Load texture, pixels and image from file.
     * @param filename String
     */
    public void loadTexture(String filename)
    {
        PImage img = parent.loadImage(filename);
        putImage(img);
    }

    /**
     * Load texture, pixels and image from file using the specified texture parameters.
     * @param filename String
     * @param params GLTextureParameters 
     */
    public void loadTexture(String filename, GLTextureParameters params)
    {
        PImage img = parent.loadImage(filename);
        putImage(img, params);
    }

    /**
     * Load texture, pixels and image from file using the specified texture format.
     * @param filename String
     * @param format int 
     */
    public void loadTexture(String filename, int format)
    {
        PImage img = parent.loadImage(filename);
        putImage(img, format);
    }

    /**
     * Load texture, pixels and image from file using the specified texture format and filtering.
     * @param filename String
     * @param format int 
     * @param  filter int
     */
    public void loadTexture(String filename, int format, int filter)
    {
        PImage img = parent.loadImage(filename);
        putImage(img, format, filter);
    }

    /**
     * Copy pixels to texture (loadPixels should have been called beforehand).
     */	
    public void loadTexture()
    {
        // Putting into texture.
        if (texInternalFormat == GL.GL_RGB) 
        {
            putBuffer(pixels, RGB);
        } 
        if (texInternalFormat == GL.GL_RGBA) 
        {
            putBuffer(pixels, ARGB);
        } 
        if (texInternalFormat == GL.GL_ALPHA) 
        {
            putBuffer(pixels, ALPHA);
        }     
    }
    
    /**
     * Copy src texture into this.
     * @param src GLTexture 
     */	
    public void copy(GLTexture src)
    {
        glstate.copyTex(src, this);	
    }

    /**
     * Copy pixels to texture using PBO (EXPERIMENTAL).
     */	
	public void loadTextureFast()
	{
		if (pbo[0] == 0)
		{
			System.err.println("Fast texture load is not enabled!");
			return; 
		}
		
		gl.glBindTexture(texTarget, tex[0]);
		gl.glBindBuffer(pboTarget, pbo[0]);
		
		//gl.glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, DATA_SIZE, 0, GL_STREAM_DRAW_ARB);
		//gl.glBufferDataARB(pboTarget, 4 * width * height, null, pboUsage);
		
		IntBuffer texels = gl.glMapBuffer(pboTarget, GL.GL_WRITE_ONLY).asIntBuffer();
		
		System.out.println(texels);
		texels.put(pixels);

		gl.glUnmapBufferARB(pboTarget);
	    gl.glBindBuffer(pboTarget, 0);		
	}
	
    /**
     * Creates a Pixel Buffer Object (PBO) to allow for faster transfers of pixel data in
     * GPU memory (EXPERIMENTAL). 
     * For a detailed tutorial on PBOs look at:
     * http://www.songho.ca/opengl/gl_pbo.html
     */    
    public void enableFastTextureLoad()
    {
        if (pbo[0] != 0)
        {
            releasePBO();
        }
    	
    	gl.glGenBuffersARB(1, pbo, 0);
    	
    	// GL.GL_PIXEL_UNPACK_BUFFER_ARB indicates that the PBO will be used to copy data
    	// to the framebuffer (unpack operations).
    	pboTarget = GL.GL_PIXEL_UNPACK_BUFFER_ARB;
    	
    	// This usage setting means that the data will be changed frequently ("dynamic") and
    	// it will be sent to GPU in order to draw (application to GL).
    	pboUsage = GL.GL_DYNAMIC_DRAW_ARB;
    	pboUsage = GL.GL_STREAM_DRAW_ARB;
    	
    	gl.glBindBufferARB(pboTarget, pbo[0]);
    	
    	// Reserve empty space for the PBO.
    	gl.glBufferDataARB(pboTarget, 4 * width * height, null, pboUsage);
    	
    	gl.glBindBufferARB(pboTarget, 0);    	
    }    
    
    /**     
     * Copy texture to pixels (doesn't call updatePixels).
     */		
    public void updateTexture()
    {
        int size = width * height;
        IntBuffer buffer = BufferUtil.newIntBuffer(size);
		
        gl.glBindTexture(texTarget, tex[0]);
        gl.glGetTexImage(texTarget, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, buffer);
        gl.glBindTexture(texTarget, 0);
		
        buffer.get(pixels);
        int[] pixelsARGB = convertToARGB(pixels);
        PApplet.arrayCopy(pixelsARGB, pixels);        
        if (flippedX) flipArrayOnX(pixels, 1);
        if (flippedY) flipArrayOnY(pixels, 1);
    }

    /**
     * Applies filter texFilter using this texture as source and destTex as destination.
     * @param texFilter GLTextureFilter
     * @param destTex GLTexture		
     */
    public void filter(GLTextureFilter texFilter, GLTexture destTex)
    {
        texFilter.apply(new GLTexture[] { this }, new GLTexture[] { destTex });
    }

    /**
     * Applies filter texFilter using this texture as source and destTex as destination.
     * Sets all the value for all the parameters, by means of a parameter list of variable length.
     * values is an array of float[].
     * @param texFilter GLTextureFilter
     * @param destTex GLTexture	
     * @param float[] values  
     */
    public void filter(GLTextureFilter texFilter, GLTexture destTex, float[]...values)
    {
        texFilter.setParameterValues(values);
        texFilter.apply(new GLTexture[] { this }, new GLTexture[] { destTex });
    }

    /**
     * Applies filter texFilter using this texture as source, destTex as destination and destA as the
     * destination alpha value for the filter.
     * @param texFilter GLTextureFilter
     * @param destTex GLTexture				
     * @param destA float			
     */	
    public void filter(GLTextureFilter texFilter, GLTexture destTex, float destA)
    {
        texFilter.apply(new GLTexture[] { this }, new GLTexture[] { destTex }, null, 1.0f, 1.0f, 1.0f, destA);
    }

    /**
     * Applies filter texFilter using this texture as source, destTex as destination and destA as the
     * destination alpha value for the filter. Sets all the value for all the parameters, by means of a 
     * parameter list of variable length. values is an array of float[].
     * @param texFilter GLTextureFilter
     * @param destTex GLTexture				
     * @param destA float
     * @param float[] values  
     */	
    public void filter(GLTextureFilter texFilter, GLTexture destTex, float destA, float[]...values)
    {
        texFilter.setParameterValues(values);    	
        texFilter.apply(new GLTexture[] { this }, new GLTexture[] { destTex }, null, 1.0f, 1.0f, 1.0f, destA);
    }    
    
    /**
     * Applies filter texFilter using this texture as source, destTex as multiple destinations  and destA as the
     * destination alpha value constant for the filter.
     * @param texFilter GLTextureFilter
     * @param destTexArray GLTexture[]	
     * @param destA float			
     */	
    public void filter(GLTextureFilter texFilter, GLTexture[] destTexArray, float destA)
    {
        texFilter.apply(new GLTexture[] { this }, destTexArray, null, 1.0f, 1.0f, 1.0f, destA);
    }	
	
    /**
     * Applies filter texFilter using this texture as source, destTex as multiple destinations and destA as the
     * destination alpha value for the filter. Sets all the value for all the parameters, by means of a 
     * parameter list of variable length. values is an array of float[].
     * @param texFilter GLTextureFilter
     * @param destTexArray GLTexture[]	
     * @param destA float
     * @param float[] values
     */	
    public void filter(GLTextureFilter texFilter, GLTexture[] destTexArray, float destA, float[]...values)
    {
        texFilter.setParameterValues(values);    	
        texFilter.apply(new GLTexture[] { this }, destTexArray, null, 1.0f, 1.0f, 1.0f, destA);
    }    
    
    /**
     * Draws the texture using the opengl commands, inside a rectangle located at the origin with the original
     * size of the texture.
     */		
    public void render()
    {
        render(0, 0, width, height);
    }

    /**
     * Draws the texture using the opengl commands, inside a rectangle located at (x,y) with the original
     * size of the texture.
     * @param x float
     * @param y float
     */	
    public void render(float x, float y)
    {
        render(x, y, width, height);
    }
    
    /**
     * Draws the texture using the opengl commands, inside a rectangle of width w and height h
     * located at (x,y).
     * @param x float
     * @param y float
     * @param w float
     * @param h float	 
     */	
    public void render(float x, float y, float w, float h)
    {
    	render(pgl, x, y, w, h);    		
    }
    
   /**
    * Draws the texture using the opengl commands, inside a rectangle of width w and height h
    * located at (x,y) and using the specified renderer.
    * @param renderer PGraphicsOpenGL
    * @param x float
    * @param y float
    * @param w float
    * @param h float	 
    */    
    public void render(PGraphicsOpenGL renderer, float x, float y, float w, float h)
    {
    	render(renderer, x, y, w, h, 0, 0, 1, 1);
    }
    
    public void render(PGraphicsOpenGL renderer, float x, float y, float w, float h, float sx, float ty, float sw, float th)
    {
    	float fw, fh;
    	if (renderer.textureMode == IMAGE)
    	{
    		fw = width;
    	    fh = height;
    	}
    	else
    	{
    		fw = 1.0f;
    	    fh = 1.0f;
    	}
    		
    	renderer.beginShape(QUADS);
    	    renderer.texture(this);
    	    renderer.vertex(x, y, fw * sx, fh * ty);
    	    renderer.vertex(x + w, y, fw * sw, fh * ty);
    	    renderer.vertex(x + w, y + h, fw * sw, fh * th);
    	    renderer.vertex(x, y + h, fw * sx, fh * th);
    	renderer.endShape();	
    }        
    
    /**
     * Copies intArray into the texture, assuming that the array contains 4 color components and pixels are unsigned bytes.
     * @param intArray int[]
     */	
    public void putBuffer(int[] intArray)
    {
        putBuffer(intArray, ARGB, TEX_BYTE);
    }

    /**
     * Copies intArray into the texture, using the specified format and assuming that the pixels are unsigned bytes.
     * @param intArray int[]
     * @param format int
     */	
    public void putBuffer(int[] intArray, int format)
    {
        putBuffer(intArray, format, TEX_BYTE);
    }

    /**
     * Copies intArray into the texture, using the specified format and assuming that the pixels are unsigned bytes.
     * @param intArray int[]
     * @param format int
     */
    public void putByteBuffer(int[] intArray, int format)
    {
        putBuffer(intArray, format, TEX_BYTE);
    }

    /**
     * Copies intArray into the texture, using the specified format and assuming that the pixels are integers.
     * @param intArray int[]
     * @param format int
     */
    public void putIntBuffer(int[] intArray, int format)
    {
        putBuffer(intArray, format, TEX_INT);
    }

    /**
     * Copies intArray into the texture, using the format and type specified.
     * @param intArray int[]
     * @param format int
     * @param type int
     */	
    public void putBuffer(int[] intArray, int format, int type)
    {
        if (tex[0] == 0)
        {
            initTexture(width, height);
        }      
  
        int[] convArray = intArray;
        int glFormat;
        if (format == ALPHA)
        {
          glFormat = GL.GL_ALPHA;
          if (type == TEX_BYTE)
          {
              // Not sure why this works, because the integer values in intArray are converted to signed bytes, but the buffer is specified
              // as GL_UNSIGNED_BYTE.
              byte[] convArray2 = convertToAlpha(intArray);
              gl.glBindTexture(texTarget, tex[0]);
              gl.glTexSubImage2D(texTarget, 0, 0, 0, width, height, glFormat, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(convArray2));
              gl.glBindTexture(texTarget, 0);
              return;              
          }
        }
        else if (format == RGB)
        {
          // If in the previous case, we need to use a byte array, here with RGB we should use a byte array with 3 bytes per color, 
          // i.e.: byte[] convArray3 = byte[3 * widht * height] and then storing RGB components from intArray as follows:
          // for (int i = 0; i < width * height; i++) { 
          // convArray3[3 * i ] = red(intArray[i]);
          // convArray3[3 * i + 1] = green(intArray[i]);
          // convArray3[3 * i + 2] = blue(intArray[i]);
          //}
          // where the red, green, green and blue operators involve the correct bit shifting to extract the component from the int value.
        	glFormat = GL.GL_RGBA;
        	if (type == TEX_BYTE) convArray = convertToRGBA(intArray, RGB);
        }
        else
        {
        	glFormat = GL.GL_RGBA;
        	if (type == TEX_BYTE) convArray = convertToRGBA(intArray, ARGB);
        }
        
        int glType;
        if (type == TEX_INT) glType = GL.GL_INT;
        else glType = GL.GL_UNSIGNED_BYTE;
        
        gl.glBindTexture(texTarget, tex[0]);
                
        if (texTarget == GL.GL_TEXTURE_1D)
        {
            gl.glTexSubImage1D(texTarget, 0, 0, width, glFormat, glType, IntBuffer.wrap(convArray));
        }
        else
        {
            if (usingMipmaps)
                GLState.glu.gluBuild2DMipmaps(texTarget, texInternalFormat, width, height, glFormat, glType, IntBuffer.wrap(convArray));
            else
              gl.glTexSubImage2D(texTarget, 0, 0, 0, width, height, glFormat, glType, IntBuffer.wrap(convArray));
        }

        gl.glBindTexture(texTarget, 0);
    }
    
    /**
     * Copies floatArray into the texture, assuming that the array has 4 components.
     * @param floatArray float[]
     * @param format int
     */
    public void putBuffer(float[] floatArray)
    {
        putBuffer(floatArray, 4);
    }

    /**
     * Copies floatArray into the texture, using the specified number of channels.
     * @param floatArray float[]
     * @param nchan int
     */
    public void putBuffer(float[] floatArray, int nchan)
    {
        if (tex[0] == 0)
        {
            initTexture(width, height);
        }

        int glFormat;
        if (nchan == 1) glFormat = GL.GL_LUMINANCE;
        else if (nchan == 3) glFormat = GL.GL_RGB;
        else glFormat = GL.GL_RGBA;

        gl.glBindTexture(texTarget, tex[0]);

        if (texTarget == GL.GL_TEXTURE_1D)
            gl.glTexSubImage1D(texTarget, 0, 0, width, glFormat, GL.GL_FLOAT, FloatBuffer.wrap(floatArray));
        else
            gl.glTexSubImage2D(texTarget, 0, 0, 0, width, height, glFormat, GL.GL_FLOAT, FloatBuffer.wrap(floatArray));

        gl.glBindTexture(texTarget, 0);
    }

    /**
     * Copies the texture into intArray, assuming that the array has 4 components and the pixels are unsigned bytes.
     * @param intArray int[]
     */
    public void getBuffer(int[] intArray)
    {
        getBuffer(intArray, ARGB, TEX_BYTE);
    }

    /**
     * Copies the texture into intArray, using the specified format and assuming that the pixels are unsigned bytes.
     * @param intArray int[]
     * @param format nchan
     */
    public void getBuffer(int[] intArray, int format)
    {
        getBuffer(intArray, format, TEX_BYTE);
    }

    /**
     * Copies the texture into intArray, using the specified format and assuming that the pixels are unsigned bytes.
     * @param intArray int[]
     * @param format int
     */
    public void getByteBuffer(int[] intArray, int format)
    {
        getBuffer(intArray, format, TEX_BYTE);
    }

    /**
     * Copies the texture into intArray, using the specified format and assuming that the pixels are integers.
     * @param intArray int[]
     * @param format int
     */
    public void getIntBuffer(int[] intArray, int format)
    {
        getBuffer(intArray, format, TEX_INT);
    }

    /**
     * Copies the texture into intArray, using the specified format and type. The resulting array
     * is not reordered into ARGB, it follows OpenGL color component ordering (for instance, for
     * 4 components, little-endian this would be ABGR). Perhaps ordering should be done automatically?
     * @param intArray int[]
     * @param format int
     * @param type int
     */
    public void getBuffer(int[] intArray, int format, int type)
    {
        int mult;
        int glFormat;
        if (format == ALPHA) 
        { 
            mult = 1;
            glFormat = GL.GL_LUMINANCE;
        }
        else if (format == RGB) 
        { 
            mult = 3;
            glFormat = GL.GL_RGB;
        }
        else
        { 
            mult = 4;
            glFormat = GL.GL_RGBA;
        }

        int size;
        int glType;
        if (type == TEX_INT)
        {
            glType = GL.GL_INT;
        }
        else
        {
            mult = 1;
            glType = GL.GL_UNSIGNED_BYTE;
        }
        size = mult * width * height;

        if (intArray.length != size) 
        {
            System.err.println("Wrong size of buffer!");
            return;
        }

        IntBuffer buffer = BufferUtil.newIntBuffer(size);
		
        gl.glBindTexture(texTarget, tex[0]);
        gl.glGetTexImage(texTarget, 0, glFormat, glType, buffer);
        gl.glBindTexture(texTarget, 0);
		
        buffer.get(intArray);
        
        if (flippedX) flipArrayOnX(intArray, mult);
        if (flippedY) flipArrayOnY(intArray, mult);
    }

    /**
     * Copies the texture into floatArray.
     * @param floatArray float[]
     * @param format nchan
     */
    public void getBuffer(float[] floatArray, int nchan)
    {
        int mult;
        int glFormat;
        if (nchan == 1) 
        { 
            mult = 1;
            glFormat = GL.GL_LUMINANCE;
        }
        else if (nchan == 3) 
        { 
            mult = 3;
            glFormat = GL.GL_RGB;
        }
        else
        { 
            mult = 4;
            glFormat = GL.GL_RGBA;
        }

        int size = mult * width * height;
        if (floatArray.length != size) 
        {
            System.err.println("Wrong size of buffer!");
            return;
        }

        FloatBuffer buffer = BufferUtil.newFloatBuffer(size);
		
        gl.glBindTexture(texTarget, tex[0]);
        gl.glGetTexImage(texTarget, 0, glFormat, GL.GL_FLOAT, buffer);
        gl.glBindTexture(texTarget, 0);
		
        buffer.get(floatArray);

        if (flippedX) flipArrayOnX(floatArray, mult);
        if (flippedY) flipArrayOnY(floatArray, mult);
    }

    /**
     * Sets the texture to have random values in the ranges specified for each component.
     * @param r0 float
     * @param r1 float
     * @param g0 float
     * @param g1 float
     * @param b0 float
     * @param b1 float
     * @param a0 float
     * @param a1 float
     */
    public void setRandom(float r0, float r1, float g0, float g1, float b0, float b1, float a0, float a1)
    {
        float randBuffer[] = new float[4 * width * height];
        for (int j = 0; j < height; j++)
            for (int i = 0; i < width; i++)
            {
                randBuffer[i * 4 + j * width * 4] = parent.random(r0, r1);
                randBuffer[i * 4 + j * width * 4 + 1] = parent.random(g0, g1);
                randBuffer[i * 4 + j * width * 4 + 2] = parent.random(b0, b1);
                randBuffer[i * 4 + j * width * 4 + 3] = parent.random(a0, a1);
            }
        putBuffer(randBuffer);
    }

    /**
     * Sets the texture to have random values in the first two coordinates chosen on the circular region defined by the parameters.
     * @param r0 float
     * @param r1 float
     * @param phi0 float
     * @param phi1 float
     */
    public void setRandomDir2D(float r0, float r1, float phi0, float phi1)
    {
        float r, phi;
        float randBuffer[] = new float[4 * width * height];
        for (int j = 0; j < height; j++)
            for (int i = 0; i < width; i++)
            {
                r = parent.random(r0, r1);
                phi = parent.random(phi0, phi1);
                randBuffer[i * 4 + j * width * 4] = r * PApplet.cos(phi);
                randBuffer[i * 4 + j * width * 4 + 1] = r * PApplet.sin(phi);
                randBuffer[i * 4 + j * width * 4 + 2] = 0.0f;
                randBuffer[i * 4 + j * width * 4 + 3] = 0.0f;
            }
        putBuffer(randBuffer);
    }

    /**
     * Sets the texture to have random values in the first three coordinates chosen on the spherical region defined by the parameters.
     * @param r0 float
     * @param r1 float
     * @param phi0 float
     * @param phi1 float
     * @param theta0 float
     * @param theta1 float
     */
    public void setRandomDir3D(float r0, float r1, float phi0, float phi1, float theta0, float theta1)
    {
        float r, phi, theta;
        float randBuffer[] = new float[4 * width * height];
        for (int j = 0; j < height; j++)
            for (int i = 0; i < width; i++)
            {
                r = parent.random(r0, r1);
                phi = parent.random(phi0, phi1);
                theta = parent.random(theta0, theta1);

                randBuffer[i * 4 + j * width * 4] = r * PApplet.cos(phi) * PApplet.sin(theta);
                randBuffer[i * 4 + j * width * 4 + 1] = r * PApplet.sin(phi) * PApplet.sin(theta);
                randBuffer[i * 4 + j * width * 4 + 2] = r * PApplet.cos(theta);
                randBuffer[i * 4 + j * width * 4 + 3] = 0.0f;
            }
        putBuffer(randBuffer);
    }

    /**
     * Sets the texture to have the same given float value in each component.
     * @param r float
     * @param g float
     * @param b float
     * @param a float
     */
    public void setValue(float r, float g, float b, float a)
    {
        float valBuffer[] = new float[4 * width * height];
        for (int j = 0; j < height; j++)
            for (int i = 0; i < width; i++)
            {
                valBuffer[i * 4 + j * width * 4] = r;
                valBuffer[i * 4 + j * width * 4 + 1] = g;
                valBuffer[i * 4 + j * width * 4 + 2] = b;
                valBuffer[i * 4 + j * width * 4 + 3] = a;
            }
        putBuffer(valBuffer);
    }

    /**
     * Sets to zero all the pixels of the texture.
     */
    public void setZero()
    {
        setValue(0.0f, 0.0f, 0.0f, 0.0f);
    }

    /**
     * Fills the texture with the specified gray tone.
     * @param gray int
     */
    public void clear(int gray)
    {
        int c = parent.color(gray);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified gray tone.
     * @param gray float
     */
    public void clear(float gray) 
    {
        int c = parent.color(gray);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified gray tone and alpha value.
     * @param gray int
     * @param alpha int
     */
    public void clear(int gray, int alpha) 
    {
        int c = parent.color(gray, alpha);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified rgb color and alpha value.
     * @param rgb int
     * @param alpha float
     */
    public void clear(int rgb, float alpha) 
    {
        int c = parent.color(rgb, alpha);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified gray tone and alpha value.
     * @param gray float
     * @param alpha float
     */
    public void clear(float gray, float alpha)
    {
        int c = parent.color(gray, alpha);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified color components.
     * @param x int
     * @param y int
     * @param z int
     */
    public void clear(int x, int y, int z) 
    {
        int c = parent.color(x, y, z);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified color components.
     * @param x float
     * @param y float
     * @param z float
     */
    public void clear(float x, float y, float z) 
    {
        int c = parent.color(x, y, z);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified color components and alpha component.
     * @param x int
     * @param y int
     * @param z int
     * @param a int
     */
    public void clear(int x, int y, int z, int a) 
    {
        int c = parent.color(x, y, z, a);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Fills the texture with the specified color components and alpha component.
     * @param x float
     * @param y float
     * @param z float
     * @param a float
     */
    public void clear(float x, float y, float z, float a)
    {
        int c = parent.color(x, y, z, a);
        glstate.clearTex(tex[0], texTarget, c);
    }

    /**
     * Paints the texture with the specified gray tone.
     * @param gray int
     */
    public void paint(int gray)
    {
        int c = parent.color(gray);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified gray tone.
     * @param gray float
     */
    public void paint(float gray) 
    {
        int c = parent.color(gray);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified gray tone and alpha value.
     * @param gray int
     * @param alpha int
     */
    public void paint(int gray, int alpha) 
    {
        int c = parent.color(gray, alpha);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified rgb color and alpha value.
     * @param rgb int
     * @param alpha float
     */
    public void paint(int rgb, float alpha) 
    {
        int c = parent.color(rgb, alpha);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified gray tone and alpha value.
     * @param gray float
     * @param alpha float
     */
    public void paint(float gray, float alpha)
    {
        int c = parent.color(gray, alpha);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified color components.
     * @param x int
     * @param y int
     * @param z int
     */
    public void paint(int x, int y, int z) 
    {
        int c = parent.color(x, y, z);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified color components.
     * @param x float
     * @param y float
     * @param z float
     */
    public void paint(float x, float y, float z) 
    {
        int c = parent.color(x, y, z);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified color components and alpha component.
     * @param x int
     * @param y int
     * @param z int
     * @param a int
     */
    public void paint(int x, int y, int z, int a) 
    {
        int c = parent.color(x, y, z, a);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }

    /**
     * Paints the texture with the specified color components and alpha component.
     * @param x float
     * @param y float
     * @param z float
     * @param a float
     */
    public void paint(float x, float y, float z, float a)
    {
        int c = parent.color(x, y, z, a);
        glstate.paintTex(tex[0], texTarget, width, height, c);
    }
    
    /**
     * Flips intArray along the X axis.
     * @param intArray int[]
     * @param mult int
     */
    protected void flipArrayOnX(int[] intArray, int mult)
    {
        int index = 0;
        int xindex = mult * (width - 1);
        for (int x = 0; x < width / 2; x++) 
        {
             for (int y = 0; y < height; y++)
             {
                 int i = index + mult * y * width;
                 int j = xindex + mult * y * width;

                 for (int c = 0; c < mult; c++) 
                 {
                     int temp = intArray[i];
                     intArray[i] = intArray[j];
                     intArray[j] = temp;
                 
                     i++;
                     j++;
                 }

            }
            index += mult;
            xindex -= mult;
        }
    }

    /**
     * Flips intArray along the Y axis.
     * @param intArray int[]
     * @param mult int
     */
    protected void flipArrayOnY(int[] intArray, int mult)
    {
        int index = 0;
        int yindex = mult * (height - 1) * width;
        for (int y = 0; y < height / 2; y++) 
        {
             for (int x = 0; x < mult * width; x++) 
             {
                  int temp = intArray[index];
                  intArray[index] = intArray[yindex];
                  intArray[yindex] = temp;

                  index++;
                  yindex++;
            }
            yindex -= mult * width * 2;
        }
	}

    /**
     * Flips floatArray along the X axis.
     * @param intArray int[]
     * @param mult int
     */
    protected void flipArrayOnX(float[] floatArray, int mult)
    {
        int index = 0;
        int xindex = mult * (width - 1);
        for (int x = 0; x < width / 2; x++) 
        {
             for (int y = 0; y < height; y++)
             {
                 int i = index + mult * y * width;
                 int j = xindex + mult * y * width;

                 for (int c = 0; c < mult; c++) 
                 {
                     float temp = floatArray[i];
                     floatArray[i] = floatArray[j];
                     floatArray[j] = temp;
                 
                     i++;
                     j++;
                 }

            }
            index += mult;
            xindex -= mult;
        }
    }

    /**
     * Flips floatArray along the Y axis.
     * @param intArray int[]
     * @param mult int
     */
    protected void flipArrayOnY(float[] floatArray, int mult)
    {
        int index = 0;
        int yindex = mult * (height - 1) * width;
        for (int y = 0; y < height / 2; y++) 
        {
             for (int x = 0; x < mult * width; x++) 
             {
                  float temp = floatArray[index];
                  floatArray[index] = floatArray[yindex];
                  floatArray[yindex] = temp;

                  index++;
                  yindex++;
            }
            yindex -= mult * width * 2;
        }
	}

    /**
     * @invisible
     * Reorders a pixel array in ARGB format into the order required by OpenGL (RGBA).
     * @param intArray int[]
     * @param arrayFormat int	 
     */
    protected int[] convertToRGBA(int[] intArray, int arrayFormat)
    {
        int twidth = width;
        int t = 0; 
        int p = 0;
        int[] tIntArray = new int[width * height];        
        if (GLGraphics.BIG_ENDIAN) 
        {
            switch (arrayFormat) 
            {
                case ALPHA:
                	
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                        {
                            tIntArray[t++] = 0xFFFFFF00 | intArray[p++];
                        }
                        t += twidth - width;
                    }
                    break;

                case RGB:
                	
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                        {
                            int pixel = intArray[p++];
                            tIntArray[t++] = (pixel << 8) | 0xff;
                        }
                        t += twidth - width;
                    }
                    break;

                case ARGB:
                	
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                        {
                            int pixel = intArray[p++];
                            tIntArray[t++] = (pixel << 8) | ((pixel >> 24) & 0xff);
                        }
                        t += twidth - width;
                    }
                    break;
                    
            }       
        } 
        else 
        {  
            // LITTLE_ENDIAN
            // ARGB native, and RGBA opengl means ABGR on windows
            // for the most part just need to swap two components here
            // the sun.cpu.endian here might be "false", oddly enough..
            // (that's why just using an "else", rather than check for "little")
        
            switch (arrayFormat) 
            {    
                case ALPHA:
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                        {
                           tIntArray[t++] = (intArray[p++] << 24) | 0x00FFFFFF;
                        }
                        t += twidth - width;
                    }
                    break;

                case RGB:
                	
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                        {
                            int pixel = intArray[p++];
                            // needs to be ABGR, stored in memory xRGB
                            // so R and B must be swapped, and the x just made FF
                            tIntArray[t++] =
                                0xff000000 |  // force opacity for good measure
                                ((pixel & 0xFF) << 16) |
                                ((pixel & 0xFF0000) >> 16) |
                                (pixel & 0x0000FF00);
                        }
                        t += twidth - width;
                    }
                    break;

                case ARGB:
                    	
                    for (int y = 0; y < height; y++) 
                    {
                        for (int x = 0; x < width; x++) 
                         {
                             int pixel = intArray[p++];
                             // needs to be ABGR stored in memory ARGB
                             // so R and B must be swapped, A and G just brought back in
                             tIntArray[t++] =
                                 ((pixel & 0xFF) << 16) |
                                 ((pixel & 0xFF0000) >> 16) |
                                 (pixel & 0xFF00FF00);
                         }
                         t += twidth - width;
                    }
                    break;
          
            }
        
        }
        
        return tIntArray;    
    }
    
    /**
     * @invisible
     * Reorders a pixel array in RGBA format into ARGB.
     * @param intArray int[]	 
     */    
    protected int[] convertToARGB(int[] intArray)
    {
        int twidth = width;
        int t = 0; 
        int p = 0;
        int[] tIntArray = new int[width * height];        
        if (GLGraphics.BIG_ENDIAN) 
        {
            for (int y = 0; y < height; y++) 
            {
                for (int x = 0; x < width; x++) 
                {
                    int pixel = intArray[p++];
                    tIntArray[t++] = (pixel >> 8) | ((pixel << 24) & 0xff);
                }
                t += twidth - width;
            }
        } 
        else 
        {  
            // LITTLE_ENDIAN
            // ARGB native, and RGBA opengl means ABGR on windows
            // for the most part just need to swap two components here
            // the sun.cpu.endian here might be "false", oddly enough..
            // (that's why just using an "else", rather than check for "little")
        
            for (int y = 0; y < height; y++) 
            {
                for (int x = 0; x < width; x++) 
                 {
                     int pixel = intArray[p++];
                     
                     // needs to be ARGB stored in memory ABGR (RGBA = ABGR -> ARGB) 
                     // so R and B must be swapped, A and G just brought back in
                     tIntArray[t++] =
                         ((pixel & 0xFF) << 16) |
                         ((pixel & 0xFF0000) >> 16) |
                         (pixel & 0xFF00FF00);
                     
                 }
                 t += twidth - width;
            }

        }
        
        return tIntArray;    
    }

    /**
     * @invisible
     * Creates a byte version of intArray..
     * @param intArray int[]   
     */    
    protected byte[] convertToAlpha(int[] intArray)
    {
        byte[] tByteArray = new byte[width * height];        
        for (int i = 0; i < width * height; i++)
        {
           tByteArray[i] = (byte)(intArray[i]);
        }      
        return tByteArray;      
    }
    
    /**
     * @invisible
     * Creates the opengl texture object.
     * @param w int
     * @param h int	 
     */
    protected void initTexture(int w, int h)
    {
        if (tex[0] != 0)
        {
            releaseTexture();
        }
        
        gl.glGenTextures(1, tex, 0);
        gl.glBindTexture(texTarget, tex[0]);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP);
        if (texTarget == GL.GL_TEXTURE_1D) gl.glTexImage1D(texTarget, 0, texInternalFormat, w, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
        else gl.glTexImage2D(texTarget, 0, texInternalFormat, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
        gl.glBindTexture(texTarget, 0);
		
        if (texTarget == GL.GL_TEXTURE_RECTANGLE_ARB)
        {
            maxTexCoordS = w;
            maxTexCoordT = h;
        }
        else
        {
            maxTexCoordS = 1.0f;
            maxTexCoordT = 1.0f; 
        }
    }

    /**
     * @invisible
     * Initializes the texture with a pre-existing OpenGL texture ID.
     * @param w int
     * @param h int
     * @param id int
     */    
    protected void initTexture(int w, int h, int id)
    {
        if (tex[0] != 0)
        {
            releaseTexture();
        }
        
        tex[0] = id;
        gl.glBindTexture(texTarget, tex[0]);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_MIN_FILTER, minFilter);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_MAG_FILTER, magFilter);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP);
        gl.glTexParameteri(texTarget, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP);
        if (texTarget == GL.GL_TEXTURE_1D) gl.glTexImage1D(texTarget, 0, texInternalFormat, w, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
        else gl.glTexImage2D(texTarget, 0, texInternalFormat, w, h, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
        gl.glBindTexture(texTarget, 0);
		
        if (texTarget == GL.GL_TEXTURE_RECTANGLE_ARB)
        {
            maxTexCoordS = w;
            maxTexCoordT = h;
        }
        else
        {
            maxTexCoordS = 1.0f;
            maxTexCoordT = 1.0f; 
        }
    }
    
    /**
     * @invisible
     * Deletes the opengl texture object.
     */
    protected void releaseTexture()
    {
        gl.glDeleteTextures(1, tex, 0);  
        tex[0] = 0;
    }

    /**
     * @invisible
     * Deletes the PBO.
     */
    protected void releasePBO()
    {
    	gl.glDeleteBuffers(1, pbo, 0);  
    	pbo[0] = 0;
    }
    
    /**
     * @invisible
     * Sets texture target and internal format according to the target and  type specified.
     * @param target int		   
     * @param params GLTextureParameters
     */		
    protected void setTextureParams(GLTextureParameters params)
    {
	    if (params.target == TEX_NORM)
        {            
            texTarget = GL.GL_TEXTURE_2D;
        }
	      else if (params.target == TEX_RECT)
        {
            texTarget = GL.GL_TEXTURE_RECTANGLE_ARB;		
        }
	      else if (params.target == TEX_ONEDIM)
        {
            texTarget = GL.GL_TEXTURE_1D;
        }

  	    if (params.format == RGB) 
  	    {
            texInternalFormat = GL.GL_RGB;
        } 
        if (params.format == ARGB) 
        {
            texInternalFormat = GL.GL_RGBA;
        } 
        if (params.format == ALPHA) 
        {
            texInternalFormat = GL.GL_ALPHA;
        }         
  	    else if (params.format == FLOAT)
        {
            texInternalFormat = GL.GL_RGBA16F_ARB;
	      }
	      else if (params.format == DOUBLE)
        {
            texInternalFormat = GL.GL_RGBA32F_ARB;
	      }		

	      if (params.minFilter == NEAREST)
	      {
            minFilter = GL.GL_NEAREST;
        }
	      else if (params.minFilter == LINEAR)
        {
            minFilter = GL.GL_LINEAR;
	      }
	      else if (params.minFilter == NEAREST_MIPMAP_NEAREST)
        {
            minFilter = GL.GL_NEAREST_MIPMAP_NEAREST;
	      }
	      else if (params.minFilter == LINEAR_MIPMAP_NEAREST)
        {
            minFilter = GL.GL_LINEAR_MIPMAP_NEAREST;
	      }		
	      else if (params.minFilter == NEAREST_MIPMAP_LINEAR)
        {
            minFilter = GL.GL_NEAREST_MIPMAP_LINEAR;
	      }
	      else if (params.minFilter == LINEAR_MIPMAP_LINEAR)
        {
            minFilter = GL.GL_LINEAR_MIPMAP_LINEAR;
        }		

	      if (params.magFilter == NEAREST)
	      {
            magFilter = GL.GL_NEAREST;
        }
	      else if (params.magFilter == LINEAR)
        {
            magFilter = GL.GL_LINEAR;
	      }

        usingMipmaps = (minFilter == GL.GL_NEAREST_MIPMAP_NEAREST) ||
                       (minFilter == GL.GL_LINEAR_MIPMAP_NEAREST) ||
                       (minFilter == GL.GL_NEAREST_MIPMAP_LINEAR) ||
                       (minFilter == GL.GL_LINEAR_MIPMAP_LINEAR);
					   
        flippedX = false;
        flippedY = false;		
    }	
	
    /**
     * @invisible
     * Generates a power-of-two box width and height so that width * height is closest to size.
     * @param size int
     */	
    protected void calculateWidthHeight(int size)
    {
        int w, h;
        float l = PApplet.sqrt(size);
        for (w = 2; w < l; w *= 2);
        int n0 = w * w;
        int n1 = w * w / 2;
        if (PApplet.abs(n0 - size) < PApplet.abs(n1 - size)) h = w;
        else h = w / 2;
        
        width = w;
        height = h;
    }
     
    /**
     * @invisible
     */	
    protected GL gl;
	
    /**
     * @invisible
     */			
    protected PGraphicsOpenGL pgl;
	
    /**
     * @invisible
     */	
    protected int[] tex = { 0 }; 
	
    /**
     * @invisible
     */			
    protected int texTarget;	
	
    /**
     * @invisible
     */			
    protected int texInternalFormat;

    /**
     * @invisible
     */			
    protected int minFilter;	

    /**
     * @invisible
     */			
    protected int magFilter;
	
    /**
     * @invisible
     */		
    protected boolean usingMipmaps;	
	
    /**
     * @invisible
     */		
    protected float maxTexCoordS;
	
    /**
     * @invisible
     */		
    protected float maxTexCoordT;
	
    /**
     * @invisible
     */		
    protected boolean flippedX;	
	
    /**
     * @invisible
     */		
    protected boolean flippedY;

    /**
     * @invisible
     */		    
    protected int[] pbo = { 0 };
    
    /**
     * @invisible
     */		    
    protected int pboTarget;
    
    /**
     * @invisible
     */    
    protected int pboUsage;
    
    /**
     * @invisible
     */			
    protected GLState glstate;
}
