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