/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-14 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
package processing.core;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.metadata.*;
/**
* ( begin auto-generated from PImage.xml )
*
* Datatype for storing images. Processing can display .gif,
* .jpg, .tga, and .png images. Images may be
* displayed in 2D and 3D space. Before an image is used, it must be loaded
* with the loadImage() function. The PImage class contains
* fields for the width and height of the image, as well as
* an array called pixels[] that contains the values for every pixel
* in the image. The methods described below allow easy access to the
* image's pixels and alpha channel and simplify the process of compositing.
*
using the pixels[] array, be sure to use the
* loadPixels() method on the image to make sure that the pixel data
* is properly loaded.
*
create a new image, use the createImage() function. Do not
* use the syntax new PImage().
*
* ( end auto-generated )
*
* @webref image
* @usage Web & Application
* @instanceName pimg any object of type PImage
* @see PApplet#loadImage(String)
* @see PApplet#imageMode(int)
* @see PApplet#createImage(int, int, int)
*/
public class PImage implements PConstants, Cloneable {
/**
* Format for this image, one of RGB, ARGB or ALPHA.
* note that RGB images still require 0xff in the high byte
* because of how they'll be manipulated by other functions
*/
public int format;
/**
* ( begin auto-generated from pixels.xml )
*
* Array containing the values for all the pixels in the display window.
* These values are of the color datatype. This array is the size of the
* display window. For example, if the image is 100x100 pixels, there will
* be 10000 values and if the window is 200x300 pixels, there will be 60000
* values. The index value defines the position of a value within
* the array. For example, the statement color b = pixels[230] will
* set the variable b to be equal to the value at that location in
* the array.
*
* Before accessing this array, the data must loaded with the
* loadPixels() function. After the array data has been modified,
* the updatePixels() function must be run to update the changes.
* Without loadPixels(), running the code may (or will in future
* releases) result in a NullPointerException.
*
* ( end auto-generated )
*
* @webref image:pixels
* @usage web_application
* @brief Array containing the color of every pixel in the image
*/
public int[] pixels;
/** 1 for most images, 2 for hi-dpi/retina */
public int pixelDensity = 1;
/** Actual dimensions of pixels array, taking into account the 2x setting. */
public int pixelWidth;
public int pixelHeight;
/**
* ( begin auto-generated from PImage_width.xml )
*
* The width of the image in units of pixels.
*
* ( end auto-generated )
* @webref pimage:field
* @usage web_application
* @brief Image width
*/
public int width;
/**
* ( begin auto-generated from PImage_height.xml )
*
* The height of the image in units of pixels.
*
* ( end auto-generated )
* @webref pimage:field
* @usage web_application
* @brief Image height
*/
public int height;
/**
* Path to parent object that will be used with save().
* This prevents users from needing savePath() to use PImage.save().
*/
public PApplet parent;
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
/** modified portion of the image */
protected boolean modified;
protected int mx1, my1, mx2, my2;
/** Loaded pixels flag */
public boolean loaded = false;
// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
// private fields
private int fracU, ifU, fracV, ifV, u1, u2, v1, v2, sX, sY, iw, iw1, ih1;
private int ul, ll, ur, lr, cUL, cLL, cUR, cLR;
private int srcXOffset, srcYOffset;
private int r, g, b, a;
private int[] srcBuffer;
// fixed point precision is limited to 15 bits!!
static final int PRECISIONB = 15;
static final int PRECISIONF = 1 = w) {
w = targetWidth;
}
if (h > targetHeight) {
h /= 2;
if (h < targetHeight) {
h = targetHeight;
}
} else if (targetHeight >= h) {
h = targetHeight;
}
if (scratchImage == null || isTranslucent) {
// Use a single scratch buffer for all iterations and then copy
// to the final, correctly-sized image before returning
scratchImage = new BufferedImage(w, h, type);
g2 = scratchImage.createGraphics();
}
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(outgoing, 0, 0, w, h, 0, 0, prevW, prevH, null);
prevW = w;
prevH = h;
outgoing = scratchImage;
} while (w != targetWidth || h != targetHeight);
if (g2 != null) {
g2.dispose();
}
// If we used a scratch buffer that is larger than our target size,
// create an image of the right size and copy the results into it
if (targetWidth != outgoing.getWidth() ||
targetHeight != outgoing.getHeight()) {
scratchImage = new BufferedImage(targetWidth, targetHeight, type);
g2 = scratchImage.createGraphics();
g2.drawImage(outgoing, 0, 0, null);
g2.dispose();
outgoing = scratchImage;
}
return outgoing;
}
//////////////////////////////////////////////////////////////
// MARKING IMAGE AS LOADED / FOR USE IN RENDERERS
public boolean isLoaded() { // ignore
return loaded;
}
public void setLoaded() { // ignore
loaded = true;
}
public void setLoaded(boolean l) { // ignore
loaded = l;
}
//////////////////////////////////////////////////////////////
// GET/SET PIXELS
/**
* ( begin auto-generated from PImage_get.xml )
*
* Reads the color of any pixel or grabs a section of an image. If no
* parameters are specified, the entire image is returned. Use the x
* and y parameters to get the value of one pixel. Get a section of
* the display window by specifying an additional width and
* height parameter. When getting an image, the x and
* y parameters define the coordinates for the upper-left corner of
* the image, regardless of the current imageMode().
*
* If the pixel requested is outside of the image window, black is
* returned. The numbers returned are scaled according to the current color
* ranges, but only RGB values are returned by this function. For example,
* even though you may have drawn a shape with colorMode(HSB), the
* numbers returned will be in RGB format.
*
* Getting the color of a single pixel with get(x, y) is easy, but
* not as fast as grabbing the data directly from pixels[]. The
* equivalent statement to get(x, y) using pixels[] is
* pixels[y*width+x]. See the reference for pixels[] for more information.
*
* ( end auto-generated )
*
* Advanced
* Returns an ARGB "color" type (a packed 32 bit int with the color.
* If the coordinate is outside the image, zero is returned
* (black, but completely transparent).
*
* If the image is in RGB format (i.e. on a PVideo object),
* the value will get its high bits set, just to avoid cases where
* they haven't been set already.
*
* If the image is in ALPHA format, this returns a white with its
* alpha value set.
*
* This function is included primarily for beginners. It is quite
* slow because it has to check to see if the x, y that was provided
* is inside the bounds, and then has to check to see what image
* type it is. If you want things to be more efficient, access the
* pixels[] array directly.
*
* @webref image:pixels
* @brief Reads the color of any pixel or grabs a rectangle of pixels
* @usage web_application
* @param x x-coordinate of the pixel
* @param y y-coordinate of the pixel
* @see PApplet#set(int, int, int)
* @see PApplet#pixels
* @see PApplet#copy(PImage, int, int, int, int, int, int, int, int)
*/
public int get(int x, int y) {
if ((x < 0) || (y < 0) || (x >= pixelWidth) || (y >= pixelHeight)) return 0;
switch (format) {
case RGB:
return pixels[y*pixelWidth + x] | 0xff000000;
case ARGB:
return pixels[y*pixelWidth + x];
case ALPHA:
return (pixels[y*pixelWidth + x] pixelWidth) {
w = pixelWidth - x;
cropped = true;
}
if (y + h > pixelHeight) {
h = pixelHeight - y;
cropped = true;
}
if (w < 0) {
w = 0;
}
if (h < 0) {
h = 0;
}
int targetFormat = format;
if (cropped && format == RGB) {
targetFormat = ARGB;
}
PImage target = new PImage(targetWidth / pixelDensity,
targetHeight / pixelDensity,
targetFormat, pixelDensity);
target.parent = parent; // parent may be null so can't use createImage()
if (w > 0 && h > 0) {
getImpl(x, y, w, h, target, targetX, targetY);
}
return target;
}
/**
* Returns a copy of this PImage. Equivalent to get(0, 0, width, height).
* Deprecated, just use copy() instead.
*/
public PImage get() {
// Formerly this used clone(), which caused memory problems.
// http://code.google.com/p/processing/issues/detail?id=42
return get(0, 0, pixelWidth, pixelHeight);
}
public PImage copy() {
return get(0, 0, pixelWidth, pixelHeight);
}
/**
* Internal function to actually handle getting a block of pixels that
* has already been properly cropped to a valid region. That is, x/y/w/h
* are guaranteed to be inside the image space, so the implementation can
* use the fastest possible pixel copying method.
*/
protected void getImpl(int sourceX, int sourceY,
int sourceWidth, int sourceHeight,
PImage target, int targetX, int targetY) {
int sourceIndex = sourceY*pixelWidth + sourceX;
int targetIndex = targetY*target.pixelWidth + targetX;
for (int row = 0; row < sourceHeight; row++) {
System.arraycopy(pixels, sourceIndex, target.pixels, targetIndex, sourceWidth);
sourceIndex += pixelWidth;
targetIndex += target.pixelWidth;
}
}
/**
* ( begin auto-generated from PImage_set.xml )
*
* Changes the color of any pixel or writes an image directly into the
* display window.
*
* The x and y parameters specify the pixel to change and the
* color parameter specifies the color value. The color parameter is
* affected by the current color mode (the default is RGB values from 0 to
* 255). When setting an image, the x and y parameters define
* the coordinates for the upper-left corner of the image, regardless of
* the current imageMode().
*
* Setting the color of a single pixel with set(x, y) is easy, but
* not as fast as putting the data directly into pixels[]. The
* equivalent statement to set(x, y, #000000) using pixels[]
* is pixels[y*width+x] = #000000. See the reference for
* pixels[] for more information.
*
* ( end auto-generated )
*
* @webref image:pixels
* @brief writes a color to any pixel or writes an image into another
* @usage web_application
* @param x x-coordinate of the pixel
* @param y y-coordinate of the pixel
* @param c any value of the color datatype
* @see PImage#get(int, int, int, int)
* @see PImage#pixels
* @see PImage#copy(PImage, int, int, int, int, int, int, int, int)
*/
public void set(int x, int y, int c) {
if ((x < 0) || (y < 0) || (x >= pixelWidth) || (y >= pixelHeight)) return;
pixels[y*pixelWidth + x] = c;
updatePixels(x, y, 1, 1); // slow...
}
/**
*
Advanced
* Efficient method of drawing an image's pixels directly to this surface.
* No variations are employed, meaning that any scale, tint, or imageMode
* settings will be ignored.
*
* @param img image to copy into the original image
*/
public void set(int x, int y, PImage img) {
int sx = 0;
int sy = 0;
int sw = img.pixelWidth;
int sh = img.pixelHeight;
if (x < 0) { // off left edge
sx -= x;
sw += x;
x = 0;
}
if (y < 0) { // off top edge
sy -= y;
sh += y;
y = 0;
}
if (x + sw > pixelWidth) { // off right edge
sw = pixelWidth - x;
}
if (y + sh > pixelHeight) { // off bottom edge
sh = pixelHeight - y;
}
// this could be nonexistent
if ((sw