-2

I've been wondering how to efficiently implement the following image scale procedure in Java or Processing. When scaling out, the bounds of the image wrap around the screen edges.I'd like to apply the same at runtime to my Pixels() array in Processing. (to keep this Processing agnostic - Pixels() is nothing else than a method that returns all pixels on my current screen in an array).

(Note that this example was made in MaxMsp/Jitter using the jit.rota module, which appears to use a very efficient implementation).

unscaled

zoomed out

Can anyone help me out on how to get started? I assume it must be a combination of downscaling the image and creating adjactent copies of it - but this doesn't sound very efficient to me. the above example works perfectly on videos with even the most extreme settings.

user3641187
  • 405
  • 5
  • 10

2 Answers2

1

One option I can think that will be fast is using a basic fragment shader.

Luckily you've got an example pretty close to what you need that ships with Processing via File > Examples > Topics > Shaders > Infinite Tiles

I won't be able to efficiently provide a decent start to finish guide, but there's an exhaustive PShader tutorial on the Processing website if you're starting the from scratch.

A really rough gist of what you need:

  • shaders are programs that run really fast and parallelised on the GPU, split into two: vertex shaders (deal with 3d geometry mainly), fragment shaders (deal with "fragments"(what's about to become pixels on screen) mainly). You'll want to play with a fragment shader
  • The language is called GLSL and is a bit different(fewer types, stricter, simpler syntax), but not totally alien(similar C type of declaring variables, functions, conditions, loops, etc.)
  • if you want to make a variable from a GLSL program accessible in Processing you prefix it with the keyword uniform
  • use textureWrap(REPEAT) to wrap edges
  • to scale the image and wrap it you'll need to scale the texture sampling coordinates:

Here's what the InfiniteTiles scroller shader looks like:

//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------

uniform float time;
uniform vec2 resolution;
uniform sampler2D tileImage;

#define TILES_COUNT_X 4.0

void main() {
  vec2 pos = gl_FragCoord.xy - vec2(4.0 * time);
  vec2 p = (resolution - TILES_COUNT_X * pos) / resolution.x;
  vec3 col = texture2D (tileImage, p).xyz;
  gl_FragColor = vec4 (col, 1.0);
}

You can simplify this a bit as you don't need to scrolling. Additionally, instead of subtracting, and multiplying(- TILES_COUNT_X * pos), you can simply multiply:

//---------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//---------------------------------------------------------

uniform float scale;
uniform vec2 resolution;
uniform sampler2D tileImage;

void main() {
  vec2 pos = gl_FragCoord.xy * vec2(scale);
  vec2 p = (resolution - pos) / resolution.x;
  vec3 col = texture2D (tileImage, p).xyz;
  gl_FragColor = vec4 (col, 1.0);
}

Notice I've repurposed the time variable to become scale, therefore the Processing code accessing this uniform variable must also change:

//-------------------------------------------------------------
// Display endless moving background using a tile texture.
// Contributed by martiSteiger
//-------------------------------------------------------------

PImage tileTexture;
PShader tileShader;

void setup() {
  size(640, 480, P2D);
  textureWrap(REPEAT);
  tileTexture = loadImage("penrose.jpg");
  loadTileShader();
}

void loadTileShader() {  
  tileShader = loadShader("scroller.glsl");
  tileShader.set("resolution", float(width), float(height));  
  tileShader.set("tileImage", tileTexture);
}

void draw() {
  tileShader.set("scale", map(mouseX,0,width,-3.0,3.0));
  shader(tileShader);
  rect(0, 0, width, height);
}

Move the mouse to change scale: fragment shader scaling normal scale

fragment shader scaling small scale

Update You can play with a very similar shader here:

George Profenza
  • 50,687
  • 19
  • 144
  • 218
0

I effectively did come up with a solution - but will implement George's method next as the speed difference using shaders seems to be worth it!

public void scalePixels(double wRatio,double hRatio, PGraphics viewPort) {
    viewPort.loadPixels();
    int[] PixelsArrayNew = viewPort.pixels.clone();
    double x_ratio = wRatio ;
    double y_ratio = hRatio ;
    double px, py ;
    for (int i=0;i<viewPort.height;i++) {
        for (int j=0;j<viewPort.width;j++) {
            px = Math.floor(j%(wRatio*viewPort.width)/x_ratio) ;
            py = Math.floor(i%(hRatio*viewPort.height)/y_ratio) ;
            viewPort.pixels[(int)(i*viewPort.width)+j] = PixelsArrayNew[(int)((py*viewPort.width)+px)] ;
        }
    }
    viewPort.updatePixels();    
}
user3641187
  • 405
  • 5
  • 10