/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.ide; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.io.*; import javax.imageio.*; import javax.swing.*; import javax.swing.event.*; import org.sikuli.basics.Debug; import org.sikuli.script.Finder; import org.sikuli.script.Location; import org.sikuli.script.Match; import org.sikuli.script.Region; import org.sikuli.script.Screen; import org.sikuli.script.ScreenImage; class PatternPaneTargetOffset extends JPanel implements MouseListener, MouseWheelListener, ChangeListener { final static String me = "PatternPaneTargetOffset: "; static int DEFAULT_H = 120; final static float DEFAULT_PATTERN_RATIO = 0.1f; private static final Color COLOR_BG_LINE = new Color(210, 210, 210, 130); ScreenImage _simg; BufferedImage _img; Match _match = null; int _viewX, _viewY, _viewW, _viewH; float _zoomRatio, _ratio; Location _tar = new Location(0, 0); Location _offset = new Location(0, 0); JSpinner txtX, txtY; private LoadingSpinner _loading; private boolean _finding = true; private JLabel _msgApplied; public PatternPaneTargetOffset( ScreenImage simg, String patFilename, Location initOffset, Dimension pDim, JLabel msgApplied) { _msgApplied = msgApplied; _simg = simg; _ratio = DEFAULT_PATTERN_RATIO; setPreferredSize(new Dimension(pDim.width, pDim.height - DEFAULT_H)); addMouseListener(this); addMouseWheelListener(this); _loading = new LoadingSpinner(); findTarget(patFilename, initOffset); } void findTarget(final String patFilename, final Location initOffset) { Thread thread = new Thread(new Runnable() { @Override public void run() { Region screenUnion = Region.create(0, 0, 1, 1); Finder f = new Finder(_simg, screenUnion); try { f.find(patFilename); if (f.hasNext()) { //TODO rewrite completely for ScreenUnion Screen s = (Screen) screenUnion.getScreen(); s.setAsScreenUnion(); _match = f.next(); s.setAsScreen(); if (initOffset != null) { setTarget(initOffset.x, initOffset.y); } else { setTarget(0, 0); } } _img = ImageIO.read(new File(patFilename)); } catch (IOException e) { Debug.error(me + "Can't load " + patFilename); } synchronized (PatternPaneTargetOffset.this) { _finding = false; } repaint(); } }); thread.start(); } static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } public void setTarget(int dx, int dy) { Debug.log(4, me + "new target: " + dx + "," + dy); if (_match != null) { Location center = _match.getCenter(); _tar.x = center.x + dx; _tar.y = center.y + dy; } else { _tar.x = dx; _tar.y = dy; } _offset = new Location(dx, dy); if (txtX != null) { txtX.setValue(new Integer(dx)); txtY.setValue(new Integer(dy)); } repaint(); } @Override public void mousePressed(MouseEvent me) { Location tar = convertViewToScreen(me.getPoint()); Debug.log(4, "click: " + me.getPoint() + " -> " + tar.toStringShort()); if (_match != null) { Location center = _match.getCenter(); setTarget(tar.x - center.x, tar.y - center.y); } else { setTarget(tar.x, tar.y); } } @Override public void mouseWheelMoved(MouseWheelEvent e) { int rot = e.getWheelRotation(); int patW = (int) (getWidth() * _ratio); // float zoomRatio = patW / (float) _img.getWidth(); int patH = (int) (_img.getHeight() * _zoomRatio); if (rot < 0) { if (patW < 2 * getWidth() && patH < 2 * getHeight()) { _ratio *= 1.1; } } else { if (patW > 20 && patH > 20) { _ratio *= 0.9; } } repaint(); } @Override public void mouseClicked(MouseEvent me) { } @Override public void mouseReleased(MouseEvent me) { } @Override public void mouseEntered(MouseEvent me) { } @Override public void mouseExited(MouseEvent me) { } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (getWidth() > 0 && getHeight() > 0) { if (_match != null) { zoomToMatch(); paintSubScreen(g2d); } else { paintPatternOnly(g2d); } paintMatch(g2d); // paintRulers(g2d); paintTarget(g2d); synchronized (this) { if (_finding) { paintLoading(g2d); } } } } private void zoomToMatch() { _viewW = (int) (_match.w / _ratio); _zoomRatio = getWidth() / (float) _viewW; _viewH = (int) (getHeight() / _zoomRatio); _viewX = _match.x + _match.w / 2 - _viewW / 2; _viewY = _match.y + _match.h / 2 - _viewH / 2; } void paintSubScreen(Graphics g2d) { if (_viewX < 0 || _viewY < 0) { paintBackground(g2d); } int subX = _viewX < 0 ? 0 : _viewX; int subY = _viewY < 0 ? 0 : _viewY; int subW = _viewW - (subX - _viewX); int subH = _viewH - (subY - _viewY); BufferedImage img = _simg.getImage(); if (subX + subW >= img.getWidth()) { subW = img.getWidth() - subX; } if (subY + subH >= img.getHeight()) { subH = img.getHeight() - subY; } BufferedImage clip = img.getSubimage(subX, subY, subW, subH); int destX = (int) ((subX - _viewX) * _zoomRatio), destY = (int) ((subY - _viewY) * _zoomRatio); int destW = (int) (subW * _zoomRatio), destH = (int) (subH * _zoomRatio); g2d.drawImage(clip, destX, destY, destW, destH, null); } void paintMatch(Graphics2D g2d) { int w = (int) (getWidth() * _ratio); int h = (int) ((float) w / _img.getWidth() * _img.getHeight()); int x = getWidth() / 2 - w / 2; int y = getHeight() / 2 - h / 2; Color c = PatternSimilaritySlider.getScoreColor((_match == null ? 1.0 : _match.getScore())); g2d.setColor(c); // g2d.fillRect(x, y, w, h); Stroke savedStroke = g2d.getStroke(); g2d.setStroke(new BasicStroke(5)); g2d.drawRect(x, y, w - 1, h - 1); g2d.setStroke(savedStroke); } void paintBackground(Graphics g2d) { g2d.setColor(Color.WHITE); g2d.fillRect(0, 0, getWidth(), getHeight()); } void paintPatternOnly(Graphics g2d) { int patW = (int) (getWidth() * _ratio); _zoomRatio = patW / (float) _img.getWidth(); int patH = (int) (_img.getHeight() * _zoomRatio); int patX = getWidth() / 2 - patW / 2, patY = getHeight() / 2 - patH / 2; paintBackground(g2d); g2d.drawImage(_img, patX, patY, patW, patH, null); } void paintLoading(Graphics2D g2d) { int w = getWidth(), h = getHeight(); g2d.setColor(new Color(0, 0, 0, 200)); g2d.fillRect(0, 0, w, h); BufferedImage spinner = _loading.getFrame(); g2d.drawImage(spinner, null, w / 2 - spinner.getWidth() / 2, h / 2 - spinner.getHeight() / 2); repaint(); } void paintTarget(Graphics2D g2d) { final int CROSS_LEN = 20 / 2; Point l = convertScreenToView(_tar); g2d.setColor(Color.BLACK); g2d.drawLine(l.x - CROSS_LEN, l.y + 1, l.x + CROSS_LEN, l.y + 1); g2d.drawLine(l.x + 1, l.y - CROSS_LEN, l.x + 1, l.y + CROSS_LEN); g2d.setColor(Color.WHITE); g2d.drawLine(l.x - CROSS_LEN, l.y, l.x + CROSS_LEN, l.y); g2d.drawLine(l.x, l.y - CROSS_LEN, l.x, l.y + CROSS_LEN); } void paintRulers(Graphics g2d) { int step = (int) (10 * _zoomRatio); if (step < 2) { step = 2; } int h = getHeight(), w = getWidth(); if (h % 2 == 1) { h--; } if (w % 2 == 1) { w--; } g2d.setColor(COLOR_BG_LINE); for (int x = w / 2; x >= 0; x -= step) { g2d.drawLine(x, 0, x, h); g2d.drawLine(w - x, 0, w - x, h); } for (int y = h / 2; y >= 0; y -= step) { g2d.drawLine(0, y, w, y); g2d.drawLine(0, h - y, w, h - y); } } Location convertViewToScreen(Point p) { Location ret = new Location(0, 0); if (_match != null) { ret.x = (int) (p.x / _zoomRatio + _viewX); ret.y = (int) (p.y / _zoomRatio + _viewY); } else { ret.x = (int) ((p.x - getWidth() / 2) / _zoomRatio); ret.y = (int) ((p.y - getHeight() / 2) / _zoomRatio); } return ret; } Point convertScreenToView(Location loc) { Point ret = new Point(); if (_match != null) { ret.x = (int) ((loc.x - _viewX) * _zoomRatio); ret.y = (int) ((loc.y - _viewY) * _zoomRatio); } else { ret.x = (int) (getWidth() / 2 + loc.x * _zoomRatio); ret.y = (int) (getHeight() / 2 + loc.y * _zoomRatio); } return ret; } public JComponent createControls() { JPanel pane = new JPanel(new GridBagLayout()); JLabel lblX = new JLabel(_I("lblTargetOffsetX")); JLabel lblY = new JLabel(_I("lblTargetOffsetY")); int x = _offset != null ? _offset.x : 0; int y = _offset != null ? _offset.y : 0; txtX = new JSpinner(new SpinnerNumberModel(x, -999, 999, 1)); txtY = new JSpinner(new SpinnerNumberModel(y, -999, 999, 1)); txtX.addChangeListener(this); txtY.addChangeListener(this); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.gridy = 0; pane.add(lblX, c); pane.add(txtX, c); pane.add(lblY, c); pane.add(txtY, c); pane.add(_msgApplied, c); return pane; } @Override public void stateChanged(javax.swing.event.ChangeEvent e) { int x = (Integer) txtX.getValue(); int y = (Integer) txtY.getValue(); setTarget(x, y); } public Location getTargetOffset() { return new Location(_offset); } }