/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include #include "ResourceFactory.h" #include "SkStream.h" #include "SkTo.h" #include "skqp.h" //////////////////////////////////////////////////////////////////////////////// extern "C" { JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring); JNIEXPORT jlong JNICALL Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv*, jobject, jint, jint); JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv*, jobject, jint); JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject); } // extern "C" //////////////////////////////////////////////////////////////////////////////// static AAssetManager* gAAssetManager = nullptr; static sk_sp open_asset_data(const char* path) { sk_sp data; if (gAAssetManager) { if (AAsset* asset = AAssetManager_open(gAAssetManager, path, AASSET_MODE_STREAMING)) { if (size_t size = SkToSizeT(AAsset_getLength(asset))) { data = SkData::MakeUninitialized(size); int ret = AAsset_read(asset, data->writable_data(), size); if (ret != SkToInt(size)) { SkDebugf("ERROR: AAsset_read != AAsset_getLength (%s)\n", path); } } AAsset_close(asset); } } return data; } namespace { struct AndroidAssetManager : public SkQPAssetManager { sk_sp open(const char* path) override { return open_asset_data(path); } }; } // TODO(halcanary): Should not have global variables; SkQP Java object should // own pointers and manage concurency. static AndroidAssetManager gAndroidAssetManager; static std::mutex gMutex; static SkQP gSkQP; #define jassert(env, cond, ret) do { if (!(cond)) { \ (env)->ThrowNew((env)->FindClass("java/lang/Exception"), \ __FILE__ ": assert(" #cond ") failed."); \ return ret; } } while (0) static void set_string_array_element(JNIEnv* env, jobjectArray a, const char* s, unsigned i) { jstring jstr = env->NewStringUTF(s); jassert(env, jstr != nullptr,); env->SetObjectArrayElement(a, (jsize)i, jstr); env->DeleteLocalRef(jstr); } //////////////////////////////////////////////////////////////////////////////// sk_sp get_resource(const char* resource) { return open_asset_data((std::string("resources/") + resource).c_str()); } //////////////////////////////////////////////////////////////////////////////// template jobjectArray to_java_string_array(JNIEnv* env, const std::vector& array, F toString) { jclass stringClass = env->FindClass("java/lang/String"); jassert(env, stringClass, nullptr); jobjectArray jarray = env->NewObjectArray((jint)array.size(), stringClass, nullptr); jassert(env, jarray != nullptr, nullptr); for (unsigned i = 0; i < array.size(); ++i) { set_string_array_element(env, jarray, std::string(toString(array[i])).c_str(), i); } return jarray; } static std::string to_string(JNIEnv* env, jstring jString) { const char* utf8String = env->GetStringUTFChars(jString, nullptr); jassert(env, utf8String && utf8String[0], ""); std::string sString(utf8String); env->ReleaseStringUTFChars(jString, utf8String); return sString; } void Java_org_skia_skqp_SkQP_nInit(JNIEnv* env, jobject object, jobject assetManager, jstring dataDir) { jclass SkQP_class = env->GetObjectClass(object); // tools/Resources gResourceFactory = &get_resource; std::string reportDirectory = to_string(env, dataDir); jassert(env, assetManager,); // This global must be set before using AndroidAssetManager gAAssetManager = AAssetManager_fromJava(env, assetManager); jassert(env, gAAssetManager,); std::lock_guard lock(gMutex); gSkQP.init(&gAndroidAssetManager, reportDirectory.c_str()); auto backends = gSkQP.getSupportedBackends(); jassert(env, backends.size() > 0,); auto gms = gSkQP.getGMs(); jassert(env, gms.size() > 0,); auto unitTests = gSkQP.getUnitTests(); jassert(env, unitTests.size() > 0,); constexpr char kStringArrayType[] = "[Ljava/lang/String;"; env->SetObjectField(object, env->GetFieldID(SkQP_class, "mBackends", kStringArrayType), to_java_string_array(env, backends, SkQP::GetBackendName)); env->SetObjectField(object, env->GetFieldID(SkQP_class, "mUnitTests", kStringArrayType), to_java_string_array(env, unitTests, SkQP::GetUnitTestName)); env->SetObjectField(object, env->GetFieldID(SkQP_class, "mGMs", kStringArrayType), to_java_string_array(env, gms, SkQP::GetGMName)); } jlong Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv* env, jobject object, jint gmIndex, jint backendIndex) { SkQP::RenderOutcome outcome; std::string except; { std::lock_guard lock(gMutex); jassert(env, backendIndex < (jint)gSkQP.getSupportedBackends().size(), -1); jassert(env, gmIndex < (jint)gSkQP.getGMs().size(), -1); SkQP::SkiaBackend backend = gSkQP.getSupportedBackends()[backendIndex]; SkQP::GMFactory gm = gSkQP.getGMs()[gmIndex]; std::tie(outcome, except) = gSkQP.evaluateGM(backend, gm); } if (!except.empty()) { (void)env->ThrowNew(env->FindClass("org/skia/skqp/SkQPException"), except.c_str()); } return (jlong)outcome.fTotalError; } jobjectArray Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv* env, jobject object, jint index) { std::vector errors; { jassert(env, index < (jint)gSkQP.getUnitTests().size(), nullptr); std::lock_guard lock(gMutex); errors = gSkQP.executeTest(gSkQP.getUnitTests()[index]); } if (errors.size() == 0) { return nullptr; } jclass stringClass = env->FindClass("java/lang/String"); jassert(env, stringClass, nullptr); jobjectArray array = env->NewObjectArray(errors.size(), stringClass, nullptr); for (unsigned i = 0; i < errors.size(); ++i) { set_string_array_element(env, array, errors[i].c_str(), i); } return (jobjectArray)env->NewGlobalRef(array); } void Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject) { std::lock_guard lock(gMutex); gSkQP.makeReport(); } ////////////////////////////////////////////////////////////////////////////////