Optisch sind sie genau identisch – der einzige Unterschied besteht darin, dass man in einigen Pixeln den halbtransparenten Hintergrund hat (Sie können die Bilder herunterladen, um es zu überprüfen).
Aber wenn ich diese Bilder als Bildcursor auf JavaFX-Knoten verwende, erhalte ich das folgende Ergebnis:
Der erste Cursor (ohne teilweise transparente Pixel) ist immer noch scharf, aber der zweite wird verzerrt.
Nachdem ich eine Weile mit dem Problem gekämpft hatte, entdeckte ich den Algorithmus, der diesen Unterschied ausmacht – Mischmodus:
Der “erwartete” Weg (den Sie beispielsweise in diesem Browser sehen können) besteht darin, die Summe der Werte pro Kanal zu nehmen, gewichtet mit Alpha-Werten: (1 - alpha) * background_color + alpha * foreground_color.
“JavaFX Cursor” gibt die andere Formel an: (1 - alpha) * background_color + alpha^2 * foreground_color (Beachten Sie das Quadrat).
Ich habe die Verzerrung entdeckt, aber ich kann nicht herausfinden, was ich falsch gemacht habe und wie ich dieses Problem beheben kann.
Hier ist der vollständige lauffähige Quellcode für mein Testprogramm:
Wie kann ich ein korrektes Rendering solcher halbtransparenter Cursor erreichen?
hast du dir stackoverflow.com/questions/39692625/… angesehen
– Tschallacka
24. Mai 2018 um 8:17 Uhr
@Tschallacka – Ja, das habe ich. Der JFXCustom-Cursor ist leistungsmäßig nicht gut (er stottert mit der Anwendung, während der native Cursor reibungslos funktioniert), und das Beispiel in der von Ihnen verlinkten Frage leidet unter demselben Problem – weiße Farbe wird als schwarz gerendert, wenn sie halbtransparent ist. Hier ist ein leicht umgeschriebener Code aus dieser Frage, der das Problem hervorhebt: pastebin.com/G5D0wK80
– Rogach
24. Mai 2018 um 10:12 Uhr
Rastergrafiken sollten nicht in Grafikelementen wie Cursors verwendet werden. Das verwendete Bild wird vergrößert und somit verzerrt. Versuchen Sie, mehrere Bilder mit unterschiedlichen Abmessungen hinzuzufügen und zu verwenden ImageCursor.chooseBestCursor() Funktion. Für mich hat ein Bild der Größe 240 x 240 px hervorragend funktioniert.
– Benutzer7909000
24. Mai 2018 um 19:24 Uhr
@ Rogach Könntest du einen Fehler melden dafür und lassen Sie das JavaFX-Team einen Blick darauf werfen?
AKTUALISIEREN: Bei genauerer Betrachtung scheint JavaFX nicht schuld zu sein – der Fehler scheint in den Videotreiberimplementierungen zu liegen. Der folgende Code funktioniert auf einigen Kombinationen von Hardware, Treibern und Betriebssystemen – aber nicht auf allen.
Leider scheint es vorerst die beste Lösung zu sein, Cursor mit teilweise transparenten weißen oder grauen Pixeln zu vermeiden. Teilweise transparente schwarze Pixel sind jedoch in Ordnung.
Ich habe einen Weg gefunden, das Problem zu umgehen (getestet auf JDK 8 und Linux&Windows). Es ist hässlich und erfordert Reflexion, scheint aber zu funktionieren. Code unten (in Scala-Syntax, kann aber leicht an Java angepasst werden):
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
)
}
hast du dir stackoverflow.com/questions/39692625/… angesehen
– Tschallacka
24. Mai 2018 um 8:17 Uhr
@Tschallacka – Ja, das habe ich. Der JFXCustom-Cursor ist leistungsmäßig nicht gut (er stottert mit der Anwendung, während der native Cursor reibungslos funktioniert), und das Beispiel in der von Ihnen verlinkten Frage leidet unter demselben Problem – weiße Farbe wird als schwarz gerendert, wenn sie halbtransparent ist. Hier ist ein leicht umgeschriebener Code aus dieser Frage, der das Problem hervorhebt: pastebin.com/G5D0wK80
– Rogach
24. Mai 2018 um 10:12 Uhr
Rastergrafiken sollten nicht in Grafikelementen wie Cursors verwendet werden. Das verwendete Bild wird vergrößert und somit verzerrt. Versuchen Sie, mehrere Bilder mit unterschiedlichen Abmessungen hinzuzufügen und zu verwenden
ImageCursor.chooseBestCursor()
Funktion. Für mich hat ein Bild der Größe 240 x 240 px hervorragend funktioniert.– Benutzer7909000
24. Mai 2018 um 19:24 Uhr
@ Rogach Könntest du einen Fehler melden dafür und lassen Sie das JavaFX-Team einen Blick darauf werfen?
– ItachiUchiha
25. Mai 2018 um 11:05 Uhr
github.com/javafxports/openjdk-jfx/issues/101
– Ryan Leach
13. Juni 2018 um 2:09 Uhr