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
import kotlinx.browser.document
import org.jetbrains.skiko.GenericSkikoView
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.onContentScaleChanged
import org.jetbrains.skiko.wasm.onWasmReady
import org.w3c.dom.HTMLCanvasElement
fun main() {
onWasmReady {
val skiaLayer = SkiaLayer()
onContentScaleChanged = { scale -> println(scale) }
val game = Clocks(skiaLayer)
skiaLayer.skikoView = GenericSkikoView(skiaLayer, game)
val canvas = document.getElementById("SkikoTarget") as HTMLCanvasElement
......
......@@ -11,7 +11,7 @@
<body>
<h1>Skiko For Web</h1>
<div>
<canvas id="SkikoTarget" width="800" height="600"></canvas>
<canvas id="SkikoTarget" width="800px" height="600px"></canvas>
</div>
</body>
</html>
......@@ -12,25 +12,44 @@ import org.jetbrains.skiko.wasm.CreateWebGLContext
import org.jetbrains.skiko.wasm.GL
import org.w3c.dom.HTMLCanvasElement
abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val width: Int, val height: Int) {
constructor(canvas: HTMLCanvasElement): this(canvas, canvas.width, canvas.height)
abstract class CanvasRenderer constructor(val htmlCanvas: HTMLCanvasElement) {
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 {
GL.makeContextCurrent(contextPointer)
context = DirectContext.makeGL()
}
private val context = DirectContext.makeGL()
private val surface = Surface.makeFromBackendRenderTarget(
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,
BackendRenderTarget.makeGL(
width, height, 1, 8, 0, 0x8058
),
renderTarget!!,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.sRGB
)
val canvas: Canvas
get() = surface.canvas
canvas = surface!!.canvas
}
fun disposeCanvas() {
surface?.close()
surface = null
renderTarget?.close()
renderTarget = null
}
abstract fun drawFrame(currentTimestamp: Double)
......@@ -44,9 +63,9 @@ abstract class CanvasRenderer constructor(htmlCanvas: HTMLCanvasElement, val wid
window.requestAnimationFrame { timestamp ->
redrawScheduled = false
GL.makeContextCurrent(contextPointer)
canvas.clear(-1)
canvas?.clear(-1)
drawFrame(timestamp)
surface.flushAndSubmit()
surface?.flushAndSubmit()
context.flush()
}
}
......
package org.jetbrains.skiko
import kotlinx.browser.window
import org.w3c.dom.HTMLCanvasElement
import org.w3c.dom.events.InputEvent
import org.w3c.dom.events.KeyboardEvent
......@@ -11,7 +12,7 @@ actual open class SkiaLayer {
actual var renderApi: GraphicsApi = GraphicsApi.WEBGL
actual val contentScale: Float
get() = 1.0f
get() = window.devicePixelRatio.toFloat()
actual var fullscreen: Boolean
get() = false
set(value) {
......@@ -39,14 +40,25 @@ actual open class SkiaLayer {
private var isPointerPressed = false
private var desiredWidth = 0
private var desiredHeight = 0
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) {
override fun drawFrame(currentTimestamp: Double) {
// currentTimestamp is in milliseconds.
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
// https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
htmlCanvas.addEventListener("pointerdown", { event ->
......@@ -83,8 +95,16 @@ actual open class SkiaLayer {
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 SkikoPlatformInputEvent = InputEvent
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