1 #undef LOG_TAG
2 #define LOG_TAG "ShaderJNI"
3 
4 #include "GraphicsJNI.h"
5 #include "SkColorFilter.h"
6 #include "SkGradientShader.h"
7 #include "SkImagePriv.h"
8 #include "SkShader.h"
9 #include "SkBlendMode.h"
10 #include "include/effects/SkRuntimeEffect.h"
11 
12 #include <vector>
13 
14 using namespace android::uirenderer;
15 
16 /**
17  * By default Skia gradients will interpolate their colors in unpremul space
18  * and then premultiply each of the results. We must set this flag to preserve
19  * backwards compatiblity by premultiplying the colors of the gradient first,
20  * and then interpolating between them.
21  */
22 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
23 
24 #define ThrowIAE_IfNull(env, ptr)   \
25     if (nullptr == ptr) {           \
26         doThrowIAE(env);            \
27         return 0;                   \
28     }
29 
Color_RGBToHSV(JNIEnv * env,jobject,jint red,jint green,jint blue,jfloatArray hsvArray)30 static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
31 {
32     SkScalar hsv[3];
33     SkRGBToHSV(red, green, blue, hsv);
34 
35     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
36     float* values = autoHSV.ptr();
37     for (int i = 0; i < 3; i++) {
38         values[i] = SkScalarToFloat(hsv[i]);
39     }
40 }
41 
Color_HSVToColor(JNIEnv * env,jobject,jint alpha,jfloatArray hsvArray)42 static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
43 {
44     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
45 #ifdef SK_SCALAR_IS_FLOAT
46     SkScalar*   hsv = autoHSV.ptr();
47 #else
48     #error Need to convert float array to SkScalar array before calling the following function.
49 #endif
50 
51     return static_cast<jint>(SkHSVToColor(alpha, hsv));
52 }
53 
54 ///////////////////////////////////////////////////////////////////////////////////////////////
55 
Shader_safeUnref(SkShader * shader)56 static void Shader_safeUnref(SkShader* shader) {
57     SkSafeUnref(shader);
58 }
59 
Shader_getNativeFinalizer(JNIEnv *,jobject)60 static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
61     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
62 }
63 
64 ///////////////////////////////////////////////////////////////////////////////////////////////
65 
BitmapShader_constructor(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY,bool filter)66 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
67         jint tileModeX, jint tileModeY, bool filter) {
68     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
69     sk_sp<SkImage> image;
70     if (bitmapHandle) {
71         // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
72         // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
73         image = android::bitmap::toBitmap(bitmapHandle).makeImage();
74     }
75 
76     if (!image.get()) {
77         SkBitmap bitmap;
78         image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
79     }
80     SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
81                                SkMipmapMode::kNone);
82     sk_sp<SkShader> shader = image->makeShader(
83             (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
84     ThrowIAE_IfNull(env, shader.get());
85 
86     if (matrix) {
87         shader = shader->makeWithLocalMatrix(*matrix);
88     }
89 
90     return reinterpret_cast<jlong>(shader.release());
91 }
92 
93 ///////////////////////////////////////////////////////////////////////////////////////////////
94 
convertColorLongs(JNIEnv * env,jlongArray colorArray)95 static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
96     const size_t count = env->GetArrayLength(colorArray);
97     const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
98 
99     std::vector<SkColor4f> colors(count);
100     for (size_t i = 0; i < count; ++i) {
101         colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
102     }
103 
104     env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
105     return colors;
106 }
107 
108 ///////////////////////////////////////////////////////////////////////////////////////////////
109 
LinearGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)110 static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
111         jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
112         jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
113     SkPoint pts[2];
114     pts[0].set(x0, y0);
115     pts[1].set(x1, y1);
116 
117     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
118 
119     AutoJavaFloatArray autoPos(env, posArray, colors.size());
120 #ifdef SK_SCALAR_IS_FLOAT
121     SkScalar* pos = autoPos.ptr();
122 #else
123     #error Need to convert float array to SkScalar array before calling the following function.
124 #endif
125 
126     sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
127                 GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
128                 static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
129     ThrowIAE_IfNull(env, shader);
130 
131     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
132     if (matrix) {
133         shader = shader->makeWithLocalMatrix(*matrix);
134     }
135 
136     return reinterpret_cast<jlong>(shader.release());
137 }
138 
139 ///////////////////////////////////////////////////////////////////////////////////////////////
140 
RadialGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat startX,jfloat startY,jfloat startRadius,jfloat endX,jfloat endY,jfloat endRadius,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)141 static jlong RadialGradient_create(JNIEnv* env,
142         jobject,
143         jlong matrixPtr,
144         jfloat startX,
145         jfloat startY,
146         jfloat startRadius,
147         jfloat endX,
148         jfloat endY,
149         jfloat endRadius,
150         jlongArray colorArray,
151         jfloatArray posArray,
152         jint tileMode,
153         jlong colorSpaceHandle) {
154 
155     SkPoint start;
156     start.set(startX, startY);
157 
158     SkPoint end;
159     end.set(endX, endY);
160 
161     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
162 
163     AutoJavaFloatArray autoPos(env, posArray, colors.size());
164 #ifdef SK_SCALAR_IS_FLOAT
165     SkScalar* pos = autoPos.ptr();
166 #else
167     #error Need to convert float array to SkScalar array before calling the following function.
168 #endif
169 
170     auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
171     auto skTileMode = static_cast<SkTileMode>(tileMode);
172     sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(start, startRadius, end,
173                     endRadius, &colors[0], std::move(colorSpace), pos, colors.size(), skTileMode,
174                     sGradientShaderFlags, nullptr);
175     ThrowIAE_IfNull(env, shader);
176 
177     // Explicitly create a new shader with the specified matrix to match existing behavior.
178     // Passing in the matrix in the instantiation above can throw exceptions for non-invertible
179     // matrices. However, makeWithLocalMatrix will still allow for the shader to be created
180     // and skia handles null-shaders internally (i.e. is ignored)
181     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
182     if (matrix) {
183         shader = shader->makeWithLocalMatrix(*matrix);
184     }
185 
186     return reinterpret_cast<jlong>(shader.release());
187 }
188 
189 ///////////////////////////////////////////////////////////////////////////////
190 
SweepGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x,jfloat y,jlongArray colorArray,jfloatArray jpositions,jlong colorSpaceHandle)191 static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
192         jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
193     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
194 
195     AutoJavaFloatArray autoPos(env, jpositions, colors.size());
196 #ifdef SK_SCALAR_IS_FLOAT
197     SkScalar* pos = autoPos.ptr();
198 #else
199     #error Need to convert float array to SkScalar array before calling the following function.
200 #endif
201 
202     sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
203             GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
204             sGradientShaderFlags, nullptr);
205     ThrowIAE_IfNull(env, shader);
206 
207     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
208     if (matrix) {
209         shader = shader->makeWithLocalMatrix(*matrix);
210     }
211 
212     return reinterpret_cast<jlong>(shader.release());
213 }
214 
215 ///////////////////////////////////////////////////////////////////////////////////////////////
216 
ComposeShader_create(JNIEnv * env,jobject o,jlong matrixPtr,jlong shaderAHandle,jlong shaderBHandle,jint xfermodeHandle)217 static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
218         jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
219     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
220     SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
221     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
222     SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
223     sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
224             sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
225 
226     SkShader* shader;
227 
228     if (matrix) {
229         shader = baseShader->makeWithLocalMatrix(*matrix).release();
230     } else {
231         shader = baseShader.release();
232     }
233     return reinterpret_cast<jlong>(shader);
234 }
235 
236 ///////////////////////////////////////////////////////////////////////////////////////////////
237 
238 ///////////////////////////////////////////////////////////////////////////////////////////////
239 
RuntimeShader_createShaderBuilder(JNIEnv * env,jobject,jstring sksl)240 static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
241     ScopedUtfChars strSksl(env, sksl);
242     auto result = SkRuntimeEffect::MakeForShader(SkString(strSksl.c_str()),
243                                                  SkRuntimeEffect::Options{});
244     if (result.effect.get() == nullptr) {
245         doThrowIAE(env, result.errorText.c_str());
246         return 0;
247     }
248     return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(result.effect)));
249 }
250 
SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder * builder)251 static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
252     delete builder;
253 }
254 
RuntimeShader_getNativeFinalizer(JNIEnv *,jobject)255 static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
256     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
257 }
258 
RuntimeShader_create(JNIEnv * env,jobject,jlong shaderBuilder,jlong matrixPtr,jboolean isOpaque)259 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
260                                   jboolean isOpaque) {
261     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
262     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
263     sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE);
264     ThrowIAE_IfNull(env, shader);
265     return reinterpret_cast<jlong>(shader.release());
266 }
267 
ThrowIAEFmt(JNIEnv * env,const char * fmt,...)268 static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
269     va_list args;
270     va_start(args, fmt);
271     int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
272     va_end(args);
273     return ret;
274 }
275 
RuntimeShader_updateUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloatArray jvalues)276 static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
277                                          jstring jUniformName, jfloatArray jvalues) {
278     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
279     ScopedUtfChars name(env, jUniformName);
280     AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
281 
282     SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
283     if (uniform.fVar == nullptr) {
284         ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
285     } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
286         ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
287               uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length());
288     }
289 }
290 
RuntimeShader_updateShader(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong shaderHandle)291 static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
292                                            jstring jUniformName, jlong shaderHandle) {
293     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
294     ScopedUtfChars name(env, jUniformName);
295     SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
296 
297     SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
298     if (child.fIndex == -1) {
299         ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
300         return;
301     }
302 
303     builder->child(name.c_str()) = sk_ref_sp(shader);
304 }
305 
306 ///////////////////////////////////////////////////////////////////////////////////////////////
307 
308 static const JNINativeMethod gColorMethods[] = {
309     { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
310     { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
311 };
312 
313 static const JNINativeMethod gShaderMethods[] = {
314     { "nativeGetFinalizer",   "()J",    (void*)Shader_getNativeFinalizer },
315 };
316 
317 static const JNINativeMethod gBitmapShaderMethods[] = {
318     { "nativeCreate",      "(JJIIZ)J",  (void*)BitmapShader_constructor },
319 };
320 
321 static const JNINativeMethod gLinearGradientMethods[] = {
322     { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
323 };
324 
325 static const JNINativeMethod gRadialGradientMethods[] = {
326     { "nativeCreate",     "(JFFFFFF[J[FIJ)J",  (void*)RadialGradient_create     },
327 };
328 
329 static const JNINativeMethod gSweepGradientMethods[] = {
330     { "nativeCreate",     "(JFF[J[FJ)J",  (void*)SweepGradient_create     },
331 };
332 
333 static const JNINativeMethod gComposeShaderMethods[] = {
334     { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
335 };
336 
337 static const JNINativeMethod gRuntimeShaderMethods[] = {
338         {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
339         {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
340         {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
341         {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
342         {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
343 };
344 
register_android_graphics_Shader(JNIEnv * env)345 int register_android_graphics_Shader(JNIEnv* env)
346 {
347     android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
348                                   NELEM(gColorMethods));
349     android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
350                                   NELEM(gShaderMethods));
351     android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
352                                   NELEM(gBitmapShaderMethods));
353     android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
354                                   NELEM(gLinearGradientMethods));
355     android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
356                                   NELEM(gRadialGradientMethods));
357     android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
358                                   NELEM(gSweepGradientMethods));
359     android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
360                                   NELEM(gComposeShaderMethods));
361     android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
362                                   NELEM(gRuntimeShaderMethods));
363 
364     return 0;
365 }
366