/* * 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.util.*; import javax.swing.*; import javax.swing.event.*; import org.sikuli.script.Finder; import org.sikuli.script.Match; import org.sikuli.script.Pattern; import org.sikuli.script.Region; import org.sikuli.script.ScreenImage; import org.sikuli.script.ScreenUnion; import org.sikuli.basics.Debug; class PatternPaneScreenshot extends JPanel implements ChangeListener, ComponentListener { private static final String me = "PatternPaneScreenshot: "; static int DEFAULT_H; static int MAX_NUM_MATCHING = EditorPatternButton.DEFAULT_NUM_MATCHES; Region _match_region; int _width, _height; double _scale, _ratio; boolean _runFind = false; float _similarity; int _numMatches; Set<Match> _fullMatches = null; final Boolean _fullMatchesSynch = true; ArrayList<Match> _showMatches = null; final Boolean _showMatchesSynch = true; protected ScreenImage _simg; protected BufferedImage _screen = null; protected Rectangle _uBound; private JLabel btnSimilar, _lblMatchCount; private JSlider sldSimilar; private JSpinner txtNumMatches; private LoadingSpinner _loading; static String _I(String key, Object... args) { return SikuliIDEI18N._I(key, args); } private JLabel _msgApplied; public PatternPaneScreenshot(ScreenImage simg, Dimension pDim, JLabel msgApplied) { init(simg, pDim, msgApplied); } private void init(ScreenImage simg, Dimension pDim, JLabel msgApplied) { _msgApplied = msgApplied; _match_region = new ScreenUnion(); _ratio = (double) _match_region.w / _match_region.h; _height = pDim.height - 200; _scale = (double) _height / _match_region.h; _width = (int) (_match_region.w * _scale); setPreferredSize(new Dimension(_width, _height)); addComponentListener(this); _simg = simg; _screen = simg.getImage(); //TODO Necessary? MAX_NUM_MATCHING = (int) Vision.getParameter("FindAllMaxReturn"); autoResize(); _loading = new LoadingSpinner(); } public JComponent createControls() { JPanel pane = new JPanel(new GridBagLayout()); btnSimilar = new JLabel(_I("lblSimilarity")); sldSimilar = createSlider(); sldSimilar.setPreferredSize(new Dimension(250, 35)); /* JLabel lblPreNumMatches = new JLabel(_I("lblNumberOfMatches")); _lblMatchCount = new JLabel("0"); Dimension size = _lblMatchCount.getPreferredSize(); size.width *= 2; _lblMatchCount.setPreferredSize(size); SpinnerNumberModel model = new SpinnerNumberModel(10, 0, PatternPaneScreenshot.MAX_NUM_MATCHING, 1); txtNumMatches = new JSpinner(model); lblPreNumMatches.setLabelFor(txtNumMatches); */ GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.gridy = 0; pane.add(sldSimilar, c); pane.add(btnSimilar, c); pane.add(_msgApplied, c); //TODO num Matches needed? /* c.fill = 0; c.gridy = 1; c.gridwidth = 1; pane.add(lblPreNumMatches, c); c.insets = new Insets(0, 10, 0, 0); pane.add(_lblMatchCount, c); c.insets = new Insets(0, 0, 0, 0); pane.add(new JLabel("/"), c); c.insets = new Insets(0, 0, 0, 100); pane.add(txtNumMatches, c); txtNumMatches.addChangeListener(this); */ return pane; } private JSlider createSlider() { sldSimilar = new PatternSimilaritySlider(0, 100, 70, btnSimilar); sldSimilar.setMajorTickSpacing(10); sldSimilar.setPaintTicks(true); Hashtable<Integer, JLabel> labelTable = new Hashtable<Integer, JLabel>(); labelTable.put(new Integer(0), new JLabel("00")); labelTable.put(new Integer(50), new JLabel("50")); labelTable.put(new Integer(100), new JLabel("99")); sldSimilar.setLabelTable(labelTable); sldSimilar.setPaintLabels(true); sldSimilar.addChangeListener(this); return sldSimilar; } public void setParameters(final String patFilename, final boolean exact, final float similarity, final int numMatches) { if (!_runFind) { _runFind = true; Thread thread = new Thread(new Runnable() { @Override public void run() { try { Finder f = new Finder(_simg, _match_region); f.findAll(new Pattern(patFilename).similar(0.00001f)); _fullMatches = new TreeSet<Match>(new Comparator() { @Override public int compare(Object o1, Object o2) { return -1 * ((Comparable) o1).compareTo(o2); } @Override public boolean equals(Object o) { return false; } @Override public int hashCode() { int hash = 3; return hash; } }); int count = 0; while (f.hasNext()) { if (++count > MAX_NUM_MATCHING) { break; } Match m = f.next(); Debug.log(4, me + "f.next(%d): " + m.toString(), count); synchronized (_fullMatchesSynch) { _fullMatches.add(m); } setParameters(exact, similarity, numMatches); } } catch (Exception e) { Debug.error(me + "Problems searching image in ScreenUnion\n%s", e.getMessage()); } } }); thread.start(); } else { setParameters(exact, similarity, numMatches); } } public void setParameters(boolean exact, float similarity, int numMatches) { if (numMatches > MAX_NUM_MATCHING) { numMatches = MAX_NUM_MATCHING; } if (!exact) { _similarity = similarity; } else { _similarity = 0.99f; } _numMatches = numMatches; filterMatches(_similarity, _numMatches); sldSimilar.setValue((int) (similarity * 100)); repaint(); } @Override public void componentHidden(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { } @Override public void componentShown(ComponentEvent e) { } @Override public void componentResized(ComponentEvent e) { autoResize(); } private void autoResize() { _width = getWidth(); if (_width == 0) { _width = (int) getPreferredSize().getWidth(); } _height = (int) ((double) _width / _ratio); _scale = (double) _height / _match_region.h; setPreferredSize(new Dimension(_width, _height)); repaint(); } public boolean isExact() { return _similarity >= 0.99f; } public float getSimilarity() { return _similarity; } public int getNumMatches() { return _numMatches; } public void setSimilarity(float similarity) { _similarity = similarity > 0.99f ? 0.99f : similarity; filterMatches(_similarity, _numMatches); repaint(); } public void setNumMatches(int numMatches) { _numMatches = numMatches; filterMatches(_similarity, _numMatches); repaint(); } void filterMatches(float similarity, int numMatches) { int count = 0; if (_fullMatches != null && numMatches >= 0) { if (_showMatches == null) { _showMatches = new ArrayList<Match>(); } synchronized (_showMatchesSynch) { _showMatches.clear(); if (numMatches == 0) { return; } synchronized (_fullMatchesSynch) { for (Match m : _fullMatches) { if (m.getScore() >= similarity) { _showMatches.add(m); if (++count >= numMatches) { break; } } } } } // _lblMatchCount.setText(Integer.toString(count)); Debug.log(4, "filterMatches(%.2f,%d): %d", similarity, numMatches, count); } } @Override public void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; if (_screen != null) { g2d.drawImage(_screen, 0, 0, _width, _height, null); if (_showMatches != null) { paintMatches(g2d); } else { paintOverlay(g2d); } } } void paintOverlay(Graphics2D g2d) { g2d.setColor(new Color(0, 0, 0, 150)); g2d.fillRect(0, 0, _width, _height); BufferedImage spinner = _loading.getFrame(); g2d.drawImage(spinner, null, _width / 2 - spinner.getWidth() / 2, _height / 2 - spinner.getHeight() / 2); repaint(); } void paintMatches(Graphics2D g2d) { synchronized (_showMatchesSynch) { for (Match m : _showMatches) { int x = (int) ((m.x - _match_region.x) * _scale); int y = (int) ((m.y - _match_region.y) * _scale); int w = (int) (m.w * _scale); int h = (int) (m.h * _scale); Color c = PatternSimilaritySlider.getScoreColor(m.getScore()); g2d.setColor(c); g2d.fillRect(x, y, w, h); g2d.drawRect(x, y, w - 1, h - 1); } } } @Override public void stateChanged(javax.swing.event.ChangeEvent e) { Object src = e.getSource(); if (src instanceof JSlider) { JSlider source = (JSlider) e.getSource(); int val = (int) source.getValue(); setSimilarity((float) val / 100); } else if (src instanceof JSpinner) { JSpinner source = (JSpinner) e.getSource(); int val = (Integer) source.getValue(); setNumMatches(val); } } }