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