diff --git a/android/core/src/processing/core/PApplet.java b/android/core/src/processing/core/PApplet.java index f6a1d4c67..8661dcf11 100644 --- a/android/core/src/processing/core/PApplet.java +++ b/android/core/src/processing/core/PApplet.java @@ -736,12 +736,12 @@ public class PApplet extends Activity implements PConstants, Runnable { // We don't need this for the time being since we are using GLES 1.1. // setEGLContextFactory(g3.getContextFactory()); - String depth = sketchColorDepth(); + String depth = sketchColordepth(); if (!depth.equals(DEFAULT_COLOR_DEPTH)) { // Setting user specified color depth. Otherwise, we let the // device to choose the configuration it pleases. - if (sketchTranslucentSurface()) { + if (sketchTranslucency()) { surfaceHolder.setFormat(PixelFormat.TRANSLUCENT); } @@ -925,11 +925,11 @@ public class PApplet extends Activity implements PConstants, Runnable { } - public boolean sketchTranslucentSurface() { + public boolean sketchTranslucency() { return true; } - public String sketchColorDepth() { + public String sketchColordepth() { return DEFAULT_COLOR_DEPTH; } diff --git a/android/core/src/processing/core/PFont.java b/android/core/src/processing/core/PFont.java index 3e5614663..d95063559 100644 --- a/android/core/src/processing/core/PFont.java +++ b/android/core/src/processing/core/PFont.java @@ -153,13 +153,7 @@ public class PFont implements PConstants { protected PTexture[] textures = null; protected int currentTex; protected int lastTex; - - /* - protected int[] texIDList = null; - protected int[] texWidthList = null; - protected int[] texHeightList = null; - */ - + public PFont() { } // for subclasses @@ -688,23 +682,45 @@ public class PFont implements PConstants { lineHeight = 0; } - public int addTexture() { - int w = maxTexWidth; - int h = maxTexHeight; - + public boolean addTexture() { + int w, h; + boolean resize; + + w = maxTexWidth; + if (-1 < currentTex && textures[currentTex].glHeight < maxTexHeight) { + // The height of the current texture is less than the maximum, this + // means we can replace it with a larger texture. + h = PApplet.min(2 * textures[currentTex].glHeight, maxTexHeight); + resize = true; + } else { + h = PApplet.max(512, maxTexHeight / 4); + resize = false; + } + PTexture tex = new PTexture(parent, w, h, new PTexture.Parameters(ARGB, NEAREST)); if (textures == null) { textures = new PTexture[1]; textures[0] = tex; + currentTex = 0; + } else if (resize) { + // Replacing old smaller texture with larger one. + // But first we must copy the contents of the older + // texture into the new one. + PTexture tex0 = textures[currentTex]; + tex.put(tex0); + textures[currentTex] = tex; } else { + // Adding new texture to the list. PTexture[] tmp = textures; textures = new PTexture[textures.length + 1]; PApplet.arrayCopy(tmp, textures, tmp.length); textures[tmp.length] = tex; + currentTex = textures.length - 1; } - currentTex = textures.length - 1; - return currentTex; + lastTex = currentTex; + + return resize; } public void setFirstTexture() { @@ -712,10 +728,9 @@ public class PFont implements PConstants { } public void setTexture(int idx) { - if (0 <= idx && idx < textures.length) { - GL10 gl = ((PGraphicsAndroid3D)parent.g).gl; - gl.glBindTexture(textures[idx].glTarget, textures[idx].glID); + if (0 <= idx && idx < textures.length) { currentTex = idx; + textures[currentTex].bind(); } } @@ -726,7 +741,17 @@ public class PFont implements PConstants { glyphs[i].addToTexture(); } } - + + public void updateGlyphsTexCoords() { + // loop over current glyphs. + for (int i = 0; i < glyphCount; i++) { + if (glyphs[i].texture != null && glyphs[i].texture.tex == currentTex) { + glyphs[i].texture.updateUV(); + } + } + } + + protected void recreateResource(PGraphicsAndroid3D renderer) { } @@ -898,8 +923,6 @@ public class PFont implements PConstants { // Adds this glyph to the opengl texture in PFont. protected void addToTexture() { // Converting the pixels array from the PImage into a valid RGBA array for OpenGL. - GL10 gl = ((PGraphicsAndroid3D)parent.g).gl; - int[] rgba = new int[width * height]; int t = 0; int p = 0; @@ -913,6 +936,7 @@ public class PFont implements PConstants { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { rgba[t++] = (image.pixels[p++] << 24) | 0x00FFFFFF; + //rgba[t++] = 0xFF00FF00; } } } @@ -925,14 +949,22 @@ public class PFont implements PConstants { lineHeight = 0; } lineHeight = Math.max(lineHeight, height); + + boolean resized = false; if (offsetY + lineHeight > textures[currentTex].glHeight) { // We run out of space in the current texture, we add a new texture: - lastTex = addTexture(); - - // Reseting texture coordinates and line. - offsetX = 0; - offsetY = 0; - lineHeight = 0; + resized = addTexture(); + if (resized) { + // Because the current texture has been resized, we need to + // update the UV coordinates of all the glyphs associated to it: + updateGlyphsTexCoords(); + } else { + // A new texture has been created. Reseting texture coordinates + // and line. + offsetX = 0; + offsetY = 0; + lineHeight = 0; + } } if (lastTex == -1) { @@ -940,12 +972,14 @@ public class PFont implements PConstants { } // We assume GL10.GL_TEXTURE_2D is enabled at this point. - if (currentTex != lastTex) { + // We reset texture when it was resized because even the + // texture index didn't change, the texture is a new one + // in fact, so we need to rebind. + if (currentTex != lastTex || resized) { setTexture(lastTex); } - - gl.glTexSubImage2D(textures[currentTex].glTarget, 0, offsetX, offsetY, width, height, - GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(rgba)); + + textures[currentTex].setTexels(offsetX, offsetY, width, height, rgba); texture = new TextureInfo(currentTex, offsetX, offsetY + height, width, -height); offsetX += width; @@ -965,6 +999,13 @@ public class PFont implements PConstants { v1 = (float)cropY / (float)textures[n].glHeight; } + void updateUV() { + u0 = (float)crop[0] / (float)textures[tex].glWidth; + u1 = u0 + (float)crop[2] / (float)textures[tex].glWidth; + v0 = (float)(crop[1] + crop[3]) / (float)textures[tex].glHeight; + v1 = (float)crop[1] / (float)textures[tex].glHeight; + } + public int tex; public int[] crop; public float u0, u1; diff --git a/android/core/src/processing/core/PFramebuffer.java b/android/core/src/processing/core/PFramebuffer.java index f40c82081..310f160cc 100644 --- a/android/core/src/processing/core/PFramebuffer.java +++ b/android/core/src/processing/core/PFramebuffer.java @@ -32,6 +32,8 @@ import javax.microedition.khronos.opengles.GL11ExtensionPack; * When created with onscreen == true, it represents the normal * framebuffer. Needed by the stack mechanism in A3D to return * to onscreen rendering after a sequence of pushFramebuffer calls. + * It transparently handles the situations when the FBO extension is + * not available. * * By Andres Colubri. */ @@ -88,14 +90,15 @@ public class PFramebuffer implements PConstants { initFramebuffer(w, h); + pixelBuffer = IntBuffer.allocate(width * height); + pixelBuffer.rewind(); + if (!screenFb && !FboMode) { // When FBOs are not available, rendering to texture is implemented by saving a portion of // the screen, doing the "offscreen" rendering on this portion, copying the screen color // buffer to the texture bound as color buffer to this PFramebuffer object and then drawing // the backup texture back on the screen. - backupTexture = new PTexture(parent, width, height, new PTexture.Parameters(ARGB, NEAREST)); - pixelBuffer = IntBuffer.allocate(width * height); - pixelBuffer.rewind(); + backupTexture = new PTexture(parent, width, height, new PTexture.Parameters(ARGB, NEAREST)); } try { @@ -297,6 +300,15 @@ public class PFramebuffer implements PConstants { } } + public void readPixels() { + gl.glReadPixels(0, 0, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, pixelBuffer); + } + + public void getPixels(int[] pixels) { + pixelBuffer.get(pixels); + pixelBuffer.rewind(); + } + public IntBuffer getPixelBuffer() { return pixelBuffer; } @@ -307,7 +319,7 @@ public class PFramebuffer implements PConstants { gl.glBindTexture(gltarget, glid); gl.glTexSubImage2D(gltarget, 0, 0, 0, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, buffer); gl.glDisable(gltarget); - } + } protected void initFramebuffer(int w, int h) { width = w; diff --git a/android/core/src/processing/core/PGraphicsAndroid3D.java b/android/core/src/processing/core/PGraphicsAndroid3D.java index 20dc60abb..d042fa62d 100644 --- a/android/core/src/processing/core/PGraphicsAndroid3D.java +++ b/android/core/src/processing/core/PGraphicsAndroid3D.java @@ -163,7 +163,7 @@ public class PGraphicsAndroid3D extends PGraphics { /** Used to store empty values to be passed when a light has no ambient, diffuse or specular component **/ public float[] zeroLight = { 0.0f, 0.0f, 0.0f, 1.0f }; - public float[] baseLight = { 0.2f, 0.2f, 0.2f, 1.0f }; + public float[] baseLight = { 0.0f, 0.0f, 0.0f, 1.0f }; boolean lightsAllocated = false; @@ -727,7 +727,7 @@ public class PGraphicsAndroid3D extends PGraphics { // Some things the user might have changed from OpenGL, // but we want to make sure they return to the Processing // defaults (plus these cannot be changed through the API - // so they remain constant anyways): + // so they should remain constant anyways): gl.glShadeModel(GL10.GL_SMOOTH); gl.glFrontFace(GL10.GL_CW); gl.glDepthFunc(GL10.GL_LEQUAL); @@ -1582,35 +1582,6 @@ public class PGraphicsAndroid3D extends PGraphics { // POINTS (override from P3D) - // Buffers to be passed to gl*Pointer() functions - // must be direct, i.e., they must be placed on the - // native heap where the garbage collector cannot - // move them. - // - // Buffers with multi-byte datatypes (e.g., short, int, float) - // must have their byte order set to native order - - // ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4); - // vbb.order(ByteOrder.nativeOrder()); - // mVertexBuffer = vbb.asIntBuffer(); - // mVertexBuffer.put(vertices); - // mVertexBuffer.position(0); - // - // ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4); - // cbb.order(ByteOrder.nativeOrder()); - // mColorBuffer = cbb.asIntBuffer(); - // mColorBuffer.put(colors); - // mColorBuffer.position(0); - // - // mIndexBuffer = ByteBuffer.allocateDirect(indices.length); - // mIndexBuffer.put(indices); - // mIndexBuffer.position(0); - - // gl.glFrontFace(gl.GL_CW); - // gl.glVertexPointer(3, gl.GL_FIXED, 0, mVertexBuffer); - // gl.glColorPointer(4, gl.GL_FIXED, 0, mColorBuffer); - // gl.glDrawElements(gl.GL_TRIANGLES, 36, gl.GL_UNSIGNED_BYTE, mIndexBuffer); - protected void renderPoints(int start, int stop) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); @@ -4937,10 +4908,10 @@ public class PGraphicsAndroid3D extends PGraphics { protected void drawTexture(PTexture tex, int[] crop, int x, int y, int w, int h) { gl.glEnable(tex.getGLTarget()); gl.glBindTexture(tex.getGLTarget(), tex.getGLID()); - gl.glDepthMask(false); + gl.glDepthMask(false); gl.glDisable(GL10.GL_BLEND); - // The texels of the texture replace the color of wherever is in the screen. + // The texels of the texture replace the color of wherever is on the screen. gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE); // Setting texture crop. diff --git a/android/core/src/processing/core/PImage.java b/android/core/src/processing/core/PImage.java index 83d7c5c8c..6d651b19a 100644 --- a/android/core/src/processing/core/PImage.java +++ b/android/core/src/processing/core/PImage.java @@ -59,17 +59,6 @@ public class PImage implements PConstants, Cloneable { protected Bitmap bitmap; protected PTexture texture; - - // This boolean variable is used to indicate that there - // was a change in the contents of the pixels array and - // hasn't been transfered to the texture yet. - protected boolean texUpdated = false; - - // This boolean variable is used to indicate that there - // was a change in the contents of the texture and - // hasn't been transfered to the pixels array yet. - protected boolean pixUpdated = true; - // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . @@ -204,7 +193,6 @@ public class PImage implements PConstants, Cloneable { public void loadTexture() { if (texture == null) { texture = new PTexture(parent, width, height, new PTexture.Parameters(format)); - texUpdated = false; } if (pixels == null) { loadPixels(); // loadPixels calls pixelsToTexture(). @@ -217,7 +205,6 @@ public class PImage implements PConstants, Cloneable { public void loadTexture(int filter) { if (texture == null) { texture = new PTexture(parent, width, height, new PTexture.Parameters(format, filter)); - texUpdated = false; } if (pixels == null) { loadPixels(); // loadPixels calls pixelsToTexture(). @@ -230,7 +217,6 @@ public class PImage implements PConstants, Cloneable { public void loadTexture(PTexture.Parameters params) { if (texture == null) { texture = new PTexture(parent, width, height, params); - texUpdated = false; } if (pixels == null) { loadPixels(); // loadPixels calls pixelsToTexture(). @@ -243,7 +229,6 @@ public class PImage implements PConstants, Cloneable { public void setTexture(PTexture tex) { if (tex.width == width && tex.height == height) { texture.set(tex); - pixUpdated = false; } else { System.err.println("PImage: cannot set texture with different resolution from PImage object"); } @@ -263,20 +248,20 @@ public class PImage implements PConstants, Cloneable { protected void pixelsToTexture() { - if (!texUpdated) { - texture.set(pixels); - texUpdated = true; - } + texture.set(pixels); } protected void textureToPixels() { - if (!pixUpdated) { - texture.get(pixels); - pixUpdated = true; - } + texture.get(pixels); } + + protected void reloadTexture() { + if (texture != null) { + texture = new PTexture(parent, width, height, texture.getParameters()); + } + } ////////////////////////////////////////////////////////////// @@ -356,7 +341,6 @@ public class PImage implements PConstants, Cloneable { if (parent.g.is3D()) { if (texture == null) { texture = new PTexture(parent, width, height, new PTexture.Parameters(format)); - texUpdated = false; } pixelsToTexture(); } @@ -423,7 +407,6 @@ public class PImage implements PConstants, Cloneable { // Assuming in good faith that the user only messed up // with the pixels in the specified region. We don't have // any way to know if he or she is lying. - texUpdated = true; } } @@ -477,12 +460,16 @@ public class PImage implements PConstants, Cloneable { high = (int) (height * diff); } PImage temp = new PImage(wide, high, this.format); + temp.parent = parent; temp.copy(this, 0, 0, width, height, 0, 0, wide, high); this.width = wide; this.height = high; this.pixels = temp.pixels; this.bitmap = null; - } + if (parent.g.is3D()) { + reloadTexture(); + } + } // Mark the pixels array as altered updatePixels(); } @@ -2480,6 +2467,10 @@ public class PImage implements PConstants, Cloneable { } PImage outgoing = new PImage(width, height, RGB); + + // Not possible because this method is static, so careful when using it. + // outgoing.parent = parent; + int index = 768; count /= 3; for (int i = 0; i < count; i++) { diff --git a/android/core/src/processing/core/PTexture.java b/android/core/src/processing/core/PTexture.java index 3b44405b4..e16003592 100644 --- a/android/core/src/processing/core/PTexture.java +++ b/android/core/src/processing/core/PTexture.java @@ -65,6 +65,9 @@ public class PTexture implements PConstants { protected boolean flippedX; protected boolean flippedY; + protected int[] tmpPixels = null; + protected PFramebuffer tmpFbo = null; + protected int recreateResourceIdx; //////////////////////////////////////////////////////////// @@ -198,12 +201,17 @@ public class PTexture implements PConstants { tex.set(this); // Now, overwriting "this" with tex. - copy(tex); + copyObject(tex); // Zeroing the texture id of tex, so the texture is not // deleted by OpenGL when the object is finalized by the GC. // "This" texture now wraps the one created in tex. tex.glID = 0; + + // Nullifying some utility objects so they are recreated with the appropriate + // size when needed. + tmpPixels = null; + tmpFbo = null; } @@ -250,29 +258,13 @@ public class PTexture implements PConstants { public void set(PTexture tex) { - set(tex, 0, 0, tex.width, tex.height); + copyTexels(tex, 0, 0, tex.width, tex.height, true); } - // Copies source texture to this. public void set(PTexture tex, int x, int y, int w, int h) { - if (tex == null) { - throw new RuntimeException("PTexture: source texture is null"); - } - - PFramebuffer fbo = new PFramebuffer(parent, glWidth, glHeight); - // This texture is the color (destination) buffer of the FBO. - fbo.setColorBuffer(this); - fbo.disableDepthTest(); - - // FBO copy: - a3d.pushFramebuffer(); - a3d.setFramebuffer(fbo); - // Rendering tex into "this", and scaling the source rectangle - // to cover the entire destination region. - a3d.drawTexture(tex, x, y, w, h, 0, 0, width, height); - a3d.popFramebuffer(); - } + copyTexels(tex, x, y, w, h, true); + } public void set(int[] pixels) { @@ -326,7 +318,7 @@ public class PTexture implements PConstants { int[] rgbaPixels = new int[glWidth * glHeight]; convertToRGBA(pixels, rgbaPixels, format, 0, 0, width, height); gl.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_GENERATE_MIPMAP, GL11.GL_TRUE); - gl.glTexSubImage2D(glTarget, 0, x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(rgbaPixels)); + setTexels(x, y, w, h, rgbaPixels); } else { /* // Code by Mike Miller obtained from here: @@ -398,47 +390,54 @@ public class PTexture implements PConstants { } int size = glWidth * glHeight; - - // TODO: This operation could be optimized somehow by declaring these two variables global - // and allocating them only once. - int[] tmp = new int[size]; - IntBuffer buffer; - PFramebuffer fbo; + + if (tmpFbo == null) { + tmpFbo = new PFramebuffer(parent, glWidth, glHeight); + } if (PGraphicsAndroid3D.fboSupported) { // 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). - - buffer = IntBuffer.allocate(size); - buffer.rewind(); - - fbo = new PFramebuffer(parent, glWidth, glHeight); - fbo.setColorBuffer(this); - + tmpFbo.setColorBuffer(this); a3d.pushFramebuffer(); - a3d.setFramebuffer(fbo); - gl.glReadPixels(0, 0, glWidth, glHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, buffer); + a3d.setFramebuffer(tmpFbo); + tmpFbo.readPixels(); a3d.popFramebuffer(); } else { // Here we don't have FBOs, so the method above is of no use. What we do instead is - // to draw the texture to the framebuffer, and then grab the pixels from the screen. - - fbo = new PFramebuffer(parent, glWidth, glHeight); + // to draw the texture to the screen framebuffer, and then grab the pixels from there. a3d.pushFramebuffer(); - a3d.setFramebuffer(fbo); + a3d.setFramebuffer(tmpFbo); a3d.drawTexture(this, 0, 0, glWidth, glHeight, 0, 0, glWidth, glHeight); - buffer = fbo.getPixelBuffer(); + tmpFbo.readPixels(); a3d.popFramebuffer(); } - buffer.get(tmp); - buffer.rewind(); + if (tmpPixels == null) { + tmpPixels = new int[size]; + } + tmpFbo.getPixels(tmpPixels); - convertToARGB(tmp, pixels); + convertToARGB(tmpPixels, pixels); if (flippedX) flipArrayOnX(pixels, 1); if (flippedY) flipArrayOnY(pixels, 1); } + + //////////////////////////////////////////////////////////// + + // Put methods + + + public void put(PTexture tex) { + copyTexels(tex, 0, 0, tex.width, tex.height, false); + } + + + public void put(PTexture tex, int x, int y, int w, int h) { + copyTexels(tex, x, y, w, h, false); + } + //////////////////////////////////////////////////////////// @@ -880,7 +879,44 @@ public class PTexture implements PConstants { } - protected void copy(PTexture src) { + // Copies source texture to this. + protected void copyTexels(PTexture tex, int x, int y, int w, int h, boolean scale) { + if (tex == null) { + throw new RuntimeException("PTexture: source texture is null"); + } + + if (tmpFbo == null) { + tmpFbo = new PFramebuffer(parent, glWidth, glHeight); + } + // This texture is the color (destination) buffer of the FBO. + tmpFbo.setColorBuffer(this); + tmpFbo.disableDepthTest(); + + // FBO copy: + a3d.pushFramebuffer(); + a3d.setFramebuffer(tmpFbo); + if (scale) { + // Rendering tex into "this", and scaling the source rectangle + // to cover the entire destination region. + a3d.drawTexture(tex, 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. + a3d.drawTexture(tex, x, y, w, h, x, y, w, h); + } + a3d.popFramebuffer(); + } + + protected void setTexels(int x, int y, int w, int h, int[] pix) { + gl.glTexSubImage2D(glTarget, 0, x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(pix)); + } + + protected void bind() { + gl.glBindTexture(glTarget, glID); + } + + protected void copyObject(PTexture src) { a3d.removeRecreateResourceMethod(recreateResourceIdx); deleteTexture();