Unverified Commit 07f4a8ad authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

Direct AWT rendering. (#5)

parent 81e843ff
skija
build
.gradle
local.properties
.DS_Store
run.sh
.idea
\ No newline at end of file
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4-M3'
id 'application'
}
repositories {
mavenLocal()
jcenter()
maven {
url 'https://dl.bintray.com/kotlin/kotlin-eap'
}
}
def os = System.getProperty("os.name")
def target = "linux"
if (os == "Mac OS X") {
target = "macos"
} else if (os.startsWith("Win")) {
target = "windows"
}
dependencies {
implementation platform('org.jetbrains.kotlin:kotlin-bom')
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
implementation "org.jetbrains.skiko:skiko-jvm-runtime-$target:0.1-SNAPSHOT"
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
application {
mainClassName = 'SkijaInjectSample.AppKt'
}
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
#!/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" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
/*
* This file was generated by the Gradle 'init' task.
*
* The settings file is used to specify which projects to include in your build.
*
* Detailed information about configuring a multi-project build in Gradle can be found
* in the user manual at https://docs.gradle.org/6.5/userguide/multi_project_builds.html
*/
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
maven {
url "https://dl.bintray.com/kotlin/kotlin-eap"
}
}
}
rootProject.name = 'SkijaInjectSample'
package SkijaInjectSample
import org.jetbrains.skiko.OpenGLApi
import org.jetbrains.skiko.Engine
import org.jetbrains.skiko.SkiaWindow
import java.awt.event.MouseEvent
import javax.swing.WindowConstants
import javax.swing.event.MouseInputAdapter
import org.jetbrains.skija.*
import org.jetbrains.skiko.SkiaRenderer
import java.awt.event.MouseMotionAdapter
import kotlin.math.cos
import kotlin.math.sin
fun main(args: Array<String>) {
createWindow("First window");
}
fun createWindow(title: String) {
val engine: Engine = Engine.get()
var mouseX = 0
var mouseY = 0
val window = SkiaWindow()
window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
val state = State()
state.text = title
window.layer.renderer = Renderer {
renderer, w, h -> displayScene(renderer, w, h, mouseX, mouseY, state)
}
window.layer.addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseMoved(event: MouseEvent) {
mouseX = event.x
mouseY = event.y
engine.render(window.layer)
}
})
window.setVisible(true);
// MANDATORY: set window size after calling setVisible(true)
window.setSize(800, 600);
}
class Renderer(val displayScene: (Renderer, Int, Int) -> Unit): SkiaRenderer {
val typeface = Typeface.makeFromFile("fonts/JetBrainsMono-Regular.ttf")
val font = Font(typeface, 40f)
val paint = Paint().apply {
setColor(0xff9BC730L.toInt())
setMode(PaintMode.FILL)
setStrokeWidth(1f)
}
var canvas: Canvas? = null
override fun onInit() {
}
override fun onDispose() {
}
override fun onReshape(width: Int, height: Int) {
}
override fun onRender(canvas: Canvas, width: Int, height: Int) {
this.canvas = canvas
displayScene(this, width, height)
}
}
class State {
var frame: Int = 0
var text: String = "Hello Skija"
}
fun displayScene(renderer: Renderer, width: Int, height: Int, xpos: Int, ypos: Int, state: State) {
val canvas = renderer.canvas!!
val watchFill = Paint().setColor(0xFFFFFFFF.toInt())
val watchStroke = Paint().setColor(0xFF000000.toInt()).setMode(PaintMode.STROKE).setStrokeWidth(1f).setAntiAlias(false)
val watchStrokeAA = Paint().setColor(0xFF000000.toInt()).setMode(PaintMode.STROKE).setStrokeWidth(1f)
val watchFillHover = Paint().setColor(0xFFE4FF01.toInt())
for (x in 0 .. (width - 50) step 50) {
for (y in 0 .. (height - 50) step 50) {
val hover = xpos > x + 0 && xpos < x + 50 && ypos > y + 0 && ypos < y + 50
val fill = if (hover) watchFillHover else watchFill
val stroke = if (x > width / 2) watchStrokeAA else watchStroke
canvas.drawOval(Rect.makeXYWH(x + 5f, y + 5f, 40f, 40f), fill)
canvas.drawOval(Rect.makeXYWH(x + 5f, y + 5f, 40f, 40f), stroke)
var angle = 0f
while (angle < 2f * Math.PI) {
canvas.drawLine(
(x + 25 - 17 * sin(angle)),
(y + 25 + 17 * cos(angle)),
(x + 25 - 20 * sin(angle)),
(y + 25 + 20 * cos(angle)),
stroke
)
angle += (2.0 * Math.PI / 12.0).toFloat()
}
val time = System.currentTimeMillis() % 60000 +
(x.toFloat() / width * 5000).toLong() +
(y.toFloat() / width * 5000).toLong()
val angle1 = (time.toFloat() / 5000 * 2f * Math.PI).toFloat()
canvas.drawLine(x + 25f, y + 25f,
x + 25f - 15f * sin(angle1),
y + 25f + 15 * cos(angle1),
stroke)
val angle2 = (time / 60000 * 2f * Math.PI).toFloat()
canvas.drawLine(x + 25f, y + 25f,
x + 25f - 10f * sin(angle2),
y + 25f + 10f * cos(angle2),
stroke)
}
}
val text = "${state.text} ${state.frame++}!"
canvas.drawString(text, xpos.toFloat(), ypos.toFloat(), renderer.font, renderer.paint)
}
/*
* This Kotlin source file was generated by the Gradle 'init' task.
*/
package SkijaInjectSample
import kotlin.test.Test
import kotlin.test.assertNotNull
class AppTest {
// @Test fun testAppHasAGreeting() {
// val classUnderTest = App()
// assertNotNull(classUnderTest.greeting, "app should have a greeting")
// }
}
build
.gradle
local.properties
.DS_Store
......@@ -22,6 +22,8 @@ val target = when {
else -> throw Error("Unknown os $hostOs")
}
val jdkHome = System.getProperty("java.home") ?: error("'java.home' is null")
repositories {
mavenCentral()
maven {
......@@ -77,10 +79,6 @@ kotlin {
// See https://docs.gradle.org/current/userguide/cpp_library_plugin.html.
tasks.withType(CppCompile::class.java).configureEach {
source.from(
fileTree("$projectDir/../skija/src/main/cc"),
fileTree("$projectDir/src/jvmMain/cpp")
)
// Prefer 'java.home' system property to simplify overriding from Intellij.
// When used from command-line, it is effectively equal to JAVA_HOME.
if (JavaVersion.current() < JavaVersion.VERSION_11) {
......@@ -154,22 +152,49 @@ tasks.withType(CppCompile::class.java).configureEach {
}
}
// Very hacky way to compile Objective-C sources and add the
// resulting object files into the final library.
project.tasks.register<Exec>("objcCompile") {
val inputDir = "$projectDir/src/jvmMain/objectiveC/$target"
val outDir = "$buildDir/objc/$target"
val objcSrc = "drawlayer"
commandLine = listOf(
"clang",
"-I$jdkHome/include",
"-I$jdkHome/include/darwin",
"-c",
"$inputDir/$objcSrc.m",
"-o",
"$outDir/$objcSrc.o"
)
file(outDir).mkdirs()
inputs.files("$inputDir/$objcSrc.m")
outputs.files("$outDir/$objcSrc.o")
}
tasks.withType(LinkSharedLibrary::class.java).configureEach {
when (target) {
"macos" -> {
dependsOn(project.tasks.named("objcCompile"))
linkerArgs.addAll(
listOf(
"-dead_strip",
"-framework", "AppKit",
"-framework", "CoreFoundation",
"-framework", "CoreGraphics",
"-framework", "CoreServices",
"-framework", "CoreText"
"-framework", "CoreText",
"-framework", "Foundation",
"-framework", "OpenGL",
"-framework", "QuartzCore" // for CoreAnimation
)
)
}
"linux" -> {
linkerArgs.addAll(
listOf(
"-lGL",
"-lfontconfig"
)
)
......@@ -177,14 +202,24 @@ tasks.withType(LinkSharedLibrary::class.java).configureEach {
"windows" -> {
linkerArgs.addAll(
listOf(
"user32.lib",
"opengl32.lib"
"gdi32.lib",
"opengl32.lib",
"shcore.lib",
"user32.lib"
)
)
}
}
}
extensions.configure<CppLibrary> {
source.from(
fileTree("$projectDir/../skija/src/main/cc"),
fileTree("$projectDir/src/jvmMain/cpp/common"),
fileTree("$projectDir/src/jvmMain/cpp/$target")
)
}
library {
linkage.addAll(listOf(Linkage.SHARED))
targetMachines.addAll(listOf(machines.macOS.x86_64, machines.linux.x86_64, machines.windows.x86_64))
......@@ -197,6 +232,9 @@ library {
else
include("**.a")
})
implementation(fileTree("$buildDir/objc/$target").matching {
include("**.o")
})
}
}
......
kotlin.code.style=official
deploy.version=0.1-SNAPSHOT
deploy.ci.version=0.1
deploy.version=0.1-SNAPSHOT
deploy.ci.version=0.1
\ No newline at end of file
#include <jni.h>
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skiko_SkiaWindow_nativeMethod
(JNIEnv* env, jobject thiz, jlong param) {
return param + 1;
}
\ No newline at end of file
#include <jawt.h>
#include <jawt_md.h>
#include <stdlib.h>
#if SK_BUILD_FOR_WIN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
typedef jboolean (*JAWT_GetAWT_t)(JNIEnv *, JAWT *);
extern "C" jboolean Skiko_GetAWT(JNIEnv *env, JAWT *awt) {
static JAWT_GetAWT_t func = nullptr;
if (!func) {
char *jdkHome = getenv("JAVA_HOME");
if (!jdkHome) {
fprintf(stderr, "No JAVA_HOME defined!");
abort();
}
#if SK_BUILD_FOR_WIN
char path[FILENAME_MAX];
snprintf(path, sizeof(path), "%s\\bin\\jawt.dll", jdkHome);
HMODULE lib = LoadLibrary(path);
if (!lib) {
fprintf(stderr, "Cannot open %s\n", path);
abort();
}
func = reinterpret_cast<JAWT_GetAWT_t>(GetProcAddress(lib, "JAWT_GetAWT"));
if (!func) {
fprintf(stderr, "Cannot find JAWT_GetAWT in %s\n", path);
abort();
}
#else
char path[FILENAME_MAX];
snprintf(path, sizeof(path), "%s/lib/libjawt%s", jdkHome,
#if SK_BUILD_FOR_MAC
".dylib"
#else
".so"
#endif
);
void *lib = dlopen(path, RTLD_LAZY | RTLD_GLOBAL);
if (!lib) {
fprintf(stderr, "Cannot open %s: %s\n", path, dlerror());
abort();
}
func = reinterpret_cast<JAWT_GetAWT_t>(dlsym(lib, "JAWT_GetAWT"));
if (!func) {
fprintf(stderr, "Cannot find JAWT_GetAWT in %s\n", path);
abort();
}
#endif
}
return func(env, awt);
}
\ No newline at end of file
#if SK_BUILD_FOR_WIN
#include <SDKDDKVer.h>
#include <windows.h>
#endif
#if SK_BUILD_FOR_MAC
#import <OpenGL/gl3.h>
#else
#include <GL/gl.h>
#endif
#include <jni.h>
extern "C" {
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glViewport(JNIEnv * env, jobject object, jint x, jint y, jint w, jint h) {
glViewport(x, y, w, h);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glClearColor(JNIEnv * env, jobject object, jfloat r, jfloat g, jfloat b, jfloat a) {
glClearColor(r, g, b, a);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glClear(JNIEnv * env, jobject object, jint mask) {
glClear(mask);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glFinish(JNIEnv * env, jobject object) {
glFinish();
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glEnable(JNIEnv * env, jobject object, jint cap) {
glEnable(cap);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_OpenGLApi_glBindTexture(JNIEnv * env, jobject object, jint target, jint texture) {
glBindTexture(target, texture);
}
JNIEXPORT jint JNICALL Java_org_jetbrains_skiko_OpenGLApi_glGetIntegerv(JNIEnv * env, jobject object, jint pname) {
GLint data;
glGetIntegerv(pname, &data);
return (jint)data;
}
}
\ No newline at end of file
// drawcanvas.cpp : Defines the exported functions for the DLL application.
#include <jawt_md.h>
#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <unistd.h>
#include <stdio.h>
#include <set>
using namespace std;
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display *, GLXFBConfig, GLXContext, Bool, const int *);
extern "C" jboolean Skiko_GetAWT(JNIEnv *env, JAWT *awt);
JavaVM *jvm = NULL;
class LayersSet
{
public:
jobject canvasRef;
GLXContext context;
void update()
{
draw();
}
void dispose()
{
context = NULL;
}
private:
bool isDrawing = false;
void draw()
{
if (isDrawing)
{
return;
}
isDrawing = true;
if (jvm != NULL)
{
JNIEnv *env;
jvm->GetEnv((void **)&env, JNI_VERSION_10);
JAWT awt;
JAWT_DrawingSurface *ds = NULL;
JAWT_DrawingSurfaceInfo *dsi = NULL;
jboolean result = JNI_FALSE;
jint lock = 0;
JAWT_X11DrawingSurfaceInfo *dsi_x11;
awt.version = (jint)JAWT_VERSION_9;
result = Skiko_GetAWT(env, &awt);
if (result == JNI_FALSE)
{
fprintf(stderr, "JAWT_GetAWT failed! Result is JNI_FALSE\n");
return;
}
ds = awt.GetDrawingSurface(env, canvasRef);
lock = ds->Lock(ds);
dsi = ds->GetDrawingSurfaceInfo(ds);
dsi_x11 = (JAWT_X11DrawingSurfaceInfo *)dsi->platformInfo;
Display *display = dsi_x11->display;
Window window = dsi_x11->drawable;
if (dsi != NULL)
{
jvm->AttachCurrentThread((void **)&env, NULL);
glXMakeCurrent(display, window, context);
jclass wndClass = env->GetObjectClass(canvasRef);
jmethodID drawMethod = env->GetMethodID(wndClass, "draw", "()V");
if (NULL == drawMethod)
{
fprintf(stderr, "The method Window.draw() not found!\n");
return;
}
env->CallVoidMethod(canvasRef, drawMethod);
glXSwapBuffers(display, window);
glXMakeCurrent(display, 0, 0);
jvm->DetachCurrentThread();
}
ds->FreeDrawingSurfaceInfo(dsi);
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);
}
isDrawing = false;
}
};
set<LayersSet *> *windowsSet = NULL;
LayersSet *findByObject(JNIEnv *env, jobject object)
{
for (auto &layer : *windowsSet)
{
if (env->IsSameObject(object, layer->canvasRef) == JNI_TRUE)
{
return layer;
}
}
fprintf(stderr, "The set does not contain this window.\n");
return NULL;
}
extern "C" {
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_updateLayer(JNIEnv *env, jobject canvas)
{
if (windowsSet != NULL)
{
LayersSet *layer = findByObject(env, canvas);
if (layer != NULL)
{
if (layer->context == NULL)
{
env->DeleteGlobalRef(layer->canvasRef);
layer->canvasRef = NULL;
windowsSet->erase(layer);
delete layer;
}
return;
}
}
else
{
windowsSet = new set<LayersSet *>();
}
if (jvm == NULL)
{
env->GetJavaVM(&jvm);
}
JAWT awt;
JAWT_DrawingSurface *ds = NULL;
JAWT_DrawingSurfaceInfo *dsi = NULL;
jboolean result = JNI_FALSE;
jint lock = 0;
JAWT_X11DrawingSurfaceInfo *dsi_x11;
awt.version = (jint)JAWT_VERSION_9;
result = Skiko_GetAWT(env, &awt);
if (result == JNI_FALSE)
{
fprintf(stderr, "JAWT_GetAWT failed! Result is JNI_FALSE\n");
return;
}
ds = awt.GetDrawingSurface(env, canvas);
lock = ds->Lock(ds);
dsi = ds->GetDrawingSurfaceInfo(ds);
dsi_x11 = (JAWT_X11DrawingSurfaceInfo *)dsi->platformInfo;
Display *display = dsi_x11->display;
Window window = dsi_x11->drawable;
if (dsi != NULL)
{
GLint att[] = {GLX_RGBA, GLX_DOUBLEBUFFER, None};
XVisualInfo *vi = glXChooseVisual(display, 0, att);
GLXContext context = glXCreateContext(display, vi, NULL, GL_TRUE);
LayersSet *layer = new LayersSet();
windowsSet->insert(layer);
jobject canvasRef = env->NewGlobalRef(canvas);
layer->canvasRef = canvasRef;
layer->context = context;
}
ds->FreeDrawingSurfaceInfo(dsi);
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_redrawLayer(JNIEnv *env, jobject canvas)
{
LayersSet *layer = findByObject(env, canvas);
if (layer != NULL)
{
layer->update();
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_disposeLayer(JNIEnv *env, jobject canvas)
{
LayersSet *layer = findByObject(env, canvas);
if (layer != NULL)
{
layer->dispose();
}
}
JNIEXPORT jfloat JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentScale(JNIEnv *env, jobject canvas)
{
return 1.0f;
}
} // extern "C"
\ No newline at end of file
// drawcanvas.cpp : Defines the exported functions for the DLL application.
#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gl/GL.h>
#include <jawt_md.h>
#include <set>
#include <Shellscalingapi.h>
#include <stdio.h>
#include <Wingdi.h>
using namespace std;
JavaVM *jvm = NULL;
extern "C" jboolean Skiko_GetAWT(JNIEnv *env, JAWT *awt);
class LayersSet {
public:
jobject windowRef;
HGLRC context;
HWND glLayer;
void update() {
draw();
}
void dispose() {
glLayer = NULL;
context = NULL;
}
private:
bool isDrawing = false;
void draw() {
if (isDrawing) {
return;
}
isDrawing = true;
HDC handler = GetDC(glLayer);
wglMakeCurrent(handler, context);
if (jvm != NULL) {
JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);
jclass wndClass = env->GetObjectClass(windowRef);
jmethodID drawMethod = env->GetMethodID(wndClass, "draw", "()V");
if (NULL == drawMethod) {
fprintf(stderr, "The method Window.draw() not found!\n");
return;
}
env->CallVoidMethod(windowRef, drawMethod);
}
wglMakeCurrent(handler, NULL);
isDrawing = false;
}
};
set<LayersSet*> *windowsSet = NULL;
LayersSet *findByObject(JNIEnv *env, jobject object) {
for (auto &layer : *windowsSet) {
if (env->IsSameObject(object, layer->windowRef) == JNI_TRUE) {
return layer;
}
}
fprintf(stderr, "The set does not contain this window.\n");
return NULL;
}
float getScaleFloatFromEnum(DEVICE_SCALE_FACTOR scaleFactor) {
switch (scaleFactor) {
case SCALE_100_PERCENT:
return 1.0f;
case SCALE_120_PERCENT:
return 1.2f;
case SCALE_125_PERCENT:
return 1.25f;
case SCALE_140_PERCENT:
return 1.4f;
case SCALE_150_PERCENT:
return 1.5f;
case SCALE_160_PERCENT:
return 1.6f;
case SCALE_175_PERCENT:
return 1.75f;
case SCALE_180_PERCENT:
return 1.8f;
case SCALE_200_PERCENT:
return 2.0f;
case SCALE_225_PERCENT:
return 2.25f;
case SCALE_250_PERCENT:
return 2.5f;
case SCALE_300_PERCENT:
return 3.0f;
case SCALE_350_PERCENT:
return 3.5f;
case SCALE_400_PERCENT:
return 4.0f;
case SCALE_450_PERCENT:
return 4.5f;
case SCALE_500_PERCENT:
return 5.0f;
case DEVICE_SCALE_FACTOR_INVALID:
return 1.0f;
}
return 1.0f;
}
extern "C" {
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_updateLayer(JNIEnv *env, jobject window)
{
if (windowsSet != NULL) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
if (layer->glLayer == NULL && layer->context == NULL) {
env->DeleteGlobalRef(layer->windowRef);
layer->windowRef = NULL;
windowsSet->erase(layer);
delete layer;
}
return;
}
} else {
windowsSet = new set<LayersSet*>();
}
JAWT awt;
JAWT_DrawingSurface* ds = NULL;
JAWT_DrawingSurfaceInfo* dsi = NULL;
PIXELFORMATDESCRIPTOR pixFormatDscr;
HGLRC context = NULL;
jboolean result = JNI_FALSE;
jint lock = 0;
JAWT_Win32DrawingSurfaceInfo* dsi_win;
awt.version = (jint)JAWT_VERSION_9;
result = Skiko_GetAWT(env, &awt);
if (result == JNI_FALSE) {
fprintf(stderr, "JAWT_GetAWT failed! Result is JNI_FALSE\n");
return;
}
if (jvm == NULL) {
env->GetJavaVM(&jvm);
}
ds = awt.GetDrawingSurface(env, window);
lock = ds->Lock(ds);
dsi = ds->GetDrawingSurfaceInfo(ds);
// Get the platform-specific drawing info
dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
HWND hwnd = dsi_win->hwnd;
HDC wndHandler = GetDC(hwnd);
if (dsi != NULL)
{
memset(&pixFormatDscr, 0, sizeof(PIXELFORMATDESCRIPTOR));
pixFormatDscr.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pixFormatDscr.nVersion = 1;
pixFormatDscr.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER_DONTCARE;
pixFormatDscr.iPixelType = PFD_TYPE_RGBA;
pixFormatDscr.cColorBits = 32;
int iPixelFormat = ChoosePixelFormat(wndHandler, &pixFormatDscr);
SetPixelFormat(wndHandler, iPixelFormat, &pixFormatDscr);
DescribePixelFormat(wndHandler, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pixFormatDscr);
context = wglCreateContext(wndHandler);
LayersSet* layer = new LayersSet();
windowsSet->insert(layer);
jobject windowRef = env->NewGlobalRef(window);
layer->windowRef = windowRef;
layer->glLayer = hwnd;
layer->context = context;
}
ds->FreeDrawingSurfaceInfo(dsi);
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_redrawLayer(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
layer->update();
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_disposeLayer(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
layer->dispose();
}
}
JNIEXPORT jfloat JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentScale(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
// get scale dpi factor of current monitor
DEVICE_SCALE_FACTOR scaleFactor;
GetScaleFactorForMonitor(MonitorFromWindow(layer->glLayer, MONITOR_DEFAULTTOPRIMARY), &scaleFactor);
return getScaleFloatFromEnum(scaleFactor);
}
return 1.0f;
}
} // extern "C"
\ No newline at end of file
package org.jetbrains.skiko
interface Drawable {
fun redrawLayer()
fun updateLayer()
fun disposeLayer()
val contentScale: Float
}
\ No newline at end of file
package org.jetbrains.skiko
class Engine private constructor() {
private var api = GraphicsApi.UNKNOWN
fun currentApi(): GraphicsApi {
return api
}
/**
* GAG - At the moment always initializes OpenGL.
* @param osType The current OS type of the device.
*/
private fun initSuitableGraphicsApi(osType: OSType) {
when {
osType === OSType.MAC_OS -> {
OpenGLApi.get() // for Metal
instance.api = GraphicsApi.OPENGL
}
osType === OSType.LINUX -> {
OpenGLApi.get() // for Vulkan and OpenGL
instance.api = GraphicsApi.OPENGL
}
osType === OSType.WINDOWS -> {
OpenGLApi.get() // for Vulkan and OpenGL
instance.api = GraphicsApi.OPENGL
}
else -> {
OpenGLApi.get() // default
instance.api = GraphicsApi.OPENGL
}
}
}
fun render(drawable: Drawable) {
drawable.updateLayer()
drawable.redrawLayer()
}
companion object {
private lateinit var instance: Engine
fun get(): Engine {
if (!this::instance.isInitialized) {
instance = Engine()
instance.initSuitableGraphicsApi(OSType.getCurrent())
}
return instance
}
}
}
package org.jetbrains.skiko
enum class GraphicsApi {
UNKNOWN, OPENGL, VULKAN, METAL
}
\ No newline at end of file
package org.jetbrains.skiko
import java.awt.Graphics
import java.awt.Canvas
open class HardwareLayer : Canvas(), Drawable {
override fun paint(g: Graphics) {
Engine.get().render(this)
}
open fun draw() {}
external override fun redrawLayer()
external override fun updateLayer()
external override fun disposeLayer()
override val contentScale: Float
external get
}
\ No newline at end of file
......@@ -16,19 +16,19 @@ object Library {
val libFileName = "$libPrefix$name$libExtension"
val url = Library::class.java.getResource(resourcePath + libFileName)
// println("Loading " + url);
val libFile = when {
url == null -> error("Library $libFileName is not found in $resourcePath")
url.protocol == "file" -> File(url.toURI())
else -> url.openStream().use { input ->
val tempRoot = File(System.getProperty("java.io.tmpdir"))
val tempDir = tempRoot.resolve("skija_" + System.nanoTime()).apply { mkdirs() }
val tempDir = tempRoot.resolve("skiko_" + System.nanoTime()).apply { mkdirs() }
File(tempDir, libFileName).also {
it.deleteOnExit()
Files.copy(input, it.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
}
//println("Loading $url from ${libFile.absolutePath}")
System.load(libFile.absolutePath)
loaded = true
}
......
......@@ -4,7 +4,17 @@ import org.jetbrains.skija.Library
import org.jetbrains.skija.Surface
fun main() {
println(SkiaWindow().nativeMethod(41))
println(SkiaLibraryTester().nativeMethod(41))
val surface = Surface.makeRasterN32Premul(100, 100)
println(surface)
}
private class SkiaLibraryTester {
companion object {
init {
Library.load("/", "skiko")
}
}
external fun nativeMethod(param: Long): Long
}
\ No newline at end of file
package org.jetbrains.skiko
enum class OSType {
MAC_OS, LINUX, WINDOWS, UNKNOWN;
companion object {
private lateinit var currentOSType: OSType
fun getCurrent(): OSType {
if (!this::currentOSType.isInitialized) {
currentOSType = defineOsType()
}
return currentOSType
}
private fun defineOsType(): OSType {
val osName = System.getProperty("os.name").toLowerCase()
if (isWindows(osName)) {
return WINDOWS
}
if (isMac(osName)) {
return MAC_OS
}
return if (isUnix(osName)) {
LINUX
} else UNKNOWN
}
private fun isWindows(name: String): Boolean {
return name.indexOf("win") >= 0
}
private fun isMac(name: String): Boolean {
return name.indexOf("mac") >= 0
}
private fun isUnix(name: String): Boolean {
return name.indexOf("nix") >= 0 || name.indexOf("nux") >= 0 || name.indexOf("aix") >= 0
}
}
}
\ No newline at end of file
package org.jetbrains.skiko
/**
* The minimum required OpenGL constants and functions for Skia to work.
* PS. In further development we should find a common pattern of using OpenGL,
* Vulkan, Metal and implement common interface to all graphics APIs.
*/
class OpenGLApi private constructor() {
// OpenGL constants
val GL_TEXTURE_2D = 0x0DE1
val GL_TEXTURE_BINDING_2D = 0x8069
val GL_DRAW_FRAMEBUFFER_BINDING = 0x8CA6
val GL_COLOR_BUFFER_BIT = 0x00004000
// OpenGL functions
external fun glViewport(x: Int, y: Int, width: Int, height: Int)
external fun glClearColor(r: Float, g: Float, b: Float, a: Float)
external fun glClear(flags: Int)
external fun glFinish()
external fun glEnable(flag: Int)
external fun glBindTexture(target: Int, texture: Int)
external fun glGetIntegerv(pname: Int): Int
companion object {
private lateinit var instance: OpenGLApi
fun get(): OpenGLApi {
if (!this::instance.isInitialized) {
instance = OpenGLApi()
}
return instance
}
}
}
\ No newline at end of file
package org.jetbrains.skiko
class SkiaWindow {
companion object {
init {
Library.load("/", "skiko")
import java.awt.event.ComponentAdapter
import java.awt.event.ComponentEvent
import javax.swing.JFrame
import org.jetbrains.skija.BackendRenderTarget
import org.jetbrains.skija.Canvas
import org.jetbrains.skija.ColorSpace
import org.jetbrains.skija.Context
import org.jetbrains.skija.FramebufferFormat
import org.jetbrains.skija.Surface
import org.jetbrains.skija.SurfaceColorFormat
import org.jetbrains.skija.SurfaceOrigin
private class SkijaState {
var context: Context? = null
var renderTarget: BackendRenderTarget? = null
var surface: Surface? = null
var canvas: Canvas? = null
fun clear() {
surface?.close()
renderTarget?.close()
}
}
}
external fun nativeMethod(param: Long): Long
interface SkiaRenderer {
fun onInit()
fun onRender(canvas: Canvas, width: Int, height: Int)
fun onReshape(width: Int, height: Int)
fun onDispose()
}
class SkiaLayer() : HardwareLayer() {
var renderer: SkiaRenderer? = null
private val skijaState = SkijaState()
var init: Boolean = false
public override fun draw() {
if (!init) {
if (skijaState.context == null) {
skijaState.context = Context.makeGL()
}
initSkija()
init = true
}
skijaState.apply {
val gl = OpenGLApi.get()
gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
canvas!!.clear(0xFFFFFFF)
renderer!!.onRender(canvas!!, width, height)
context!!.flush()
gl.glFinish()
}
}
private fun initSkija() {
val dpi = contentScale
skijaState.clear()
val gl: OpenGLApi = OpenGLApi.get()
val fbId = gl.glGetIntegerv(gl.GL_DRAW_FRAMEBUFFER_BINDING)
skijaState.renderTarget = BackendRenderTarget.makeGL(
(width * dpi).toInt(),
(height * dpi).toInt(),
0,
8,
fbId,
FramebufferFormat.GR_GL_RGBA8
)
skijaState.surface = Surface.makeFromBackendRenderTarget(
skijaState.context,
skijaState.renderTarget,
SurfaceOrigin.BOTTOM_LEFT,
SurfaceColorFormat.RGBA_8888,
ColorSpace.getSRGB()
)
skijaState.canvas = skijaState.surface!!.canvas
skijaState.canvas!!.scale(dpi, dpi)
}
}
class SkiaWindow() : JFrame() {
companion object {
init {
Library.load("/", "skiko")
}
}
val layer: SkiaLayer
init {
layer = SkiaLayer()
setLayout(null);
add(layer)
addComponentListener(object : ComponentAdapter() {
public override fun componentResized(e: ComponentEvent) {
layer.init = false
layer.setSize(width, height)
Engine.get().render(layer)
}
})
}
}
#import "jawt.h"
#import "jawt_md.h"
#define GL_SILENCE_DEPRECATION
#import <Cocoa/Cocoa.h>
#import <QuartzCore/QuartzCore.h>
#import <OpenGL/gl3.h>
@interface AWTGLLayer : CAOpenGLLayer
@property jobject windowRef;
@property JavaVM *jvm;
@property bool isDrawing;
@end
@implementation AWTGLLayer
jobject windowRef;
JavaVM *jvm;
- (id)init
{
self = [super init];
if (self)
{
self.needsDisplayOnBoundsChange = YES;
self.asynchronous = NO;
self.windowRef = NULL;
self.jvm = NULL;
self.isDrawing = NO;
}
return self;
}
-(void)drawInCGLContext:(CGLContextObj)ctx
pixelFormat:(CGLPixelFormatObj)pf
forLayerTime:(CFTimeInterval)t
displayTime:(const CVTimeStamp *)ts
{
if (self.isDrawing) return;
self.isDrawing = YES;
CGLSetCurrentContext(ctx);
if (self.jvm != NULL) {
JNIEnv *env;
(*self.jvm)->AttachCurrentThread(self.jvm, (void **)&env, NULL);
jclass wndClass = (*env)->GetObjectClass(env, self.windowRef);
jmethodID drawMethod = (*env)->GetMethodID(env, wndClass, "draw", "()V");
if (NULL == drawMethod) {
NSLog(@"The method Window.draw() not found!");
return;
}
(*env)->CallVoidMethod(env, self.windowRef, drawMethod);
}
[super drawInCGLContext:ctx pixelFormat:pf forLayerTime:t displayTime:ts];
self.isDrawing = NO;
}
- (void) dispose
{
self.windowRef = NULL;
self.jvm = NULL;
}
@end
@interface LayersSet : NSObject
@property jobject windowRef;
@property (retain, strong) CALayer *caLayer;
@property (retain, strong) AWTGLLayer *glLayer;
@end
@implementation LayersSet
jobject windowRef;
CALayer *caLayer;
AWTGLLayer *glLayer;
- (id)init
{
self = [super init];
if (self)
{
self.windowRef = NULL;
self.caLayer = NULL;
self.glLayer = NULL;
}
return self;
}
- (void) syncSize
{
self.glLayer.bounds = self.caLayer.bounds;
self.glLayer.frame = self.caLayer.frame;
}
- (void) update
{
[self.glLayer performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:0 waitUntilDone:NO];
[self.glLayer performSelectorOnMainThread:@selector(displayIfNeeded) withObject:0 waitUntilDone:NO];
}
- (void) dispose
{
[self.glLayer dispose];
self.glLayer = NULL;
self.caLayer = NULL;
}
@end
NSMutableSet *windowsSet = nil;
LayersSet * findByObject(JNIEnv *env, jobject object) {
for (LayersSet* value in windowsSet) {
if ((*env)->IsSameObject(env, object, value.windowRef) == JNI_TRUE) {
return value;
}
}
NSLog(@"The set does not contain this window.");
return NULL;
}
extern jboolean Skiko_GetAWT(JNIEnv* env, JAWT* awt);
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_updateLayer(JNIEnv *env, jobject window)
{
if (windowsSet != nil) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
if (layer.caLayer == NULL && layer.glLayer == NULL) {
(*env)->DeleteGlobalRef(env, layer.windowRef);
layer.windowRef = NULL;
[windowsSet removeObject: layer];
return;
}
[layer syncSize];
return;
}
} else {
windowsSet = [[NSMutableSet alloc] init];
}
JAWT awt;
JAWT_DrawingSurface *ds = NULL;
JAWT_DrawingSurfaceInfo *dsi = NULL;
CGLPixelFormatObj pixFormatObj = NULL;
CGLContextObj context;
jboolean result = JNI_FALSE;
jint lock = 0;
NSObject<JAWT_SurfaceLayers>* dsi_mac = NULL;
awt.version = JAWT_VERSION_9;
result = Skiko_GetAWT(env, &awt);
assert(result != JNI_FALSE);
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
ds = awt.GetDrawingSurface(env, window);
assert(ds != NULL);
lock = ds->Lock(ds);
assert((lock & JAWT_LOCK_ERROR) == 0);
dsi = ds->GetDrawingSurfaceInfo(ds);
if (dsi != NULL)
{
dsi_mac = ( __bridge NSObject<JAWT_SurfaceLayers> *) dsi->platformInfo;
LayersSet *layersSet = [[LayersSet alloc] init];
[windowsSet addObject: layersSet];
layersSet.caLayer = [dsi_mac windowLayer];
NSScreen *screen = [NSScreen mainScreen];
float scaleFactor = screen.backingScaleFactor;
layersSet.caLayer.contentsScale = scaleFactor;
layersSet.glLayer = [AWTGLLayer new];
[layersSet.caLayer addSublayer: layersSet.glLayer];
layersSet.glLayer.backgroundColor = CGColorCreateSRGB(1.0f, 1.0f, 1.0f, 1.0f);
layersSet.glLayer.contentsScale = scaleFactor;
jobject windowRef = (*env)->NewGlobalRef(env, window);
[layersSet.glLayer setJvm: jvm];
[layersSet.glLayer setWindowRef: windowRef];
[layersSet setWindowRef: windowRef];
ds->FreeDrawingSurfaceInfo(dsi);
ds->Unlock(ds);
awt.FreeDrawingSurface(ds);
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_redrawLayer(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
[layer update];
}
}
JNIEXPORT void JNICALL Java_org_jetbrains_skiko_HardwareLayer_disposeLayer(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
[layer dispose];
}
}
JNIEXPORT jfloat JNICALL Java_org_jetbrains_skiko_HardwareLayer_getContentScale(JNIEnv *env, jobject window) {
LayersSet *layer = findByObject(env, window);
if (layer != NULL) {
return [layer.glLayer contentsScale];
}
return 1.0f;
}
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