gpt4 book ai didi

java - 为什么 javafx 会破坏我的半透明光标?

转载 作者:行者123 更新时间:2023-12-02 07:12:07 30 4
gpt4 key购买 nike

下面是两张 PNG 图像:

enter image description here enter image description here

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

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

enter image description here enter image description here

第一个光标(没有部分透明的像素)仍然清晰,但第二个光标扭曲。

在与这个问题斗争了一段时间后,我发现了解释这种差异的算法 - 混合模式:

  • “预期”方式(例如,您可以在此浏览器中看到)是获取每个 channel 的值总和,并按 alpha 值加权:(1 - alpha) * background_color + alpha *前景颜色

  • “JavaFX Cursor”给出了不同的公式:(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 没有错误 - 错误似乎出在视频驱动程序实现中。下面的代码确实适用于某些硬件、驱动程序和操作系统的组合 - 但不适用于所有组合。

不幸的是,目前最好的解决方案似乎是避免使用具有部分透明的白色或灰色像素的光标。不过,部分透明的黑色像素就可以了。

<小时/>

我找到了解决该问题的方法(在 JDK 8 和 Linux&Windows 上测试)。它很丑陋,需要反射(reflection),但似乎有效。下面的代码(使用 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
)
}


关于java - 为什么 javafx 会破坏我的半透明光标?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46426283/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com