blob: 1d45ffd74748dd9e1c9e337121815600f343626b [file] [log] [blame]
/*
* 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 <mutex>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <jni.h>
#include <sys/stat.h>
#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<SkData> open_asset_data(const char* path) {
sk_sp<SkData> 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<SkData> 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<SkData> get_resource(const char* resource) {
return open_asset_data((std::string("resources/") + resource).c_str());
}
////////////////////////////////////////////////////////////////////////////////
template <typename T, typename F>
jobjectArray to_java_string_array(JNIEnv* env,
const std::vector<T>& 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<std::mutex> 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<std::mutex> 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<std::string> errors;
{
jassert(env, index < (jint)gSkQP.getUnitTests().size(), nullptr);
std::lock_guard<std::mutex> 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<std::mutex> lock(gMutex);
gSkQP.makeReport();
}
////////////////////////////////////////////////////////////////////////////////