【问题标题】:Save image without having to use awt无需使用 awt 即可保存图像
【发布时间】:2015-06-23 05:32:06
【问题描述】:

JavaFX 中有一个旧错误会阻止您正确使用保存图像

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

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

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

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

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

Not an IssueFixed

但是,这是一个问题,并没有解决。

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

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 版本:

【问题讨论】:

  • 也许我遗漏了一些东西,但这在 Java 8u60 ea、OS X 10.9.5 上对我来说是正确的。 Java 8u20 中也为我解决了此类问题(请参阅:stackoverflow.com/questions/16721917/display-rtp-mjpeg)。请注意,在测试时,我注释掉了您的快照代码,使 image 变量成为 ImageSave 类的成员,并使用 fromFXImage( image, null) 而不是 fromFXImage( wi, null)。不确定这些修改是否会影响您的测试或您试图演示的内容。如果您仍然认为这是一个问题,那么总会有:bugreport.java.com
  • 快照方法是问题所在,因为它将 PixelWriter.Type 设置为 INT_ARGB_PRE,因此之后您的图像中(甚至在图像中)有一个 alpha。 SwingFXUtils.fromFXImage() 将完成剩下的工作,即使您尝试将 BufferedImage 添加到 RGB 类型的方法。该方法不应在 jpeg 文件上设置 alpha!他想为快照添加透明度。
  • @jewelsea:我刚试过,你是对的。 NwDx也是如此。快照方法是问题所在。当我加载图像然后保存它时,一切都很好。当我对图像进行快照并保存时,就会出现问题。
  • @NwDX:我不想增加透明度。我希望快照和保存到 jpg 的工作正常。
  • @Roland 感谢您解决问题。

标签: javafx


【解决方案1】:

我做了更多的研究,并做了一个例子来说明一些方法的错误处理:

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 方法创建的图像。所以,这对我来说是一个错误!

更新

关于错误的新评论:

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

srcView.snapshot(parameters, insert); 

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

insert = srcView.snapshot(parameters, insert);

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

【讨论】:

  • 然后是 png,在文本编辑器中检查文件的第一个字符。我需要一张jpg。
  • 如您所说,jpeg 无法处理透明度。
  • 是的,但 ImageIO.write 似乎不知道 :-)
  • 我刚刚阅读了错误报告。嗯,2012年报的,可能你再试试报吧。 JavaFX 现在是 JRE/JDK 的一部分,因此将其设置为“无问题”的参数已经消失。
  • 我已经报告了这个错误,review id: JI-9021748
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-10
  • 2016-10-04
  • 2016-10-08
  • 2023-03-21
  • 2017-01-10
  • 1970-01-01
相关资源
最近更新 更多