public void update(boolean tint, int tintColor) {
      int bufferType = BufferedImage.TYPE_INT_ARGB;
      boolean opaque = (tintColor & 0xFF000000) == 0xFF000000;
      if (source.format == RGB) {
        if (!tint || (tint && opaque)) {
          bufferType = BufferedImage.TYPE_INT_RGB;
        }
      }
      boolean wrongType = (image != null) && (image.getType() != bufferType);
      if ((image == null) || wrongType) {
        image = new BufferedImage(source.width, source.height, bufferType);
      }

      WritableRaster wr = image.getRaster();
      if (tint) {
        if (tintedPixels == null || tintedPixels.length != source.width) {
          tintedPixels = new int[source.width];
        }
        int a2 = (tintColor >> 24) & 0xff;
        int r2 = (tintColor >> 16) & 0xff;
        int g2 = (tintColor >> 8) & 0xff;
        int b2 = (tintColor) & 0xff;

        if (bufferType == BufferedImage.TYPE_INT_RGB) {
          //int alpha = tintColor & 0xFF000000;
          int index = 0;
          for (int y = 0; y < source.height; y++) {
            for (int x = 0; x < source.width; x++) {
              int argb1 = source.pixels[index++];
              int r1 = (argb1 >> 16) & 0xff;
              int g1 = (argb1 >> 8) & 0xff;
              int b1 = (argb1) & 0xff;

              tintedPixels[x] = //0xFF000000 |
                (((r2 * r1) & 0xff00) << 8) |
                ((g2 * g1) & 0xff00) |
                (((b2 * b1) & 0xff00) >> 8);
            }
            wr.setDataElements(0, y, source.width, 1, tintedPixels);
          }
          // could this be any slower?
//          float[] scales = { tintR, tintG, tintB };
//          float[] offsets = new float[3];
//          RescaleOp op = new RescaleOp(scales, offsets, null);
//          op.filter(image, image);

        } else if (bufferType == BufferedImage.TYPE_INT_ARGB) {
          int index = 0;
          for (int y = 0; y < source.height; y++) {
            if (source.format == RGB) {
              int alpha = tintColor & 0xFF000000;
              for (int x = 0; x < source.width; x++) {
                int argb1 = source.pixels[index++];
                int r1 = (argb1 >> 16) & 0xff;
                int g1 = (argb1 >> 8) & 0xff;
                int b1 = (argb1) & 0xff;
                tintedPixels[x] = alpha |
                  (((r2 * r1) & 0xff00) << 8) |
                  ((g2 * g1) & 0xff00) |
                  (((b2 * b1) & 0xff00) >> 8);
              }
            } else if (source.format == ARGB) {
              for (int x = 0; x < source.width; x++) {
                int argb1 = source.pixels[index++];
                int a1 = (argb1 >> 24) & 0xff;
                int r1 = (argb1 >> 16) & 0xff;
                int g1 = (argb1 >> 8) & 0xff;
                int b1 = (argb1) & 0xff;
                tintedPixels[x] =
                  (((a2 * a1) & 0xff00) << 16) |
                  (((r2 * r1) & 0xff00) << 8) |
                  ((g2 * g1) & 0xff00) |
                  (((b2 * b1) & 0xff00) >> 8);
              }
            } else if (source.format == ALPHA) {
              int lower = tintColor & 0xFFFFFF;
              for (int x = 0; x < source.width; x++) {
                int a1 = source.pixels[index++];
                tintedPixels[x] =
                  (((a2 * a1) & 0xff00) << 16) | lower;
              }
            }
            wr.setDataElements(0, y, source.width, 1, tintedPixels);
          }
          // Not sure why ARGB images take the scales in this order...
//          float[] scales = { tintR, tintG, tintB, tintA };
//          float[] offsets = new float[4];
//          RescaleOp op = new RescaleOp(scales, offsets, null);
//          op.filter(image, image);
        }
      } else {
        wr.setDataElements(0, 0, source.width, source.height, source.pixels);
      }
      this.tinted = tint;
      this.tintedColor = tintColor;
    }