Pixel Processing: Grabbing the Canvas, and Where's the Alpha?

Hello again.

I’d like to be able to grab a small area from the canvas and process the pixels in such a way that I can alter the color of intersecting lines. Here’s an example of what I’m trying to accomplish.

void setup(){
  size(200, 200); // P3D fixes PImage alpha?
  background(255, 255);
  
  noFill();
  
  stroke(0, 100);
  strokeWeight(1);
  strokeCap(SQUARE);
  rect(59, 59, 82, 82);

  strokeWeight(20);
  line(100, 60, 100, 140);
  line(60, 100, 140, 100);
  
  // is there a way to grab only a rectangle from the canvas instead of the whole thing?
  loadPixels();
  
  PImage img = createImage(80, 80, RGB);
  int cnt = 0;
  for (int x=60; x<140; x++){
    for (int y=60; y<140; y++){
      int loc = x + y*width;
      img.pixels[cnt] = pixels[loc];
      
      float r = red(img.pixels[cnt]);
      float a = alpha(img.pixels[cnt]);
      if (cnt % 250 == 0){ println(cnt," ",r," ",a); }
      if (r == 155){ 
        img.pixels[cnt] = color(0);
        // however, using the P3D renderer, img.pixels[cnt] = color(0, 20) doesn't seem to have alpha... WTF?
      } else if (r < 155) {
        img.pixels[cnt] = color(255);
      } else {
        // where's the alpha? 
        // (fixed using P3D renderer, which seems to initialize a PImage object with color(0,0))
        img.pixels[cnt] = color(255, 0);
        // however, using the P3D renderer, img.pixels[cnt] = color(0, 0, 255, 0); doesn't seem to have alpha... WTF?
      }
      cnt++;
    }
  }
  
  image(img, 0, 0);
}

I have a few questions, since I find this approach a bit awkward, and it doesn’t entirely work. I figure there’s got to be a better way to do this.

  1. Is there a way to grab only a part of the canvas rather than execute loadPixels() which grabs it all?
  2. PImage provides a way to get() pixel colors, or to grab a rect of pixels. But loadPixels() only seems to be able to access its color data via the pixels array. Is there a way to use get() from loadPixels()?
  3. Printing the red and alpha values shows that loadPixels() doesn't retrieve alpha. The doc for PImage.copy() says: "No alpha information is used in the process, however if the source image has an alpha channel set, it will be copied as well.", which implies that the canvas doesn't contain alpha. So how do I obtain alpha information from the canvas?
  4. The PImage object I create replaces white with full transparency, yet it doesn't work. Why not? Because the source image doesn't have alpha? But since I'm defining the pixels in the object, shouldn't it be able to create its own alpha? Actually, I answered my own question by trying the P3D renderer, which seems to initialize a PImage object with color(0,0). And yet, when I apply alpha to a pixel, it seems to ignore it. What's going on?

Thank you for any help.

1 Like

AFAIK, the main canvas is 100% opaque. :roll_eyes:
And any alpha values in its pixels[] are simply ignored. :flushed:

So it seems, then, that I’ll need to create a PGraphics object and draw into it; then grab its data once its placed on the canvas as a PImage object?

1 Like

Both PImage & its subclass PGraphics objects have their alpha values respected when rendered by image(). :wink:

Then, citing my example above, why does alpha not work when I set it in my PImage object? (Eg: img.pixels[cnt] = color(0, 0, 255, 0); )

I guess a 100% transparent color renders a 100% invisible pixel AFAIK. :thinking:

And yet it appears bright blue…

I believe it’s b/c you’re using RGB instead of ARGB:

noLoop();

background(0);
imageMode(CENTER);

final int w = round(width * .75), h = round(height * .75);
println(w, h);

final PImage img = createImage(w, h, ARGB);  // 100% invisible
//final PImage img = createImage(w, h, RGB); // 100% visible blue

// Fill PImage w/ a 100% transparent blue color:
java.util.Arrays.fill(img.pixels, color(0, 0, 255, 0));
img.updatePixels();

image(img, width >> 1, height >> 1);
2 Likes

(…sigh…) It seems you’re right. I must tend to the docs more carefully…

Thank you.

Ok, so I’m using PGraphics to create my image now so that I’ll have alpha. According to the docs “Unlike the main drawing surface which is completely opaque, surfaces created with createGraphics() can have transparency. This makes it possible to draw into a graphics and maintain the alpha channel. By using save() to write a PNG or TGA file, the transparency of the graphics object will be honored.” Great, just what I want.

But… if I set the my PGraphics object to use the P3D renderer I get a NullPointerException in my pixel loop. If I don’t set the P3D renderer I don’t get alpha from my PGraphics object.

Is there some other obvious thing I’m overlooking here?

void setup(){
  size(200, 200, P3D);
  background(255);
  
  noFill();
  
  stroke(0, 100);
  strokeWeight(1);
  strokeCap(SQUARE);
  rect(59, 59, 82, 82);

  //// if I set the renderer to P3D...
  //PGraphics pg = createGraphics(80, 80);
  PGraphics pg = createGraphics(80, 80, P3D); // P3D restored
  pg.beginDraw();
  pg.background(255);
  pg.stroke(0, 100);
  pg.strokeWeight(20);
  pg.strokeCap(SQUARE);
  pg.line(40, 0, 40, 80); 
  pg.line(0, 40, 80, 40);
  pg.endDraw();
  
  imageMode(CENTER);  
  image(pg, 100, 100);
  
  pg.loadPixels(); // thx TfGuy44 post 11 below!
  for (int x=0; x<pg.width; x++){
    for (int y=0; y<pg.height; y++){
      int loc = x + y*pg.width;

      // ... I used to get a NullPointerException here, but not anymore with pg.loadPixels()
      float r = red(pg.pixels[loc]);

      // ... but the P3D renderer (still) gives me no alpha
      float a = alpha(pg.pixels[loc]);

      if (loc % 250 == 0){ println(loc," ",r," ",a); }
    }
  }
}

Hit the Docs again. Look for loadPixels(). :grinning:

That is, switch back to using P3D, and before you start your for loops for x and y, call pg.loadPixels();.

2 Likes

D’oh! Yes, forgot that step.

But I’m still not getting any alpha from the pixels array, even after switching the renderer back to P3D.

:sob:

Note: I edited the code at comment 10 to reflect the changes.

JAVA2D is Processing’s most stable renderer.
Unless you really need 3D capabilities, you’re better off sticking w/ JAVA2D.

The gray scale value 255 is 100% opaque.
If you need to clear the PGraphics w/ 100% transparency, use clear() instead:

3 Likes

D’OH! :scream:

Yes, that was it… of course…

I’ll just slink away now…

Alternatively, we can use pg.background(0, 0); in place of clear().
For 100% invisible white, use pg.background(255, 0);

Processing.org/reference/background_.html

3 Likes