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