Question about openProcessing sketch (Retro CRT TV Distortion Effect)

edited February 2018 in Questions about Code

I ran into an interesting legacy sketch by Luis Gonzalez on openProcessing and I was trying to figure out how it works. Unfortunately it seems like it's not working for me, at least in processing 3. What needs to be changed to make this work as intended? Here's a link to the sketch: https://www.openprocessing.org/sketch/112597#

/*

 Retro CRT TV Distortion Effect 

 RGB Shifting for random color bleeding
 television scanlines
 random flicker of entire image
 rolling bar
 tv noise static
 turning on fades in image from black
 turning off fades image to white while shrinking it to a horizontal line

 */

int w=480;
int h=270;
PImage tvscreen;
PImage TVOverlay;
PImage tvnoise;

int barY1=10;
int barY2=30;


//tv noise
int[] ppx;
int[] px = new int[w];

boolean tvstate = true;


long timeIndexInfo;
long firstCountInfo;

float lerpAmount = 0;
float lerpScale = 0;
float lerpShrink = 0;

int tvheight=0;
int tvwidth=0;

void setup()
{
  size(480, 270, P2D);
  tvscreen=loadImage("obey.png");
  TVOverlay=loadImage("tv.png");

  // precalculate tv noise
  tvnoise = createImage(w,h,RGB);

  tvnoise.loadPixels();
  ppx = new int[tvnoise.pixels.length];
  for (int y = 0; y < ppx.length;)
    ppx[y++] = int(random(-32,32));
  loadPixels();


  tvwidth = w;
  tvheight = h;

  timeIndexInfo = millis();
  firstCountInfo = millis();

  noSmooth();

}

void draw() {
  background(0);
  renderDistort();
  image(TVOverlay, 0, 0);
}

int powerUpCounter = 256;

void scaleIt(float amountA, float amountB, int valueA, int valueB, int valueC, int valueD){
  if (lerpAmount < 1)
  {
    lerpAmount += amountA;
    powerUpCounter = (int)lerp(valueA,valueB,lerpAmount);
  }

  if (lerpScale < 1)
  {
    lerpScale += amountB;
    tvheight = (int)lerp(valueC,valueD,lerpScale);
  }  

  // final horiontal line shrink on power off
  if (!tvstate && tvheight < 100)
  {
    if (lerpShrink < 1)
    {
      tvwidth = (int)lerp(width,1,lerpShrink);
      lerpShrink += .09;



    }
  }
}

// RGB Distort
void renderDistort() {
  int i = 0;

  int offRed  = (int)(Math.random() * 2) * 2;
  int offGreen= (int)(Math.random() * 2) * 2;
  int offBlue = (int)(Math.random() * 2) * 2;

  if (barY2 > h) {
    barY1=10 -40;
    barY2=30 -40;
  }

  barY1 +=2;
  barY2 +=2;

  if (tvstate)
    scaleIt(0.02,0.2,156,0,2,tvscreen.height);  // turning on 
  else
    scaleIt(0.1,0.2,0,-400,tvscreen.height,1);   // turning off

  // dark vs light flicker + gradual fade in
  int flicker = (offBlue*8)+powerUpCounter; 

  for ( int y = 1; y < h; y++ ) {

    // vertically moving horizonal strip + flicker
    int colDiv = ( y < barY2 && y > barY1 ) ? 20+flicker : flicker; 

    // horizontal scanlines
    int strips=(y&1)*64 +colDiv; 

    // grab a random line of precalculated TV noise
    int noiseLine = int(random(0,height)) * width;

    for ( int x=0; x < w; x++ ) {
      int imagePixelR = tvscreen.pixels[i+offRed] >> 16 & 0xFF ;
      int imagePixelG = tvscreen.pixels[i+offGreen] >> 8 & 0xFF ;
      int imagePixelB = tvscreen.pixels[i+offBlue] & 0xFF ; 
      int processEffect = -strips-ppx[noiseLine+x];
      tvnoise.pixels[i++] =  color(imagePixelR+processEffect, imagePixelG+processEffect, imagePixelB+processEffect);      
    }
  }
  tvnoise.updatePixels();

  //  image(tvnoise,0,0,tvscreen.width,270);




  image(tvnoise,(tvscreen.width-tvwidth/2)-tvscreen.width/2,(tvscreen.height-tvheight/2)-tvscreen.height/2,tvwidth,tvheight);
}

