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