无需使用awt即可保存图像 [英] Save image without having to use awt

查看:158
本文介绍了无需使用awt即可保存图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

JavaFX中存在一个旧错误,它阻止您使用

There's an old bug in JavaFX which prevents you to save images properly using

ImageIO.write(SwingFXUtils.fromFXImage( wi, null), "jpg", new File( fileName1));

从节点拍摄快照并尝试将其保存为jpg文件时会出现问题。当您将jpg加载到图像并保存该图像时不会发生。

The problem occurs when you take a snapshot from a node and try to save it as jpg file. It doesn't occur when you load a jpg into and image and save that image.

现在已经有一段时间了,它仍然没有修复。是否有一个适当的解决方法,而不必使用awt?

It's been a while now and it still isn't fixed. Is there a proper workaround for this without having to use awt?

我知道SwingFXUtils在内部使用awt,但在你自己的项目中使用它感觉是错误的。

I know that SwingFXUtils uses awt internally, but having it in your own project feels just wrong.

我检查了错误报告。他们被关闭了

I checked the bug reports. They were closed with

问题

已修复

然而,这是一个问题而且没有修复。

However, it is an issue and it isn't fixed.

这是完整的示例代码,请更改fileName1和fileName2变量以匹配您的路径:

Here's full example code, please change the fileName1 and fileName2 variables to match your path:

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.scene.Group;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import javax.imageio.ImageIO;

public class ImageSave extends Application {

    String fileName1 = "c:/temp/1.jpg"; // TODO: change filepath
    String fileName2 = "c:/temp/2.jpg"; // TODO: change filepath

    ImageView imageView;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        primaryStage.setTitle("Image Crop");

        BorderPane root = new BorderPane();


        Button button = new Button( "Save");
        button.setOnAction(e -> save());

        root.setTop(button);

        // 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);

        primaryStage.setScene(new Scene(root, 1024, 768));
        primaryStage.show();
    }

    private void save() {

        SnapshotParameters parameters = new SnapshotParameters();
        // parameters.setFill(Color.TRANSPARENT);

        WritableImage wi = new WritableImage( (int) imageView.getBoundsInLocal().getWidth(), (int) imageView.getBoundsInLocal().getHeight());
        imageView.snapshot(parameters, wi);

        // save image 
        // !!! has bug because of transparency (use approach below) !!!
        // --------------------------------
        try {

            ImageIO.write(SwingFXUtils.fromFXImage( wi, null), "jpg", new File( fileName1));

            System.out.println( "Image saved to " + fileName1);

        } 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", new File( fileName2)); 

            System.out.println( "Image saved to " + fileName2);

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

        graphics.dispose();

    }

}

JavaFX版本:

awt版本:

推荐答案

我做了一些更多的研究并做了一个显示错误的例子处理一些方法:

I've done a bit more reasearch and made an example showing the wrong handling of some methods:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SplitPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.image.WritablePixelFormat;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javax.imageio.ImageIO;

public class WritableImageDemo extends Application {

  private Image src;
  private int width;
  private int height;
  ImageView srcView;
  ImageView srcView2;
  ImageView srcView3;

  @Override
  public void start(Stage primaryStage) {
    primaryStage.setTitle("Image");

    src = new Image("http://www.gnu.org/graphics/gnu-head.jpg");
    width = (int) src.getWidth();
    height = (int) src.getHeight();

    srcView = new ImageView(src);
    srcView2 = new ImageView();
    srcView3 = new ImageView();

    ScrollPane scrollPane = new ScrollPane();
    ScrollPane scrollPane2 = new ScrollPane();
    ScrollPane scrollPane3 = new ScrollPane();

    scrollPane.setContent(srcView);
    scrollPane2.setContent(srcView2);
    scrollPane3.setContent(srcView3);

    SplitPane root = new SplitPane(scrollPane, scrollPane2, scrollPane3);

    primaryStage.setScene(new Scene(root, 800, 600));
    primaryStage.show();

    srcView2.setImage(writeToFile());
  }

  private WritableImage writeToFile() {
    WritableImage insert = new WritableImage(width, height);
    WritableImage newimage = new WritableImage(width, height);

    SnapshotParameters parameters = new SnapshotParameters();
    parameters.setFill(Color.TRANSPARENT);
    // make a snapshot
    srcView.snapshot(parameters, insert);

    PixelReader reader = insert.getPixelReader();
    PixelWriter writer = newimage.getPixelWriter();
    WritablePixelFormat<IntBuffer> format = WritablePixelFormat.getIntArgbInstance();

    System.out.println("WritablePixelFormat.getType(): " + format.getType());
    // the following normally creates an exact copy of the original
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        int recWidth = 1;
        int recHeight = 1;

        int[] buffer = new int[recWidth * recHeight];
        reader.getPixels(x, y, recWidth, recHeight, format, buffer, 0, recWidth);

        int alpha = 0;
        int red = 0;
        int green = 0;
        int blue = 0;

        for (int color : buffer) {
          alpha += (color >>> 24);
          red += (color >>> 16 & 0xFF);
          green += (color >>> 8 & 0xFF);
          blue += (color & 0xFF);
        }
        alpha = alpha / recWidth / recHeight;
        red = red / recWidth / recHeight;
        green = green / recWidth / recHeight;
        blue = blue / recWidth / recHeight;

        int color = (alpha << 24) + (red << 16) + (green << 8) + blue;
        Arrays.fill(buffer, color);

        writer.setPixels(x, y, recWidth, recHeight, format, buffer, 0, recWidth);
      }

    }
    System.out.println("Reader Type: " + reader.getPixelFormat().getType());
    System.out.println("Writer Type: " + writer.getPixelFormat().getType());

    try {
      BufferedImage bf = SwingFXUtils.fromFXImage(newimage, null);
      ImageIO.write(bf, "jpg", new File("C:\\temp\\test1.jpg"));
    } catch (IOException ex) {
      Logger.getLogger(WritableImageDemo.class.getName()).
              log(Level.SEVERE, null, ex);
    }
    try {
      Image img = new Image(new FileInputStream(new File("C:\\temp\\test1.jpg")));
      srcView3.setImage(img);
    } catch (IOException ex) {
      Logger.getLogger(WritableImageDemo.class.getName()).log(Level.SEVERE, null, ex);
    }
    return newimage;
  }

  public static void main(String[] args) {
    launch(args);
  }
}

如果我运行此代码,我会得到这个结果。在左侧显示原始读取图像,在中间从方法复制,在右侧显示由SwingFXUtils.fromFXImage方法创建的图像。所以,这对我来说是一个错误!

If I run this code, I'll get this result. Showing in the left, the original read image, in middle the copied from the method and in the right the created one by the SwingFXUtils.fromFXImage method. So, this is to me a bug!

关于错误的新评论:


这可能与JIRA中的问题无关,但
以下是您程序中的错误:

It's probably not relevant to the problem in thie JIRA, but the following is a bug in your program:

srcView.snapshot(parameters, insert); 

您不能依赖实际使用传入图像的快照。它是
,如果可以,但为了正确,你需要使用方法的返回值
。例如:

You must not rely on snapshot actually using the passed in image. It will if it can, but for correctness you need to use the return value of the method. For example:

insert = srcView.snapshot(parameters, insert);


他们已安排版本9的错误修复。

and they have scheduled the bug fix for version 9.

这篇关于无需使用awt即可保存图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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