1 #undef LOG_TAG
2 #define LOG_TAG "ShaderJNI"
3 
4 #include <vector>
5 
6 #include "Gainmap.h"
7 #include "GraphicsJNI.h"
8 #include "SkBitmap.h"
9 #include "SkBlendMode.h"
10 #include "SkColor.h"
11 #include "SkColorFilter.h"
12 #include "SkGradientShader.h"
13 #include "SkImage.h"
14 #include "SkImagePriv.h"
15 #include "SkMatrix.h"
16 #include "SkPoint.h"
17 #include "SkRefCnt.h"
18 #include "SkSamplingOptions.h"
19 #include "SkScalar.h"
20 #include "SkShader.h"
21 #include "SkString.h"
22 #include "SkTileMode.h"
23 #include "effects/GainmapRenderer.h"
24 #include "include/effects/SkRuntimeEffect.h"
25 
26 using namespace android::uirenderer;
27 
28 /**
29  * By default Skia gradients will interpolate their colors in unpremul space
30  * and then premultiply each of the results. We must set this flag to preserve
31  * backwards compatibility by premultiplying the colors of the gradient first,
32  * and then interpolating between them.
33  */
34 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
35 
36 #define ThrowIAE_IfNull(env, ptr)   \
37     if (nullptr == ptr) {           \
38         doThrowIAE(env);            \
39         return 0;                   \
40     }
41 
Color_RGBToHSV(JNIEnv * env,jobject,jint red,jint green,jint blue,jfloatArray hsvArray)42 static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
43 {
44     SkScalar hsv[3];
45     SkRGBToHSV(red, green, blue, hsv);
46 
47     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
48     float* values = autoHSV.ptr();
49     for (int i = 0; i < 3; i++) {
50         values[i] = SkScalarToFloat(hsv[i]);
51     }
52 }
53 
Color_HSVToColor(JNIEnv * env,jobject,jint alpha,jfloatArray hsvArray)54 static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
55 {
56     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
57     SkScalar* hsv = autoHSV.ptr();
58     return static_cast<jint>(SkHSVToColor(alpha, hsv));
59 }
60 
61 ///////////////////////////////////////////////////////////////////////////////////////////////
62 
Shader_safeUnref(SkShader * shader)63 static void Shader_safeUnref(SkShader* shader) {
64     SkSafeUnref(shader);
65 }
66 
Shader_getNativeFinalizer(JNIEnv *,jobject)67 static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
68     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
69 }
70 
createBitmapShaderHelper(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY,bool isDirectSampled,const SkSamplingOptions & sampling)71 static jlong createBitmapShaderHelper(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
72                                       jint tileModeX, jint tileModeY, bool isDirectSampled,
73                                       const SkSamplingOptions& sampling) {
74     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
75     sk_sp<SkImage> image;
76     if (bitmapHandle) {
77         // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
78         // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
79         auto& bitmap = android::bitmap::toBitmap(bitmapHandle);
80         image = bitmap.makeImage();
81 
82         if (!isDirectSampled && bitmap.hasGainmap()) {
83             sk_sp<SkShader> gainmapShader = MakeGainmapShader(
84                     image, bitmap.gainmap()->bitmap->makeImage(), bitmap.gainmap()->info,
85                     (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
86             if (gainmapShader) {
87                 if (matrix) {
88                     gainmapShader = gainmapShader->makeWithLocalMatrix(*matrix);
89                 }
90                 return reinterpret_cast<jlong>(gainmapShader.release());
91             }
92         }
93     }
94 
95     if (!image.get()) {
96         SkBitmap bitmap;
97         image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
98     }
99 
100     sk_sp<SkShader> shader;
101     if (isDirectSampled) {
102         shader = image->makeRawShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
103     } else {
104         shader = image->makeShader((SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
105     }
106     ThrowIAE_IfNull(env, shader.get());
107 
108     if (matrix) {
109         shader = shader->makeWithLocalMatrix(*matrix);
110     }
111 
112     return reinterpret_cast<jlong>(shader.release());
113 }
114 
115 ///////////////////////////////////////////////////////////////////////////////////////////////
116 
BitmapShader_constructor(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY,bool filter,bool isDirectSampled)117 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
118                                       jint tileModeX, jint tileModeY, bool filter,
119                                       bool isDirectSampled) {
120     SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
121                                SkMipmapMode::kNone);
122     return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY,
123                                     isDirectSampled, sampling);
124 }
125 
BitmapShader_constructorWithMaxAniso(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY,jint maxAniso,bool isDirectSampled)126 static jlong BitmapShader_constructorWithMaxAniso(JNIEnv* env, jobject o, jlong matrixPtr,
127                                                   jlong bitmapHandle, jint tileModeX,
128                                                   jint tileModeY, jint maxAniso,
129                                                   bool isDirectSampled) {
130     auto sampling = SkSamplingOptions::Aniso(static_cast<int>(maxAniso));
131     return createBitmapShaderHelper(env, o, matrixPtr, bitmapHandle, tileModeX, tileModeY,
132                                     isDirectSampled, sampling);
133 }
134 
135 ///////////////////////////////////////////////////////////////////////////////////////////////
136 
convertColorLongs(JNIEnv * env,jlongArray colorArray)137 static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
138     const size_t count = env->GetArrayLength(colorArray);
139     const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
140 
141     std::vector<SkColor4f> colors(count);
142     for (size_t i = 0; i < count; ++i) {
143         colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
144     }
145 
146     env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
147     return colors;
148 }
149 
150 ///////////////////////////////////////////////////////////////////////////////////////////////
151 
LinearGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)152 static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
153         jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
154         jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
155     SkPoint pts[2];
156     pts[0].set(x0, y0);
157     pts[1].set(x1, y1);
158 
159     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
160 
161     AutoJavaFloatArray autoPos(env, posArray, colors.size());
162     SkScalar* pos = autoPos.ptr();
163 
164     sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
165                 GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
166                 static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
167     ThrowIAE_IfNull(env, shader);
168 
169     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
170     if (matrix) {
171         shader = shader->makeWithLocalMatrix(*matrix);
172     }
173 
174     return reinterpret_cast<jlong>(shader.release());
175 }
176 
177 ///////////////////////////////////////////////////////////////////////////////////////////////
178 
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)179 static jlong RadialGradient_create(JNIEnv* env,
180         jobject,
181         jlong matrixPtr,
182         jfloat startX,
183         jfloat startY,
184         jfloat startRadius,
185         jfloat endX,
186         jfloat endY,
187         jfloat endRadius,
188         jlongArray colorArray,
189         jfloatArray posArray,
190         jint tileMode,
191         jlong colorSpaceHandle) {
192 
193     SkPoint start;
194     start.set(startX, startY);
195 
196     SkPoint end;
197     end.set(endX, endY);
198 
199     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
200 
201     AutoJavaFloatArray autoPos(env, posArray, colors.size());
202     SkScalar* pos = autoPos.ptr();
203 
204     auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
205     auto skTileMode = static_cast<SkTileMode>(tileMode);
206     sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(start, startRadius, end,
207                     endRadius, &colors[0], std::move(colorSpace), pos, colors.size(), skTileMode,
208                     sGradientShaderFlags, nullptr);
209     ThrowIAE_IfNull(env, shader);
210 
211     // Explicitly create a new shader with the specified matrix to match existing behavior.
212     // Passing in the matrix in the instantiation above can throw exceptions for non-invertible
213     // matrices. However, makeWithLocalMatrix will still allow for the shader to be created
214     // and skia handles null-shaders internally (i.e. is ignored)
215     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
216     if (matrix) {
217         shader = shader->makeWithLocalMatrix(*matrix);
218     }
219 
220     return reinterpret_cast<jlong>(shader.release());
221 }
222 
223 ///////////////////////////////////////////////////////////////////////////////
224 
SweepGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x,jfloat y,jlongArray colorArray,jfloatArray jpositions,jlong colorSpaceHandle)225 static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
226         jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
227     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
228 
229     AutoJavaFloatArray autoPos(env, jpositions, colors.size());
230     SkScalar* pos = autoPos.ptr();
231 
232     sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
233             GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
234             sGradientShaderFlags, nullptr);
235     ThrowIAE_IfNull(env, shader);
236 
237     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
238     if (matrix) {
239         shader = shader->makeWithLocalMatrix(*matrix);
240     }
241 
242     return reinterpret_cast<jlong>(shader.release());
243 }
244 
245 ///////////////////////////////////////////////////////////////////////////////////////////////
246 
ComposeShader_create(JNIEnv * env,jobject o,jlong matrixPtr,jlong shaderAHandle,jlong shaderBHandle,jint xfermodeHandle)247 static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
248         jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
249     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
250     SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
251     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
252     SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
253     sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
254             sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
255 
256     SkShader* shader;
257 
258     if (matrix) {
259         shader = baseShader->makeWithLocalMatrix(*matrix).release();
260     } else {
261         shader = baseShader.release();
262     }
263     return reinterpret_cast<jlong>(shader);
264 }
265 
266 ///////////////////////////////////////////////////////////////////////////////////////////////
267 
268 ///////////////////////////////////////////////////////////////////////////////////////////////
269 
RuntimeShader_createShaderBuilder(JNIEnv * env,jobject,jstring sksl)270 static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
271     ScopedUtfChars strSksl(env, sksl);
272     auto result = SkRuntimeEffect::MakeForShader(SkString(strSksl.c_str()),
273                                                  SkRuntimeEffect::Options{});
274     if (result.effect.get() == nullptr) {
275         doThrowIAE(env, result.errorText.c_str());
276         return 0;
277     }
278     return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(result.effect)));
279 }
280 
SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder * builder)281 static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
282     delete builder;
283 }
284 
RuntimeShader_getNativeFinalizer(JNIEnv *,jobject)285 static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
286     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
287 }
288 
RuntimeShader_create(JNIEnv * env,jobject,jlong shaderBuilder,jlong matrixPtr)289 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr) {
290     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
291     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
292     sk_sp<SkShader> shader = builder->makeShader(matrix);
293     ThrowIAE_IfNull(env, shader);
294     return reinterpret_cast<jlong>(shader.release());
295 }
296 
ThrowIAEFmt(JNIEnv * env,const char * fmt,...)297 static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
298     va_list args;
299     va_start(args, fmt);
300     int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
301     va_end(args);
302     return ret;
303 }
304 
isIntUniformType(const SkRuntimeEffect::Uniform::Type & type)305 static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
306     switch (type) {
307         case SkRuntimeEffect::Uniform::Type::kFloat:
308         case SkRuntimeEffect::Uniform::Type::kFloat2:
309         case SkRuntimeEffect::Uniform::Type::kFloat3:
310         case SkRuntimeEffect::Uniform::Type::kFloat4:
311         case SkRuntimeEffect::Uniform::Type::kFloat2x2:
312         case SkRuntimeEffect::Uniform::Type::kFloat3x3:
313         case SkRuntimeEffect::Uniform::Type::kFloat4x4:
314             return false;
315         case SkRuntimeEffect::Uniform::Type::kInt:
316         case SkRuntimeEffect::Uniform::Type::kInt2:
317         case SkRuntimeEffect::Uniform::Type::kInt3:
318         case SkRuntimeEffect::Uniform::Type::kInt4:
319             return true;
320     }
321 }
322 
UpdateFloatUniforms(JNIEnv * env,SkRuntimeShaderBuilder * builder,const char * uniformName,const float values[],int count,bool isColor)323 static void UpdateFloatUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder,
324                                 const char* uniformName, const float values[], int count,
325                                 bool isColor) {
326     SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
327     if (uniform.fVar == nullptr) {
328         ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
329     } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
330         if (isColor) {
331             jniThrowExceptionFmt(
332                     env, "java/lang/IllegalArgumentException",
333                     "attempting to set a color uniform using the non-color specific APIs: %s %x",
334                     uniformName, uniform.fVar->flags);
335         } else {
336             ThrowIAEFmt(env,
337                         "attempting to set a non-color uniform using the setColorUniform APIs: %s",
338                         uniformName);
339         }
340     } else if (isIntUniformType(uniform.fVar->type)) {
341         ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
342                     uniformName);
343     } else if (!uniform.set<float>(values, count)) {
344         ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
345                     uniform.fVar->sizeInBytes(), sizeof(float) * count);
346     }
347 }
348 
RuntimeShader_updateFloatUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloat value1,jfloat value2,jfloat value3,jfloat value4,jint count)349 static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
350                                               jstring jUniformName, jfloat value1, jfloat value2,
351                                               jfloat value3, jfloat value4, jint count) {
352     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
353     ScopedUtfChars name(env, jUniformName);
354     const float values[4] = {value1, value2, value3, value4};
355     UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
356 }
357 
RuntimeShader_updateFloatArrayUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloatArray jvalues,jboolean isColor)358 static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
359                                                    jstring jUniformName, jfloatArray jvalues,
360                                                    jboolean isColor) {
361     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
362     ScopedUtfChars name(env, jUniformName);
363     AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
364     UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
365 }
366 
UpdateIntUniforms(JNIEnv * env,SkRuntimeShaderBuilder * builder,const char * uniformName,const int values[],int count)367 static void UpdateIntUniforms(JNIEnv* env, SkRuntimeShaderBuilder* builder, const char* uniformName,
368                               const int values[], int count) {
369     SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(uniformName);
370     if (uniform.fVar == nullptr) {
371         ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
372     } else if (!isIntUniformType(uniform.fVar->type)) {
373         ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
374                     uniformName);
375     } else if (!uniform.set<int>(values, count)) {
376         ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
377                     uniform.fVar->sizeInBytes(), sizeof(float) * count);
378     }
379 }
380 
RuntimeShader_updateIntUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jint value1,jint value2,jint value3,jint value4,jint count)381 static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
382                                             jstring jUniformName, jint value1, jint value2,
383                                             jint value3, jint value4, jint count) {
384     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
385     ScopedUtfChars name(env, jUniformName);
386     const int values[4] = {value1, value2, value3, value4};
387     UpdateIntUniforms(env, builder, name.c_str(), values, count);
388 }
389 
RuntimeShader_updateIntArrayUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jintArray jvalues)390 static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
391                                                  jstring jUniformName, jintArray jvalues) {
392     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
393     ScopedUtfChars name(env, jUniformName);
394     AutoJavaIntArray autoValues(env, jvalues, 0);
395     UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
396 }
397 
RuntimeShader_updateShader(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong shaderHandle)398 static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
399                                            jstring jUniformName, jlong shaderHandle) {
400     SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
401     ScopedUtfChars name(env, jUniformName);
402     SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
403 
404     SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
405     if (child.fChild == nullptr) {
406         ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
407         return;
408     }
409 
410     builder->child(name.c_str()) = sk_ref_sp(shader);
411 }
412 
413 ///////////////////////////////////////////////////////////////////////////////////////////////
414 
415 static const JNINativeMethod gColorMethods[] = {
416     { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
417     { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
418 };
419 
420 static const JNINativeMethod gShaderMethods[] = {
421     { "nativeGetFinalizer",   "()J",    (void*)Shader_getNativeFinalizer },
422 };
423 
424 static const JNINativeMethod gBitmapShaderMethods[] = {
425         {"nativeCreate", "(JJIIZZ)J", (void*)BitmapShader_constructor},
426         {"nativeCreateWithMaxAniso", "(JJIIIZ)J", (void*)BitmapShader_constructorWithMaxAniso},
427 
428 };
429 
430 static const JNINativeMethod gLinearGradientMethods[] = {
431     { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
432 };
433 
434 static const JNINativeMethod gRadialGradientMethods[] = {
435     { "nativeCreate",     "(JFFFFFF[J[FIJ)J",  (void*)RadialGradient_create     },
436 };
437 
438 static const JNINativeMethod gSweepGradientMethods[] = {
439     { "nativeCreate",     "(JFF[J[FJ)J",  (void*)SweepGradient_create     },
440 };
441 
442 static const JNINativeMethod gComposeShaderMethods[] = {
443     { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
444 };
445 
446 static const JNINativeMethod gRuntimeShaderMethods[] = {
447         {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
448         {"nativeCreateShader", "(JJ)J", (void*)RuntimeShader_create},
449         {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
450         {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
451          (void*)RuntimeShader_updateFloatArrayUniforms},
452         {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
453          (void*)RuntimeShader_updateFloatUniforms},
454         {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
455          (void*)RuntimeShader_updateIntArrayUniforms},
456         {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
457          (void*)RuntimeShader_updateIntUniforms},
458         {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
459 };
460 
register_android_graphics_Shader(JNIEnv * env)461 int register_android_graphics_Shader(JNIEnv* env)
462 {
463     android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
464                                   NELEM(gColorMethods));
465     android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
466                                   NELEM(gShaderMethods));
467     android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
468                                   NELEM(gBitmapShaderMethods));
469     android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
470                                   NELEM(gLinearGradientMethods));
471     android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
472                                   NELEM(gRadialGradientMethods));
473     android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
474                                   NELEM(gSweepGradientMethods));
475     android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
476                                   NELEM(gComposeShaderMethods));
477     android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
478                                   NELEM(gRuntimeShaderMethods));
479 
480     return 0;
481 }
482