Old 02-14-2023, 03:57 AM   #1
antiClick
Human being with feelings
 
antiClick's Avatar
 
Join Date: Mar 2007
Location: Mediterrenean Sea
Posts: 979
Default Rutt Etra video effect

Hi!

I was wondering if somebody did actually try to code a Rutt/Etra video effect. It's a very cool effect that was very used by analog video synth pioneers from the 70's.
I'v searched in ReaPack and in the forums but couldn't find anything.



and as a video:
https://youtu.be/bIrL1fMmm_k


Some code I found in javascript that seems to be very related, but looking at the included reaper video fx I cannot seem to understand the logic under these. For ex., how do you iterate through all the pixels in the current frame?

Would it be viable to iterate through every pixel, send it to an image buffer and then render via blit? Would it be better to blit directly? can you blit several times per frame? would it have a too big impact on performance?

Too many questions, and the documentation is barely inexistent.
I'd really apreciate a little bit of help.

Kind Regards!


Attached Images
File Type: jpeg WhatsApp Image 2023-02-14 at 12.02.47.jpeg (72.1 KB, 499 views)

Last edited by antiClick; 02-14-2023 at 08:38 AM.
antiClick is offline   Reply With Quote
Old 02-14-2023, 04:41 AM   #2
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Cool effect! This is a good candidate for "gfx_evalrect()" that processes code for each pixel. Unfortunately I won't be able to look into this type of effects for a couple of months. Also there is a sharpen filter is in the queue. If you are interested in "gfx_evalrect()" coding you can look at this thread and also to the new functions available since v6.70.
papagirafe is offline   Reply With Quote
Old 02-15-2023, 10:14 AM   #3
antiClick
Human being with feelings
 
antiClick's Avatar
 
Join Date: Mar 2007
Location: Mediterrenean Sea
Posts: 979
Default

Quote:
Originally Posted by papagirafe View Post
Cool effect! This is a good candidate for "gfx_evalrect()" that processes code for each pixel. Unfortunately I won't be able to look into this type of effects for a couple of months. Also there is a sharpen filter is in the queue. If you are interested in "gfx_evalrect()" coding you can look at this thread and also to the new functions available since v6.70.
I looked into that and I think I got even more confused.
Could you please point me some documentation about the subject?

Any extra help would also be very appreciated. Maybe some examples I could use as a starting point.... like painting top corner pixels in pink or something like that.
Thanks
antiClick is offline   Reply With Quote
Old 02-15-2023, 12:58 PM   #4
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 7,417
Default

Yeah this looks really cool.

If I understand the Javascript code correctly, a pixel different from the current pixel is changed based on the RGB values of the current pixel. I am not sure that this can be done in gfx_evalrect. If anyone knows how to do that, please let us know.

