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 <jni.h>
10 
11 #include <vector>
12 
13 using namespace android::uirenderer;
14 
15 /**
16  * By default Skia gradients will interpolate their colors in unpremul space
17  * and then premultiply each of the results. We must set this flag to preserve
18  * backwards compatiblity by premultiplying the colors of the gradient first,
19  * and then interpolating between them.
20  */
21 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
22 
23 #define ThrowIAE_IfNull(env, ptr)   \
24     if (nullptr == ptr) {           \
25         doThrowIAE(env);            \
26         return 0;                   \
27     }
28 
Color_RGBToHSV(JNIEnv * env,jobject,jint red,jint green,jint blue,jfloatArray hsvArray)29 static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
30 {
31     SkScalar hsv[3];
32     SkRGBToHSV(red, green, blue, hsv);
33 
34     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
35     float* values = autoHSV.ptr();
36     for (int i = 0; i < 3; i++) {
37         values[i] = SkScalarToFloat(hsv[i]);
38     }
39 }
40 
Color_HSVToColor(JNIEnv * env,jobject,jint alpha,jfloatArray hsvArray)41 static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
42 {
43     AutoJavaFloatArray  autoHSV(env, hsvArray, 3);
44 #ifdef SK_SCALAR_IS_FLOAT
45     SkScalar*   hsv = autoHSV.ptr();
46 #else
47     #error Need to convert float array to SkScalar array before calling the following function.
48 #endif
49 
50     return static_cast<jint>(SkHSVToColor(alpha, hsv));
51 }
52 
53 ///////////////////////////////////////////////////////////////////////////////////////////////
54 
Shader_safeUnref(SkShader * shader)55 static void Shader_safeUnref(SkShader* shader) {
56     SkSafeUnref(shader);
57 }
58 
Shader_getNativeFinalizer(JNIEnv *,jobject)59 static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
60     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
61 }
62 
63 ///////////////////////////////////////////////////////////////////////////////////////////////
64 
BitmapShader_constructor(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY)65 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
66         jint tileModeX, jint tileModeY) {
67     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
68     sk_sp<SkImage> image;
69     if (bitmapHandle) {
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(bitmapHandle).makeImage();
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     ThrowIAE_IfNull(env, shader.get());
82 
83     if (matrix) {
84         shader = shader->makeWithLocalMatrix(*matrix);
85     }
86 
87     return reinterpret_cast<jlong>(shader.release());
88 }
89 
90 ///////////////////////////////////////////////////////////////////////////////////////////////
91 
convertColorLongs(JNIEnv * env,jlongArray colorArray)92 static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
93     const size_t count = env->GetArrayLength(colorArray);
94     const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
95 
96     std::vector<SkColor4f> colors(count);
97     for (size_t i = 0; i < count; ++i) {
98         colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
99     }
100 
101     env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
102     return colors;
103 }
104 
105 ///////////////////////////////////////////////////////////////////////////////////////////////
106 
LinearGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)107 static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
108         jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
109         jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
110     SkPoint pts[2];
111     pts[0].set(x0, y0);
112     pts[1].set(x1, y1);
113 
114     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
115 
116     AutoJavaFloatArray autoPos(env, posArray, colors.size());
117 #ifdef SK_SCALAR_IS_FLOAT
118     SkScalar* pos = autoPos.ptr();
119 #else
120     #error Need to convert float array to SkScalar array before calling the following function.
121 #endif
122 
123     sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
124                 GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
125                 static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr));
126     ThrowIAE_IfNull(env, shader);
127 
128     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
129     if (matrix) {
130         shader = shader->makeWithLocalMatrix(*matrix);
131     }
132 
133     return reinterpret_cast<jlong>(shader.release());
134 }
135 
136 ///////////////////////////////////////////////////////////////////////////////////////////////
137 
RadialGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x,jfloat y,jfloat radius,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)138 static jlong RadialGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
139         jfloat radius, jlongArray colorArray, jfloatArray posArray, jint tileMode,
140         jlong colorSpaceHandle) {
141     SkPoint center;
142     center.set(x, y);
143 
144     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
145 
146     AutoJavaFloatArray autoPos(env, posArray, colors.size());
147 #ifdef SK_SCALAR_IS_FLOAT
148     SkScalar* pos = autoPos.ptr();
149 #else
150     #error Need to convert float array to SkScalar array before calling the following function.
151 #endif
152 
153     sk_sp<SkShader> shader = SkGradientShader::MakeRadial(center, radius, &colors[0],
154             GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
155             static_cast<SkShader::TileMode>(tileMode), sGradientShaderFlags, nullptr);
156     ThrowIAE_IfNull(env, shader);
157 
158     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
159     if (matrix) {
160         shader = shader->makeWithLocalMatrix(*matrix);
161     }
162 
163     return reinterpret_cast<jlong>(shader.release());
164 }
165 
166 ///////////////////////////////////////////////////////////////////////////////
167 
SweepGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x,jfloat y,jlongArray colorArray,jfloatArray jpositions,jlong colorSpaceHandle)168 static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
169         jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
170     std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
171 
172     AutoJavaFloatArray autoPos(env, jpositions, colors.size());
173 #ifdef SK_SCALAR_IS_FLOAT
174     SkScalar* pos = autoPos.ptr();
175 #else
176     #error Need to convert float array to SkScalar array before calling the following function.
177 #endif
178 
179     sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
180             GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
181             sGradientShaderFlags, nullptr);
182     ThrowIAE_IfNull(env, shader);
183 
184     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
185     if (matrix) {
186         shader = shader->makeWithLocalMatrix(*matrix);
187     }
188 
189     return reinterpret_cast<jlong>(shader.release());
190 }
191 
192 ///////////////////////////////////////////////////////////////////////////////////////////////
193 
ComposeShader_create(JNIEnv * env,jobject o,jlong matrixPtr,jlong shaderAHandle,jlong shaderBHandle,jint xfermodeHandle)194 static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
195         jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
196     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
197     SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
198     SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
199     SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
200     sk_sp<SkShader> baseShader(SkShader::MakeComposeShader(
201             sk_ref_sp(shaderA), sk_ref_sp(shaderB), mode));
202 
203     SkShader* shader;
204 
205     if (matrix) {
206         shader = baseShader->makeWithLocalMatrix(*matrix).release();
207     } else {
208         shader = baseShader.release();
209     }
210     return reinterpret_cast<jlong>(shader);
211 }
212 
213 ///////////////////////////////////////////////////////////////////////////////////////////////
214 
215 static const JNINativeMethod gColorMethods[] = {
216     { "nativeRGBToHSV",    "(III[F)V", (void*)Color_RGBToHSV   },
217     { "nativeHSVToColor",  "(I[F)I",   (void*)Color_HSVToColor }
218 };
219 
220 static const JNINativeMethod gShaderMethods[] = {
221     { "nativeGetFinalizer",   "()J",    (void*)Shader_getNativeFinalizer },
222 };
223 
224 static const JNINativeMethod gBitmapShaderMethods[] = {
225     { "nativeCreate",      "(JJII)J",  (void*)BitmapShader_constructor },
226 };
227 
228 static const JNINativeMethod gLinearGradientMethods[] = {
229     { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
230 };
231 
232 static const JNINativeMethod gRadialGradientMethods[] = {
233     { "nativeCreate",     "(JFFF[J[FIJ)J",  (void*)RadialGradient_create     },
234 };
235 
236 static const JNINativeMethod gSweepGradientMethods[] = {
237     { "nativeCreate",     "(JFF[J[FJ)J",  (void*)SweepGradient_create     },
238 };
239 
240 static const JNINativeMethod gComposeShaderMethods[] = {
241     { "nativeCreate",      "(JJJI)J",   (void*)ComposeShader_create     },
242 };
243 
register_android_graphics_Shader(JNIEnv * env)244 int register_android_graphics_Shader(JNIEnv* env)
245 {
246     android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
247                                   NELEM(gColorMethods));
248     android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
249                                   NELEM(gShaderMethods));
250     android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
251                                   NELEM(gBitmapShaderMethods));
252     android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
253                                   NELEM(gLinearGradientMethods));
254     android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
255                                   NELEM(gRadialGradientMethods));
256     android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
257                                   NELEM(gSweepGradientMethods));
258     android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
259                                   NELEM(gComposeShaderMethods));
260 
261     return 0;
262 }
263