【问题标题】:JavaFX Image (PNG) transparency crispness being lost when rendering渲染时 JavaFX 图像 (PNG) 透明度清晰度丢失
【发布时间】:2013-02-16 02:22:11
【问题描述】:

我正在渐变背景上加载和显示透明 PNG,结果如下:

如果我在 Paint.NET 中打开同一个文件,添加如下所示的背景:

不知何故,JavaFX 使图像松散清晰,我担心这可能是我应用程序中所有图像的问题,而它只是在这种特殊情况下最明显。

这里是显示我如何加载这个特定图像的提取代码:

ImageView imgDownload = new ImageView(this.getClass().getResource("/img/docstore/document_downloaded_btn.png").toExternalForm());
imgDownload.setFitWidth(59);
imgDownload.setFitHeight(32);
GridPane.setHalignment(imgDownload, HPos.CENTER);
grid_item.add(imgDownload, 3, 0);

供参考,here is a link to the original image

我正在寻找一个答案,强调发生这种情况的可能原因

【问题讨论】:

  • 看起来它正在尝试进行亚像素放置,可能会有一点点拉伸。
  • 这是我可以关闭的东西吗?或以其他方式解决?
  • 我尝试将其设置为 true 或 false 但没有帮助:docs.oracle.com/javafx/2/api/javafx/scene/layout/…
  • 接受jewelsea的回答;由(可能的)工具包错误引起的亚像素放置?棘手! (考虑到布局引擎要求它做一件多么糟糕的事情,渲染器似乎做得非常好。)

标签: java image png transparency javafx


【解决方案1】:

更新

Java 8 中的 JavaFX 图像现在在所有情况下都能清晰呈现。

问题中描述的原始问题已得到解决。

Dreen 提交的有关此行为的错误 RT-28765 Incorrect subpixel placement for an Image 已作为 RT-19282 [StackPane] unwanted blur with ImageView 的副本关闭。 RT-19282 已按 Java 8 的固定方式关闭。

我在 Windows 7 上测试了Java 8 build 108。此答案中的示例应用程序现在可以正确显示(没有模糊图像有时会在 x 或 y 方向偏移半个像素)。


这是一个很好的问题和非常好奇的行为。

我整理了一个示例程序,它提供了可能的解释和解决方法。

运行该程序并稍微拖动边框以调整其大小后的输出如下。左边的模糊云是一个标准的ImageView 放在一个GridPane 中。右边清晰的云是一个 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 并在错误中链接回这个堆栈溢出问题并放置一个在评论中链接到创建的错误。

清晰版本保持清晰,因为它位于确保像素对齐的自定义区域实现中。

测试系统为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); }
}

【讨论】:

  • 听起来可能是因为有人忘记了他们在浮点上下文中使用dimension / 2,或者其他人将类型从整数切换到浮点(目的是修复其他问题)。那种微妙的问题将是一个真正的经典!
  • 感谢jewelsea的回答,我会尽快实施并测试它,希望今天。
猜你喜欢
  • 1970-01-01
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
  • 2013-09-13
  • 2017-12-27
  • 1970-01-01
相关资源
最近更新 更多