Unverified Commit 37699f21 authored by Oleksandr Karpovich's avatar Oleksandr Karpovich Committed by GitHub

Shaping options and Shaper interop (#343)

* update Shaper jni bindings to use common ShapingOptions interop

* add Shaper shapeLine native binding impl

* add ShaperTest.canShapeLine for all targets

* add ShaperTest.canShapeTextBlob for all targets

* add PR review improvements

* remove unnecessary std::unique_ptr in Shaper.cc
Co-authored-by: 's avatarOleksandr Karpovich <oleksandr.karpovich@jetbrains.com>
parent c24886be
...@@ -98,14 +98,20 @@ class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt) ...@@ -98,14 +98,20 @@ class FontFeature(val _tag: Int, val value: Int, val start: UInt, val end: UInt)
return FontFeature(m.group(tagIx)!!, value, start, end) return FontFeature(m.group(tagIx)!!, value, start, end)
} }
fun parse(str: String): Array<FontFeature?> { fun parse(str: String): Array<FontFeature> {
return _splitPattern.split(str).map { s -> parseOne(s) }.toTypedArray() return _splitPattern.split(str).map { s -> parseOne(s) }.toTypedArray()
} }
internal fun InteropScope.toInterop(fontFeatures: Array<FontFeature>?): InteropPointer { internal fun InteropScope.arrayOfFontFeaturesToInterop(fontFeatures: Array<FontFeature>?): InteropPointer {
val ints = fontFeatures?.flatMap { val ints = IntArray(4 * (fontFeatures?.size ?: 0))
it.toInteropIntArray().toList()
}?.toIntArray() fontFeatures?.forEachIndexed { ix, fontFeature ->
val j = ix * 4
ints[j] = fontFeature._tag
ints[j + 1] = fontFeature.value
ints[j + 2] = fontFeature.start.toInt()
ints[j + 3] = fontFeature.end.toInt()
}
return toInterop(ints) return toInterop(ints)
} }
......
...@@ -191,11 +191,20 @@ class TextBlob internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -191,11 +191,20 @@ class TextBlob internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
val glyphs: ShortArray val glyphs: ShortArray
get() = try { get() = try {
Stats.onNativeCall() Stats.onNativeCall()
_nGetGlyphs(_ptr) withResult(ShortArray(glyphsLength)) {
_nGetGlyphs(_ptr, it)
}
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
internal val glyphsLength: Int
get() = try {
Stats.onNativeCall()
_nGetGlyphsLength(_ptr)
} finally {
reachabilityBarrier(this)
}
/** /**
* *
* Return result depends on how blob was constructed. * Return result depends on how blob was constructed.
...@@ -325,8 +334,11 @@ private external fun _nMakeFromPos(glyphs: InteropPointer, pos: InteropPointer, ...@@ -325,8 +334,11 @@ private external fun _nMakeFromPos(glyphs: InteropPointer, pos: InteropPointer,
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nMakeFromRSXform") @ExternalSymbolName("org_jetbrains_skia_TextBlob__1nMakeFromRSXform")
private external fun _nMakeFromRSXform(glyphs: InteropPointer, xform: InteropPointer, fontPtr: NativePointer): NativePointer private external fun _nMakeFromRSXform(glyphs: InteropPointer, xform: InteropPointer, fontPtr: NativePointer): NativePointer
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetGlyphsLength")
private external fun _nGetGlyphsLength(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetGlyphs") @ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetGlyphs")
private external fun _nGetGlyphs(ptr: NativePointer): ShortArray private external fun _nGetGlyphs(ptr: NativePointer, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetPositions") @ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetPositions")
private external fun _nGetPositions(ptr: NativePointer): FloatArray private external fun _nGetPositions(ptr: NativePointer): FloatArray
......
...@@ -31,6 +31,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -31,6 +31,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val capHeight: Float val capHeight: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -40,6 +41,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -40,6 +41,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val xHeight: Float val xHeight: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -49,6 +51,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -49,6 +51,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val descent: Float val descent: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -58,6 +61,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -58,6 +61,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val leading: Float val leading: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -67,6 +71,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -67,6 +71,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val width: Float val width: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -76,6 +81,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -76,6 +81,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val height: Float val height: Float
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -85,6 +91,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -85,6 +91,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val textBlob: TextBlob? val textBlob: TextBlob?
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
...@@ -95,11 +102,25 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize ...@@ -95,11 +102,25 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this) reachabilityBarrier(this)
} }
} }
val glyphs: ShortArray val glyphs: ShortArray
get() { get() {
Stats.onNativeCall() Stats.onNativeCall()
return try { return try {
TextLine_nGetGlyphs(_ptr) val length = glyphsLength
withResult(ShortArray(length)) {
TextLine_nGetGlyphs(_ptr, it, length)
}
} finally {
reachabilityBarrier(this)
}
}
internal val glyphsLength: Int
get() {
Stats.onNativeCall()
return try {
TextLine_nGetGlyphsLength(_ptr)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
} }
...@@ -204,8 +225,11 @@ private external fun TextLine_nGetWidth(ptr: NativePointer): Float ...@@ -204,8 +225,11 @@ private external fun TextLine_nGetWidth(ptr: NativePointer): Float
@ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetHeight") @ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetHeight")
private external fun TextLine_nGetHeight(ptr: NativePointer): Float private external fun TextLine_nGetHeight(ptr: NativePointer): Float
@ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetGlyphsLength")
private external fun TextLine_nGetGlyphsLength(ptr: NativePointer): Int
@ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetGlyphs") @ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetGlyphs")
private external fun TextLine_nGetGlyphs(ptr: NativePointer): ShortArray private external fun TextLine_nGetGlyphs(ptr: NativePointer, resultGlyphs: InteropPointer, resultLength: Int)
@ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetPositions") @ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetPositions")
private external fun TextLine_nGetPositions(ptr: NativePointer): FloatArray private external fun TextLine_nGetPositions(ptr: NativePointer): FloatArray
......
...@@ -3,13 +3,9 @@ package org.jetbrains.skia.shaper ...@@ -3,13 +3,9 @@ package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad import org.jetbrains.skia.impl.Library.Companion.staticLoad
import org.jetbrains.skia.* import org.jetbrains.skia.*
import org.jetbrains.skia.impl.Managed
import org.jetbrains.skia.impl.Stats
import org.jetbrains.skia.impl.reachabilityBarrier
import org.jetbrains.skia.impl.use
import org.jetbrains.skia.ExternalSymbolName import org.jetbrains.skia.ExternalSymbolName
import org.jetbrains.skia.impl.NativePointer import org.jetbrains.skia.FontFeature.Companion.arrayOfFontFeaturesToInterop
import org.jetbrains.skia.impl.getPtr import org.jetbrains.skia.impl.*
/** /**
* Shapes text using HarfBuzz and places the shaped text into a * Shapes text using HarfBuzz and places the shaped text into a
...@@ -110,15 +106,19 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH ...@@ -110,15 +106,19 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH
fun shape(text: String?, font: Font?, opts: ShapingOptions, width: Float, offset: Point): TextBlob? { fun shape(text: String?, font: Font?, opts: ShapingOptions, width: Float, offset: Point): TextBlob? {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
val ptr = _nShapeBlob( val ptr = interopScope {
_ptr, _nShapeBlob(
text, _ptr,
getPtr(font), ManagedString(text)._ptr,
opts, getPtr(font),
width, optsFeaturesLen = opts.features?.size ?: 0,
offset.x, optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
offset.y optsBooleanProps = opts._booleanPropsToInt(),
) width = width,
offsetX = offset.x,
offsetY = offset.y
)
}
if (NullPointer == ptr) null else TextBlob(ptr) if (NullPointer == ptr) null else TextBlob(ptr)
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
...@@ -185,31 +185,39 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH ...@@ -185,31 +185,39 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH
runHandler: RunHandler runHandler: RunHandler
): Shaper { ): Shaper {
Stats.onNativeCall() Stats.onNativeCall()
_nShape( interopScope {
_ptr, _nShape(
getPtr(textUtf8), _ptr,
fontIter, getPtr(textUtf8),
bidiIter, fontIter,
scriptIter, bidiIter,
langIter, scriptIter,
opts, langIter,
width, optsFeaturesLen = opts.features?.size ?: 0,
runHandler optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
) optsBooleanProps = opts._booleanPropsToInt(),
width = width,
runHandler = runHandler
)
}
return this return this
} }
fun shapeLine(text: String?, font: Font?, opts: ShapingOptions): TextLine { fun shapeLine(text: String?, font: Font?, opts: ShapingOptions): TextLine {
return try { return try {
Stats.onNativeCall() Stats.onNativeCall()
TextLine( interopScope {
_nShapeLine( TextLine(
_ptr, _nShapeLine(
text, _ptr,
getPtr(font), ManagedString(text)._ptr,
opts getPtr(font),
optsFeaturesLen = opts.features?.size ?: 0,
optsFeatures = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt()
)
) )
) }
} finally { } finally {
reachabilityBarrier(this) reachabilityBarrier(this)
reachabilityBarrier(font) reachabilityBarrier(font)
...@@ -250,9 +258,11 @@ private external fun _nMakeCoreText(): NativePointer ...@@ -250,9 +258,11 @@ private external fun _nMakeCoreText(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShapeBlob") @ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShapeBlob")
private external fun _nShapeBlob( private external fun _nShapeBlob(
ptr: NativePointer, ptr: NativePointer,
text: String?, text: NativePointer,
fontPtr: NativePointer, fontPtr: NativePointer,
opts: ShapingOptions?, optsFeaturesLen: Int,
optsFeaturesIntArray: InteropPointer,
optsBooleanProps: Int,
width: Float, width: Float,
offsetX: Float, offsetX: Float,
offsetY: Float offsetY: Float
...@@ -260,7 +270,14 @@ private external fun _nShapeBlob( ...@@ -260,7 +270,14 @@ private external fun _nShapeBlob(
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShapeLine") @ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShapeLine")
private external fun _nShapeLine(ptr: NativePointer, text: String?, fontPtr: NativePointer, opts: ShapingOptions?): NativePointer private external fun _nShapeLine(
ptr: NativePointer,
text: NativePointer,
fontPtr: NativePointer,
optsFeaturesLen: Int,
optsFeatures: InteropPointer,
optsBooleanProps: Int
): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShape") @ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShape")
private external fun _nShape( private external fun _nShape(
...@@ -270,7 +287,9 @@ private external fun _nShape( ...@@ -270,7 +287,9 @@ private external fun _nShape(
bidiIter: Iterator<BidiRun?>?, bidiIter: Iterator<BidiRun?>?,
scriptIter: Iterator<ScriptRun?>?, scriptIter: Iterator<ScriptRun?>?,
langIter: Iterator<LanguageRun?>?, langIter: Iterator<LanguageRun?>?,
opts: ShapingOptions?, optsFeaturesLen: Int,
optsFeaturesIntArray: InteropPointer,
optsBooleanProps: Int,
width: Float, width: Float,
runHandler: RunHandler? runHandler: RunHandler?
) )
...@@ -5,7 +5,7 @@ import org.jetbrains.skia.FontMgr ...@@ -5,7 +5,7 @@ import org.jetbrains.skia.FontMgr
class ShapingOptions( class ShapingOptions(
internal val fontMgr: FontMgr?, internal val fontMgr: FontMgr?,
internal val features: Array<FontFeature?>?, internal val features: Array<FontFeature>?,
internal val isLeftToRight: Boolean, internal val isLeftToRight: Boolean,
/** /**
* If enabled, fallback font runs will not be broken by whitespace from original font * If enabled, fallback font runs will not be broken by whitespace from original font
...@@ -17,18 +17,26 @@ class ShapingOptions( ...@@ -17,18 +17,26 @@ class ShapingOptions(
internal val isApproximatePunctuation: Boolean internal val isApproximatePunctuation: Boolean
) { ) {
internal fun _booleanPropsToInt(): Int {
var i = 0
if (isLeftToRight) i = i or 0x04
if (isApproximateSpaces) i = i or 0x02
if (isApproximatePunctuation) i = i or 0x01
return i
}
/** /**
* If enabled, fallback font runs will not be broken by whitespace from original font * If enabled, fallback font runs will not be broken by whitespace from original font
*/ */
/** /**
* If enabled, fallback font runs will not be broken by punctuation from original font * If enabled, fallback font runs will not be broken by punctuation from original font
*/ */
fun withFeatures(features: Array<FontFeature?>?): ShapingOptions { fun withFeatures(features: Array<FontFeature>?): ShapingOptions {
return ShapingOptions(fontMgr, features, isLeftToRight, isApproximateSpaces, isApproximatePunctuation) return ShapingOptions(fontMgr, features, isLeftToRight, isApproximateSpaces, isApproximatePunctuation)
} }
fun withFeatures(featuresString: String?): ShapingOptions { fun withFeatures(featuresString: String?): ShapingOptions {
return if (featuresString == null) withFeatures(null as Array<FontFeature?>?) else withFeatures( return if (featuresString == null) withFeatures(null as Array<FontFeature>?) else withFeatures(
FontFeature.Companion.parse( FontFeature.Companion.parse(
featuresString featuresString
) )
...@@ -111,4 +119,4 @@ class ShapingOptions( ...@@ -111,4 +119,4 @@ class ShapingOptions(
companion object { companion object {
val DEFAULT = ShapingOptions(null, null, true, true, true) val DEFAULT = ShapingOptions(null, null, true, true, true)
} }
} }
\ No newline at end of file
package org.jetbrains.skia
import org.jetbrains.skia.shaper.Shaper
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
class ShaperTest {
private suspend fun fontInter36() =
Font(Typeface.makeFromResource("./fonts/Inter-Hinted-Regular.ttf"), 36f)
@Test
fun canShapeLine() = runTest {
val textLine = Shaper.make().shapeLine(
text = "Abc123", font = fontInter36()
)
assertEquals(6, textLine.glyphsLength)
assertContentEquals(
expected = shortArrayOf(2, 574, 581, 1292, 1293, 1295),
actual = textLine.glyphs
)
}
@Test
fun canShapeTextBlob() = runTest {
val textBlob = Shaper.make().shape(
text = "text",
font = fontInter36(),
width = 100f
)
assertNotEquals(null, textBlob)
assertEquals(4, textBlob!!.glyphsLength)
assertContentEquals(
expected = shortArrayOf(882, 611, 943, 882),
actual = textBlob.glyphs
)
}
}
...@@ -162,19 +162,32 @@ public: ...@@ -162,19 +162,32 @@ public:
} }
}; };
extern "C" JNIEXPORT jshortArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetGlyphs extern "C" JNIEXPORT jint JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetGlyphsLength
(JNIEnv* env, jclass jclass, jlong ptr) { (JNIEnv* env, jclass jclass, jlong ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
jint stored = 0;
while (iter.next(&run)) {
stored += run.fGlyphCount;
}
return stored;
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetGlyphs
(JNIEnv* env, jclass jclass, jlong ptr, jshortArray resultArray) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr)); SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(static_cast<uintptr_t>(ptr));
SkTextBlob::Iter iter(*instance); SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run; SkTextBlob::Iter::Run run;
std::vector<jshort> glyphs;
size_t stored = 0; size_t stored = 0;
jshort * shorts = env->GetShortArrayElements(resultArray, nullptr);
while (iter.next(&run)) { while (iter.next(&run)) {
glyphs.resize(stored + run.fGlyphCount); memcpy(shorts + stored, run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
memcpy(&glyphs[stored], run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
stored += run.fGlyphCount; stored += run.fGlyphCount;
} }
return javaShortArray(env, glyphs); env->ReleaseShortArrayElements(resultArray, shorts, 0);
} }
extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetPositions extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetPositions
...@@ -209,7 +222,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGet ...@@ -209,7 +222,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGet
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1; auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (!runRecord->isExtended()) if (!runRecord->isExtended())
return nullptr; return nullptr;
skija::UtfIndicesConverter conv(runRecord->textBuffer(), runRecord->textSize()); skija::UtfIndicesConverter conv(runRecord->textBuffer(), runRecord->textSize());
clusters.resize(stored + run.fGlyphCount); clusters.resize(stored + run.fGlyphCount);
uint32_t* clusterBuffer = runRecord->clusterBuffer(); uint32_t* clusterBuffer = runRecord->clusterBuffer();
...@@ -217,7 +230,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGet ...@@ -217,7 +230,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGet
clusters[stored + i] = runStart16 + conv.from8To16(clusterBuffer[i]); clusters[stored + i] = runStart16 + conv.from8To16(clusterBuffer[i]);
runStart16 += conv.from8To16(runRecord->textSize()); runStart16 += conv.from8To16(runRecord->textSize());
// memcpy(&clusters[stored], runRecord->clusterBuffer(), run.fGlyphCount * sizeof(uint32_t)); // memcpy(&clusters[stored], runRecord->clusterBuffer(), run.fGlyphCount * sizeof(uint32_t));
stored += run.fGlyphCount; stored += run.fGlyphCount;
} }
return javaIntArray(env, clusters); return javaIntArray(env, clusters);
...@@ -235,7 +248,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetTi ...@@ -235,7 +248,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetTi
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1; auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr; return nullptr;
runRecord->fFont.measureText(runRecord->glyphBuffer(), run.fGlyphCount * sizeof(uint16_t), SkTextEncoding::kGlyphID, &tmpBounds, nullptr); runRecord->fFont.measureText(runRecord->glyphBuffer(), run.fGlyphCount * sizeof(uint16_t), SkTextEncoding::kGlyphID, &tmpBounds, nullptr);
SkScalar* posBuffer = runRecord->posBuffer(); SkScalar* posBuffer = runRecord->posBuffer();
tmpBounds.offset(posBuffer[0], posBuffer[1]); tmpBounds.offset(posBuffer[0], posBuffer[1]);
...@@ -251,7 +264,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl ...@@ -251,7 +264,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl
SkTextBlob::Iter::Run run; SkTextBlob::Iter::Run run;
auto bounds = SkRect::MakeEmpty(); auto bounds = SkRect::MakeEmpty();
SkFontMetrics metrics; SkFontMetrics metrics;
while (iter.next(&run)) { while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself // run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1; auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
...@@ -261,14 +274,14 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl ...@@ -261,14 +274,14 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl
SkScalar* posBuffer = runRecord->posBuffer(); SkScalar* posBuffer = runRecord->posBuffer();
const SkFont& font = runRecord->fFont; const SkFont& font = runRecord->fFont;
font.getMetrics(&metrics); font.getMetrics(&metrics);
SkScalar lastLeft = posBuffer[(run.fGlyphCount - 1) * 2]; SkScalar lastLeft = posBuffer[(run.fGlyphCount - 1) * 2];
SkScalar lastWidth; SkScalar lastWidth;
if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft)) if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft))
lastWidth = 0; lastWidth = 0;
else else
font.getWidths(&run.fGlyphIndices[run.fGlyphCount - 1], 1, &lastWidth); font.getWidths(&run.fGlyphIndices[run.fGlyphCount - 1], 1, &lastWidth);
auto runBounds = SkRect::MakeLTRB(posBuffer[0], posBuffer[1] + metrics.fAscent, lastLeft + lastWidth, posBuffer[1] + metrics.fDescent); auto runBounds = SkRect::MakeLTRB(posBuffer[0], posBuffer[1] + metrics.fAscent, lastLeft + lastWidth, posBuffer[1] + metrics.fDescent);
bounds.join(runBounds); bounds.join(runBounds);
} }
......
...@@ -65,17 +65,32 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_TextLineKt__1nGetText ...@@ -65,17 +65,32 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_TextLineKt__1nGetText
return reinterpret_cast<jlong>(instance->fBlob.get()); return reinterpret_cast<jlong>(instance->fBlob.get());
} }
extern "C" JNIEXPORT jshortArray JNICALL Java_org_jetbrains_skia_TextLineKt_TextLine_1nGetGlyphs extern "C" JNIEXPORT jint Java_org_jetbrains_skia_TextLineKt_TextLine_1nGetGlyphsLength
(JNIEnv* env, jclass jclass, jlong ptr) { (JNIEnv* env, jclass jclass, jlong ptr) {
TextLine* instance = reinterpret_cast<TextLine*>(static_cast<uintptr_t>(ptr)); TextLine* instance = reinterpret_cast<TextLine*>(static_cast<uintptr_t>(ptr));
std::vector<jshort> glyphs(instance->fGlyphCount); return instance->fGlyphCount;
}
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_TextLineKt_TextLine_1nGetGlyphs
(JNIEnv* env, jclass jclass, jlong ptr, jshortArray resultGlyphs, jint resultLength) {
TextLine* instance = reinterpret_cast<TextLine*>(static_cast<uintptr_t>(ptr));
jshort* glyphs = env->GetShortArrayElements(resultGlyphs, nullptr);
size_t idx = 0; size_t idx = 0;
size_t freeBytesN = resultLength * sizeof(uint16_t);
for (auto& run: instance->fRuns) { for (auto& run: instance->fRuns) {
memcpy(glyphs.data() + idx, run.fGlyphs, run.fGlyphCount * sizeof(uint16_t)); size_t addBytesLen = run.fGlyphCount * sizeof(uint16_t);
idx += run.fGlyphCount; if (freeBytesN - addBytesLen >= 0) {
memcpy(&glyphs[idx], run.fGlyphs, addBytesLen);
idx += run.fGlyphCount;
freeBytesN -= addBytesLen;
} else {
SkDEBUGFAIL("Incorrect resultGlyphs size");
}
} }
env->ReleaseShortArrayElements(resultGlyphs, glyphs, 0);
SkASSERTF(idx == instance->fGlyphCount, "TextLine.cc: idx = %d != instance->fGlyphCount = %d", idx, instance->fGlyphCount); SkASSERTF(idx == instance->fGlyphCount, "TextLine.cc: idx = %d != instance->fGlyphCount = %d", idx, instance->fGlyphCount);
return javaShortArray(env, glyphs);
} }
extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skia_TextLineKt_TextLine_1nGetPositions extern "C" JNIEXPORT jfloatArray JNICALL Java_org_jetbrains_skia_TextLineKt_TextLine_1nGetPositions
...@@ -173,4 +188,4 @@ extern "C" JNIEXPORT jfloat JNICALL Java_org_jetbrains_skia_TextLineKt__1nGetCoo ...@@ -173,4 +188,4 @@ extern "C" JNIEXPORT jfloat JNICALL Java_org_jetbrains_skia_TextLineKt__1nGetCoo
} }
return instance->fWidth; return instance->fWidth;
} }
\ No newline at end of file
...@@ -56,26 +56,20 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt_Shape ...@@ -56,26 +56,20 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt_Shape
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShapeBlob extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShapeBlob
(JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts, jfloat width, jfloat offsetX, jfloat offsetY) { (JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jlong fontPtr, jint optsFeaturesLen, jintArray optsFeatures, jint optsBooleanProps, jfloat width, jfloat offsetX, jfloat offsetY) {
SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr)); SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr));
SkString text = skString(env, textObj); SkString& text = *(reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr)));
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text); std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0; if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr)); SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeatures(env, opts);
std::unique_ptr<SkShaper::FontRunIterator> fontRunIter(new FontRunIterator( std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(env, optsFeatures, optsFeaturesLen);
text.c_str(),
text.size(),
*font,
SkFontMgr::RefDefault(),
graphemeIter,
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)
));
if (!fontRunIter) return 0;
uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL; bool aproximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool aproximateSpaces = (optsBooleanProps & 0x02) != 0;
bool isLeftToRight = (optsBooleanProps & 0x04) != 0;
uint8_t defaultBiDiLevel = isLeftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel)); std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0; if (!bidiRunIter) return 0;
...@@ -85,35 +79,44 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSh ...@@ -85,35 +79,44 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSh
std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size())); std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size()));
if (!languageRunIter) return 0; if (!languageRunIter) return 0;
FontRunIterator fontRunIter(
text.c_str(),
text.size(),
*font,
SkFontMgr::RefDefault(),
graphemeIter,
aproximateSpaces,
aproximatePunctuation
);
SkTextBlobBuilderRunHandler rh(text.c_str(), {offsetX, offsetY}); SkTextBlobBuilderRunHandler rh(text.c_str(), {offsetX, offsetY});
instance->shape(text.c_str(), text.size(), *fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), width, &rh); instance->shape(text.c_str(), text.size(), fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), width, &rh);
SkTextBlob* blob = rh.makeBlob().release(); SkTextBlob* blob = rh.makeBlob().release();
return reinterpret_cast<jlong>(blob); return reinterpret_cast<jlong>(blob);
} }
extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShapeLine extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShapeLine
(JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts) { (JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jlong fontPtr, jint optsFeaturesLen, jintArray optsFeatures, jint optsBooleanProps) {
SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr)); SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr));
SkString text = skString(env, textObj); SkString& text = *(reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr)));
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
if (text.size() == 0) {
return reinterpret_cast<jlong>(new TextLine(*font));
}
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text); std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0; if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr)); std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(env, optsFeatures, optsFeaturesLen);
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeatures(env, opts);
std::unique_ptr<SkShaper::FontRunIterator> fontRunIter(new FontRunIterator( bool aproximatePunctuation = (optsBooleanProps & 0x01) != 0;
text.c_str(), bool aproximateSpaces = (optsBooleanProps & 0x02) != 0;
text.size(), bool isLeftToRight = (optsBooleanProps & 0x04) != 0;
*font,
SkFontMgr::RefDefault(),
graphemeIter,
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximateSpaces),
env->GetBooleanField(opts, skija::shaper::ShapingOptions::_approximatePunctuation)));
if (!fontRunIter) return 0;
uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL; uint8_t defaultBiDiLevel = isLeftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel)); std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0; if (!bidiRunIter) return 0;
...@@ -123,15 +126,19 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSh ...@@ -123,15 +126,19 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSh
std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size())); std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size()));
if (!languageRunIter) return 0; if (!languageRunIter) return 0;
TextLine* line; FontRunIterator fontRunIter(
if (text.size() == 0) text.c_str(),
line = new TextLine(*font); text.size(),
else { *font,
TextLineRunHandler rh(text, graphemeIter); SkFontMgr::RefDefault(),
instance->shape(text.c_str(), text.size(), *fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), std::numeric_limits<float>::infinity(), &rh); graphemeIter,
line = rh.makeLine().release(); aproximateSpaces,
} aproximatePunctuation);
return reinterpret_cast<jlong>(line);
TextLineRunHandler rh(text, graphemeIter);
instance->shape(text.c_str(), text.size(), fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), std::numeric_limits<float>::infinity(), &rh);
return reinterpret_cast<jlong>(rh.makeLine().release());
} }
template <typename RunIteratorSubclass> template <typename RunIteratorSubclass>
...@@ -160,7 +167,7 @@ public: ...@@ -160,7 +167,7 @@ public:
size_t endOfCurrentRun() const override { size_t endOfCurrentRun() const override {
return fEndOfRun; return fEndOfRun;
} }
bool atEnd() const override { bool atEnd() const override {
return !fHasNext; return !fHasNext;
} }
...@@ -177,7 +184,7 @@ protected: ...@@ -177,7 +184,7 @@ protected:
class SkijaFontRunIterator: public SkijaRunIterator<SkShaper::FontRunIterator> { class SkijaFontRunIterator: public SkijaRunIterator<SkShaper::FontRunIterator> {
public: public:
SkijaFontRunIterator(JNIEnv* env, jobject obj, SkString text): SkijaFontRunIterator(JNIEnv* env, jobject obj, SkString text):
SkijaRunIterator<SkShaper::FontRunIterator>(env, obj, text) SkijaRunIterator<SkShaper::FontRunIterator>(env, obj, text)
{} {}
...@@ -198,7 +205,7 @@ protected: ...@@ -198,7 +205,7 @@ protected:
class SkijaBidiRunIterator: public SkijaRunIterator<SkShaper::BiDiRunIterator> { class SkijaBidiRunIterator: public SkijaRunIterator<SkShaper::BiDiRunIterator> {
public: public:
SkijaBidiRunIterator(JNIEnv* env, jobject obj, SkString& text): SkijaBidiRunIterator(JNIEnv* env, jobject obj, SkString& text):
SkijaRunIterator<SkShaper::BiDiRunIterator>(env, obj, text) SkijaRunIterator<SkShaper::BiDiRunIterator>(env, obj, text)
{} {}
...@@ -210,7 +217,7 @@ public: ...@@ -210,7 +217,7 @@ public:
fLevel = fEnv->GetIntField(runObj, skija::shaper::BidiRun::_level) & 0xFF; fLevel = fEnv->GetIntField(runObj, skija::shaper::BidiRun::_level) & 0xFF;
return fEnv->GetIntField(runObj, skija::shaper::BidiRun::_end); return fEnv->GetIntField(runObj, skija::shaper::BidiRun::_end);
} }
protected: protected:
uint8_t fLevel; uint8_t fLevel;
}; };
...@@ -229,7 +236,7 @@ public: ...@@ -229,7 +236,7 @@ public:
fScript = fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_scriptTag); fScript = fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_scriptTag);
return fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_end); return fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_end);
} }
protected: protected:
uint32_t fScript; uint32_t fScript;
}; };
...@@ -249,7 +256,7 @@ public: ...@@ -249,7 +256,7 @@ public:
fLang = skString(fEnv, langObj); fLang = skString(fEnv, langObj);
return fEnv->GetIntField(runObj, skija::shaper::LanguageRun::_end); return fEnv->GetIntField(runObj, skija::shaper::LanguageRun::_end);
} }
protected: protected:
SkString fLang; SkString fLang;
}; };
...@@ -329,7 +336,7 @@ private: ...@@ -329,7 +336,7 @@ private:
}; };
extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShape extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nShape
(JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jobject fontRunIterObj, jobject bidiRunIterObj, jobject scriptRunIterObj, jobject languageRunIterObj, jobject opts, jfloat width, jobject runHandlerObj) (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)); SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr));
SkString* text = reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr)); SkString* text = reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr));
...@@ -348,11 +355,11 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSha ...@@ -348,11 +355,11 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSha
std::unique_ptr<SkijaScriptRunIterator> localScriptRunIter; std::unique_ptr<SkijaScriptRunIterator> localScriptRunIter;
if (nativeScriptRunIter == nullptr) if (nativeScriptRunIter == nullptr)
localScriptRunIter.reset(new SkijaScriptRunIterator(env, scriptRunIterObj, *text)); localScriptRunIter.reset(new SkijaScriptRunIterator(env, scriptRunIterObj, *text));
auto languageRunIter = SkijaLanguageRunIterator(env, languageRunIterObj, *text); auto languageRunIter = SkijaLanguageRunIterator(env, languageRunIterObj, *text);
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeatures(env, opts); std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(env, optsFeatures, optsFeaturesLen);
auto nativeRunHandler = (SkShaper::RunHandler*) skija::impl::Native::fromJava(env, runHandlerObj, skija::shaper::TextBlobBuilderRunHandler::cls); auto nativeRunHandler = (SkShaper::RunHandler*) skija::impl::Native::fromJava(env, runHandlerObj, skija::shaper::TextBlobBuilderRunHandler::cls);
std::unique_ptr<SkijaRunHandler> localRunHandler; std::unique_ptr<SkijaRunHandler> localRunHandler;
if (nativeRunHandler == nullptr) if (nativeRunHandler == nullptr)
...@@ -367,4 +374,4 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSha ...@@ -367,4 +374,4 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSha
features.size(), features.size(),
width, width,
nativeRunHandler != nullptr ? nativeRunHandler : localRunHandler.get()); nativeRunHandler != nullptr ? nativeRunHandler : localRunHandler.get());
} }
\ No newline at end of file
...@@ -46,7 +46,7 @@ namespace skija { ...@@ -46,7 +46,7 @@ namespace skija {
void onLoad(JNIEnv* env) { void onLoad(JNIEnv* env) {
jclass local = env->FindClass("org/jetbrains/skia/shaper/HbIcuScriptRunIterator"); jclass local = env->FindClass("org/jetbrains/skia/shaper/HbIcuScriptRunIterator");
cls = static_cast<jclass>(env->NewGlobalRef(local)); cls = static_cast<jclass>(env->NewGlobalRef(local));
} }
void onUnload(JNIEnv* env) { void onUnload(JNIEnv* env) {
...@@ -60,7 +60,7 @@ namespace skija { ...@@ -60,7 +60,7 @@ namespace skija {
void onLoad(JNIEnv* env) { void onLoad(JNIEnv* env) {
jclass local = env->FindClass("org/jetbrains/skia/shaper/IcuBidiRunIterator"); jclass local = env->FindClass("org/jetbrains/skia/shaper/IcuBidiRunIterator");
cls = static_cast<jclass>(env->NewGlobalRef(local)); cls = static_cast<jclass>(env->NewGlobalRef(local));
} }
void onUnload(JNIEnv* env) { void onUnload(JNIEnv* env) {
...@@ -124,7 +124,7 @@ namespace skija { ...@@ -124,7 +124,7 @@ namespace skija {
jobject toJava(JNIEnv* env, const SkShaper::RunHandler::RunInfo& info, size_t begin, size_t end) { jobject toJava(JNIEnv* env, const SkShaper::RunHandler::RunInfo& info, size_t begin, size_t end) {
SkFont* font = new SkFont(info.fFont); SkFont* font = new SkFont(info.fFont);
return env->NewObject( return env->NewObject(
cls, cls,
ctor, ctor,
reinterpret_cast<jlong>(font), reinterpret_cast<jlong>(font),
info.fBidiLevel, info.fBidiLevel,
...@@ -172,6 +172,10 @@ namespace skija { ...@@ -172,6 +172,10 @@ namespace skija {
std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts) { std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts) {
return skija::FontFeature::fromJavaArray(env, (jobjectArray) env->GetObjectField(opts, _features)); return skija::FontFeature::fromJavaArray(env, (jobjectArray) env->GetObjectField(opts, _features));
} }
std::vector<SkShaper::Feature> getFeaturesFromIntsArray(JNIEnv* env, jintArray featuresArray, jint featuresLen) {
return skija::FontFeature::fromIntArray(env, featuresArray, featuresLen);
}
} }
namespace TextBlobBuilderRunHandler { namespace TextBlobBuilderRunHandler {
...@@ -186,7 +190,7 @@ namespace skija { ...@@ -186,7 +190,7 @@ namespace skija {
env->DeleteGlobalRef(cls); env->DeleteGlobalRef(cls);
} }
} }
void onLoad(JNIEnv* env) { void onLoad(JNIEnv* env) {
BidiRun::onLoad(env); BidiRun::onLoad(env);
FontMgrRunIterator::onLoad(env); FontMgrRunIterator::onLoad(env);
...@@ -214,7 +218,7 @@ namespace skija { ...@@ -214,7 +218,7 @@ namespace skija {
std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text) { std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text) {
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
ICUUText utext(utext_openUTF8(nullptr, text.c_str(), text.size(), &status)); ICUUText utext(utext_openUTF8(nullptr, text.c_str(), text.size(), &status));
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
SkDEBUGF("utext_openUTF8 error: %s", u_errorName(status)); SkDEBUGF("utext_openUTF8 error: %s", u_errorName(status));
...@@ -236,7 +240,7 @@ namespace skija { ...@@ -236,7 +240,7 @@ namespace skija {
return nullptr; return nullptr;
} }
return graphemeIter; return graphemeIter;
} }
} }
} }
\ No newline at end of file
...@@ -57,7 +57,7 @@ namespace skija { ...@@ -57,7 +57,7 @@ namespace skija {
extern jmethodID runOffset; extern jmethodID runOffset;
extern jmethodID commitRun; extern jmethodID commitRun;
extern jmethodID commitLine; extern jmethodID commitLine;
void onLoad(JNIEnv* env); void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env); void onUnload(JNIEnv* env);
} }
...@@ -89,6 +89,7 @@ namespace skija { ...@@ -89,6 +89,7 @@ namespace skija {
void onLoad(JNIEnv* env); void onLoad(JNIEnv* env);
std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts); std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts);
std::vector<SkShaper::Feature> getFeaturesFromIntsArray(JNIEnv* env, jintArray featuresArray, jint featuresLen);
} }
namespace TextBlobBuilderRunHandler { namespace TextBlobBuilderRunHandler {
...@@ -97,11 +98,11 @@ namespace skija { ...@@ -97,11 +98,11 @@ namespace skija {
void onLoad(JNIEnv* env); void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env); void onUnload(JNIEnv* env);
} }
void onLoad(JNIEnv* env); void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env); void onUnload(JNIEnv* env);
using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>; using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text); std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text);
} }
} }
\ No newline at end of file
...@@ -43,12 +43,12 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromPos ...@@ -43,12 +43,12 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromPos
(KShort* glyphsArr, KFloat* posArr, KNativePointer fontPtr ) { (KShort* glyphsArr, KFloat* posArr, KNativePointer fontPtr ) {
TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromPos"); TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromPos");
} }
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromRSXform SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromRSXform
(KShort* glyphsArr, KFloat* xformArr, KNativePointer fontPtr ) { (KShort* glyphsArr, KFloat* xformArr, KNativePointer fontPtr ) {
TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromRSXform"); TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromRSXform");
} }
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nSerializeToData SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nSerializeToData
(KNativePointer ptr) { (KNativePointer ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>((ptr)); SkTextBlob* instance = reinterpret_cast<SkTextBlob*>((ptr));
...@@ -63,10 +63,29 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromData ...@@ -63,10 +63,29 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromData
return reinterpret_cast<KNativePointer>(instance); return reinterpret_cast<KNativePointer>(instance);
} }
SKIKO_EXPORT KInt org_jetbrains_skia_TextBlob__1nGetGlyphsLength
SKIKO_EXPORT KShort* org_jetbrains_skia_TextBlob__1nGetGlyphs
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetGlyphs"); SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(ptr);
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
int stored = 0;
while (iter.next(&run)) {
stored += run.fGlyphCount;
}
return stored;
}
SKIKO_EXPORT void org_jetbrains_skia_TextBlob__1nGetGlyphs
(KNativePointer ptr, KShort* resultArray) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>(ptr);
SkTextBlob::Iter iter(*instance);
SkTextBlob::Iter::Run run;
size_t stored = 0;
while (iter.next(&run)) {
memcpy(resultArray + stored, run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
stored += run.fGlyphCount;
}
} }
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextBlob__1nGetPositions SKIKO_EXPORT KFloat* org_jetbrains_skia_TextBlob__1nGetPositions
...@@ -78,12 +97,12 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextBlob__1nGetClusters ...@@ -78,12 +97,12 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextBlob__1nGetClusters
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetClusters"); TODO("implement org_jetbrains_skia_TextBlob__1nGetClusters");
} }
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetTightBounds SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetTightBounds
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetTightBounds"); TODO("implement org_jetbrains_skia_TextBlob__1nGetTightBounds");
} }
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetBlockBounds SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetBlockBounds
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetBlockBounds"); TODO("implement org_jetbrains_skia_TextBlob__1nGetBlockBounds");
...@@ -97,4 +116,4 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetFirstBaseline ...@@ -97,4 +116,4 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetFirstBaseline
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetLastBaseline SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetLastBaseline
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetLastBaseline"); TODO("implement org_jetbrains_skia_TextBlob__1nGetLastBaseline");
} }
\ No newline at end of file
...@@ -5,28 +5,24 @@ ...@@ -5,28 +5,24 @@
#include <iostream> #include <iostream>
#include "SkShaper.h" #include "SkShaper.h"
#include "common.h" #include "common.h"
#include "TextLine.hh"
static void unrefTextLine(TextLine* ptr) {
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetFinalizer ptr->unref();
() {
TODO("implement org_jetbrains_skia_TextLine__1nGetFinalizer");
} }
#if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetFinalizer SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetFinalizer
() { () {
return reinterpret_cast<KNativePointer>((&unrefTextLine)); return reinterpret_cast<KNativePointer>(&unrefTextLine);
} }
#endif
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetAscent SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetAscent
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetAscent"); TODO("implement org_jetbrains_skia_TextLine__1nGetAscent");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetAscent SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetAscent
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -40,8 +36,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight ...@@ -40,8 +36,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetCapHeight"); TODO("implement org_jetbrains_skia_TextLine__1nGetCapHeight");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -55,8 +51,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight ...@@ -55,8 +51,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetXHeight"); TODO("implement org_jetbrains_skia_TextLine__1nGetXHeight");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -70,8 +66,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent ...@@ -70,8 +66,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetDescent"); TODO("implement org_jetbrains_skia_TextLine__1nGetDescent");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -85,8 +81,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading ...@@ -85,8 +81,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetLeading"); TODO("implement org_jetbrains_skia_TextLine__1nGetLeading");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -100,8 +96,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth ...@@ -100,8 +96,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetWidth"); TODO("implement org_jetbrains_skia_TextLine__1nGetWidth");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -115,8 +111,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight ...@@ -115,8 +111,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetHeight"); TODO("implement org_jetbrains_skia_TextLine__1nGetHeight");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -130,8 +126,8 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob ...@@ -130,8 +126,8 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetTextBlob"); TODO("implement org_jetbrains_skia_TextLine__1nGetTextBlob");
} }
#if 0 #if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -143,35 +139,40 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob ...@@ -143,35 +139,40 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
#endif #endif
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetGlyphsLength
SKIKO_EXPORT KShort* org_jetbrains_skia_TextLine__1nGetGlyphs
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetGlyphs"); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
return instance->fGlyphCount;
} }
#if 0 SKIKO_EXPORT void org_jetbrains_skia_TextLine__1nGetGlyphs
SKIKO_EXPORT KShort* org_jetbrains_skia_TextLine__1nGetGlyphs (KNativePointer ptr, KInteropPointer glyphs, KInt resultLength) {
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
std::vector<KShort> glyphs(instance->fGlyphCount);
size_t idx = 0; size_t idx = 0;
size_t freeBytesN = resultLength * sizeof(uint16_t);
KShort* glyphsPtr = reinterpret_cast<KShort*>(glyphs);
for (auto& run: instance->fRuns) { for (auto& run: instance->fRuns) {
memcpy(glyphs.data() + idx, run.fGlyphs, run.fGlyphCount * sizeof(uint16_t)); size_t addBytesLen = run.fGlyphCount * sizeof(uint16_t);
idx += run.fGlyphCount; if (freeBytesN - addBytesLen >= 0) {
memcpy(&glyphsPtr[idx], run.fGlyphs, addBytesLen);
idx += run.fGlyphCount;
freeBytesN -= addBytesLen;
} else {
SkDEBUGFAIL("Incorrect resultGlyphs size");
}
} }
SkASSERTF(idx == instance->fGlyphCount, "TextLine.cc: idx = %d != instance->fGlyphCount = %d", idx, instance->fGlyphCount); SkASSERTF(idx == instance->fGlyphCount, "TextLine.cc: idx = %d != instance->fGlyphCount = %d", idx, instance->fGlyphCount);
return javaShortArray(env, glyphs);
} }
#endif
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetPositions"); TODO("implement org_jetbrains_skia_TextLine__1nGetPositions");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -192,8 +193,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions ...@@ -192,8 +193,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetRunPositions"); TODO("implement org_jetbrains_skia_TextLine__1nGetRunPositions");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -210,8 +211,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions ...@@ -210,8 +211,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetBreakPositions"); TODO("implement org_jetbrains_skia_TextLine__1nGetBreakPositions");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -228,8 +229,8 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets ...@@ -228,8 +229,8 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets
(KNativePointer ptr) { (KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetBreakOffsets"); TODO("implement org_jetbrains_skia_TextLine__1nGetBreakOffsets");
} }
#if 0 #if 0
SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets
(KNativePointer ptr) { (KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -246,8 +247,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord ...@@ -246,8 +247,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord
(KNativePointer ptr, KFloat x) { (KNativePointer ptr, KFloat x) {
TODO("implement org_jetbrains_skia_TextLine__1nGetOffsetAtCoord"); TODO("implement org_jetbrains_skia_TextLine__1nGetOffsetAtCoord");
} }
#if 0 #if 0
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord
(KNativePointer ptr, KFloat x) { (KNativePointer ptr, KFloat x) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -275,8 +276,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord ...@@ -275,8 +276,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord
(KNativePointer ptr, KFloat x) { (KNativePointer ptr, KFloat x) {
TODO("implement org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord"); TODO("implement org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord");
} }
#if 0 #if 0
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord
(KNativePointer ptr, KFloat x) { (KNativePointer ptr, KFloat x) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
...@@ -302,8 +303,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset ...@@ -302,8 +303,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset
(KNativePointer ptr, KInt offset16) { (KNativePointer ptr, KInt offset16) {
TODO("implement org_jetbrains_skia_TextLine__1nGetCoordAtOffset"); TODO("implement org_jetbrains_skia_TextLine__1nGetCoordAtOffset");
} }
#if 0 #if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset
(KNativePointer ptr, KInt offset16) { (KNativePointer ptr, KInt offset16) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr)); TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
#include "SkSurfaceProps.h" #include "SkSurfaceProps.h"
#include "SkSVGTypes.h" #include "SkSVGTypes.h"
#include "TextStyle.h" #include "TextStyle.h"
#include "SkFont.h"
#include "SkShaper.h"
#include "SkLoadICU.h"
#include "unicode/ubrk.h"
#include "types.h" #include "types.h"
...@@ -95,6 +99,15 @@ namespace skija { ...@@ -95,6 +99,15 @@ namespace skija {
} }
} }
namespace shaper {
namespace ShapingOptions {
std::vector<SkShaper::Feature> getFeaturesFromIntsArray(KInt* featuresArray, KInt featuresLen);
}
using ICUUText = std::unique_ptr<UText, SkFunctionWrapper<decltype(utext_close), utext_close>>;
std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text);
}
namespace AnimationFrameInfo { namespace AnimationFrameInfo {
void copyToInterop(const SkCodec::FrameInfo& info, KInteropPointer dst); void copyToInterop(const SkCodec::FrameInfo& info, KInteropPointer dst);
void copyToInterop(const std::vector<SkCodec::FrameInfo>& infos, KInteropPointer dst); void copyToInterop(const std::vector<SkCodec::FrameInfo>& infos, KInteropPointer dst);
......
...@@ -80,6 +80,40 @@ namespace skija { ...@@ -80,6 +80,40 @@ namespace skija {
} }
} }
} }
namespace shaper {
namespace ShapingOptions {
std::vector<SkShaper::Feature> getFeaturesFromIntsArray(KInt* featuresArray, KInt featuresLen) {
return skija::FontFeature::fromIntArray(featuresArray, featuresLen);
}
}
std::shared_ptr<UBreakIterator> graphemeBreakIterator(SkString& text) {
UErrorCode status = U_ZERO_ERROR;
ICUUText utext(utext_openUTF8(nullptr, text.c_str(), text.size(), &status));
if (U_FAILURE(status)) {
SkDEBUGF("utext_openUTF8 error: %s", u_errorName(status));
return nullptr;
}
std::shared_ptr<UBreakIterator> graphemeIter(
ubrk_open(static_cast<UBreakIteratorType>(UBreakIteratorType::UBRK_CHARACTER), uloc_getDefault(), nullptr, 0, &status),
[](UBreakIterator* p) { ubrk_close(p); }
);
if (U_FAILURE(status)) {
SkDEBUGF("ubrk_open error: %s", u_errorName(status));
return nullptr;
}
ubrk_setUText(graphemeIter.get(), utext.get(), &status);
if (U_FAILURE(status)) {
SkDEBUGF("ubrk_setUText error: %s", u_errorName(status));
return nullptr;
}
return graphemeIter;
}
}
} }
skija::UtfIndicesConverter::UtfIndicesConverter(const char* chars8, size_t len8): skija::UtfIndicesConverter::UtfIndicesConverter(const char* chars8, size_t len8):
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
#include "src/utils/SkUTF.h" #include "src/utils/SkUTF.h"
#include "unicode/ubidi.h" #include "unicode/ubidi.h"
#include "common.h" #include "common.h"
#include "FontRunIterator.hh"
#include "src/utils/SkUTF.h"
#include "TextLineRunHandler.hh"
static void deleteShaper(SkShaper* instance) { static void deleteShaper(SkShaper* instance) {
// std::cout << "Deleting [SkShaper " << instance << "]" << std::endl; // std::cout << "Deleting [SkShaper " << instance << "]" << std::endl;
...@@ -55,13 +58,91 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nMake ...@@ -55,13 +58,91 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nMake
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeBlob SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeBlob
(KNativePointer ptr, KInteropPointer textObj, KNativePointer fontPtr, KInteropPointer opts, KFloat width, KFloat offsetX, KFloat offsetY) { (KNativePointer ptr, KNativePointer textPtr, KNativePointer fontPtr, KInt optsFeaturesLen, KInt* optsFeatures, KInt optsBooleanProps, KFloat width, KFloat offsetX, KFloat offsetY) {
TODO("implement org_jetbrains_skia_shaper_Shaper__1nShapeBlob"); SkShaper* instance = reinterpret_cast<SkShaper*>(ptr);
SkString& text = *(reinterpret_cast<SkString*>(textPtr));
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast<SkFont*>(fontPtr);
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(optsFeatures, optsFeaturesLen);
bool aproximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool aproximateSpaces = (optsBooleanProps & 0x02) != 0;
bool isLeftToRight = (optsBooleanProps & 0x04) != 0;
uint8_t defaultBiDiLevel = isLeftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0;
std::unique_ptr<SkShaper::ScriptRunIterator> scriptRunIter(SkShaper::MakeHbIcuScriptRunIterator(text.c_str(), text.size()));
if (!scriptRunIter) return 0;
std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size()));
if (!languageRunIter) return 0;
FontRunIterator fontRunIter(
text.c_str(),
text.size(),
*font,
SkFontMgr::RefDefault(),
graphemeIter,
aproximateSpaces,
aproximatePunctuation
);
SkTextBlobBuilderRunHandler rh(text.c_str(), {offsetX, offsetY});
instance->shape(text.c_str(), text.size(), fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), width, &rh);
SkTextBlob* blob = rh.makeBlob().release();
return reinterpret_cast<KNativePointer>(blob);
} }
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeLine SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeLine
(KNativePointer ptr, KInteropPointer textObj, KNativePointer fontPtr, KInteropPointer opts) { (KNativePointer ptr, KNativePointer textManagedStringPtr, KNativePointer fontPtr, KInt optsFeaturesLen, KInt* optsFeatures, KInt optsBooleanProps) {
TODO("implement org_jetbrains_skia_shaper_Shaper__1nShapeLine"); SkShaper* instance = reinterpret_cast<SkShaper*>(ptr);
SkString& text = *(reinterpret_cast<SkString*>(textManagedStringPtr));
SkFont* font = reinterpret_cast<SkFont*>(fontPtr);
if (text.size() == 0) {
return reinterpret_cast<KNativePointer>(new TextLine(*font));
}
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0;
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(optsFeatures, optsFeaturesLen);
bool aproximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool aproximateSpaces = (optsBooleanProps & 0x02) != 0;
bool isLeftToRight = (optsBooleanProps & 0x04) != 0;
uint8_t defaultBiDiLevel = isLeftToRight ? UBIDI_DEFAULT_LTR : UBIDI_DEFAULT_RTL;
std::unique_ptr<SkShaper::BiDiRunIterator> bidiRunIter(SkShaper::MakeBiDiRunIterator(text.c_str(), text.size(), defaultBiDiLevel));
if (!bidiRunIter) return 0;
std::unique_ptr<SkShaper::ScriptRunIterator> scriptRunIter(SkShaper::MakeHbIcuScriptRunIterator(text.c_str(), text.size()));
if (!scriptRunIter) return 0;
std::unique_ptr<SkShaper::LanguageRunIterator> languageRunIter(SkShaper::MakeStdLanguageRunIterator(text.c_str(), text.size()));
if (!languageRunIter) return 0;
FontRunIterator fontRunIter(
text.c_str(),
text.size(),
*font,
SkFontMgr::RefDefault(),
graphemeIter,
aproximateSpaces,
aproximatePunctuation);
TextLineRunHandler rh(text, graphemeIter);
instance->shape(text.c_str(), text.size(), fontRunIter, *bidiRunIter, *scriptRunIter, *languageRunIter, features.data(), features.size(), std::numeric_limits<float>::infinity(), &rh);
return reinterpret_cast<KNativePointer>(rh.makeLine().release());
} }
SKIKO_EXPORT void org_jetbrains_skia_shaper_Shaper__1nShape 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, KInteropPointer opts, KFloat width, KInteropPointer runHandlerObj)
{ {
......
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