package com.luooqi.ocr.snap; import cn.hutool.core.swing.ScreenUtil; import cn.hutool.log.StaticLog; import com.luooqi.ocr.MainFm; import com.luooqi.ocr.model.CaptureInfo; import com.luooqi.ocr.utils.CommUtils; import javafx.animation.AnimationTimer; import javafx.application.Platform; import javafx.embed.swing.SwingFXUtils; import javafx.scene.Cursor; import javafx.scene.Scene; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.WritableImage; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseButton; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.stage.Stage; import java.awt.*; import java.awt.image.BufferedImage; /** * This is the Window which is used from the user to draw the rectangle representing an area on the screen to be captured. * * @author GOXR3PLUS */ public class ScreenCapture { private BorderPane rootPane; private Canvas mainCanvas; private CaptureInfo data; private GraphicsContext gc; private Scene scene; private Stage stage; public static boolean isSnapping = false; /** * When a key is being pressed into the capture window then this Animation Timer is doing it's magic. */ private AnimationTimer yPressedAnimation = new AnimationTimer() { private long nextSecond = 0L; private long precisionLevel; @Override public void start() { nextSecond = 0L; precisionLevel = 0L; super.start(); } @Override public void handle(long nanos) { if (nanos >= nextSecond) { nextSecond = nanos + precisionLevel; // With special key pressed // (we want [LEFT] and [DOWN] side of the rectangle to be // movable) // No Special Key is Pressed // (we want [RIGHT] and [UP] side of the rectangle to be // movable) // ------------------------------ if (data.rightPressed.get()) { if (data.shiftPressed.get()) { // Special Key? if (data.mouseXNow > data.mouseXPressed) { // Mouse gone Right? data.mouseXPressed += 1; } else { data.mouseXNow += 1; } } else { if (data.mouseXNow > data.mouseXPressed) { // Mouse gone Right? data.mouseXNow += 1; } else { data.mouseXPressed += 1; } } } if (data.leftPressed.get()) { if (data.shiftPressed.get()) { // Special Key? if (data.mouseXNow > data.mouseXPressed) { // Mouse gone Right? data.mouseXPressed -= 1; } else { data.mouseXNow -= 1; } } else { if (data.mouseXNow > data.mouseXPressed) { // Mouse gone Right? data.mouseXNow -= 1; } else { data.mouseXPressed -= 1; } } } if (data.upPressed.get()) { if (data.shiftPressed.get()) { // Special Key? if (data.mouseYNow > data.mouseYPressed) { // Mouse gone UP? data.mouseYNow -= 1; } else { data.mouseYPressed -= 1; } } else { if (data.mouseYNow > data.mouseYPressed) { // Mouse gone UP? data.mouseYPressed -= 1; } else { data.mouseYNow -= 1; } } } if (data.downPressed.get()) { if (data.shiftPressed.get()) { // Special Key? if (data.mouseYNow > data.mouseYPressed) { // Mouse gone UP? data.mouseYNow += 1; } else { data.mouseYPressed += 1; } } else { if (data.mouseYNow > data.mouseYPressed) { // Mouse gone UP? data.mouseYPressed += 1; } else { data.mouseYNow += 1; } } } if (data.mouseXPressed < 0){ data.mouseXPressed = 0; } if (data.mouseXNow < 0){ data.mouseXNow = 0; } if (data.mouseXPressed > CaptureInfo.ScreenWidth){ data.mouseXPressed = CaptureInfo.ScreenWidth; } if (data.mouseXNow > CaptureInfo.ScreenWidth){ data.mouseXNow = CaptureInfo.ScreenWidth; } repaintCanvas(); } } }; /** * Constructor. */ public ScreenCapture(Stage mainStage) { data = new CaptureInfo(); stage = mainStage; rootPane = new BorderPane(); mainCanvas = new Canvas(); mainCanvas.setCursor(Cursor.CROSSHAIR); mainCanvas.setStyle(CommUtils.STYLE_TRANSPARENT); rootPane.getChildren().add(mainCanvas); // Scene scene = new Scene(rootPane, CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight, Color.TRANSPARENT); scene.setCursor(Cursor.NONE); addKeyHandlers(); // Canvas mainCanvas.setWidth(CaptureInfo.ScreenWidth); mainCanvas.setHeight(CaptureInfo.ScreenHeight); mainCanvas.setOnMousePressed(m -> { if (m.getButton() == MouseButton.PRIMARY) { data.mouseXPressed = (int) m.getX(); data.mouseYPressed = (int) m.getY(); } }); mainCanvas.setOnMouseDragged(m -> { if (m.getButton() == MouseButton.PRIMARY) { if (m.getScreenX() >= CaptureInfo.ScreenMinX && m.getScreenX() <= CaptureInfo.ScreenMaxX){ data.mouseXNow = (int) m.getX(); } else if(m.getScreenX() > CaptureInfo.ScreenMaxX){ data.mouseXNow = CaptureInfo.ScreenWidth; } if (m.getScreenY() <= CaptureInfo.ScreenHeight){ data.mouseYNow = (int) m.getY(); } else{ data.mouseYNow = CaptureInfo.ScreenHeight; } repaintCanvas(); } }); // graphics context 2D initGraphContent(); // HideFeaturesPressed data.hideExtraFeatures.addListener((observable, oldValue, newValue) -> repaintCanvas()); } private void initGraphContent() { gc = mainCanvas.getGraphicsContext2D(); gc.setLineDashes(6); gc.setFont(Font.font("null", FontWeight.BOLD, 14)); } /** * Adds the KeyHandlers to the Scene. */ private void addKeyHandlers() { // -------------Read the below to understand the Code------------------- // the default prototype of the below code is // 1->when the user is pressing RIGHT ARROW -> The rectangle is // increasing from the RIGHT side // 2->when the user is pressing LEFT ARROW -> The rectangle is // decreasing from the RIGHT side // 3->when the user is pressing UP ARROW -> The rectangle is increasing // from the UP side // 4->when the user is pressing DOWN ARROW -> The rectangle is // decreasing from the UP side // when ->LEFT KEY <- is pressed // 1->when the user is pressing RIGHT ARROW -> The rectangle is // increasing from the LEFT side // 2->when the user is pressing LEFT ARROW -> The rectangle is // decreasing from the LEFT side // 3->when the user is pressing UP ARROW -> The rectangle is increasing // from the DOWN side // 4->when the user is pressing DOWN ARROW -> The rectangle is // decreasing from the DOWN side scene.setOnKeyPressed(key -> { if (key.isShiftDown()) data.shiftPressed.set(true); if (key.getCode() == KeyCode.LEFT) data.leftPressed.set(true); if (key.getCode() == KeyCode.RIGHT) data.rightPressed.set(true); if (key.getCode() == KeyCode.UP) data.upPressed.set(true); if (key.getCode() == KeyCode.DOWN) data.downPressed.set(true); if (key.getCode() == KeyCode.H) data.hideExtraFeatures.set(true); }); // keyReleased scene.setOnKeyReleased(key -> { if (key.getCode() == KeyCode.SHIFT) { data.shiftPressed.set(false); } if (key.getCode() == KeyCode.RIGHT) { if (key.isControlDown()) { data.mouseXNow = (int) stage.getWidth(); repaintCanvas(); } data.rightPressed.set(false); } if (key.getCode() == KeyCode.LEFT) { if (key.isControlDown()) { data.mouseXPressed = 0; repaintCanvas(); } data.leftPressed.set(false); } if (key.getCode() == KeyCode.UP) { if (key.isControlDown()) { data.mouseYPressed = 0; repaintCanvas(); } data.upPressed.set(false); } if (key.getCode() == KeyCode.DOWN) { if (key.isControlDown()) { data.mouseYNow = (int) stage.getHeight(); repaintCanvas(); } data.downPressed.set(false); } if (key.getCode() == KeyCode.A && key.isControlDown()) { selectWholeScreen(); } if (key.getCode() == KeyCode.H) { data.hideExtraFeatures.set(false); } if (key.getCode() == KeyCode.ESCAPE || key.getCode() == KeyCode.BACK_SPACE) { cancelSnap(); isSnapping = false; } else if (key.getCode() == KeyCode.ENTER || key.getCode() == KeyCode.SPACE) { deActivateAllKeys(); isSnapping = false; prepareImage(); } }); data.anyPressed.addListener((obs, wasPressed, isNowPressed) -> { if (isNowPressed) { yPressedAnimation.start(); } else { yPressedAnimation.stop(); } }); rootPane.setOnMouseClicked(event -> { if (event.getClickCount() > 1){ if (data.rectWidth * data.rectHeight > 0){ rootPane.fireEvent(new KeyEvent(KeyEvent.KEY_RELEASED, "", "", KeyCode.ENTER, false, false, false, false)); } } }); } /** * Deactivates the keys contained into this method. */ private void deActivateAllKeys() { data.shiftPressed.set(false); data.upPressed.set(false); data.rightPressed.set(false); data.downPressed.set(false); data.leftPressed.set(false); data.hideExtraFeatures.set(false); } /** * Repaint the canvas of the capture window. */ private void repaintCanvas() { gc.clearRect(0, 0, CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight); gc.setFill(CommUtils.MASK_COLOR); gc.fillRect(0, 0, CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight); gc.setFont(data.font); gc.setStroke(Color.RED); gc.setLineWidth(1); // smart calculation of where the mouse has been dragged data.rectWidth = (data.mouseXNow > data.mouseXPressed) ? data.mouseXNow - data.mouseXPressed // RIGHT : data.mouseXPressed - data.mouseXNow // LEFT ; data.rectHeight = (data.mouseYNow > data.mouseYPressed) ? data.mouseYNow - data.mouseYPressed // DOWN : data.mouseYPressed - data.mouseYNow // UP ; data.rectUpperLeftX = // -------->UPPER_LEFT_X (data.mouseXNow > data.mouseXPressed) ? data.mouseXPressed // RIGHT : data.mouseXNow// LEFT ; data.rectUpperLeftY = // -------->UPPER_LEFT_Y (data.mouseYNow > data.mouseYPressed) ? data.mouseYPressed // DOWN : data.mouseYNow // UP ; gc.strokeRect(data.rectUpperLeftX - 1.00, data.rectUpperLeftY - 1.00, data.rectWidth + 2.00, data.rectHeight + 2.00); gc.clearRect(data.rectUpperLeftX, data.rectUpperLeftY, data.rectWidth, data.rectHeight); // draw the text if (!data.hideExtraFeatures.getValue() && (data.rectWidth > 0 || data.rectHeight > 0)) { double middle = data.rectUpperLeftX + data.rectWidth / 2.00; gc.setLineWidth(1); gc.setFill(Color.FIREBRICK); gc.fillRect(middle - 77, data.rectUpperLeftY < 50 ? data.rectUpperLeftY + 2 : data.rectUpperLeftY - 18.00, 100, 18); gc.setFill(Color.WHITE); gc.fillText(data.rectWidth + " * " + data.rectHeight, middle - 77 + 9, data.rectUpperLeftY < 50 ? data.rectUpperLeftY + 17.00 : data.rectUpperLeftY - 4.00); } } /** * Selects whole Screen. */ private void selectWholeScreen() { data.mouseXPressed = 0; data.mouseYPressed = 0; data.mouseXNow = (int) stage.getWidth(); data.mouseYNow = (int) stage.getHeight(); repaintCanvas(); } public void prepareForCapture() { isSnapping = true; MainFm.stage.setOpacity(0.0f); Platform.runLater(() -> { Rectangle rectangle = CommUtils.getDisplayScreen(MainFm.stage); data.reset(); CaptureInfo.ScreenMinX = rectangle.x; CaptureInfo.ScreenMaxX = rectangle.x + rectangle.width; CaptureInfo.ScreenWidth = rectangle.width; CaptureInfo.ScreenHeight = rectangle.height; BufferedImage bufferedImage = ScreenUtil.captureScreen(rectangle); //bufferedImage = Scalr.resize(bufferedImage, Scalr.Method.QUALITY, Scalr.Mode.AUTOMATIC, CaptureInfo.ScreenWidth * 2, CaptureInfo.ScreenHeight * 2); WritableImage fxImage = SwingFXUtils.toFXImage(bufferedImage, null); deActivateAllKeys(); scene.setRoot(new Pane()); scene = new Scene(rootPane, CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight, Color.TRANSPARENT); addKeyHandlers(); mainCanvas.setWidth(CaptureInfo.ScreenWidth); mainCanvas.setHeight(CaptureInfo.ScreenHeight); mainCanvas.setCursor(Cursor.CROSSHAIR); initGraphContent(); rootPane.setBackground(new Background(new BackgroundImage(fxImage, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.CENTER, new BackgroundSize(CaptureInfo.ScreenWidth, CaptureInfo.ScreenHeight, false, false, true, true)))); repaintCanvas(); stage.setScene(scene); stage.setFullScreenExitHint(""); if (stage.isIconified()){ stage.setIconified(false); } stage.setFullScreen(true); stage.setAlwaysOnTop(true); stage.setOpacity(1.0f); stage.requestFocus(); }); } private void prepareImage() { gc.clearRect(0, 0, stage.getWidth(), stage.getHeight()); BufferedImage image; try { mainCanvas.setDisable(true); image = new Robot().createScreenCapture(new Rectangle(data.rectUpperLeftX + CaptureInfo.ScreenMinX, data.rectUpperLeftY, data.rectWidth, data.rectHeight)); } catch (AWTException ex) { StaticLog.error(ex); return; } finally { mainCanvas.setDisable(false); MainFm.restore(false); } MainFm.doOcr(image); } public void cancelSnap() { deActivateAllKeys(); MainFm.restore(true); } }