1 /* 2 * Copyright 2017 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include <mutex> 9 10 #include <android/asset_manager.h> 11 #include <android/asset_manager_jni.h> 12 #include <jni.h> 13 #include <sys/stat.h> 14 15 #include "ResourceFactory.h" 16 #include "SkStream.h" 17 #include "SkTo.h" 18 19 #include "skqp.h" 20 21 //////////////////////////////////////////////////////////////////////////////// 22 extern "C" { 23 JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nInit(JNIEnv*, jobject, jobject, jstring); 24 JNIEXPORT jlong JNICALL Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv*, jobject, jint, jint); 25 JNIEXPORT jobjectArray JNICALL Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv*, jobject, jint); 26 JNIEXPORT void JNICALL Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject); 27 } // extern "C" 28 //////////////////////////////////////////////////////////////////////////////// 29 30 static AAssetManager* gAAssetManager = nullptr; 31 32 static sk_sp<SkData> open_asset_data(const char* path) { 33 sk_sp<SkData> data; 34 if (gAAssetManager) { 35 if (AAsset* asset = AAssetManager_open(gAAssetManager, path, AASSET_MODE_STREAMING)) { 36 if (size_t size = SkToSizeT(AAsset_getLength(asset))) { 37 data = SkData::MakeUninitialized(size); 38 int ret = AAsset_read(asset, data->writable_data(), size); 39 if (ret != SkToInt(size)) { 40 SkDebugf("ERROR: AAsset_read != AAsset_getLength (%s)\n", path); 41 } 42 } 43 AAsset_close(asset); 44 } 45 } 46 return data; 47 } 48 49 namespace { 50 struct AndroidAssetManager : public SkQPAssetManager { 51 sk_sp<SkData> open(const char* path) override { return open_asset_data(path); } 52 }; 53 } 54 55 // TODO(halcanary): Should not have global variables; SkQP Java object should 56 // own pointers and manage concurency. 57 static AndroidAssetManager gAndroidAssetManager; 58 static std::mutex gMutex; 59 static SkQP gSkQP; 60 61 #define jassert(env, cond, ret) do { if (!(cond)) { \ 62 (env)->ThrowNew((env)->FindClass("java/lang/Exception"), \ 63 __FILE__ ": assert(" #cond ") failed."); \ 64 return ret; } } while (0) 65 66 static void set_string_array_element(JNIEnv* env, jobjectArray a, const char* s, unsigned i) { 67 jstring jstr = env->NewStringUTF(s); 68 jassert(env, jstr != nullptr,); 69 env->SetObjectArrayElement(a, (jsize)i, jstr); 70 env->DeleteLocalRef(jstr); 71 } 72 73 //////////////////////////////////////////////////////////////////////////////// 74 75 sk_sp<SkData> get_resource(const char* resource) { 76 return open_asset_data((std::string("resources/") + resource).c_str()); 77 } 78 79 //////////////////////////////////////////////////////////////////////////////// 80 81 template <typename T, typename F> 82 jobjectArray to_java_string_array(JNIEnv* env, 83 const std::vector<T>& array, 84 F toString) { 85 jclass stringClass = env->FindClass("java/lang/String"); 86 jassert(env, stringClass, nullptr); 87 jobjectArray jarray = env->NewObjectArray((jint)array.size(), stringClass, nullptr); 88 jassert(env, jarray != nullptr, nullptr); 89 for (unsigned i = 0; i < array.size(); ++i) { 90 set_string_array_element(env, jarray, std::string(toString(array[i])).c_str(), i); 91 } 92 return jarray; 93 } 94 95 static std::string to_string(JNIEnv* env, jstring jString) { 96 const char* utf8String = env->GetStringUTFChars(jString, nullptr); 97 jassert(env, utf8String && utf8String[0], ""); 98 std::string sString(utf8String); 99 env->ReleaseStringUTFChars(jString, utf8String); 100 return sString; 101 } 102 103 void Java_org_skia_skqp_SkQP_nInit(JNIEnv* env, jobject object, jobject assetManager, 104 jstring dataDir) { 105 jclass SkQP_class = env->GetObjectClass(object); 106 107 // tools/Resources 108 gResourceFactory = &get_resource; 109 110 std::string reportDirectory = to_string(env, dataDir); 111 112 jassert(env, assetManager,); 113 // This global must be set before using AndroidAssetManager 114 gAAssetManager = AAssetManager_fromJava(env, assetManager); 115 jassert(env, gAAssetManager,); 116 117 std::lock_guard<std::mutex> lock(gMutex); 118 gSkQP.init(&gAndroidAssetManager, reportDirectory.c_str()); 119 120 auto backends = gSkQP.getSupportedBackends(); 121 jassert(env, backends.size() > 0,); 122 auto gms = gSkQP.getGMs(); 123 jassert(env, gms.size() > 0,); 124 auto unitTests = gSkQP.getUnitTests(); 125 jassert(env, unitTests.size() > 0,); 126 127 constexpr char kStringArrayType[] = "[Ljava/lang/String;"; 128 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mBackends", kStringArrayType), 129 to_java_string_array(env, backends, SkQP::GetBackendName)); 130 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mUnitTests", kStringArrayType), 131 to_java_string_array(env, unitTests, SkQP::GetUnitTestName)); 132 env->SetObjectField(object, env->GetFieldID(SkQP_class, "mGMs", kStringArrayType), 133 to_java_string_array(env, gms, SkQP::GetGMName)); 134 } 135 136 jlong Java_org_skia_skqp_SkQP_nExecuteGM(JNIEnv* env, 137 jobject object, 138 jint gmIndex, 139 jint backendIndex) { 140 SkQP::RenderOutcome outcome; 141 std::string except; 142 { 143 std::lock_guard<std::mutex> lock(gMutex); 144 jassert(env, backendIndex < (jint)gSkQP.getSupportedBackends().size(), -1); 145 jassert(env, gmIndex < (jint)gSkQP.getGMs().size(), -1); 146 SkQP::SkiaBackend backend = gSkQP.getSupportedBackends()[backendIndex]; 147 SkQP::GMFactory gm = gSkQP.getGMs()[gmIndex]; 148 std::tie(outcome, except) = gSkQP.evaluateGM(backend, gm); 149 } 150 151 if (!except.empty()) { 152 (void)env->ThrowNew(env->FindClass("org/skia/skqp/SkQPException"), except.c_str()); 153 } 154 return (jlong)outcome.fTotalError; 155 } 156 157 jobjectArray Java_org_skia_skqp_SkQP_nExecuteUnitTest(JNIEnv* env, 158 jobject object, 159 jint index) { 160 std::vector<std::string> errors; 161 { 162 jassert(env, index < (jint)gSkQP.getUnitTests().size(), nullptr); 163 std::lock_guard<std::mutex> lock(gMutex); 164 errors = gSkQP.executeTest(gSkQP.getUnitTests()[index]); 165 } 166 if (errors.size() == 0) { 167 return nullptr; 168 } 169 jclass stringClass = env->FindClass("java/lang/String"); 170 jassert(env, stringClass, nullptr); 171 jobjectArray array = env->NewObjectArray(errors.size(), stringClass, nullptr); 172 for (unsigned i = 0; i < errors.size(); ++i) { 173 set_string_array_element(env, array, errors[i].c_str(), i); 174 } 175 return (jobjectArray)env->NewGlobalRef(array); 176 } 177 178 void Java_org_skia_skqp_SkQP_nMakeReport(JNIEnv*, jobject) { 179 std::lock_guard<std::mutex> lock(gMutex); 180 gSkQP.makeReport(); 181 } 182 183 //////////////////////////////////////////////////////////////////////////////// 184 185