A non-functional skeleton for this that I crafted from the javascript code:
Code:
function processRuttEtra(x0, y0, w0, h0, step)
(
    xx = x0; yy = y0; 
    res = gfx_evalrect(x0, y0, w0, h0,
    "
    (yy % step) == 0 ? 
    (
        lum = r * 0.34 + g * 0.5 + b * 0.16; // r, g, b are the current pixel's color
        z = floor(lum/16); // not exactly same as Math.round(), but close
        newy = max(0, y-z);
        // Here the pixel (xx, newy) is updated with the RGB values of (xx, yy)
        // Don't know how to do that...
    );
    r = g = b = 0; // set the current pixel (xx, yy) to black
    (xx += 1) >= w0 ? (xx = x0; yy += 1); // next pixel
    ",
    1);
);
I assume here (but don't know) that pixData[(y*can.width + x)*4 + n] of the Javascript code gives the red for n=0, green n=1, blue n=2, values for pixel (x, y), which would be pixel (xx, yy) in the code above.
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...
Fabian is offline   Reply With Quote
Old 02-15-2023, 01:16 PM   #5
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by antiClick View Post
I looked into that and I think I got even more confused.
Could you please point me some documentation about the subject?

Any extra help would also be very appreciated. Maybe some examples I could use as a starting point.... like painting top corner pixels in pink or something like that.
Thanks
Pressing F1 in the editor window will give you a "leaf view" documentation of each video processor function but unfortunately nothing about the tree nor the forest. Ultraschall has put the same documentation on his site + a short starting guide.
Nevertheless here are simple examples of gfx_evalrect():
Code:
//gfx_evalrect() painting in RGB
//@param pr r 0 0 255 128 1
//@param pg g 0 0 255 128 1
//@param pb b 0 0 255 128 1
colorspace='RGBA';
gfx_set(1,1,1,1,0,-1,1);    //want 100% solid white for frame buffer
gfx_img_resize(-1,400,400,1);  //resize frame buffer to 400x400 and clear it with white
//lets paint a center square with our chosen color
gfx_evalrect(100,100,200,200,"r=pr;g=pg;b=pb",1); //flag=1 means single thread
Code:
//gfx_evalrect() invert colors (RGB)
src=0;    //current video source input#
input_info(src,sw,sh)?(   //lets make sure we have a source
  colorspace='RGBA';  //makes variables R G B available
  gfx_set(0,0,0,1,0,-1,1);    //explicitly set all parameters 
  gfx_img_resize(-1,sw,sh);   //make sure the frame buffer is same size as source
  gfx_blit(0);  //copy current source in frame buffer
  gfx_evalrect(0,0,sw,sh,"r=255-r;g=255-g;b=255-b",1);  //flag=1 means single thread = better for testing
);
Code:
//gfx_evalrect() invert colors (YUY2)
src=0;    //current video source input#
input_info(src,sw,sh)?(   //lets make sure we have a source
  colorspace='YUY2';  //makes variables R G B available
  gfx_set(0,0,0,1,0,-1,1);    //explicitly set all parameters 
  gfx_img_resize(-1,sw,sh);   //make sure the frame buffer is same size as source
  gfx_blit(0);  //copy current source in frame buffer
  gfx_evalrect(0,0,sw,sh,"u=255-u;v=255-v;y1=255-y1;y2=255-y2",1);  //flag=1 means single thread = better for testing
);
papagirafe is offline   Reply With Quote
Old 02-15-2023, 01:35 PM   #6
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by Fabian View Post
Yeah this looks really cool.

If I understand the Javascript code correctly, a pixel different from the current pixel is changed based on the RGB values of the current pixel. I am not sure that this can be done in gfx_evalrect. If anyone knows how to do that, please let us know.

<snip>
The example given in the documentation of gfx_evalrect(),
"r[0]+=1; g[256]+=1; b[512]+=1;
(0.299*r + 0.587*g + 0.114*b)[768] += 1;"
seems to suggest that it is possible but I am still not quite sure how to use the vector addressing in this context. "0,256,512,768" are no trivial values but I have no clue what this example does.

Last edited by papagirafe; 02-15-2023 at 02:01 PM. Reason: incorrect info
papagirafe is offline   Reply With Quote
Old 02-15-2023, 05:55 PM   #7
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

I’ve never understood evalrect, and would love to see some more examples or documentation. The brute force way would be to use getpixel to see each pixel value and then single pixel blits (we don’t have setpixel in VP).

Last edited by ashcat_lt; 02-15-2023 at 06:21 PM.
ashcat_lt is offline   Reply With Quote
Old 02-16-2023, 04:21 AM   #8
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by ashcat_lt View Post
I’ve never understood evalrect, and would love to see some more examples or documentation. The brute force way would be to use getpixel to see each pixel value and then single pixel blits (we don’t have setpixel in VP).
I provided a few simple examples of evalrect() above but here is an emulation of gfx_setpixel():
Code:
//gfx_setpixel() emulation
function gfx_setpixel(x,y,r,g,b)
  global(_spr,_spg,_spb)
(
  _spr=r;_spg=g;_spb=b;  //evalrect can only access globals or thread variables
  gfx_evalrect(x,y,1,1,"r=_spr;g=_spg;b=_spb");
);

//some testing code...
colorspace='RGBA';
gfx_set(0);
gfx_img_resize(-1,200,200,1); //400x400 black square bitmap
res=gfx_setpixel(100,100,255,0,0);  //one red pixel in the middle
papagirafe is offline   Reply With Quote
Old 02-16-2023, 09:23 AM   #9
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

I guess when I said “single pixel blit” I meant single pixel _fillrect. (How) Is _evalrect better at that?
ashcat_lt is offline   Reply With Quote
Old 02-16-2023, 10:29 AM   #10
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by ashcat_lt View Post
I guess when I said “single pixel blit” I meant single pixel _fillrect. (How) Is _evalrect better at that?
From my experience, gfx_fillrect() needs at least 2 pixels wide to work properly but either one are equivalent in performance.
papagirafe is offline   Reply With Quote
Old 02-16-2023, 10:48 AM   #11
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by papagirafe View Post
The example given in the documentation of gfx_evalrect(),
"r[0]+=1; g[256]+=1; b[512]+=1;
(0.299*r + 0.587*g + 0.114*b)[768] += 1;"
seems to suggest that it is possible but I am still not quite sure how to use the vector addressing in this context. "0,256,512,768" are no trivial values but I have no clue what this example does.
update: this example is misleading. I checked r[0] g[whatever] are regular vector variables. Lets suppose red value = 200, "r[0]" means the 200+0 = position 200 in the unfamous EEL shared vector. if green value = 40, g[256] means 256+40=position 296. COnclusion: the first line of the example just collects stats on how many pixels of each level you have and the second does the same but with a weirder formula.

In other words, we cannot address other pixel except the current one. The only solution would be to convert the algorythm to multiple phases.
papagirafe is offline   Reply With Quote
Old 02-16-2023, 11:24 AM   #12
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 7,417
Default

Quote:
Originally Posted by papagirafe View Post
update: this example is misleading. I checked r[0] g[whatever] are regular vector variables. Lets suppose red value = 200, "r[0]" means the 200+0 = position 200 in the unfamous EEL shared vector. if green value = 40, g[256] means 256+40=position 296. COnclusion: the first line of the example just collects stats on how many pixels of each level you have and the second does the same but with a weirder formula.
Thanks for clearing this up.

Quote:
Originally Posted by papagirafe View Post
In other words, we cannot address other pixel except the current one. The only solution would be to convert the algorythm to multiple phases.
That's a pity. Have to think of other ways to achieve the Rutt Etra, then. Assuming that I interpreted the Javascript code correctly; and now I think I did Javascript ImageData.
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...
Fabian is offline   Reply With Quote
Old 02-16-2023, 07:48 PM   #13
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Quote:
Originally Posted by papagirafe View Post
From my experience, gfx_fillrect() needs at least 2 pixels wide to work properly but either one are equivalent in performance.
Don't know what your deal is there, but I've confirmed that it works a number of different ways.

This seems to do the thing:
Code:
//Rutt
step = 4;

oldCS = colorspace;
colorspace = 'RGBA';
pw = project_w;
ph = project_h;
gfx_set (0, 0, 0, 1, 0, -1 );
x = y = z = newy = lum = r = g = b = 0;

while (y < ph)
  (x = 0;
   while (x < pw)
     (y % step == 0 ?
        (gfx_getpixel (0, x, y, r, g, b);
         lum = r * 0.34 + g * 0.5 + b * 0.16; 
         z = floor((lum/16) + 0.5); 
         newy = max(0, y-z);         
         gfx_set (r, g, b);
         gfx_fillrect (x, newy, 1, 1););
      gfx_set (0, 0, 0);
      gfx_fillrect (x, y, 1, 1);
      z = newy = lum = r = g = b = 0;
      x += 1;);
   y += 1;);
colorspace = oldCS;
I wrote it on this crappy old laptop so it's tough to say what kind of performance you'd get on a real machine. It could obviously be refined a bit with a parameter or two, maybe functionized or whatever, but it proves the concept and will work the way the OP example code does.
ashcat_lt is offline   Reply With Quote
Old 02-17-2023, 04:08 AM   #14
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by ashcat_lt View Post
Don't know what your deal is there, but I've confirmed that it works a number of different ways.

This seems to do the thing:
<snip>
I wrote it on this crappy old laptop so it's tough to say what kind of performance you'd get on a real machine. It could obviously be refined a bit with a parameter or two, maybe functionized or whatever, but it proves the concept and will work the way the OP example code does.
Wow super! I'll give it a try soon. BTW you're right: gfx_fillrect() does work for 1x1 but the minimal bitmap size is 1x2...my confusion with limits testing I did in the past.
Also with current versions of Reaper (and after numerous confirmations), each new instance of video processor gets its most cucial context variables restored to default/project's values, which mean you don't need to save/restore them. Namely:
  • frame buffer (-1) gets restored to project's dimensions (or input 0 dims if not set)
  • colorspace is back to project's settings (auto, RGBA,YU12 etc...)
  • defaults to gfx_set(0,0,0,1,0,-1,1)
  • project_w/project_h to project's dims if set
papagirafe is offline   Reply With Quote
Old 02-17-2023, 10:00 AM   #15
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Yeah I suppose I sometimes just like to be explicit. Also, if somebody was to functionize this and put it into a bigger preset, there’s no good way of knowing where things would be set when called, so you might kind of want some of these things to be handled internally. Might do an if/then or something, but…. I really just assigned _w and _h to shorter variables so I didn’t have to type so much.
ashcat_lt is offline   Reply With Quote
Old 02-17-2023, 10:54 AM   #16
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by ashcat_lt View Post
Yeah I suppose I sometimes just like to be explicit. Also, if somebody was to functionize this and put it into a bigger preset, there’s no good way of knowing where things would be set when called, so you might kind of want some of these things to be handled internally. Might do an if/then or something, but…. I really just assigned _w and _h to shorter variables so I didn’t have to type so much.
No offense or critic intended, I just thought this was the right time and place to insert this piece of knowledge gathered over time. Thanks again for providing such a fast solution to this challenge!
papagirafe is offline   Reply With Quote
Old 02-17-2023, 11:13 AM   #17
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 7,417
Default

Quote:
Originally Posted by ashcat_lt View Post
This seems to do the thing:
Code:
//Rutt
step = 4;

oldCS = colorspace;
colorspace = 'RGBA';
pw = project_w;
ph = project_h;
gfx_set (0, 0, 0, 1, 0, -1 );
x = y = z = newy = lum = r = g = b = 0;

while (y < ph)
  (x = 0;
   while (x < pw)
     (y % step == 0 ?
        (gfx_getpixel (0, x, y, r, g, b);
         lum = r * 0.34 + g * 0.5 + b * 0.16; 
         z = floor((lum/16) + 0.5); 
         newy = max(0, y-z);         
         gfx_set (r, g, b);
         gfx_fillrect (x, newy, 1, 1););
      gfx_set (0, 0, 0);
      gfx_fillrect (x, y, 1, 1);
      z = newy = lum = r = g = b = 0;
      x += 1;);
   y += 1;);
colorspace = oldCS;
Ah!
A 1x1 rectangle to set a pixel. Of course.

Maybe I'm Dunning-Kruger'ing here but...
I think there are two things different from Reaper and the Javascript code:
  1. The RGB values are 0..255 in Javascript, and
  2. Each pixel is represented by 4 such values
Because of this, probably lum needs to be scaled by 255, since Reaper's RGB values are 0..1. And also I think lum should be divided by 4, instead of 16, when computing z.

But I guess that will reveal itself when we look at the actual result. Has anyone tried it in the video processor yet?

EDIT: OK I tried it in Reaper now, and I do not get the expected results. But my hunches above don't seem to be correct either.

There's a nice web page here Rutt-Etra-Izer, and this gets me what I expect when I load the same picture there as I have in Reaper.
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...

Last edited by Fabian; 02-17-2023 at 12:40 PM.
Fabian is offline   Reply With Quote
Old 02-18-2023, 11:40 AM   #18
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 7,417
Default

Quote:
Originally Posted by ashcat_lt View Post
Code:
//Rutt
step = 4;

oldCS = colorspace;
colorspace = 'RGBA';
pw = project_w;
ph = project_h;
gfx_set (0, 0, 0, 1, 0, -1 );
x = y = z = newy = lum = r = g = b = 0;

while (y < ph)
  (x = 0;
   while (x < pw)
     (y % step == 0 ?
        (gfx_getpixel (0, x, y, r, g, b);
         lum = r * 0.34 + g * 0.5 + b * 0.16; 
         z = floor((lum/16) + 0.5); 
         newy = max(0, y-z);         
         gfx_set (r/255, g/255, b/255);
         gfx_fillrect (x, newy, 1, 1););
      gfx_set (0, 0, 0);
      gfx_fillrect (x, y, 1, 1);
      z = newy = lum = r = g = b = 0;
      x += 1;);
   y += 1;);
colorspace = oldCS;
There was a small issue in the original code, that I now fixed, see the bold line above. gfx_getpixel returns rgb values in 0..255, while gfx_set takes rgb values in 0..1. This was revealed in this spinoff thread. So a division by 255 was needed.

This also explains why it wasn't necessary to scale the lum variable by 255, as I suggested above.
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...

Last edited by Fabian; 02-18-2023 at 12:10 PM.
Fabian is offline   Reply With Quote
Old 02-18-2023, 12:44 PM   #19
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Weird. Glad you fingered it out! It’s strangish to me, because I’m working with gfx in JS atm and when I saw your post yesterday I realized I need to scale things coming out of getpixel to work properly, and it does. So I guess that’s another one of those weird little things which are different between JS and VP?!?
ashcat_lt is offline   Reply With Quote
Old 02-18-2023, 12:54 PM   #20
Fabian
Human being with feelings
 
Fabian's Avatar
 
Join Date: Sep 2008
Location: Sweden
Posts: 7,417
Default

Quote:
Originally Posted by ashcat_lt View Post
So I guess that’s another one of those weird little things which are different between JS and VP?!?
Yeah, there are some of those, for sure. The one that keeps biting me is gfx_blit:
Code:
Video processor: gfx_blit(input[,preserve_aspect=0,x,y,w,h,srcx,srcy,srcw,srch])

JSFX: gfx_blit(source, scale, rotation[, srcx, srcy, srcw, srch, destx, desty, destw, desth, rotxoffs, rotyoffs])
__________________
// MVHMF
I never always did the right thing, but all I did wasn't wrong...
Fabian is offline   Reply With Quote
Old 02-18-2023, 01:32 PM   #21
ashcat_lt
Human being with feelings
 
Join Date: Dec 2012
Posts: 7,272
Default

Yeah I think blit in JS is more like rotoblit, though I haven’t messed with it much. I think I vaguely remember a mismatch when I was passing pixels back and forth via gmem, now that we mention it…
ashcat_lt is offline   Reply With Quote
Old 02-19-2023, 07:50 AM   #22
papagirafe
Human being with feelings
 
papagirafe's Avatar
 
Join Date: Aug 2020
Location: Brasil
Posts: 679
Default

Quote:
Originally Posted by Fabian View Post
Yeah, there are some of those, for sure. The one that keeps biting me is gfx_blit:
Code:
Video processor: gfx_blit(input[,preserve_aspect=0,x,y,w,h,srcx,srcy,srcw,srch])

JSFX: gfx_blit(source, scale, rotation[, srcx, srcy, srcw, srch, destx, desty, destw, desth, rotxoffs, rotyoffs])
The secret for consistent results for gfx_blit() is to never rely on "preserve aspect" to do the work for you. So its better to put all parameters explicitly (or just the first if you resized the frame buffer to your needs)
Code:
gfx_blit(
  source_input_id,
  0,      //let me control the output
  targetXdest, targetYdest,targetWidth,targetHeight,
  xOffsetInSourceBitmap,YOffsetInSourceBitmap, Width2copyfromSource,Heigth2CopyFromSource
);
// or the alternative
gfx_img_resize(-1,targetWidth,targetHeight);
gfx_blit(source_input);
This explains also how to zoom with different scales on x/y axis

Last edited by papagirafe; 02-19-2023 at 09:33 AM.
papagirafe is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 08:41 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.