Golan-TextRain
Here is a Processing version of the classic TextRain (1999) by Camille Utterback and Romy Achituv:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | // Text Rain (Processing cover version) // Original by Camille Utterback and Romy Achituv (1999): // http://camilleutterback.com/projects/text-rain/ // Implemented in Processing 1.2.1 by Golan Levin import processing.video.*; Capture video; float brightnessThreshold = 50; TextRainLetter poemLetters[]; int nLetters; //----------------------------------- void setup() { size(320,240); video = new Capture(this, width, height); String poemString = "A poem about bodies"; nLetters = poemString.length(); poemLetters = new TextRainLetter[nLetters]; for (int i=0; i<nLetters; i++) { char c = poemString.charAt(i); float x = width * (float)(i+1)/(nLetters+1); float y = 10; poemLetters[i] = new TextRainLetter(c,x,y); } } //----------------------------------- void draw() { if (video.available()) { video.read(); video.loadPixels(); image (video, 0, 0, width, height); for (int i=0; i<nLetters; i++) { poemLetters[i].update(); poemLetters[i].draw(); } } } //=================================================================== class TextRainLetter { float gravity = 1.5; char c; float x; float y; TextRainLetter (char cc, float xx, float yy) { c = cc; x = xx; y = yy; } //----------------------------------- void update() { // Update the position of a TextRainLetter. // 1. Compute the pixel index corresponding to the (x,y) location of the particle. int index = width*(int)y + (int)x; index = constrain (index, 0, width*height-1); // 2. Fetch the color of the pixel there, and compute its brightness. float pixelBrightness = brightness(video.pixels[index]); // 3. If we're in a bright area, move downwards. // If we're in a dark area, move up until we're in a light area. if (pixelBrightness > brightnessThreshold) { y += gravity; //move downward } else { while ((y > 0) && (pixelBrightness <= brightnessThreshold)){ y -= gravity; // travel up intil it's bright index = width*(int)y + (int)x; index = constrain (index, 0, width*height-1); pixelBrightness = brightness(video.pixels[index]); } } if ((y >= height-1) || (y < 0)){ y = 0; } } //----------------------------------- void draw() { // Draw the letter. Use a simple black "drop shadow" // to achieve improved contrast for the typography. fill(0); text (""+c, x+1,y+1); text (""+c, x-1,y+1); text (""+c, x+1,y-1); text (""+c, x-1,y-1); fill(255); text (""+c, x,y); } } |
Here is a version for OpenFrameworks v.062, based on the MovieCapture example. First, the header (.h) file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #ifndef _TEST_APP #define _TEST_APP #include "ofMain.h" class TextRainLetter { public: char c; float x; float y; TextRainLetter (char cc, float xx, float yy); float getPixelBrightness (unsigned char *rgbPixels, int baseIndex); void update (unsigned char *rgbPixels, int width, int height, float threshold); void draw(); }; class testApp : public ofBaseApp { public: void setup(); void update(); void draw(); void keyPressed(int key); void keyReleased(int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(int x, int y, int button); void windowResized(int w, int h); ofVideoGrabber vidGrabber; int camWidth; int camHeight; float brightnessThreshold; vector <TextRainLetter> poemLetters; int nLetters; }; #endif |
And the C++ (.cpp) file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ camWidth = 320; camHeight = 240; vidGrabber.setVerbose(true); vidGrabber.initGrabber(camWidth,camHeight); brightnessThreshold = 50; string poemString = "A poem about bodies"; nLetters = poemString.length(); for (int i=0; i<nLetters; i++) { char c = poemString[i]; float x = camWidth * (float)(i+1)/(nLetters+1); float y = 10; poemLetters.push_back( TextRainLetter(c,x,y) ); } } //-------------------------------------------------------------- void testApp::update(){ vidGrabber.grabFrame(); if (vidGrabber.isFrameNew()){ unsigned char *videoPixels = vidGrabber.getPixels(); for (int i=0; i<nLetters; i++) { poemLetters[i].update(videoPixels, camWidth, camHeight, brightnessThreshold); } } } //-------------------------------------------------------------- void testApp::draw(){ ofBackground(100,100,100); ofSetHexColor(0xffffff); vidGrabber.draw(0,0); for (int i=0; i<nLetters; i++) { poemLetters[i].draw(); } } //-------------------------------------------------------------- void testApp::keyPressed (int key){} void testApp::keyReleased(int key){} void testApp::mouseMoved(int x, int y ){} void testApp::mouseDragged(int x, int y, int button){} void testApp::mousePressed(int x, int y, int button){} void testApp::mouseReleased(int x, int y, int button){} void testApp::windowResized(int w, int h){} //===================================================================== TextRainLetter::TextRainLetter (char cc, float xx, float yy) { // Constructor function c = cc; x = xx; y = yy; } //----------------------------------- void TextRainLetter::draw(){ char drawStr[1]; sprintf(drawStr, "%c", c); ofSetColor(0,0,0); ofDrawBitmapString(drawStr, x-1,y-1); ofDrawBitmapString(drawStr, x+1,y-1); ofDrawBitmapString(drawStr, x-1,y+1); ofDrawBitmapString(drawStr, x+1,y+1); ofSetColor(255,255,255); ofDrawBitmapString(drawStr, x,y); } //----------------------------------- void TextRainLetter::update (unsigned char *rgbPixels, int width, int height, float threshold) { int index = 3* (width*(int)y + (int)x); index = (int) ofClamp (index, 0, 3*width*height-1); float pixelBrightness = getPixelBrightness(rgbPixels, index); float gravity = 2.0; if (pixelBrightness > threshold) { y += gravity; } else { while ((y > 0) && (pixelBrightness <= threshold)){ y -= gravity; index = 3* (width*(int)y + (int)x); index = (int) ofClamp (index, 0, 3*width*height-1); pixelBrightness = getPixelBrightness(rgbPixels, index); } } if ((y >= height-1) || (y < 10)){ y = 10; } } //----------------------------------- float TextRainLetter::getPixelBrightness (unsigned char *rgbPixels, int baseIndex){ // small utility function int r = rgbPixels[baseIndex + 0]; int g = rgbPixels[baseIndex + 1]; int b = rgbPixels[baseIndex + 2]; float pixelBrightness = (r+g+b)/3.0; return pixelBrightness; } |
Comments Off on Golan-TextRain