问题
I created a simple JavaFX image editor. There are two instances of ImageView (on for the original image and one for the edited one). A method converts the WritableImage to grayscale. The problem is, the conversion takes approximately twice the time Swing needs to do the same task. Any idea why?
@FXML
void loadImage(ActionEvent event) {
FileChooser fileChooser = new FileChooser();
File file = fileChooser.showOpenDialog(Main.primaryStage);
if (file != null) {
Image image = new Image("file:" + file.getAbsolutePath());
imViewOrig.setImage(image);
PixelReader pr = imViewOrig.getImage().getPixelReader();
WritableImage wi = new WritableImage(
pr,
(int)imViewOrig.getImage().getWidth(),
(int)imViewOrig.getImage().getHeight());
imViewEdit.setImage(wi);
}
}
@FXML
void greyscale(ActionEvent event) {
PixelReader pr = imViewOrig.getImage().getPixelReader();
PixelWriter pw = ((WritableImage)imViewEdit.getImage()).getPixelWriter();
long beginTime = System.currentTimeMillis();
for (int x = 0; x < imViewEdit.getImage().getWidth(); x++) {
for (int y = 0; y < imViewEdit.getImage().getHeight(); y++) {
Color c = pr.getColor(x, y);
double avg = (c.getRed() + c.getGreen() + c.getBlue()) / 3.;
Color grey = new Color(avg, avg, avg, c.getOpacity());
pw.setColor(x, y, grey);
}
}
System.out.println(System.currentTimeMillis() - beginTime + "ms");
}
Swing code:
public void greyscale() {
long beginTime = System.currentTimeMillis();
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
Color c = new Color(image.getRGB(x, y));
int avg = (c.getRed() + c.getGreen() + c.getBlue()) / 3;
Color grey = new Color(avg, avg, avg);
image.setRGB(x, y, grey.getRGB());
}
}
repaint();
System.out.println(System.currentTimeMillis() - beginTime + "ms");
}
回答1:
In a quick test, it works of the order of 10 times faster if you just do the int computations, instead of manipulating Color objects. The JavaFX Color class is a substantially richer implementation than the AWT Color class; I guess this comes at a cost of additional overhead.
int width = (int)image.getWidth();
int height = (int)image.getHeight();
int[] pixels = new int[width * height];
WritablePixelFormat<IntBuffer> pixelFormat = PixelFormat.getIntArgbPreInstance();
pr.getPixels(0, 0, width, height, pixelFormat, pixels, 0, width);
int[] newPixels = new int[pixels.length];
for (int i = 0 ; i < pixels.length; i++) {
int c = pixels[i];
int a = c & 0xFF000000 ;
int r = (c >> 16) & 0xFF ;
int g = (c >> 8) & 0xFF ;
int b = c & 0xFF ;
int gray = (r+g+b) / 3 ;
newPixels[i] = a | (gray << 16) | (gray << 8) | gray ;
}
pw.setPixels(0, 0, width, height, pixelFormat, newPixels, 0, width);
回答2:
ColorAdjust based sample which converts a color image to monochrome by desaturating it when you mouse-over the image.
Given appropriate hardware (which is available to most systems JavaFX runs on), the JavaFX runtime may perform some of the implementation of the color adjustment in hardware, which might lead to a more efficient implementation than manipulating pixels using a PixelWriter.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.image.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Shadow extends Application {
@Override
public void start(Stage stage) throws Exception {
Image image = new Image(
"http://icons.iconarchive.com/icons/designbolts/smurfs-movie/512/smurfette-icon.png"
);
ImageView imageView = new ImageView(image);
ColorAdjust monochrome = new ColorAdjust();
monochrome.setSaturation(-1.0);
imageView.effectProperty().bind(
Bindings
.when(imageView.hoverProperty())
.then(monochrome)
.otherwise((ColorAdjust) null)
);
stage.setScene(new Scene(new Group(imageView), Color.AQUA));
stage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
来源:https://stackoverflow.com/questions/29260147/javafx-pixelwriter-low-performance