Unverified Commit 82445626 authored by Manuel Unterhofer's avatar Manuel Unterhofer Committed by GitHub

Fix passing of PixelGeometry to JNI on Windows 11 (#656)

... and make software renderers respect the pixel geometry

We accidentally didn't prepare the SurfaceProps into an IntArray when passing them down into the JNI in the Windows version. The memory layout must have accidentally matched between both representations on Windows 10, but it doesn't work on Windows 11 for some reason. This is the proper fix.
parent 5766e0b3
package SkiaAwtSample package SkiaAwtSample
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.jetbrains.skia.PixelGeometry
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import java.awt.Color import java.awt.Color
import java.awt.Dimension import java.awt.Dimension
import java.awt.Toolkit import java.awt.Toolkit
import java.awt.event.* import java.awt.event.*
import java.awt.RenderingHints
import javax.swing.* import javax.swing.*
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
...@@ -22,7 +24,15 @@ fun main(args: Array<String>) { ...@@ -22,7 +24,15 @@ fun main(args: Array<String>) {
} }
fun createWindow(title: String, exitOnClose: Boolean) = SwingUtilities.invokeLater { fun createWindow(title: String, exitOnClose: Boolean) = SwingUtilities.invokeLater {
val skiaLayer = SkiaLayer() val renderingHints = Toolkit.getDefaultToolkit().getDesktopProperty("awt.font.desktophints") as Map<Any, Any>
val pixelGeometry = when (renderingHints[RenderingHints.KEY_TEXT_ANTIALIASING]) {
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB -> PixelGeometry.RGB_H
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HBGR -> PixelGeometry.BGR_H
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VRGB -> PixelGeometry.RGB_V
RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_VBGR -> PixelGeometry.BGR_V
else -> PixelGeometry.UNKNOWN
}
val skiaLayer = SkiaLayer(pixelGeometry = pixelGeometry)
val clocks = ClocksAwt(skiaLayer) val clocks = ClocksAwt(skiaLayer)
val window = JFrame(title) val window = JFrame(title)
......
...@@ -2,17 +2,17 @@ package SkiaAwtSample ...@@ -2,17 +2,17 @@ package SkiaAwtSample
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skia.paragraph.FontCollection import org.jetbrains.skia.paragraph.*
import org.jetbrains.skia.paragraph.ParagraphBuilder
import org.jetbrains.skia.paragraph.ParagraphStyle
import org.jetbrains.skia.paragraph.TextStyle
import kotlin.math.cos import kotlin.math.cos
import kotlin.math.sin import kotlin.math.sin
import kotlin.math.PI import kotlin.math.PI
class ClocksAwt(private val layer: SkiaLayer): SkikoView { class ClocksAwt(private val layer: SkiaLayer) : SkikoView {
private val typeface = Typeface.makeFromFile("fonts/JetBrainsMono-Regular.ttf") private val typeface = Typeface.makeFromFile("fonts/JetBrainsMono-Regular.ttf")
private val font = Font(typeface, 40f) private val font = Font(typeface, 13f).apply {
edging = FontEdging.SUBPIXEL_ANTI_ALIAS
hinting = FontHinting.SLIGHT
}
private val paint = Paint().apply { private val paint = Paint().apply {
color = 0xff9BC730L.toInt() color = 0xff9BC730L.toInt()
mode = PaintMode.FILL mode = PaintMode.FILL
...@@ -75,16 +75,20 @@ class ClocksAwt(private val layer: SkiaLayer): SkikoView { ...@@ -75,16 +75,20 @@ class ClocksAwt(private val layer: SkiaLayer): SkikoView {
} }
val text = "Frames: ${frame++}!" val text = "Frames: ${frame++}!"
canvas.drawString(text, xpos.toFloat(), ypos.toFloat(), font, paint) val x = xpos.toFloat()
val y = ypos.toFloat()
canvas.drawString(text, x, y, font, paint)
val style = ParagraphStyle() val style = ParagraphStyle().apply {
val renderInfo = ParagraphBuilder(style, fontCollection) fontRastrSettings = FontRastrSettings(FontEdging.SUBPIXEL_ANTI_ALIAS, FontHinting.SLIGHT, true)
}
val paragraph = ParagraphBuilder(style, fontCollection)
.pushStyle(TextStyle().setColor(0xFF000000.toInt())) .pushStyle(TextStyle().setColor(0xFF000000.toInt()))
.addText("Graphics API: ${layer.renderApi} ✿゚ $currentSystemTheme") .addText("Graphics API: ${layer.renderApi} ✿゚ $currentSystemTheme")
.popStyle() .popStyle()
.build() .build()
renderInfo.layout(Float.POSITIVE_INFINITY) paragraph.layout(Float.POSITIVE_INFINITY)
renderInfo.paint(canvas, 5f, 5f) paragraph.paint(canvas, 5f, 5f)
// Alpha layers test // Alpha layers test
val rectW = 100f val rectW = 100f
......
...@@ -7,12 +7,14 @@ ...@@ -7,12 +7,14 @@
#include "SkColorSpace.h" #include "SkColorSpace.h"
#include "SkSurface.h" #include "SkSurface.h"
#include "src/core/SkAutoMalloc.h" #include "src/core/SkAutoMalloc.h"
#include "../common/interop.hh"
class SoftwareDevice class SoftwareDevice
{ {
public: public:
HWND window; HWND window;
RECT clientRect; RECT clientRect;
std::unique_ptr<SkSurfaceProps> surfaceProps;
SkAutoMalloc surfaceMemory; SkAutoMalloc surfaceMemory;
sk_sp<SkSurface> surface; sk_sp<SkSurface> surface;
...@@ -22,10 +24,11 @@ public: ...@@ -22,10 +24,11 @@ public:
extern "C" extern "C"
{ {
JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsSoftwareRedrawer_createDevice( JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_redrawer_WindowsSoftwareRedrawer_createDevice(
JNIEnv *env, jobject redrawer, jlong contentHandle, jboolean transparency) JNIEnv *env, jobject redrawer, jlong contentHandle, jintArray surfacePropsInts, jboolean transparency)
{ {
SoftwareDevice *device = new SoftwareDevice(); SoftwareDevice *device = new SoftwareDevice();
device->window = (HWND)contentHandle; device->window = (HWND)contentHandle;
device->surfaceProps = skija::SurfaceProps::toSkSurfaceProps(env, surfacePropsInts);
if (transparency) if (transparency)
{ {
HWND parent = GetAncestor(device->window, GA_PARENT); HWND parent = GetAncestor(device->window, GA_PARENT);
...@@ -56,7 +59,7 @@ extern "C" ...@@ -56,7 +59,7 @@ extern "C"
SkImageInfo info = SkImageInfo::Make( SkImageInfo info = SkImageInfo::Make(
width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType, width, height, kBGRA_8888_SkColorType, kPremul_SkAlphaType,
SkColorSpace::MakeSRGB()); SkColorSpace::MakeSRGB());
device->surface = SkSurface::MakeRasterDirect(info, pixels, sizeof(uint32_t) * width); device->surface = SkSurface::MakeRasterDirect(info, pixels, sizeof(uint32_t) * width, device->surfaceProps.get());
GetClientRect(device->window, &device->clientRect); GetClientRect(device->window, &device->clientRect);
} }
__except(EXCEPTION_EXECUTE_HANDLER) { __except(EXCEPTION_EXECUTE_HANDLER) {
......
...@@ -4,6 +4,7 @@ import org.jetbrains.skia.Bitmap ...@@ -4,6 +4,7 @@ import org.jetbrains.skia.Bitmap
import org.jetbrains.skia.Canvas import org.jetbrains.skia.Canvas
import org.jetbrains.skia.ColorAlphaType import org.jetbrains.skia.ColorAlphaType
import org.jetbrains.skia.ImageInfo import org.jetbrains.skia.ImageInfo
import org.jetbrains.skia.SurfaceProps
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.hostOs import org.jetbrains.skiko.hostOs
import org.jetbrains.skiko.OS import org.jetbrains.skiko.OS
...@@ -55,7 +56,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : JvmContextHandler(laye ...@@ -55,7 +56,7 @@ internal class SoftwareContextHandler(layer: SkiaLayer) : JvmContextHandler(laye
storage.allocPixelsFlags(ImageInfo.makeS32(w, h, ColorAlphaType.PREMUL), false) storage.allocPixelsFlags(ImageInfo.makeS32(w, h, ColorAlphaType.PREMUL), false)
} }
canvas = Canvas(storage) canvas = Canvas(storage, SurfaceProps(pixelGeometry = layer.pixelGeometry))
} }
override fun flush() { override fun flush() {
......
...@@ -5,6 +5,8 @@ import kotlinx.coroutines.withContext ...@@ -5,6 +5,8 @@ import kotlinx.coroutines.withContext
import org.jetbrains.skia.DirectContext import org.jetbrains.skia.DirectContext
import org.jetbrains.skia.Surface import org.jetbrains.skia.Surface
import org.jetbrains.skia.SurfaceProps import org.jetbrains.skia.SurfaceProps
import org.jetbrains.skia.impl.interopScope
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skiko.* import org.jetbrains.skiko.*
import org.jetbrains.skiko.context.Direct3DContextHandler import org.jetbrains.skiko.context.Direct3DContextHandler
...@@ -86,9 +88,11 @@ internal class Direct3DRedrawer( ...@@ -86,9 +88,11 @@ internal class Direct3DRedrawer(
makeDirectXContext(device) makeDirectXContext(device)
) )
fun makeSurface(context: Long, width: Int, height: Int, surfaceProps: SurfaceProps, index: Int) = Surface( fun makeSurface(context: Long, width: Int, height: Int, surfaceProps: SurfaceProps, index: Int): Surface {
makeDirectXSurface(device, context, width, height, surfaceProps, index) return interopScope {
) Surface(makeDirectXSurface(device, context, width, height, toInterop(surfaceProps.packToIntArray()), index))
}
}
fun resizeBuffers(width: Int, height: Int) = resizeBuffers(device, width, height) fun resizeBuffers(width: Int, height: Int) = resizeBuffers(device, width, height)
...@@ -102,7 +106,7 @@ internal class Direct3DRedrawer( ...@@ -102,7 +106,7 @@ internal class Direct3DRedrawer(
private external fun chooseAdapter(adapterPriority: Int): Long private external fun chooseAdapter(adapterPriority: Int): Long
private external fun createDirectXDevice(adapter: Long, contentHandle: Long, transparency: Boolean): Long private external fun createDirectXDevice(adapter: Long, contentHandle: Long, transparency: Boolean): Long
private external fun makeDirectXContext(device: Long): Long private external fun makeDirectXContext(device: Long): Long
private external fun makeDirectXSurface(device: Long, context: Long, width: Int, height: Int, surfaceProps: SurfaceProps, index: Int): Long private external fun makeDirectXSurface(device: Long, context: Long, width: Int, height: Int, surfacePropsIntArray: InteropPointer, index: Int): Long
private external fun resizeBuffers(device: Long, width: Int, height: Int) private external fun resizeBuffers(device: Long, width: Int, height: Int)
private external fun swap(device: Long, isVsyncEnabled: Boolean) private external fun swap(device: Long, isVsyncEnabled: Boolean)
private external fun disposeDevice(device: Long) private external fun disposeDevice(device: Long)
......
package org.jetbrains.skiko.redrawer package org.jetbrains.skiko.redrawer
import org.jetbrains.skia.impl.interopScope
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.SurfaceProps
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkiaLayerProperties import org.jetbrains.skiko.SkiaLayerProperties
import org.jetbrains.skiko.RenderException import org.jetbrains.skiko.RenderException
...@@ -13,13 +16,15 @@ internal class WindowsSoftwareRedrawer( ...@@ -13,13 +16,15 @@ internal class WindowsSoftwareRedrawer(
init { init {
onDeviceChosen("Software") onDeviceChosen("Software")
device = createDevice(layer.contentHandle, layer.transparency).also { device = interopScope {
createDevice(layer.contentHandle, toInterop(SurfaceProps(pixelGeometry = layer.pixelGeometry).packToIntArray()), layer.transparency).also {
if (it == 0L) { if (it == 0L) {
throw RenderException("Failed to create Software device") throw RenderException("Failed to create Software device")
} }
} }
}
onContextInit() onContextInit()
} }
private external fun createDevice(contentHandle: Long, transparency: Boolean): Long private external fun createDevice(contentHandle: Long, surfacePropsIntArray: InteropPointer, transparency: Boolean): Long
} }
\ No newline at end of file
package org.jetbrains.skia package org.jetbrains.skia
// The order and values must be aligned with SkPixelGeometry from the C++ side
enum class PixelGeometry { enum class PixelGeometry {
UNKNOWN, UNKNOWN,
RGB_H, RGB_H,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment