如何制作 Javafx 图像裁剪应用程序 [英] How to make a Javafx Image Crop App
问题描述
我正在为我的同事构建一个应用程序来裁剪图像.我使用 FXML 和 Scene Builder 来构建 GUI.用户单击按钮从他的计算机中选择图像.然后,图像显示在 GUI 中.用户可以在窗格中缩放和移动.最后,点击一个按钮,将编辑好的图片保存到他的电脑.
但是,我真的不知道我应该使用什么库来构建应用程序.
这是我第一次处理图形.我不知道如何读取图像、裁剪图像和写入图像.Javafx Canvas 用于窗格?
除了 Java 文档之外,还有什么好的资源可以阅读以了解如何做到这一点?
I am building an application for my colleagues to crop image.
I use FXML with Scene Builder to build the GUI. The user click a button to choose a image from his computer. Then, the image is shown in the GUI. User can zoom and move in the pane. At last, click a button to save the edited image to his computer.
However, I don't really know what library should I use to build the app.
This is my first time to deal with graphics. I have no idea how to read image, crop the image and write the image. Javafx Canvas for the Pane?
Any good resources other than the java doc to read to learn how can I do this?
推荐答案
您的问题太多,无法在 StackOverflow 上得到解答.我建议您首先阅读 关于 JavaFX 的官方 Oracle 文档.
Your question is too much to be answered on StackOverflow. I suggest you start with reading the official Oracle documentation about JavaFX.
然而,由于这是一个有趣的话题,这里是代码中的答案.
However, since it's an interesting topic, here's the answer in code.
您需要考虑以下几点:
- 使用 ImageView 作为容器
- 如果图像较大,请使用 ScrollPane
- 提供选择机制
- 裁剪图像本身
- 将图像保存到文件中,提供文件选择器对话框
差不多就是这样.在下面的示例中,使用鼠标左键进行选择,使用鼠标右键进行裁剪上下文菜单,然后在选择边界处拍摄 ImageView 节点的快照,然后将图像保存到文件中.
This is pretty much it. In the example below use the left mouse button for selection, the right mouse button for the crop context menu which then takes a snapshot of the ImageView node at the seleciton bounds an then saves the image to a file.
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeLineCap;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
/**
* Load image, provide rectangle for rubberband selection. Press right mouse button for "crop" context menu which then crops the image at the selection rectangle and saves it as jpg.
*/
public class ImageCropWithRubberBand extends Application {
RubberBandSelection rubberBandSelection;
ImageView imageView;
Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
primaryStage.setTitle("Image Crop");
BorderPane root = new BorderPane();
// container for image layers
ScrollPane scrollPane = new ScrollPane();
// image layer: a group of images
Group imageLayer = new Group();
// load the image
// Image image = new Image( getClass().getResource( "cat.jpg").toExternalForm());
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Gatto_europeo4.jpg/1024px-Gatto_europeo4.jpg");
// the container for the image as a javafx node
imageView = new ImageView( image);
// add image to layer
imageLayer.getChildren().add( imageView);
// use scrollpane for image view in case the image is large
scrollPane.setContent(imageLayer);
// put scrollpane in scene
root.setCenter(scrollPane);
// rubberband selection
rubberBandSelection = new RubberBandSelection(imageLayer);
// create context menu and menu items
ContextMenu contextMenu = new ContextMenu();
MenuItem cropMenuItem = new MenuItem("Crop");
cropMenuItem.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
// get bounds for image crop
Bounds selectionBounds = rubberBandSelection.getBounds();
// show bounds info
System.out.println( "Selected area: " + selectionBounds);
// crop the image
crop( selectionBounds);
}
});
contextMenu.getItems().add( cropMenuItem);
// set context menu on image layer
imageLayer.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if (event.isSecondaryButtonDown()) {
contextMenu.show(imageLayer, event.getScreenX(), event.getScreenY());
}
}
});
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
}
private void crop( Bounds bounds) {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Save Image");
File file = fileChooser.showSaveDialog( primaryStage);
if (file == null)
return;
int width = (int) bounds.getWidth();
int height = (int) bounds.getHeight();
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
parameters.setViewport(new Rectangle2D( bounds.getMinX(), bounds.getMinY(), width, height));
WritableImage wi = new WritableImage( width, height);
imageView.snapshot(parameters, wi);
// save image
// !!! has bug because of transparency (use approach below) !!!
// --------------------------------
// try {
// ImageIO.write(SwingFXUtils.fromFXImage( wi, null), "jpg", file);
// } catch (IOException e) {
// e.printStackTrace();
// }
// save image (without alpha)
// --------------------------------
BufferedImage bufImageARGB = SwingFXUtils.fromFXImage(wi, null);
BufferedImage bufImageRGB = new BufferedImage(bufImageARGB.getWidth(), bufImageARGB.getHeight(), BufferedImage.OPAQUE);
Graphics2D graphics = bufImageRGB.createGraphics();
graphics.drawImage(bufImageARGB, 0, 0, null);
try {
ImageIO.write(bufImageRGB, "jpg", file);
System.out.println( "Image saved to " + file.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
graphics.dispose();
}
/**
* Drag rectangle with mouse cursor in order to get selection bounds
*/
public static class RubberBandSelection {
final DragContext dragContext = new DragContext();
Rectangle rect = new Rectangle();
Group group;
public Bounds getBounds() {
return rect.getBoundsInParent();
}
public RubberBandSelection( Group group) {
this.group = group;
rect = new Rectangle( 0,0,0,0);
rect.setStroke(Color.BLUE);
rect.setStrokeWidth(1);
rect.setStrokeLineCap(StrokeLineCap.ROUND);
rect.setFill(Color.LIGHTBLUE.deriveColor(0, 1.2, 1, 0.6));
group.addEventHandler(MouseEvent.MOUSE_PRESSED, onMousePressedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_DRAGGED, onMouseDraggedEventHandler);
group.addEventHandler(MouseEvent.MOUSE_RELEASED, onMouseReleasedEventHandler);
}
EventHandler<MouseEvent> onMousePressedEventHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( event.isSecondaryButtonDown())
return;
// remove old rect
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().remove( rect);
// prepare new drag operation
dragContext.mouseAnchorX = event.getX();
dragContext.mouseAnchorY = event.getY();
rect.setX(dragContext.mouseAnchorX);
rect.setY(dragContext.mouseAnchorY);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().add( rect);
}
};
EventHandler<MouseEvent> onMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( event.isSecondaryButtonDown())
return;
double offsetX = event.getX() - dragContext.mouseAnchorX;
double offsetY = event.getY() - dragContext.mouseAnchorY;
if( offsetX > 0)
rect.setWidth( offsetX);
else {
rect.setX(event.getX());
rect.setWidth(dragContext.mouseAnchorX - rect.getX());
}
if( offsetY > 0) {
rect.setHeight( offsetY);
} else {
rect.setY(event.getY());
rect.setHeight(dragContext.mouseAnchorY - rect.getY());
}
}
};
EventHandler<MouseEvent> onMouseReleasedEventHandler = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
if( event.isSecondaryButtonDown())
return;
// remove rectangle
// note: we want to keep the ruuberband selection for the cropping => code is just commented out
/*
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);
group.getChildren().remove( rect);
*/
}
};
private static final class DragContext {
public double mouseAnchorX;
public double mouseAnchorY;
}
}
}
截图:
裁剪后的图像:
这篇关于如何制作 Javafx 图像裁剪应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!