PImage
Data / CompositeDescription
Datatype for storing and manipulating images. Load with loadImage() or create with createImage().
Syntax
PImage* img = loadImage("file.png")
PImage* img = createImage(w, h, ARGB)
Parameters
None
Returns
PImageMethods
| pixels[] | Array of color values for the image |
| width | Width of the image in pixels |
| height | Height of the image in pixels |
| loadPixels() | Load pixel data into pixels[] |
| updatePixels() | Update image from pixels[] |
| get(int x, int y) | Get pixel color at position |
| set(int x, int y, color c) | Set pixel color at position |
| resize(int w, int h) | Resize the image |
| filter(int mode) | Apply an image filter |
| mask(PImage m) | Apply an alpha mask |
Related
Under the Hood
From Processing.h:
class PImage {
public:
int width = 0;
int height = 0;
std::vector<unsigned int> pixels;
GLuint texID = 0;
bool dirty = false;
PImage() = default;
PImage(int w, int h) {
// Guard against bad dimensions from corrupted files or failed loads
if (w > 0 && h > 0 && w < 16384 && h < 16384) {
width = w; height = h;
pixels.assign((size_t)w * h, 0xFF000000);
}
}
// Pixel read/write (bounds-checked)
unsigned int get(int x, int y) const {
if (x<0||x>=width||y<0||y>=height) return 0;
return pixels[y*width+x];
}
void set(int x, int y, unsigned int c) {
if (x<0||x>=width||y<0||y>=height) return;
pixels[y*width+x] = c;
dirty = true;
}
// These mirror the Processing Java API; dirty flag is used by updatePixels()
void loadPixels() {}
void updatePixels() { dirty = true; }
// Upload CPU pixels to the GPU texture
void uploadTexture(); // defined in Processing.cpp
void resize(int w, int h) { width=w; height=h; pixels.assign(w*h, 0xFF000000); dirty=true; }
// Apply an image filter to all pixels
void filter(int mode) {
for (auto& p : pixels) {
int r=(p>>16)&0xFF, g=(p>>8)&0xFF, b=p&0xFF, a=(p>>24)&0xFF;
if (mode == GRAY) { int gr=(r+g+b)/3; p=(a<<24)|(gr<<16)|(gr<<8)|gr; }
else if (mode == INVERT) { p=(a<<24)|((255-r)<<16)|((255-g)<<8)|(255-b); }
else if (mode == THRESHOLD) { int gr=(r+g+b)/3; int t=gr>127?255:0; p=(a<<24)|(t<<16)|(t<<8)|t; }
}
dirty = true;
}
// Extract a sub-image
PImage get(int x, int y, int w, int h) const {
PImage out(w, h);
for (int iy=0; iy<h; iy++)
for (int ix=0; ix<w; ix++)
out.pixels[iy*w+ix] = get(x+ix, y+iy);
return out;
}
// Copy from another image with scaling
void copy(const PImage& src, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh) {
for (int iy=0; iy<dh; iy++)
for (int ix=0; ix<dw; ix++) {
int srcX = sx+(int)(ix*(float)sw/dw);
int srcY = sy+(int)(iy*(float)sh/dh);
set(dx+ix, dy+iy, src.get(srcX, srcY));
}
dirty = true;
}
// Apply alpha mask from another grayscale image
void mask(const PImage& m) {
for (int i=0; i<width*height && i<(int)m.pixels.size(); i++) {
int a = (m.pixels[i]>>16)&0xFF;
pixels[i] = (pixels[i]&0x00FFFFFF)|(a<<24);
}
dirty = true;
}
void mask(const PImage* m) { if (m) mask(*m); }
// Destructor frees GPU texture
virtual ~PImage() { if (texID) glDeleteTextures(1, &texID); }
// Non-copyable (owns GPU resource -- use PImage* for assignment)
PImage(const PImage&) __attribute__((error(
"E0002: PImage value-style copying is not supported. "
"Declare PImage* instead of PImage. "
"See https://processing-cpp.github.io/error/E0002.html"
)));
PImage& operator=(const PImage&) __attribute__((error(
"E0002: PImage value-style assignment is not supported. "
"Declare PImage* instead of PImage. "
"See https://processing-cpp.github.io/error/E0002.html"
)));
// Movable
PImage(PImage&& o) noexcept
: width(o.width), height(o.height), pixels(std::move(o.pixels)),
texID(o.texID), dirty(o.dirty) { o.texID=0; }
};
PGraphics(int w, int h) : PImage(w, h) {
// Can't reach PApplet::g_papplet here -- PApplet's complete type
// isn't available yet at this point in the header (PGraphics is
// defined before it). Default directly to real Processing's own
// P2D/P3D default (smooth(2)) rather than reaching across that
// forward-reference gap. A sketch wanting a different level for
// its buffers can extend this later if needed.
samples = 0; // TEMPORARY: forced off to test if MSAA itself is the bug
// Resolve target: plain, non-multisampled FBO + texture --
// unchanged from before, just filled via a blit-resolve now.
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
if (texID == 0) glGenTextures(1, &texID);
glBindTexture(GL_TEXTURE_2D, texID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0);
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Multisample render target: only created if antialiasing was
// actually requested. beginDraw() binds THIS one; endDraw() blits
// it down into the resolve target above.
if (samples > 0) {
glGenFramebuffers(1, &msaaFbo);
glBindFramebuffer(GL_FRAMEBUFFER, msaaFbo);
glGenRenderbuffers(1, &msaaColorRbo);
glBindRenderbuffer(GL_RENDERBUFFER, msaaColorRbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorRbo);
glGenRenderbuffers(1, &msaaDepthRbo);
glBindRenderbuffer(GL_RENDERBUFFER, msaaDepthRbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, w, h);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaaDepthRbo);
GLenum msaaStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (msaaStatus != GL_FRAMEBUFFER_COMPLETE) {
glDeleteFramebuffers(1, &msaaFbo); msaaFbo = 0;
glDeleteRenderbuffers(1, &msaaColorRbo); msaaColorRbo = 0;
glDeleteRenderbuffers(1, &msaaDepthRbo); msaaDepthRbo = 0;
samples = 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
Under the Hood
From Processing.cpp:
return new PImage();