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