diff --git a/core/src/processing/opengl/PGL.java b/core/src/processing/opengl/PGL.java index be53ed820..bef26d45b 100644 --- a/core/src/processing/opengl/PGL.java +++ b/core/src/processing/opengl/PGL.java @@ -125,7 +125,12 @@ public abstract class PGL { * order to make sure the lines are always on top of the fill geometry */ protected static float STROKE_DISPLACEMENT = 0.999f; - protected static boolean DOUBLE_BUFFERED = true; + // ........................................................ + + // Variables to handle single-buffered situations (i.e.: Android) + + protected IntBuffer firstFrame; + protected static boolean SINGLE_BUFFERED = false; // ........................................................ @@ -698,6 +703,10 @@ public abstract class PGL { pclearColor = clearColor; clearColor = false; + if (SINGLE_BUFFERED && sketch.frameCount == 1) { + restoreFirstFrame(); + } + if (fboLayerEnabledReq) { fboLayerEnabled = true; fboLayerEnabledReq = false; @@ -708,7 +717,7 @@ public abstract class PGL { destroyFBOLayer(); fbolayerResetReq = false; } - if (!fboLayerCreated && DOUBLE_BUFFERED) { + if (!fboLayerCreated) { createFBOLayer(); } @@ -812,12 +821,17 @@ public abstract class PGL { fboLayerEnabled = false; fboLayerDisableReq = false; } - } else if (!clearColor && 0 < sketch.frameCount || !sketch.isLooping()) { - enableFBOLayer(); - } + } else { + if (SINGLE_BUFFERED && sketch.frameCount == 0) { + saveFirstFrame(); + } - if (fboLayerEnabledReq && !fboLayerCreated && !DOUBLE_BUFFERED) { - createFBOLayer(); + if (!clearColor && 0 < sketch.frameCount || !sketch.isLooping()) { + enableFBOLayer(); + if (SINGLE_BUFFERED) { + createFBOLayer(); + } + } } } @@ -942,6 +956,46 @@ public abstract class PGL { protected abstract void initFBOLayer(); + + protected void saveFirstFrame() { + firstFrame = allocateDirectIntBuffer(graphics.width * graphics.height); + readBuffer(BACK); + readPixelsImpl(0, 0, graphics.width, graphics.height, RGBA, UNSIGNED_BYTE, firstFrame); + } + + + protected void restoreFirstFrame() { + IntBuffer tex = allocateIntBuffer(1); + genTextures(1, tex); + + int w, h; + float scale = getPixelScale(); + if (hasNpotTexSupport()) { + w = (int)(scale * graphics.width); + h = (int)(scale * graphics.height); + } else { + w = nextPowerOfTwo((int)(scale * graphics.width)); + h = nextPowerOfTwo((int)(scale * graphics.height)); + } + + bindTexture(TEXTURE_2D, tex.get(0)); + texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, NEAREST); + texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, NEAREST); + texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE); + texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE); + texImage2D(TEXTURE_2D, 0, RGBA, w, h, 0, RGBA, UNSIGNED_BYTE, null); + texSubImage2D(TEXTURE_2D, 0, 0, 0, graphics.width, graphics.height, RGBA, UNSIGNED_BYTE, firstFrame); + + drawTexture(TEXTURE_2D, tex.get(0), w, h, + 0, 0, graphics.width, graphics.height, + 0, 0, (int)(scale * graphics.width), (int)(scale * graphics.height), + 0, 0, graphics.width, graphics.height); + + deleteTextures(1, tex); + firstFrame.clear(); + firstFrame = null; + } + protected void destroyFBOLayer() { if (threadIsCurrent() && fboLayerCreated) { deleteFramebuffers(1, glColorFbo); diff --git a/core/src/processing/opengl/PGraphicsOpenGL.java b/core/src/processing/opengl/PGraphicsOpenGL.java index ae3d05456..54014ff4d 100644 --- a/core/src/processing/opengl/PGraphicsOpenGL.java +++ b/core/src/processing/opengl/PGraphicsOpenGL.java @@ -696,6 +696,64 @@ public class PGraphicsOpenGL extends PGraphics { } + public boolean saveImpl(String filename) { +// return super.save(filename); // ASYNC save frame using PBOs not yet available on Android + + if (getHint(DISABLE_ASYNC_SAVEFRAME)) { + // Act as an opaque surface for the purposes of saving. + if (primaryGraphics) { + int prevFormat = format; + format = RGB; + boolean result = super.save(filename); + format = prevFormat; + return result; + } + + return super.save(filename); + } + + if (asyncImageSaver == null) { + asyncImageSaver = new AsyncImageSaver(); + } + + if (!asyncPixelReaderInitialized) { + // First call! Get this guy initialized + if (pgl.hasPBOs() && pgl.hasSynchronization()) { + asyncPixelReader = new AsyncPixelReader(); + } + asyncPixelReaderInitialized = true; + } + + if (asyncPixelReader != null && !loaded) { + boolean needEndDraw = false; + if (!drawing) { + beginDraw(); + needEndDraw = true; + } + flush(); + updatePixelSize(); + + // get the whole async package + asyncPixelReader.readAndSaveAsync(filename); + + if (needEndDraw) endDraw(); + } else { + // async transfer is not supported or + // pixels are already in memory, just do async save + if (!loaded) loadPixels(); + int format = primaryGraphics ? RGB : ARGB; + PImage target = asyncImageSaver.getAvailableTarget(pixelWidth, pixelHeight, + format); + if (target == null) return false; + int count = PApplet.min(pixels.length, target.pixels.length); + System.arraycopy(pixels, 0, target.pixels, 0, count); + asyncImageSaver.saveTargetAsync(this, target, filename); + } + + return true; + } + + ////////////////////////////////////////////////////////////// // IMAGE METADATA FOR THIS RENDERER @@ -714,6 +772,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override + @SuppressWarnings("rawtypes") public Object getCache(PImage image) { Object storage = getPrimaryPG().cacheMap.get(image); if (storage != null && storage.getClass() == WeakReference.class) { @@ -773,7 +832,7 @@ public class PGraphicsOpenGL extends PGraphics { if (res == null) { break; } - System.out.println("Disposing texture resource " + iterations + " " + res.hashCode()); +// System.out.println("Disposing texture resource " + iterations + " " + res.hashCode()); res.dispose(); ++iterations; } @@ -851,7 +910,7 @@ public class PGraphicsOpenGL extends PGraphics { if (res == null) { break; } - System.out.println("Disposing VertexBuffer resource " + iterations + " " + res.hashCode()); +// System.out.println("Disposing VertexBuffer resource " + iterations + " " + res.hashCode()); res.dispose(); ++iterations; } @@ -1028,7 +1087,7 @@ public class PGraphicsOpenGL extends PGraphics { if (res == null) { break; } - System.out.println("Disposing framebuffer resource " + iterations + " " + res.hashCode()); +// System.out.println("Disposing framebuffer resource " + iterations + " " + res.hashCode()); res.dispose(); ++iterations; } @@ -5544,59 +5603,7 @@ public class PGraphicsOpenGL extends PGraphics { @Override public boolean save(String filename) { - - if (getHint(DISABLE_ASYNC_SAVEFRAME)) { - // Act as an opaque surface for the purposes of saving. - if (primaryGraphics) { - int prevFormat = format; - format = RGB; - boolean result = super.save(filename); - format = prevFormat; - return result; - } - - return super.save(filename); - } - - if (asyncImageSaver == null) { - asyncImageSaver = new AsyncImageSaver(); - } - - if (!asyncPixelReaderInitialized) { - // First call! Get this guy initialized - if (pgl.hasPBOs() && pgl.hasSynchronization()) { - asyncPixelReader = new AsyncPixelReader(); - } - asyncPixelReaderInitialized = true; - } - - if (asyncPixelReader != null && !loaded) { - boolean needEndDraw = false; - if (!drawing) { - beginDraw(); - needEndDraw = true; - } - flush(); - updatePixelSize(); - - // get the whole async package - asyncPixelReader.readAndSaveAsync(filename); - - if (needEndDraw) endDraw(); - } else { - // async transfer is not supported or - // pixels are already in memory, just do async save - if (!loaded) loadPixels(); - int format = primaryGraphics ? RGB : ARGB; - PImage target = asyncImageSaver.getAvailableTarget(pixelWidth, pixelHeight, - format); - if (target == null) return false; - int count = PApplet.min(pixels.length, target.pixels.length); - System.arraycopy(pixels, 0, target.pixels, 0, count); - asyncImageSaver.saveTargetAsync(this, target, filename); - } - - return true; + return saveImpl(filename); } @@ -5780,7 +5787,7 @@ public class PGraphicsOpenGL extends PGraphics { if (widths[head] * heights[head] != pixelWidth * pixelHeight) { pgl.bindBuffer(PGL.PIXEL_PACK_BUFFER, pbos[head]); pgl.bufferData(PGL.PIXEL_PACK_BUFFER, - Integer.BYTES * pixelWidth * pixelHeight, + Integer.SIZE/8 * pixelWidth * pixelHeight, null, PGL.STREAM_READ); } widths[head] = pixelWidth;