Home | History | Annotate | Download | only in src
      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