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