package com.shaneisrael.st.overlay; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Cursor; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.KeyStroke; import com.shaneisrael.st.data.Logger; import com.shaneisrael.st.editor.Editor; import com.shaneisrael.st.prefs.Preferences; import com.shaneisrael.st.upload.SimpleFTPUploader; import com.shaneisrael.st.utilities.CaptureScreen; import com.shaneisrael.st.utilities.ImageUtilities; import com.shaneisrael.st.utilities.Save; import com.shaneisrael.st.utilities.SoundNotifications; import com.shaneisrael.st.utilities.Upload; @SuppressWarnings("serial") public class Overlay extends JPanel implements MouseListener, MouseMotionListener, MouseWheelListener { private static final int LEFT_MOUSE_BUTTON = MouseEvent.BUTTON1; private static final int MIDDLE_MOUSE_BUTTON = MouseEvent.BUTTON2; private static final int RIGHT_MOUSE_BUTTON = MouseEvent.BUTTON3; public static final int SAVE = 0; public static final int UPLOAD = 1; public static final int UPLOAD_FTP = 2; private int mode = 0; private Color selectionColor = new Color(255, 0, 0); private Color overlayColor = new Color(0, 0, 0, 150); private Color infoGreenColor = new Color(0, 255, 0, 128); private Rectangle2D selection; private Rectangle screenRectangle; private Point startPoint = new Point(); private Point endPoint = new Point(); private int mouseX, mouseY; private Font infoFont = new Font("sansserif", Font.BOLD, 12); private BufferedImage selectionImage; private BufferedImage screenshot; private BufferedImage background; //this is a slightly larger image than the screenshot used for the Zoom window private Image zoomImage; //Since scaling has to be an Image private Graphics2D g2d; /* Zoom Rects Values */ private boolean zoomEnabled = true; private int zoomDimension = 40; private int zoomFactor = 4; private int zoomGridMinWidth = zoomFactor * 2; private int zoomMargin = 25; private int zoomWindowWidth = 120; private int zoomCrosshairRadius = 40; private int zoomCrosshairMargin = zoomWindowWidth - zoomCrosshairRadius; private int zoomX = 0; private int zoomY = 0; private int screenOffset = zoomWindowWidth; private CaptureScreen capture; private ScreenBounds bounds; private Toolkit toolkit = Toolkit.getDefaultToolkit(); private OverlayFrame parent; private Editor editor; private Save save; private ArrayList<BufferedImage> selections; private ArrayList<Rectangle> selectionRects; private Rectangle selectionRect; public Overlay(OverlayFrame of) { setupOverlay(); parent = of; selection = new Rectangle2D.Double(); addMouseListener(this); addMouseWheelListener(this); addMouseMotionListener(this); Action submit = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if (activeSelection() || selectionRects.size() > 0) { //create the multi-snippet image if (selections.isEmpty() == false) { selectionImage = ImageUtilities.createMultiSnippet(selections); } if (Preferences.getInstance().isEditorEnabled()) { // send the snippet to the editor Editor.getInstance().initialize(selectionImage, mode); } else { // send the snippet directly to the upload/save queue if (mode == Overlay.UPLOAD) { new Upload(selectionImage, false); } else if (mode == Overlay.SAVE) { save = new Save(); save.save(selectionImage); } else if (mode == Overlay.UPLOAD_FTP) { new SimpleFTPUploader(ImageUtilities.saveTemporarily(selectionImage)); } } parent.disposeAll(); // remove the overlay } } }; Action add = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { if(activeSelection()) { SoundNotifications.playShutter(); selections.add(selectionImage); selectionRect = new Rectangle(startPoint); selectionRect.add(endPoint); selectionRects.add(selectionRect); selectionImage = null; startPoint = new Point(0, 0); endPoint = new Point(0, 0); } } }; Action escape = new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { // setOverlayVisible(false); //remove the overlay selections.clear(); selectionRects.clear(); parent.disposeAll(); } }; this.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "submit"); this.getActionMap().put("submit", submit); this.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "escape"); this.getActionMap().put("escape", escape); this.getInputMap().put(KeyStroke.getKeyStroke("SPACE"), "add"); this.getActionMap().put("add", add); selections = new ArrayList<BufferedImage>(); selectionRects = new ArrayList<Rectangle>(); setFocusable(true); requestFocus(); setMouseCursor(); } private boolean activeSelection() { return selectionImage != null && selectionImage.getWidth() > 0 && selectionImage.getHeight() > 0; } public void setupOverlay() { startPoint = new Point(0, 0); endPoint = new Point(0, 0); capture = new CaptureScreen(); bounds = new ScreenBounds(); screenshot = getScreenShot(); screenRectangle = bounds.getBounds(); // setMouseCursor(); } public BufferedImage getScreenShot() { BufferedImage screenCap = capture.getScreenCapture(); background = new BufferedImage(screenCap.getWidth() + (screenOffset * 2), screenCap.getHeight() + (screenOffset * 2), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = background.createGraphics(); g2d.setColor(Color.BLACK); g2d.fill(new Rectangle(background.getWidth(), background.getHeight())); g2d.drawImage(screenCap, screenOffset / 2, screenOffset / 2, null); g2d.dispose(); screenCap = background; return screenCap; } private void setMouseCursor() { ImageIcon ii = new ImageIcon(this.getClass().getResource("/images/cursor.png")); Image cursorImage = ii.getImage(); Point cursorHotSpot = new Point(0, 0); Cursor customCursor = toolkit.createCustomCursor(cursorImage, cursorHotSpot, "Cursor"); this.setCursor(customCursor); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setStroke(new BasicStroke(1.35f)); drawOverlay(g2d); drawMultiSnippetShadows(g2d); drawSelection(g2d); if (selection.getWidth() > 0 && selection.getHeight() > 0) { drawInfo(g2d); } try { Thread.sleep(5); } catch (InterruptedException e) { Logger.Log(e); e.printStackTrace(); } if (zoomEnabled) drawMagnifyingGlass(g2d); super.repaint(); } private void drawMultiSnippetShadows(Graphics2D g2d2) { for (Rectangle rect : selectionRects) { g2d.setColor(new Color(255, 0, 0, 150)); g2d.draw(rect); g2d.setColor(new Color(255, 255, 255, 50)); g2d.fill(rect); } } private void drawInfo(Graphics2D g2d) { List<MultiColorText> messages = new ArrayList<>(); messages.add(getHelpText()); messages.add(getDimensionText()); MessageBox helpBox = new MessageBox(messages, infoFont); Point infoPoint = calculateInfoPoint(helpBox.getBoundsWithPadding(g2d)); helpBox.setLocation(infoPoint.x, infoPoint.y); helpBox.draw(g2d); } private MultiColorText getHelpText() { LinkedHashMap<String, Color> helpMessage = new LinkedHashMap<>(); helpMessage.put("Press [", infoGreenColor); helpMessage.put("enter", Color.orange); helpMessage.put("] to continue", infoGreenColor); return new MultiColorText(helpMessage); } private MultiColorText getDimensionText() { int imageWidth = (int) selection.getWidth(); int imageHeight = (int) selection.getHeight(); LinkedHashMap<String, Color> sizeMessage = new LinkedHashMap<>(); sizeMessage.put("Dimensions: [", infoGreenColor); sizeMessage.put("" + imageWidth, Color.orange); sizeMessage.put(" x ", infoGreenColor); sizeMessage.put("" + imageHeight, Color.orange); sizeMessage.put("]", infoGreenColor); return new MultiColorText(sizeMessage); } private Point calculateInfoPoint(Rectangle2D bounds) { int bWidth = (int) bounds.getWidth(); int bHeight = (int) bounds.getHeight(); int infoX = 0; int infoY = 0; if (endPoint.x > startPoint.x) { infoX = endPoint.x - bWidth; } else if (endPoint.x < startPoint.x) { infoX = endPoint.x; } if (endPoint.y > startPoint.y) { infoY = endPoint.y; } else if (endPoint.y < startPoint.y) { infoY = endPoint.y - bHeight; } return new Point(infoX, infoY); } private void drawOverlay(Graphics2D g2d) { g2d.drawImage(screenshot, -screenOffset / 2, -screenOffset / 2, null); g2d.setColor(overlayColor); g2d.fillRect(0, 0, screenRectangle.width, screenRectangle.height); } private void drawSelection(Graphics2D g2d) { g2d.setColor(selectionColor); selection.setFrameFromDiagonal(startPoint, endPoint); Rectangle select = selection.getBounds(); if (getSubimage() != null) { selectionImage = getSubimage(); g2d.drawImage(selectionImage, select.x, select.y, null); g2d.drawRect(select.x, select.y, select.width, select.height); } } private void drawMagnifyingGlass(Graphics2D g2d) { Rectangle zoom = getZoomRectangle(); if (getSubimage(zoom) != null) { zoomImage = getSubimage(zoom); zoomImage = zoomImage.getScaledInstance(zoomWindowWidth, zoomWindowWidth, Image.SCALE_SMOOTH); getZoomX(); getZoomY(); g2d.drawImage(zoomImage, zoomX, zoomY, null); g2d.setColor(Color.BLACK); g2d.setStroke(new BasicStroke(2f)); g2d.drawRect(zoomX, zoomY, zoomImage.getWidth(null), zoomImage.getHeight(null)); //draw grid int gridWidth = zoomWindowWidth / (zoomWindowWidth / zoomDimension); if (gridWidth <= zoomGridMinWidth) { g2d.setColor(Color.lightGray); g2d.setStroke(new BasicStroke(1f)); for (int i = 0; i <= zoomWindowWidth; i += (Math.round(zoomWindowWidth / zoomDimension))) { g2d.drawLine(zoomX + i, zoomY, zoomX + i, zoomY + zoomWindowWidth); g2d.drawLine(zoomX, zoomY + i, zoomX + zoomWindowWidth, zoomY + i); } } g2d.setColor(Color.red); g2d.setStroke(new BasicStroke(2.5f)); g2d.drawLine(zoomX + (zoomWindowWidth / 2), zoomY + zoomCrosshairMargin, zoomX + (zoomWindowWidth / 2), zoomY + zoomWindowWidth - zoomCrosshairMargin); g2d.drawLine(zoomX + zoomCrosshairMargin, zoomY + (zoomWindowWidth / 2), zoomX + zoomWindowWidth - zoomCrosshairMargin, zoomY + (zoomWindowWidth / 2)); g2d.setStroke(new BasicStroke(1.35f)); } } private void getZoomX() { if ((mouseX + zoomMargin + zoomWindowWidth) > screenRectangle.width) { zoomX = (mouseX - zoomMargin - zoomWindowWidth); } else if ((mouseX - zoomMargin - zoomWindowWidth) < 0) { zoomX = (mouseX + zoomMargin); } else zoomX = (mouseX + zoomMargin); } private void getZoomY() { if ((mouseY + zoomMargin + zoomWindowWidth) > screenRectangle.height) { zoomY = (mouseY - zoomMargin - zoomWindowWidth); } else if ((mouseY - zoomMargin - zoomWindowWidth) < 0) { zoomY = (mouseY + zoomMargin); } else zoomY = (mouseY + zoomMargin); } private Rectangle getZoomRectangle() { return new Rectangle(mouseX - (zoomDimension / 2), mouseY - (zoomDimension / 2), zoomDimension, zoomDimension); } private BufferedImage getSubimage() { Rectangle select = selection.getBounds(); if (select.width > 0 && select.height > 0) { return screenshot.getSubimage(select.x + (screenOffset / 2), select.y + (screenOffset / 2), select.width, select.height); } else { return null; } } private BufferedImage getSubimage(Rectangle rect) { return screenshot .getSubimage(rect.x + (screenOffset / 2), rect.y + (screenOffset / 2), rect.width, rect.height); } public void setMode(int mode) { this.mode = mode; } @Override public void mouseDragged(MouseEvent me) { if (me.getButton() != MIDDLE_MOUSE_BUTTON) { mouseX = me.getX(); mouseY = me.getY(); endPoint = new Point(mouseX, mouseY); repaint(); } } @Override public void mouseMoved(MouseEvent me) { mouseX = me.getX(); mouseY = me.getY(); repaint(); } @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MIDDLE_MOUSE_BUTTON) { SoundNotifications.playShutter(); float op = parent.getOpacity(); parent.setOpacity(0f); screenshot = getScreenShot(); parent.setOpacity(op); parent.repaint(); } else if (e.getButton() == RIGHT_MOUSE_BUTTON) { zoomEnabled = !zoomEnabled; } } @Override public void mouseEntered(MouseEvent arg0) { } @Override public void mouseExited(MouseEvent arg0) { } @Override public void mousePressed(MouseEvent e) { if (e.getButton() == LEFT_MOUSE_BUTTON) { startPoint = new Point(mouseX, mouseY); endPoint = new Point(mouseX, mouseY); } } @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == LEFT_MOUSE_BUTTON) { mouseX = e.getX(); mouseY = e.getY(); endPoint = new Point(mouseX, mouseY); } } @Override public void mouseWheelMoved(MouseWheelEvent e) { int notches = e.getWheelRotation(); if (notches < 0) { if (!zoomEnabled) { if (parent.getOpacity() - .05f >= .005f) parent.setOpacity(parent.getOpacity() - .05f); } else { if ((zoomDimension - zoomFactor) >= zoomFactor) zoomDimension -= zoomFactor; } } else { if (!zoomEnabled) { if (parent.getOpacity() + .05f <= 1f) parent.setOpacity(parent.getOpacity() + .05f); else parent.setOpacity(1f); } else { if ((zoomDimension + zoomFactor) <= zoomWindowWidth) zoomDimension += zoomFactor; } } } public void dispose() { selections.clear(); screenshot = null; selectionImage = null; background = null; zoomImage = null; g2d.dispose(); } }