Unverified Commit 8aff350d authored by Aleksandr Veselov's avatar Aleksandr Veselov Committed by GitHub

Implement Shaper::shape with RunHandler (#415)

parent 7ce02618
......@@ -45,10 +45,16 @@ expect class InteropScope() {
// Callbacks
fun callback(callback: (() -> Unit)?): InteropPointer
fun intCallback(callback: (() -> Int)?): InteropPointer
fun nativePointerCallback(callback: (() -> NativePointer)?): InteropPointer
fun interopPointerCallback(callback: (() -> InteropPointer)?): InteropPointer
fun booleanCallback(callback: (() -> Boolean)?): InteropPointer
// Virtual methods
fun virtual(method: () -> Unit): InteropPointer
fun virtualInt(method: () -> Int): InteropPointer
fun virtualNativePointer(method: () -> NativePointer): InteropPointer
fun virtualInteropPointer(method: () -> InteropPointer): InteropPointer
fun virtualBoolean(method: () -> Boolean): InteropPointer
fun release()
......
@file:Suppress("NESTED_EXTERNAL_DECLARATION")
package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.*
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.Font
import org.jetbrains.skia.FontMgr
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.reachabilityBarrier
class FontMgrRunIterator(text: ManagedString?, manageText: Boolean, font: Font?, opts: ShapingOptions) :
ManagedRunIterator<FontRun?>(
_nMake(getPtr(text), getPtr(font), opts), text, manageText
_nMake(getPtr(text), getPtr(font), getPtr(opts.fontMgr), opts._booleanPropsToInt()), text, manageText
) {
companion object {
init {
......@@ -19,6 +21,9 @@ class FontMgrRunIterator(text: ManagedString?, manageText: Boolean, font: Font?,
}
}
private val _font: Font? = font
private val _fontMgr: FontMgr? = opts.fontMgr
constructor(text: String?, font: Font?, opts: ShapingOptions) : this(ManagedString(text), true, font, opts)
constructor(text: String?, font: Font?) : this(ManagedString(text), true, font, ShapingOptions.DEFAULT)
......@@ -43,8 +48,8 @@ class FontMgrRunIterator(text: ManagedString?, manageText: Boolean, font: Font?,
}
}
@ExternalSymbolName("org_jetbrains_skia_FontMgrRunIterator__1nMake")
private external fun _nMake(textPtr: NativePointer, fontPtr: NativePointer, opts: ShapingOptions?): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_FontMgrRunIterator__1nMake")
private external fun _nMake(textPtr: NativePointer, fontPtr: NativePointer, fontMgrPtr: NativePointer, optsBooleanProps: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_FontMgrRunIterator__1nGetCurrentFont")
@ExternalSymbolName("org_jetbrains_skia_shaper_FontMgrRunIterator__1nGetCurrentFont")
private external fun _nGetCurrentFont(ptr: NativePointer): NativePointer
@file:Suppress("NESTED_EXTERNAL_DECLARATION")
package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.reachabilityBarrier
class HbIcuScriptRunIterator(text: ManagedString?, manageText: Boolean) : ManagedRunIterator<ScriptRun?>(
_nMake(
......@@ -42,8 +42,8 @@ class HbIcuScriptRunIterator(text: ManagedString?, manageText: Boolean) : Manage
}
@ExternalSymbolName("org_jetbrains_skia_HbIcuScriptRunIterator__1nMake")
@ExternalSymbolName("org_jetbrains_skia_shaper_HbIcuScriptRunIterator__1nMake")
private external fun _nMake(textPtr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_HbIcuScriptRunIterator__1nGetCurrentScriptTag")
@ExternalSymbolName("org_jetbrains_skia_shaper_HbIcuScriptRunIterator__1nGetCurrentScriptTag")
private external fun _nGetCurrentScriptTag(ptr: NativePointer): Int
@file:Suppress("NESTED_EXTERNAL_DECLARATION")
package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.reachabilityBarrier
class IcuBidiRunIterator(text: ManagedString?, manageText: Boolean, bidiLevel: Int) : ManagedRunIterator<BidiRun?>(
_nMake(getPtr(text), bidiLevel), text, manageText
......@@ -39,8 +39,8 @@ class IcuBidiRunIterator(text: ManagedString?, manageText: Boolean, bidiLevel: I
}
}
@ExternalSymbolName("org_jetbrains_skia_IcuBidiRunIterator__1nMake")
@ExternalSymbolName("org_jetbrains_skia_shaper_IcuBidiRunIterator__1nMake")
private external fun _nMake(textPtr: NativePointer, bidiLevel: Int): NativePointer
@ExternalSymbolName("org_jetbrains_skia_IcuBidiRunIterator__1nGetCurrentLevel")
@ExternalSymbolName("org_jetbrains_skia_shaper_IcuBidiRunIterator__1nGetCurrentLevel")
private external fun _nGetCurrentLevel(ptr: NativePointer): Int
@file:Suppress("NESTED_EXTERNAL_DECLARATION")
package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.impl.Managed
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.impl.NativePointer
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.reachabilityBarrier
abstract class ManagedRunIterator<T> internal constructor(
ptr: NativePointer,
......@@ -53,14 +53,14 @@ abstract class ManagedRunIterator<T> internal constructor(
}
@ExternalSymbolName("org_jetbrains_skia_ManagedRunIterator__1nGetFinalizer")
@ExternalSymbolName("org_jetbrains_skia_shaper_ManagedRunIterator__1nGetFinalizer")
private external fun _nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_ManagedRunIterator__1nConsume")
@ExternalSymbolName("org_jetbrains_skia_shaper_ManagedRunIterator__1nConsume")
internal external fun _nConsume(ptr: NativePointer)
@ExternalSymbolName("org_jetbrains_skia_ManagedRunIterator__1nGetEndOfCurrentRun")
@ExternalSymbolName("org_jetbrains_skia_shaper_ManagedRunIterator__1nGetEndOfCurrentRun")
private external fun _nGetEndOfCurrentRun(ptr: NativePointer, textPtr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_ManagedRunIterator__1nIsAtEnd")
@ExternalSymbolName("org_jetbrains_skia_shaper_ManagedRunIterator__1nIsAtEnd")
private external fun _nIsAtEnd(ptr: NativePointer): Boolean
package org.jetbrains.skia.shaper
import org.jetbrains.skia.*
import org.jetbrains.skia.Font
import org.jetbrains.skia.Point
import org.jetbrains.skia.impl.Native
import org.jetbrains.skia.impl.NativePointer
class RunInfo(
var _fontPtr: NativePointer, val bidiLevel: Int, val advanceX: Float, val advanceY: Float, val glyphCount: NativePointer,
var _fontPtr: NativePointer, val bidiLevel: Int, val advanceX: Float, val advanceY: Float, val glyphCount: Int,
/**
* WARN does not work in Shaper.makeCoreText https://bugs.chromium.org/p/skia/issues/detail?id=10899
*/
......
@file:Suppress("NESTED_EXTERNAL_DECLARATION")
package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.*
import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.FontFeature.Companion.arrayOfFontFeaturesToInterop
import org.jetbrains.skia.impl.*
import org.jetbrains.skia.impl.Library.Companion.staticLoad
/**
* Shapes text using HarfBuzz and places the shaped text into a
......@@ -185,21 +184,7 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH
runHandler: RunHandler
): Shaper {
Stats.onNativeCall()
interopScope {
_nShape(
_ptr,
getPtr(textUtf8),
fontIter,
bidiIter,
scriptIter,
langIter,
optsFeaturesLen = opts.features?.size ?: 0,
optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt(),
width = width,
runHandler = runHandler
)
}
doShape(textUtf8, fontIter, bidiIter, scriptIter, langIter, opts, width, runHandler)
return this
}
......@@ -233,6 +218,17 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH
}
}
internal expect fun Shaper.doShape(
textUtf8: ManagedString,
fontIter: Iterator<FontRun?>,
bidiIter: Iterator<BidiRun?>,
scriptIter: Iterator<ScriptRun?>,
langIter: Iterator<LanguageRun?>,
opts: ShapingOptions,
width: Float,
runHandler: RunHandler
)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nGetFinalizer")
private external fun Shaper_nGetFinalizer(): NativePointer
......@@ -280,16 +276,65 @@ private external fun _nShapeLine(
): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShape")
private external fun _nShape(
internal external fun Shaper_nShape(
ptr: NativePointer,
textPtr: NativePointer,
fontIter: Iterator<FontRun?>?,
bidiIter: Iterator<BidiRun?>?,
scriptIter: Iterator<ScriptRun?>?,
langIter: Iterator<LanguageRun?>?,
fontIter: InteropPointer,
bidiIter: InteropPointer,
scriptIter: InteropPointer,
langIter: InteropPointer,
optsFeaturesLen: Int,
optsFeaturesIntArray: InteropPointer,
optsBooleanProps: Int,
width: Float,
runHandler: RunHandler?
runHandler: InteropPointer
)
// Native/JS only
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunIterator_1nGetFinalizer")
internal external fun RunIterator_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunIterator_1nCreateRunIterator")
internal external fun RunIterator_nCreateRunIterator(type: Int, textPtr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunIterator_1nInitRunIterator")
internal external fun RunIterator_nInitRunIterator(
ptr: NativePointer,
type: Int,
onConsume: InteropPointer,
onEndOfCurrentRun: InteropPointer,
onAtEnd: InteropPointer,
onCurrent: InteropPointer
)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nCreate")
internal external fun RunHandler_nCreate(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetFinalizer")
internal external fun RunHandler_nGetFinalizer(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nInit")
internal external fun RunHandler_nInit(
ptr: NativePointer,
onBeginLine: InteropPointer,
onRunInfo: InteropPointer,
onCommitRunInfo: InteropPointer,
onRunOffset: InteropPointer,
onCommitRun: InteropPointer,
onCommitLine: InteropPointer
)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetGlyphs")
internal external fun RunHandler_nGetGlyphs(ptr: NativePointer, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetClusters")
internal external fun RunHandler_nGetClusters(ptr: NativePointer, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetPositions")
internal external fun RunHandler_nGetPositions(ptr: NativePointer, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nSetOffset")
internal external fun RunHandler_nSetOffset(ptr: NativePointer, x: Float, y: Float)
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetRunInfo")
internal external fun RunHandler_nGetRunInfo(ptr: NativePointer, result: InteropPointer): NativePointer
package org.jetbrains.skia
import org.jetbrains.skia.shaper.RunHandler
import org.jetbrains.skia.shaper.RunInfo
import org.jetbrains.skia.shaper.Shaper
import org.jetbrains.skia.shaper.ShapingOptions
import org.jetbrains.skia.tests.assertContentCloseEnough
import org.jetbrains.skia.tests.makeFromResource
import org.jetbrains.skiko.tests.SkipJsTarget
import org.jetbrains.skiko.tests.SkipNativeTarget
import org.jetbrains.skiko.tests.runTest
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.*
class ShaperTest {
......@@ -45,4 +44,90 @@ class ShaperTest {
)
}
@Test
fun canShapeWithRunHandler() = runTest {
val callCount = object {
var beginLine = 0
var runInfo = 0
var commitRunInfo = 0
var runOffset = 0
var commitRun = 0
var commitLine = 0
}
data class CommitRunArgs(
val glyphs: ShortArray?,
val positions: Array<Point?>?,
val clusters: IntArray?
)
val commitRuns = mutableListOf<CommitRunArgs>()
val runInfos = mutableListOf<RunInfo?>()
Shaper.make().shape(
text = "text\ntext text\r\ntext",
font = fontInter36(),
opts = ShapingOptions.DEFAULT,
width = 100f,
runHandler = object : RunHandler {
override fun beginLine() {
callCount.beginLine += 1
}
override fun runInfo(info: RunInfo?) {
callCount.runInfo += 1
runInfos.add(info)
}
override fun commitRunInfo() {
callCount.commitRunInfo += 1
}
override fun runOffset(info: RunInfo?): Point {
callCount.runOffset += 1
return Point.ZERO
}
override fun commitRun(
info: RunInfo?,
glyphs: ShortArray?,
positions: Array<Point?>?,
clusters: IntArray?
) {
callCount.commitRun += 1
commitRuns.add(CommitRunArgs(glyphs, positions, clusters))
}
override fun commitLine() {
callCount.commitLine += 1
}
}
)
assertEquals(4, callCount.beginLine)
assertEquals(4, callCount.runInfo)
assertEquals(4, callCount.commitRunInfo)
assertEquals(4, callCount.runOffset)
assertEquals(4, callCount.commitRun)
assertEquals(4, callCount.commitLine)
assertTrue(runInfos[2]!!.advance.x > 80.0f)
assertEquals(10, runInfos[2]?.rangeBegin)
assertEquals(16, runInfos[2]?.rangeEnd)
assertEquals(6, runInfos[2]?.glyphCount)
// For some reasons \r and \n are handled differently on macos and ios, so they are ignored during tests
assertContentEquals(intArrayOf(10, 11, 12, 13, 14, 15), commitRuns[2].clusters)
assertContentEquals(shortArrayOf(882, 611, 943, 882), commitRuns[2].glyphs?.sliceArray(0..3))
@Suppress("UNCHECKED_CAST")
assertContentCloseEnough(arrayOf(
Point(0.0f, 0.0f),
Point(12.795441f, 0.0f),
Point(33.284073f, 0.0f),
Point(52.284073f, 0.0f),
Point(65.28407f, 0.0f)
), commitRuns[2].positions!!.sliceArray(0..4) as Array<Point>, 0.01f)
}
}
package org.jetbrains.skia
import kotlinx.browser.window
actual abstract class OutputStream
actual fun <R> commonSynchronized(lock: Any, block: () -> R) {
......@@ -27,7 +29,13 @@ actual class Matcher constructor(private val regex: Regex, private val input: Ch
actual fun matches(): Boolean = matches
}
actual fun defaultLanguageTag(): String = TODO()
@Suppress("RedundantNullableReturnType")
val LANG: String by lazy {
val lang: String? = window.navigator.language
if (lang == null || lang.isEmpty()) { "en-US" } else { lang }
}
actual fun defaultLanguageTag(): String = LANG
actual fun compilePattern(regex: String): Pattern = Pattern(regex)
......
......@@ -195,14 +195,19 @@ actual class InteropScope actual constructor() {
return toInterop(interopPointers.toIntArray())
}
actual fun booleanCallback(callback: (() -> Boolean)?): InteropPointer {
private fun <T> callbackImpl(callback: (() -> T)?): InteropPointer {
if (callback == null) { return 0 }
initCallbacks()
val data = CallbackData<Boolean>(null)
val data = CallbackData<T>(null)
return _registerCallback({ data.value = callback() }, data, global = false)
}
actual fun booleanCallback(callback: (() -> Boolean)?) = callbackImpl(callback)
actual fun intCallback(callback: (() -> Int)?) = callbackImpl(callback)
actual fun nativePointerCallback(callback: (() -> NativePointer)?) = callbackImpl(callback)
actual fun interopPointerCallback(callback: (() -> InteropPointer)?) = callbackImpl(callback)
actual fun callback(callback: (() -> Unit)?): InteropPointer {
if (callback == null) { return 0 }
initCallbacks()
......@@ -210,14 +215,19 @@ actual class InteropScope actual constructor() {
return _registerCallback({ callback() }, null, global = false)
}
private fun <T> virtualImpl(method: () -> T): InteropPointer {
val data = CallbackData<T>(null)
return _registerCallback({ data.value = method() }, data, global = true)
}
actual fun virtual(method: () -> Unit): InteropPointer {
return _registerCallback({ method() }, null, global = true)
}
actual fun virtualBoolean(method: () -> Boolean): InteropPointer {
val data = CallbackData<Boolean>(null)
return _registerCallback({ data.value = method() }, data, global = true);
}
actual fun virtualBoolean(method: () -> Boolean) = virtualImpl(method)
actual fun virtualInt(method: () -> Int) = virtualImpl(method)
actual fun virtualNativePointer(method: () -> NativePointer) = virtualImpl(method)
actual fun virtualInteropPointer(method: () -> InteropPointer) = virtualImpl(method)
actual fun release() {
elements.forEach {
......
......@@ -6,24 +6,26 @@
#include "SkShaper.h"
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_FontMgrRunIteratorKt__1nMake
(JNIEnv* env, jclass jclass, jlong textPtr, jlong fontPtr, jobject opts) {
(JNIEnv* env, jclass jclass, jlong textPtr, jlong fontPtr, jobject fontMgrPtr, jint optsBooleanProps) {
SkString* text = reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr));
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
jobject fontMgrObj = env->GetObjectField(opts, skija::shaper::ShapingOptions::_fontMgr);
sk_sp<SkFontMgr> fontMgr = fontMgrObj == nullptr
sk_sp<SkFontMgr> fontMgr = fontMgrPtr == nullptr
? SkFontMgr::RefDefault()
: sk_ref_sp(reinterpret_cast<SkFontMgr*>(skija::impl::Native::fromJava(env, fontMgrObj, skija::FontMgr::cls)));
: sk_ref_sp(reinterpret_cast<SkFontMgr*>(skija::impl::Native::fromJava(env, fontMgrPtr, skija::FontMgr::cls)));
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(*text);
if (!graphemeIter) return 0;
bool approximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool approximateSpaces = (optsBooleanProps & 0x02) != 0;
auto instance = new FontRunIterator(
text->c_str(),
text->size(),
*font,
fontMgr,
graphemeIter,
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)
approximateSpaces,
approximatePunctuation
);
return reinterpret_cast<jlong>(instance);
}
......
......@@ -335,7 +335,7 @@ private:
std::vector<jint> fClusters;
};
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShape
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt_Shaper_1nShape
(JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jobject fontRunIterObj, jobject bidiRunIterObj, jobject scriptRunIterObj, jobject languageRunIterObj, jint optsFeaturesLen, jintArray optsFeatures, jint optsBooleanProps, jfloat width, jobject runHandlerObj)
{
SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr));
......
......@@ -113,7 +113,7 @@ namespace skija {
void onLoad(JNIEnv* env) {
jclass local = env->FindClass("org/jetbrains/skia/shaper/RunInfo");
cls = static_cast<jclass>(env->NewGlobalRef(local));
ctor = env->GetMethodID(cls, "<init>", "(JIFFJII)V");
ctor = env->GetMethodID(cls, "<init>", "(JIFFIII)V");
_fontPtr = env->GetFieldID(cls, "_fontPtr", "J");
}
......
......@@ -80,9 +80,15 @@ actual open class InteropScope actual constructor() {
actual fun toInteropForArraysOfPointers(interopPointers: Array<InteropPointer>): InteropPointer = interopPointers
actual fun callback(callback: (() -> Unit)?) = callback as Any?
actual fun intCallback(callback: (() -> Int)?) = callback as Any?
actual fun nativePointerCallback(callback: (() -> NativePointer)?) = callback as Any?
actual fun interopPointerCallback(callback: (() -> InteropPointer)?) = callback as Any?
actual fun booleanCallback(callback: (() -> Boolean)?) = callback as Any?
actual fun virtual(method: () -> Unit) = callback(method)
actual fun virtualInt(method: () -> Int) = intCallback(method)
actual fun virtualNativePointer(method: () -> NativePointer) = nativePointerCallback(method)
actual fun virtualInteropPointer(method: () -> InteropPointer) = interopPointerCallback(method)
actual fun virtualBoolean(method: () -> Boolean) = booleanCallback(method)
actual fun release() {}
......
package org.jetbrains.skia.shaper
import org.jetbrains.skia.FontFeature.Companion.arrayOfFontFeaturesToInterop
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.impl.interopScope
internal actual fun Shaper.doShape(
textUtf8: ManagedString,
fontIter: Iterator<FontRun?>,
bidiIter: Iterator<BidiRun?>,
scriptIter: Iterator<ScriptRun?>,
langIter: Iterator<LanguageRun?>,
opts: ShapingOptions,
width: Float,
runHandler: RunHandler
) {
interopScope {
Shaper_nShape(
_ptr,
getPtr(textUtf8),
fontIter as InteropPointer,
bidiIter as InteropPointer,
scriptIter as InteropPointer,
langIter as InteropPointer,
optsFeaturesLen = opts.features?.size ?: 0,
optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt(),
width = width,
runHandler = runHandler as InteropPointer
)
}
}
\ No newline at end of file
......@@ -167,15 +167,23 @@ static inline KFloat fromBits(KInt i) {
typedef void (*SkikoDisposeCallback)(KInteropPointer);
typedef void (*SkikoCallVoidCallback)(KInteropPointer);
typedef KBoolean (*SkikoCallBooleanCallback)(KInteropPointer);
typedef KInt (*SkikoCallIntCallback)(KInteropPointer);
typedef KNativePointer (*SkikoCallNativePointerCallback)(KInteropPointer);
typedef KInteropPointer (*SkikoCallInteropPointerCallback)(KInteropPointer);
typedef void* KOpaquePointer;
void disposeCallback(KInteropPointer cb);
void callVoidCallback(KInteropPointer cb);
KBoolean callBooleanCallback(KInteropPointer cb);
KInt callIntCallback(KInteropPointer cb);
KNativePointer callNativePointerCallback(KInteropPointer cb);
KInteropPointer callInteropPointerCallback(KInteropPointer cb);
template <typename T, T(*Apply)(KInteropPointer)>
class KCallback {
public:
using return_type = T;
explicit KCallback(KInteropPointer data) : data(data) {}
virtual ~KCallback() {
......@@ -193,7 +201,7 @@ public:
other.data = t;
}
KCallback& operator=(KCallback&& other) {
KCallback& operator=(KCallback&& other) noexcept {
std::swap(data, other.data);
return *this;
}
......@@ -207,6 +215,9 @@ private:
typedef KCallback<KBoolean, callBooleanCallback> KBooleanCallback;
typedef KCallback<void, callVoidCallback> KVoidCallback;
typedef KCallback<KInt, callIntCallback> KIntCallback;
typedef KCallback<KNativePointer, callNativePointerCallback> KNativePointerCallback;
typedef KCallback<KInteropPointer, callInteropPointerCallback> KInteropPointerCallback;
#endif /* SKIKO_COMMON_H */
......@@ -351,6 +351,27 @@ KBoolean callBooleanCallback(KInteropPointer cb) {
return static_cast<KBoolean>(value);
}
KInt callIntCallback(KInteropPointer cb) {
int value = EM_ASM_INT({
return _callCallback($0).value;
}, cb);
return static_cast<KInt>(value);
}
KNativePointer callNativePointerCallback(KInteropPointer cb) {
int value = EM_ASM_INT({
return _callCallback($0).value;
}, cb);
return reinterpret_cast<KNativePointer>(value);
}
KInteropPointer callInteropPointerCallback(KInteropPointer cb) {
int value = EM_ASM_INT({
return _callCallback($0).value;
}, cb);
return reinterpret_cast<KInteropPointer>(value);
}
void callVoidCallback(KInteropPointer cb) {
EM_ASM({
_callCallback($0);
......@@ -362,9 +383,13 @@ void callVoidCallback(KInteropPointer cb) {
static SkikoDisposeCallback disposeCallbackImpl = nullptr;
static SkikoCallBooleanCallback callBooleanCallbackImpl = nullptr;
static SkikoCallVoidCallback callVoidCallbackImpl = nullptr;
static SkikoCallIntCallback callIntCallbackImpl = nullptr;
static SkikoCallNativePointerCallback callNativePointerCallbackImpl = nullptr;
SKIKO_EXPORT void skiko_initCallbacks(KOpaquePointer callBoolean, KOpaquePointer callVoid, KOpaquePointer dispose) {
SKIKO_EXPORT void skiko_initCallbacks(KOpaquePointer callBoolean, KOpaquePointer callInt, KOpaquePointer callNativePointer, KOpaquePointer callVoid, KOpaquePointer dispose) {
callBooleanCallbackImpl = reinterpret_cast<SkikoCallBooleanCallback>(callBoolean);
callIntCallbackImpl = reinterpret_cast<SkikoCallIntCallback>(callInt);
callNativePointerCallbackImpl = reinterpret_cast<SkikoCallNativePointerCallback>(callNativePointer);
callVoidCallbackImpl = reinterpret_cast<SkikoCallVoidCallback>(callVoid);
disposeCallbackImpl = reinterpret_cast<SkikoDisposeCallback>(dispose);
}
......@@ -377,6 +402,18 @@ KBoolean callBooleanCallback(KInteropPointer cb) {
return callBooleanCallbackImpl(cb);
}
KInt callIntCallback(KInteropPointer cb) {
return callIntCallbackImpl(cb);
}
KNativePointer callNativePointerCallback(KInteropPointer cb) {
return callNativePointerCallbackImpl(cb);
}
KInteropPointer callInteropPointerCallback(KInteropPointer cb) {
return callNativePointerCallbackImpl(cb);
}
void callVoidCallback(KInteropPointer cb) {
return callVoidCallbackImpl(cb);
}
......
......@@ -4,14 +4,38 @@
#include "SkFontMgr.h"
#include "SkShaper.h"
#include "common.h"
#include "FontRunIterator.hh"
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_FontMgrRunIterator__1nMake
(KNativePointer textPtr, KNativePointer fontPtr, KInteropPointer opts) {
TODO("implement org_jetbrains_skia_shaper_FontMgrRunIterator__1nMake");
(KNativePointer textPtr, KNativePointer fontPtr, KNativePointer fontMgrPtr, KInt optsBooleanProps) {
SkString* text = reinterpret_cast<SkString*>(textPtr);
SkFont* font = reinterpret_cast<SkFont*>(fontPtr);
sk_sp<SkFontMgr> fontMgr = fontMgrPtr == nullptr
? SkFontMgr::RefDefault()
: sk_ref_sp(reinterpret_cast<SkFontMgr*>(fontMgrPtr));
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(*text);
if (!graphemeIter) return nullptr;
bool approximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool approximateSpaces = (optsBooleanProps & 0x02) != 0;
auto instance = new FontRunIterator(
text->c_str(),
text->size(),
*font,
fontMgr,
graphemeIter,
approximateSpaces,
approximatePunctuation
);
return reinterpret_cast<KNativePointer>(instance);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_FontMgrRunIterator__1nGetCurrentFont
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_shaper_FontMgrRunIterator__1nGetCurrentFont");
SkShaper::FontRunIterator* instance = reinterpret_cast<SkShaper::FontRunIterator*>(ptr);
SkFont* font = new SkFont(instance->currentFont());
return reinterpret_cast<KNativePointer>(font);
}
......@@ -144,7 +144,311 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeLine
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper__1nShape
(KNativePointer ptr, KNativePointer textPtr, KInteropPointer fontRunIterObj, KInteropPointer bidiRunIterObj, KInteropPointer scriptRunIterObj, KInteropPointer languageRunIterObj, KInteropPointer opts, KFloat width, KInteropPointer runHandlerObj)
(KNativePointer ptr, KNativePointer textPtr, KInteropPointer fontRunIterObj, KInteropPointer bidiRunIterObj, KInteropPointer scriptRunIterObj, KInteropPointer languageRunIterObj, KInt optsFeaturesLen, KInt* optsFeatures, KInt optsBooleanProps, KFloat width, KInteropPointer runHandlerObj)
{
TODO("implement org_jetbrains_skia_shaper_Shaper__1nShape");
auto* instance = reinterpret_cast<SkShaper*>(ptr);
SkString& text = *(reinterpret_cast<SkString*>(textPtr));
if (text.size() == 0) {
return;
}
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(optsFeatures, optsFeaturesLen);
auto* fontRunIter = reinterpret_cast<SkShaper::FontRunIterator*>(fontRunIterObj);
auto* languageRunIter = reinterpret_cast<SkShaper::LanguageRunIterator*>(languageRunIterObj);
auto* scriptRunIter = reinterpret_cast<SkShaper::ScriptRunIterator*>(scriptRunIterObj);
auto* bidiRunIter = reinterpret_cast<SkShaper::BiDiRunIterator*>(bidiRunIterObj);
auto* runHandler = reinterpret_cast<SkShaper::RunHandler*>(runHandlerObj);
instance->shape(text.c_str(), text.size(), *fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), width, runHandler);
}
template<typename T, typename CurrentCallback>
class SkikoRunIterator: public T {
static_assert(std::is_base_of<SkShaper::RunIterator, T>::value, "");
public:
SkikoRunIterator(const SkString& text) :
_onCurrent(nullptr),
_onAtEnd(nullptr),
_onEndOfCurrentRun(nullptr),
_onConsume(nullptr),
_indicesConverter(text) {}
void consume() override {
_onConsume();
_endOfCurrentRun = _indicesConverter.from16To8(_onEndOfCurrentRun());
}
size_t endOfCurrentRun() const override {
return _endOfCurrentRun;
}
bool atEnd() const override {
return static_cast<bool>(_onAtEnd());
}
void init(KInteropPointer onConsume, KInteropPointer onEndOfCurrentRun, KInteropPointer onAtEnd, KInteropPointer onCurrent) {
_onConsume = KVoidCallback(onConsume);
_onEndOfCurrentRun = KIntCallback(onEndOfCurrentRun);
_onAtEnd = KBooleanCallback(onAtEnd);
_onCurrent = CurrentCallback(onCurrent);
}
protected:
CurrentCallback _onCurrent;
typename CurrentCallback::return_type current() const {
auto current = _onCurrent();
return current;
}
private:
KVoidCallback _onConsume;
KIntCallback _onEndOfCurrentRun;
KBooleanCallback _onAtEnd;
skija::UtfIndicesConverter _indicesConverter;
size_t _endOfCurrentRun = 0;
};
class SkikoFontRunIterator : public SkikoRunIterator<SkShaper::FontRunIterator, KNativePointerCallback> {
public:
SkikoFontRunIterator(const SkString& text) : SkikoRunIterator<SkShaper::FontRunIterator, KNativePointerCallback>(text) {}
const SkFont& currentFont() const override {
return *reinterpret_cast<SkFont*>(current());
}
};
class SkikoBiDiRunIterator : public SkikoRunIterator<SkShaper::BiDiRunIterator, KIntCallback> {
public:
SkikoBiDiRunIterator(const SkString& text) : SkikoRunIterator<SkShaper::BiDiRunIterator, KIntCallback>(text) {}
uint8_t currentLevel() const override {
return static_cast<uint8_t>(current());
}
};
class SkikoScriptRunIterator : public SkikoRunIterator<SkShaper::ScriptRunIterator, KIntCallback> {
public:
SkikoScriptRunIterator(const SkString& text) : SkikoRunIterator<SkShaper::ScriptRunIterator, KIntCallback>(text) {}
SkFourByteTag currentScript() const override {
return static_cast<SkFourByteTag>(current());
}
};
class SkikoLanguageRunIterator : public SkikoRunIterator<SkShaper::LanguageRunIterator, KInteropPointerCallback> {
public:
SkikoLanguageRunIterator(const SkString& text) : SkikoRunIterator<SkShaper::LanguageRunIterator, KInteropPointerCallback>(text) {}
const char* currentLanguage() const override {
return reinterpret_cast<char *>(current());
}
};
static void deleteRunIterator(SkShaper::RunIterator* runIterator) {
delete runIterator;
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper_RunIterator_1nGetFinalizer() {
return reinterpret_cast<KNativePointer>(deleteRunIterator);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper_RunIterator_1nCreateRunIterator(KInt type, KNativePointer textPtr) {
const auto& text = *(reinterpret_cast<SkString*>(textPtr));
switch (type) {
case 1: // FontRunIterator
return reinterpret_cast<KNativePointer>(new SkikoFontRunIterator(text));
case 2: // BiDiRunIterator
return reinterpret_cast<KNativePointer>(new SkikoBiDiRunIterator(text));
case 3: // ScriptRunIterator
return reinterpret_cast<KNativePointer>(new SkikoScriptRunIterator(text));
case 4: // LanguageRunIterator
return reinterpret_cast<KNativePointer>(new SkikoLanguageRunIterator(text));
default:
TODO("unsupported run iterator type");
}
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunIterator_1nInitRunIterator
(KNativePointer ptr, KInt type, KInteropPointer onConsume, KInteropPointer onEndOfCurrentRun, KInteropPointer onAtEnd, KInteropPointer onCurrent) {
switch (type) {
case 1: {// FontRunIterator
auto *runIter = reinterpret_cast<SkikoFontRunIterator *>(ptr);
runIter->init(onConsume, onEndOfCurrentRun, onAtEnd, onCurrent);
break;
}
case 2: {// BiDiRunIterator
auto *runIter = reinterpret_cast<SkikoBiDiRunIterator *>(ptr);
runIter->init(onConsume, onEndOfCurrentRun, onAtEnd, onCurrent);
break;
}
case 3: {// ScriptRunIterator
auto *runIter = reinterpret_cast<SkikoScriptRunIterator *>(ptr);
runIter->init(onConsume, onEndOfCurrentRun, onAtEnd, onCurrent);
break;
}
case 4: {// LanguageRunIterator
auto *runIter = reinterpret_cast<SkikoLanguageRunIterator *>(ptr);
runIter->init(onConsume, onEndOfCurrentRun, onAtEnd, onCurrent);
break;
}
default:
TODO("unsupported run iterator type");
}
}
// Run Handler
class SkikoRunHandler : public SkShaper::RunHandler {
public:
SkikoRunHandler() :
_onBeginLine(nullptr),
_onRunInfo(nullptr),
_onCommitRunInfo(nullptr),
_onRunOffset(nullptr),
_onCommitRun(nullptr),
_onCommitLine(nullptr),
_runInfo(nullptr) {}
void init(
KInteropPointer onBeginLine,
KInteropPointer onRunInfo,
KInteropPointer onCommitRunInfo,
KInteropPointer onRunOffset,
KInteropPointer onCommitRun,
KInteropPointer onCommitLine
) {
_onBeginLine = KVoidCallback(onBeginLine);
_onRunInfo = KVoidCallback(onRunInfo);
_onCommitRunInfo = KVoidCallback(onCommitRunInfo);
_onRunOffset = KVoidCallback(onRunOffset);
_onCommitRun = KVoidCallback(onCommitRun);
_onCommitLine = KVoidCallback(onCommitLine);
}
void beginLine() override {
_onBeginLine();
}
void runInfo(const RunInfo& info) override {
_runInfo = &info;
_onRunInfo();
_runInfo = nullptr;
}
void commitRunInfo() override {
_onCommitRunInfo();
}
Buffer runBuffer(const RunInfo& info) override {
_glyphs = std::vector<SkGlyphID>(info.glyphCount);
_positions = std::vector<SkPoint>(info.glyphCount);
_clusters = std::vector<uint32_t>(info.glyphCount);
_runInfo = &info;
_onRunOffset();
_runInfo = nullptr;
return {
_glyphs.data(),
_positions.data(),
nullptr,
_clusters.data(),
_point
};
}
void commitRunBuffer(const RunInfo& info) override {
_runInfo = &info;
_onCommitRun();
_runInfo = nullptr;
}
virtual void commitLine() override {
_onCommitLine();
}
void setOffset(KFloat x, KFloat y) {
_point = { x, y };
}
const std::vector<SkGlyphID>& glyphs() const { return _glyphs; }
const std::vector<SkPoint>& positions() const { return _positions; }
const std::vector<uint32_t>& clusters() const { return _clusters; }
const RunInfo& runInfo() const { return *_runInfo; }
private:
KVoidCallback _onBeginLine;
KVoidCallback _onRunInfo;
KVoidCallback _onCommitRunInfo;
KVoidCallback _onRunOffset;
KVoidCallback _onCommitRun;
KVoidCallback _onCommitLine;
SkPoint _point;
std::vector<SkGlyphID> _glyphs;
std::vector<SkPoint> _positions;
std::vector<uint32_t> _clusters;
const RunInfo* _runInfo;
};
static void deleteRunHandler(SkikoRunHandler* runHandler) {
delete runHandler;
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetFinalizer() {
return reinterpret_cast<KNativePointer>(deleteRunHandler);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetRunInfo
(KNativePointer ptr, KInteropPointer fields) {
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
KInt* repr = reinterpret_cast<KInt*>(fields);
repr[0] = instance->runInfo().fBidiLevel;
repr[1] = rawBits(instance->runInfo().fAdvance.x());
repr[2] = rawBits(instance->runInfo().fAdvance.y());
repr[3] = instance->runInfo().glyphCount;
repr[4] = instance->runInfo().utf8Range.begin();
repr[5] = instance->runInfo().utf8Range.size();
auto* font = const_cast<SkFont*>(&(instance->runInfo().fFont));
return reinterpret_cast<KNativePointer>(font);
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetGlyphs
(KNativePointer ptr, KInteropPointer result) {
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
auto& src = instance->glyphs();
std::copy(src.cbegin(), src.cend(), reinterpret_cast<SkGlyphID*>(result));
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetPositions
(KNativePointer ptr, KInteropPointer result) {
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
auto& src = instance->positions();
std::copy(src.cbegin(), src.cend(), reinterpret_cast<SkPoint*>(result));
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunHandler_1nGetClusters
(KNativePointer ptr, KInteropPointer result) {
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
auto& src = instance->clusters();
std::copy(src.cbegin(), src.cend(), reinterpret_cast<uint32_t*>(result));
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunHandler_1nSetOffset
(KNativePointer ptr, KFloat x, KFloat y) {
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
instance->setOffset(x, y);
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper_RunHandler_1nCreate() {
return reinterpret_cast<KNativePointer>(new SkikoRunHandler());
}
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper_RunHandler_1nInit
(KNativePointer ptr, KInteropPointer onBeginLine, KInteropPointer onRunInfo, KInteropPointer onCommitRunInfo, KInteropPointer onRunOffset, KInteropPointer onCommitRun, KInteropPointer onCommitLine)
{
auto* instance = reinterpret_cast<SkikoRunHandler*>(ptr);
instance->init(onBeginLine, onRunInfo, onCommitRunInfo, onRunOffset, onCommitRun, onCommitLine);
}
\ No newline at end of file
package org.jetbrains.skia.shaper
import org.jetbrains.skia.FontFeature.Companion.arrayOfFontFeaturesToInterop
import org.jetbrains.skia.ManagedString
import org.jetbrains.skia.Point
import org.jetbrains.skia.impl.*
internal actual fun Shaper.doShape(
textUtf8: ManagedString,
fontIter: Iterator<FontRun?>,
bidiIter: Iterator<BidiRun?>,
scriptIter: Iterator<ScriptRun?>,
langIter: Iterator<LanguageRun?>,
opts: ShapingOptions,
width: Float,
runHandler: RunHandler
) {
val managedFontIter = toManaged(fontIter, textUtf8)
val managedBidiIter = toManaged(bidiIter, textUtf8)
val managedScriptIter = toManaged(scriptIter, textUtf8)
val managedLangIter = toManaged(langIter, textUtf8)
val managedRunHandler = RunHandlerImpl(runHandler)
try {
interopScope {
@Suppress("CAST_NEVER_SUCCEEDS")
Shaper_nShape(
_ptr,
getPtr(textUtf8),
getPtr(managedFontIter) as InteropPointer,
getPtr(managedBidiIter) as InteropPointer,
getPtr(managedScriptIter) as InteropPointer,
getPtr(managedLangIter) as InteropPointer,
optsFeaturesLen = opts.features?.size ?: 0,
optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt(),
width = width,
runHandler = getPtr(managedRunHandler) as InteropPointer
)
}
} finally {
reachabilityBarrier(managedFontIter)
reachabilityBarrier(managedBidiIter)
reachabilityBarrier(managedScriptIter)
reachabilityBarrier(managedLangIter)
reachabilityBarrier(managedRunHandler)
reachabilityBarrier(textUtf8)
}
}
private fun toManaged(iter: Iterator<FontRun?>, text: ManagedString): Managed =
if (iter is ManagedRunIterator<FontRun?>) { iter } else { RunIteratorBase.fromIterator(iter, text) }
private fun toManaged(iter: Iterator<LanguageRun?>, text: ManagedString): Managed =
if (iter is ManagedRunIterator<LanguageRun?>) { iter } else { RunIteratorBase.fromIterator(iter, text) }
private fun toManaged(iter: Iterator<BidiRun?>, text: ManagedString): Managed =
if (iter is ManagedRunIterator<BidiRun?>) { iter } else { RunIteratorBase.fromIterator(iter, text) }
private fun toManaged(iter: Iterator<ScriptRun?>, text: ManagedString): Managed =
if (iter is ManagedRunIterator<ScriptRun?>) { iter } else { RunIteratorBase.fromIterator(iter, text) }
sealed class RunIteratorBase(type: Int, ptr: NativePointer) : Managed(ptr, _FinalizerHolder.PTR) {
private object _FinalizerHolder {
val PTR = RunIterator_nGetFinalizer()
}
abstract fun consume()
abstract fun endOfCurrentRun(): Int
abstract fun atEnd(): Boolean
init {
interopScope {
RunIterator_nInitRunIterator (
_ptr,
type,
onConsume = virtual { this@RunIteratorBase.consume() },
onEndOfCurrentRun = virtualInt { this@RunIteratorBase.endOfCurrentRun() },
onAtEnd = virtualBoolean { this@RunIteratorBase.atEnd() },
onCurrent = getOnCurrentCallback(),
)
}
}
protected abstract fun InteropScope.getOnCurrentCallback(): InteropPointer
companion object {
fun fromIterator(iterator: Iterator<BidiRun?>, text: ManagedString): RunIteratorBase =
IteratorBasedBidiRunIterator(RunIterator_nCreateRunIterator(BIDI_RUN_ITERATOR_TYPE, getPtr(text)), iterator)
fun fromIterator(iterator: Iterator<FontRun?>, text: ManagedString): RunIteratorBase =
IteratorBasedFontRunIterator(RunIterator_nCreateRunIterator(FONT_RUN_ITERATOR_TYPE, getPtr(text)), iterator)
fun fromIterator(iterator: Iterator<ScriptRun?>, text: ManagedString): RunIteratorBase =
IteratorBasedScriptRunIterator(RunIterator_nCreateRunIterator(SCRIPT_RUN_ITERATOR_TYPE, getPtr(text)), iterator)
fun fromIterator(iterator: Iterator<LanguageRun?>, text: ManagedString): RunIteratorBase =
IteratorBasedLanguageRunIterator(RunIterator_nCreateRunIterator(LANGUAGE_RUN_ITERATOR_TYPE, getPtr(text)), iterator)
}
}
private sealed class FontRunIterator(ptr: NativePointer): RunIteratorBase(FONT_RUN_ITERATOR_TYPE, ptr) {
abstract fun currentFont(): FontRun?
override fun InteropScope.getOnCurrentCallback() = virtualNativePointer { getPtr(currentFont()?.font) }
}
private sealed class BiDiRunIterator(ptr: NativePointer): RunIteratorBase(BIDI_RUN_ITERATOR_TYPE, ptr) {
abstract fun currentLevel(): BidiRun?
override fun InteropScope.getOnCurrentCallback() = virtualInt { currentLevel()?.level ?: 0 }
}
private sealed class ScriptRunIterator(ptr: NativePointer): RunIteratorBase(SCRIPT_RUN_ITERATOR_TYPE, ptr) {
abstract fun currentScript(): ScriptRun?
override fun InteropScope.getOnCurrentCallback() = virtualInt { currentScript()?.scriptTag ?: 0 }
}
private sealed class LanguageRunIterator(ptr: NativePointer): RunIteratorBase(LANGUAGE_RUN_ITERATOR_TYPE, ptr) {
abstract fun currentLanguage(): LanguageRun?
abstract fun currentLanguageInterop(): InteropPointer
override fun InteropScope.getOnCurrentCallback() = virtualInteropPointer { currentLanguageInterop() }
}
private class IteratorBasedFontRunIterator(ptr: NativePointer, val iterator: Iterator<FontRun?>) : FontRunIterator(ptr) {
private var current: FontRun? = null
override fun currentFont() = current
override fun consume() { current = iterator.next() }
override fun endOfCurrentRun() = current?.end ?: 0
override fun atEnd() = !iterator.hasNext()
}
private class IteratorBasedBidiRunIterator(ptr: NativePointer, val iterator: Iterator<BidiRun?>) : BiDiRunIterator(ptr) {
private var current: BidiRun? = null
override fun currentLevel() = current
override fun consume() { current = iterator.next() }
override fun endOfCurrentRun() = current?.end ?: 0
override fun atEnd() = !iterator.hasNext()
}
private class IteratorBasedScriptRunIterator(ptr: NativePointer, val iterator: Iterator<ScriptRun?>) : ScriptRunIterator(ptr) {
private var current: ScriptRun? = null
override fun currentScript() = current
override fun consume() { current = iterator.next() }
override fun endOfCurrentRun() = current?.end ?: 0
override fun atEnd() = !iterator.hasNext()
}
private class IteratorBasedLanguageRunIterator(ptr: NativePointer, val iterator: Iterator<LanguageRun?>) : LanguageRunIterator(ptr) {
private var current: LanguageRun? = null
private var interopScope: InteropScope? = null
private var currentInterop: InteropPointer = interopScope { toInterop(null as ByteArray?) }
override fun currentLanguage() = current
override fun currentLanguageInterop() = currentInterop
override fun endOfCurrentRun() = current?.end ?: 0
override fun atEnd() = !iterator.hasNext()
override fun consume() {
current = iterator.next()
currentInterop = langToInterop()
}
private fun langToInterop(): InteropPointer {
if (interopScope != null) {
interopScope!!.release()
}
interopScope = InteropScope()
return interopScope!!.toInterop(currentLanguage()?.language)
}
}
private const val FONT_RUN_ITERATOR_TYPE: Int = 1
private const val BIDI_RUN_ITERATOR_TYPE: Int = 2
private const val SCRIPT_RUN_ITERATOR_TYPE: Int = 3
private const val LANGUAGE_RUN_ITERATOR_TYPE: Int = 4
private class RunHandlerImpl(val runHandler: RunHandler) : Managed(RunHandler_nCreate(), _FinalizerHolder.PTR) {
init {
val impl = this
interopScope {
RunHandler_nInit(
_ptr,
onBeginLine = virtual { runHandler.beginLine() },
onCommitRunInfo = virtual { runHandler.commitRunInfo() },
onCommitLine = virtual { runHandler.commitLine() },
onCommitRun = virtual {
val info = impl.runInfo
val glyphs = impl.getGlyphs(info.glyphCount)
val clusters = impl.getClusters(info.glyphCount)
val positions = impl.getPositions(info.glyphCount)
runHandler.commitRun(info, glyphs, positions, clusters)
},
onRunInfo = virtual { runHandler.runInfo(impl.runInfo) },
onRunOffset = virtual { runHandler.runOffset(impl.runInfo)?.let { impl.setOffset(it) } }
)
}
}
private fun getGlyphs(count: Int) = withResult(ShortArray(count)) {
RunHandler_nGetGlyphs(_ptr, it)
}
private fun getClusters(count: Int) = withResult(IntArray(count)) {
RunHandler_nGetClusters(_ptr, it)
}
private fun getPositions(count: Int) = Point.fromArray(
withResult(FloatArray(count * 2)) {
RunHandler_nGetPositions(_ptr, it)
}
)
private fun setOffset(point: Point) {
RunHandler_nSetOffset(_ptr, point.x, point.y)
}
private val runInfo: RunInfo
get() {
var fontPtr = NullPointer
val repr = withResult(IntArray(6)) {
fontPtr = RunHandler_nGetRunInfo(_ptr, it)
}
return RunInfo(
_fontPtr = fontPtr,
bidiLevel = repr[0],
advanceX = Float.fromBits(repr[1]),
advanceY = Float.fromBits(repr[2]),
glyphCount = repr[3],
rangeBegin = repr[4],
rangeSize = repr[5]
)
}
private object _FinalizerHolder {
val PTR = RunHandler_nGetFinalizer()
}
}
package org.jetbrains.skia
import kotlinx.cinterop.ByteVar
import kotlinx.cinterop.CPointer
import org.jetbrains.skia.impl.InteropPointer
import org.jetbrains.skia.impl.withResult
actual abstract class OutputStream
actual fun <R> commonSynchronized(lock: Any, block: () -> R) {
......@@ -27,8 +32,23 @@ actual class Matcher constructor(private val regex: Regex, private val input: Ch
actual fun matches(): Boolean = matches
}
actual fun defaultLanguageTag(): String = TODO()
private val LANG by lazy {
val localeFromICU = uloc_getDefault()
var length = 0
val maxLength = 128
val langTag = withResult(ByteArray(maxLength)) {
length = uloc_toLanguageTag(localeFromICU, it, maxLength, false, toInterop(intArrayOf(0)))
}.decodeToString(0, length)
langTag.ifEmpty { "en-US" }
}
actual fun defaultLanguageTag(): String = LANG
actual fun compilePattern(regex: String): Pattern = Pattern(regex)
actual typealias ExternalSymbolName = kotlin.native.SymbolName
@SymbolName("uloc_getDefault")
private external fun uloc_getDefault(): CPointer<ByteVar>
@SymbolName("uloc_toLanguageTag")
private external fun uloc_toLanguageTag(localeId: CPointer<ByteVar>, buffer: InteropPointer, size: Int, strict: Boolean, err: InteropPointer): Int
\ No newline at end of file
......@@ -26,6 +26,8 @@ actual abstract class Native actual constructor(ptr: NativePointer) {
init {
initCallbacks(
staticCFunction(::callBooleanCallback),
staticCFunction(::callIntCallback),
staticCFunction(::callNativePtrCallback),
staticCFunction(::callVoidCallback),
staticCFunction(::disposeCallback),
)
......@@ -201,20 +203,17 @@ actual class InteropScope actual constructor() {
return toInterop(interopPointers.map { it.toLong() }.toLongArray())
}
actual fun booleanCallback(callback: (() -> Boolean)?): InteropPointer
= callback?.let {
val ptr = StableRef.create(it).asCPointer()
NativePtr.NULL.plus(ptr.toLong())
} ?: NativePtr.NULL
actual fun callback(callback: (() -> Unit)?): InteropPointer
= callback?.let {
val ptr = StableRef.create(it).asCPointer()
NativePtr.NULL.plus(ptr.toLong())
} ?: NativePtr.NULL
actual fun callback(callback: (() -> Unit)?) = callbackImpl(callback)
actual fun intCallback(callback: (() -> Int)?) = callbackImpl(callback)
actual fun nativePointerCallback(callback: (() -> NativePointer)?) = callbackImpl(callback)
actual fun interopPointerCallback(callback: (() -> InteropPointer)?) = callbackImpl(callback)
actual fun booleanCallback(callback: (() -> Boolean)?) = callbackImpl(callback)
actual fun virtual(method: () -> Unit) = callback(method)
actual fun virtualBoolean(method: () -> Boolean) = booleanCallback(method)
actual fun virtual(method: () -> Unit) = callbackImpl(method)
actual fun virtualInt(method: () -> Int) = callbackImpl(method)
actual fun virtualNativePointer(method: () -> NativePointer) = callbackImpl(method)
actual fun virtualInteropPointer(method: () -> InteropPointer) = callbackImpl(method)
actual fun virtualBoolean(method: () -> Boolean) = callbackImpl(method)
actual fun release() {
elements.forEach {
......@@ -222,6 +221,11 @@ actual class InteropScope actual constructor() {
}
}
private fun <T> callbackImpl(callback: (() -> T)?): InteropPointer = callback?.let {
val ptr = StableRef.create(it).asCPointer()
NativePtr.NULL.plus(ptr.toLong())
} ?: NativePtr.NULL
private val elements = mutableListOf<Pinned<*>>()
}
......@@ -250,9 +254,23 @@ private fun callBooleanCallback(ptr: COpaquePointer): Boolean {
return ptr.asStableRef<() -> Boolean>().get().invoke()
}
private fun callIntCallback(ptr: COpaquePointer): Int {
return ptr.asStableRef<() -> Int>().get().invoke()
}
private fun callNativePtrCallback(ptr: COpaquePointer): Long {
return ptr.asStableRef<() -> NativePointer>().get().invoke().toLong()
}
private fun disposeCallback(ptr: COpaquePointer) {
ptr.asStableRef<Any>().dispose()
}
@ExternalSymbolName("skiko_initCallbacks")
private external fun initCallbacks(callBoolean: COpaquePointer, callVoid: COpaquePointer, dispose: COpaquePointer)
\ No newline at end of file
private external fun initCallbacks(
callBoolean: COpaquePointer,
callInt: COpaquePointer,
callNativePointer: COpaquePointer,
callVoid: COpaquePointer,
dispose: COpaquePointer
)
\ No newline at end of file
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