Improved texture handling in text rendering (closes issue 394). Methods sketchTransparentSurface and sketchColorDepth were renamed to sketchTransparency and sketchColordepth

This commit is contained in:
codeanticode
2010-11-13 12:58:41 +00:00
parent 5783c34cd7
commit 977363ee57
6 changed files with 192 additions and 141 deletions
@@ -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;
}
+70 -29
View File
@@ -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;
@@ -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;
@@ -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.
+17 -26
View File
@@ -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++) {
+81 -45
View File
@@ -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();