Shader.cpp revision 59069e00a8965cb67350e95f32d62c31d6bf010e
1#include "GraphicsJNI.h"
2#include "SkGradientShader.h"
3#include "SkImagePriv.h"
4#include "SkShader.h"
5#include "SkBlendMode.h"
6#include "core_jni_helpers.h"
7
8#include <Caches.h>
9#include <jni.h>
10
11using namespace android::uirenderer;
12
13/**
14 * By default Skia gradients will interpolate their colors in unpremul space
15 * and then premultiply each of the results. We must set this flag to preserve
16 * backwards compatiblity by premultiplying the colors of the gradient first,
17 * and then interpolating between them.
18 */
19static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
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_safeUnref(JNIEnv* env, jobject o, jlong shaderHandle) {
54    SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
55    SkSafeUnref(shader);
56}
57
58///////////////////////////////////////////////////////////////////////////////////////////////
59
60static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jobject jbitmap,
61        jint tileModeX, jint tileModeY) {
62    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
63    SkBitmap bitmap;
64    if (jbitmap) {
65        // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
66        // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
67        android::bitmap::toBitmap(env, jbitmap).getSkBitmapForShaders(&bitmap);
68    }
69
70    sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
71    sk_sp<SkShader> baseShader = image->makeShader(
72            (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY);
73
74    SkShader* shader;
75    if (matrix) {
76        shader = baseShader->makeWithLocalMatrix(*matrix).release();
77    } else {
78        shader = baseShader.release();
79    }
80
81    ThrowIAE_IfNull(env, shader);
82    return reinterpret_cast<jlong>(shader);
83}
84
85///////////////////////////////////////////////////////////////////////////////////////////////
86
87static jlong LinearGradient_create1(JNIEnv* env, jobject o, jlong matrixPtr,
88        jfloat x0, jfloat y0, jfloat x1, jfloat y1,
89        jintArray colorArray, jfloatArray posArray, jint tileMode) {
90    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
91    SkPoint pts[2];
92    pts[0].set(x0, y0);
93    pts[1].set(x1, y1);
94
95    size_t count = env->GetArrayLength(colorArray);
96    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
97
98    AutoJavaFloatArray autoPos(env, posArray, count);
99#ifdef SK_SCALAR_IS_FLOAT
100    SkScalar* pos = autoPos.ptr();
101#else
102    #error Need to convert float array to SkScalar array before calling the following function.
103#endif
104
105    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts,
106            reinterpret_cast<const SkColor*>(colorValues), pos, count,
107            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
108
109    SkShader* shader;
110    if (matrix) {
111        shader = baseShader->makeWithLocalMatrix(*matrix).release();
112    } else {
113        shader = baseShader.release();
114    }
115
116    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues), JNI_ABORT);
117    ThrowIAE_IfNull(env, shader);
118    return reinterpret_cast<jlong>(shader);
119}
120
121static jlong LinearGradient_create2(JNIEnv* env, jobject o, jlong matrixPtr,
122        jfloat x0, jfloat y0, jfloat x1, jfloat y1, jint color0, jint color1, jint tileMode) {
123    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
124
125    SkPoint pts[2];
126    pts[0].set(x0, y0);
127    pts[1].set(x1, y1);
128
129    SkColor colors[2];
130    colors[0] = color0;
131    colors[1] = color1;
132
133    sk_sp<SkShader> baseShader(SkGradientShader::MakeLinear(pts, colors, NULL, 2,
134            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL));
135
136    SkShader* s;
137    if (matrix) {
138        s = baseShader->makeWithLocalMatrix(*matrix).release();
139    } else {
140        s = baseShader.release();
141    }
142
143    ThrowIAE_IfNull(env, s);
144    return reinterpret_cast<jlong>(s);
145}
146
147///////////////////////////////////////////////////////////////////////////////////////////////
148
149static jlong RadialGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
150        jfloat radius, jintArray colorArray, jfloatArray posArray, jint tileMode) {
151    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
152    SkPoint center;
153    center.set(x, y);
154
155    size_t      count = env->GetArrayLength(colorArray);
156    const jint* colorValues = env->GetIntArrayElements(colorArray, NULL);
157
158    AutoJavaFloatArray autoPos(env, posArray, count);
159#ifdef SK_SCALAR_IS_FLOAT
160    SkScalar* pos = autoPos.ptr();
161#else
162    #error Need to convert float array to SkScalar array before calling the following function.
163#endif
164
165    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius,
166            reinterpret_cast<const SkColor*>(colorValues), pos, count,
167            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
168
169    SkShader* shader;
170    if (matrix) {
171        shader = baseShader->makeWithLocalMatrix(*matrix).release();
172    } else {
173        shader = baseShader.release();
174    }
175
176    env->ReleaseIntArrayElements(colorArray, const_cast<jint*>(colorValues),
177                                 JNI_ABORT);
178
179    ThrowIAE_IfNull(env, shader);
180    return reinterpret_cast<jlong>(shader);
181}
182
183static jlong RadialGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y, jfloat radius,
184        jint color0, jint color1, jint tileMode) {
185    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
186    SkPoint center;
187    center.set(x, y);
188
189    SkColor colors[2];
190    colors[0] = color0;
191    colors[1] = color1;
192
193    sk_sp<SkShader> baseShader = SkGradientShader::MakeRadial(center, radius, colors, NULL, 2,
194            static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, NULL);
195
196    SkShader* shader;
197    if (matrix) {
198        shader = baseShader->makeWithLocalMatrix(*matrix).release();
199    } else {
200        shader = baseShader.release();
201    }
202    ThrowIAE_IfNull(env, shader);
203    return reinterpret_cast<jlong>(shader);
204}
205
206///////////////////////////////////////////////////////////////////////////////
207
208static jlong SweepGradient_create1(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
209        jintArray jcolors, jfloatArray jpositions) {
210    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
211    size_t      count = env->GetArrayLength(jcolors);
212    const jint* colors = env->GetIntArrayElements(jcolors, NULL);
213
214    AutoJavaFloatArray autoPos(env, jpositions, count);
215#ifdef SK_SCALAR_IS_FLOAT
216    SkScalar* pos = autoPos.ptr();
217#else
218    #error Need to convert float array to SkScalar array before calling the following function.
219#endif
220
221    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y,
222            reinterpret_cast<const SkColor*>(colors), pos, count,
223            sGradientShaderFlags, NULL);
224
225    SkShader* shader;
226    if (matrix) {
227        shader = baseShader->makeWithLocalMatrix(*matrix).release();
228    } else {
229        shader = baseShader.release();
230    }
231
232    env->ReleaseIntArrayElements(jcolors, const_cast<jint*>(colors),
233                                 JNI_ABORT);
234    ThrowIAE_IfNull(env, shader);
235    return reinterpret_cast<jlong>(shader);
236}
237
238static jlong SweepGradient_create2(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
239        int color0, int color1) {
240    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
241    SkColor colors[2];
242    colors[0] = color0;
243    colors[1] = color1;
244
245    sk_sp<SkShader> baseShader = SkGradientShader::MakeSweep(x, y, colors,
246            NULL, 2, sGradientShaderFlags, NULL);
247
248    SkShader* shader;
249    if (matrix) {
250        shader = baseShader->makeWithLocalMatrix(*matrix).release();
251    } else {
252        shader = baseShader.release();
253    }
254    ThrowIAE_IfNull(env, shader);
255    return reinterpret_cast<jlong>(shader);
256}
257
258///////////////////////////////////////////////////////////////////////////////////////////////
259
260static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
261        jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
262    const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
263    SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
264    SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
265    SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
266    sk_sp<SkShader> baseShader(SkShader::MakeComposeShader(
267            sk_ref_sp(shaderA), sk_ref_sp(shaderB), mode));
268
269    SkShader* shader;
270
271    if (matrix) {
272        shader = baseShader->makeWithLocalMatrix(*matrix).release();
273    } else {
274        shader = baseShader.release();
275    }
276    return reinterpret_cast<jlong>(shader);
277}
278
279///////////////////////////////////////////////////////////////////////////////////////////////
280
281static const JNINativeMethod gColorMethods[] = {
282    { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
283    { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
284};
285
286static const JNINativeMethod gShaderMethods[] = {
287    { "nativeSafeUnref",   "(J)V",    (void*)Shader_safeUnref },
288};
289
290static const JNINativeMethod gBitmapShaderMethods[] = {
291    { "nativeCreate",      "(JLandroid/graphics/Bitmap;II)J",  (void*)BitmapShader_constructor },
292};
293
294static const JNINativeMethod gLinearGradientMethods[] = {
295    { "nativeCreate1",     "(JFFFF[I[FI)J",  (void*)LinearGradient_create1     },
296    { "nativeCreate2",     "(JFFFFIII)J",    (void*)LinearGradient_create2     },
297};
298
299static const JNINativeMethod gRadialGradientMethods[] = {
300    { "nativeCreate1",     "(JFFF[I[FI)J",  (void*)RadialGradient_create1     },
301    { "nativeCreate2",     "(JFFFIII)J",    (void*)RadialGradient_create2     },
302};
303
304static const JNINativeMethod gSweepGradientMethods[] = {
305    { "nativeCreate1",     "(JFF[I[F)J",  (void*)SweepGradient_create1     },
306    { "nativeCreate2",     "(JFFII)J",    (void*)SweepGradient_create2     },
307};
308
309static const JNINativeMethod gComposeShaderMethods[] = {
310    { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
311};
312
313int register_android_graphics_Shader(JNIEnv* env)
314{
315    android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
316                                  NELEM(gColorMethods));
317    android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
318                                  NELEM(gShaderMethods));
319    android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
320                                  NELEM(gBitmapShaderMethods));
321    android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
322                                  NELEM(gLinearGradientMethods));
323    android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
324                                  NELEM(gRadialGradientMethods));
325    android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
326                                  NELEM(gSweepGradientMethods));
327    android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
328                                  NELEM(gComposeShaderMethods));
329
330    return 0;
331}
332