Krystian Niżeniec
Krystian Niżeniec

Reputation: 101

Why is my pixel manipulation function so slow?

Ok, so I need my own pixel manipulation function. Found something like ths on the internet, changed it a bit and my questios is, why is it so laggy? I need it for a bigger project and this function seems o be a problem. What can I do to improve it? Here's the necessary code:

  class kolo {
  int x;
  int y;
  kolo(int _x, int _y) {
    x=_x;
    y=_y;
  }
  void rysuj() {
    fill(255, 0, 0);
    circle(x, y, 60);
  }
}


ArrayList tab, punkty;
kolo p1=new kolo(100, 400);
kolo p2=new kolo(230, 400);
kolo p3=new kolo(360, 400);
kolo p4=new kolo(490, 400);
boolean drawing=true;
int N=20;


void setup() {
  size(800, 800);
  tab = new ArrayList<kolo>(4);
  punkty=new ArrayList<PVector>(N);
  tab.add(p1);
  tab.add(p2);
  tab.add(p3);
  tab.add(p4);

}

void draw() {
  background(0);

  p1.rysuj();
  p2.rysuj();
  p3.rysuj();
  p4.rysuj();
  stroke(100, 100, 100);
  line(p1.x, p1.y, p2.x, p2.y);
  line(p2.x, p2.y, p3.x, p3.y);
  line(p3.x, p3.y, p4.x, p4.y);
  for(int i=0; i<50; i++){
   putPixel(200, i+50, 255,255,255); 
  }

}


int teller() {
  if (dist(mouseX, mouseY, p1.x, p1.y) < 60) return 1;
  if (dist(mouseX, mouseY, p2.x, p2.y) < 60) return 2;
  if (dist(mouseX, mouseY, p3.x, p3.y) < 60) return 3;
  if (dist(mouseX, mouseY, p4.x, p4.y) < 60) return 4;
  else return 0;
}

void mouseDragged() {
  if (teller()==1) {
    p1.x=mouseX;
    p1.y=mouseY;
    redraw();
  }
  if (teller()==2) {
    p2.x=mouseX;
    p2.y=mouseY;
    redraw();
  }
  if (teller()==3) {
    p3.x=mouseX;
    p3.y=mouseY;
    redraw();
  }
  if (teller()==4) {
    p4.x=mouseX;
    p4.y=mouseY;
    redraw();
  }
}



void putPixel(int x, int y, int r, int g, int b) {
  loadPixels();
  drawing=true;
  for (int i = 0; i < width; i++) 
  {
    for (int j = 0; j < height; j++) 
    {

      int loc = x + y * width;
      if (loc<width*height)
        //print(loc);
        pixels[loc] = color(r, g, b);
      loc=0;
    }
  }
  updatePixels();
}

If you try it yourself, you'll see that the little line drawn with those pixels is lagging the whole sketch, moving those balls without it is smooth.

Upvotes: 1

Views: 233

Answers (1)

George Profenza
George Profenza

Reputation: 51837

The putPixel function is extremely inefficient in your case.

It loops through and updates all pixels in your sketch just set a single pixel, every single time it's called. It does the same as set() just more long winded and inefficient.

The way it's used is also inefficient:

  for(int i=0; i<50; i++){
   putPixel(200, i+50, 255,255,255); 
  }

This draws a 50 px tall line, same as line(200,50,200,100);

full listing:

class kolo {
  int x;
  int y;
  kolo(int _x, int _y) {
    x=_x;
    y=_y;
  }
  void rysuj() {
    fill(255, 0, 0);
    ellipse(x, y, 60,60);
  }
}


ArrayList tab, punkty;
kolo p1=new kolo(100, 400);
kolo p2=new kolo(230, 400);
kolo p3=new kolo(360, 400);
kolo p4=new kolo(490, 400);
boolean drawing=true;
int N=20;


void setup() {
  size(800, 800);
  tab = new ArrayList<kolo>(4);
  punkty=new ArrayList<PVector>(N);
  tab.add(p1);
  tab.add(p2);
  tab.add(p3);
  tab.add(p4);

}

void draw() {
  background(0);

  p1.rysuj();
  p2.rysuj();
  p3.rysuj();
  p4.rysuj();
  stroke(100, 100, 100);
  line(p1.x, p1.y, p2.x, p2.y);
  line(p2.x, p2.y, p3.x, p3.y);
  line(p3.x, p3.y, p4.x, p4.y);
  line(200,50,200,100);
  //for(int i=0; i<50; i++){
  // putPixel(200, i+50, 255,255,255); 
  //}
  surface.setTitle((int)frameRate + "fps");
}


int teller() {
  if (dist(mouseX, mouseY, p1.x, p1.y) < 60) return 1;
  if (dist(mouseX, mouseY, p2.x, p2.y) < 60) return 2;
  if (dist(mouseX, mouseY, p3.x, p3.y) < 60) return 3;
  if (dist(mouseX, mouseY, p4.x, p4.y) < 60) return 4;
  else return 0;
}

