Commit 86aaf759 authored by Alexander Gorshenev's avatar Alexander Gorshenev Committed by alexander-gorshenev

[Native] Experimental skiko for kotlin/native available under -Pskiko.native.enabled=true.

parent 8ebf6920
plugins {
kotlin("multiplatform") version "1.4.30"
}
repositories {
mavenLocal()
jcenter()
maven("https://dl.bintray.com/kotlin/kotlin-eap")
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
}
val osName = System.getProperty("os.name")
val targetOs = when {
osName == "Mac OS X" -> "macos"
osName.startsWith("Win") -> "windows"
osName.startsWith("Linux") -> "linux"
else -> error("Unsupported OS: $osName")
}
val osArch = System.getProperty("os.arch")
var targetArch = when (osArch) {
"x86_64", "amd64" -> "x64"
"aarch64" -> "arm64"
else -> error("Unsupported arch: $osArch")
}
val target = "${targetOs}-${targetArch}"
var version = "0.0.0-SNAPSHOT"
if (project.hasProperty("skiko.version")) {
version = project.properties["skiko.version"] as String
}
kotlin {
val nativeTarget = when {
osName == "Mac OS X" -> macosX64()
osName.startsWith("Win") -> mingwX64()
osName.startsWith("Linux") -> linuxX64()
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
binaries {
executable {
entryPoint = "org.jetbrains.skiko.sample.native.main"
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val nativeMain by creating {
dependsOn(commonMain)
dependencies {
}
}
val macosX64Main by getting {
dependsOn(nativeMain)
dependencies {
implementation("org.jetbrains.skiko:skiko-native-runtime-$target:$version")
implementation("org.jetbrains.skiko:skiko-native-skia-interop-$target:$version")
}
}
}
}
kotlin.code.style=official
kotlin.native.version=1.5.20-dev-5723
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
maven {
url = uri("https://dl.bintray.com/kotlin/kotlin-eap")
}
}
}
rootProject.name = "SkiaNativeSample"
package org.jetbrains.skiko.sample.native
import platform.AppKit.*
import org.jetbrains.skiko.skia.native.*
import org.jetbrains.skiko.native.SkiaLayer
import org.jetbrains.skiko.native.SkiaRenderer
import org.jetbrains.skiko.native.SkiaWindow
import kotlinx.cinterop.*
import kotlin.math.cos
import kotlin.math.sin
fun main(args: Array<String>) {
NSApplication.sharedApplication()
createWindow("Skia/Native macos sample")
NSApp?.run()
}
fun createWindow(title: String) {
val window = SkiaWindow()
var angle = 0.0f
window.layer.renderer = Renderer(window.layer) {
renderer, w, h, nanoTime -> displayScene(renderer, nanoTime)
}
window.nsWindow.orderFrontRegardless()
}
class Renderer(
val layer: SkiaLayer,
val displayScene: (Renderer, Int, Int, Long) -> Unit
): SkiaRenderer {
var canvas: SkCanvas? = null
override fun onRender(canvas: SkCanvas, width: Int, height: Int, nanoTime: Long) {
this.canvas = canvas
val contentScale = layer.contentScale
canvas.scale(contentScale, contentScale)
displayScene(this, (width / contentScale).toInt(), (height / contentScale).toInt(), nanoTime)
layer.needRedraw()
}
}
fun displayScene(renderer: Renderer, nanoTime: Long) {
val canvas = renderer.canvas!!
val paint = SkPaint()
paint.setColor(SK_ColorGREEN)
canvas.clear(SK_ColorRED);
canvas.save();
canvas.translate(128.0f, 128.0f)
canvas.rotate(nanoTime.toFloat() / 1e7f)
val rect = SkRect.MakeXYWH(-90.5f, -90.5f, 181.0f, 181.0f)
canvas.drawRect(rect, paint.ptr)
canvas.restore();
// TODO: need memory management.
// nativeHeap.free(paint)
}
......@@ -2,9 +2,11 @@ import de.undercouch.gradle.tasks.download.Download
import org.gradle.crypto.checksum.Checksum
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinTest
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile
plugins {
kotlin("multiplatform") version "1.3.72"
kotlin("multiplatform") version "1.4.30"
`cpp-library`
`maven-publish`
id("org.gradle.crypto.checksum") version "1.1.0"
......@@ -146,6 +148,9 @@ val skijaSrcDir = run {
}.map { delombokSkijaSrcDir }
}
val Project.supportNative: Boolean
get() = properties.get("skiko.native.enabled") == "true"
kotlin {
jvm {
compilations.all {
......@@ -154,6 +159,35 @@ kotlin {
withJava()
}
if (supportNative) {
val nativeTarget = when (target) {
"macos-x64" -> macosX64()
// "linux-x64" -> linuxX64()
// "windows-x64" -> mingwX64()
else -> null
}
nativeTarget?.apply {
compilations.getByName("main") {
val skia by cinterops.creating {
defFile("src/nativeInterop/cinterop/skia.def")
val skiaDir = skiaDir.get().absolutePath
val kotlinNativeDataPath = System.getenv("KONAN_DATA_DIR")?.let { File(it) }
?: File(System.getProperty("user.home")).resolve(".konan")
compilerOpts(
// TODO: Why do we need `include/c++/v1`?
"-I$kotlinNativeDataPath/dependencies/clang-llvm-apple-8.0.0-darwin-macos//include/c++/v1",
"-I$skiaDir"
)
extraOpts("-staticLibrary", "libskia.a")
extraOpts("-libraryPath", "$skiaDir/out/${buildType.id}-${targetArch.id}")
}
}
}
}
sourceSets {
val commonMain by getting {
dependencies {
......@@ -182,7 +216,16 @@ kotlin {
implementation(kotlin("test-junit"))
}
}
if (supportNative) {
val macosX64Main by getting {
}
}
}
}
tasks.withType(CInteropProcess::class.java).forEach {
it.dependsOn(skiaDir)
}
tasks.withType(JavaCompile::class.java).configureEach {
......@@ -616,6 +659,23 @@ publishing {
artifact(skikoJvmRuntimeJar.map { it.archiveFile.get() })
}
}
if (supportNative) {
create<MavenPublication>("skikoNativeSkiaInterop") {
artifactId = SkikoArtifacts.nativeSkiaInteropArtifactIdFor(targetOs, targetArch)
afterEvaluate {
artifact(project.tasks.withType(CInteropProcess::class.java).single().outputs.getFiles().single())
}
}
create<MavenPublication>("skikoNativeRuntime") {
artifactId = SkikoArtifacts.nativeRuntimeArtifactIdFor(targetOs, targetArch)
afterEvaluate {
artifact(project.tasks.withType(KotlinNativeCompile::class.java)
.single { it.name.startsWith("compileKotlin") } // Exclude compileTestKotlin.
.outputs.getFiles().single { it.name.endsWith(".klib") }
)
}
}
}
}
}
......
......@@ -135,4 +135,8 @@ object SkikoArtifacts {
val commonArtifactId = "skiko-jvm"
fun runtimeArtifactIdFor(os: OS, arch: Arch) =
"skiko-jvm-runtime-${targetId(os, arch)}"
fun nativeRuntimeArtifactIdFor(os: OS, arch: Arch) =
"skiko-native-runtime-${targetId(os, arch)}"
fun nativeSkiaInteropArtifactIdFor(os: OS, arch: Arch) =
"skiko-native-skia-interop-${targetId(os, arch)}"
}
\ No newline at end of file
kotlin.code.style=official
deploy.version=0.0.0
kotlin.native.version=1.5.20-dev-5723
dependencies.skija.git.commit=a5d6dfd746b8a8ec2ef52ebea4baa53f299d080c
dependencies.skia.windows-x64=m90-d9182fe437/Skia-m90-d9182fe437-windows-Release-x64
......
// TODO: Commonize! This is a complete copy of jvm counterpart.
package org.jetbrains.skiko.native
enum class GraphicsApi {
UNKNOWN, SOFTWARE, OPENGL, DIRECT3D, VULKAN, METAL
}
\ No newline at end of file
package org.jetbrains.skiko.native
import kotlinx.cinterop.readValue
import platform.Cocoa.*
import platform.AppKit.*
import platform.Foundation.*
import platform.CoreGraphics.*
abstract class HardwareLayer {
val nsView = NSView(NSMakeRect(0.0, 0.0, 640.0, 480.0))
// getDpiScale is expensive operation on some platforms, so we cache it
private var _contentScale: Float? = null
private var isInit = false
fun checkIsShowing() {
if (!isInit && !nsView.hiddenOrHasHiddenAncestor) {
// _contentScale = getDpiScale()
_contentScale = 1.0f // TODO: what's the proper way here?
init()
isInit = true
}
}
open fun init() {
}
protected open fun nativeInit(platformInfo: Long) {
}
open fun dispose() {
}
protected open fun contentScaleChanged() = Unit
private fun checkContentScale() {
}
internal abstract fun update(nanoTime: Long)
internal abstract fun draw()
val contentScale: Float
get() = _contentScale!!
}
package org.jetbrains.skiko.native
import org.jetbrains.skiko.native.*
import org.jetbrains.skiko.native.SkikoProperties.renderApi
import org.jetbrains.skiko.native.redrawer.*
internal interface PlatformOperations {
fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties): Redrawer
}
internal val platformOperations: PlatformOperations by lazy {
object: PlatformOperations {
override fun createRedrawer(layer: HardwareLayer, properties: SkiaLayerProperties) = when(renderApi) {
GraphicsApi.OPENGL -> MacOsOpenGLRedrawer(layer, properties)
else -> error("No software rendering for native yet")
}
}
}
package org.jetbrains.skiko.native
import kotlinx.cinterop.pointed
import kotlinx.cinterop.useContents
import org.jetbrains.skiko.native.context.*
import org.jetbrains.skiko.native.redrawer.*
import org.jetbrains.skiko.skia.native.*
interface SkiaRenderer {
fun onRender(canvas: SkCanvas, width: Int, height: Int, nanoTime: Long)
}
// TODO: this is exact copy of jvm counterpart. Commonize!
private class PictureHolder(val instance: SkPicture, val width: Int, val height: Int)
open class SkiaLayer(
private val properties: SkiaLayerProperties = SkiaLayerProperties()
) : HardwareLayer() {
var renderer: SkiaRenderer? = null
internal var skiaState = createContextHandler(this)
private var isDisposed = false
private var redrawer: Redrawer? = null
private var picture: PictureHolder? = null
private val pictureRecorder = SkPictureRecorder()
private val pictureLock = Any()
override fun init() {
println("SkiaLayer::init")
super.init()
redrawer = platformOperations.createRedrawer(this, properties)
redrawer?.redrawImmediately()
}
override fun dispose() {
redrawer?.dispose()
super.dispose()
}
fun needRedraw() {
redrawer?.needRedraw()
}
override fun update(nanoTime: Long) {
println("SkiaLayer::update")
val width = nsView.frame.useContents { size.width }
val height = nsView.frame.useContents { size.height }
val pictureWidth = (width * contentScale).coerceAtLeast(0.0)
val pictureHeight = (height * contentScale).coerceAtLeast(0.0)
val bounds = SkRect.MakeWH(pictureWidth.toFloat(), pictureHeight.toFloat())
val canvas = pictureRecorder.beginRecording(bounds, null)!!
// TODO: get rid of .pointed?
renderer?.onRender(canvas.pointed, pictureWidth.toInt(), pictureHeight.toInt(), nanoTime)
val picture = pictureRecorder.finishRecordingAsPicture()!!
this.picture = PictureHolder(picture, pictureWidth.toInt(), pictureHeight.toInt())
}
override fun draw() {
println("SkiaLayer::draw")
skiaState.apply {
if (!initContext()) {
error("initContext() failure. No fallback to raster for Skia/native yet.")
return
}
initCanvas()
clearCanvas()
val picture = picture
println("SkiaLayer::draw: picture=$picture")
if (picture != null) {
drawOnCanvas(picture.instance)
}
flush()
}
}
}
package org.jetbrains.skiko.native
class SkiaLayerProperties(
val isVsyncEnabled: Boolean = SkikoProperties.vsyncEnabled
)
\ No newline at end of file
package org.jetbrains.skiko.native
import platform.AppKit.*
import platform.Cocoa.*
import platform.Foundation.*
open class SkiaWindow(
properties: SkiaLayerProperties = SkiaLayerProperties()
) {
val windowStyle =
NSWindowStyleMaskTitled or
NSWindowStyleMaskMiniaturizable or
NSWindowStyleMaskClosable or
NSWindowStyleMaskResizable
// TODO: what's the proper way to create window here?
// Should we subclass NSWindow?
// Provide multiple constructors for SkiaWindow?
// Behave similar to Skiko/JVM?
val nsWindow = NSWindow(
contentRect = NSMakeRect(0.0, 0.0, 640.0, 480.0),
styleMask = windowStyle,
backing = NSBackingStoreBuffered,
defer = true)
val layer = SkiaLayer(properties)
init {
nsWindow.contentView?.addSubview(layer.nsView)
layer.checkIsShowing() // TODO: The awt versions has hierarchy listener. What should we use here?
}
}
package org.jetbrains.skiko.native
// TODO: jvm version uses java properties here.
// there is no properties support available for native.
internal object SkikoProperties {
val vsyncEnabled: Boolean = true
val renderApi: GraphicsApi by lazy {
GraphicsApi.OPENGL
}
private fun parseRenderApi(text: String?): GraphicsApi {
when(text) {
"SOFTWARE" -> return GraphicsApi.SOFTWARE
"OPENGL" -> return GraphicsApi.OPENGL
else -> return GraphicsApi.OPENGL
}
}
}
package org.jetbrains.skiko.native.context
import kotlinx.cinterop.ptr
import org.jetbrains.skiko.skia.native.*
import org.jetbrains.skiko.native.*
internal fun createContextHandler(layer: HardwareLayer): ContextHandler {
return OpenGLContextHandler(layer)
//return when (SkikoProperties.renderApi) {
// GraphicsApi.SOFTWARE -> SoftwareContextHandler(layer)
// GraphicsApi.OPENGL -> OpenGLContextHandler(layer)
// else -> TODO("Unsupported yet")
//}
}
internal abstract class ContextHandler(val layer: HardwareLayer) {
// TODO: hostOs is all written in jdk kotlin.
// open val bleachConstant = if (hostOs == OS.MacOS) 0 else -1
open val bleachConstant = 0U
var context: GrDirectContext? = null
var renderTarget: GrBackendRenderTarget? = null
var surface: SkSurface? = null
var canvas: SkCanvas? = null
abstract fun initContext(): Boolean
abstract fun initCanvas()
fun clearCanvas() {
println("ContextHandler::clearCanvas")
canvas?.clear(bleachConstant)
}
open fun drawOnCanvas(picture: SkPicture) {
println("ContextHandler::drawOnCanvas")
canvas?.drawPicture(picture.ptr)
}
open fun flush() {
println("ContextHandler::flush")
context?.flush()
}
fun dispose() {
println("Need to free surface")
println("Need to free renderTarget")
// surface?.close()
// renderTarget?.close()
}
}
package org.jetbrains.skiko.native.context
import kotlinx.cinterop.*
import org.jetbrains.skiko.skia.native.*
import org.jetbrains.skiko.native.*
import platform.OpenGL.GL_DRAW_FRAMEBUFFER_BINDING
import platform.OpenGL.GL_RGBA8
import platform.OpenGL.glGetIntegerv
import platform.OpenGL.*
import platform.OpenGLCommon.GLenum
internal class OpenGLContextHandler(layer: HardwareLayer) : ContextHandler(layer) {
override fun initContext(): Boolean {
println("OpenGLContextHandler::initContext")
try {
if (context == null) {
context = GrDirectContext.MakeGL()
}
} catch (e: Exception) {
println("Failed to create Skia OpenGL context!")
return false
}
return true
}
@ExperimentalUnsignedTypes
private fun openglGetIntegerv(pname: GLenum): UInt {
var result: UInt = 0U
memScoped {
val data = alloc<IntVar>()
glGetIntegerv(pname, data.ptr);
result = data.value.toUInt();
}
return result
}
@ExperimentalUnsignedTypes
override fun initCanvas() {
println("OpenGLContextHandler::initCanvas")
dispose()
val scale = layer.contentScale
val w = (layer.nsView.frame.useContents { size.width } * scale).toInt().coerceAtLeast(0)
val h = (layer.nsView.frame.useContents { size.height } * scale).toInt().coerceAtLeast(0)
val fbId = openglGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING.toUInt())
// TODO: Skia C++ interop: glInfo is a `struct`, not `class`,
// so we fallback to C interop here.
memScoped {
val glInfo: GrGLFramebufferInfo = alloc<GrGLFramebufferInfo>()
glInfo.fFBOID = fbId
glInfo.fFormat = GL_RGBA8.toUInt()
renderTarget = GrBackendRenderTarget(w, h, 0, 8, glInfo.readValue())
}
surface = SkSurface.MakeFromBackendRenderTarget(
// TODO: C++ interop knows nothing about inheritance.
context!!.ptr.reinterpret<GrRecordingContext>(),
renderTarget!!.ptr,
GrSurfaceOrigin.kBottomLeft_GrSurfaceOrigin,
colorType = kRGBA_8888_SkColorType,
colorSpace = SkColorSpace.MakeSRGB(),
surfaceProps = null,
releaseProc = null,
releaseContext = null
) ?: error("Could not obtain SkSurface")
canvas = surface!!.getCanvas()?.pointed
?: error("Could not obtain SkCanvas from SkSurface")
}
}
package org.jetbrains.skiko.native.redrawer
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.useContents
import org.jetbrains.skiko.native.*
import platform.CoreFoundation.CFTimeInterval
import platform.CoreGraphics.CGRectMake
import platform.CoreVideo.CVTimeStamp
import platform.OpenGLCommon.CGLContextObj
import platform.OpenGLCommon.CGLPixelFormatObj
import platform.OpenGLCommon.CGLSetCurrentContext
import platform.QuartzCore.CAOpenGLLayer
import platform.QuartzCore.*
import kotlin.system.getTimeNanos
internal class MacOsOpenGLRedrawer(
private val layer: HardwareLayer,
private val properties: SkiaLayerProperties
) : Redrawer {
private var isDisposed = false
private val drawLayer = MacosGLLayer(layer, setNeedsDisplayOnBoundsChange = true)
override fun dispose() {
drawLayer.dispose()
}
override fun syncSize() {
// TODO: What do we really do here?
layer.nsView.frame.useContents {
drawLayer.setFrame(
origin.x.toInt(),
origin.y.toInt(),
size.width.toInt().coerceAtLeast(0),
size.height.toInt().coerceAtLeast(0)
)
}
}
override fun needRedraw() {
}
override fun redrawImmediately() {
layer.update(getTimeNanos())
}
}
class MacosGLLayer(val layer: HardwareLayer, setNeedsDisplayOnBoundsChange: Boolean) : CAOpenGLLayer() {
val container = layer.nsView
init {
this.setNeedsDisplayOnBoundsChange(setNeedsDisplayOnBoundsChange)
this.removeAllAnimations()
this.setAutoresizingMask(kCALayerWidthSizable or kCALayerHeightSizable )
container.layer = this
container.wantsLayer = true
}
fun draw() {
println("MacosGLLayer::draw")
layer.update(getTimeNanos())
layer.draw()
}
fun setFrame(x: Int, y: Int, width: Int, height: Int) {
val newY = container.frame.useContents { size.height } - y - height
CATransaction.begin()
CATransaction.setDisableActions(true)
this.frame = CGRectMake(x.toDouble(), newY, width.toDouble(), height.toDouble())
CATransaction.commit()
}
fun dispose() {
this.removeFromSuperlayer()
// TODO: anything else to dispose the layer?
}
protected open fun canDraw() = true
@Suppress("unused")
private fun performDraw() {
try {
draw()
} catch (e: Throwable) {
e.printStackTrace()
}
//display.finish()
}
override fun canDrawInCGLContext(
ctx: CGLContextObj?,
pixelFormat: CGLPixelFormatObj?,
forLayerTime: CFTimeInterval,
displayTime: CPointer<CVTimeStamp>?
): Boolean {
return canDraw()
}
override fun drawInCGLContext(
ctx: CGLContextObj?,
pixelFormat: CGLPixelFormatObj?,
forLayerTime: CFTimeInterval,
displayTime: CPointer<CVTimeStamp>?
) {
CGLSetCurrentContext(ctx);
println("MacosGLLayer::drawInCGLContext")
performDraw()
//context.flush() // TODO: I thought the below should call context.flush().
super.drawInCGLContext(ctx, pixelFormat,forLayerTime, displayTime)
}
}
package org.jetbrains.skiko.native.redrawer
// TODO: this is exact copy of jvm counterpart. Commonize!
interface Redrawer {
fun dispose()
fun needRedraw()
fun redrawImmediately()
fun syncSize() = Unit
}
\ No newline at end of file
package = org.jetbrains.skiko.skia.native
language = C++
plugin = org.jetbrains.kotlin.native.cinterop.plugin.skia
compilerOpts.osx = -x c++ -std=c++14 \
-DSK_GL \
-DSK_SUPPORT_GPU=1 \
-DSK_SHAPER_CORETEXT_AVAILABLE \
-DSK_BUILD_FOR_MAC \
-DSK_METAL \
-DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=1 \
-DSK_FORCE_DISTANCE_FIELD_TEXT=0 \
-DSK_GAMMA_APPLY_TO_A8 \
-DSK_GAMMA_SRGB \
-DSK_SCALAR_TO_FLOAT_EXCLUDED \
-DSK_SHAPER_HARFBUZZ_AVAILABLE \
-DSK_SUPPORT_OPENCL=0 \
-DSK_UNICODE_AVAILABLE
headers = include/core/SkCanvas.h include/core/SkData.h include/core/SkImage.h include/core/SkMath.h include/core/SkStream.h include/core/SkSurface.h include/core/SkSurfaceProps.h include/core/SkTypes.h include/core/SkPaint.h include/core/SkPictureRecorder.h include/core/SkImageInfo.h include/core/SkColor.h include/core/SkColorSpace.h include/gpu/GrDirectContext.h include/gpu/GrBackendSurface.h include/gpu/GrConfig.h include/gpu/gl/GrGLInterface.h
headerFilter = include/core/SkCanvas.h include/core/SkData.h include/core/SkImage.h include/core/SkMath.h include/core/SkStream.h include/core/SkSurface.h include/core/SkSurfaceProps.h include/core/SkTypes.h include/core/SkPaint.h include/core/SkPictureRecorder.h include/core/SkImageInfo.h include/core/SkColor.h include/core/SkColorSpace.h include/gpu/GrDirectContext.h include/gpu/GrBackendSurface.h include/gpu/GrConfig.h include/gpu/gl/GrGLInterface.h
# const overloads are not supported yet
excludedFunctions = saveLayer mapRect ptr priv
linkerOpts.osx = -framework CoreServices -framework CoreText -framework CoreGraphics -framework Metal
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