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