/** * FeedbackEffects is a wrapper around a PImage that knows how to get() * and texture() itself so as to produce a variety of feedback effects that * can mimic real-world "camera-pointed-at-screen" video feedback, and also * very prevalent in music player visualizations. *
* Works to varying degrees of success with both OpenGL and P3D renderers. * OpenGL generally looks better, with higher-quality texture filtering, * but may not perform as well if the video card/driver doesn't provide good access * to the pixels array -- it's entirely dependent on the video card/driver. * P3D doesn't look quite as good, but performance scales more consistently with * cpu since it's a software-only renderer. *
* CAUTION: It appears that some versions of the JRE have a memory leak that * is manifest by some portion of this code. If you suffer from this problem * you will eventually run out of memory while grabbing and applying textures. * Comments welcome from anyone with further insight on how to address that. *
* @author David Bollinger * @author http://www.davebollinger.com * @version 0.01 June 2007 as of Processing 014 Beta */ class FeedbackEffects { /** * rotation value used with defaults constructor */ static final float DEFAULT_ROTATION = 0f; /** * scale value used with defaults constructor */ static final float DEFAULT_SCALE = 1f; /** * alpha value used with defaults constructor */ static final float DEFAULT_ALPHA = 240f; /** * the instance of PApplet that "owns" this */ private PApplet applet; /** * a reference to the PGraphics of the PApplet object */ private PGraphics g; /** * half the width of the PGraphics object */ private float halfwid; /** * half the height of the PGraphics object */ private float halfhei; /** * a "fudge factor" to correct for coordinate differences between OpenGL and P3D * -- * the OpenGL renderer gets the texture coordinates exactly right, so that when * rot=0f and sca=1f and source is static then feedback will sit exactly under the * source image and you won't see it - that's the ideal result: grab the screen, * draw full screen textured quad, and it lines up perfectly. * -- * the P3D renderer has about a half-pixel? offset to the upper-left that will * create a soft gradient blur even with rot==0, sca==1f. it's less noticable * when used as a rotozoomer, but it's a big deal for motion blur where the * difference is very noticeable, so this fudge factor was an attempt to correct * for it. (hmm, there also appears to be a slight difference in scale even at 1f) * thanks in advance if anyone can suggest a better solution. :) */ private float fudge; /** * the current feedback image */ public PImage img; /** * the rotation value in radians used by the "roto" portion of the rotozoomer */ float rot; /** * the scale value as a unity ratio used by the "zoom" portion of the rotozoomer */ float sca; /** * the alpha value used when drawing the feedback texture */ float alf; /** * flag to indicate if wiggle mode is active */ private boolean wiggleMode; /** * current internal wiggle theta value */ private float wiggleTheta; /** * frequency of wiggle theta in delta radians per frame */ private float wiggleFrequency; /** * amplitude of wiggle effect */ private float wiggleAmplitude; /** * bias (offset) of wiggle effect */ private float wiggleBias; /** * flag to indicate if wobble mode is active */ boolean wobbleMode; /** * current internal wobble theta value */ private float wobbleTheta; /** * frequency of wobble theta in delta radians per frame */ private float wobbleFrequency; /** * amplitude of wobble effect */ private float wobbleAmplitude; /** * bias (offset) of wobble effect */ private float wobbleBias; /** * Constructor using all default values for roto-zoom * @param applet an instance of PApplet using either P3D or OpenGL renderer */ FeedbackEffects(PApplet applet) { this(applet, DEFAULT_ROTATION, DEFAULT_SCALE, DEFAULT_ALPHA); } /** * Constructor with all standard roto-zoom values specified * @param applet an instance of PApplet using either P3D or OpenGL renderer * @param rot rotation per frame in radians * @param sca scale per frame as a ratio (<1f==zoom out, 1f=no scaling, >1f zoom in) * @param alf alpha value in range 0..255 */ FeedbackEffects(PApplet applet, float rot, float sca, float alf) { this.applet = applet; this.g = applet.g; // nasty fudge... // as worded requires importing opengl even if p3d is used, blech! // (can't reverse test if instanceof PGraphics3D, because BOTH are) fudge = (g instanceof PGraphicsOpenGL) ? 0f : 0.5f; // so use this line instead if you KNOW you'll only be using with P3D //fudge = 0.5f; this.halfwid = g.width / 2f; this.halfhei = g.height / 2f; config(rot, sca, alf); } /** * Configures the standard roto-zoom effects. * @param rot rotation per frame in radians * @param sca scale per frame as a ratio (<1f==zoom out, 1f=no scaling, >1f zoom in) * @param alf alpha value in range 0..255 */ void config(float rot, float sca, float alf) { this.rot = rot; this.sca = sca; this.alf = alf; // we also turn off esoterics whenever reconfigured this.wiggleMode = false; this.wobbleMode = false; } /** * Turn on/off the esoteric wiggle effect * @param flag A boolean indicating whether to turn on or off the effect * @param freq The frequency of the effect, controls the speed of the wiggles * @param ampl The amplitude of the effect, controls the depth of the wiggles * @param bias The offset of the effect, controls the base depth of the wiggles (normally 0f) */ void wiggle(boolean flag, float freq, float ampl, float bias) { wiggleMode = flag; wiggleTheta = 0f; wiggleFrequency = freq; wiggleAmplitude = ampl; wiggleBias = bias; } /** * Turn on/off the esoteric wobble effect * @param flag A boolean indicating whether to turn on or off the effect * @param freq The frequency of the effect, controls the speed of the wobbles * @param ampl The amplitude of the effect, controls the depth of the wobbles * @param bias The offset of the effect, controls the base depth of the wobbles (normally 1f) */ void wobble(boolean flag, float freq, float ampl, float bias) { wobbleMode = flag; wobbleTheta = 0f; wobbleFrequency = freq; wobbleAmplitude = ampl; wobbleBias = bias; } /** * Grabs a new copy of the texture from the current screen */ void grab() { img = null; img = g.get(); } /** * Set the feedback image as the current texture() */ void apply() { if (img==null) return; texture(img); } /** * Draw the feedback texture to the screen. * Expects to be called near the top of draw() before other camera/transforms occur. */ void draw() { if (img==null) return; fill(255,alf); stroke(0); pushMatrix(); translate(halfwid+fudge, halfhei+fudge, 0); // sure, go ahead, put a rotateX() or rotateY() here too! :-) // (would mimic titling a camera off axis relative to screen) rotateZ(rot); scale(sca); beginShape(QUADS); texture(img); textureMode(NORMALIZED); vertex(-halfwid, -halfhei, 0, 0); vertex(halfwid, -halfhei, 1, 0); vertex(halfwid, halfhei, 1, 1); vertex(-halfwid, halfhei, 0, 1); endShape(); popMatrix(); updateEsoterics(); } /** * updates the internal values used to control the esoteric effects */ private void updateEsoterics() { if (wiggleMode) { wiggleTheta += wiggleFrequency; rot = wiggleBias + wiggleAmplitude * cos(wiggleTheta); } if (wobbleMode) { wobbleTheta += wobbleFrequency; sca = wobbleBias + wobbleAmplitude * cos(wobbleTheta); } } }