Reputation: 101
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
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
Upvotes: 3