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,jlong colorSpacePtr)269 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
270 jlong colorSpacePtr) {
271 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
272 const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
273 auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpacePtr);
274 sk_sp<SkShader> shader = builder->makeShader(matrix);
275 ThrowIAE_IfNull(env, shader);
276 if (colorSpace) {
277 shader = shader->makeWithWorkingColorSpace(colorSpace);
278 ThrowIAE_IfNull(env, shader);
279 }
280 return reinterpret_cast<jlong>(shader.release());
281 }
282
ThrowIAEFmt(JNIEnv * env,const char * fmt,...)283 static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
284 va_list args;
285 va_start(args, fmt);
286 int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
287 va_end(args);
288 return ret;
289 }
290
RuntimeShader_updateFloatUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloat value1,jfloat value2,jfloat value3,jfloat value4,jint count)291 static void RuntimeShader_updateFloatUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
292 jstring jUniformName, jfloat value1, jfloat value2,
293 jfloat value3, jfloat value4, jint count) {
294 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
295 ScopedUtfChars name(env, jUniformName);
296 const float values[4] = {value1, value2, value3, value4};
297 UpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
298 }
299
RuntimeShader_updateFloatArrayUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jfloatArray jvalues,jboolean isColor)300 static void RuntimeShader_updateFloatArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
301 jstring jUniformName, jfloatArray jvalues,
302 jboolean isColor) {
303 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
304 ScopedUtfChars name(env, jUniformName);
305 AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
306 UpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), isColor);
307 }
308
RuntimeShader_updateIntUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jint value1,jint value2,jint value3,jint value4,jint count)309 static void RuntimeShader_updateIntUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
310 jstring jUniformName, jint value1, jint value2,
311 jint value3, jint value4, jint count) {
312 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
313 ScopedUtfChars name(env, jUniformName);
314 const int values[4] = {value1, value2, value3, value4};
315 UpdateIntUniforms(env, builder, name.c_str(), values, count);
316 }
317
RuntimeShader_updateIntArrayUniforms(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jintArray jvalues)318 static void RuntimeShader_updateIntArrayUniforms(JNIEnv* env, jobject, jlong shaderBuilder,
319 jstring jUniformName, jintArray jvalues) {
320 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
321 ScopedUtfChars name(env, jUniformName);
322 AutoJavaIntArray autoValues(env, jvalues, 0);
323 UpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
324 }
325
RuntimeShader_updateShader(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong shaderHandle)326 static void RuntimeShader_updateShader(JNIEnv* env, jobject, jlong shaderBuilder,
327 jstring jUniformName, jlong shaderHandle) {
328 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
329 ScopedUtfChars name(env, jUniformName);
330 SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
331
332 SkRuntimeShaderBuilder::BuilderChild child = builder->child(name.c_str());
333 if (child.fChild == nullptr) {
334 ThrowIAEFmt(env, "unable to find shader named %s", name.c_str());
335 return;
336 }
337
338 builder->child(name.c_str()) = sk_ref_sp(shader);
339 }
340
RuntimeShader_updateColorFilter(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong colorFilterHandle)341 static void RuntimeShader_updateColorFilter(JNIEnv* env, jobject, jlong shaderBuilder,
342 jstring jUniformName, jlong colorFilterHandle) {
343 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
344 ScopedUtfChars name(env, jUniformName);
345 auto* childEffect = reinterpret_cast<ColorFilter*>(colorFilterHandle);
346
347 UpdateChild(env, builder, name.c_str(), childEffect->getInstance().release());
348 }
349
RuntimeShader_updateChild(JNIEnv * env,jobject,jlong shaderBuilder,jstring jUniformName,jlong childHandle)350 static void RuntimeShader_updateChild(JNIEnv* env, jobject, jlong shaderBuilder,
351 jstring jUniformName, jlong childHandle) {
352 SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
353 ScopedUtfChars name(env, jUniformName);
354 auto* childEffect = reinterpret_cast<SkFlattenable*>(childHandle);
355
356 UpdateChild(env, builder, name.c_str(), childEffect);
357 }
358
RuntimeShader_no(JNIEnv * env)359 static void RuntimeShader_no(JNIEnv* env) {
360 jniThrowRuntimeException(env, "Not supported");
361 }
362
363 ///////////////////////////////////////////////////////////////////////////////////////////////
364
365 static const JNINativeMethod gShaderMethods[] = {
nativeGetFinalizer()366 { "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer },
367 };
368
369 static const JNINativeMethod gBitmapShaderMethods[] = {
nativeCreate(JJIIIZZJ)370 {"nativeCreate", "(JJIIIZZJ)J", (void*)BitmapShader_constructor},
371
372 };
373
374 static const JNINativeMethod gLinearGradientMethods[] = {
nativeCreate(JFFFF[J[FIJ)375 { "nativeCreate", "(JFFFF[J[FIJ)J", (void*)LinearGradient_create },
376 };
377
378 static const JNINativeMethod gRadialGradientMethods[] = {
nativeCreate(JFFFFFF[J[FIJ)379 { "nativeCreate", "(JFFFFFF[J[FIJ)J", (void*)RadialGradient_create },
380 };
381
382 static const JNINativeMethod gSweepGradientMethods[] = {
nativeCreate(JFF[J[FJ)383 { "nativeCreate", "(JFF[J[FJ)J", (void*)SweepGradient_create },
384 };
385
386 static const JNINativeMethod gComposeShaderMethods[] = {
nativeCreate(JJJI)387 { "nativeCreate", "(JJJI)J", (void*)ComposeShader_create },
388 };
389
390 static const JNINativeMethod gRuntimeShaderMethods[] = {
nativeGetFinalizer()391 {"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
nativeCreateShader(JJ)392 {"nativeCreateShader", "(JJ)J", (void*)RuntimeShader_no},
nativeCreateShader(JJJ)393 {"nativeCreateShader", "(JJJ)J", (void*)RuntimeShader_create},
nativeCreateBuilder(Ljava/lang/String;)394 {"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
nativeUpdateUniforms(JLjava/lang/String;[FZ)395 {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
396 (void*)RuntimeShader_updateFloatArrayUniforms},
nativeUpdateUniforms(JLjava/lang/String;FFFFI)397 {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V",
398 (void*)RuntimeShader_updateFloatUniforms},
nativeUpdateUniforms(JLjava/lang/String;[I)399 {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V",
400 (void*)RuntimeShader_updateIntArrayUniforms},
nativeUpdateUniforms(JLjava/lang/String;IIIII)401 {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V",
402 (void*)RuntimeShader_updateIntUniforms},
nativeUpdateShader(JLjava/lang/String;J)403 {"nativeUpdateShader", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateShader},
nativeUpdateColorFilter(JLjava/lang/String;J)404 {"nativeUpdateColorFilter", "(JLjava/lang/String;J)V",
405 (void*)RuntimeShader_updateColorFilter},
nativeUpdateChild(JLjava/lang/String;J)406 {"nativeUpdateChild", "(JLjava/lang/String;J)V", (void*)RuntimeShader_updateChild},
407 };
408
register_android_graphics_Shader(JNIEnv * env)409 int register_android_graphics_Shader(JNIEnv* env)
410 {
411 android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
412 NELEM(gShaderMethods));
413 android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
414 NELEM(gBitmapShaderMethods));
415 android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
416 NELEM(gLinearGradientMethods));
417 android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
418 NELEM(gRadialGradientMethods));
419 android::RegisterMethodsOrDie(env, "android/graphics/SweepGradient", gSweepGradientMethods,
420 NELEM(gSweepGradientMethods));
421 android::RegisterMethodsOrDie(env, "android/graphics/ComposeShader", gComposeShaderMethods,
422 NELEM(gComposeShaderMethods));
423 android::RegisterMethodsOrDie(env, "android/graphics/RuntimeShader", gRuntimeShaderMethods,
424 NELEM(gRuntimeShaderMethods));
425
426 return 0;
427 }
428