如何制作Javafx图像裁剪应用程序 [英] How to make a Javafx Image Crop App

查看:179
本文介绍了如何制作Javafx图像裁剪应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的同事构建一个应用程序来裁剪图像。
我使用FXML和Scene Builder来构建GUI。用户点击一个按钮从他的电脑中选择一个图像。然后,图像显示在GUI中。用户可以在窗格中缩放和移动。最后,点击一个按钮将编辑后的图像保存到他的电脑上。

然而,我真的不知道应该使用什么库来构建应用程序。
这是我第一次处理图形。我不知道如何读取图像,裁剪图像并写入图像。为窗格 Javafx画布

你的问题也是如此很多StackOverflow需要回答。我建议您先阅读有关JavaFX的官方Oracle文档



然而,由于这是一个有趣的话题,所以这里是代码的答案。



有几件事情需要考虑:




  • 使用ImageView作为容器

  • 在图片较大时使用ScrollPane

  • 提供选择机制

  • 裁剪图像本身

  • 将图像保存到文件中,提供文件选择器对话框



这非常棒。在下面的例子中,使用鼠标左键进行选择,右键单击鼠标右键作为上下文菜单,然后在选择边界处选择ImageView节点的快照,然后将图像保存到文件中。

  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;


/ **
*加载图片,为橡皮筋选择提供矩形。按下鼠标右键裁剪上下文菜单,然后在选择矩形处裁剪图像并将其保存为jpg。
* /
public class ImageCropWithRubberBand扩展应用程序{

RubberBandSelection rubberBandSelection;
ImageView imageView;

阶段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();

//图层的容器
ScrollPane scrollPane = new ScrollPane();

//图像层:一组图像
组imageLayer = new Group();

//载入图片
//图片图片= 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);

//将图像的容器作为javafx节点
imageView = new ImageView(image);

//将图像添加到图层
imageLayer.getChildren()。add(imageView);

//在图片很大的情况下,使用scrollpane进行图片视图
scrollPane.setContent(imageLayer);

//在场景中放置滚动条
root.setCenter(scrollPane);

//橡皮筋选择
rubberBandSelection = new RubberBandSelection(imageLayer);

//创建上下文菜单和菜单项
ContextMenu contextMenu = new ContextMenu();

MenuItem cropMenuItem = new MenuItem(Crop);
cropMenuItem.setOnAction(new EventHandler< ActionEvent>(){
public void handle(ActionEvent e){

//获取图像的边界
Bounds selectionBounds = rubberBandSelection.getBounds();

//显示边界信息
System.out.println(Selected area:+ selectionBounds);

//裁剪图片
crop(selectionBounds);

}
});
contextMenu.getItems()。add(cropMenuItem);

//在图像层设置上下文菜单
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);

档案档= 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);

//保存图片
// !!!由于透明度有问题(使用下面的方法)!!!
// --------------------------------
//尝试{
// ImageIO.write(SwingFXUtils.fromFXImage(wi,null),jpg,file);
//} catch(IOException e){
// e.printStackTrace();
//}


//保存图片(不含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);

尝试{

ImageIO.write(bufImageRGB,jpg,file);

System.out.println(Image saved to+ file.getAbsolutePath());

} catch(IOException e){
e.printStackTrace();
}

graphics.dispose();


$ b / **
*用鼠标光标拖动矩形以获得选择范围
* /
public static class RubberBandSelection {

final DragContext dragContext = new DragContext();
Rectangle rect = new Rectangle();

小组小组;


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>(){
$ b $ @Override
public void handle(MouseEvent event){
$ b $ if(event.isSecondaryButtonDown())
return;

//删除旧rect
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);

group.getChildren()。remove(rect);


//准备新的拖动操作
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>(){
$ b $ @Override
public void handle(MouseEvent event){
$ b $ 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){
$ b $ if(event.isSecondaryButtonDown())
return;

//删除矩形
//注意:我们想保留裁剪的ruuberband选择=>代码仅被注释掉
/ *
rect.setX(0);
rect.setY(0);
rect.setWidth(0);
rect.setHeight(0);

group.getChildren()。remove(rect);
* /

}
};
private static final DragContext {

public double mouseAnchorX;
public double mouseAnchorY;

$ b}
}
}

/ p>

裁剪后的图片:


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?

解决方案

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.

There are several things you need to consider:

  • use an ImageView as container
  • use a ScrollPane in case the image is larger
  • provide a selection mechanism
  • crop the image itself
  • save the image to a file, providing a file chooser dialog

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;


        }
    }
}

Screenshot:

The cropped image:

这篇关于如何制作Javafx图像裁剪应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