JavaFX:如何在调整大小后强制重新渲染 WriteableImage?
JavaFX: How to force the re-rendering of a WriteableImage after resize?
这个问题和下面的代码有历史,比较
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ImageOperationApp extends Application
{
private SimpleDoubleProperty gridSize = new SimpleDoubleProperty(3.0);
private SimpleDoubleProperty hueFactor = new SimpleDoubleProperty(12.0);
private SimpleDoubleProperty hueOffset = new SimpleDoubleProperty(240.0);
Rectangle2D scr=Screen.getPrimary().getVisualBounds();
int size1 = (int) (scr.getWidth() * 0.62);
int size2 = (int) (scr.getHeight() * 0.62);
SimpleIntegerProperty sip1=new SimpleIntegerProperty(888);
SimpleIntegerProperty sip2=new SimpleIntegerProperty(888);
private void renderImage(WritableImage img, double gridSize, double hueFactor, double hueOffset)
{
PixelWriter pw = img.getPixelWriter();
double w = sip1.doubleValue();
double h = sip2.doubleValue();
System.out.println("w "+w+" h "+h);
double xRatio = 0.0;
double yRatio = 0.0;
double hue = 0.0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
xRatio = x / w;
yRatio = y / h;
hue = Math.sin(yRatio * (gridSize * Math.PI)) * Math.sin(xRatio * (gridSize * Math.PI))
* Math.tan(hueFactor / 20.0) * 360.0 + hueOffset;
Color c = Color.hsb(hue, 1.0, 1.0);
pw.setColor(x, y, c);
}
}
public Parent createContent()
{
StackPane root = new StackPane();
WritableImage img = new WritableImage(sip1.get(), sip2.get());
gridSize.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueFactor.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueOffset.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip1.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip2.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
renderImage(img, 3.0, 12.0, 240.0);
ImageView view = new ImageView(img);
root.getChildren().add(view);
return root;
}
@Override
public void start(Stage primaryStage) throws Exception
{
Scene scene = new Scene(createContent());
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
@Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.X)
gridSize.set(gridSize.get() + 0.5);
if (event.getCode() == KeyCode.Y)
{
double t = gridSize.get();
t -= .5;
if (t < 0)
t = 0;
gridSize.set(t);
}
if (event.getCode() == KeyCode.V)
hueFactor.set(hueFactor.get() + 0.5);
if (event.getCode() == KeyCode.C)
{
double t = hueFactor.get();
t -= .5;
if (t < 0)
t = 0;
hueFactor.set(t);
}
if (event.getCode() == KeyCode.N)
hueOffset.set(hueOffset.get() + 10);
if (event.getCode() == KeyCode.B)
{
double t = hueOffset.get();
t -= 10;
if (t < 0)
t = 0;
hueOffset.set(t);
}
}
});
scene.widthProperty().addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
System.out.println("Width: " + newSceneWidth);
sip1.set(newSceneWidth.intValue());
}
});
scene.heightProperty().addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
System.out.println("Height: " + newSceneHeight);
sip2.set(newSceneHeight.intValue());
}
});
primaryStage.setTitle("JavaFX Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
为了使其独立且可运行,我发布了整个内容。
正如您可能说服自己的那样,renderImage()
-方法注意到了调整大小事件,但它没有显示出预期的行为。
显然,for
-循环不会重新着色所有像素,但是只是一部分。是否有简单的修复或 WriteableImage 原则上无法处理调整大小事件?
我对您的代码做了一些更改:
1) 新实例变量(包括WritableImage
):
private static final int IMAGE_MAX_WIDTH = 888;
private static final int IMAGE_MAX_HEIGHT = 888;
private final SimpleIntegerProperty sip1 = new SimpleIntegerProperty(IMAGE_MAX_WIDTH);
private final SimpleIntegerProperty sip2 = new SimpleIntegerProperty(IMAGE_MAX_HEIGHT);
private WritableImage img = new WritableImage(sip1.get(), sip2.get());
private ImageView view;
2) 在 renderImage
的第一行实例化一个新的 WritableImage
并将其设置为 view
private void renderImage(double gridSize, double hueFactor, double hueOffset) {
img = new WritableImage(sip1.intValue(), sip2.intValue());
view.setImage(img);
.....
}
3) 在 createContent
public Parent createContent() {
StackPane root = new StackPane();
img = new WritableImage(sip1.intValue(), sip2.intValue());
view = new ImageView(img);
....
}
4) 在 start
中修复异常:
scene.widthProperty().addListener((observableValue, oldSceneWidth, newSceneWidth) -> {
System.out.println("Width: " + newSceneWidth);
int newValue = newSceneWidth.intValue();
if (newValue < IMAGE_MAX_WIDTH) {
sip1.set(newValue);
}
});
scene.heightProperty().addListener((observableValue, oldSceneHeight, newSceneHeight) -> {
System.out.println("Height: " + newSceneHeight);
int newValue = newSceneHeight.intValue();
if (newValue < IMAGE_MAX_HEIGHT) {
sip2.set(newValue);
}
});
如果我正确理解了您的问题,这些更改应该可以解决它。
整个class:
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ImageOperationApp extends Application {
private SimpleDoubleProperty gridSize = new SimpleDoubleProperty(3.0);
private SimpleDoubleProperty hueFactor = new SimpleDoubleProperty(12.0);
private SimpleDoubleProperty hueOffset = new SimpleDoubleProperty(240.0);
Rectangle2D scr = Screen.getPrimary().getVisualBounds();
int size1 = (int) (scr.getWidth() * 0.62);
int size2 = (int) (scr.getHeight() * 0.62);
private static final int IMAGE_MAX_WIDTH = 888;
private static final int IMAGE_MAX_HEIGHT = 888;
private final SimpleIntegerProperty sip1 = new SimpleIntegerProperty(IMAGE_MAX_WIDTH);
private final SimpleIntegerProperty sip2 = new SimpleIntegerProperty(IMAGE_MAX_HEIGHT);
private WritableImage img = new WritableImage(sip1.get(), sip2.get());
private ImageView view;
private void renderImage(double gridSize, double hueFactor, double hueOffset) {
img = new WritableImage(sip1.get(), sip2.get());
view.setImage(img);
PixelWriter pw = img.getPixelWriter();
double w = sip1.doubleValue();
double h = sip2.doubleValue();
System.out.println("w " + w + " h " + h);
double xRatio = 0.0;
double yRatio = 0.0;
double hue = 0.0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
xRatio = x / w;
yRatio = y / h;
hue = Math.sin(yRatio * (gridSize * Math.PI)) * Math.sin(xRatio * (gridSize * Math.PI))
* Math.tan(hueFactor / 20.0) * 360.0 + hueOffset;
Color c = Color.hsb(hue, 1.0, 1.0);
pw.setColor(x, y, c);
}
}
public Parent createContent() {
StackPane root = new StackPane();
img = new WritableImage(sip1.intValue(), sip2.intValue());
view = new ImageView(img);
gridSize.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueFactor.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueOffset.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip1.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip2.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
renderImage(3.0, 12.0, 240.0);
root.getChildren().add(view);
return root;
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.X)
gridSize.set(gridSize.get() + 0.5);
if (event.getCode() == KeyCode.Y) {
double t = gridSize.get();
t -= .5;
if (t < 0)
t = 0;
gridSize.set(t);
}
if (event.getCode() == KeyCode.V)
hueFactor.set(hueFactor.get() + 0.5);
if (event.getCode() == KeyCode.C) {
double t = hueFactor.get();
t -= .5;
if (t < 0)
t = 0;
hueFactor.set(t);
}
if (event.getCode() == KeyCode.N)
hueOffset.set(hueOffset.get() + 10);
if (event.getCode() == KeyCode.B) {
double t = hueOffset.get();
t -= 10;
if (t < 0)
t = 0;
hueOffset.set(t);
}
}
});
scene.widthProperty().addListener((observableValue, oldSceneWidth, newSceneWidth) -> {
System.out.println("Width: " + newSceneWidth);
int newValue = newSceneWidth.intValue();
if (newValue < IMAGE_MAX_WIDTH) {
sip1.set(newValue);
}
});
scene.heightProperty().addListener((observableValue, oldSceneHeight, newSceneHeight) -> {
System.out.println("Height: " + newSceneHeight);
int newValue = newSceneHeight.intValue();
if (newValue < IMAGE_MAX_HEIGHT) {
sip2.set(newValue);
}
});
primaryStage.setTitle("JavaFX Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
结果:
这个问题和下面的代码有历史,比较
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ImageOperationApp extends Application
{
private SimpleDoubleProperty gridSize = new SimpleDoubleProperty(3.0);
private SimpleDoubleProperty hueFactor = new SimpleDoubleProperty(12.0);
private SimpleDoubleProperty hueOffset = new SimpleDoubleProperty(240.0);
Rectangle2D scr=Screen.getPrimary().getVisualBounds();
int size1 = (int) (scr.getWidth() * 0.62);
int size2 = (int) (scr.getHeight() * 0.62);
SimpleIntegerProperty sip1=new SimpleIntegerProperty(888);
SimpleIntegerProperty sip2=new SimpleIntegerProperty(888);
private void renderImage(WritableImage img, double gridSize, double hueFactor, double hueOffset)
{
PixelWriter pw = img.getPixelWriter();
double w = sip1.doubleValue();
double h = sip2.doubleValue();
System.out.println("w "+w+" h "+h);
double xRatio = 0.0;
double yRatio = 0.0;
double hue = 0.0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
{
xRatio = x / w;
yRatio = y / h;
hue = Math.sin(yRatio * (gridSize * Math.PI)) * Math.sin(xRatio * (gridSize * Math.PI))
* Math.tan(hueFactor / 20.0) * 360.0 + hueOffset;
Color c = Color.hsb(hue, 1.0, 1.0);
pw.setColor(x, y, c);
}
}
public Parent createContent()
{
StackPane root = new StackPane();
WritableImage img = new WritableImage(sip1.get(), sip2.get());
gridSize.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueFactor.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueOffset.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip1.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip2.addListener((Observable observable) -> {
renderImage(img, gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
renderImage(img, 3.0, 12.0, 240.0);
ImageView view = new ImageView(img);
root.getChildren().add(view);
return root;
}
@Override
public void start(Stage primaryStage) throws Exception
{
Scene scene = new Scene(createContent());
scene.setOnKeyPressed(new EventHandler<KeyEvent>()
{
@Override
public void handle(KeyEvent event)
{
if (event.getCode() == KeyCode.X)
gridSize.set(gridSize.get() + 0.5);
if (event.getCode() == KeyCode.Y)
{
double t = gridSize.get();
t -= .5;
if (t < 0)
t = 0;
gridSize.set(t);
}
if (event.getCode() == KeyCode.V)
hueFactor.set(hueFactor.get() + 0.5);
if (event.getCode() == KeyCode.C)
{
double t = hueFactor.get();
t -= .5;
if (t < 0)
t = 0;
hueFactor.set(t);
}
if (event.getCode() == KeyCode.N)
hueOffset.set(hueOffset.get() + 10);
if (event.getCode() == KeyCode.B)
{
double t = hueOffset.get();
t -= 10;
if (t < 0)
t = 0;
hueOffset.set(t);
}
}
});
scene.widthProperty().addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
System.out.println("Width: " + newSceneWidth);
sip1.set(newSceneWidth.intValue());
}
});
scene.heightProperty().addListener(new ChangeListener<Number>() {
@Override public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
System.out.println("Height: " + newSceneHeight);
sip2.set(newSceneHeight.intValue());
}
});
primaryStage.setTitle("JavaFX Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
为了使其独立且可运行,我发布了整个内容。
正如您可能说服自己的那样,renderImage()
-方法注意到了调整大小事件,但它没有显示出预期的行为。
显然,for
-循环不会重新着色所有像素,但是只是一部分。是否有简单的修复或 WriteableImage 原则上无法处理调整大小事件?
我对您的代码做了一些更改:
1) 新实例变量(包括WritableImage
):
private static final int IMAGE_MAX_WIDTH = 888;
private static final int IMAGE_MAX_HEIGHT = 888;
private final SimpleIntegerProperty sip1 = new SimpleIntegerProperty(IMAGE_MAX_WIDTH);
private final SimpleIntegerProperty sip2 = new SimpleIntegerProperty(IMAGE_MAX_HEIGHT);
private WritableImage img = new WritableImage(sip1.get(), sip2.get());
private ImageView view;
2) 在 renderImage
的第一行实例化一个新的 WritableImage
并将其设置为 view
private void renderImage(double gridSize, double hueFactor, double hueOffset) {
img = new WritableImage(sip1.intValue(), sip2.intValue());
view.setImage(img);
.....
}
3) 在 createContent
public Parent createContent() {
StackPane root = new StackPane();
img = new WritableImage(sip1.intValue(), sip2.intValue());
view = new ImageView(img);
....
}
4) 在 start
中修复异常:
scene.widthProperty().addListener((observableValue, oldSceneWidth, newSceneWidth) -> {
System.out.println("Width: " + newSceneWidth);
int newValue = newSceneWidth.intValue();
if (newValue < IMAGE_MAX_WIDTH) {
sip1.set(newValue);
}
});
scene.heightProperty().addListener((observableValue, oldSceneHeight, newSceneHeight) -> {
System.out.println("Height: " + newSceneHeight);
int newValue = newSceneHeight.intValue();
if (newValue < IMAGE_MAX_HEIGHT) {
sip2.set(newValue);
}
});
如果我正确理解了您的问题,这些更改应该可以解决它。
整个class:
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.EventHandler;
import javafx.geometry.Rectangle2D;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class ImageOperationApp extends Application {
private SimpleDoubleProperty gridSize = new SimpleDoubleProperty(3.0);
private SimpleDoubleProperty hueFactor = new SimpleDoubleProperty(12.0);
private SimpleDoubleProperty hueOffset = new SimpleDoubleProperty(240.0);
Rectangle2D scr = Screen.getPrimary().getVisualBounds();
int size1 = (int) (scr.getWidth() * 0.62);
int size2 = (int) (scr.getHeight() * 0.62);
private static final int IMAGE_MAX_WIDTH = 888;
private static final int IMAGE_MAX_HEIGHT = 888;
private final SimpleIntegerProperty sip1 = new SimpleIntegerProperty(IMAGE_MAX_WIDTH);
private final SimpleIntegerProperty sip2 = new SimpleIntegerProperty(IMAGE_MAX_HEIGHT);
private WritableImage img = new WritableImage(sip1.get(), sip2.get());
private ImageView view;
private void renderImage(double gridSize, double hueFactor, double hueOffset) {
img = new WritableImage(sip1.get(), sip2.get());
view.setImage(img);
PixelWriter pw = img.getPixelWriter();
double w = sip1.doubleValue();
double h = sip2.doubleValue();
System.out.println("w " + w + " h " + h);
double xRatio = 0.0;
double yRatio = 0.0;
double hue = 0.0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
xRatio = x / w;
yRatio = y / h;
hue = Math.sin(yRatio * (gridSize * Math.PI)) * Math.sin(xRatio * (gridSize * Math.PI))
* Math.tan(hueFactor / 20.0) * 360.0 + hueOffset;
Color c = Color.hsb(hue, 1.0, 1.0);
pw.setColor(x, y, c);
}
}
public Parent createContent() {
StackPane root = new StackPane();
img = new WritableImage(sip1.intValue(), sip2.intValue());
view = new ImageView(img);
gridSize.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueFactor.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
hueOffset.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip1.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
sip2.addListener((Observable observable) -> {
renderImage(gridSize.doubleValue(), hueFactor.doubleValue(), hueOffset.doubleValue());
});
renderImage(3.0, 12.0, 240.0);
root.getChildren().add(view);
return root;
}
@Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.X)
gridSize.set(gridSize.get() + 0.5);
if (event.getCode() == KeyCode.Y) {
double t = gridSize.get();
t -= .5;
if (t < 0)
t = 0;
gridSize.set(t);
}
if (event.getCode() == KeyCode.V)
hueFactor.set(hueFactor.get() + 0.5);
if (event.getCode() == KeyCode.C) {
double t = hueFactor.get();
t -= .5;
if (t < 0)
t = 0;
hueFactor.set(t);
}
if (event.getCode() == KeyCode.N)
hueOffset.set(hueOffset.get() + 10);
if (event.getCode() == KeyCode.B) {
double t = hueOffset.get();
t -= 10;
if (t < 0)
t = 0;
hueOffset.set(t);
}
}
});
scene.widthProperty().addListener((observableValue, oldSceneWidth, newSceneWidth) -> {
System.out.println("Width: " + newSceneWidth);
int newValue = newSceneWidth.intValue();
if (newValue < IMAGE_MAX_WIDTH) {
sip1.set(newValue);
}
});
scene.heightProperty().addListener((observableValue, oldSceneHeight, newSceneHeight) -> {
System.out.println("Height: " + newSceneHeight);
int newValue = newSceneHeight.intValue();
if (newValue < IMAGE_MAX_HEIGHT) {
sip2.set(newValue);
}
});
primaryStage.setTitle("JavaFX Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
结果: