1 #undef LOG_TAG
2 #define LOG_TAG "ShaderJNI"
3
4 #include "GraphicsJNI.h"
5 #include "SkColorFilter.h"
6 #include "SkGradientShader.h"
7 #include "SkImagePriv.h"
8 #include "SkShader.h"
9 #include "SkBlendMode.h"
10 #include "include/effects/SkRuntimeEffect.h"
11
12 #include <vector>
13
14 using namespace android::uirenderer;
15
16 /**
17 * By default Skia gradients will interpolate their colors in unpremul space
18 * and then premultiply each of the results. We must set this flag to preserve
19 * backwards compatiblity by premultiplying the colors of the gradient first,
20 * and then interpolating between them.
21 */
22 static const uint32_t sGradientShaderFlags = SkGradientShader::kInterpolateColorsInPremul_Flag;
23
24 #define ThrowIAE_IfNull(env, ptr) \
25 if (nullptr == ptr) { \
26 doThrowIAE(env); \
27 return 0; \
28 }
29
Color_RGBToHSV(JNIEnv * env,jobject,jint red,jint green,jint blue,jfloatArray hsvArray)30 static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
31 {
32 SkScalar hsv[3];
33 SkRGBToHSV(red, green, blue, hsv);
34
35 AutoJavaFloatArray autoHSV(env, hsvArray, 3);
36 float* values = autoHSV.ptr();
37 for (int i = 0; i < 3; i++) {
38 values[i] = SkScalarToFloat(hsv[i]);
39 }
40 }
41
Color_HSVToColor(JNIEnv * env,jobject,jint alpha,jfloatArray hsvArray)42 static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
43 {
44 AutoJavaFloatArray autoHSV(env, hsvArray, 3);
45 #ifdef SK_SCALAR_IS_FLOAT
46 SkScalar* hsv = autoHSV.ptr();
47 #else
48 #error Need to convert float array to SkScalar array before calling the following function.
49 #endif
50
51 return static_cast<jint>(SkHSVToColor(alpha, hsv));
52 }
53
54 ///////////////////////////////////////////////////////////////////////////////////////////////
55
Shader_safeUnref(SkShader * shader)56 static void Shader_safeUnref(SkShader* shader) {
57 SkSafeUnref(shader);
58 }
59
Shader_getNativeFinalizer(JNIEnv *,jobject)60 static jlong Shader_getNativeFinalizer(JNIEnv*, jobject) {
61 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Shader_safeUnref));
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////////////////////
65
BitmapShader_constructor(JNIEnv * env,jobject o,jlong matrixPtr,jlong bitmapHandle,jint tileModeX,jint tileModeY,bool filter)66 static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong matrixPtr, jlong bitmapHandle,
67 jint tileModeX, jint tileModeY, bool filter) {
68 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
69 sk_sp<SkImage> image;
70 if (bitmapHandle) {
71 // Only pass a valid SkBitmap object to the constructor if the Bitmap exists. Otherwise,
72 // we'll pass an empty SkBitmap to avoid crashing/excepting for compatibility.
73 image = android::bitmap::toBitmap(bitmapHandle).makeImage();
74 }
75
76 if (!image.get()) {
77 SkBitmap bitmap;
78 image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode);
79 }
80 SkSamplingOptions sampling(filter ? SkFilterMode::kLinear : SkFilterMode::kNearest,
81 SkMipmapMode::kNone);
82 sk_sp<SkShader> shader = image->makeShader(
83 (SkTileMode)tileModeX, (SkTileMode)tileModeY, sampling);
84 ThrowIAE_IfNull(env, shader.get());
85
86 if (matrix) {
87 shader = shader->makeWithLocalMatrix(*matrix);
88 }
89
90 return reinterpret_cast<jlong>(shader.release());
91 }
92
93 ///////////////////////////////////////////////////////////////////////////////////////////////
94
convertColorLongs(JNIEnv * env,jlongArray colorArray)95 static std::vector<SkColor4f> convertColorLongs(JNIEnv* env, jlongArray colorArray) {
96 const size_t count = env->GetArrayLength(colorArray);
97 const jlong* colorValues = env->GetLongArrayElements(colorArray, nullptr);
98
99 std::vector<SkColor4f> colors(count);
100 for (size_t i = 0; i < count; ++i) {
101 colors[i] = GraphicsJNI::convertColorLong(colorValues[i]);
102 }
103
104 env->ReleaseLongArrayElements(colorArray, const_cast<jlong*>(colorValues), JNI_ABORT);
105 return colors;
106 }
107
108 ///////////////////////////////////////////////////////////////////////////////////////////////
109
LinearGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x0,jfloat y0,jfloat x1,jfloat y1,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)110 static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr,
111 jfloat x0, jfloat y0, jfloat x1, jfloat y1, jlongArray colorArray,
112 jfloatArray posArray, jint tileMode, jlong colorSpaceHandle) {
113 SkPoint pts[2];
114 pts[0].set(x0, y0);
115 pts[1].set(x1, y1);
116
117 std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
118
119 AutoJavaFloatArray autoPos(env, posArray, colors.size());
120 #ifdef SK_SCALAR_IS_FLOAT
121 SkScalar* pos = autoPos.ptr();
122 #else
123 #error Need to convert float array to SkScalar array before calling the following function.
124 #endif
125
126 sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0],
127 GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
128 static_cast<SkTileMode>(tileMode), sGradientShaderFlags, nullptr));
129 ThrowIAE_IfNull(env, shader);
130
131 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
132 if (matrix) {
133 shader = shader->makeWithLocalMatrix(*matrix);
134 }
135
136 return reinterpret_cast<jlong>(shader.release());
137 }
138
139 ///////////////////////////////////////////////////////////////////////////////////////////////
140
RadialGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat startX,jfloat startY,jfloat startRadius,jfloat endX,jfloat endY,jfloat endRadius,jlongArray colorArray,jfloatArray posArray,jint tileMode,jlong colorSpaceHandle)141 static jlong RadialGradient_create(JNIEnv* env,
142 jobject,
143 jlong matrixPtr,
144 jfloat startX,
145 jfloat startY,
146 jfloat startRadius,
147 jfloat endX,
148 jfloat endY,
149 jfloat endRadius,
150 jlongArray colorArray,
151 jfloatArray posArray,
152 jint tileMode,
153 jlong colorSpaceHandle) {
154
155 SkPoint start;
156 start.set(startX, startY);
157
158 SkPoint end;
159 end.set(endX, endY);
160
161 std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
162
163 AutoJavaFloatArray autoPos(env, posArray, colors.size());
164 #ifdef SK_SCALAR_IS_FLOAT
165 SkScalar* pos = autoPos.ptr();
166 #else
167 #error Need to convert float array to SkScalar array before calling the following function.
168 #endif
169
170 auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
171 auto skTileMode = static_cast<SkTileMode>(tileMode);
172 sk_sp<SkShader> shader = SkGradientShader::MakeTwoPointConical(start, startRadius, end,
173 endRadius, &colors[0], std::move(colorSpace), pos, colors.size(), skTileMode,
174 sGradientShaderFlags, nullptr);
175 ThrowIAE_IfNull(env, shader);
176
177 // Explicitly create a new shader with the specified matrix to match existing behavior.
178 // Passing in the matrix in the instantiation above can throw exceptions for non-invertible
179 // matrices. However, makeWithLocalMatrix will still allow for the shader to be created
180 // and skia handles null-shaders internally (i.e. is ignored)
181 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
182 if (matrix) {
183 shader = shader->makeWithLocalMatrix(*matrix);
184 }
185
186 return reinterpret_cast<jlong>(shader.release());
187 }
188
189 ///////////////////////////////////////////////////////////////////////////////
190
SweepGradient_create(JNIEnv * env,jobject,jlong matrixPtr,jfloat x,jfloat y,jlongArray colorArray,jfloatArray jpositions,jlong colorSpaceHandle)191 static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat x, jfloat y,
192 jlongArray colorArray, jfloatArray jpositions, jlong colorSpaceHandle) {
193 std::vector<SkColor4f> colors = convertColorLongs(env, colorArray);
194
195 AutoJavaFloatArray autoPos(env, jpositions, colors.size());
196 #ifdef SK_SCALAR_IS_FLOAT
197 SkScalar* pos = autoPos.ptr();
198 #else
199 #error Need to convert float array to SkScalar array before calling the following function.
200 #endif
201
202 sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0],
203 GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(),
204 sGradientShaderFlags, nullptr);
205 ThrowIAE_IfNull(env, shader);
206
207 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
208 if (matrix) {
209 shader = shader->makeWithLocalMatrix(*matrix);
210 }
211
212 return reinterpret_cast<jlong>(shader.release());
213 }
214
215 ///////////////////////////////////////////////////////////////////////////////////////////////
216
ComposeShader_create(JNIEnv * env,jobject o,jlong matrixPtr,jlong shaderAHandle,jlong shaderBHandle,jint xfermodeHandle)217 static jlong ComposeShader_create(JNIEnv* env, jobject o, jlong matrixPtr,
218 jlong shaderAHandle, jlong shaderBHandle, jint xfermodeHandle) {
219 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
220 SkShader* shaderA = reinterpret_cast<SkShader *>(shaderAHandle);
221 SkShader* shaderB = reinterpret_cast<SkShader *>(shaderBHandle);
222 SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
223 sk_sp<SkShader> baseShader(SkShaders::Blend(mode,
224 sk_ref_sp(shaderA), sk_ref_sp(shaderB)));
225
226 SkShader* shader;
227
228 if (matrix) {
229 shader = baseShader->makeWithLocalMatrix(*matrix).release();
230 } else {
231 shader = baseShader.release();
232 }
233 return reinterpret_cast<jlong>(shader);
234 }
235
236 ///////////////////////////////////////////////////////////////////////////////////////////////
237
238 ///////////////////////////////////////////////////////////////////////////////////////////////
239
RuntimeShader_createShaderBuilder(JNIEnv * env,jobject,jstring sksl)240 static jlong RuntimeShader_createShaderBuilder(JNIEnv* env, jobject, jstring sksl) {
241 ScopedUtfChars strSksl(env, sksl);
242 auto result = SkRuntimeEffect::MakeForShader(SkString(strSksl.c_str()),
243 SkRuntimeEffect::Options{});
244 if (result.effect.get() == nullptr) {
245 doThrowIAE(env, result.errorText.c_str());
246 return 0;
247 }
248 return reinterpret_cast<jlong>(new SkRuntimeShaderBuilder(std::move(result.effect)));
249 }
250
SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder * builder)251 static void SkRuntimeShaderBuilder_delete(SkRuntimeShaderBuilder* builder) {
252 delete builder;
253 }
254
RuntimeShader_getNativeFinalizer(JNIEnv *,jobject)255 static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
256 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
257 }
258
RuntimeShader_create(JNIEnv * env,jobject,jlong shaderBuilder,jlong matrixPtr,jboolean isOpaque)259 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
260 jboolean isOpaque) {
261 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
262 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
263 sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE);
264 ThrowIAE_IfNull(env, shader);
265 return reinterpret_cast<jlong>(shader.release());
266 }
267
ThrowIAEFmt(JNIEnv * env,const char * fmt,...)268 static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
269 va_list args;
270 va_start(args, fmt);
271 int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
272 va_end(args);
273 return ret;
274 }
275
RuntimeShader_updateUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloatArray jvalues)276 static void RuntimeShader_updateUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
277 jstring jUniformName, jfloatArray jvalues) {
278 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
279 ScopedUtfChars name(env, jUniformName);
280 AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
281
282 SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name.c_str());
283 if (uniform.fVar == nullptr) {
284 ThrowIAEFmt(env, "unable to find uniform named %s", name.c_str());
285 } else if (!uniform.set<float>(autoValues.ptr(), autoValues.length())) {
286 ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
287 uniform.fVar->sizeInBytes(), sizeof(float) * autoValues.length());
288 }
289 }
290
RuntimeShader_updateShader(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong shaderHandle)291 static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
292 jstring jUniformName, jlong shaderHandle) {
293 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
294 ScopedUtfChars name(env, jUniformName);
295 SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
296
297 SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
298 if (child.fIndex == -1) {
299 ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
300 return;
301 }
302
303 builder->child(name.c_str()) = sk_ref_sp(shader);
304 }
305
306 ///////////////////////////////////////////////////////////////////////////////////////////////
307
308 static const JNINativeMethod gColorMethods[] = {
309 { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
310 { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
311 };
312
313 static const JNINativeMethod gShaderMethods[] = {
314 { "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer },
315 };
316
317 static const JNINativeMethod gBitmapShaderMethods[] = {
318 { "nativeCreate", "(JJIIZ)J", (void*)BitmapShader_constructor },
319 };
320
321 static const JNINativeMethod gLinearGradientMethods[] = {
322 { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
323 };
324
325 static const JNINativeMethod gRadialGradientMethods[] = {
326 { "nativeCreate", "(JFFFFFF[J[FIJ)J", (void*)RadialGradient_create },
327 };
328
329 static const JNINativeMethod gSweepGradientMethods[] = {
330 { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create },
331 };
332
333 static const JNINativeMethod gComposeShaderMethods[] = {
334 { "nativeCreate", "(JJJI)J", (void*)ComposeShader_create },
335 };
336
337 static const JNINativeMethod gRuntimeShaderMethods[] = {
338 {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
339 {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
340 {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
341 {"nativeUpdateUniforms", "(JLjava/lang/String;[F)V", (void*)RuntimeShader_updateUniforms},
342 {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
343 };
344
register_android_graphics_Shader(JNIEnv * env)345 int register_android_graphics_Shader(JNIEnv* env)
346 {
347 android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
348 NELEM(gColorMethods));
349 android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
350 NELEM(gShaderMethods));
351 android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
352 NELEM(gBitmapShaderMethods));
353 android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
354 NELEM(gLinearGradientMethods));
355 android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
356 NELEM(gRadialGradientMethods));
357 android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
358 NELEM(gSweepGradientMethods));
359 android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
360 NELEM(gComposeShaderMethods));
361 android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
362 NELEM(gRuntimeShaderMethods));
363
364 return 0;
365 }
366