platform/cc/shaper/Shaper.cc (305 lines of code) (raw):

#include <iostream> #include <jni.h> #include "../interop.hh" #include "interop.hh" #include "FontRunIterator.hh" #include "SkShaper.h" #include "src/utils/SkUTF.h" #include "TextLineRunHandler.hh" #include "unicode/ubidi.h" static void deleteShaper(SkShaper* instance) { // std::cout << "Deleting [SkShaper " << instance << "]" << std::endl; delete instance; } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nGetFinalizer(JNIEnv* env, jclass jclass) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deleteShaper)); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMakePrimitive (JNIEnv* env, jclass jclass) { return reinterpret_cast<jlong>(SkShaper::MakePrimitive().release()); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMakeShaperDrivenWrapper (JNIEnv* env, jclass jclass, jlong fontMgrPtr) { SkFontMgr* fontMgr = reinterpret_cast<SkFontMgr*>(static_cast<uintptr_t>(fontMgrPtr)); return reinterpret_cast<jlong>(SkShaper::MakeShaperDrivenWrapper(sk_ref_sp(fontMgr)).release()); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMakeShapeThenWrap (JNIEnv* env, jclass jclass, jlong fontMgrPtr) { SkFontMgr* fontMgr = reinterpret_cast<SkFontMgr*>(static_cast<uintptr_t>(fontMgrPtr)); return reinterpret_cast<jlong>(SkShaper::MakeShapeThenWrap(sk_ref_sp(fontMgr)).release()); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMakeShapeDontWrapOrReorder (JNIEnv* env, jclass jclass, jlong fontMgrPtr) { SkFontMgr* fontMgr = reinterpret_cast<SkFontMgr*>(static_cast<uintptr_t>(fontMgrPtr)); return reinterpret_cast<jlong>(SkShaper::MakeShapeDontWrapOrReorder(sk_ref_sp(fontMgr)).release()); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMakeCoreText (JNIEnv* env, jclass jclass) { #ifdef SK_SHAPER_CORETEXT_AVAILABLE return reinterpret_cast<jlong>(SkShaper::MakeCoreText().release()); #else return 0; #endif } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nMake (JNIEnv* env, jclass jclass, jlong fontMgrPtr) { SkFontMgr* fontMgr = reinterpret_cast<SkFontMgr*>(static_cast<uintptr_t>(fontMgrPtr)); return reinterpret_cast<jlong>(SkShaper::Make(sk_ref_sp(fontMgr)).release()); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShapeBlob (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts, jfloat width, jfloat offsetX, jfloat offsetY) { SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr)); SkString text = skString(env, textObj); 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; uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? 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; 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<jlong>(blob); } extern "C" JNIEXPORT jlong JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShapeLine (JNIEnv* env, jclass jclass, jlong ptr, jstring textObj, jlong fontPtr, jobject opts) { SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr)); SkString text = skString(env, textObj); 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; uint8_t defaultBiDiLevel = env->GetBooleanField(opts, skija::shaper::ShapingOptions::_leftToRight) ? 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; 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); } template <typename RunIteratorSubclass> class SkijaRunIterator: public RunIteratorSubclass { public: SkijaRunIterator(JNIEnv* env, jobject obj, SkString& text): fEnv(env), fIteratorObj(obj), fIndicesConverter(text.c_str(), text.size()), fEndOfRun(0) { fHasNext = fEnv->CallBooleanMethod(fIteratorObj, java::util::Iterator::hasNext); java::lang::Throwable::exceptionThrown(fEnv); } void consume() override { SkASSERT(fHasNext); skija::AutoLocal<jobject> runObj(fEnv, fEnv->CallObjectMethod(fIteratorObj, java::util::Iterator::next)); java::lang::Throwable::exceptionThrown(fEnv); jint endOfRun16 = onConsume(runObj.get()); fEndOfRun = fIndicesConverter.from16To8(endOfRun16); fHasNext = fEnv->CallBooleanMethod(fIteratorObj, java::util::Iterator::hasNext); java::lang::Throwable::exceptionThrown(fEnv); } size_t endOfCurrentRun() const override { return fEndOfRun; } bool atEnd() const override { return !fHasNext; } protected: JNIEnv* fEnv; jobject fIteratorObj; skija::UtfIndicesConverter fIndicesConverter; size_t fEndOfRun; bool fHasNext; virtual jint onConsume(jobject) = 0; }; class SkijaFontRunIterator: public SkijaRunIterator<SkShaper::FontRunIterator> { public: SkijaFontRunIterator(JNIEnv* env, jobject obj, SkString text): SkijaRunIterator<SkShaper::FontRunIterator>(env, obj, text) {} const SkFont& currentFont() const override { return *fFont; } jint onConsume(jobject runObj) override { jlong fontPtr = fEnv->CallLongMethod(runObj, skija::shaper::FontRun::_getFontPtr); java::lang::Throwable::exceptionThrown(fEnv); fFont = reinterpret_cast<SkFont*>(static_cast<uintptr_t>(fontPtr)); return fEnv->GetIntField(runObj, skija::shaper::FontRun::_end); } protected: SkFont* fFont; }; class SkijaBidiRunIterator: public SkijaRunIterator<SkShaper::BiDiRunIterator> { public: SkijaBidiRunIterator(JNIEnv* env, jobject obj, SkString& text): SkijaRunIterator<SkShaper::BiDiRunIterator>(env, obj, text) {} uint8_t currentLevel() const override { return fLevel; } jint onConsume(jobject runObj) override { fLevel = fEnv->GetIntField(runObj, skija::shaper::BidiRun::_level) & 0xFF; return fEnv->GetIntField(runObj, skija::shaper::BidiRun::_end); } protected: uint8_t fLevel; }; class SkijaScriptRunIterator: public SkijaRunIterator<SkShaper::ScriptRunIterator> { public: SkijaScriptRunIterator(JNIEnv* env, jobject obj, SkString text): SkijaRunIterator<SkShaper::ScriptRunIterator>(env, obj, text) {} SkFourByteTag currentScript() const override { return fScript; } jint onConsume(jobject runObj) override { fScript = fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_scriptTag); return fEnv->GetIntField(runObj, skija::shaper::ScriptRun::_end); } protected: uint32_t fScript; }; class SkijaLanguageRunIterator: public SkijaRunIterator<SkShaper::LanguageRunIterator> { public: SkijaLanguageRunIterator(JNIEnv* env, jobject obj, SkString text): SkijaRunIterator<SkShaper::LanguageRunIterator>(env, obj, text) {} const char* currentLanguage() const override { return fLang.c_str(); } jint onConsume(jobject runObj) override { jstring langObj = static_cast<jstring>(fEnv->GetObjectField(runObj, skija::shaper::LanguageRun::_language)); fLang = skString(fEnv, langObj); return fEnv->GetIntField(runObj, skija::shaper::LanguageRun::_end); } protected: SkString fLang; }; class SkijaRunHandler: public SkShaper::RunHandler { public: SkijaRunHandler(JNIEnv* env, jobject runHandler, const SkString& text): fEnv(env), fRunHandler(runHandler), fIndicesConverter(text.c_str(), text.size()) {} void beginLine() { fEnv->CallVoidMethod(fRunHandler, skija::shaper::RunHandler::beginLine); java::lang::Throwable::exceptionThrown(fEnv); } void runInfo(const SkShaper::RunHandler::RunInfo& info) { skija::AutoLocal<jobject> runInfoObj(fEnv, skija::shaper::RunInfo::toJava(fEnv, info, fIndicesConverter)); fEnv->CallVoidMethod(fRunHandler, skija::shaper::RunHandler::runInfo, runInfoObj.get()); java::lang::Throwable::exceptionThrown(fEnv); fEnv->SetLongField(runInfoObj.get(), skija::shaper::RunInfo::_fontPtr, 0); } void commitRunInfo() { fEnv->CallVoidMethod(fRunHandler, skija::shaper::RunHandler::commitRunInfo); java::lang::Throwable::exceptionThrown(fEnv); } SkShaper::RunHandler::Buffer runBuffer(const SkShaper::RunHandler::RunInfo& info) { fGlyphs = std::vector<jshort>(info.glyphCount); fPositions = std::vector<SkPoint>(info.glyphCount); fClusters = std::vector<jint>(info.glyphCount); skija::AutoLocal<jobject> runInfoObj(fEnv, skija::shaper::RunInfo::toJava(fEnv, info, fIndicesConverter)); skija::AutoLocal<jobject> point(fEnv, fEnv->CallObjectMethod(fRunHandler, skija::shaper::RunHandler::runOffset, runInfoObj.get())); java::lang::Throwable::exceptionThrown(fEnv); fEnv->SetLongField(runInfoObj.get(), skija::shaper::RunInfo::_fontPtr, 0); jfloat x = fEnv->GetFloatField(point.get(), skija::Point::x); jfloat y = fEnv->GetFloatField(point.get(), skija::Point::y); return SkShaper::RunHandler::Buffer{ reinterpret_cast<SkGlyphID*>(fGlyphs.data()), fPositions.data(), nullptr, reinterpret_cast<uint32_t*>(fClusters.data()), {x, y}}; } void commitRunBuffer(const SkShaper::RunHandler::RunInfo& info) { size_t begin = fIndicesConverter.from8To16(info.utf8Range.fBegin); for (int i = 0; i < fClusters.size(); ++i) fClusters[i] = fIndicesConverter.from8To16(fClusters[i]); skija::AutoLocal<jintArray> clusters(fEnv, javaIntArray(fEnv, fClusters)); size_t end = fIndicesConverter.from8To16(info.utf8Range.fBegin + info.utf8Range.fSize); skija::AutoLocal<jobject> runInfoObj(fEnv, skija::shaper::RunInfo::toJava(fEnv, info, begin, end)); skija::AutoLocal<jshortArray> glyphs(fEnv, javaShortArray(fEnv, fGlyphs)); skija::AutoLocal<jobjectArray> positions(fEnv, skija::Point::fromSkPoints(fEnv, fPositions)); fEnv->CallVoidMethod(fRunHandler, skija::shaper::RunHandler::commitRun, runInfoObj.get(), glyphs.get(), positions.get(), clusters.get()); java::lang::Throwable::exceptionThrown(fEnv); fEnv->SetLongField(runInfoObj.get(), skija::shaper::RunInfo::_fontPtr, 0); } void commitLine() { fEnv->CallVoidMethod(fRunHandler, skija::shaper::RunHandler::commitLine); java::lang::Throwable::exceptionThrown(fEnv); } private: JNIEnv* fEnv; jobject fRunHandler; skija::UtfIndicesConverter fIndicesConverter; std::vector<jshort> fGlyphs; std::vector<SkPoint> fPositions; std::vector<jint> fClusters; }; extern "C" JNIEXPORT void JNICALL Java_org_jetbrains_skija_shaper_Shaper__1nShape (JNIEnv* env, jclass jclass, jlong ptr, jlong textPtr, jobject fontRunIterObj, jobject bidiRunIterObj, jobject scriptRunIterObj, jobject languageRunIterObj, jobject opts, jfloat width, jobject runHandlerObj) { SkShaper* instance = reinterpret_cast<SkShaper*>(static_cast<uintptr_t>(ptr)); SkString* text = reinterpret_cast<SkString*>(static_cast<uintptr_t>(textPtr)); auto nativeFontRunIter = (SkShaper::FontRunIterator*) skija::impl::Native::fromJava(env, fontRunIterObj, skija::shaper::FontMgrRunIterator::cls); std::unique_ptr<SkijaFontRunIterator> localFontRunIter; if (nativeFontRunIter == nullptr) localFontRunIter.reset(new SkijaFontRunIterator(env, fontRunIterObj, *text)); auto nativeBidiRunIter = (SkShaper::BiDiRunIterator*) skija::impl::Native::fromJava(env, bidiRunIterObj, skija::shaper::IcuBidiRunIterator::cls); std::unique_ptr<SkijaBidiRunIterator> localBidiRunIter; if (nativeBidiRunIter == nullptr) localBidiRunIter.reset(new SkijaBidiRunIterator(env, bidiRunIterObj, *text)); auto nativeScriptRunIter = (SkShaper::ScriptRunIterator*) skija::impl::Native::fromJava(env, scriptRunIterObj, skija::shaper::HbIcuScriptRunIterator::cls); 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); auto nativeRunHandler = (SkShaper::RunHandler*) skija::impl::Native::fromJava(env, runHandlerObj, skija::shaper::TextBlobBuilderRunHandler::cls); std::unique_ptr<SkijaRunHandler> localRunHandler; if (nativeRunHandler == nullptr) localRunHandler.reset(new SkijaRunHandler(env, runHandlerObj, *text)); instance->shape(text->c_str(), text->size(), nativeFontRunIter != nullptr ? *nativeFontRunIter : *localFontRunIter, nativeBidiRunIter != nullptr ? *nativeBidiRunIter : *localBidiRunIter, nativeScriptRunIter != nullptr ? *nativeScriptRunIter : *localScriptRunIter, languageRunIter, features.data(), features.size(), width, nativeRunHandler != nullptr ? nativeRunHandler : localRunHandler.get()); }