Shader.cpp revision 5932b0d8fc313f9e4f2c8e5c431ac0e270f9f259
1#include <jni.h>
2#include "GraphicsJNI.h"
3
4#include "SkShader.h"
5#include "SkGradientShader.h"
6#include "SkPorterDuff.h"
7#include "SkComposeShader.h"
8#include "SkTemplates.h"
9#include "SkXfermode.h"
10
11#include <SkiaShader.h>
12#include <Caches.h>
13
14using namespace android::uirenderer;
15
16static struct {
17    jclass clazz;
18    jfieldID shader;
19} gShaderClassInfo;
20
21static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
22    if (NULL == ptr) {
23        doThrowIAE(env);
24    }
25}
26
27static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
28{
29    SkScalar hsv[3];
30    SkRGBToHSV(red, green, blue, hsv);
31
32    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
33    float* values = autoHSV.ptr();
34    for (int i = 0; i < 3; i++) {
35        values[i] = SkScalarToFloat(hsv[i]);
36    }
37}
38
39static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
40{
41    AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
42#ifdef SK_SCALAR_IS_FLOAT
43    SkScalar*   hsv = autoHSV.ptr();
44#else
45    #error Need to convert float array to SkScalar array before calling the following function.
46#endif
47
48    return static_cast<jint>(SkHSVToColor(alpha, hsv));
49}
50
51///////////////////////////////////////////////////////////////////////////////////////////////
52
53static void Shader_destructor(JNIEnv* env, jobject o, jlong shaderHandle, jlong skiaShaderHandle)
54{
55    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
56    SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
57    SkSafeUnref(shader);
58    // skiaShader == NULL when not !USE_OPENGL_RENDERER, so no need to delete it outside the ifdef
59#ifdef USE_OPENGL_RENDERER
60    if (android::uirenderer::Caches::hasInstance()) {
61        android::uirenderer::Caches::getInstance().resourceCache.destructor(skiaShader);
62    } else {
63        delete skiaShader;
64    }
65#endif
66}
67
68static void Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle,
69        jlong skiaShaderHandle, jlong matrixHandle)
70{
71    SkShader* shader       = reinterpret_cast<SkShader*>(shaderHandle);
72    SkiaShader* skiaShader = reinterpret_cast<SkiaShader*>(skiaShaderHandle);
73    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
74    if (shader) {
75        if (NULL == matrix) {
76            shader->resetLocalMatrix();
77        }
78        else {
79            shader->setLocalMatrix(*matrix);
80        }
81#ifdef USE_OPENGL_RENDERER
82        skiaShader->setMatrix(const_cast<SkMatrix*>(matrix));
83#endif
84    }
85}
86
87///////////////////////////////////////////////////////////////////////////////////////////////
88
89static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong bitmapHandle,
90                                      jint tileModeX, jint tileModeY)
91{
92    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
93    SkShader* s = SkShader::CreateBitmapShader(*bitmap,
94                                        (SkShader::TileMode)tileModeX,
95                                        (SkShader::TileMode)tileModeY);
96
97    ThrowIAE_IfNull(env, s);
98    return reinterpret_cast<jlong>(s);
99}
100
101static jlong BitmapShader_postConstructor(JNIEnv* env, jobject o, jlong shaderHandle,
102        jlong bitmapHandle, jint tileModeX, jint tileModeY) {
103    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
104    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
105#ifdef USE_OPENGL_RENDERER
106    SkiaShader* skiaShader = new SkiaBitmapShader(bitmap, shader,
107            static_cast<SkShader::TileMode>(tileModeX), static_cast<SkShader::TileMode>(tileModeY),
108            NULL, !shader->isOpaque());
109    return reinterpret_cast<jlong>(skiaShader);
110#else
111    return NULL;
112#endif
113}
114
115///////////////////////////////////////////////////////////////////////////////////////////////
116
117static jlong LinearGradient_create1(JNIEnv* env, jobject o,
118                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
119                                    jintArray colorArray, jfloatArray posArray, jint tileMode)
120{
121    SkPoint pts[2];
122    pts[0].set(x0, y0);
123    pts[1].set(x1, y1);
124
125    size_t count = env->GetArrayLength(colorArray);
126    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
127
128    AutoJavaFloatArray autoPos(env, posArray, count);
129#ifdef SK_SCALAR_IS_FLOAT
130    SkScalar* pos = autoPos.ptr();
131#else
132    #error Need to convert float array to SkScalar array before calling the following function.
133#endif
134
135    SkShader* shader = SkGradientShader::CreateLinear(pts,
136            reinterpret_cast<const SkColor*>(colorValues), pos, count,
137            static_cast<SkShader::TileMode>(tileMode));
138
139    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
140    ThrowIAE_IfNull(env, shader);
141    return reinterpret_cast<jlong>(shader);
142}
143
144static jlong LinearGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
145        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jintArray colorArray,
146        jfloatArray posArray, jint tileMode) {
147#ifdef USE_OPENGL_RENDERER
148    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
149    size_t count = env->GetArrayLength(colorArray);
150    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
151
152    jfloat* storedBounds = new jfloat[4];
153    storedBounds[0] = x0; storedBounds[1] = y0;
154    storedBounds[2] = x1; storedBounds[3] = y1;
155
156    bool missFirst = false;
157    bool missLast = false;
158    size_t stopCount = count;
159
160    jfloat* storedPositions = NULL;
161    if (posArray) {
162        AutoJavaFloatArray autoPos(env, posArray, count);
163        const float* posValues = autoPos.ptr();
164
165        missFirst = posValues[0] != 0.0f;
166        missLast = posValues[count - 1] != 1.0f;
167
168        stopCount += missFirst + missLast;
169        storedPositions = new jfloat[stopCount];
170
171        if (missFirst) {
172            storedPositions[0] = 0.0f;
173        }
174
175        for (size_t i = missFirst; i < count + missFirst; i++) {
176            storedPositions[i] = posValues[i - missFirst];
177        }
178
179        if (missLast) {
180            storedPositions[stopCount - 1] = 1.0f;
181        }
182    } else {
183        storedPositions = new jfloat[count];
184        storedPositions[0] = 0.0f;
185        const jfloat step = 1.0f / (count - 1);
186        for (size_t i = 1; i < count - 1; i++) {
187            storedPositions[i] = step * i;
188        }
189        storedPositions[count - 1] = 1.0f;
190    }
191
192    uint32_t* storedColors = new uint32_t[stopCount];
193
194    if (missFirst) {
195        storedColors[0] = static_cast<uint32_t>(colorValues[0]);
196    }
197
198    for (size_t i = missFirst; i < count + missFirst; i++) {
199        storedColors[i] = static_cast<uint32_t>(colorValues[i - missFirst]);
200    }
201
202    if (missLast) {
203        storedColors[stopCount - 1] = static_cast<uint32_t>(colorValues[count - 1]);
204    }
205
206    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
207            storedPositions, stopCount, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
208            !shader->isOpaque());
209
210    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
211    return reinterpret_cast<jlong>(skiaShader);
212#else
213    return NULL;
214#endif
215}
216
217static jlong LinearGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
218        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
219#ifdef USE_OPENGL_RENDERER
220    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
221    float* storedBounds = new float[4];
222    storedBounds[0] = x0; storedBounds[1] = y0;
223    storedBounds[2] = x1; storedBounds[3] = y1;
224
225    float* storedPositions = new float[2];
226    storedPositions[0] = 0.0f;
227    storedPositions[1] = 1.0f;
228
229    uint32_t* storedColors = new uint32_t[2];
230    storedColors[0] = static_cast<uint32_t>(color0);
231    storedColors[1] = static_cast<uint32_t>(color1);
232
233    SkiaShader* skiaShader = new SkiaLinearGradientShader(storedBounds, storedColors,
234            storedPositions, 2, shader, static_cast<SkShader::TileMode>(tileMode), NULL,
235            !shader->isOpaque());
236
237    return reinterpret_cast<jlong>(skiaShader);
238#else
239    return NULL;
240#endif
241}
242
243static jlong LinearGradient_create2(JNIEnv* env, jobject o,
244                                    jfloat x0, jfloat y0, jfloat x1, jfloat y1,
245                                    jint color0, jint color1, jint tileMode)
246{
247    SkPoint pts[2];
248    pts[0].set(x0, y0);
249    pts[1].set(x1, y1);
250
251    SkColor colors[2];
252    colors[0] = color0;
253    colors[1] = color1;
254
255    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, (SkShader::TileMode)tileMode);
256
257    ThrowIAE_IfNull(env, s);
258    return reinterpret_cast<jlong>(s);
259}
260
261///////////////////////////////////////////////////////////////////////////////////////////////
262
263static jlong RadialGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
264        jintArray colorArray, jfloatArray posArray, jint tileMode) {
265    SkPoint center;
266    center.set(x, y);
267
268    size_t      count = env->GetArrayLength(colorArray);
269    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
270
271    AutoJavaFloatArray autoPos(env, posArray, count);
272#ifdef SK_SCALAR_IS_FLOAT
273    SkScalar* pos = autoPos.ptr();
274#else
275    #error Need to convert float array to SkScalar array before calling the following function.
276#endif
277
278    SkShader* shader = SkGradientShader::CreateRadial(center, radius,
279            reinterpret_cast<const SkColor*>(colorValues), pos, count,
280            static_cast<SkShader::TileMode>(tileMode));
281    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
282                                 JNI_ABORT);
283
284    ThrowIAE_IfNull(env, shader);
285    return reinterpret_cast<jlong>(shader);
286}
287
288static jlong RadialGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y, jfloat radius,
289        jint color0, jint color1, jint tileMode) {
290    SkPoint center;
291    center.set(x, y);
292
293    SkColor colors[2];
294    colors[0] = color0;
295    colors[1] = color1;
296
297    SkShader* s = SkGradientShader::CreateRadial(center, radius, colors, NULL, 2,
298            (SkShader::TileMode)tileMode);
299    ThrowIAE_IfNull(env, s);
300    return reinterpret_cast<jlong>(s);
301}
302
303static jlong RadialGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
304        jfloat x, jfloat y, jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
305#ifdef USE_OPENGL_RENDERER
306    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
307    size_t count = env->GetArrayLength(colorArray);
308    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
309
310    jfloat* storedPositions = new jfloat[count];
311    uint32_t* storedColors = new uint32_t[count];
312    for (size_t i = 0; i < count; i++) {
313        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
314    }
315
316    if (posArray) {
317        AutoJavaFloatArray autoPos(env, posArray, count);
318        const float* posValues = autoPos.ptr();
319        for (size_t i = 0; i < count; i++) {
320            storedPositions[i] = posValues[i];
321        }
322    } else {
323        storedPositions[0] = 0.0f;
324        const jfloat step = 1.0f / (count - 1);
325        for (size_t i = 1; i < count - 1; i++) {
326            storedPositions[i] = step * i;
327        }
328        storedPositions[count - 1] = 1.0f;
329    }
330
331    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
332            storedPositions, count, shader, (SkShader::TileMode) tileMode, NULL,
333            !shader->isOpaque());
334
335    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
336    return reinterpret_cast<jlong>(skiaShader);
337#else
338    return NULL;
339#endif
340}
341
342static jlong RadialGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
343        jfloat x, jfloat y, jfloat radius, jint color0, jint color1, jint tileMode) {
344#ifdef USE_OPENGL_RENDERER
345    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
346    float* storedPositions = new float[2];
347    storedPositions[0] = 0.0f;
348    storedPositions[1] = 1.0f;
349
350    uint32_t* storedColors = new uint32_t[2];
351    storedColors[0] = static_cast<uint32_t>(color0);
352    storedColors[1] = static_cast<uint32_t>(color1);
353
354    SkiaShader* skiaShader = new SkiaCircularGradientShader(x, y, radius, storedColors,
355            storedPositions, 2, shader, (SkShader::TileMode) tileMode, NULL,
356            !shader->isOpaque());
357
358    return reinterpret_cast<jlong>(skiaShader);
359#else
360    return NULL;
361#endif
362}
363
364///////////////////////////////////////////////////////////////////////////////
365
366static jlong SweepGradient_create1(JNIEnv* env, jobject, jfloat x, jfloat y,
367        jintArray jcolors, jfloatArray jpositions) {
368    size_t      count = env->GetArrayLength(jcolors);
369    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
370
371    AutoJavaFloatArray autoPos(env, jpositions, count);
372#ifdef SK_SCALAR_IS_FLOAT
373    SkScalar* pos = autoPos.ptr();
374#else
375    #error Need to convert float array to SkScalar array before calling the following function.
376#endif
377
378    SkShader* shader = SkGradientShader::CreateSweep(x, y,
379            reinterpret_cast<const SkColor*>(colors), pos, count);
380    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
381                                 JNI_ABORT);
382    ThrowIAE_IfNull(env, shader);
383    return reinterpret_cast<jlong>(shader);
384}
385
386static jlong SweepGradient_create2(JNIEnv* env, jobject, jfloat x, jfloat y,
387        int color0, int color1) {
388    SkColor colors[2];
389    colors[0] = color0;
390    colors[1] = color1;
391    SkShader* s = SkGradientShader::CreateSweep(x, y, colors, NULL, 2);
392    ThrowIAE_IfNull(env, s);
393    return reinterpret_cast<jlong>(s);
394}
395
396static jlong SweepGradient_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
397        jfloat x, jfloat y, jintArray colorArray, jfloatArray posArray) {
398#ifdef USE_OPENGL_RENDERER
399    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
400    size_t count = env->GetArrayLength(colorArray);
401    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
402
403    jfloat* storedPositions = new jfloat[count];
404    uint32_t* storedColors = new uint32_t[count];
405    for (size_t i = 0; i < count; i++) {
406        storedColors[i] = static_cast<uint32_t>(colorValues[i]);
407    }
408
409    if (posArray) {
410        AutoJavaFloatArray autoPos(env, posArray, count);
411        const float* posValues = autoPos.ptr();
412        for (size_t i = 0; i < count; i++) {
413            storedPositions[i] = posValues[i];
414        }
415    } else {
416        storedPositions[0] = 0.0f;
417        const jfloat step = 1.0f / (count - 1);
418        for (size_t i = 1; i < count - 1; i++) {
419            storedPositions[i] = step * i;
420        }
421        storedPositions[count - 1] = 1.0f;
422    }
423
424    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, count,
425            shader, NULL, !shader->isOpaque());
426
427    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
428    return reinterpret_cast<jlong>(skiaShader);
429#else
430    return NULL;
431#endif
432}
433
434static jlong SweepGradient_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
435        jfloat x, jfloat y, jint color0, jint color1) {
436#ifdef USE_OPENGL_RENDERER
437    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
438    float* storedPositions = new float[2];
439    storedPositions[0] = 0.0f;
440    storedPositions[1] = 1.0f;
441
442    uint32_t* storedColors = new uint32_t[2];
443    storedColors[0] = static_cast<uint32_t>(color0);
444    storedColors[1] = static_cast<uint32_t>(color1);
445
446    SkiaShader* skiaShader = new SkiaSweepGradientShader(x, y, storedColors, storedPositions, 2,
447            shader, NULL, !shader->isOpaque());
448
449    return reinterpret_cast<jlong>(skiaShader);
450#else
451    return NULL;
452#endif
453}
454
455///////////////////////////////////////////////////////////////////////////////////////////////
456
457static jlong ComposeShader_create1(JNIEnv* env, jobject o,
458        jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle)
459{
460    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
461    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
462    SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
463    SkShader* shader = new SkComposeShader(shaderA, shaderB, mode);
464    return reinterpret_cast<jlong>(shader);
465}
466
467static jlong ComposeShader_create2(JNIEnv* env, jobject o,
468        jlong shaderAHandle, jlong shaderBHandle, jint porterDuffModeHandle)
469{
470    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
471    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
472    SkPorterDuff::Mode porterDuffMode = static_cast<SkPorterDuff::Mode>(porterDuffModeHandle);
473    SkAutoUnref au(SkPorterDuff::CreateXfermode(porterDuffMode));
474    SkXfermode* mode = (SkXfermode*) au.get();
475    SkShader* shader = new SkComposeShader(shaderA, shaderB, mode);
476    return reinterpret_cast<jlong>(shader);
477}
478
479static jlong ComposeShader_postCreate2(JNIEnv* env, jobject o, jlong shaderHandle,
480        jlong shaderAHandle, jlong shaderBHandle, jint porterDuffModeHandle) {
481#ifdef USE_OPENGL_RENDERER
482    SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
483    SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
484    SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
485    SkPorterDuff::Mode porterDuffMode = static_cast<SkPorterDuff::Mode>(porterDuffModeHandle);
486    SkXfermode::Mode mode = SkPorterDuff::ToXfermodeMode(porterDuffMode);
487    SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, mode, shader);
488    return reinterpret_cast<jlong>(skiaShader);
489#else
490    return NULL;
491#endif
492}
493
494static jlong ComposeShader_postCreate1(JNIEnv* env, jobject o, jlong shaderHandle,
495        jlong shaderAHandle, jlong shaderBHandle, jlong modeHandle) {
496#ifdef USE_OPENGL_RENDERER
497    SkShader* shader = reinterpret_cast<SkShader *>(shaderHandle);
498    SkiaShader* shaderA = reinterpret_cast<SkiaShader *>(shaderAHandle);
499    SkiaShader* shaderB = reinterpret_cast<SkiaShader *>(shaderBHandle);
500    SkXfermode* mode = reinterpret_cast<SkXfermode *>(modeHandle);
501    SkXfermode::Mode skiaMode;
502    if (!SkXfermode::AsMode(mode, &skiaMode)) {
503        // TODO: Support other modes
504        skiaMode = SkXfermode::kSrcOver_Mode;
505    }
506    SkiaShader* skiaShader = new SkiaComposeShader(shaderA, shaderB, skiaMode, shader);
507    return reinterpret_cast<jlong>(skiaShader);
508#else
509    return NULL;
510#endif
511}
512
513///////////////////////////////////////////////////////////////////////////////////////////////
514
515static JNINativeMethod gColorMethods[] = {
516    { "nativeRGBToHSV",     "(III[F)V", (void*)Color_RGBToHSV   },
517    { "nativeHSVToColor",   "(I[F)I",   (void*)Color_HSVToColor }
518};
519
520static JNINativeMethod gShaderMethods[] = {
521    { "nativeDestructor",        "(JJ)V",    (void*)Shader_destructor        },
522    { "nativeSetLocalMatrix",    "(JJJ)V",   (void*)Shader_setLocalMatrix    }
523};
524
525static JNINativeMethod gBitmapShaderMethods[] = {
526    { "nativeCreate",     "(JII)J",  (void*)BitmapShader_constructor },
527    { "nativePostCreate", "(JJII)J", (void*)BitmapShader_postConstructor }
528};
529
530static JNINativeMethod gLinearGradientMethods[] = {
531    { "nativeCreate1",     "(FFFF[I[FI)J",  (void*)LinearGradient_create1     },
532    { "nativeCreate2",     "(FFFFIII)J",    (void*)LinearGradient_create2     },
533    { "nativePostCreate1", "(JFFFF[I[FI)J", (void*)LinearGradient_postCreate1 },
534    { "nativePostCreate2", "(JFFFFIII)J",   (void*)LinearGradient_postCreate2 }
535};
536
537static JNINativeMethod gRadialGradientMethods[] = {
538    { "nativeCreate1",     "(FFF[I[FI)J",  (void*)RadialGradient_create1     },
539    { "nativeCreate2",     "(FFFIII)J",    (void*)RadialGradient_create2     },
540    { "nativePostCreate1", "(JFFF[I[FI)J", (void*)RadialGradient_postCreate1 },
541    { "nativePostCreate2", "(JFFFIII)J",   (void*)RadialGradient_postCreate2 }
542};
543
544static JNINativeMethod gSweepGradientMethods[] = {
545    { "nativeCreate1",     "(FF[I[F)J",  (void*)SweepGradient_create1     },
546    { "nativeCreate2",     "(FFII)J",    (void*)SweepGradient_create2     },
547    { "nativePostCreate1", "(JFF[I[F)J", (void*)SweepGradient_postCreate1 },
548    { "nativePostCreate2", "(JFFII)J",   (void*)SweepGradient_postCreate2 }
549};
550
551static JNINativeMethod gComposeShaderMethods[] = {
552    { "nativeCreate1",      "(JJJ)J",   (void*)ComposeShader_create1     },
553    { "nativeCreate2",      "(JJI)J",   (void*)ComposeShader_create2     },
554    { "nativePostCreate1",  "(JJJJ)J",  (void*)ComposeShader_postCreate1 },
555    { "nativePostCreate2",  "(JJJI)J",  (void*)ComposeShader_postCreate2 }
556};
557
558#include <android_runtime/AndroidRuntime.h>
559
560#define REG(env, name, array)                                                                       \
561    result = android::AndroidRuntime::registerNativeMethods(env, name, array, SK_ARRAY_COUNT(array));  \
562    if (result < 0) return result
563
564int register_android_graphics_Shader(JNIEnv* env)
565{
566    int result;
567
568    REG(env, "android/graphics/Color", gColorMethods);
569    REG(env, "android/graphics/Shader", gShaderMethods);
570    REG(env, "android/graphics/BitmapShader", gBitmapShaderMethods);
571    REG(env, "android/graphics/LinearGradient", gLinearGradientMethods);
572    REG(env, "android/graphics/RadialGradient", gRadialGradientMethods);
573    REG(env, "android/graphics/SweepGradient", gSweepGradientMethods);
574    REG(env, "android/graphics/ComposeShader", gComposeShaderMethods);
575
576    return result;
577}
578