更新
JavaFX的圖像中的Java 8,現在在所有情況下清脆呈現。
問題中描述的原始問題已得到解決。
Dreen針對此行爲提出的錯誤RT-28765 Incorrect subpixel placement for an Image已作爲RT-19282 [StackPane] unwanted blur with ImageView的副本關閉。對於Java 8,RT-19282已關閉。
我在Windows 7上測試了Java 8 build 108。此答案中的示例應用程序現在正確顯示(沒有模糊圖像有時在x或y方向上偏移半個像素)。
這是一個很好的問題和非常好奇的行爲。
我整理了一個示例程序,它提供了一個可能的解釋和解決方法。
運行該程序並拖動邊框稍微調整大小後,此程序的輸出如下所示。左邊的模糊雲是放置在GridPane
中的標準ImageView
。右側的清晰雲是一個ImageView
包裹在我的解決方法修復程序類(CenteredRegion
)中,放置在相同的GridPane
中。
在顯示上述的圖像,該程序的輸出結果是:
Layout SnapToPixel: true
...
fuzzy: New Bounds: BoundingBox [minX:14.5, minY:12.5, minZ:0.0, width:59.0, height:32.0, depth:0.0, maxX:73.5, maxY:44.5, maxZ:0.0]
fuzzy: xDisplacement: 0.5, yDisplacement: 0.5
crisp: New Bounds: BoundingBox [minX:84.0, minY:13.0, minZ:0.0, width:59.0, height:32.0, depth:0.0, maxX:143.0, maxY:45.0, maxZ:0.0]
crisp: xDisplacement: 0.0, yDisplacement: 0.0
正如可以看到的,模糊圖像尚未像素對準,並且由半象素偏移在x和y方向,導致它看起來模糊。儘管網格區域具有snapToPixel
,但設置爲true
。
由於包含佈局的舞臺是動態調整大小的,所以模糊圖像將在像素對齊而不是像素對齊(取決於場景的寬度和高度的奇怪或均勻性)之間交替。由於模糊圖像在模糊和清晰之間不斷交替,所以在調整舞臺邊框的大小時會產生惱人的閃爍效果。
當父容器上的snapToPixel
設置爲true時,模糊圖像的行爲似乎是一個錯誤,所以我建議filing a bug against the JavaFX runtime project並鏈接回該堆棧溢出問題的bug並將鏈接放置到在評論中創建了錯誤。
清晰的版本保持清晰,因爲它位於自定義區域實現中,以確保像素對齊。
測試系統爲win7 + jdk8b77 + ATI HD4600顯卡。
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.*;
import javafx.geometry.*;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import static javafx.scene.layout.Region.USE_PREF_SIZE;
import javafx.stage.Stage;
public class TransparentPngSample extends Application {
public static final String IMAGE_LOC = "http://i.imgur.com/byY8whh.png";
@Override public void start(Stage stage) {
Pane layout = createSceneContent();
stage.setScene(new Scene(layout));
stage.show();
System.out.println("Layout SnapToPixel: " + layout.snapToPixelProperty().get());
}
private Pane createSceneContent() {
final Image cloudImage = new Image(IMAGE_LOC);
final ImageView fuzzyCloud = new ImageView(cloudImage);
final CenteredRegion crispCloud = new CenteredRegion(new ImageView(cloudImage));
GridPane layout = new GridPane();
layout.setHgap(10);
layout.setVgap(10);
layout.addRow(0, fuzzyCloud, crispCloud);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10px; -fx-background-color: slategrey;");
fuzzyCloud.boundsInParentProperty().addListener(new BoundsReporter("fuzzy"));
crispCloud.boundsInParentProperty().addListener(new BoundsReporter("crisp"));
return layout;
}
class CenteredRegion extends Region {
private Node content;
CenteredRegion(Node content) {
this.content = content;
getChildren().add(content);
}
@Override protected void layoutChildren() {
content.relocate(
Math.round(getWidth()/2 - content.prefWidth(USE_PREF_SIZE)/2),
Math.round(getHeight()/2 - content.prefHeight(USE_PREF_SIZE)/2)
);
System.out.println("crisp content relocated to: " +
getLayoutX() + "," + getLayoutY()
);
}
public Node getContent() {
return content;
}
}
class BoundsReporter implements ChangeListener<Bounds> {
final String logPrefix;
BoundsReporter(String logPrefix) {
this.logPrefix = logPrefix;
}
@Override public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds newBounds) {
System.out.println(logPrefix + ": " +
"New Bounds: " + newBounds
);
double xDisplacement = newBounds.getMinX() - Math.floor(newBounds.getMinX());
double yDisplacement = newBounds.getMinY() - Math.floor(newBounds.getMinY());
System.out.println(logPrefix + ": " +
"xDisplacement: " + xDisplacement + ", " +
"yDisplacement: " + yDisplacement
);
}
}
public static void main(String[] args) { launch(args); }
}
看起來它試圖做子像素放置,也許只是一點點拉伸。 – 2013-03-01 14:57:38
這是我可以關閉的東西嗎?或以其他方式解決? – Dreen 2013-03-01 15:16:53
我試着把它設置爲true或false,但它沒有幫助:http://docs.oracle.com/javafx/2/api/javafx/scene/layout/Region.html#snapToPixelProperty – Dreen 2013-03-01 15:23:09