summaryrefslogtreecommitdiff
path: root/libs/hwui/jni/Shader.cpp
diff options
context:
space:
mode:
authorDerek Sollenberger <djsollen@google.com>2020-02-21 11:43:02 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-02-21 11:43:02 +0000
commit76e7430baead21c67fd4c81a36774a6461d2fb99 (patch)
tree239a62b59c57b4b95d8e82943bf9a48e84439819 /libs/hwui/jni/Shader.cpp
parent1ab8e44c06ca05806a81c6737cb3f6e316d8d25a (diff)
parent2173ea286afff6766043227de0bc2d82d9595f77 (diff)
Merge changes from topic "HWUI_JNI"
* changes: Export symbols for the newly exposed APEX/internal headers Remove dependence on libandroid_runtime from Bitmap.cpp Update Region.cpp to use AParcel NDK APIs Cleanup header and build targets for libhwui clients. Remove dependencies on headers outside UI module Cleanup LOG_TAG when bundled in HWUI Move android.graphics JNI & APEX files into HWUI
Diffstat (limited to 'libs/hwui/jni/Shader.cpp')
-rw-r--r--libs/hwui/jni/Shader.cpp305
1 files changed, 305 insertions, 0 deletions
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
new file mode 100644
index 000000000000..0f6837640524
--- /dev/null
+++ b/libs/hwui/jni/Shader.cpp
@@ -0,0 +1,305 @@
+#include "GraphicsJNI.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+#include "SkImagePriv.h"
+#include "SkShader.h"
+#include "SkBlendMode.h"
+#include "include/effects/SkRuntimeEffect.h"
+
+#include <vector>
+
+using namespace android::uirenderer;
+
+/**
+ * By default Skia gradients will interpolate their colors in unpremul space
+ * and then premultiply each of the results. We must set this flag to preserve
+ * backwards compatiblity by premultiplying the colors of the gradient first,
+ * and then interpolating between them.
+ */
+static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
+
+#define ThrowIAE_IfNull(env, ptr) \
+ if (nullptr == ptr) { \
+ doThrowIAE(env); \
+ return 0; \
+ }
+
+static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
+{
+ SkScalar hsv[3];
+ SkRGBToHSV(red, green, blue, hsv);
+
+ AutoJavaFloatArray autoHSV(env, hsvArray, 3);
+ float* values = autoHSV.ptr();
+ for (int i = 0; i < 3; i++) {
+ values[i] = SkScalarToFloat(hsv[i]);
+ }
+}
+
+static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
+{
+ AutoJavaFloatArray autoHSV(env, hsvArray, 3);
+#ifdef SK_SCALAR_IS_FLOAT
+ SkScalar* hsv = autoHSV.ptr();
+#else
+ #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+ return static_cast<jint>(SkHSVToColor(alpha, hsv));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void Shader_safeUnref(SkShader* shader) {
+ SkSafeUnref(shader);
+}
+
+static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
+ jint tileModeX, jint tileModeY) {
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ sk_sp<SkImage> image;
+ if (bitmapHandle) {
+ // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
+ // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
+ image = android::bitmap::toBitmap(bitmapHandle).makeImage();
+ }
+
+ if (!image.get()) {
+ SkBitmap bitmap;
+ image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
+ }
+ sk_sp<SkShader> shader = image->makeShader(
+ (SkTileMode)tileModeX, (SkTileMode)tileModeY);
+ ThrowIAE_IfNull(env, shader.get());
+
+ if (matrix) {
+ shader = shader->makeWithLocalMatrix(*matrix);
+ }
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
+ const size_t count = env->GetArrayLength(colorArray);
+ const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
+
+ std::vector<SkColor4f> colors(count);
+ for (size_t i = 0; i < count; ++i) {
+ colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
+ }
+
+ env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
+ return colors;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
+ jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
+ jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
+ SkPoint pts[2];
+ pts[0].set(x0, y0);
+ pts[1].set(x1, y1);
+
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+ AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+ SkScalar* pos = autoPos.ptr();
+#else
+ #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+ sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
+ ThrowIAE_IfNull(env, shader);
+
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ if (matrix) {
+ shader = shader->makeWithLocalMatrix(*matrix);
+ }
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+ jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
+ jlong colorSpaceHandle) {
+ SkPoint center;
+ center.set(x, y);
+
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+ AutoJavaFloatArray autoPos(env, posArray, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+ SkScalar* pos = autoPos.ptr();
+#else
+ #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+ sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr);
+ ThrowIAE_IfNull(env, shader);
+
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ if (matrix) {
+ shader = shader->makeWithLocalMatrix(*matrix);
+ }
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
+ jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
+ std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
+
+ AutoJavaFloatArray autoPos(env, jpositions, colors.size());
+#ifdef SK_SCALAR_IS_FLOAT
+ SkScalar* pos = autoPos.ptr();
+#else
+ #error Need to convert float array to SkScalar array before calling the following function.
+#endif
+
+ sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
+ GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
+ sGradientShaderFlags, nullptr);
+ ThrowIAE_IfNull(env, shader);
+
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ if (matrix) {
+ shader = shader->makeWithLocalMatrix(*matrix);
+ }
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
+ jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
+ SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
+ SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
+ sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
+ sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
+
+ SkShader* shader;
+
+ if (matrix) {
+ shader = baseShader->makeWithLocalMatrix(*matrix).release();
+ } else {
+ shader = baseShader.release();
+ }
+ return reinterpret_cast<jlong>(shader);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
+ jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+ SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
+ AutoJavaByteArray arInputs(env, inputs);
+
+ sk_sp<SkData> fData;
+ fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
+ const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+ sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
+ ThrowIAE_IfNull(env, shader);
+
+ return reinterpret_cast<jlong>(shader.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
+ ScopedUtfChars strSksl(env, sksl);
+ sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str())));
+ ThrowIAE_IfNull(env, effect);
+
+ return reinterpret_cast<jlong>(effect.release());
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void Effect_safeUnref(SkRuntimeEffect* effect) {
+ SkSafeUnref(effect);
+}
+
+static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gColorMethods[] = {
+ { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
+ { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
+};
+
+static const JNINativeMethod gShaderMethods[] = {
+ { "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer },
+};
+
+static const JNINativeMethod gBitmapShaderMethods[] = {
+ { "nativeCreate", "(JJII)J", (void*)BitmapShader_constructor },
+};
+
+static const JNINativeMethod gLinearGradientMethods[] = {
+ { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
+};
+
+static const JNINativeMethod gRadialGradientMethods[] = {
+ { "nativeCreate", "(JFFF[J[FIJ)J", (void*)RadialGradient_create },
+};
+
+static const JNINativeMethod gSweepGradientMethods[] = {
+ { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create },
+};
+
+static const JNINativeMethod gComposeShaderMethods[] = {
+ { "nativeCreate", "(JJJI)J", (void*)ComposeShader_create },
+};
+
+static const JNINativeMethod gRuntimeShaderMethods[] = {
+ { "nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer },
+ { "nativeCreate", "(JJ[BJZ)J", (void*)RuntimeShader_create },
+ { "nativeCreateShaderFactory", "(Ljava/lang/String;)J",
+ (void*)RuntimeShader_createShaderFactory },
+};
+
+int register_android_graphics_Shader(JNIEnv* env)
+{
+ android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
+ NELEM(gColorMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
+ NELEM(gShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
+ NELEM(gBitmapShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
+ NELEM(gLinearGradientMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
+ NELEM(gRadialGradientMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
+ NELEM(gSweepGradientMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
+ NELEM(gComposeShaderMethods));
+ android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
+ NELEM(gRuntimeShaderMethods));
+
+ return 0;
+}