/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include "RenderScriptToolkit.h" #include "Utils.h" #define LOG_TAG "renderscript.toolkit.JniEntryPoints" using namespace android::renderscript; /** * I compared using env->GetPrimitiveArrayCritical vs. env->GetByteArrayElements to get access * to the underlying data. On Pixel 4, it's actually faster to not use critical. The code is left * here if you want to experiment. Note that USE_CRITICAL could block the garbage collector. */ // #define USE_CRITICAL class ByteArrayGuard { private: JNIEnv* env; jbyteArray array; jbyte* data; public: ByteArrayGuard(JNIEnv* env, jbyteArray array) : env{env}, array{array} { #ifdef USE_CRITICAL data = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); #else data = env->GetByteArrayElements(array, nullptr); #endif } ~ByteArrayGuard() { #ifdef USE_CRITICAL env->ReleasePrimitiveArrayCritical(array, data, 0); #else env->ReleaseByteArrayElements(array, data, 0); #endif } uint8_t* get() { return reinterpret_cast(data); } }; class IntArrayGuard { private: JNIEnv* env; jintArray array; jint* data; public: IntArrayGuard(JNIEnv* env, jintArray array) : env{env}, array{array} { #ifdef USE_CRITICAL data = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); #else data = env->GetIntArrayElements(array, nullptr); #endif } ~IntArrayGuard() { #ifdef USE_CRITICAL env->ReleasePrimitiveArrayCritical(array, data, 0); #else env->ReleaseIntArrayElements(array, data, 0); #endif } int* get() { return reinterpret_cast(data); } }; class FloatArrayGuard { private: JNIEnv* env; jfloatArray array; jfloat* data; public: FloatArrayGuard(JNIEnv* env, jfloatArray array) : env{env}, array{array} { #ifdef USE_CRITICAL data = reinterpret_cast(env->GetPrimitiveArrayCritical(array, nullptr)); #else data = env->GetFloatArrayElements(array, nullptr); #endif } ~FloatArrayGuard() { #ifdef USE_CRITICAL env->ReleasePrimitiveArrayCritical(array, data, 0); #else env->ReleaseFloatArrayElements(array, data, 0); #endif } float* get() { return reinterpret_cast(data); } }; class BitmapGuard { private: JNIEnv* env; jobject bitmap; AndroidBitmapInfo info; int bytesPerPixel; void* bytes; bool valid; public: BitmapGuard(JNIEnv* env, jobject jBitmap) : env{env}, bitmap{jBitmap}, bytes{nullptr} { valid = false; if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) { ALOGE("AndroidBitmap_getInfo failed"); return; } if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888 && info.format != ANDROID_BITMAP_FORMAT_A_8) { ALOGE("AndroidBitmap in the wrong format"); return; } bytesPerPixel = info.stride / info.width; if (bytesPerPixel != 1 && bytesPerPixel != 4) { ALOGE("Expected a vector size of 1 or 4. Got %d. Extra padding per line not currently " "supported", bytesPerPixel); return; } if (AndroidBitmap_lockPixels(env, bitmap, &bytes) != ANDROID_BITMAP_RESULT_SUCCESS) { ALOGE("AndroidBitmap_lockPixels failed"); return; } valid = true; } ~BitmapGuard() { if (valid) { AndroidBitmap_unlockPixels(env, bitmap); } } uint8_t* get() const { assert(valid); return reinterpret_cast(bytes); } int width() const { return info.width; } int height() const { return info.height; } int vectorSize() const { return bytesPerPixel; } }; /** * Copies the content of Kotlin Range2d object into the equivalent C++ struct. */ class RestrictionParameter { private: bool isNull; Restriction restriction; public: RestrictionParameter(JNIEnv* env, jobject jRestriction) : isNull{jRestriction == nullptr} { if (isNull) { return; } /* TODO Measure how long FindClass and related functions take. Consider passing the * four values instead. This would also require setting the default when Range2D is null. */ jclass restrictionClass = env->FindClass("android/renderscript/toolkit/Range2d"); if (restrictionClass == nullptr) { ALOGE("RenderScriptToolit. Internal error. Could not find the Kotlin Range2d class."); isNull = true; return; } jfieldID startXId = env->GetFieldID(restrictionClass, "startX", "I"); jfieldID startYId = env->GetFieldID(restrictionClass, "startY", "I"); jfieldID endXId = env->GetFieldID(restrictionClass, "endX", "I"); jfieldID endYId = env->GetFieldID(restrictionClass, "endY", "I"); restriction.startX = env->GetIntField(jRestriction, startXId); restriction.startY = env->GetIntField(jRestriction, startYId); restriction.endX = env->GetIntField(jRestriction, endXId); restriction.endY = env->GetIntField(jRestriction, endYId); } Restriction* get() { return isNull ? nullptr : &restriction; } }; extern "C" JNIEXPORT jlong JNICALL Java_android_renderscript_toolkit_Toolkit_createNative(JNIEnv* /*env*/, jobject /*thiz*/) { return reinterpret_cast(new RenderScriptToolkit()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_destroyNative( JNIEnv* /*env*/, jobject /*thiz*/, jlong native_handle) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); delete toolkit; } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeBlend( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jint jmode, jbyteArray source_array, jbyteArray dest_array, jint size_x, jint size_y, jobject restriction) { auto toolkit = reinterpret_cast(native_handle); auto mode = static_cast(jmode); RestrictionParameter restrict {env, restriction}; ByteArrayGuard source{env, source_array}; ByteArrayGuard dest{env, dest_array}; toolkit->blend(mode, source.get(), dest.get(), size_x, size_y, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeBlendBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jint jmode, jobject source_bitmap, jobject dest_bitmap, jobject restriction) { auto toolkit = reinterpret_cast(native_handle); auto mode = static_cast(jmode); RestrictionParameter restrict {env, restriction}; BitmapGuard source{env, source_bitmap}; BitmapGuard dest{env, dest_bitmap}; toolkit->blend(mode, source.get(), dest.get(), source.width(), source.height(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeBlur( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint vectorSize, jint size_x, jint size_y, jint radius, jbyteArray output_array, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; toolkit->blur(input.get(), output.get(), size_x, size_y, vectorSize, radius, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeBlurBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jint radius, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; toolkit->blur(input.get(), output.get(), input.width(), input.height(), input.vectorSize(), radius, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeColorMatrix( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint input_vector_size, jint size_x, jint size_y, jbyteArray output_array, jint output_vector_size, jfloatArray jmatrix, jfloatArray add_vector, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; FloatArrayGuard matrix{env, jmatrix}; FloatArrayGuard add{env, add_vector}; toolkit->colorMatrix(input.get(), output.get(), input_vector_size, output_vector_size, size_x, size_y, matrix.get(), add.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeColorMatrixBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jfloatArray jmatrix, jfloatArray add_vector, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; FloatArrayGuard matrix{env, jmatrix}; FloatArrayGuard add{env, add_vector}; toolkit->colorMatrix(input.get(), output.get(), input.vectorSize(), output.vectorSize(), input.width(), input.height(), matrix.get(), add.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeConvolve( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint vectorSize, jint size_x, jint size_y, jbyteArray output_array, jfloatArray coefficients, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; FloatArrayGuard coeffs{env, coefficients}; switch (env->GetArrayLength(coefficients)) { case 9: toolkit->convolve3x3(input.get(), output.get(), vectorSize, size_x, size_y, coeffs.get(), restrict.get()); break; case 25: toolkit->convolve5x5(input.get(), output.get(), vectorSize, size_x, size_y, coeffs.get(), restrict.get()); break; } } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeConvolveBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jfloatArray coefficients, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; FloatArrayGuard coeffs{env, coefficients}; switch (env->GetArrayLength(coefficients)) { case 9: toolkit->convolve3x3(input.get(), output.get(), input.vectorSize(), input.width(), input.height(), coeffs.get(), restrict.get()); break; case 25: toolkit->convolve5x5(input.get(), output.get(), input.vectorSize(), input.width(), input.height(), coeffs.get(), restrict.get()); break; } } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeHistogram( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint vector_size, jint size_x, jint size_y, jintArray output_array, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; IntArrayGuard output{env, output_array}; toolkit->histogram(input.get(), output.get(), size_x, size_y, vector_size, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeHistogramBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jintArray output_array, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; IntArrayGuard output{env, output_array}; toolkit->histogram(input.get(), output.get(), input.width(), input.height(), input.vectorSize(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeHistogramDot( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint vector_size, jint size_x, jint size_y, jintArray output_array, jfloatArray coefficients, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; IntArrayGuard output{env, output_array}; FloatArrayGuard coeffs{env, coefficients}; toolkit->histogramDot(input.get(), output.get(), size_x, size_y, vector_size, coeffs.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeHistogramDotBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jintArray output_array, jfloatArray coefficients, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; IntArrayGuard output{env, output_array}; FloatArrayGuard coeffs{env, coefficients}; toolkit->histogramDot(input.get(), output.get(), input.width(), input.height(), input.vectorSize(), coeffs.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeLut( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jbyteArray output_array, jint size_x, jint size_y, jbyteArray red_table, jbyteArray green_table, jbyteArray blue_table, jbyteArray alpha_table, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; ByteArrayGuard red{env, red_table}; ByteArrayGuard green{env, green_table}; ByteArrayGuard blue{env, blue_table}; ByteArrayGuard alpha{env, alpha_table}; toolkit->lut(input.get(), output.get(), size_x, size_y, red.get(), green.get(), blue.get(), alpha.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeLutBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jbyteArray red_table, jbyteArray green_table, jbyteArray blue_table, jbyteArray alpha_table, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; ByteArrayGuard red{env, red_table}; ByteArrayGuard green{env, green_table}; ByteArrayGuard blue{env, blue_table}; ByteArrayGuard alpha{env, alpha_table}; toolkit->lut(input.get(), output.get(), input.width(), input.height(), red.get(), green.get(), blue.get(), alpha.get(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeLut3d( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jbyteArray output_array, jint size_x, jint size_y, jbyteArray cube_values, jint cubeSizeX, jint cubeSizeY, jint cubeSizeZ, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; ByteArrayGuard cube{env, cube_values}; toolkit->lut3d(input.get(), output.get(), size_x, size_y, cube.get(), cubeSizeX, cubeSizeY, cubeSizeZ, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeLut3dBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jbyteArray cube_values, jint cubeSizeX, jint cubeSizeY, jint cubeSizeZ, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; ByteArrayGuard cube{env, cube_values}; toolkit->lut3d(input.get(), output.get(), input.width(), input.height(), cube.get(), cubeSizeX, cubeSizeY, cubeSizeZ, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeResize( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint vector_size, jint input_size_x, jint input_size_y, jbyteArray output_array, jint output_size_x, jint output_size_y, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; toolkit->resize(input.get(), output.get(), input_size_x, input_size_y, vector_size, output_size_x, output_size_y, restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeResizeBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jobject input_bitmap, jobject output_bitmap, jobject restriction) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); RestrictionParameter restrict {env, restriction}; BitmapGuard input{env, input_bitmap}; BitmapGuard output{env, output_bitmap}; toolkit->resize(input.get(), output.get(), input.width(), input.height(), input.vectorSize(), output.width(), output.height(), restrict.get()); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeYuvToRgb( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jbyteArray output_array, jint size_x, jint size_y, jint format) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); ByteArrayGuard input{env, input_array}; ByteArrayGuard output{env, output_array}; toolkit->yuvToRgb(input.get(), output.get(), size_x, size_y, static_cast(format)); } extern "C" JNIEXPORT void JNICALL Java_android_renderscript_toolkit_Toolkit_nativeYuvToRgbBitmap( JNIEnv* env, jobject /*thiz*/, jlong native_handle, jbyteArray input_array, jint size_x, jint size_y, jobject output_bitmap, jint format) { RenderScriptToolkit* toolkit = reinterpret_cast(native_handle); BitmapGuard output{env, output_bitmap}; ByteArrayGuard input{env, input_array}; toolkit->yuvToRgb(input.get(), output.get(), size_x, size_y, static_cast(format)); }