/**
  * Overrides Step draw method.
  *
  * @param panel the drawing panel requesting the drawing
  * @param _g the graphics context on which to draw
  */
 public void draw(DrawingPanel panel, Graphics _g) {
   if (track.trackerPanel == panel) {
     AutoTracker autoTracker = track.trackerPanel.getAutoTracker();
     if (autoTracker.isInteracting(track)) return;
   }
   if (panel instanceof TrackerPanel) {
     TrackerPanel trackerPanel = (TrackerPanel) panel;
     super.draw(trackerPanel, _g);
     Graphics2D g = (Graphics2D) _g;
     if (isLabelVisible()) {
       TextLayout layout = textLayouts.get(trackerPanel);
       if (layout == null) return;
       Point p = getLayoutPosition(trackerPanel);
       Paint gpaint = g.getPaint();
       Font gfont = g.getFont();
       g.setPaint(footprint.getColor());
       g.setFont(textLayoutFont);
       layout.draw(g, p.x, p.y);
       g.setPaint(gpaint);
       g.setFont(gfont);
     }
   }
 }
 /**
  * Rebuilds the template from an input image. The input image dimensions must match the original.
  * The input and original are overlaid onto the existing template, if any. Pixels that fall
  * outside the mask are ignored in the final template.
  *
  * @param image the input image
  * @param alphaInput the opacity with which the input image is overlaid
  * @param alphaOriginal the opacity with which the original image is overlaid
  */
 public void rebuildTemplate(BufferedImage image, int alphaInput, int alphaOriginal) {
   int w = image.getWidth();
   int h = image.getHeight();
   // return if image dimensions do not match original image
   if (original.getWidth() != w || original.getHeight() != h) return;
   // return if both alphas are zero
   if (alphaInput == 0 && alphaOriginal == 0) return;
   // draw image onto argb input
   BufferedImage input = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
   input.createGraphics().drawImage(image, 0, 0, null);
   // create working image if needed
   if (working == null) {
     working = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
   }
   // reset template dimensions and create new template if needed
   if (template == null || w != wTemplate || h != hTemplate) {
     wTemplate = w;
     hTemplate = h;
     int len = w * h;
     template = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
     pixels = new int[len];
     templateR = new int[len];
     templateG = new int[len];
     templateB = new int[len];
     isPixelTransparent = new boolean[len];
   }
   // set alpha of input and draw onto working
   Graphics2D gWorking = working.createGraphics();
   alphaInput = Math.max(0, Math.min(255, alphaInput));
   if (alphaInput > 0) { // overlay only if not transparent
     gWorking.setComposite(getComposite(alphaInput));
     input.getRaster().getDataElements(0, 0, w, h, pixels);
     gWorking.drawImage(input, 0, 0, null);
   }
   // set alpha of original and draw onto working
   alphaOriginal = Math.max(0, Math.min(255, alphaOriginal));
   if (alphaOriginal > 0) { // overlay only if not transparent
     gWorking.setComposite(getComposite(alphaOriginal));
     original.getRaster().getDataElements(0, 0, w, h, pixels);
     gWorking.drawImage(original, 0, 0, null);
   }
   // read pixels from working raster
   working.getRaster().getDataElements(0, 0, wTemplate, hTemplate, pixels);
   if (mask != null) {
     // set pixels outside mask to transparent
     for (int i = 0; i < pixels.length; i++) {
       boolean inside = true;
       // pixel is inside only if all corners are inside
       int x = i % wTemplate, y = i / wTemplate;
       for (int j = 0; j < 2; j++) {
         for (int k = 0; k < 2; k++) {
           p.setLocation(x + j, y + k);
           inside = inside && mask.contains(p);
         }
       }
       if (!inside) pixels[i] = pixels[i] & (0 << 24); // set alpha to zero (transparent)
     }
   }
   // write pixels to template raster
   template.getRaster().setDataElements(0, 0, wTemplate, hTemplate, pixels);
   // trim transparent edges from template
   int trimRight = 0, trimBottom = 0;
   trimLeft = trimTop = 0;
   // left edge
   boolean transparentEdge = true;
   while (transparentEdge && trimLeft < wTemplate) {
     for (int line = 0; line < hTemplate; line++) {
       int i = line * wTemplate + trimLeft;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimLeft++;
   }
   // right edge
   transparentEdge = true;
   while (transparentEdge && (trimLeft + trimRight) < wTemplate) {
     for (int line = 0; line < hTemplate; line++) {
       int i = (line + 1) * wTemplate - 1 - trimRight;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimRight++;
   }
   // top edge
   transparentEdge = true;
   while (transparentEdge && trimTop < hTemplate) {
     for (int col = 0; col < wTemplate; col++) {
       int i = trimTop * wTemplate + col;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimTop++;
   }
   // bottom edge
   transparentEdge = true;
   while (transparentEdge && (trimTop + trimBottom) < hTemplate) {
     for (int col = 0; col < wTemplate; col++) {
       int i = (hTemplate - 1 - trimBottom) * wTemplate + col;
       transparentEdge = transparentEdge && getAlpha(pixels[i]) == 0;
     }
     if (transparentEdge) trimBottom++;
   }
   // reduce size of template if needed
   if (trimLeft + trimRight + trimTop + trimBottom > 0) {
     wTemplate -= (trimLeft + trimRight);
     hTemplate -= (trimTop + trimBottom);
     pixels = new int[wTemplate * hTemplate];
     templateR = new int[wTemplate * hTemplate];
     templateG = new int[wTemplate * hTemplate];
     templateB = new int[wTemplate * hTemplate];
     isPixelTransparent = new boolean[wTemplate * hTemplate];
     BufferedImage bi = new BufferedImage(wTemplate, hTemplate, BufferedImage.TYPE_INT_ARGB);
     bi.createGraphics().drawImage(template, -trimLeft, -trimTop, null);
     template = bi;
     template.getRaster().getDataElements(0, 0, wTemplate, hTemplate, pixels);
   }
   // set up rgb and transparency arrays for faster matching
   for (int i = 0; i < pixels.length; i++) {
     int val = pixels[i];
     templateR[i] = getRed(val); // red
     templateG[i] = getGreen(val); // green
     templateB[i] = getBlue(val); // blue
     isPixelTransparent[i] = getAlpha(val) == 0; // alpha
   }
 }