void mouseDragged() {
  if (teller()==1) {
    p1.x=mouseX;
    p1.y=mouseY;
    redraw();
  }
  if (teller()==2) {
    p2.x=mouseX;
    p2.y=mouseY;
    redraw();
  }
  if (teller()==3) {
    p3.x=mouseX;
    p3.y=mouseY;
    redraw();
  }
  if (teller()==4) {
    p4.x=mouseX;
    p4.y=mouseY;
    redraw();
  }
}



void putPixel(int x, int y, int r, int g, int b) {
  loadPixels();
  drawing=true;
  for (int i = 0; i < width; i++) 
  {
    for (int j = 0; j < height; j++) 
    {

      int loc = x + y * width;
      if (loc<width*height)
        //print(loc);
        pixels[loc] = color(r, g, b);
      loc=0;
    }
  }
  updatePixels();
}

Currently your code mostly uses drawing functions (e.g. line(), ellipse(), etc.), not a lot pixel manipulation.

If you do want to manipulate pixels, you can. What's important to know is that set() sets a single pixel but updates the whole images.

The more efficient way of manipulating pixels is accessing the pixels\[\], manipulating many in a batch, then updating the whole image once at the end using updatePixels()

You will need to convert from a x,y position (2D array indexing) to a single 1D index for the pixels[]. Your code already includes this: x + y * width

set() example :

import java.util.Arrays;

PImage canvas;

void setup(){
  size(300,300);
  canvas = createImage(width,height,RGB);
}

void draw(){
  manipulatePixels();
  // render pixels
  image(canvas,0,0);  
}

void manipulatePixels(){
  //clear frame
  Arrays.fill(canvas.pixels,0);
  //draw something
  float scalar = sin(frameCount * 0.1);
  slowCircle(canvas,mouseX,mouseY,(int)map(scalar,-1.0,1.0,10,30),color(map(scalar,-1.0,1.0,64,255)));
}

void slowCircle(PImage canvas,int x,int y,int radius,color fill){
  for(int py = y - radius; py < y + radius; py++){
    for(int px = x - radius; px < x + radius; px++){
      if(dist(x,y,px,py) < radius){
        canvas.set(px,py,fill);
      }
    }
  } 
}

pixels[] example:

import java.util.Arrays;

PImage canvas;

void setup(){
  size(300,300);
  canvas = createImage(width,height,RGB);
}

void draw(){
  manipulatePixels();
  // render pixels
  image(canvas,0,0);  
}

void manipulatePixels(){
  //clear frame
  Arrays.fill(canvas.pixels,0);
  //draw something
  float scalar = sin(frameCount * 0.1);
  //slowEllipse(canvas,mouseX,mouseY,(int)map(scalar,-1.0,1.0,10,30),color(map(scalar,-1.0,1.0,64,255)));
  fastCircle(canvas,mouseX,mouseY,(int)map(scalar,-1.0,1.0,10,30),color(map(scalar,-1.0,1.0,64,255)));
  // update all Pixels in one go
  canvas.updatePixels();
}

void slowCircle(PImage canvas,int x,int y,int radius,color fill){
  for(int py = y - radius; py < y + radius; py++){
    for(int px = x - radius; px < x + radius; px++){
      if(dist(x,y,px,py) < radius){
        canvas.set(px,py,fill);
      }
    }
  } 
}

void fastCircle(PImage canvas,int x,int y,int radius,color fill){
  int radiusSquared = radius * radius;
  int halfRadius = radius / 2;
  int halfRadiusSquared = halfRadius * halfRadius;
  int numPixels = canvas.pixels.length;
  //for each pixel in the ellipse bounding box
  for(int i = 0 ; i < radiusSquared; i++){
    int cx = (x - halfRadius) + (i % radius);
    int cy = (y - halfRadius) + (i / radius);
    // calculate the index within the full image
    int pixelIndex = (cx + cy * canvas.width);
    // constrain pixel index to array size bounds
    if(pixelIndex < 0) pixelIndex = 0;
    if(pixelIndex >= numPixels) pixelIndex = numPixels-1;
    // calculate the difference between circle centre and current pixel coordinates
    int dx = cx-x;
    int dy = cy-y;
    // calculate distance squared
    int distanceSquared = (dx*dx) + (dy*dy);
    // compare if the pixel is inside the circle using squared distance (a bit faster since it avoids sqrt())
    if(distanceSquared < halfRadiusSquared){
      //fill circle pixels
      canvas.pixels[pixelIndex] = fill;
    }
  }
}

It's worth noting you can easily rasterise shapes to pixels using PGraphics.

You can see a fun PGraphics example here

Where to go next ? If you've got the hang of playing with pixels[] and you want to create an awesome effect that might run slow on the CPU you can look at moving to the GPU and Processing supports that via PShader: it's the fragment shader part you're after. You can see an example of porting pixels[] based CPU code to a GPU fragment shader here

GLSL fragment shader

Upvotes: 3

Related Questions