Many improvements in the PImage/PTexture API

This commit is contained in:
codeanticode
2010-10-06 19:14:49 +00:00
parent bda73203a8
commit 0a5b3acf7b
5 changed files with 205 additions and 115 deletions
+15 -15
View File
@@ -1502,7 +1502,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PImage createImage(int wide, int high, int format) {
PImage image = new PImage(wide, high, format);
image.parent = this; // make save() work
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
image.format = ARGB;
image.loadTexture();
@@ -1514,7 +1514,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PImage createImage(int wide, int high, int format, int filter) {
PImage image = new PImage(wide, high, format);
image.parent = this; // make save() work
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
image.format = ARGB;
image.loadTexture(filter);
@@ -1526,7 +1526,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PImage createImage(int wide, int high, PTexture.Parameters params) {
PImage image = new PImage(wide, high, params.format);
image.parent = this; // make save() work
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
image.format = ARGB;
image.loadTexture(params);
@@ -3314,7 +3314,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
// println("loadImage(" + filename + ") was " + nfc(much));
PImage image = new PImage(bitmap);
image.parent = this;
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
image.format = ARGB;
image.loadTexture(params);
@@ -3493,7 +3493,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
e.printStackTrace();
}
} else if (filename.toLowerCase().endsWith(".obj")) {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
return new PShape3D(this, filename, mode);
} else {
throw new RuntimeException("OBJ files can be loaded only when using the A3D renderer.");
@@ -3515,7 +3515,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
* Creates an empty 3D shape, with space for nvert vertices.
*/
public PShape3D createShape(int nvert, int kind, int mode) {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
PShape3D.Parameters params = PShape3D.newParameters(kind, mode);
PShape3D model = new PShape3D(this, nvert, params);
return model;
@@ -3537,7 +3537,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
* Tesselates a PShape into a PShape3D with the desired drawing mode (STATID or DYNAMIC)..
*/
public PShape3D createShape(PShape shape, int mode) {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
PGraphicsAndroid3D a3d = (PGraphicsAndroid3D)g;
a3d.beginShapeRecorderImpl();
shape(shape, 0, 0, 1, 1);
@@ -6921,7 +6921,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PShape beginRecord() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
return ((PGraphicsAndroid3D) g).beginRecord();
} else {
throw new RuntimeException("The shapes recorder can only be used with the A3D renderer.");
@@ -6930,7 +6930,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public void beginShapesRecorder() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
((PGraphicsAndroid3D) g).beginShapesRecorder();
} else {
throw new RuntimeException("The shapes recorder can only be used with the A3D renderer.");
@@ -6939,7 +6939,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public void beginShapeRecorder() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
((PGraphicsAndroid3D) g).beginShapeRecorder();
} else {
throw new RuntimeException("The shape recorder can only be used with the A3D renderer.");
@@ -6948,7 +6948,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public void beginShapeRecorder(int kind) {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
((PGraphicsAndroid3D) g).beginShapeRecorder(kind);
} else {
throw new RuntimeException("The shape recorder can only be used with the A3D renderer.");
@@ -7022,7 +7022,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public void endRecord() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
((PGraphicsAndroid3D) g).endRecord();
} else {
throw new RuntimeException("The shapes recorder can only be used with the A3D renderer.");
@@ -7031,7 +7031,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PShape3D endShapesRecorder() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
return ((PGraphicsAndroid3D) g).endShapesRecorder();
} else {
throw new RuntimeException("The shapes recorder can only be used with the A3D renderer.");
@@ -7040,7 +7040,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PShape3D endShapeRecorder() {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
return ((PGraphicsAndroid3D) g).endShapeRecorder();
} else {
throw new RuntimeException("The shape recorder can only be used with the A3D renderer.");
@@ -7049,7 +7049,7 @@ public class PApplet extends Activity implements PConstants, Runnable {
public PShape3D endShapeRecorder(int mode) {
if (g instanceof PGraphicsAndroid3D) {
if (g.is3D()) {
return ((PGraphicsAndroid3D) g).endShapeRecorder(mode);
} else {
throw new RuntimeException("The shape recorder can only be used with the A3D renderer.");
@@ -5094,7 +5094,7 @@ public class PGraphics extends PImage implements PConstants {
// TODO: Discuss proper integration into PGraphics API.
public PImage getOffscreenImage() {
if (!(this instanceof PGraphicsAndroid3D)) {
if (!is3D()) {
showMissingWarning("getOffscreenImage");
}
return null;
@@ -5102,14 +5102,14 @@ public class PGraphics extends PImage implements PConstants {
public void blend(int mode) {
if (!(this instanceof PGraphicsAndroid3D)) {
if (!is3D()) {
showMissingWarning("blend");
}
}
public void noBlend() {
if (!(this instanceof PGraphicsAndroid3D)) {
if (!is3D()) {
showMissingWarning("noBlend");
}
}
@@ -51,7 +51,7 @@ import processing.core.PShape3D.VertexGroup;
// setRasterPos() is also commented out
/*
* Android 3D renderer implemented with pure OpenGL ES 1.0/1.1
* Android 3D renderer implemented with pure OpenGL ES 1.1
* By Andres Colubri
*
* TODO: Comment A3D, PShape3D, PTexture, PFramebuffer,
@@ -92,9 +92,6 @@ public class PGraphicsAndroid3D extends PGraphics {
/** Aspect ratio of camera's view. */
public float cameraAspect;
protected float currentEyeX;
protected float currentCenterY;
/** Modelview and projection matrices **/
// Array version for use with OpenGL
@@ -3434,11 +3431,7 @@ public class PGraphicsAndroid3D extends PGraphics {
float centerY, float centerZ, float upX, float upY, float upZ) {
eyeY = height - eyeY;
centerY = height - centerY;
// Needed for properly setting ortographic projection.
currentEyeX = eyeX;
currentCenterY = centerY;
// Calculating Z vector
float z0 = eyeX - centerX;
float z1 = eyeY - centerY;
@@ -3564,20 +3557,21 @@ public class PGraphicsAndroid3D extends PGraphics {
}
/**
* Properly setting the ortographic projection depends on the values
* used to set the camera (eye and center).
* Sets orthographic projection. The left, right, bottom and top
* values refer to the top left corner of the screen, not to the
* center or eye of the camera. This is like this because making
* it relative to the camera is not very intuitive if we think
* of the perspective function, which is also independent of the
* camera position.
*
*/
public void ortho(float left, float right, float bottom, float top,
float near, float far) {
// TODO: check if this equation is correct in the case the camera is not aligned
// to the XYZ axis (I guess the eye-camera vector needs to be used to determine
// the amounds to substract from left,right,bottom and top).
left -= currentEyeX;
right -= currentEyeX;
left -= width/2;
right -= width/2;
bottom -= currentCenterY;
top -= currentCenterY;
bottom -= height/2;
top -= height/2;
float x = 2.0f / (right - left);
float y = 2.0f / (top - bottom);
@@ -4927,8 +4921,8 @@ public class PGraphicsAndroid3D extends PGraphics {
// Setting texture crop.
gl11.glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES, crop, 0);
// There is no need to setup orthographic projection or any related matrix set/restore
// operations here because glDrawTexiOES operates on window coordinates directly:
// There is no need to setup orthographic projection or call any related matrix set/restore
// functions here because glDrawTexiOES operates on window coordinates directly:
// "glDrawTexiOES takes window coordinates and bypasses the transform pipeline
// (except for mapping Z to the depth range), so there is no need for any
// matrix setup/restore code."
@@ -5418,6 +5412,13 @@ public class PGraphicsAndroid3D extends PGraphics {
+ printConfig(egl, display, bestConfig);
System.out.println(configStr);
}
PApplet.println("OPENGL DISPLAY CONFIG:");
PApplet.println("redBits " + redBits);
PApplet.println("greenBits " + greenBits);
PApplet.println("blueBits " + blueBits);
PApplet.println("alphaBits " + alphaBits);
PApplet.println("depthBits " + depthBits);
PApplet.println("stencilBits " + stencilBits);
return bestConfig;
}
+76 -30
View File
@@ -59,6 +59,16 @@ 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;
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
@@ -192,44 +202,79 @@ public class PImage implements PConstants, Cloneable {
public void loadTexture() {
texture = new PTexture(parent, width, height, new PTexture.Parameters(format));
pixelsToTexture();
if (texture == null) {
texture = new PTexture(parent, width, height, new PTexture.Parameters(format));
texUpdated = false;
}
if (pixels != null) {
// loadTexture has lower "permissions" than loadPixels, because
// loadPixels calls loadTexture which in turns creates the texture
// if it is null, but the contrary doesn't happen (loadTexture cannot
// result in the creation of the pixels array if it is null).
pixelsToTexture();
}
}
public void loadTexture(int filter) {
texture = new PTexture(parent, width, height, new PTexture.Parameters(format, filter));
pixelsToTexture();
if (texture == null) {
texture = new PTexture(parent, width, height, new PTexture.Parameters(format, filter));
texUpdated = false;
}
if (pixels != null) {
pixelsToTexture();
}
}
public void loadTexture(PTexture.Parameters params) {
texture = new PTexture(parent, width, height, params);
pixelsToTexture();
if (texture == null) {
texture = new PTexture(parent, width, height, params);
texUpdated = false;
}
if (pixels != null) {
pixelsToTexture();
}
}
public void pixelsToTexture() {
loadPixels();
texture.set(this);
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");
}
}
public void textureToPixels() {
loadPixels();
texture.get(pixels);
}
public void setTexture(PTexture texture) {
this.texture = texture;
}
public PTexture getTexture() {
return texture;
}
public void updateTexture() {
if (pixels != null) {
textureToPixels();
}
}
protected void pixelsToTexture() {
if (!texUpdated) {
texture.set(pixels);
texUpdated = true;
}
}
protected void textureToPixels() {
if (!pixUpdated) {
texture.get(pixels);
pixUpdated = true;
}
}
//////////////////////////////////////////////////////////////
@@ -306,9 +351,8 @@ public class PImage implements PConstants, Cloneable {
if (bitmap != null) {
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
}
if (texture != null) {
// texture.get(pixels); HERE?
if (parent.g.is3D()) {
loadTexture();
}
}
@@ -369,12 +413,15 @@ public class PImage implements PConstants, Cloneable {
}
if (texture != null) {
// texture.set(this); HERE?
texture.set(pixels, mx1, my1, mx2 - mx1, my2 - my1);
// 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;
}
}
//////////////////////////////////////////////////////////////
// COPYING IMAGE DATA
@@ -526,7 +573,7 @@ public class PImage implements PConstants, Cloneable {
index2 += w;
}
}
if (parent.g instanceof PGraphicsAndroid3D) {
if (parent.g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
newbie.format = ARGB;
newbie.loadTexture();
@@ -541,7 +588,7 @@ public class PImage implements PConstants, Cloneable {
public PImage get() {
try {
PImage img = (PImage) clone();
if (parent.g instanceof PGraphicsAndroid3D) {
if (parent.g.is3D()) {
// TODO: Check why textures doesn't work in formats other than ARGB...
img.format = ARGB;
img.loadTexture();
@@ -2744,4 +2791,3 @@ public class PImage implements PConstants, Cloneable {
//return success;
}
}
+90 -47
View File
@@ -35,10 +35,9 @@ import android.opengl.GLUtils;
import java.nio.*;
/**
* This class adds an opengl texture to a PImage object.
* This class wraps an OpenGL texture.
* By Andres Colubri
* TODO: Finish integration with PImage
* TODO: Revise updating mechanism (what happens when the pixels change in the PImage, etc).
*
*/
@SuppressWarnings("unused")
public class PTexture implements PConstants {
@@ -163,7 +162,7 @@ public class PTexture implements PConstants {
/**
* 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
* it first destroys the current OpenGL texture object and then creates a new one with the specified
* size.
* @param width int
* @param height int
@@ -175,7 +174,7 @@ public class PTexture implements PConstants {
/**
* 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
* 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
@@ -190,17 +189,18 @@ public class PTexture implements PConstants {
public void resize(int wide, int high) {
// Creating new texture with the apropriate size.
// Creating new texture with the appropriate size.
PTexture tex = new PTexture(parent, wide, high, getParameters());
// Copying the contents of this texture into tex.
tex.set(this);
// Now, overwriting this with tex.
// Now, overwriting "this" with tex.
copy(tex);
// Zeroing the texture id of tex, so the texture is not
// deleted by opengl when the object is finalized by the GC.
// deleted by OpenGL when the object is finalized by the GC.
// "This" texture now wraps the one created in tex.
tex.glTextureID = 0;
}
@@ -224,10 +224,9 @@ public class PTexture implements PConstants {
width = img.width;
height = img.height;
createTexture(width, height);
}
}
img.loadPixels();
set(img.pixels, img.format);
set(img.texture);
}
@@ -243,42 +242,58 @@ public class PTexture implements PConstants {
width = h;
createTexture(w, h);
}
img.loadPixels();
int p0;
int dest[] = new int[w * h];
for (int j = 0; j < h; j++) {
p0 = y * img.width + x + (img.width - w) * j;
PApplet.arrayCopy(img.pixels, p0 + w * j, dest, w * j, w);
}
set(dest, img.format);
img.loadPixels();
set(img.texture, x, y, w, h);
}
public void set(PTexture tex) {
set(tex, 0, 0, tex.width, tex.height);
}
// Copies source texture to this by means of FBO.
public void set(PTexture tex) {
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 is the color (destination) buffer of the FBO.
// 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.
a3d.drawTexture(tex, 0, 0, tex.glWidth, tex.glHeight, 0, 0, glWidth, glHeight);
// 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();
}
public void set(int[] pixels) {
set(pixels, ARGB);
set(pixels, 0, 0, glWidth, glHeight, ARGB);
}
public void set(int[] intArray, int arrayFormat) {
if (intArray.length != width * height) {
public void set(int[] pixels, int format) {
set(pixels, 0, 0, glWidth, glHeight, 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) {
// TODO: Should we throw exceptions here or just a warning?
if (pixels == null) {
throw new RuntimeException("PTexture: null pixels array");
}
if (pixels.length != width * height) {
throw new RuntimeException("PTexture: wrong length of pixels array");
}
@@ -286,35 +301,53 @@ public class PTexture implements PConstants {
createTexture(width, height);
}
int[] convArray = new int[glWidth * glHeight];
/*
int p0;
int dest[] = new int[w * h];
for (int j = 0; j < h; j++) {
p0 = y * img.width + x + (img.width - w) * j;
PApplet.arrayCopy(img.pixels, p0 + w * j, dest, w * j, w);
}
int p0;
int dest[] = new int[w * h];
for (int j = 0; j < h; j++) {
p0 = y * img.width + x + (img.width - w) * j;
PApplet.arrayCopy(img.pixels, p0 + w * j, dest, w * j, w);
}
*/
gl.glEnable(glTarget);
gl.glBindTexture(glTarget, glTextureID);
if (usingMipmaps) {
if (a3d.gl11 != null && PGraphicsAndroid3D.mipmapSupported) {
convertToRGBA(intArray, convArray, arrayFormat);
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, 0, 0, glWidth, glHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(convArray));
gl.glTexSubImage2D(glTarget, 0, x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(rgbaPixels));
} else {
/*
// Code by Mike Miller obtained from here:
// http://insanitydesign.com/wp/2009/08/01/android-opengl-es-mipmaps/
copyARGB(intArray, convArray);
// TODO: Check if this algorithm works only for pot textures or for any resolution.
// and adapt for copying only one texture rectangle.
int[] argbPixels = new int[glWidth * glHeight];
copyARGB(pixels, argbPixels);
int level = 0;
int w = glWidth;
int h = glHeight;
int w0 = glWidth;
int h0 = glHeight;
// We create a Bitmap because then we use its built-in filtered downsampling
// functionality.
Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
bitmap.setPixels(convArray, 0, w, 0, 0, w, h);
bitmap.setPixels(argbPixels, 0, w0, 0, 0, w0, h0);
while (w >= 1 || h >= 1) {
while (w0 >= 1 || h0 >= 1) {
//First of all, generate the texture from our bitmap and set it to the according level
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, level, bitmap, 0);
// We are done.
if (w == 1 || h == 1) {
if (w0 == 1 || h0 == 1) {
break;
}
@@ -322,18 +355,25 @@ public class PTexture implements PConstants {
level++;
// Downsampling bitmap
h /= 2;
w /= 2;
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w, h, true);
h0 /= 2;
w0 /= 2;
Bitmap bitmap2 = Bitmap.createScaledBitmap(bitmap, w0, h0, true);
// Clean up
bitmap.recycle();
bitmap = bitmap2;
}
*/
// No mipmaps for now.
int[] rgbaPixels = new int[glWidth * glHeight];
convertToRGBA(pixels, rgbaPixels, format, 0, 0, width, height);
gl.glTexSubImage2D(glTarget, 0, x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(rgbaPixels));
}
} else {
convertToRGBA(intArray, convArray, arrayFormat);
gl.glTexSubImage2D(glTarget, 0, 0, 0, glWidth, glHeight, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(convArray));
int[] rgbaPixels = new int[glWidth * glHeight];
convertToRGBA(pixels, rgbaPixels, format, 0, 0, width, height);
gl.glTexSubImage2D(glTarget, 0, x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, IntBuffer.wrap(rgbaPixels));
}
gl.glDisable(glTarget);
@@ -342,13 +382,15 @@ public class PTexture implements PConstants {
////////////////////////////////////////////////////////////
// Update methods
// Get methods
/**
* Copy texture to pixels. Involves video memory to main memory transfer (slow).
*/
public void get(int[] pixels) {
// TODO: here is ok to create a new pixels array, or an error/warning
// should be thrown instead?
if ((pixels == null) || (pixels.length != width * height)) {
pixels = new int[width * height];
}
@@ -398,7 +440,7 @@ public class PTexture implements PConstants {
////////////////////////////////////////////////////////////
// Get opengl parameters
// Get OpenGL parameters
protected int getGLWidth() {
@@ -412,7 +454,7 @@ public class PTexture implements PConstants {
/**
* Provides the ID of the opengll texture object.
* Provides the ID of the OpenGL texture object.
* @return int
*/
protected int getGLTextureID() {
@@ -592,7 +634,8 @@ public class PTexture implements PConstants {
* @param tIntArray int[]
* @param arrayFormat int
*/
protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat) {
// TODO: Finish handling of rectangular area.
protected void convertToRGBA(int[] intArray, int[] tIntArray, int arrayFormat, int x0, int y0, int w, int h) {
int t = 0;
int p = 0;
if (PGraphicsAndroid3D.BIG_ENDIAN) {