我正在制作一张照片修饰的JavaFX应用程序。最终用户可以加载多个图像。当他点击REVERSE按钮时,使用Task
为每个图像启动Executor
。这些Task
中的每一个都执行反转算法:它填充ArrayBlockingQueue<Pixel>
(使用add
方法)。
当最终用户点击按钮REVERSE时,正如我所说,这些Task
已启动。但是在这些语句之后,我告诉JavaFX应用程序线程绘制Pixel
的{{1}}(使用ArrayBlockingQueue<Pixel>
方法)。
因此,JavaFX应用程序线程与remove
之间以及ArrayBlockingQueue<Pixel>
之间存在并行性和并发性(由Task
解决)。
要绘制Task
的{{1}},JavaFX应用程序线程将启动Pixel
。后者包含前面提到的ArrayBlockingQueue<Pixel>
方法。为每个图像启动此AnimationTimer
。
我想你自己想知道这个remove
如何知道它所删除的AnimationTimer
属于什么图像?实际上,每个AnimationTimer
都有一个属性Pixel
,用于指定图像的属性。
告诉我,如果我错了,但我的计划应该有效。确实:
Pixel
只是进行计算。writable_image
(特别是没有排水的可能性),因此没有并发性。Task
知道每个BlockingQueue
属于哪个图片。然而,它(显然!)并非如此(否则我不会创建这个问题哈哈!)。
我的问题是我的JavaFX应用程序冻结(第一个问题),在绘制了仅一些反转像素(不是所有像素)之后。此外,在最后加载的图片上(第三个问题)。
但我需要你的意见。
AnimationTimer
当然不能直接绘制每个图像的反转像素:这是动画的。最终用户可以一点一点地看到图像的每个像素被反转。它在其他算法中非常实用,因为用户可以“查看”算法的工作原理。
但要做到这一点,Pixel
需要读取一个名为AnimationTimer
的变量。此变量在... AnimationTimer
中被修改(写入)。但它是max
。所以如果我不错,Task
本身之间或JavaFX应用程序线程与这些AtomicLong
之间没有任何并发问题。
然而,问题可能是:Task
n°1(=图像编号1)中Task
的值可能为2000,max
中的值为59 } n°2(=图像n°2)。问题是Task
对于图像n°1必须使用2000,对于n°2必须使用59。但是如果Task
n°1 et n°2已经完成,AnimationTimer
已知的唯一值将是59 ...
我们启动了多个Task
并开始了几次次 AnimationTimer
。 CLASS:RightPane.java
Task
AnimationTimer
是CLASS GraphicEngine的一部分,其中包含WritableImage current_writable_image;
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getGraphicEngine().executor.execute(this.gui.getGraphicEngine().createTask(current_writable_image));
}
for(int i = 0; i < this.gui.getArrayListImageViewsImpacted().size(); i++) {
current_writable_image = (WritableImage) this.gui.getArrayListImageViewsImpacted().get(i).getImage();
this.gui.getImageAnimation().setWritableImage(current_writable_image);
this.gui.getImageAnimation().startAnimation();
}
:
Task
相同的CLASS,GraphicEngine,也包含反转算法:
Executor
最后,这是CLASS public final Executor executor = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
public Task createTask(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
Task ret = new Task() {
protected Void call() {
switch(operation_to_do) {
case "reverse" :
gui.getImageAnimation().setMax(image_width*image_height); // USE OF "MAX" VARIABLE
reverseImg(writable_image);
break;
}
return null;
}
};
return ret;
}
的代码。没有什么特别的。请注意,此处也使用变量private void reverseImg(WritableImage writable_image) {
int image_width = (int) writable_image.getWidth(), image_height = (int) writable_image.getHeight();
BlockingQueue<Pixel> updates = gui.getUpdates();
PixelReader pixel_reader = writable_image.getPixelReader();
double[] rgb_reversed;
for (int x = 0; x < image_width; x++) {
for (int y = 0; y < image_height; y++) {
rgb_reversed = PhotoRetouchingFormulas.reverse(pixel_reader.getColor(x, y).getRed(), pixel_reader.getColor(x, y).getGreen(), pixel_reader.getColor(x, y).getBlue());
updates.add(new Pixel(x, y, Color.color(rgb_reversed[0], rgb_reversed[1], rgb_reversed[2], pixel_reader.getColor(x, y).getOpacity()), writable_image));
}
}
}
(以及CLASS AnimationTimer
:max
)。
GraphicEngine