下面是两张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();
}
}
更新:经过更深入的检查,似乎不是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命令时的输出: 有人能解释一下这种
这是我的密码: 将按如下方式生成: 都很好。一切都被包装成标签。 现在我需要将这一行添加到上面的代码中: 下面是生成的代码: 但令人惊讶的是,它将被错误地生成: 看到没?元素跳出了标记。我怎么能把所有的东西都放在里面?