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