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)
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()
}
internal fun InteropScope.toInterop(fontFeatures: Array<FontFeature>?): InteropPointer {
val ints = fontFeatures?.flatMap {
it.toInteropIntArray().toList()
}?.toIntArray()
internal fun InteropScope.arrayOfFontFeaturesToInterop(fontFeatures: Array<FontFeature>?): InteropPointer {
val ints = IntArray(4 * (fontFeatures?.size ?: 0))
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)
}
......
......@@ -191,11 +191,20 @@ class TextBlob internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
val glyphs: ShortArray
get() = try {
Stats.onNativeCall()
_nGetGlyphs(_ptr)
withResult(ShortArray(glyphsLength)) {
_nGetGlyphs(_ptr, it)
}
} finally {
reachabilityBarrier(this)
}
internal val glyphsLength: Int
get() = try {
Stats.onNativeCall()
_nGetGlyphsLength(_ptr)
} finally {
reachabilityBarrier(this)
}
/**
*
* Return result depends on how blob was constructed.
......@@ -325,8 +334,11 @@ private external fun _nMakeFromPos(glyphs: InteropPointer, pos: InteropPointer,
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nMakeFromRSXform")
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")
private external fun _nGetGlyphs(ptr: NativePointer): ShortArray
private external fun _nGetGlyphs(ptr: NativePointer, result: InteropPointer)
@ExternalSymbolName("org_jetbrains_skia_TextBlob__1nGetPositions")
private external fun _nGetPositions(ptr: NativePointer): FloatArray
......
......@@ -31,6 +31,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val capHeight: Float
get() {
Stats.onNativeCall()
......@@ -40,6 +41,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val xHeight: Float
get() {
Stats.onNativeCall()
......@@ -49,6 +51,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val descent: Float
get() {
Stats.onNativeCall()
......@@ -58,6 +61,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val leading: Float
get() {
Stats.onNativeCall()
......@@ -67,6 +71,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val width: Float
get() {
Stats.onNativeCall()
......@@ -76,6 +81,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val height: Float
get() {
Stats.onNativeCall()
......@@ -85,6 +91,7 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val textBlob: TextBlob?
get() {
Stats.onNativeCall()
......@@ -95,11 +102,25 @@ class TextLine internal constructor(ptr: NativePointer) : Managed(ptr, _Finalize
reachabilityBarrier(this)
}
}
val glyphs: ShortArray
get() {
Stats.onNativeCall()
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 {
reachabilityBarrier(this)
}
......@@ -204,8 +225,11 @@ private external fun TextLine_nGetWidth(ptr: NativePointer): Float
@ExternalSymbolName("org_jetbrains_skia_TextLine__1nGetHeight")
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")
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")
private external fun TextLine_nGetPositions(ptr: NativePointer): FloatArray
......
......@@ -3,13 +3,9 @@ package org.jetbrains.skia.shaper
import org.jetbrains.skia.impl.Library.Companion.staticLoad
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.impl.NativePointer
import org.jetbrains.skia.impl.getPtr
import org.jetbrains.skia.FontFeature.Companion.arrayOfFontFeaturesToInterop
import org.jetbrains.skia.impl.*
/**
* Shapes text using HarfBuzz and places the shaped text into a
......@@ -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? {
return try {
Stats.onNativeCall()
val ptr = _nShapeBlob(
_ptr,
text,
getPtr(font),
opts,
width,
offset.x,
offset.y
)
val ptr = interopScope {
_nShapeBlob(
_ptr,
ManagedString(text)._ptr,
getPtr(font),
optsFeaturesLen = opts.features?.size ?: 0,
optsFeaturesIntArray = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt(),
width = width,
offsetX = offset.x,
offsetY = offset.y
)
}
if (NullPointer == ptr) null else TextBlob(ptr)
} finally {
reachabilityBarrier(this)
......@@ -185,31 +185,39 @@ class Shaper internal constructor(ptr: NativePointer) : Managed(ptr, _FinalizerH
runHandler: RunHandler
): Shaper {
Stats.onNativeCall()
_nShape(
_ptr,
getPtr(textUtf8),
fontIter,
bidiIter,
scriptIter,
langIter,
opts,
width,
runHandler
)
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
)
}
return this
}
fun shapeLine(text: String?, font: Font?, opts: ShapingOptions): TextLine {
return try {
Stats.onNativeCall()
TextLine(
_nShapeLine(
_ptr,
text,
getPtr(font),
opts
interopScope {
TextLine(
_nShapeLine(
_ptr,
ManagedString(text)._ptr,
getPtr(font),
optsFeaturesLen = opts.features?.size ?: 0,
optsFeatures = arrayOfFontFeaturesToInterop(opts.features),
optsBooleanProps = opts._booleanPropsToInt()
)
)
)
}
} finally {
reachabilityBarrier(this)
reachabilityBarrier(font)
......@@ -250,9 +258,11 @@ private external fun _nMakeCoreText(): NativePointer
@ExternalSymbolName("org_jetbrains_skia_shaper_Shaper__1nShapeBlob")
private external fun _nShapeBlob(
ptr: NativePointer,
text: String?,
text: NativePointer,
fontPtr: NativePointer,
opts: ShapingOptions?,
optsFeaturesLen: Int,
optsFeaturesIntArray: InteropPointer,
optsBooleanProps: Int,
width: Float,
offsetX: Float,
offsetY: Float
......@@ -260,7 +270,14 @@ private external fun _nShapeBlob(
@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")
private external fun _nShape(
......@@ -270,7 +287,9 @@ private external fun _nShape(
bidiIter: Iterator<BidiRun?>?,
scriptIter: Iterator<ScriptRun?>?,
langIter: Iterator<LanguageRun?>?,
opts: ShapingOptions?,
optsFeaturesLen: Int,
optsFeaturesIntArray: InteropPointer,
optsBooleanProps: Int,
width: Float,
runHandler: RunHandler?
)
......@@ -5,7 +5,7 @@ import org.jetbrains.skia.FontMgr
class ShapingOptions(
internal val fontMgr: FontMgr?,
internal val features: Array<FontFeature?>?,
internal val features: Array<FontFeature>?,
internal val isLeftToRight: Boolean,
/**
* If enabled, fallback font runs will not be broken by whitespace from original font
......@@ -17,18 +17,26 @@ class ShapingOptions(
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 punctuation from original font
*/
fun withFeatures(features: Array<FontFeature?>?): ShapingOptions {
fun withFeatures(features: Array<FontFeature>?): ShapingOptions {
return ShapingOptions(fontMgr, features, isLeftToRight, isApproximateSpaces, isApproximatePunctuation)
}
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(
featuresString
)
......@@ -111,4 +119,4 @@ class ShapingOptions(
companion object {
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:
}
};
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) {
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::Iter iter(*instance);
SkTextBlob::Iter::Run run;
std::vector<jshort> glyphs;
size_t stored = 0;
jshort * shorts = env->GetShortArrayElements(resultArray, nullptr);
while (iter.next(&run)) {
glyphs.resize(stored + run.fGlyphCount);
memcpy(&glyphs[stored], run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
memcpy(shorts + stored, run.fGlyphIndices, run.fGlyphCount * sizeof(uint16_t));
stored += run.fGlyphCount;
}
return javaShortArray(env, glyphs);
env->ReleaseShortArrayElements(resultArray, shorts, 0);
}
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
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (!runRecord->isExtended())
return nullptr;
skija::UtfIndicesConverter conv(runRecord->textBuffer(), runRecord->textSize());
clusters.resize(stored + run.fGlyphCount);
uint32_t* clusterBuffer = runRecord->clusterBuffer();
......@@ -217,7 +230,7 @@ extern "C" JNIEXPORT jintArray JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGet
clusters[stored + i] = runStart16 + conv.from8To16(clusterBuffer[i]);
runStart16 += conv.from8To16(runRecord->textSize());
// memcpy(&clusters[stored], runRecord->clusterBuffer(), run.fGlyphCount * sizeof(uint32_t));
stored += run.fGlyphCount;
}
return javaIntArray(env, clusters);
......@@ -235,7 +248,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetTi
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
if (runRecord->positioning() != 2) // kFull_Positioning
return nullptr;
runRecord->fFont.measureText(runRecord->glyphBuffer(), run.fGlyphCount * sizeof(uint16_t), SkTextEncoding::kGlyphID, &tmpBounds, nullptr);
SkScalar* posBuffer = runRecord->posBuffer();
tmpBounds.offset(posBuffer[0], posBuffer[1]);
......@@ -251,7 +264,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl
SkTextBlob::Iter::Run run;
auto bounds = SkRect::MakeEmpty();
SkFontMetrics metrics;
while (iter.next(&run)) {
// run.fGlyphIndices points directly to runRecord.glyphBuffer(), which comes directly after RunRecord itself
auto runRecord = reinterpret_cast<const RunRecordClone*>(run.fGlyphIndices) - 1;
......@@ -261,14 +274,14 @@ extern "C" JNIEXPORT jobject JNICALL Java_org_jetbrains_skia_TextBlobKt__1nGetBl
SkScalar* posBuffer = runRecord->posBuffer();
const SkFont& font = runRecord->fFont;
font.getMetrics(&metrics);
SkScalar lastLeft = posBuffer[(run.fGlyphCount - 1) * 2];
SkScalar lastWidth;
if (run.fGlyphCount > 1 && SkScalarNearlyEqual(posBuffer[(run.fGlyphCount - 2) * 2], lastLeft))
lastWidth = 0;
else
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);
bounds.join(runBounds);
}
......
......@@ -65,17 +65,32 @@ extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skia_TextLineKt__1nGetText
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) {
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 freeBytesN = resultLength * sizeof(uint16_t);
for (auto& run: instance->fRuns) {
memcpy(glyphs.data() + idx, run.fGlyphs, run.fGlyphCount * sizeof(uint16_t));
idx += run.fGlyphCount;
size_t addBytesLen = run.fGlyphCount * sizeof(uint16_t);
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);
return javaShortArray(env, glyphs);
}
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
}
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
}
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));
SkString text = skString(env, textObj);
SkString& text = *(reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr)));
std::shared_ptr<UBreakIterator> graphemeIter = skija::shaper::graphemeBreakIterator(text);
if (!graphemeIter) return 0;
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(
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;
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(env, optsFeatures, optsFeaturesLen);
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));
if (!bidiRunIter) return 0;
......@@ -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()));
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);
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<jlong>(blob);
}
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));
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);
if (!graphemeIter) return 0;
SkFont* font = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr));
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeatures(env, opts);
std::vector<SkShaper::Feature> features = skija::shaper::ShapingOptions::getFeaturesFromIntsArray(env, optsFeatures, optsFeaturesLen);
std::unique_ptr<SkShaper::FontRunIterator> fontRunIter(new FontRunIterator(
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;
bool aproximatePunctuation = (optsBooleanProps & 0x01) != 0;
bool aproximateSpaces = (optsBooleanProps & 0x02) != 0;
bool isLeftToRight = (optsBooleanProps & 0x04) != 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));
if (!bidiRunIter) return 0;
......@@ -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()));
if (!languageRunIter) return 0;
TextLine* line;
if (text.size() == 0)
line = new TextLine(*font);
else {
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);
line = rh.makeLine().release();
}
return reinterpret_cast<jlong>(line);
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<jlong>(rh.makeLine().release());
}
template <typename RunIteratorSubclass>
......@@ -160,7 +167,7 @@ public:
size_t endOfCurrentRun() const override {
return fEndOfRun;
}
bool atEnd() const override {
return !fHasNext;
}
......@@ -177,7 +184,7 @@ protected:
class SkijaFontRunIterator: public SkijaRunIterator<SkShaper::FontRunIterator> {
public:
SkijaFontRunIterator(JNIEnv* env, jobject obj, SkString text):
SkijaFontRunIterator(JNIEnv* env, jobject obj, SkString text):
SkijaRunIterator<SkShaper::FontRunIterator>(env, obj, text)
{}
......@@ -198,7 +205,7 @@ protected:
class SkijaBidiRunIterator: public SkijaRunIterator<SkShaper::BiDiRunIterator> {
public:
SkijaBidiRunIterator(JNIEnv* env, jobject obj, SkString& text):
SkijaBidiRunIterator(JNIEnv* env, jobject obj, SkString& text):
SkijaRunIterator<SkShaper::BiDiRunIterator>(env, obj, text)
{}
......@@ -210,7 +217,7 @@ public:
fLevel = fEnv->GetIntField(runObj, skija::shaper::BidiRun::_level) & 0xFF;
return fEnv->GetIntField(runObj, skija::shaper::BidiRun::_end);
}
protected:
uint8_t fLevel;
};
......@@ -229,7 +236,7 @@ public:
fScript = fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_scriptTag);
return fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_end);
}
protected:
uint32_t fScript;
};
......@@ -249,7 +256,7 @@ public:
fLang = skString(fEnv, langObj);
return fEnv->GetIntField(runObj, skija::shaper::LanguageRun::_end);
}
protected:
SkString fLang;
};
......@@ -329,7 +336,7 @@ private:
};
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));
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
std::unique_ptr<SkijaScriptRunIterator> localScriptRunIter;
if (nativeScriptRunIter == nullptr)
localScriptRunIter.reset(new SkijaScriptRunIterator(env, scriptRunIterObj, *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);
std::unique_ptr<SkijaRunHandler> localRunHandler;
if (nativeRunHandler == nullptr)
......@@ -367,4 +374,4 @@ extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skia_shaper_ShaperKt__1nSha
features.size(),
width,
nativeRunHandler != nullptr ? nativeRunHandler : localRunHandler.get());
}
\ No newline at end of file
}
......@@ -46,7 +46,7 @@ namespace skija {
void onLoad(JNIEnv* env) {
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) {
......@@ -60,7 +60,7 @@ namespace skija {
void onLoad(JNIEnv* env) {
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) {
......@@ -124,7 +124,7 @@ namespace skija {
jobject toJava(JNIEnv* env, const SkShaper::RunHandler::RunInfo& info, size_t begin, size_t end) {
SkFont* font = new SkFont(info.fFont);
return env->NewObject(
cls,
cls,
ctor,
reinterpret_cast<jlong>(font),
info.fBidiLevel,
......@@ -172,6 +172,10 @@ namespace skija {
std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts) {
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 {
......@@ -186,7 +190,7 @@ namespace skija {
env->DeleteGlobalRef(cls);
}
}
void onLoad(JNIEnv* env) {
BidiRun::onLoad(env);
FontMgrRunIterator::onLoad(env);
......@@ -214,7 +218,7 @@ namespace skija {
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));
......@@ -236,7 +240,7 @@ namespace skija {
return nullptr;
}
return graphemeIter;
return graphemeIter;
}
}
}
\ No newline at end of file
}
......@@ -57,7 +57,7 @@ namespace skija {
extern jmethodID runOffset;
extern jmethodID commitRun;
extern jmethodID commitLine;
void onLoad(JNIEnv* env);
void onUnload(JNIEnv* env);
}
......@@ -89,6 +89,7 @@ namespace skija {
void onLoad(JNIEnv* env);
std::vector<SkShaper::Feature> getFeatures(JNIEnv* env, jobject opts);
std::vector<SkShaper::Feature> getFeaturesFromIntsArray(JNIEnv* env, jintArray featuresArray, jint featuresLen);
}
namespace TextBlobBuilderRunHandler {
......@@ -97,11 +98,11 @@ namespace skija {
void onLoad(JNIEnv* env);
void onUnload(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>>;
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
(KShort* glyphsArr, KFloat* posArr, KNativePointer fontPtr ) {
TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromPos");
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromRSXform
(KShort* glyphsArr, KFloat* xformArr, KNativePointer fontPtr ) {
TODO("implement org_jetbrains_skia_TextBlob__1nMakeFromRSXform");
}
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nSerializeToData
(KNativePointer ptr) {
SkTextBlob* instance = reinterpret_cast<SkTextBlob*>((ptr));
......@@ -63,10 +63,29 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextBlob__1nMakeFromData
return reinterpret_cast<KNativePointer>(instance);
}
SKIKO_EXPORT KShort* org_jetbrains_skia_TextBlob__1nGetGlyphs
SKIKO_EXPORT KInt org_jetbrains_skia_TextBlob__1nGetGlyphsLength
(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
......@@ -78,12 +97,12 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextBlob__1nGetClusters
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetClusters");
}
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetTightBounds
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetTightBounds");
}
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetBlockBounds
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetBlockBounds");
......@@ -97,4 +116,4 @@ SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetFirstBaseline
SKIKO_EXPORT KInteropPointer org_jetbrains_skia_TextBlob__1nGetLastBaseline
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextBlob__1nGetLastBaseline");
}
\ No newline at end of file
}
......@@ -5,28 +5,24 @@
#include <iostream>
#include "SkShaper.h"
#include "common.h"
#include "TextLine.hh"
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetFinalizer
() {
TODO("implement org_jetbrains_skia_TextLine__1nGetFinalizer");
static void unrefTextLine(TextLine* ptr) {
ptr->unref();
}
#if 0
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
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetAscent");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetAscent
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -40,8 +36,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetCapHeight");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCapHeight
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -55,8 +51,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetXHeight");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetXHeight
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -70,8 +66,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetDescent");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetDescent
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -85,8 +81,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetLeading");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetLeading
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -100,8 +96,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetWidth");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetWidth
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -115,8 +111,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetHeight");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetHeight
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -130,8 +126,8 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetTextBlob");
}
#if 0
#if 0
SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -143,35 +139,40 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_TextLine__1nGetTextBlob
#endif
SKIKO_EXPORT KShort* org_jetbrains_skia_TextLine__1nGetGlyphs
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetGlyphsLength
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetGlyphs");
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
return instance->fGlyphCount;
}
#if 0
SKIKO_EXPORT KShort* org_jetbrains_skia_TextLine__1nGetGlyphs
(KNativePointer ptr) {
SKIKO_EXPORT void org_jetbrains_skia_TextLine__1nGetGlyphs
(KNativePointer ptr, KInteropPointer glyphs, KInt resultLength) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
std::vector<KShort> glyphs(instance->fGlyphCount);
size_t idx = 0;
size_t freeBytesN = resultLength * sizeof(uint16_t);
KShort* glyphsPtr = reinterpret_cast<KShort*>(glyphs);
for (auto& run: instance->fRuns) {
memcpy(glyphs.data() + idx, run.fGlyphs, run.fGlyphCount * sizeof(uint16_t));
idx += run.fGlyphCount;
size_t addBytesLen = run.fGlyphCount * sizeof(uint16_t);
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);
return javaShortArray(env, glyphs);
}
#endif
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetPositions");
}
#if 0
#if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetPositions
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -192,8 +193,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetRunPositions");
}
#if 0
#if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetRunPositions
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -210,8 +211,8 @@ SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetBreakPositions");
}
#if 0
#if 0
SKIKO_EXPORT KFloat* org_jetbrains_skia_TextLine__1nGetBreakPositions
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -228,8 +229,8 @@ SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets
(KNativePointer ptr) {
TODO("implement org_jetbrains_skia_TextLine__1nGetBreakOffsets");
}
#if 0
#if 0
SKIKO_EXPORT KInt* org_jetbrains_skia_TextLine__1nGetBreakOffsets
(KNativePointer ptr) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -246,8 +247,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord
(KNativePointer ptr, KFloat x) {
TODO("implement org_jetbrains_skia_TextLine__1nGetOffsetAtCoord");
}
#if 0
#if 0
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetOffsetAtCoord
(KNativePointer ptr, KFloat x) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -275,8 +276,8 @@ SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord
(KNativePointer ptr, KFloat x) {
TODO("implement org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord");
}
#if 0
#if 0
SKIKO_EXPORT KInt org_jetbrains_skia_TextLine__1nGetLeftOffsetAtCoord
(KNativePointer ptr, KFloat x) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......@@ -302,8 +303,8 @@ SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset
(KNativePointer ptr, KInt offset16) {
TODO("implement org_jetbrains_skia_TextLine__1nGetCoordAtOffset");
}
#if 0
#if 0
SKIKO_EXPORT KFloat org_jetbrains_skia_TextLine__1nGetCoordAtOffset
(KNativePointer ptr, KInt offset16) {
TextLine* instance = reinterpret_cast<TextLine*>((ptr));
......
......@@ -21,6 +21,10 @@
#include "SkSurfaceProps.h"
#include "SkSVGTypes.h"
#include "TextStyle.h"
#include "SkFont.h"
#include "SkShaper.h"
#include "SkLoadICU.h"
#include "unicode/ubrk.h"
#include "types.h"
......@@ -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 {
void copyToInterop(const SkCodec::FrameInfo& info, KInteropPointer dst);
void copyToInterop(const std::vector<SkCodec::FrameInfo>& infos, KInteropPointer dst);
......
......@@ -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):
......
......@@ -6,6 +6,9 @@
#include "src/utils/SkUTF.h"
#include "unicode/ubidi.h"
#include "common.h"
#include "FontRunIterator.hh"
#include "src/utils/SkUTF.h"
#include "TextLineRunHandler.hh"
static void deleteShaper(SkShaper* instance) {
// std::cout << "Deleting [SkShaper " << instance << "]" << std::endl;
......@@ -55,13 +58,91 @@ SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nMake
SKIKO_EXPORT KNativePointer org_jetbrains_skia_shaper_Shaper__1nShapeBlob
(KNativePointer ptr, KInteropPointer textObj, KNativePointer fontPtr, KInteropPointer opts, KFloat width, KFloat offsetX, KFloat offsetY) {
TODO("implement org_jetbrains_skia_shaper_Shaper__1nShapeBlob");
(KNativePointer ptr, KNativePointer textPtr, KNativePointer fontPtr, KInt optsFeaturesLen, KInt* optsFeatures, KInt optsBooleanProps, KFloat width, KFloat offsetX, KFloat offsetY) {
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
(KNativePointer ptr, KInteropPointer textObj, KNativePointer fontPtr, KInteropPointer opts) {
TODO("implement org_jetbrains_skia_shaper_Shaper__1nShapeLine");
(KNativePointer ptr, KNativePointer textManagedStringPtr, KNativePointer fontPtr, KInt optsFeaturesLen, KInt* optsFeatures, KInt optsBooleanProps) {
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
(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