/* * Copyright (c) 2010-2016, Sikuli.org, sikulix.com * Released under the MIT License. * */ package org.sikuli.util; import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; import java.awt.image.RasterFormatException; import java.awt.image.RescaleOp; import javax.swing.JFrame; import org.sikuli.basics.Debug; import org.sikuli.script.IScreen; import org.sikuli.script.Location; import org.sikuli.script.Screen; import org.sikuli.script.ScreenImage; /** * INTERNAL USE implements the screen overlay used with the capture feature */ public class OverlayCapturePrompt extends JFrame implements EventSubject { final static float MIN_DARKER_FACTOR = 0.6f; final static long MSG_DISPLAY_TIME = 2000; final static long WIN_FADE_IN_TIME = 200; static final Font fontMsg = new Font("Arial", Font.PLAIN, 60); static final Color selFrameColor = new Color(1.0f, 1.0f, 1.0f, 1.0f); static final Color selCrossColor = new Color(1.0f, 0.0f, 0.0f, 0.6f); static final Color screenFrameColor = new Color(1.0f, 0.0f, 0.0f, 0.6f); private Rectangle screenFrame = null; static final BasicStroke strokeScreenFrame = new BasicStroke(5); static final BasicStroke _StrokeCross = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1, new float[]{2f}, 0); static final BasicStroke bs = new BasicStroke(1); private EventObserver captureObserver = null; private IScreen scrOCP; private BufferedImage scr_img = null; private BufferedImage scr_img_darker = null; private BufferedImage bi = null; private float darker_factor; private Rectangle rectSelection; private int srcScreenId = -1; private Location srcScreenLocation = null; private Location destScreenLocation = null; private int srcx, srcy, destx, desty; private boolean canceled = false; private String promptMsg = ""; private boolean dragging = false; private boolean hasFinished = false; private boolean hasStarted = false; private boolean mouseMoves = false; private int scr_img_type = BufferedImage.TYPE_INT_RGB; private double scr_img_scale = 1; private Rectangle scr_img_rect = null; private ScreenImage scr_img_original = null; private boolean isLocalScreen = true; // private JPanel _panel = null; // private Graphics2D _currG2D = null; public OverlayCapturePrompt(IScreen scr) { // super(); Debug.log(3, "TRACE: OverlayCapturePrompt: init: S(%d)", scr.getID()); scrOCP = scr; canceled = false; setUndecorated(true); setAlwaysOnTop(true); setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); rectSelection = new Rectangle(); if (scr.isOtherScreen()) { isLocalScreen = false; } // _panel = new javax.swing.JPanel() { // @Override // protected void paintComponent(Graphics g) { // if (g instanceof Graphics2D) { // Graphics2D g2d = (Graphics2D) g; // _currG2D = g2d; // } else { // super.paintComponent(g); // } // } // }; // _panel.setLayout(null); // add(_panel); addMouseListener(new MouseAdapter() { @Override public void mousePressed(java.awt.event.MouseEvent e) { if (scr_img == null) { return; } if (e.getButton() != java.awt.event.MouseEvent.BUTTON1) { return; } hasStarted = true; destx = srcx = e.getX(); desty = srcy = e.getY(); if (isLocalScreen) { srcScreenId = scrOCP.getIdFromPoint(srcx, srcy); srcScreenLocation = new Location(srcx + scrOCP.getX(), srcy + scrOCP.getY()); Debug.log(3, "CapturePrompt: started at (%d,%d) as %s on %d", srcx, srcy, srcScreenLocation.toStringShort(), srcScreenId); } promptMsg = null; repaint(); } @Override public void mouseReleased(java.awt.event.MouseEvent e) { if (scr_img == null) { return; } if (e.getButton() != java.awt.event.MouseEvent.BUTTON1) { canceled = true; Debug.log(3, "CapturePrompt: aborted: not using left mouse button"); } else { if (isLocalScreen) { destScreenLocation = new Location(destx + scrOCP.getX(), desty + scrOCP.getY()); Debug.log(3, "CapturePrompt: finished at (%d,%d) as %s on %d", destx, desty, destScreenLocation.toStringShort(), srcScreenId); } } hasFinished = true; setVisible(false); notifyObserver(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(java.awt.event.MouseEvent e) { if (promptMsg == null) { return; } if (!mouseMoves) { mouseMoves = true; return; } promptMsg = null; repaint(); } @Override public void mouseDragged(java.awt.event.MouseEvent e) { if (!hasStarted || scr_img == null) { return; } if (!dragging) { if (promptMsg != null) { Screen.closePrompt((Screen) scrOCP); } dragging = true; } destx = e.getX(); desty = e.getY(); repaint(); } }); addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { hasFinished = canceled = true; Debug.log(3, "CapturePrompt: aborted using key ESC"); setVisible(false); notifyObserver(); } } }); } public int getScrID() { return srcScreenId; } public void close() { Debug.log(4, "CapturePrompt.close: S(%d) freeing resources", scrOCP.getID()); setVisible(false); dispose(); scr_img = null; scr_img_darker = null; bi = null; } public void prompt(String msg, int delayMS) { try { Thread.sleep(delayMS); } catch (InterruptedException ie) { } prompt(msg); } public void prompt(int delayMS) { prompt(null, delayMS); } public void prompt() { prompt(null); } public void prompt(String msg) { scr_img_original = scrOCP.capture(); scr_img = scr_img_original.getImage(); scr_img_darker = scr_img; scr_img_type = scr_img.getType(); scr_img_rect = new Rectangle(scrOCP.getBounds()); promptMsg = msg; if (isLocalScreen) { darker_factor = 0.6f; RescaleOp op = new RescaleOp(darker_factor, 0, null); scr_img_darker = op.filter(scr_img, null); } else { promptMsg = null; if (scr_img_rect.height > Screen.getPrimaryScreen().getBounds().getHeight()) { scr_img_scale = Screen.getPrimaryScreen().getBounds().getHeight() / scr_img_rect.height; } if (scr_img_rect.width > Screen.getPrimaryScreen().getBounds().getWidth()) { scr_img_scale = Math.min(Screen.getPrimaryScreen().getBounds().getWidth() / scr_img_rect.width, scr_img_scale); } if (1 != scr_img_scale) { scr_img_rect.width = (int) (scr_img_rect.width * scr_img_scale); scr_img_rect.height = (int) (scr_img_rect.height * scr_img_scale); Image tmp = scr_img.getScaledInstance(scr_img_rect.width, scr_img_rect.height, Image.SCALE_SMOOTH); scr_img = new BufferedImage(scr_img_rect.width, scr_img_rect.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = scr_img.createGraphics(); g2d.drawImage(tmp, 0, 0, null); g2d.dispose(); scr_img_darker = scr_img; } } this.setBounds(scr_img_rect); this.setVisible(true); } public boolean isComplete() { return hasFinished; } @Override public void addObserver(EventObserver obs) { Debug.log(3, "TRACE: OverlayCapturePrompt: addObserver: %s", obs != null); captureObserver = obs; } @Override public void notifyObserver() { Debug.log(3, "TRACE: OverlayCapturePrompt: notifyObserver: %s", captureObserver != null); if (null != captureObserver) { captureObserver.update(this); } } public ScreenImage getSelection() { if (canceled) { return null; } BufferedImage cropImg = cropSelection(); if (cropImg == null) { return null; } rectSelection.x += scrOCP.getX(); rectSelection.y += scrOCP.getY(); ScreenImage ret = new ScreenImage(rectSelection, cropImg); return ret; } private BufferedImage cropSelection() { int w = rectSelection.width, h = rectSelection.height; if (w <= 0 || h <= 0) { return null; } int x = rectSelection.x; int y = rectSelection.y; if (!isLocalScreen && scr_img_scale != 1) { x = (int) (x / scr_img_scale); y = (int) (y / scr_img_scale); w = (int) (w / scr_img_scale); h = (int) (h / scr_img_scale); } BufferedImage crop = new BufferedImage(w, h, scr_img_type); Graphics2D crop_g2d = crop.createGraphics(); try { crop_g2d.drawImage(scr_img_original.getImage().getSubimage(x, y, w, h), null, 0, 0); } catch (RasterFormatException e) { Debug.error("OverlayCapturePrompt: cropSelection: RasterFormatException", e.getMessage()); } crop_g2d.dispose(); return crop; } void drawMessage(Graphics2D g2d) { if (promptMsg == null) { return; } g2d.setFont(fontMsg); g2d.setColor(new Color(1f, 1f, 1f, 1)); int sw = g2d.getFontMetrics().stringWidth(promptMsg); int sh = g2d.getFontMetrics().getMaxAscent(); Rectangle ubound = scrOCP.getBounds(); for (int i = 0; i < Screen.getNumberScreens(); i++) { if (!Screen.getScreen(i).hasPrompt()) { continue; } Rectangle bound = Screen.getBounds(i); int cx = bound.x + (bound.width - sw) / 2 - ubound.x; int cy = bound.y + (bound.height - sh) / 2 - ubound.y; g2d.drawString(promptMsg, cx, cy); } } private void drawSelection(Graphics2D g2d) { if (srcx != destx || srcy != desty) { int x1 = (srcx < destx) ? srcx : destx; int y1 = (srcy < desty) ? srcy : desty; int x2 = (srcx > destx) ? srcx : destx; int y2 = (srcy > desty) ? srcy : desty; rectSelection.x = x1; rectSelection.y = y1; rectSelection.width = (x2 - x1) + 1; rectSelection.height = (y2 - y1) + 1; if (rectSelection.width > 0 && rectSelection.height > 0) { g2d.drawImage(scr_img.getSubimage(x1, y1, x2 - x1 + 1, y2 - y1 + 1), null, x1, y1); } g2d.setColor(selFrameColor); g2d.setStroke(bs); g2d.draw(rectSelection); int cx = (x1 + x2) / 2; int cy = (y1 + y2) / 2; g2d.setColor(selCrossColor); g2d.setStroke(_StrokeCross); g2d.drawLine(cx, y1, cx, y2); g2d.drawLine(x1, cy, x2, cy); if (isLocalScreen && Screen.getNumberScreens() > 1) { drawScreenFrame(g2d, srcScreenId); } } } private void drawScreenFrame(Graphics2D g2d, int scrId) { if (!isLocalScreen) { return; } g2d.setColor(screenFrameColor); g2d.setStroke(strokeScreenFrame); if (screenFrame == null) { screenFrame = Screen.getBounds(scrId); Rectangle ubound = scrOCP.getBounds(); screenFrame.x -= ubound.x; screenFrame.y -= ubound.y; int sw = (int) (strokeScreenFrame.getLineWidth() / 2); screenFrame.x += sw; screenFrame.y += sw; screenFrame.width -= sw * 2; screenFrame.height -= sw * 2; } g2d.draw(screenFrame); } @Override public void paint(Graphics g) { if (scr_img != null) { Graphics2D g2dWin = (Graphics2D) g; if (bi == null) { bi = new BufferedImage(scr_img_rect.width, scr_img_rect.height, scr_img_type); } Graphics2D bfG2 = bi.createGraphics(); bfG2.drawImage(scr_img_darker, 0, 0, this); drawMessage(bfG2); drawSelection(bfG2); g2dWin.drawImage(bi, 0, 0, this); setVisible(true); } else { setVisible(false); } } }