void mousePressed(){
  lerpAmount = 0;
  lerpScale = 0;
  lerpShrink = 0;
  tvstate = !tvstate;
  //tvheight=280;
  tvwidth=width;  
  println(" tvheight: " + tvheight + " tvscreen.height: " + tvscreen.height );
}

Answers

  • Unfortunately it seems like it's not working for me

    in what way? what do you expect? what is it doing instead? 'not working' doesn't help us.

    an can you supply the images? we can't run it without the images

  • edited February 2018

    Unfortunately I can't figure out how to download the images from openprocessing. Not sure if there is anyway to figure out what they were since it's a legacy sketch, but I'm not very familiar with the site. I just changed the images to dummy images, nothing happened at all. Only the TVoverlay image was displayed.

  • edited February 2018

    Oh, perhaps because this was written in processing.js and I'm using processing 3.3 which doesn't support processing.js? Is there any way to convert it into normal Java mode?

  • Answer ✓

    that's not .js, it's a standard .pde file. it looks fine.

    (processing.js is a transpiler that can convert .pde to .js for use on web pages. but this was submitted in 2013 which probably predates that)

    i'm guessing the second image is important... the way it's displayed at the bottom of draw() like it is suggests there's some transparency going on, otherwise it would just overwrite what's currently in the buffer.

  • Ah, that makes sense. It works with an image.

    Can I ask a few noob questions about the syntax?

    (int)(Math.random() * 2) * 2;

    What is the logic behind this? Doesn't random need a limit? What does "math." do? Why multiply by two and then two again? Also, why does it seem like he uses extra parenthesis?

    Shouldn't it be offsetRed = int(Math.random() *2) *2;?

  • Another question, in

    int imagePixelR = tvscreen.pixels[i+offRed] >> 16 & 0xFF ;

    What does ">> 16 & 0xFF ;" do? I understand none of that.

    Finally

    int colDiv = ( y < barY2 && y > barY1 ) ? 20+flicker : flicker;

    "? 20+flicker : flicker;"

    Same problem here, what is this for exactly? What do the ? and : characters mean?

    Thanks!

  • edited February 2018 Answer ✓

    random needs a limit, but that is Math.random, java's version, which has different parameters

    Math.random() returns 0-1 (not including 1) so Math.random() * 2 returns 0 to 2 (not including 2). int() of that is 0 or 1. * 2 of that is 0 or 2. so the above returns 0 or 2. the equiv using random() would be (int)random(2) * 2;

    https://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#random()

  • edited February 2018 Answer ✓

    tvscreen.pixels[i+offRed] >> 16 & 0xFF ;

    colours are packed into each of the ints in the pixels array as

    aaaaaaaa rrrrrrrr gggggggg bbbbbbbb (a = alpha...)

    that gets the pixel, shifts it 16 times right and then masks it with 0xff. this leaves

    00000000 00000000 00000000 rrrrrrrr

    ie, it's exactly the same as calling red() on a pixel (but faster)

    in fact, it's mentioned in the reference page for red()

    https://processing.org/reference/red_.html

  • edited February 2018 Answer ✓

    int colDiv = ( y < barY2 && y > barY1 ) ? 20+flicker : flicker;

    a == 0 ? b() : c()

    is shorthand for if (a == 0) then b() else c()

    i don't like it because it tends to be less readable than writing it out longhand. if i wanted my code to be more punctuation than actual words i'd learn perl.

    https://processing.org/reference/conditional.html

  • Wow, thanks for your crystal clear explanations. The hex system sure is a bit confusing. Good to know about math.random and now I finally know how to decipher a bit of that punctuation code. I definitely agree on the readability aspect. Wouldn't use that myself but definitely good to know for reading other's code.

  • those last two ARE in the reference, but you have to know to look for them. i've added links above.

Sign In or Register to comment.