Unverified Commit 5c7bfe39 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

Support hi-dpi for Web targets (#426)

Co-authored-by: 's avatarRoman Sedaikin <roman.sedaikin@jetbrains.com>
parent ed693b57
...@@ -3,12 +3,14 @@ package org.jetbrains.skiko.sample ...@@ -3,12 +3,14 @@ package org.jetbrains.skiko.sample
import kotlinx.browser.document import kotlinx.browser.document
import org.jetbrains.skiko.GenericSkikoView import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.onContentScaleChanged
import org.jetbrains.skiko.wasm.onWasmReady import org.jetbrains.skiko.wasm.onWasmReady
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
fun main() { fun main() {
onWasmReady { onWasmReady {
val skiaLayer = SkiaLayer() val skiaLayer = SkiaLayer()
onContentScaleChanged = { scale -> println(scale) }
val game = Clocks(skiaLayer) val game = Clocks(skiaLayer)
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game) skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<body> <body>
<h1>Skiko For Web</h1> <h1>Skiko For Web</h1>
<div> <div>
<canvas id="SkikoTarget" width="800" height="600"></canvas> <canvas id="SkikoTarget" width="800px" height="600px"></canvas>
</div> </div>
</body> </body>
</html> </html>
...@@ -12,25 +12,44 @@ import org.jetbrains.skiko.wasm.CreateWebGLContext ...@@ -12,25 +12,44 @@ import org.jetbrains.skiko.wasm.CreateWebGLContext
import org.jetbrains.skiko.wasm.GL import org.jetbrains.skiko.wasm.GL
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val width: Int, val height: Int) { abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
constructor(canvas: HTMLCanvasElement): this(canvas, canvas.width, canvas.height)
private val contextPointer = CreateWebGLContext(htmlCanvas) private val contextPointer = CreateWebGLContext(htmlCanvas)
private val context: DirectContext
private var surface: Surface? = null
private var renderTarget: BackendRenderTarget? = null
var canvas: Canvas? = null
val width: Int
get() = htmlCanvas.width
val height: Int
get() = htmlCanvas.height
init { init {
GL.makeContextCurrent(contextPointer) GL.makeContextCurrent(contextPointer)
context = DirectContext.makeGL()
}
fun initCanvas(desiredWidth: Int, desiredHeight: Int, scale: Float) {
disposeCanvas()
htmlCanvas.width = (desiredWidth * scale).toInt()
htmlCanvas.height = (desiredHeight * scale).toInt()
renderTarget = BackendRenderTarget.makeGL(width, height, 1, 8, 0, 0x8058)
surface = Surface.makeFromBackendRenderTarget(
context,
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.sRGB
)
canvas = surface!!.canvas
}
fun disposeCanvas() {
surface?.close()
surface = null
renderTarget?.close()
renderTarget = null
} }
private val context = DirectContext.makeGL()
private val surface = Surface.makeFromBackendRenderTarget(
context,
BackendRenderTarget.makeGL(
width, height, 1, 8, 0, 0x8058
),
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.sRGB
)
val canvas: Canvas
get() = surface.canvas
abstract fun drawFrame(currentTimestamp: Double) abstract fun drawFrame(currentTimestamp: Double)
...@@ -44,9 +63,9 @@ abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val wid ...@@ -44,9 +63,9 @@ abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val wid
window.requestAnimationFrame { timestamp -> window.requestAnimationFrame { timestamp ->
redrawScheduled = false redrawScheduled = false
GL.makeContextCurrent(contextPointer) GL.makeContextCurrent(contextPointer)
canvas.clear(-1) canvas?.clear(-1)
drawFrame(timestamp) drawFrame(timestamp)
surface.flushAndSubmit() surface?.flushAndSubmit()
context.flush() context.flush()
} }
} }
......
package org.jetbrains.skiko package org.jetbrains.skiko
import kotlinx.browser.window
import org.w3c.dom.HTMLCanvasElement import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.events.InputEvent import org.w3c.dom.events.InputEvent
import org.w3c.dom.events.KeyboardEvent import org.w3c.dom.events.KeyboardEvent
...@@ -11,7 +12,7 @@ actual open class SkiaLayer { ...@@ -11,7 +12,7 @@ actual open class SkiaLayer {
actual var renderApi: GraphicsApi = GraphicsApi.WEBGL actual var renderApi: GraphicsApi = GraphicsApi.WEBGL
actual val contentScale: Float actual val contentScale: Float
get() = 1.0f get() = window.devicePixelRatio.toFloat()
actual var fullscreen: Boolean actual var fullscreen: Boolean
get() = false get() = false
set(value) { set(value) {
...@@ -39,14 +40,25 @@ actual open class SkiaLayer { ...@@ -39,14 +40,25 @@ actual open class SkiaLayer {
private var isPointerPressed = false private var isPointerPressed = false
private var desiredWidth = 0
private var desiredHeight = 0
fun attachTo(htmlCanvas: HTMLCanvasElement, autoDetach: Boolean = true) { fun attachTo(htmlCanvas: HTMLCanvasElement, autoDetach: Boolean = true) {
// Scale canvas to allow high DPI rendering as suggested in
// https://www.khronos.org/webgl/wiki/HandlingHighDPI.
desiredWidth = htmlCanvas.width
desiredHeight = htmlCanvas.height
htmlCanvas.style.width = "${desiredWidth}px"
htmlCanvas.style.height = "${desiredHeight}px"
setOnChangeScaleNotifier()
state = object: CanvasRenderer(htmlCanvas) { state = object: CanvasRenderer(htmlCanvas) {
override fun drawFrame(currentTimestamp: Double) { override fun drawFrame(currentTimestamp: Double) {
// currentTimestamp is in milliseconds. // currentTimestamp is in milliseconds.
val currentNanos = currentTimestamp * 1_000_000 val currentNanos = currentTimestamp * 1_000_000
skikoView?.onRender(canvas, width, height, currentNanos.toLong()) skikoView?.onRender(canvas!!, width, height, currentNanos.toLong())
} }
} }.apply { initCanvas(desiredWidth, desiredHeight, contentScale) }
// See https://www.w3schools.com/jsref/dom_obj_event.asp // See https://www.w3schools.com/jsref/dom_obj_event.asp
// https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events // https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
htmlCanvas.addEventListener("pointerdown", { event -> htmlCanvas.addEventListener("pointerdown", { event ->
...@@ -83,8 +95,16 @@ actual open class SkiaLayer { ...@@ -83,8 +95,16 @@ actual open class SkiaLayer {
skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP)) skikoView?.onKeyboardEvent(toSkikoEvent(event, SkikoKeyboardEventKind.UP))
}) })
} }
private fun setOnChangeScaleNotifier() {
state?.initCanvas(desiredWidth, desiredHeight, contentScale)
window.matchMedia("(resolution: ${contentScale}dppx)").addEventListener("change", { setOnChangeScaleNotifier() }, true)
onContentScaleChanged?.invoke(contentScale)
}
} }
var onContentScaleChanged: ((Float) -> Unit)? = null
actual typealias SkikoGesturePlatformEvent = Any actual typealias SkikoGesturePlatformEvent = Any
actual typealias SkikoPlatformInputEvent = InputEvent actual typealias SkikoPlatformInputEvent = InputEvent
actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent actual typealias SkikoPlatformKeyboardEvent = KeyboardEvent
......
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