xref: /aosp_15_r20/frameworks/base/libs/hwui/jni/Shader.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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