xref: /aosp_15_r20/external/skia/platform_tools/android/apps/skottie/skottielib/src/main/cpp/native-lib.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 
2 /*
3  * Copyright 2018 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "include/codec/SkCodec.h"
10 #include "include/codec/SkGifDecoder.h"
11 #include "include/codec/SkJpegDecoder.h"
12 #include "include/codec/SkPngDecoder.h"
13 #include "include/core/SkBitmap.h"
14 #include "include/core/SkCanvas.h"
15 #include "include/core/SkColor.h"
16 #include "include/core/SkColorSpace.h"
17 #include "include/core/SkFontMgr.h"
18 #include "include/core/SkStream.h"
19 #include "include/core/SkSurface.h"
20 #include "include/ports/SkFontMgr_empty.h"
21 
22 #include "include/gpu/ganesh/GrBackendSurface.h"
23 #include "include/gpu/ganesh/GrContextOptions.h"
24 #include "include/gpu/ganesh/GrDirectContext.h"
25 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
26 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
27 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
28 #include "include/gpu/ganesh/gl/GrGLInterface.h"
29 #include "include/gpu/ganesh/gl/GrGLTypes.h"
30 #include "include/gpu/ganesh/gl/egl/GrGLMakeEGLInterface.h"
31 
32 #include "modules/skottie/include/Skottie.h"
33 #include "modules/skresources/include/SkResources.h"
34 #include "modules/sksg/include/SkSGInvalidationController.h"
35 #include "modules/skshaper/utils/FactoryHelpers.h"
36 
37 #include <jni.h>
38 #include <math.h>
39 #include <string>
40 #include <utility>
41 
42 #include <GLES2/gl2.h>
43 #include <GLES2/gl2ext.h>
44 
45 #include <GLES3/gl3.h>
46 
47 #define STENCIL_BUFFER_SIZE 8
48 
49 /*#define ATRACE_NAME(name) ScopedTrace ___tracer(name)
50 
51 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
52 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
53 namespace {
54     class ScopedTrace {
55     public:
56         inline ScopedTrace(const char *name) {
57             ATrace_beginSection(name);
58         }
59 
60         inline ~ScopedTrace() {
61             ATrace_endSection();
62         }
63     };
64 
65 }*/
66 
67 //disable atrace
68 #define ATRACE_NAME(name)
69 #define ATRACE_CALL()
70 
71 struct SkottieRunner {
72     sk_sp<GrDirectContext> mDContext;
73 };
74 
75 static JavaVM* sJVM = nullptr;
76 
release_global_jni_ref(const void *,void * context)77 static void release_global_jni_ref(const void* /*data*/, void* context) {
78     JNIEnv* env;
79     if (sJVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
80         SK_ABORT("Attempting to release a JNI ref on a thread without a JVM attached.");
81     }
82     jobject obj = reinterpret_cast<jobject>(context);
83     env->DeleteGlobalRef(obj);
84 }
85 
86 extern "C" JNIEXPORT jlong
87 JNICALL
Java_org_skia_skottie_SkottieRunner_nCreateProxy(JNIEnv * env,jclass clazz)88 Java_org_skia_skottie_SkottieRunner_nCreateProxy(JNIEnv *env, jclass clazz) {
89     sk_sp<const GrGLInterface> glInterface = GrGLInterfaces::MakeEGL();
90     if (!glInterface) {
91         return 0;
92     }
93 
94     GrContextOptions options;
95     options.fDisableDistanceFieldPaths = true;
96     sk_sp<GrDirectContext> dContext = GrDirectContexts::MakeGL(std::move(glInterface), options);
97     if (!dContext) {
98         return 0;
99     }
100 
101     SkottieRunner* skottie = new SkottieRunner();
102     skottie->mDContext = std::move(dContext);
103 
104     return (jlong) skottie;
105 }
106 
107 extern "C" JNIEXPORT void
108 JNICALL
Java_org_skia_skottie_SkottieRunner_nDeleteProxy(JNIEnv * env,jclass clazz,jlong nativeProxy)109 Java_org_skia_skottie_SkottieRunner_nDeleteProxy(JNIEnv *env, jclass clazz, jlong nativeProxy) {
110     if (!nativeProxy) {
111         return;
112     }
113     SkottieRunner* skottie = reinterpret_cast<SkottieRunner*>(nativeProxy);
114     if (skottie->mDContext) {
115         skottie->mDContext->releaseResourcesAndAbandonContext();
116         skottie->mDContext.reset();
117     }
118     delete skottie;
119 }
120 
121 
122 extern "C" JNIEXPORT void
123 JNICALL
Java_org_skia_skottie_SkottieRunner_nSetMaxCacheSize(JNIEnv * env,jclass clazz,jint maxCacheSize,jlong nativeProxy)124 Java_org_skia_skottie_SkottieRunner_nSetMaxCacheSize(JNIEnv *env, jclass clazz, jint maxCacheSize,
125                                                                                 jlong nativeProxy) {
126     if (!nativeProxy) {
127             return;
128     }
129     SkottieRunner* skottie = reinterpret_cast<SkottieRunner*>(nativeProxy);
130     if (skottie->mDContext) {
131         skottie->mDContext->setResourceCacheLimit(maxCacheSize);
132     }
133 }
134 
135 struct SkottieAnimation {
136     SkottieRunner *mRunner;
137     std::unique_ptr<SkStream> mStream;
138     sk_sp<skottie::Animation> mAnimation;
139     long                      mTimeBase;
140     float                     mDuration; //in milliseconds
141 };
142 
143 extern "C" JNIEXPORT jlong
144 JNICALL
Java_org_skia_skottie_SkottieAnimation_nCreateProxy(JNIEnv * env,jobject clazz,jlong runner,jobject bufferObj)145 Java_org_skia_skottie_SkottieAnimation_nCreateProxy(JNIEnv *env,
146                                                     jobject clazz,
147                                                     jlong runner,
148                                                     jobject bufferObj) {
149 
150     if (!runner) {
151         return 0;
152     }
153     SkottieRunner *skottieRunner = reinterpret_cast<SkottieRunner*>(runner);
154 
155     const void* buffer = env->GetDirectBufferAddress(bufferObj);
156     jlong bufferSize = env->GetDirectBufferCapacity(bufferObj);
157     if (buffer == nullptr || bufferSize <= 0) {
158         return 0;
159     }
160 
161     env->GetJavaVM(&sJVM);
162     jobject bufferRef = env->NewGlobalRef(bufferObj);
163     if (bufferRef == nullptr) {
164         return 0;
165     }
166 
167     sk_sp<SkData> data(SkData::MakeWithProc(buffer, bufferSize, release_global_jni_ref,
168                                             reinterpret_cast<void*>(bufferRef)));
169     std::unique_ptr<SkStream> stream = SkMemoryStream::Make(data);
170     if (!stream) {
171         // Cannot create a stream
172         return 0;
173     }
174 
175     SkottieAnimation* skottieAnimation = new SkottieAnimation();
176     skottieAnimation->mRunner = skottieRunner;
177     skottieAnimation->mStream = std::move(stream);
178 
179     sk_sp<SkFontMgr> freetypeMgr = SkFontMgr_New_Custom_Empty();
180 
181     SkCodecs::Register(SkPngDecoder::Decoder());
182     SkCodecs::Register(SkGifDecoder::Decoder());
183     SkCodecs::Register(SkJpegDecoder::Decoder());
184 
185     skottieAnimation->mAnimation = skottie::Animation::Builder()
186         // Note, this nullptr ResourceProvider will only be able to decode base64 encoded images
187         // (using the above registered codecs) or base64 encoded FreeType typefaces.
188         .setResourceProvider(skresources::DataURIResourceProviderProxy::Make(nullptr,
189             skresources::ImageDecodeStrategy::kPreDecode, freetypeMgr))
190         .setTextShapingFactory(sk_make_sp<SkShapers::HarfbuzzFactory>())
191         .make(skottieAnimation->mStream.get());
192     skottieAnimation->mTimeBase  = 0.0f; // force a time reset
193     skottieAnimation->mDuration = 1000 * skottieAnimation->mAnimation->duration();
194 
195     if (!skottieAnimation->mAnimation) {
196         //failed to load Bodymovin animation
197         delete skottieAnimation;
198         return 0;
199     }
200 
201     return (jlong) skottieAnimation;
202 }
203 
204 extern "C" JNIEXPORT void
205 JNICALL
Java_org_skia_skottie_SkottieAnimation_nDeleteProxy(JNIEnv * env,jclass clazz,jlong nativeProxy)206 Java_org_skia_skottie_SkottieAnimation_nDeleteProxy(JNIEnv *env, jclass clazz,
207                                                     jlong nativeProxy) {
208     if (!nativeProxy) {
209         return;
210     }
211     SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy);
212     delete skottieAnimation;
213 }
214 
215 extern "C" JNIEXPORT bool
216 JNICALL
Java_org_skia_skottie_SkottieAnimation_nDrawFrame(JNIEnv * env,jclass clazz,jlong nativeProxy,jint width,jint height,jboolean wideColorGamut,jfloat progress,jint backgroundColor,jboolean forceDraw)217 Java_org_skia_skottie_SkottieAnimation_nDrawFrame(JNIEnv *env, jclass clazz,
218                                                   jlong nativeProxy, jint width,
219                                                   jint height,
220                                                   jboolean wideColorGamut,
221                                                   jfloat progress,
222                                                   jint backgroundColor,
223                                                   jboolean forceDraw) {
224     ATRACE_NAME("SkottieDrawFrame");
225     if (!nativeProxy) {
226         return false;
227     }
228     SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy);
229 
230     auto dContext = skottieAnimation->mRunner->mDContext.get();
231 
232     if (!dContext) {
233         return false;
234     }
235 
236     sksg::InvalidationController ic;
237 
238     if (skottieAnimation->mAnimation) {
239         skottieAnimation->mAnimation->seek(progress, &ic);
240         if (!forceDraw && ic.bounds().isEmpty()) {
241             return false;
242         }
243     }
244 
245     SkColorType colorType;
246     // setup surface for fbo0
247     GrGLFramebufferInfo fboInfo;
248     fboInfo.fFBOID = 0;
249     if (wideColorGamut) {
250         fboInfo.fFormat = GL_RGBA16F;
251         colorType = kRGBA_F16_SkColorType;
252     } else {
253         fboInfo.fFormat = GL_RGBA8;
254         colorType = kN32_SkColorType;
255     }
256     fboInfo.fProtected = skgpu::Protected::kNo;
257     auto backendRT = GrBackendRenderTargets::MakeGL(width, height, 0, STENCIL_BUFFER_SIZE, fboInfo);
258 
259     SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
260 
261     sk_sp<SkSurface> renderTarget(SkSurfaces::WrapBackendRenderTarget(
262             dContext, backendRT, kBottomLeft_GrSurfaceOrigin, colorType, nullptr, &props));
263 
264     auto canvas = renderTarget->getCanvas();
265     canvas->clear(backgroundColor);
266 
267     SkAutoCanvasRestore acr(canvas, true);
268     SkRect bounds = SkRect::MakeWH(width, height);
269     skottieAnimation->mAnimation->render(canvas, &bounds);
270     dContext->flushAndSubmit();
271     return true;
272 }
273 
274 extern "C" JNIEXPORT jlong
275 JNICALL
Java_org_skia_skottie_SkottieAnimation_nGetDuration(JNIEnv * env,jclass clazz,jlong nativeProxy)276 Java_org_skia_skottie_SkottieAnimation_nGetDuration(JNIEnv *env,
277                                                     jclass clazz,
278                                                     jlong nativeProxy) {
279     if (!nativeProxy) {
280         return 0;
281     }
282     SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy);
283     return (jlong) skottieAnimation->mDuration;
284 }
285