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