public PImage getImpl(int x, int y, int w, int h) {
    PImage output = new PImage(w, h);
    output.parent = parent;

    // oops, the last parameter is the scan size of the *target* buffer
    // ((BufferedImage) image).getRGB(x, y, w, h, output.pixels, 0, w);
    WritableRaster raster = ((BufferedImage) (primarySurface ? offscreen : image)).getRaster();
    raster.getDataElements(x, y, w, h, output.pixels);

    return output;
  }
 protected void setImpl(int dx, int dy, int sx, int sy, int sw, int sh, PImage src) {
   WritableRaster raster = ((BufferedImage) (primarySurface ? offscreen : image)).getRaster();
   if ((sx == 0) && (sy == 0) && (sw == src.width) && (sh == src.height)) {
     raster.setDataElements(dx, dy, src.width, src.height, src.pixels);
   } else {
     // TODO Optimize, incredibly inefficient to reallocate this much memory
     PImage temp = src.get(sx, sy, sw, sh);
     raster.setDataElements(dx, dy, temp.width, temp.height, temp.pixels);
   }
 }
  /** Handle renderer-specific image drawing. */
  protected void imageImpl(
      PImage who, float x1, float y1, float x2, float y2, int u1, int v1, int u2, int v2) {
    // Image not ready yet, or an error
    if (who.width <= 0 || who.height <= 0) return;

    if (who.getCache(this) == null) {
      // System.out.println("making new image cache");
      who.setCache(this, new ImageCache(who));
      who.updatePixels(); // mark the whole thing for update
      who.modified = true;
    }

    ImageCache cash = (ImageCache) who.getCache(this);
    // if image previously was tinted, or the color changed
    // or the image was tinted, and tint is now disabled
    if ((tint && !cash.tinted)
        || (tint && (cash.tintedColor != tintColor))
        || (!tint && cash.tinted)) {
      // for tint change, mark all pixels as needing update
      who.updatePixels();
    }

    if (who.modified) {
      cash.update(tint, tintColor);
      who.modified = false;
    }

    g2.drawImage(
        ((ImageCache) who.getCache(this)).image,
        (int) x1,
        (int) y1,
        (int) x2,
        (int) y2,
        u1,
        v1,
        u2,
        v2,
        null);
  }
  // Takes a PImage and compresses it into a JPEG byte stream
  // Adapted from Dan Shiffman's UDP Sender code
  public byte[] compressImage(PImage img) {
    // We need a buffered image to do the JPG encoding
    BufferedImage bimg = new BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_RGB);

    img.loadPixels();
    bimg.setRGB(0, 0, img.width, img.height, img.pixels, 0, img.width);

    // Need these output streams to get image as bytes for UDP communication
    ByteArrayOutputStream baStream = new ByteArrayOutputStream();
    BufferedOutputStream bos = new BufferedOutputStream(baStream);

    // Turn the BufferedImage into a JPG and put it in the BufferedOutputStream
    // Requires try/catch
    try {
      ImageIO.write(bimg, "jpg", bos);
    } catch (IOException e) {
      e.printStackTrace();
    }

    // Get the byte array, which we will send out via UDP!
    return baStream.toByteArray();
  }