From 45930bda6357eff6bf80f37350852ee489a2e9af Mon Sep 17 00:00:00 2001 From: codeanticode Date: Mon, 3 Sep 2012 01:48:59 +0000 Subject: [PATCH] android sync --- .../src/processing/opengl/FrameBuffer.java | 2 +- android/core/src/processing/opengl/PGL.java | 26 +- .../src/processing/opengl/PGraphics2D.java | 2 +- .../processing/opengl/PGraphicsOpenGL.java | 174 +- .../core/src/processing/opengl/PShader.java | 15 +- .../src/processing/opengl/PShapeOpenGL.java | 10 +- .../core/src/processing/opengl/Texture.java | 3126 +++++++++-------- 7 files changed, 1694 insertions(+), 1661 deletions(-) diff --git a/android/core/src/processing/opengl/FrameBuffer.java b/android/core/src/processing/opengl/FrameBuffer.java index d032cc378..27e0f87f5 100644 --- a/android/core/src/processing/opengl/FrameBuffer.java +++ b/android/core/src/processing/opengl/FrameBuffer.java @@ -196,7 +196,7 @@ public class FrameBuffer implements PConstants { public void finish() { if (noDepth) { // No need to clear depth buffer because depth testing was disabled. - if (pg.hintEnabled(ENABLE_DEPTH_TEST)) { + if (pg.getHint(ENABLE_DEPTH_TEST)) { pgl.enable(PGL.DEPTH_TEST); } else { pgl.disable(PGL.DEPTH_TEST); diff --git a/android/core/src/processing/opengl/PGL.java b/android/core/src/processing/opengl/PGL.java index 4a3d45454..489e7ab68 100644 --- a/android/core/src/processing/opengl/PGL.java +++ b/android/core/src/processing/opengl/PGL.java @@ -3,8 +3,7 @@ /* Part of the Processing project - http://processing.org - Copyright (c) 2011 Andres Colubri - Copyright (c) 2010 Ben Fry and Casey Reas + Copyright (c) 2011-12 Ben Fry and Casey Reas This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -520,10 +519,19 @@ public class PGL { } + protected int primaryDrawBuffer() { + if (PGraphicsOpenGL.screenFramebuffer.glFbo != 0) { + return GLES20.GL_BACK; + } else { + return GLES20.GL_COLOR_ATTACHMENT0; + } + } + +/* protected boolean primaryIsDoubleBuffered() { return PGraphicsOpenGL.screenFramebuffer.glFbo != 0; } - +*/ protected boolean primaryIsFboBacked() { return PGraphicsOpenGL.screenFramebuffer.glFbo != 0; @@ -569,6 +577,14 @@ public class PGL { } + protected void bindBackBufferTex() { + } + + + protected void unbindBackBufferTex() { + } + + /////////////////////////////////////////////////////////// // Frame rendering @@ -576,8 +592,8 @@ public class PGL { protected void beginOnscreenDraw(boolean clear) { if (clear && !FORCE_SCREEN_FBO) { - // Simplest scenario: clear mode means we clear both the color and depth buffers. - // No need for saving front color buffer, etc. + // Simplest scenario: clear mode means we clear both the color and depth + // buffers. No need for saving front color buffer, etc. GLES20.glClearColor(0, 0, 0, 0); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); PGraphicsOpenGL.screenFramebuffer.glFbo = 0; diff --git a/android/core/src/processing/opengl/PGraphics2D.java b/android/core/src/processing/opengl/PGraphics2D.java index fb625ae7b..737fae02d 100644 --- a/android/core/src/processing/opengl/PGraphics2D.java +++ b/android/core/src/processing/opengl/PGraphics2D.java @@ -66,7 +66,7 @@ public class PGraphics2D extends PGraphicsOpenGL { @Override public void hint(int which) { if (which == ENABLE_STROKE_PERSPECTIVE) { - showWarning("2D lines cannot be perspective-corrected."); + showWarning("Strokes cannot be perspective-corrected in 2D."); return; } super.hint(which); diff --git a/android/core/src/processing/opengl/PGraphicsOpenGL.java b/android/core/src/processing/opengl/PGraphicsOpenGL.java index b8795e2dc..7c769feec 100644 --- a/android/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/android/core/src/processing/opengl/PGraphicsOpenGL.java @@ -47,10 +47,6 @@ public class PGraphicsOpenGL extends PGraphics { protected WeakHashMap fontMap = new WeakHashMap(); - /** Additional image parameters not covered by the cache. */ - protected WeakHashMap paramMap = - new WeakHashMap(); - // ........................................................ // Basic rendering parameters: @@ -632,38 +628,6 @@ public class PGraphicsOpenGL extends PGraphics { } - ////////////////////////////////////////////////////////////// - - /** - * Store parameters for a renderer that requires extra metadata of - * some kind. - * @param renderer The PGraphics renderer associated to the image - * @param storage The parameters required by the renderer - */ - public void setParams(PImage image, Object params) { - paramMap.put(image, params); - } - - - /** - * Get the parameters for the specified renderer. - * @param renderer The PGraphics renderer associated to the image - * @return parameters stored for the specified renderer - */ - public Object getParams(PImage image) { - return paramMap.get(image); - } - - - /** - * Remove information associated with this renderer from the cache, if any. - * @param renderer The PGraphics renderer whose parameters should be removed - */ - public void removeParams(PImage image) { - paramMap.remove(image); - } - - ////////////////////////////////////////////////////////////// // RESOURCE HANDLING @@ -672,6 +636,7 @@ public class PGraphicsOpenGL extends PGraphics { protected class GLResource { int id; int context; + GLResource(int id, int context) { this.id = id; this.context = context; @@ -1589,9 +1554,7 @@ public class PGraphicsOpenGL extends PGraphics { if (primarySurface) { pgl.updatePrimary(); - if (pgl.primaryIsDoubleBuffered()) { - pgl.drawBuffer(PGL.BACK); - } + pgl.drawBuffer(pgl.primaryDrawBuffer()); } else { if (!pgl.initialized) { initOffscreen(); @@ -1677,7 +1640,6 @@ public class PGraphicsOpenGL extends PGraphics { // The screen texture should be deleted because it // corresponds to the old window size. pgPrimary.removeCache(this); - pgPrimary.removeParams(this); texture = null; loadTexture(); } @@ -1709,8 +1671,7 @@ public class PGraphicsOpenGL extends PGraphics { } // Because y is flipped, the vertices that should be specified by - // the user in CCW order to define a front-facing facet, end up being - // CW. + // the user in CCW order to define a front-facing facet, end up being CW. pgl.frontFace(PGL.CW); pgl.disable(PGL.CULL_FACE); @@ -1897,35 +1858,32 @@ public class PGraphicsOpenGL extends PGraphics { pgl.depthMask(true); } - if (pgl.primaryIsDoubleBuffered()) { - pgl.drawBuffer(PGL.BACK); - } + pgl.drawBuffer(pgl.primaryDrawBuffer()); } protected void beginPixelsOp(int op) { if (primarySurface) { - if (pgl.primaryIsDoubleBuffered()) { - // We read or write from the back buffer, where all the - // drawing in the current frame is taking place. + if (pgl.primaryIsFboBacked()) { if (op == OP_READ) { - pgl.readBuffer(PGL.BACK); - } else { - pgl.drawBuffer(PGL.BACK); - } - offscreenNotCurrent = false; - } else if (pgl.primaryIsFboBacked()) { - if (op == OP_READ) { - // We read from the color FBO, but the multisample FBO is currently bound, so: + // We read from the color FBO, but the multisample FBO is currently + // bound, so: offscreenNotCurrent = true; pgl.bindPrimaryColorFBO(); - pgl.readBuffer(PGL.COLOR_ATTACHMENT0); + pgl.readBuffer(pgl.primaryDrawBuffer()); } else { // We write directly to the multisample FBO. offscreenNotCurrent = false; - pgl.drawBuffer(PGL.COLOR_ATTACHMENT0); + pgl.drawBuffer(pgl.primaryDrawBuffer()); } } else { + // We read or write from the back buffer, where all the + // drawing in the current frame is taking place. + if (op == OP_READ) { + pgl.readBuffer(pgl.primaryDrawBuffer()); + } else { + pgl.drawBuffer(pgl.primaryDrawBuffer()); + } offscreenNotCurrent = false; } } else { @@ -2175,7 +2133,7 @@ public class PGraphicsOpenGL extends PGraphics { } - protected boolean hintEnabled(int which) { + protected boolean getHint(int which) { if (which > 0) { return hints[which]; } else { @@ -2225,10 +2183,12 @@ public class PGraphicsOpenGL extends PGraphics { } } + protected void endShape(int[] indices) { endShape(indices, null); } + protected void endShape(int[] indices, int[] edges) { if (shape != TRIANGLE && shape != TRIANGLES) { throw new RuntimeException("Indices and edges can only be set for " + @@ -5240,6 +5200,7 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// // RENDERER SUPPORT QUERIES @@ -5253,6 +5214,7 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// // PIMAGE METHODS @@ -5510,9 +5472,8 @@ public class PGraphicsOpenGL extends PGraphics { } if (tex != null) { texture = tex; - texture.setFlippedY(true); + texture.invertedY(true); pgPrimary.setCache(this, texture); - pgPrimary.setParams(this, params); if (!primarySurface && offscreenFramebuffer != null) { // Attach as the color buffer for this offscreen surface @@ -5549,9 +5510,8 @@ public class PGraphicsOpenGL extends PGraphics { Texture.Parameters params = new Texture.Parameters(ARGB, sampling, mipmap); texture = new Texture(parent, width, height, params); - texture.setFlippedY(true); - this.setCache(pgPrimary, texture); - this.setParams(pgPrimary, params); + texture.invertedY(true); + pgPrimary.setCache(this, texture); } } @@ -5580,6 +5540,7 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// // IMAGE CONVERSION @@ -5615,6 +5576,7 @@ public class PGraphicsOpenGL extends PGraphics { */ + ////////////////////////////////////////////////////////////// // MASK @@ -5644,6 +5606,7 @@ public class PGraphicsOpenGL extends PGraphics { } + ////////////////////////////////////////////////////////////// // FILTER @@ -5689,7 +5652,7 @@ public class PGraphicsOpenGL extends PGraphics { Texture.Parameters params = new Texture.Parameters(ARGB, Texture.POINT, false); textureCopy = new Texture(parent, width, height, params); - textureCopy.setFlippedY(true); + textureCopy.invertedY(true); imageCopy = wrapTexture(textureCopy); } textureCopy.set(texture.glTarget, texture.glName, @@ -5946,27 +5909,33 @@ public class PGraphicsOpenGL extends PGraphics { } + protected void bindBackTexture() { + if (primarySurface) { + pgl.bindBackBufferTex(); + } else { + + } + } + + + protected void unbindBackTexture() { + if (primarySurface) { + pgl.unbindBackBufferTex(); + } else { + + } + } + + /** * This utility method creates a texture for the provided image, and adds it * to the metadata cache of the image. * @param img the image to have a texture metadata associated to it */ protected Texture addTexture(PImage img) { - Texture.Parameters params = (Texture.Parameters)pgPrimary.getParams(img); - if (params == null) { - params = new Texture.Parameters(); - if (hints[DISABLE_TEXTURE_MIPMAPS]) { - params.mipmaps = false; - } else { - params.mipmaps = true; - } - params.sampling = textureSampling; - if (params.sampling == Texture.TRILINEAR && !params.mipmaps) { - params.sampling = Texture.BILINEAR; - } - params.wrapU = textureWrap; - params.wrapV = textureWrap; - } + Texture.Parameters params = + new Texture.Parameters(ARGB, textureSampling, + getHint(ENABLE_TEXTURE_MIPMAPS),textureWrap); return addTexture(img, params); } @@ -5981,7 +5950,6 @@ public class PGraphicsOpenGL extends PGraphics { } Texture tex = new Texture(img.parent, img.width, img.height, params); pgPrimary.setCache(img, tex); - pgPrimary.setParams(img, params); return tex; } @@ -6015,7 +5983,6 @@ public class PGraphicsOpenGL extends PGraphics { img.height = tex.height; img.format = ARGB; pgPrimary.setCache(img, tex); - pgPrimary.setParams(img, tex.getParameters()); return img; } @@ -6527,6 +6494,8 @@ public class PGraphicsOpenGL extends PGraphics { protected int projmodelviewMatrixLoc; protected int modelviewMatrixLoc; protected int projectionMatrixLoc; + protected int backbufferSamplerLoc; + protected int resolutionLoc; protected int inVertexLoc; protected int inColorLoc; @@ -6555,6 +6524,9 @@ public class PGraphicsOpenGL extends PGraphics { projmodelviewMatrixLoc = getUniformLoc("projmodelviewMatrix"); modelviewMatrixLoc = getUniformLoc("modelviewMatrix"); projectionMatrixLoc = getUniformLoc("projectionMatrix"); + + backbufferSamplerLoc = getUniformLoc("backbufferSampler"); + resolutionLoc = getUniformLoc("resolution"); } @Override @@ -6595,6 +6567,16 @@ public class PGraphicsOpenGL extends PGraphics { pgCurrent.updateGLProjection(); setUniformMatrix(projectionMatrixLoc, pgCurrent.glProjection); } + + float w = pgCurrent.width; + float h = pgCurrent.height; + setUniformValue(resolutionLoc, w, h); + + if (-1 < backbufferSamplerLoc) { + setUniformValue(backbufferSamplerLoc, lastTexUnit); + pgl.activeTexture(PGL.TEXTURE0 + lastTexUnit); + pgCurrent.bindBackTexture(); + } } @Override @@ -6602,6 +6584,12 @@ public class PGraphicsOpenGL extends PGraphics { if (-1 < inVertexLoc) pgl.disableVertexAttribArray(inVertexLoc); if (-1 < inColorLoc) pgl.disableVertexAttribArray(inColorLoc); + if (-1 < backbufferSamplerLoc) { + pgl.activeTexture(PGL.TEXTURE0 + lastTexUnit); + pgCurrent.unbindBackTexture(); + pgl.activeTexture(PGL.TEXTURE0); + } + pgl.bindBuffer(PGL.ARRAY_BUFFER, 0); super.unbind(); @@ -6836,20 +6824,20 @@ public class PGraphicsOpenGL extends PGraphics { float dispu = 0; float dispv = 0; - if (tex.isFlippedX()) { + if (tex.invertedX()) { scaleu = -1; dispu = 1; } - if (tex.isFlippedY()) { + if (tex.invertedY()) { scalev = -1; dispv = 1; } - scaleu *= tex.maxTexcoordU; - dispu *= tex.maxTexcoordU; - scalev *= tex.maxTexcoordV; - dispv *= tex.maxTexcoordV; + scaleu *= tex.maxTexcoordU(); + dispu *= tex.maxTexcoordU(); + scalev *= tex.maxTexcoordV(); + dispv *= tex.maxTexcoordV(); if (-1 < texcoordMatrixLoc) { if (tcmat == null) { @@ -6936,12 +6924,12 @@ public class PGraphicsOpenGL extends PGraphics { float dispu = 0; float dispv = 0; - if (tex.isFlippedX()) { + if (tex.invertedX()) { scaleu = -1; dispu = 1; } - if (tex.isFlippedY()) { + if (tex.invertedY()) { scalev = -1; dispv = 1; } @@ -7078,13 +7066,13 @@ public class PGraphicsOpenGL extends PGraphics { float h = pgCurrent.viewport[3]; setUniformValue(viewportLoc, x, y, w, h); - if (pgCurrent.hintEnabled(ENABLE_STROKE_PERSPECTIVE)) { + if (pgCurrent.getHint(ENABLE_STROKE_PERSPECTIVE)) { setUniformValue(perspectiveLoc, 1); } else { setUniformValue(perspectiveLoc, 0); } - if (pgCurrent.hintEnabled(ENABLE_ACCURATE_2D)) { + if (pgCurrent.getHint(ENABLE_ACCURATE_2D)) { setUniformValue(scaleLoc, 1.0f, 1.0f, 1.0f); } else { if (usingOrthoProjection) { @@ -7199,7 +7187,7 @@ public class PGraphicsOpenGL extends PGraphics { float h = pgCurrent.viewport[3]; setUniformValue(viewportLoc, x, y, w, h); - if (pgCurrent.hintEnabled(ENABLE_STROKE_PERSPECTIVE)) { + if (pgCurrent.getHint(ENABLE_STROKE_PERSPECTIVE)) { setUniformValue(perspectiveLoc, 1); } else { setUniformValue(perspectiveLoc, 0); diff --git a/android/core/src/processing/opengl/PShader.java b/android/core/src/processing/opengl/PShader.java index c1eb3e566..d73644555 100644 --- a/android/core/src/processing/opengl/PShader.java +++ b/android/core/src/processing/opengl/PShader.java @@ -75,6 +75,7 @@ public class PShader { protected HashMap textures; protected int firstTexUnit; + protected int lastTexUnit; public PShader() { @@ -473,6 +474,12 @@ public class PShader { } + protected void setUniformTex(int loc, Texture tex) { + // get unit from last value in bindTextures ... + // pgl.activeTexture(PGL.TEXTURE0 + unit); + tex.bind(); + } + /* // The individual attribute setters are not really needed, read this: @@ -523,7 +530,7 @@ public class PShader { protected void consumeUniforms() { if (uniformValues != null && 0 < uniformValues.size()) { - int texUnit = firstTexUnit; + lastTexUnit = firstTexUnit; for (Integer loc: uniformValues.keySet()) { UniformValue val = uniformValues.get(loc); if (val.type == UniformValue.INT1) { @@ -586,12 +593,12 @@ public class PShader { } else if (val.type == UniformValue.SAMPLER2D) { PImage img = (PImage)val.value; Texture tex = pgMain.getTexture(img); - pgl.uniform1i(loc, texUnit); + pgl.uniform1i(loc, lastTexUnit); if (textures == null) { textures = new HashMap(); } - textures.put(texUnit, tex); - texUnit++; + textures.put(lastTexUnit, tex); + lastTexUnit++; } } uniformValues.clear(); diff --git a/android/core/src/processing/opengl/PShapeOpenGL.java b/android/core/src/processing/opengl/PShapeOpenGL.java index 850a9a75f..8048a4854 100644 --- a/android/core/src/processing/opengl/PShapeOpenGL.java +++ b/android/core/src/processing/opengl/PShapeOpenGL.java @@ -394,7 +394,7 @@ public class PShapeOpenGL extends PShape { } - public void updateRoot(PShape root) { + protected void updateRoot(PShape root) { this.root = (PShapeOpenGL) root; if (family == GROUP) { for (int i = 0; i < childCount; i++) { @@ -546,6 +546,7 @@ public class PShapeOpenGL extends PShape { } + /* @Override public PVector getTop(PVector top) { if (top == null) { @@ -568,6 +569,7 @@ public class PShapeOpenGL extends PShape { getVertexMax(bottom); return bottom; } + */ protected void getVertexMin(PVector min) { @@ -2880,6 +2882,7 @@ public class PShapeOpenGL extends PShape { fill, stroke, curveDetail, code); code = VERTEX; idx++; + break; case BREAK: code = BREAK; @@ -2931,6 +2934,7 @@ public class PShapeOpenGL extends PShape { fill, stroke, curveDetail, code); code = VERTEX; idx++; + break; case BREAK: code = BREAK; @@ -4118,7 +4122,7 @@ public class PShapeOpenGL extends PShape { // Or accurate 2D mode is enabled, which forces each // shape to be rendered separately. protected boolean fragmentedGroup(PGraphicsOpenGL g) { - return g.hintEnabled(ENABLE_ACCURATE_2D) || + return g.getHint(ENABLE_ACCURATE_2D) || (textures != null && 1 < textures.size()) || strokedTexture; } @@ -4137,7 +4141,7 @@ public class PShapeOpenGL extends PShape { @Override - public void post(PGraphics g) { + protected void post(PGraphics g) { if (g instanceof PGraphicsOpenGL) { } else { super.post(g); diff --git a/android/core/src/processing/opengl/Texture.java b/android/core/src/processing/opengl/Texture.java index e604563bb..6e3d05a97 100644 --- a/android/core/src/processing/opengl/Texture.java +++ b/android/core/src/processing/opengl/Texture.java @@ -1,1554 +1,1572 @@ -/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Part of the Processing project - http://processing.org - - Copyright (c) 2011-12 Ben Fry and Casey Reas - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA -*/ - -package processing.opengl; - -import processing.core.PApplet; -import processing.core.PConstants; -import processing.core.PGraphics; -import processing.core.PImage; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.LinkedList; -import java.util.NoSuchElementException; - -/** - * This class wraps an OpenGL texture. - * By Andres Colubri - * - */ -public class Texture implements PConstants { - // texture constants - - /** - * Texture with normalized UV. - */ - protected static final int TEX2D = 0; - /** - * Texture with un-normalized UV. - */ - protected static final int TEXRECT = 1; - - /** Point sampling: both magnification and minification filtering are set - * to nearest */ - protected static final int POINT = 2; - /** Linear sampling: magnification filtering is nearest, minification set - * to linear */ - protected static final int LINEAR = 3; - /** Bilinear sampling: both magnification filtering is set to linear and - * minification either to linear-mipmap-nearest (linear interplation is used - * within a mipmap, but not between different mipmaps). */ - protected static final int BILINEAR = 4; - /** Trilinear sampling: magnification filtering set to linear, minification to - * linear-mipmap-linear, which offers the best mipmap quality since linear - * interpolation to compute the value in each of two maps and then - * interpolates linearly between these two value. */ - protected static final int TRILINEAR = 5; - - public int width, height; - - public int glName; - public int glTarget; - public int glFormat; - public int glMinFilter; - public int glMagFilter; - public int glWrapS; - public int glWrapT; - public int glWidth; - public int glHeight; - - protected PApplet parent; // The Processing applet - protected PGraphicsOpenGL pg; // The main renderer - protected PGL pgl; // The interface between Processing and OpenGL. - protected PGL.Context context; // The context that created this texture. - - protected boolean usingMipmaps; - protected boolean usingRepeat; - protected float maxTexcoordU; - protected float maxTexcoordV; - protected boolean bound; - - protected boolean flippedX; - protected boolean flippedY; - - protected FrameBuffer tempFbo = null; - - /** Modified portion of the texture */ - protected boolean modified; - protected int mx1, my1, mx2, my2; - - protected Object bufferSource; - protected LinkedList bufferCache = null; - protected Method disposeBufferMethod; - public static final int MAX_BUFFER_CACHE_SIZE = 3; - - //////////////////////////////////////////////////////////// - - // Constructors. - - - /** - * Creates an instance of PTexture with size width x height. The texture is - * initialized (empty) to that size. - * @param parent PApplet - * @param width int - * @param height int - */ - public Texture(PApplet parent, int width, int height) { - this(parent, width, height, new Parameters()); - } - - - /** - * Creates an instance of PTexture 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 Parameters - */ - public Texture(PApplet parent, int width, int height, Object params) { - this.parent = parent; - - pg = (PGraphicsOpenGL)parent.g; - pgl = pg.pgl; - context = pgl.createEmptyContext(); - - glName = 0; - - init(width, height, (Parameters)params); - } - - - @Override - protected void finalize() throws Throwable { - try { - if (glName != 0) { - pg.finalizeTextureObject(glName, context.id()); - } - } finally { - super.finalize(); - } - } - - - //////////////////////////////////////////////////////////// - - // Init, resize methods - - - /** - * 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) { - Parameters params; - if (0 < glName) { - // Re-initializing a pre-existing texture. - // We use the current parameters as default: - params = getParameters(); - } else { - // Just built-in default parameters otherwise: - params = new Parameters(); - } - init(width, height, params); - } - - - /** - * 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, Parameters params) { - setParameters(params); - setSize(width, height); - allocate(); - } - - - public void resize(int wide, int high) { - // Marking the texture object as finalized so it is deleted - // when creating the new texture. - release(); - - // Creating new texture with the appropriate size. - Texture tex = new Texture(parent, wide, high, getParameters()); - - // Copying the contents of this texture into tex. - tex.set(this); - - // Now, overwriting "this" with tex. - copyObject(tex); - - // Nullifying some utility objects so they are recreated with the - // appropriate size when needed. - tempFbo = null; - } - - - /** - * Returns true if the texture has been initialized. - * @return boolean - */ - public boolean available() { - return 0 < glName; - } - - - //////////////////////////////////////////////////////////// - - // Set methods - - - public void set(PImage img) { - Texture tex = (Texture)pg.getCache(img); - set(tex); - } - - - public void set(PImage img, int x, int y, int w, int h) { - Texture tex = (Texture)pg.getCache(img); - set(tex, x, y, w, h); - } - - - public void set(Texture tex) { - copyTexture(tex, 0, 0, tex.width, tex.height, true); - } - - - public void set(Texture tex, int x, int y, int w, int h) { - copyTexture(tex, x, y, w, h, true); - } - - - public void set(int texTarget, int texName, int texWidth, int texHeight, - int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); - } - - - public void set(int texTarget, int texName, int texWidth, int texHeight, - int target, int tex, int x, int y, int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); - } - - - public void set(int[] pixels) { - set(pixels, 0, 0, width, height, ARGB); - } - - - public void set(int[] pixels, int format) { - set(pixels, 0, 0, width, height, format); - } - - - public void set(int[] pixels, int x, int y, int w, int h) { - set(pixels, x, y, w, h, ARGB); - } - - - public void set(int[] pixels, int x, int y, int w, int h, int format) { - if (pixels == null) { - pixels = null; - PGraphics.showWarning("The pixels array is null."); - return; - } - if (pixels.length != w * h) { - PGraphics.showWarning("The pixels array has a length of " + - pixels.length + ", but it should be " + w * h); - return; - } - - if (pixels.length == 0) { - // Nothing to do (means that w == h == 0) but not an erroneous situation - return; - } - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - pgl.bindTexture(glTarget, glName); - - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - // Automatic mipmap generation. - int[] rgbaPixels = new int[w * h]; - convertToRGBA(pixels, rgbaPixels, format, w, h); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - IntBuffer.wrap(rgbaPixels)); - pgl.generateMipmap(glTarget); - rgbaPixels = null; - } else { - // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, - // making it work in npot textures (embed npot tex into larger pot tex?), subregions, - // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. - // Actually, this whole code should go into PGL, so the Android implementation can - // use Bitmap, and desktop use BufferedImage. - - /* - if (w != width || h != height) { - System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); - return; - } - - // Code by Mike Miller obtained from here: - // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ - int w0 = glWidth; - int h0 = glHeight; - int[] argbPixels = new int[w0 * h0]; - convertToARGB(pixels, argbPixels, format); - int level = 0; - int denom = 1; - - // We create a Bitmap because then we use its built-in filtered downsampling - // functionality. - Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); - bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); - - while (w0 >= 1 || h0 >= 1) { - //First of all, generate the texture from our bitmap and set it to the according level - GLUtils.texImage2D(glTarget, level, bitmap, 0); - - // We are done. - if (w0 == 1 && h0 == 1) { - break; - } - - // Increase the mipmap level - level++; - denom *= 2; - - // Downsampling bitmap. We must eventually arrive to the 1x1 level, - // and if the width and height are different, there will be a few 1D - // texture levels just before. - // This update formula also allows for NPOT resolutions. - w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); - h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); - // (see getScaledInstance in AWT Image) - Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); - - // Clean up - bitmap.recycle(); - bitmap = bitmap2; - } - */ - - int[] rgbaPixels = new int[w * h]; - convertToRGBA(pixels, rgbaPixels, format, w, h); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - IntBuffer.wrap(rgbaPixels)); - rgbaPixels = null; - } - } else { - int[] rgbaPixels = new int[w * h]; - convertToRGBA(pixels, rgbaPixels, format, w, h); - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - IntBuffer.wrap(rgbaPixels)); - rgbaPixels = null; - } - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - - updateTexels(x, y, w, h); - } - - - //////////////////////////////////////////////////////////// - - // Native set methods - - - public void setNative(int[] pixels) { - setNative(pixels, 0, 0, width, height); - } - - - public void setNative(int[] pixels, int x, int y, int w, int h) { - setNative(IntBuffer.wrap(pixels), x, y, w, h); - } - - - public void setNative(IntBuffer pixels, int x, int y, int w, int h) { - if (pixels == null) { - pixels = null; - PGraphics.showWarning("The pixel buffer is null."); - return; - } - if (pixels.capacity() != w * h) { - PGraphics.showWarning("The pixels array has a length of " + - pixels.capacity() + ", but it should be " + w * h); - return; - } - - if (pixels.capacity() == 0) { - // Nothing to do (means that w == h == 0) but not an erroneous situation - return; - } - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - pgl.bindTexture(glTarget, glName); - - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixels); - pgl.generateMipmap(glTarget); - } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixels); - } - } else { - pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, - pixels); - } - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - - updateTexels(x, y, w, h); - } - - - //////////////////////////////////////////////////////////// - - // Get methods - - - /** - * Copy texture to pixels. Involves video memory to main memory transfer (slow). - */ - public void get(int[] pixels) { - if (pixels == null) { - throw new RuntimeException("Trying to copy texture to null pixels array"); - } - if (pixels.length != width * height) { - throw new RuntimeException("Trying to copy texture to pixels array of " + - "wrong size"); - } - - if (tempFbo == null) { - tempFbo = new FrameBuffer(parent, glWidth, glHeight); - } - - // Attaching the texture to the color buffer of a FBO, binding the FBO and - // reading the pixels from the current draw buffer (which is the color - // buffer of the FBO). - tempFbo.setColorBuffer(this); - pg.pushFramebuffer(); - pg.setFramebuffer(tempFbo); - tempFbo.readPixels(); - pg.popFramebuffer(); - - tempFbo.getPixels(pixels); - convertToARGB(pixels); - - if (flippedX) flipArrayOnX(pixels, 1); - if (flippedY) flipArrayOnY(pixels, 1); - } - - - /** - * Copies the contents of the texture to the pixels array. - * @param pixels - */ - public void loadPixels(int[] pixels) { - if (hasBuffers()) { - // Updates the texture AND the pixels array of the image at the same time, - // getting the pixels directly from the buffer data (and thus avoiding - // expensive transfer between video and main memory). - bufferUpdate(pixels); - } - - if (isModified()) { - // Regular pixel copy from texture. - get(pixels); - } - - setModified(false); - } - - - //////////////////////////////////////////////////////////// - - // Put methods (the source texture is not resized to cover the entire - // destination). - - - public void put(Texture tex) { - copyTexture(tex, 0, 0, tex.width, tex.height, false); - } - - - public void put(Texture tex, int x, int y, int w, int h) { - copyTexture(tex, x, y, w, h, false); - } - - - public void put(int texTarget, int texName, int texWidth, int texHeight, - int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); - } - - - public void put(int texTarget, int texName, int texWidth, int texHeight, - int target, int tex, int x, int y, int w, int h) { - copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); - } - - - //////////////////////////////////////////////////////////// - - // Get OpenGL parameters - - - /** - * Returns true or false whether or not the texture is using mipmaps. - * @return boolean - */ - public boolean usingMipmaps() { - return usingMipmaps; - } - - - public void usingMipmaps(boolean mipmaps, int sampling) { - if (mipmaps) { - if (glMinFilter != PGL.LINEAR_MIPMAP_NEAREST && - glMinFilter != PGL.LINEAR_MIPMAP_LINEAR) { - if (sampling == POINT) { - glMagFilter = PGL.NEAREST; - glMinFilter = PGL.NEAREST; - } else if (sampling == LINEAR) { - glMagFilter = PGL.NEAREST; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (sampling == BILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (sampling == TRILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = - PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; - } else { - throw new RuntimeException("Unknown texture filtering mode"); - } - } - - usingMipmaps = true; - } else { - if (glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { - glMinFilter = PGL.LINEAR; - } - usingMipmaps = false; - } - - bind(); - pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); - if (usingMipmaps) { - if (PGraphicsOpenGL.autoMipmapGenSupported) { - pgl.generateMipmap(glTarget); - } else { - // TODO: need manual generation here.. - } - } - unbind(); - } - - - /** - * Returns true or false whether or not the texture is using repeat wrap mode - * along either U or V directions. - * @return boolean - */ - public boolean usingRepeat() { - return usingRepeat; - } - - - public void usingRepeat(boolean repeat) { - if (repeat) { - glWrapS = PGL.REPEAT; - glWrapT = PGL.REPEAT; - usingRepeat = true; - } else { - glWrapS = PGL.CLAMP_TO_EDGE; - glWrapT = PGL.CLAMP_TO_EDGE; - usingRepeat = false; - } - - bind(); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); - unbind(); - } - - - /** - * Returns the maximum possible value for the texture coordinate U - * (horizontal). - * @return float - */ - public float getMaxU() { - return maxTexcoordU; - } - - - /** - * Returns the maximum possible value for the texture coordinate V (vertical). - * @return float - */ - public float getMaxV() { - return maxTexcoordV; - } - - - /** - * 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; - } - - //////////////////////////////////////////////////////////// - - // Bind/unbind - - - public void bind() { - // Binding a texture automatically enables texturing for the - // texture target from that moment onwards. Unbinding the texture - // won't disable texturing. - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - } - pgl.bindTexture(glTarget, glName); - bound = true; - } - - - public void unbind() { - if (pgl.textureIsBound(glTarget, glName)) { - // We don't want to unbind another texture - // that might be bound instead of this one. - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - pgl.bindTexture(glTarget, 0); - pgl.disableTexturing(glTarget); - } else { - pgl.bindTexture(glTarget, 0); - } - } - bound = false; - } - - - public boolean bound() { - // A true result might not necessarily mean that texturing is enabled - // (a texture can be bound to the target, but texturing is disabled). - return bound; - } - - - ////////////////////////////////////////////////////////////// - - // Modified flag - - - public boolean isModified() { - return modified; - } - - - public void setModified() { - modified = true; - } - - - public void setModified(boolean m) { - modified = m; - } - - - public int getModifiedX1() { - return mx1; - } - - - public int getModifiedX2() { - return mx2; - } - - - public int getModifiedY1() { - return my1; - } - - - public int getModifiedY2() { - return my2; - } - - - public void updateTexels() { - updateTexelsImpl(0, 0, width, height); - } - - - public void updateTexels(int x, int y, int w, int h) { - updateTexelsImpl(x, y, w, h); - } - - - protected void updateTexelsImpl(int x, int y, int w, int h) { - int x2 = x + w; - int y2 = y + h; - - if (!modified) { - mx1 = PApplet.max(0, x); - mx2 = PApplet.min(width - 1, x2); - my1 = PApplet.max(0, y); - my2 = PApplet.min(height - 1, y2); - modified = true; - - } else { - if (x < mx1) mx1 = PApplet.max(0, x); - if (x > mx2) mx2 = PApplet.min(width - 1, x); - if (y < my1) my1 = PApplet.max(0, y); - if (y > my2) my2 = y; - - if (x2 < mx1) mx1 = PApplet.max(0, x2); - if (x2 > mx2) mx2 = PApplet.min(width - 1, x2); - if (y2 < my1) my1 = PApplet.max(0, y2); - if (y2 > my2) my2 = PApplet.min(height - 1, y2); - } - } - - - //////////////////////////////////////////////////////////// - - // Buffer sink interface. - - - public void setBufferSource(Object source) { - bufferSource = source; - getSourceMethods(); - } - - - public void copyBufferFromSource(Object natRef, ByteBuffer byteBuf, - int w, int h) { - if (bufferCache == null) { - bufferCache = new LinkedList(); - } - - if (bufferCache.size() + 1 <= MAX_BUFFER_CACHE_SIZE) { - bufferCache.add(new BufferData(natRef, byteBuf.asIntBuffer(), w, h)); - } else { - // The buffer cache reached the maximum size, so we just dispose - // the new buffer. - try { - disposeBufferMethod.invoke(bufferSource, new Object[] { natRef }); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - public boolean hasBufferSource() { - return bufferSource != null; - } - - - public boolean hasBuffers() { - return bufferSource != null && bufferCache != null && - 0 < bufferCache.size(); - } - - - protected boolean bufferUpdate() { - BufferData data = null; - try { - data = bufferCache.remove(0); - } catch (NoSuchElementException ex) { - PGraphics.showWarning("Don't have pixel data to copy to texture"); - } - - if (data != null) { - if ((data.w != width) || (data.h != height)) { - init(data.w, data.h); - } - setNative(data.rgbBuf, 0, 0, width, height); - - data.dispose(); - - return true; - } else { - return false; - } - } - - - protected boolean bufferUpdate(int[] pixels) { - BufferData data = null; - try { - data = bufferCache.remove(0); - } catch (NoSuchElementException ex) { - PGraphics.showWarning("Don't have pixel data to copy to texture"); - } - - if (data != null) { - if ((data.w != width) || (data.h != height)) { - init(data.w, data.h); - } - setNative(data.rgbBuf, 0, 0, width, height); - - data.rgbBuf.get(pixels); - convertToARGB(pixels); - - data.dispose(); - - return true; - } else { - return false; - } - } - - - protected void getSourceMethods() { - try { - disposeBufferMethod = bufferSource.getClass(). - getMethod("disposeBuffer", new Class[] { Object.class }); - } catch (Exception e) { - throw new RuntimeException("Provided source object doesn't have a " + - "disposeBuffer method."); - } - } - - - //////////////////////////////////////////////////////////// - - // Utilities - - - /** - * 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; - } - } - - - /** - * Reorders a pixel array in the given format into the order required by - * OpenGL (RGBA). Both arrays are assumed to be of the same length. The width - * and height parameters are used in the YUV420 to RBGBA conversion. - * @param intArray int[] - * @param tIntArray int[] - * @param arrayFormat int - * @param w int - * @param h int - */ - protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat, - int w, int h) { - if (PGL.BIG_ENDIAN) { - switch (arrayFormat) { - case ALPHA: - - // Converting from xxxA into RGBA. RGB is set to white - // (0xFFFFFF, i.e.: (255, 255, 255)) - for (int i = 0; i< intArray.length; i++) { - tIntArray[i] = 0xFFFFFF00 | intArray[i]; - } - break; - - case RGB: - - // Converting xRGB into RGBA. A is set to 0xFF (255, full opacity). - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = (pixel << 8) | 0xFF; - } - break; - - case ARGB: - - // Converting ARGB into RGBA. Shifting RGB to 8 bits to the left, - // and bringing A to the first byte. - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = (pixel << 8) | ((pixel >> 24) & 0xFF); - } - 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: - - // Converting xxxA into ARGB, with RGB set to white. - for (int i = 0; i< intArray.length; i++) { - tIntArray[i] = (intArray[i] << 24) | 0x00FFFFFF; - } - break; - - case RGB: - - // We need to convert xRGB into ABGR, - // so R and B must be swapped, and the x just made 0xFF. - for (int i = 0; i< intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = 0xFF000000 | - ((pixel & 0xFF) << 16) | - ((pixel & 0xFF0000) >> 16) | - (pixel & 0x0000FF00); - } - break; - - case ARGB: - - // We need to convert ARGB into ABGR, - // so R and B must be swapped, A and G just brought back in. - for (int i = 0; i < intArray.length; i++) { - int pixel = intArray[i]; - tIntArray[i] = ((pixel & 0xFF) << 16) | - ((pixel & 0xFF0000) >> 16) | - (pixel & 0xFF00FF00); - } - break; - - } - - } - } - - - /** - * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be - * of size width * height. - * @param intArray int[] - */ - protected void convertToARGB(int[] intArray) { - int t = 0; - int p = 0; - if (PGL.BIG_ENDIAN) { - - // RGBA to ARGB conversion: shifting RGB 8 bits to the right, - // and placing A 24 bits to the left. - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int pixel = intArray[p++]; - intArray[t++] = (pixel >> 8) | ((pixel << 24) & 0xFF000000); - } - } - - } else { - - // We have to convert ABGR into ARGB, so R and B must be swapped, - // A and G just brought back in. - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int pixel = intArray[p++]; - intArray[t++] = ((pixel & 0xFF) << 16) | - ((pixel & 0xFF0000) >> 16) | - (pixel & 0xFF00FF00); - - } - } - } - } - - - - /////////////////////////////////////////////////////////// - - // Allocate/release texture. - - - protected void setSize(int w, int h) { - width = w; - height = h; - - if (PGraphicsOpenGL.npotTexSupported) { - glWidth = w; - glHeight = h; - } else { - glWidth = PGL.nextPowerOfTwo(w); - glHeight = PGL.nextPowerOfTwo(h); - } - - if (glWidth > PGraphicsOpenGL.maxTextureSize || - glHeight > PGraphicsOpenGL.maxTextureSize) { - glWidth = glHeight = 0; - throw new RuntimeException("Image width and height cannot be" + - " larger than " + - PGraphicsOpenGL.maxTextureSize + - " with this graphics card."); - } - - // If non-power-of-two textures are not supported, and the specified width - // or height is non-power-of-two, then glWidth (glHeight) will be greater - // than w (h) because it is chosen to be the next power of two, and this - // quotient will give the appropriate maximum texture coordinate value given - // this situation. - maxTexcoordU = (float)width / glWidth; - maxTexcoordV = (float)height / glHeight; - } - - - /** - * Allocates the opengl texture object. - */ - protected void allocate() { - release(); // Just in the case this object is being re-allocated. - - boolean enabledTex = false; - if (!pgl.texturingIsEnabled(glTarget)) { - pgl.enableTexturing(glTarget); - enabledTex = true; - } - - context = pgl.getCurrentContext(); - glName = pg.createTextureObject(context.id()); - - pgl.bindTexture(glTarget, glName); - pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); - pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); - - // First, we use glTexImage2D to set the full size of the texture (glW/glH - // might be diff from w/h in the case that the GPU doesn't support NPOT - // textures) - pgl.texImage2D(glTarget, 0, glFormat, glWidth, glHeight, 0, - PGL.RGBA, PGL.UNSIGNED_BYTE, null); - - // Makes sure that the texture buffer in video memory doesn't contain - // any garbage. - pgl.initTexture(glTarget, PGL.RGBA, width, height); - - pgl.bindTexture(glTarget, 0); - if (enabledTex) { - pgl.disableTexturing(glTarget); - } - bound = false; - } - - - /** - * Marks the texture object for deletion. - */ - protected void release() { - if (glName != 0) { - pg.finalizeTextureObject(glName, context.id()); - glName = 0; - } - } - - - protected boolean contextIsOutdated() { - boolean outdated = !pgl.contextIsCurrent(context); - if (outdated) { - // Removing the texture object from the renderer's list so it - // doesn't get deleted by OpenGL. The texture object was - // automatically disposed when the old context was destroyed. - pg.removeTextureObject(glName, context.id()); - - // And then set the id to zero, so it doesn't try to be - // deleted when the object's finalizer is invoked by the GC. - glName = 0; - } - return outdated; - } - - - /////////////////////////////////////////////////////////// - - // Utilities. - - - // Copies source texture tex into this. - protected void copyTexture(Texture tex, int x, int y, int w, int h, - boolean scale) { - if (tex == null) { - throw new RuntimeException("Source texture is null"); - } - - if (tempFbo == null) { - tempFbo = new FrameBuffer(parent, glWidth, glHeight); - } - - // This texture is the color (destination) buffer of the FBO. - tempFbo.setColorBuffer(this); - tempFbo.disableDepthTest(); - - // FBO copy: - pg.pushFramebuffer(); - pg.setFramebuffer(tempFbo); - if (scale) { - // Rendering tex into "this", and scaling the source rectangle - // to cover the entire destination region. - pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, 0, 0, width, height); - - } else { - // Rendering tex into "this" but without scaling so the contents - // of the source texture fall in the corresponding texels of the - // destination. - pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, - x, y, w, h, x, y, w, h); - } - pg.popFramebuffer(); - updateTexels(x, y, w, h); - } - - - // Copies source texture tex into this. - protected void copyTexture(int texTarget, int texName, - int texWidth, int texHeight, - int x, int y, int w, int h, boolean scale) { - if (tempFbo == null) { - tempFbo = new FrameBuffer(parent, glWidth, glHeight); - } - - // This texture is the color (destination) buffer of the FBO. - tempFbo.setColorBuffer(this); - tempFbo.disableDepthTest(); - - // FBO copy: - pg.pushFramebuffer(); - pg.setFramebuffer(tempFbo); - if (scale) { - // Rendering tex into "this", and scaling the source rectangle - // to cover the entire destination region. - pgl.drawTexture(texTarget, texName, texWidth, texHeight, - x, y, w, h, 0, 0, width, height); - - } else { - // Rendering tex into "this" but without scaling so the contents - // of the source texture fall in the corresponding texels of the - // destination. - pgl.drawTexture(texTarget, texName, texWidth, texHeight, - x, y, w, h, x, y, w, h); - } - pg.popFramebuffer(); - updateTexels(x, y, w, h); - } - - - protected void copyObject(Texture src) { - // The OpenGL texture of this object is replaced with the one from the - // source object, so we delete the former to avoid resource wasting. - release(); - - width = src.width; - height = src.height; - - parent = src.parent; - pg = src.pg; - - glName = src.glName; - glTarget = src.glTarget; - glFormat = src.glFormat; - glMinFilter = src.glMinFilter; - glMagFilter = src.glMagFilter; - - glWidth= src.glWidth; - glHeight = src.glHeight; - - usingMipmaps = src.usingMipmaps; - usingRepeat = src.usingRepeat; - maxTexcoordU = src.maxTexcoordU; - maxTexcoordV = src.maxTexcoordV; - - flippedX = src.flippedX; - flippedY = src.flippedY; - } - - - /////////////////////////////////////////////////////////// - - // Parameter handling - - - public Parameters getParameters() { - Parameters res = new Parameters(); - - if (glTarget == PGL.TEXTURE_2D) { - res.target = TEX2D; - } - - if (glFormat == PGL.RGB) { - res.format = RGB; - } else if (glFormat == PGL.RGBA) { - res.format = ARGB; - } else if (glFormat == PGL.ALPHA) { - res.format = ALPHA; - } - - if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.NEAREST) { - res.sampling = POINT; - res.mipmaps = false; - } else if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.LINEAR) { - res.sampling = LINEAR; - res.mipmaps = false; - } else if (glMagFilter == PGL.NEAREST && - glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { - res.sampling = LINEAR; - res.mipmaps = true; - } else if (glMagFilter == PGL.LINEAR && glMinFilter == PGL.LINEAR) { - res.sampling = BILINEAR; - res.mipmaps = false; - } else if (glMagFilter == PGL.LINEAR && - glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { - res.sampling = BILINEAR; - res.mipmaps = true; - } else if (glMagFilter == PGL.LINEAR && - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { - res.sampling = TRILINEAR; - res.mipmaps = true; - } - - if (glWrapS == PGL.CLAMP_TO_EDGE) { - res.wrapU = CLAMP; - } else if (glWrapS == PGL.REPEAT) { - res.wrapU = REPEAT; - } - - if (glWrapT == PGL.CLAMP_TO_EDGE) { - res.wrapV = CLAMP; - } else if (glWrapT == PGL.REPEAT) { - res.wrapV = REPEAT; - } - - return res; - } - - - /** - * Sets texture target and internal format according to the target and - * type specified. - * @param target int - * @param params GLTextureParameters - */ - protected void setParameters(Parameters params) { - if (params.target == TEX2D) { - glTarget = PGL.TEXTURE_2D; - } else { - throw new RuntimeException("Unknown texture target"); - } - - if (params.format == RGB) { - glFormat = PGL.RGB; - } else if (params.format == ARGB) { - glFormat = PGL.RGBA; - } else if (params.format == ALPHA) { - glFormat = PGL.ALPHA; - } else { - throw new RuntimeException("Unknown texture format"); - } - - if (params.sampling == POINT) { - glMagFilter = PGL.NEAREST; - glMinFilter = PGL.NEAREST; - } else if (params.sampling == LINEAR) { - glMagFilter = PGL.NEAREST; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (params.sampling == BILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; - } else if (params.sampling == TRILINEAR) { - glMagFilter = PGL.LINEAR; - glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? - PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; - } else { - throw new RuntimeException("Unknown texture filtering mode"); - } - - if (params.wrapU == CLAMP) { - glWrapS = PGL.CLAMP_TO_EDGE; - } else if (params.wrapU == REPEAT) { - glWrapS = PGL.REPEAT; - } else { - throw new RuntimeException("Unknown wrapping mode"); - } - - if (params.wrapV == CLAMP) { - glWrapT = PGL.CLAMP_TO_EDGE; - } else if (params.wrapV == REPEAT) { - glWrapT = PGL.REPEAT; - } else { - throw new RuntimeException("Unknown wrapping mode"); - } - - usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || - glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; - - usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; - - flippedX = false; - flippedY = false; - } - - - /////////////////////////////////////////////////////////////////////////// - - // Parameters object - - - /** - * This class stores the parameters for a texture: target, internal format, - * minimization filter and magnification filter. - */ - static public class Parameters { - /** - * Texture target. - */ - public int target; - - /** - * Texture internal format. - */ - public int format; - - /** - * Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR). - */ - public int sampling; - - /** - * Use mipmaps or not. - */ - public boolean mipmaps; - - /** - * Wrapping mode along U. - */ - public int wrapU; - - /** - * Wrapping mode along V. - */ - public int wrapV; - - /** - * Sets all the parameters to default values. - */ - public Parameters() { - this.target = TEX2D; - this.format = ARGB; - this.sampling = BILINEAR; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format) { - this.target = TEX2D; - this.format = format; - this.sampling = BILINEAR; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format, int sampling) { - this.target = TEX2D; - this.format = format; - this.sampling = sampling; - this.mipmaps = true; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(int format, int sampling, boolean mipmaps) { - this.target = TEX2D; - this.format = format; - this.sampling = sampling; - this.mipmaps = mipmaps; - this.wrapU = CLAMP; - this.wrapV = CLAMP; - } - - public Parameters(Parameters src) { - set(src); - } - - public void set(int format) { - this.format = format; - } - - public void set(int format, int sampling) { - this.format = format; - this.sampling = sampling; - } - - public void set(int format, int sampling, boolean mipmaps) { - this.format = format; - this.sampling = sampling; - this.mipmaps = mipmaps; - } - - public void set(Parameters src) { - this.target = src.target; - this.format = src.format; - this.sampling = src.sampling; - this.mipmaps = src.mipmaps; - this.wrapU = src.wrapU; - this.wrapV = src.wrapV; - } - } - - /** - * This class stores a buffer copied from the buffer source. - * - */ - protected class BufferData { - int w, h; - // Native buffer object. - Object natBuf; - // Buffer viewed as int. - IntBuffer rgbBuf; - - BufferData(Object nat, IntBuffer rgb, int w, int h) { - natBuf = nat; - rgbBuf = rgb; - this.w = w; - this.h = h; - } - - void dispose() { - try { - // Disposing the native buffer. - disposeBufferMethod.invoke(bufferSource, new Object[] { natBuf }); - natBuf = null; - rgbBuf = null; - } catch (Exception e) { - e.printStackTrace(); - } - } - } -} +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2011-12 Ben Fry and Casey Reas + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.opengl; + +import processing.core.PApplet; +import processing.core.PConstants; +import processing.core.PGraphics; +import processing.core.PImage; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +/** + * This class wraps an OpenGL texture. + * By Andres Colubri + * + */ +public class Texture implements PConstants { + // texture constants + + /** + * Texture with normalized UV. + */ + protected static final int TEX2D = 0; + /** + * Texture with un-normalized UV. + */ + protected static final int TEXRECT = 1; + + /** Point sampling: both magnification and minification filtering are set + * to nearest */ + protected static final int POINT = 2; + /** Linear sampling: magnification filtering is nearest, minification set + * to linear */ + protected static final int LINEAR = 3; + /** Bilinear sampling: both magnification filtering is set to linear and + * minification either to linear-mipmap-nearest (linear interplation is used + * within a mipmap, but not between different mipmaps). */ + protected static final int BILINEAR = 4; + /** Trilinear sampling: magnification filtering set to linear, minification to + * linear-mipmap-linear, which offers the best mipmap quality since linear + * interpolation to compute the value in each of two maps and then + * interpolates linearly between these two value. */ + protected static final int TRILINEAR = 5; + + public int width, height; + + public int glName; + public int glTarget; + public int glFormat; + public int glMinFilter; + public int glMagFilter; + public int glWrapS; + public int glWrapT; + public int glWidth; + public int glHeight; + + protected PApplet parent; // The Processing applet + protected PGraphicsOpenGL pg; // The main renderer + protected PGL pgl; // The interface between Processing and OpenGL. + protected PGL.Context context; // The context that created this texture. + + protected boolean usingMipmaps; + protected boolean usingRepeat; + protected float maxTexcoordU; + protected float maxTexcoordV; + protected boolean bound; + + protected boolean invertedX; + protected boolean invertedY; + + protected FrameBuffer tempFbo = null; + + /** Modified portion of the texture */ + protected boolean modified; + protected int mx1, my1, mx2, my2; + + protected Object bufferSource; + protected LinkedList bufferCache = null; + protected Method disposeBufferMethod; + public static final int MAX_BUFFER_CACHE_SIZE = 3; + + //////////////////////////////////////////////////////////// + + // Constructors. + + + /** + * Creates an instance of PTexture with size width x height. The texture is + * initialized (empty) to that size. + * @param parent PApplet + * @param width int + * @param height int + */ + public Texture(PApplet parent, int width, int height) { + this(parent, width, height, new Parameters()); + } + + + /** + * Creates an instance of PTexture 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 Parameters + */ + public Texture(PApplet parent, int width, int height, Object params) { + this.parent = parent; + + pg = (PGraphicsOpenGL)parent.g; + pgl = pg.pgl; + context = pgl.createEmptyContext(); + + glName = 0; + + init(width, height, (Parameters)params); + } + + + @Override + protected void finalize() throws Throwable { + try { + if (glName != 0) { + pg.finalizeTextureObject(glName, context.id()); + } + } finally { + super.finalize(); + } + } + + + //////////////////////////////////////////////////////////// + + // Init, resize methods + + + /** + * 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) { + Parameters params; + if (0 < glName) { + // Re-initializing a pre-existing texture. + // We use the current parameters as default: + params = getParameters(); + } else { + // Just built-in default parameters otherwise: + params = new Parameters(); + } + init(width, height, params); + } + + + /** + * 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, Parameters params) { + setParameters(params); + setSize(width, height); + allocate(); + } + + + public void resize(int wide, int high) { + // Marking the texture object as finalized so it is deleted + // when creating the new texture. + release(); + + // Creating new texture with the appropriate size. + Texture tex = new Texture(parent, wide, high, getParameters()); + + // Copying the contents of this texture into tex. + tex.set(this); + + // Now, overwriting "this" with tex. + copyObject(tex); + + // Nullifying some utility objects so they are recreated with the + // appropriate size when needed. + tempFbo = null; + } + + + /** + * Returns true if the texture has been initialized. + * @return boolean + */ + public boolean available() { + return 0 < glName; + } + + + //////////////////////////////////////////////////////////// + + // Set methods + + + public void set(PImage img) { + Texture tex = (Texture)pg.getCache(img); + set(tex); + } + + + public void set(PImage img, int x, int y, int w, int h) { + Texture tex = (Texture)pg.getCache(img); + set(tex, x, y, w, h); + } + + + public void set(Texture tex) { + copyTexture(tex, 0, 0, tex.width, tex.height, true); + } + + + public void set(Texture tex, int x, int y, int w, int h) { + copyTexture(tex, x, y, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, + int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, true); + } + + + public void set(int texTarget, int texName, int texWidth, int texHeight, + int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, true); + } + + + public void set(int[] pixels) { + set(pixels, 0, 0, width, height, ARGB); + } + + + public void set(int[] pixels, int format) { + set(pixels, 0, 0, width, height, format); + } + + + public void set(int[] pixels, int x, int y, int w, int h) { + set(pixels, x, y, w, h, ARGB); + } + + + public void set(int[] pixels, int x, int y, int w, int h, int format) { + if (pixels == null) { + pixels = null; + PGraphics.showWarning("The pixels array is null."); + return; + } + if (pixels.length != w * h) { + PGraphics.showWarning("The pixels array has a length of " + + pixels.length + ", but it should be " + w * h); + return; + } + + if (pixels.length == 0) { + // Nothing to do (means that w == h == 0) but not an erroneous situation + return; + } + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + pgl.bindTexture(glTarget, glName); + + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + // Automatic mipmap generation. + int[] rgbaPixels = new int[w * h]; + convertToRGBA(pixels, rgbaPixels, format, w, h); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + IntBuffer.wrap(rgbaPixels)); + pgl.generateMipmap(glTarget); + rgbaPixels = null; + } else { + // TODO: finish manual mipmap generation, replacing Bitmap with AWT's BufferedImage, + // making it work in npot textures (embed npot tex into larger pot tex?), subregions, + // and moving GLUtils.texImage2D (originally from Android SDK) into PGL. + // Actually, this whole code should go into PGL, so the Android implementation can + // use Bitmap, and desktop use BufferedImage. + + /* + if (w != width || h != height) { + System.err.println("Sorry but I don't know how to generate mipmaps for a subregion."); + return; + } + + // Code by Mike Miller obtained from here: + // http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/ + int w0 = glWidth; + int h0 = glHeight; + int[] argbPixels = new int[w0 * h0]; + convertToARGB(pixels, argbPixels, format); + int level = 0; + int denom = 1; + + // We create a Bitmap because then we use its built-in filtered downsampling + // functionality. + Bitmap bitmap = Bitmap.createBitmap(w0, h0, Config.ARGB_8888); + bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0); + + while (w0 >= 1 || h0 >= 1) { + //First of all, generate the texture from our bitmap and set it to the according level + GLUtils.texImage2D(glTarget, level, bitmap, 0); + + // We are done. + if (w0 == 1 && h0 == 1) { + break; + } + + // Increase the mipmap level + level++; + denom *= 2; + + // Downsampling bitmap. We must eventually arrive to the 1x1 level, + // and if the width and height are different, there will be a few 1D + // texture levels just before. + // This update formula also allows for NPOT resolutions. + w0 = PApplet.max(1, PApplet.floor((float)glWidth / denom)); + h0 = PApplet.max(1, PApplet.floor((float)glHeight / denom)); + // (see getScaledInstance in AWT Image) + Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true); + + // Clean up + bitmap.recycle(); + bitmap = bitmap2; + } + */ + + int[] rgbaPixels = new int[w * h]; + convertToRGBA(pixels, rgbaPixels, format, w, h); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + IntBuffer.wrap(rgbaPixels)); + rgbaPixels = null; + } + } else { + int[] rgbaPixels = new int[w * h]; + convertToRGBA(pixels, rgbaPixels, format, w, h); + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + IntBuffer.wrap(rgbaPixels)); + rgbaPixels = null; + } + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + + updateTexels(x, y, w, h); + } + + + //////////////////////////////////////////////////////////// + + // Native set methods + + + public void setNative(int[] pixels) { + setNative(pixels, 0, 0, width, height); + } + + + public void setNative(int[] pixels, int x, int y, int w, int h) { + setNative(IntBuffer.wrap(pixels), x, y, w, h); + } + + + public void setNative(IntBuffer pixels, int x, int y, int w, int h) { + if (pixels == null) { + pixels = null; + PGraphics.showWarning("The pixel buffer is null."); + return; + } + if (pixels.capacity() != w * h) { + PGraphics.showWarning("The pixels array has a length of " + + pixels.capacity() + ", but it should be " + w * h); + return; + } + + if (pixels.capacity() == 0) { + // Nothing to do (means that w == h == 0) but not an erroneous situation + return; + } + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + pgl.bindTexture(glTarget, glName); + + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixels); + pgl.generateMipmap(glTarget); + } else { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixels); + } + } else { + pgl.texSubImage2D(glTarget, 0, x, y, w, h, PGL.RGBA, PGL.UNSIGNED_BYTE, + pixels); + } + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + + updateTexels(x, y, w, h); + } + + + //////////////////////////////////////////////////////////// + + // Get methods + + + /** + * Copy texture to pixels. Involves video memory to main memory transfer (slow). + */ + public void get(int[] pixels) { + if (pixels == null) { + throw new RuntimeException("Trying to copy texture to null pixels array"); + } + if (pixels.length != width * height) { + throw new RuntimeException("Trying to copy texture to pixels array of " + + "wrong size"); + } + + if (tempFbo == null) { + tempFbo = new FrameBuffer(parent, glWidth, glHeight); + } + + // Attaching the texture to the color buffer of a FBO, binding the FBO and + // reading the pixels from the current draw buffer (which is the color + // buffer of the FBO). + tempFbo.setColorBuffer(this); + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); + tempFbo.readPixels(); + pg.popFramebuffer(); + + tempFbo.getPixels(pixels); + convertToARGB(pixels); + + if (invertedX) flipArrayOnX(pixels, 1); + if (invertedY) flipArrayOnY(pixels, 1); + } + + + /** + * Copies the contents of the texture to the pixels array. + * @param pixels + */ + public void loadPixels(int[] pixels) { + if (hasBuffers()) { + // Updates the texture AND the pixels array of the image at the same time, + // getting the pixels directly from the buffer data (and thus avoiding + // expensive transfer between video and main memory). + bufferUpdate(pixels); + } + + if (isModified()) { + // Regular pixel copy from texture. + get(pixels); + } + + setModified(false); + } + + + //////////////////////////////////////////////////////////// + + // Put methods (the source texture is not resized to cover the entire + // destination). + + + public void put(Texture tex) { + copyTexture(tex, 0, 0, tex.width, tex.height, false); + } + + + public void put(Texture tex, int x, int y, int w, int h) { + copyTexture(tex, x, y, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, + int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, 0, 0, w, h, false); + } + + + public void put(int texTarget, int texName, int texWidth, int texHeight, + int target, int tex, int x, int y, int w, int h) { + copyTexture(texTarget, texName, texWidth, texHeight, x, y, w, h, false); + } + + + //////////////////////////////////////////////////////////// + + // Get OpenGL parameters + + + /** + * Returns true or false whether or not the texture is using mipmaps. + * @return boolean + */ + public boolean usingMipmaps() { + return usingMipmaps; + } + + + public void usingMipmaps(boolean mipmaps, int sampling) { + if (mipmaps) { + if (glMinFilter != PGL.LINEAR_MIPMAP_NEAREST && + glMinFilter != PGL.LINEAR_MIPMAP_LINEAR) { + if (sampling == POINT) { + glMagFilter = PGL.NEAREST; + glMinFilter = PGL.NEAREST; + } else if (sampling == LINEAR) { + glMagFilter = PGL.NEAREST; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (sampling == BILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (sampling == TRILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = + PGL.MIPMAPS_ENABLED ? PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; + } else { + throw new RuntimeException("Unknown texture filtering mode"); + } + } + + usingMipmaps = true; + } else { + if (glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { + glMinFilter = PGL.LINEAR; + } + usingMipmaps = false; + } + + bind(); + pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); + if (usingMipmaps) { + if (PGraphicsOpenGL.autoMipmapGenSupported) { + pgl.generateMipmap(glTarget); + } else { + // TODO: need manual generation here.. + } + } + unbind(); + } + + + /** + * Returns true or false whether or not the texture is using repeat wrap mode + * along either U or V directions. + * @return boolean + */ + public boolean usingRepeat() { + return usingRepeat; + } + + + public void usingRepeat(boolean repeat) { + if (repeat) { + glWrapS = PGL.REPEAT; + glWrapT = PGL.REPEAT; + usingRepeat = true; + } else { + glWrapS = PGL.CLAMP_TO_EDGE; + glWrapT = PGL.CLAMP_TO_EDGE; + usingRepeat = false; + } + + bind(); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); + unbind(); + } + + + /** + * Returns the maximum possible value for the texture coordinate U + * (horizontal). + * @return float + */ + public float maxTexcoordU() { + return maxTexcoordU; + } + + + /** + * Returns the maximum possible value for the texture coordinate V (vertical). + * @return float + */ + public float maxTexcoordV() { + return maxTexcoordV; + } + + + /** + * Returns true if the texture is inverted along the horizontal direction. + * @return boolean; + */ + public boolean invertedX() { + return invertedX; + } + + + /** + * Sets the texture as inverted or not along the horizontal direction. + * @param v boolean; + */ + public void invertedX(boolean v) { + invertedX = v; + } + + + /** + * Returns true if the texture is inverted along the vertical direction. + * @return boolean; + */ + public boolean invertedY() { + return invertedY; + } + + + /** + * Sets the texture as inverted or not along the vertical direction. + * @param v boolean; + */ + public void invertedY(boolean v) { + invertedY = v; + } + + + //////////////////////////////////////////////////////////// + + // Bind/unbind + + + public void bind() { + // Binding a texture automatically enables texturing for the + // texture target from that moment onwards. Unbinding the texture + // won't disable texturing. + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + } + pgl.bindTexture(glTarget, glName); + bound = true; + } + + + public void unbind() { + if (pgl.textureIsBound(glTarget, glName)) { + // We don't want to unbind another texture + // that might be bound instead of this one. + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + pgl.bindTexture(glTarget, 0); + pgl.disableTexturing(glTarget); + } else { + pgl.bindTexture(glTarget, 0); + } + } + bound = false; + } + + + public boolean bound() { + // A true result might not necessarily mean that texturing is enabled + // (a texture can be bound to the target, but texturing is disabled). + return bound; + } + + + ////////////////////////////////////////////////////////////// + + // Modified flag + + + public boolean isModified() { + return modified; + } + + + public void setModified() { + modified = true; + } + + + public void setModified(boolean m) { + modified = m; + } + + + public int getModifiedX1() { + return mx1; + } + + + public int getModifiedX2() { + return mx2; + } + + + public int getModifiedY1() { + return my1; + } + + + public int getModifiedY2() { + return my2; + } + + + public void updateTexels() { + updateTexelsImpl(0, 0, width, height); + } + + + public void updateTexels(int x, int y, int w, int h) { + updateTexelsImpl(x, y, w, h); + } + + + protected void updateTexelsImpl(int x, int y, int w, int h) { + int x2 = x + w; + int y2 = y + h; + + if (!modified) { + mx1 = PApplet.max(0, x); + mx2 = PApplet.min(width - 1, x2); + my1 = PApplet.max(0, y); + my2 = PApplet.min(height - 1, y2); + modified = true; + + } else { + if (x < mx1) mx1 = PApplet.max(0, x); + if (x > mx2) mx2 = PApplet.min(width - 1, x); + if (y < my1) my1 = PApplet.max(0, y); + if (y > my2) my2 = y; + + if (x2 < mx1) mx1 = PApplet.max(0, x2); + if (x2 > mx2) mx2 = PApplet.min(width - 1, x2); + if (y2 < my1) my1 = PApplet.max(0, y2); + if (y2 > my2) my2 = PApplet.min(height - 1, y2); + } + } + + + //////////////////////////////////////////////////////////// + + // Buffer sink interface. + + + public void setBufferSource(Object source) { + bufferSource = source; + getSourceMethods(); + } + + + public void copyBufferFromSource(Object natRef, ByteBuffer byteBuf, + int w, int h) { + if (bufferCache == null) { + bufferCache = new LinkedList(); + } + + if (bufferCache.size() + 1 <= MAX_BUFFER_CACHE_SIZE) { + bufferCache.add(new BufferData(natRef, byteBuf.asIntBuffer(), w, h)); + } else { + // The buffer cache reached the maximum size, so we just dispose + // the new buffer. + try { + disposeBufferMethod.invoke(bufferSource, new Object[] { natRef }); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + + public boolean hasBufferSource() { + return bufferSource != null; + } + + + public boolean hasBuffers() { + return bufferSource != null && bufferCache != null && + 0 < bufferCache.size(); + } + + + protected boolean bufferUpdate() { + BufferData data = null; + try { + data = bufferCache.remove(0); + } catch (NoSuchElementException ex) { + PGraphics.showWarning("Don't have pixel data to copy to texture"); + } + + if (data != null) { + if ((data.w != width) || (data.h != height)) { + init(data.w, data.h); + } + setNative(data.rgbBuf, 0, 0, width, height); + + data.dispose(); + + return true; + } else { + return false; + } + } + + + protected boolean bufferUpdate(int[] pixels) { + BufferData data = null; + try { + data = bufferCache.remove(0); + } catch (NoSuchElementException ex) { + PGraphics.showWarning("Don't have pixel data to copy to texture"); + } + + if (data != null) { + if ((data.w != width) || (data.h != height)) { + init(data.w, data.h); + } + setNative(data.rgbBuf, 0, 0, width, height); + + data.rgbBuf.get(pixels); + convertToARGB(pixels); + + data.dispose(); + + return true; + } else { + return false; + } + } + + + protected void getSourceMethods() { + try { + disposeBufferMethod = bufferSource.getClass(). + getMethod("disposeBuffer", new Class[] { Object.class }); + } catch (Exception e) { + throw new RuntimeException("Provided source object doesn't have a " + + "disposeBuffer method."); + } + } + + + //////////////////////////////////////////////////////////// + + // Utilities + + + /** + * 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; + } + } + + + /** + * Reorders a pixel array in the given format into the order required by + * OpenGL (RGBA). Both arrays are assumed to be of the same length. The width + * and height parameters are used in the YUV420 to RBGBA conversion. + * @param intArray int[] + * @param tIntArray int[] + * @param arrayFormat int + * @param w int + * @param h int + */ + protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat, + int w, int h) { + if (PGL.BIG_ENDIAN) { + switch (arrayFormat) { + case ALPHA: + + // Converting from xxxA into RGBA. RGB is set to white + // (0xFFFFFF, i.e.: (255, 255, 255)) + for (int i = 0; i< intArray.length; i++) { + tIntArray[i] = 0xFFFFFF00 | intArray[i]; + } + break; + + case RGB: + + // Converting xRGB into RGBA. A is set to 0xFF (255, full opacity). + for (int i = 0; i< intArray.length; i++) { + int pixel = intArray[i]; + tIntArray[i] = (pixel << 8) | 0xFF; + } + break; + + case ARGB: + + // Converting ARGB into RGBA. Shifting RGB to 8 bits to the left, + // and bringing A to the first byte. + for (int i = 0; i< intArray.length; i++) { + int pixel = intArray[i]; + tIntArray[i] = (pixel << 8) | ((pixel >> 24) & 0xFF); + } + 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: + + // Converting xxxA into ARGB, with RGB set to white. + for (int i = 0; i< intArray.length; i++) { + tIntArray[i] = (intArray[i] << 24) | 0x00FFFFFF; + } + break; + + case RGB: + + // We need to convert xRGB into ABGR, + // so R and B must be swapped, and the x just made 0xFF. + for (int i = 0; i< intArray.length; i++) { + int pixel = intArray[i]; + tIntArray[i] = 0xFF000000 | + ((pixel & 0xFF) << 16) | + ((pixel & 0xFF0000) >> 16) | + (pixel & 0x0000FF00); + } + break; + + case ARGB: + + // We need to convert ARGB into ABGR, + // so R and B must be swapped, A and G just brought back in. + for (int i = 0; i < intArray.length; i++) { + int pixel = intArray[i]; + tIntArray[i] = ((pixel & 0xFF) << 16) | + ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + } + break; + + } + + } + } + + + /** + * Reorders an OpenGL pixel array (RGBA) into ARGB. The array must be + * of size width * height. + * @param intArray int[] + */ + protected void convertToARGB(int[] intArray) { + int t = 0; + int p = 0; + if (PGL.BIG_ENDIAN) { + + // RGBA to ARGB conversion: shifting RGB 8 bits to the right, + // and placing A 24 bits to the left. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = intArray[p++]; + intArray[t++] = (pixel >> 8) | ((pixel << 24) & 0xFF000000); + } + } + + } else { + + // We have to convert ABGR into ARGB, so R and B must be swapped, + // A and G just brought back in. + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int pixel = intArray[p++]; + intArray[t++] = ((pixel & 0xFF) << 16) | + ((pixel & 0xFF0000) >> 16) | + (pixel & 0xFF00FF00); + + } + } + } + } + + + + /////////////////////////////////////////////////////////// + + // Allocate/release texture. + + + protected void setSize(int w, int h) { + width = w; + height = h; + + if (PGraphicsOpenGL.npotTexSupported) { + glWidth = w; + glHeight = h; + } else { + glWidth = PGL.nextPowerOfTwo(w); + glHeight = PGL.nextPowerOfTwo(h); + } + + if (glWidth > PGraphicsOpenGL.maxTextureSize || + glHeight > PGraphicsOpenGL.maxTextureSize) { + glWidth = glHeight = 0; + throw new RuntimeException("Image width and height cannot be" + + " larger than " + + PGraphicsOpenGL.maxTextureSize + + " with this graphics card."); + } + + // If non-power-of-two textures are not supported, and the specified width + // or height is non-power-of-two, then glWidth (glHeight) will be greater + // than w (h) because it is chosen to be the next power of two, and this + // quotient will give the appropriate maximum texture coordinate value given + // this situation. + maxTexcoordU = (float)width / glWidth; + maxTexcoordV = (float)height / glHeight; + } + + + /** + * Allocates the opengl texture object. + */ + protected void allocate() { + release(); // Just in the case this object is being re-allocated. + + boolean enabledTex = false; + if (!pgl.texturingIsEnabled(glTarget)) { + pgl.enableTexturing(glTarget); + enabledTex = true; + } + + context = pgl.getCurrentContext(); + glName = pg.createTextureObject(context.id()); + + pgl.bindTexture(glTarget, glName); + pgl.texParameteri(glTarget, PGL.TEXTURE_MIN_FILTER, glMinFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_MAG_FILTER, glMagFilter); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_S, glWrapS); + pgl.texParameteri(glTarget, PGL.TEXTURE_WRAP_T, glWrapT); + + // First, we use glTexImage2D to set the full size of the texture (glW/glH + // might be diff from w/h in the case that the GPU doesn't support NPOT + // textures) + pgl.texImage2D(glTarget, 0, glFormat, glWidth, glHeight, 0, + PGL.RGBA, PGL.UNSIGNED_BYTE, null); + + // Makes sure that the texture buffer in video memory doesn't contain + // any garbage. + pgl.initTexture(glTarget, PGL.RGBA, width, height); + + pgl.bindTexture(glTarget, 0); + if (enabledTex) { + pgl.disableTexturing(glTarget); + } + bound = false; + } + + + /** + * Marks the texture object for deletion. + */ + protected void release() { + if (glName != 0) { + pg.finalizeTextureObject(glName, context.id()); + glName = 0; + } + } + + + protected boolean contextIsOutdated() { + boolean outdated = !pgl.contextIsCurrent(context); + if (outdated) { + // Removing the texture object from the renderer's list so it + // doesn't get deleted by OpenGL. The texture object was + // automatically disposed when the old context was destroyed. + pg.removeTextureObject(glName, context.id()); + + // And then set the id to zero, so it doesn't try to be + // deleted when the object's finalizer is invoked by the GC. + glName = 0; + } + return outdated; + } + + + /////////////////////////////////////////////////////////// + + // Utilities. + + + // Copies source texture tex into this. + protected void copyTexture(Texture tex, int x, int y, int w, int h, + boolean scale) { + if (tex == null) { + throw new RuntimeException("Source texture is null"); + } + + if (tempFbo == null) { + tempFbo = new FrameBuffer(parent, glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, + x, y, w, h, 0, 0, width, height); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(tex.glTarget, tex.glName, tex.glWidth, tex.glHeight, + x, y, w, h, x, y, w, h); + } + pg.popFramebuffer(); + updateTexels(x, y, w, h); + } + + + // Copies source texture tex into this. + protected void copyTexture(int texTarget, int texName, + int texWidth, int texHeight, + int x, int y, int w, int h, boolean scale) { + if (tempFbo == null) { + tempFbo = new FrameBuffer(parent, glWidth, glHeight); + } + + // This texture is the color (destination) buffer of the FBO. + tempFbo.setColorBuffer(this); + tempFbo.disableDepthTest(); + + // FBO copy: + pg.pushFramebuffer(); + pg.setFramebuffer(tempFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, 0, 0, width, height); + + } else { + // Rendering tex into "this" but without scaling so the contents + // of the source texture fall in the corresponding texels of the + // destination. + pgl.drawTexture(texTarget, texName, texWidth, texHeight, + x, y, w, h, x, y, w, h); + } + pg.popFramebuffer(); + updateTexels(x, y, w, h); + } + + + protected void copyObject(Texture src) { + // The OpenGL texture of this object is replaced with the one from the + // source object, so we delete the former to avoid resource wasting. + release(); + + width = src.width; + height = src.height; + + parent = src.parent; + pg = src.pg; + + glName = src.glName; + glTarget = src.glTarget; + glFormat = src.glFormat; + glMinFilter = src.glMinFilter; + glMagFilter = src.glMagFilter; + + glWidth= src.glWidth; + glHeight = src.glHeight; + + usingMipmaps = src.usingMipmaps; + usingRepeat = src.usingRepeat; + maxTexcoordU = src.maxTexcoordU; + maxTexcoordV = src.maxTexcoordV; + + invertedX = src.invertedX; + invertedY = src.invertedY; + } + + + /////////////////////////////////////////////////////////// + + // Parameter handling + + + public Parameters getParameters() { + Parameters res = new Parameters(); + + if (glTarget == PGL.TEXTURE_2D) { + res.target = TEX2D; + } + + if (glFormat == PGL.RGB) { + res.format = RGB; + } else if (glFormat == PGL.RGBA) { + res.format = ARGB; + } else if (glFormat == PGL.ALPHA) { + res.format = ALPHA; + } + + if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.NEAREST) { + res.sampling = POINT; + res.mipmaps = false; + } else if (glMagFilter == PGL.NEAREST && glMinFilter == PGL.LINEAR) { + res.sampling = LINEAR; + res.mipmaps = false; + } else if (glMagFilter == PGL.NEAREST && + glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { + res.sampling = LINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.LINEAR && glMinFilter == PGL.LINEAR) { + res.sampling = BILINEAR; + res.mipmaps = false; + } else if (glMagFilter == PGL.LINEAR && + glMinFilter == PGL.LINEAR_MIPMAP_NEAREST) { + res.sampling = BILINEAR; + res.mipmaps = true; + } else if (glMagFilter == PGL.LINEAR && + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR) { + res.sampling = TRILINEAR; + res.mipmaps = true; + } + + if (glWrapS == PGL.CLAMP_TO_EDGE) { + res.wrapU = CLAMP; + } else if (glWrapS == PGL.REPEAT) { + res.wrapU = REPEAT; + } + + if (glWrapT == PGL.CLAMP_TO_EDGE) { + res.wrapV = CLAMP; + } else if (glWrapT == PGL.REPEAT) { + res.wrapV = REPEAT; + } + + return res; + } + + + /** + * Sets texture target and internal format according to the target and + * type specified. + * @param target int + * @param params GLTextureParameters + */ + protected void setParameters(Parameters params) { + if (params.target == TEX2D) { + glTarget = PGL.TEXTURE_2D; + } else { + throw new RuntimeException("Unknown texture target"); + } + + if (params.format == RGB) { + glFormat = PGL.RGB; + } else if (params.format == ARGB) { + glFormat = PGL.RGBA; + } else if (params.format == ALPHA) { + glFormat = PGL.ALPHA; + } else { + throw new RuntimeException("Unknown texture format"); + } + + if (params.sampling == POINT) { + glMagFilter = PGL.NEAREST; + glMinFilter = PGL.NEAREST; + } else if (params.sampling == LINEAR) { + glMagFilter = PGL.NEAREST; + glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? + PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (params.sampling == BILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? + PGL.LINEAR_MIPMAP_NEAREST : PGL.LINEAR; + } else if (params.sampling == TRILINEAR) { + glMagFilter = PGL.LINEAR; + glMinFilter = params.mipmaps && PGL.MIPMAPS_ENABLED ? + PGL.LINEAR_MIPMAP_LINEAR : PGL.LINEAR; + } else { + throw new RuntimeException("Unknown texture filtering mode"); + } + + if (params.wrapU == CLAMP) { + glWrapS = PGL.CLAMP_TO_EDGE; + } else if (params.wrapU == REPEAT) { + glWrapS = PGL.REPEAT; + } else { + throw new RuntimeException("Unknown wrapping mode"); + } + + if (params.wrapV == CLAMP) { + glWrapT = PGL.CLAMP_TO_EDGE; + } else if (params.wrapV == REPEAT) { + glWrapT = PGL.REPEAT; + } else { + throw new RuntimeException("Unknown wrapping mode"); + } + + usingMipmaps = glMinFilter == PGL.LINEAR_MIPMAP_NEAREST || + glMinFilter == PGL.LINEAR_MIPMAP_LINEAR; + + usingRepeat = glWrapS == PGL.REPEAT || glWrapT == PGL.REPEAT; + + invertedX = false; + invertedY = false; + } + + + /////////////////////////////////////////////////////////////////////////// + + // Parameters object + + + /** + * This class stores the parameters for a texture: target, internal format, + * minimization filter and magnification filter. + */ + static public class Parameters { + /** + * Texture target. + */ + public int target; + + /** + * Texture internal format. + */ + public int format; + + /** + * Texture filtering (POINT, LINEAR, BILINEAR or TRILINEAR). + */ + public int sampling; + + /** + * Use mipmaps or not. + */ + public boolean mipmaps; + + /** + * Wrapping mode along U. + */ + public int wrapU; + + /** + * Wrapping mode along V. + */ + public int wrapV; + + /** + * Sets all the parameters to default values. + */ + public Parameters() { + this.target = TEX2D; + this.format = ARGB; + this.sampling = BILINEAR; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format) { + this.target = TEX2D; + this.format = format; + this.sampling = BILINEAR; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling) { + this.target = TEX2D; + this.format = format; + this.sampling = sampling; + this.mipmaps = true; + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling, boolean mipmaps) { + this.target = TEX2D; + this.format = format; + this.mipmaps = mipmaps; + if (sampling == TRILINEAR && !mipmaps) { + this.sampling = BILINEAR; + } else { + this.sampling = sampling; + } + this.wrapU = CLAMP; + this.wrapV = CLAMP; + } + + public Parameters(int format, int sampling, boolean mipmaps, int wrap) { + this.target = TEX2D; + this.format = format; + this.mipmaps = mipmaps; + if (sampling == TRILINEAR && !mipmaps) { + this.sampling = BILINEAR; + } else { + this.sampling = sampling; + } + this.wrapU = wrap; + this.wrapV = wrap; + } + + public Parameters(Parameters src) { + set(src); + } + + public void set(int format) { + this.format = format; + } + + public void set(int format, int sampling) { + this.format = format; + this.sampling = sampling; + } + + public void set(int format, int sampling, boolean mipmaps) { + this.format = format; + this.sampling = sampling; + this.mipmaps = mipmaps; + } + + public void set(Parameters src) { + this.target = src.target; + this.format = src.format; + this.sampling = src.sampling; + this.mipmaps = src.mipmaps; + this.wrapU = src.wrapU; + this.wrapV = src.wrapV; + } + } + + /** + * This class stores a buffer copied from the buffer source. + * + */ + protected class BufferData { + int w, h; + // Native buffer object. + Object natBuf; + // Buffer viewed as int. + IntBuffer rgbBuf; + + BufferData(Object nat, IntBuffer rgb, int w, int h) { + natBuf = nat; + rgbBuf = rgb; + this.w = w; + this.h = h; + } + + void dispose() { + try { + // Disposing the native buffer. + disposeBufferMethod.invoke(bufferSource, new Object[] { natBuf }); + natBuf = null; + rgbBuf = null; + } catch (Exception e) { + e.printStackTrace(); + } + } + } +}