当前位置: 首页 > 知识库问答 >
问题:

为什么javafx会破坏我的半透明游标?

赵刚豪
2023-03-14

下面是两张PNG图片:

从视觉上看,它们是完全一样的--唯一的区别是一个在某些像素中有半透明的背景(你可以下载图像来检查)。

但是当我在JavaFX节点上使用这些图像作为图像光标时,我得到了以下结果:

在与问题搏斗了一会之后,我发现了解释这种差异--混合模式的算法:

>

  • “预期”的方法(例如,您可以在此浏览器中看到)是取每个通道的值之和,并用alpha值加权:(1-alpha)*background_color+alpha*foreground_color

    “JavaFX游标”给出了不同的公式:(1-alpha)*background_color+alpha^2*foreground_color(注意正方形)。

    我发现了扭曲的地方,但我想不出我做错了什么,如何才能纠正这个问题。

    下面是我的测试程序的完整的可运行源代码:

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    import javafx.scene.ImageCursor;
    import javafx.scene.image.Image;
    
    public class HelloWorld extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
            System.out.println(ImageCursor.getBestSize(32, 32));
    
            primaryStage.setTitle("Hello World!");
    
            StackPane root = new StackPane();
            root.setCursor(new ImageCursor(new Image("/test-cursor.png"), 0, 0));
    
            primaryStage.setScene(new Scene(root, 100, 100));
            primaryStage.show();
        }
    }
    
  • 共有1个答案

    谭仰岳
    2023-03-14

    更新:经过更深入的检查,似乎不是JavaFX出了问题--问题似乎出在视频驱动实现上。下面的代码可以在硬件、驱动程序和操作系统的某些组合上工作--但不能在所有的组合上工作。

    不幸的是,目前最好的解决方案是避免光标具有部分透明的白色或灰色像素。不过,部分透明的黑色像素也很好。

    我找到了一种解决这个问题的方法(在JDK8和Linux&Windows上进行了测试)。这很难看,需要反思,但似乎很管用。下面的代码(使用Scala语法,但可以很容易地适应Java):

      import com.sun.prism.PixelFormat
      import javafx.scene.ImageCursor
      import javafx.scene.image.{Image, WritableImage}
    
      private def undoPremultipliedAlpha(image: Image): Image = {
        // Fixes JavaFX bug with semi-transparent cursors -
        // somewhere deep in JavaFX code they premultiply alpha
        // on already premultiplied image, which screws up transparencies.
        // This method attempts to counteract it by removing premultiplied alpha
        // directly from bytes of internal JavaFX image.
    
        def getPlatformImage(image: Image) = image.impl_getPlatformImage()
    
        val platformImage = getPlatformImage(image)
    
        val pixelFormat = platformImage.getClass.getDeclaredMethod("getPixelFormat").invoke(platformImage).asInstanceOf[PixelFormat]
        if (pixelFormat != PixelFormat.BYTE_BGRA_PRE) {
          println(s"wrong platform image pixel format (${pixelFormat}), unable to apply cursor transparency bug workaround")
        } else {
          val pixelBufferField = platformImage.getClass.getDeclaredField("pixelBuffer")
          pixelBufferField.setAccessible(true)
          val pixelBuffer = pixelBufferField.get(platformImage).asInstanceOf[java.nio.Buffer]
          val pixelArray = pixelBuffer.array().asInstanceOf[Array[Byte]]
          for (i <- 0 until pixelArray.length / 4) {
    
            val alpha = (pixelArray(i * 4 + 3).toInt & 0xff) / 255.0
            if (alpha != 0) {
              pixelArray(i * 4) = math.min(255, math.max(0, ((pixelArray(i * 4).toInt & 0xff).toDouble / alpha))).toInt.toByte
              pixelArray(i * 4 + 1) = math.min(255, math.max(0, ((pixelArray(i * 4 + 1).toInt & 0xff).toDouble / alpha))).toInt.toByte
              pixelArray(i * 4 + 2) = math.min(255, math.max(0, ((pixelArray(i * 4 + 2).toInt & 0xff).toDouble / alpha))).toInt.toByte
            }
          }
        }
    
        image
      }
    
      def createImageCursor(resource: String, hotspotX: Int, hotspotY: Int): ImageCursor = {
        new ImageCursor(
          undoPremultipliedAlpha(
            new Image(resource)),
          hotspotX,
          hotspotY
        )
      }
    
    
    
     类似资料:
    • 问题内容: 实际上,我已经找到导致问题的原因。我的问题是为什么现在加入到你的断? 原始问题 对我来说,最简单的CSS任务似乎失败了:不保持元素相对于视点的位置。考虑以下样式表: 首次加载页面时,定位正确。但是,视口的任何更改(例如滚动或调整大小)都不会影响元素的位置。可以这么说,它无法使其位置适应新的视口。 足够奇怪的是,该站点显示了如何工作,实际上在我的浏览器中正常工作,没有任何问题! 因此,问

    • 这是我的pom.xml文件的节选: 这是Tomcat版本: 编译之后(用Maven),一切看起来都很好,没有构建错误,构建了war文件,一切都很好,就像以前的任何其他构建一样。只有现在,我尝试命中的任何endpoint都返回404,如果我移除'spring-boot-starter-jdbc'依赖项,则全部返回正常。 没有依赖项得日志: 具有依赖项得日志: 我看到日志中提到了“数据源”,这是我在a

    • 问题内容: 在我的本地路由http:// localhost:9000 /#/ deviceDetail /中, 我有一个控制器来管理该视图。在进入该视图之前,我将一些变量设置为( 例如)。 一旦进入该视图,我就可以访问仪表盘属性,但是例如当我用键刷新页面时,该属性仪表盘就会丢失。 我试图将localSave变量保存,但是该方法遇到了循环引用问题。 有什么技巧可以解决吗? 问题答案: Angula

    • 只有当我点击图标返回应用程序时,它才会被破坏。如果我打开正在运行的应用程序列表并从那里返回,它将正常恢复。我在onDestroy()函数上设置了一个断点,因为我找不到在它之前调用的任何代码。这是它调用的线程: 看起来它收到了一些消息,从某处破坏了我的活动。在循环器中有一个名为msg的变量。这就是它的价值: 有人知道这里发生了什么吗?如何使我的应用程序恢复正常,就像我从正在运行的应用程序列表中选择它

    • 我注意到一个问题,当从shell脚本循环运行多个adb命令时,大多数命令都不会执行。 这是一个示例脚本。 脚本名称:: 注释掉adb命令的输出 我连接了5台设备。在不使用theadab命令的情况下从bash文件运行时,这是输出。它遍历5个循环中的每一个。 包含ADB命令的循环输出 当取消注释adb shell命令时,它只会正确地迭代第一个循环的行。这是未注释adb命令时的输出: 有人能解释一下这种

    • 这是我的密码: 将按如下方式生成: 都很好。一切都被包装成标签。 现在我需要将这一行添加到上面的代码中: 下面是生成的代码: 但令人惊讶的是,它将被错误地生成: 看到没?元素跳出了标记。我怎么能把所有的东西都放在里面?