| |
| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <jni.h> |
| #include <math.h> |
| #include <string> |
| #include <utility> |
| #include <SkColor.h> |
| #include <SkCanvas.h> |
| #include <SkBitmap.h> |
| #include <SkSurface.h> |
| #include <SkTime.h> |
| |
| #include <GrContextOptions.h> |
| #include <GrContext.h> |
| #include <gl/GrGLInterface.h> |
| #include <GrBackendSurface.h> |
| #include <gl/GrGLTypes.h> |
| |
| #include <Skottie.h> |
| |
| #include <GLES2/gl2.h> |
| #include <GLES2/gl2ext.h> |
| |
| #include <GLES3/gl3.h> |
| #include <android/trace.h> |
| #include "JavaInputStreamAdaptor.h" |
| |
| #define STENCIL_BUFFER_SIZE 8 |
| |
| /*#define ATRACE_NAME(name) ScopedTrace ___tracer(name) |
| |
| // ATRACE_CALL is an ATRACE_NAME that uses the current function name. |
| #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) |
| namespace { |
| class ScopedTrace { |
| public: |
| inline ScopedTrace(const char *name) { |
| ATrace_beginSection(name); |
| } |
| |
| inline ~ScopedTrace() { |
| ATrace_endSection(); |
| } |
| }; |
| |
| }*/ |
| |
| //disable atrace |
| #define ATRACE_NAME(name) |
| #define ATRACE_CALL() |
| |
| struct SkottieRunner { |
| sk_sp<GrContext> mGrContext; |
| }; |
| |
| extern "C" JNIEXPORT jlong |
| JNICALL |
| Java_org_skia_skottie_SkottieRunner_nCreateProxy(JNIEnv *env, jclass clazz) { |
| sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); |
| if (!glInterface.get()) { |
| return 0; |
| } |
| |
| GrContextOptions options; |
| options.fDisableDistanceFieldPaths = true; |
| options.fDisableCoverageCountingPaths = true; |
| sk_sp<GrContext> grContext = GrContext::MakeGL(std::move(glInterface), options); |
| if (!grContext.get()) { |
| return 0; |
| } |
| |
| SkottieRunner* skottie = new SkottieRunner(); |
| skottie->mGrContext = grContext; |
| |
| return (jlong) skottie; |
| } |
| |
| extern "C" JNIEXPORT void |
| JNICALL |
| Java_org_skia_skottie_SkottieRunner_nDeleteProxy(JNIEnv *env, jclass clazz, jlong nativeProxy) { |
| if (!nativeProxy) { |
| return; |
| } |
| SkottieRunner* skottie = reinterpret_cast<SkottieRunner*>(nativeProxy); |
| if (skottie->mGrContext) { |
| skottie->mGrContext->releaseResourcesAndAbandonContext(); |
| skottie->mGrContext.reset(); |
| } |
| delete skottie; |
| } |
| |
| struct SkottieAnimation { |
| SkottieRunner *mRunner; |
| std::unique_ptr<SkStream> mStream; |
| sk_sp<skottie::Animation> mAnimation; |
| long mTimeBase; |
| float mDuration; //in milliseconds |
| }; |
| |
| extern "C" JNIEXPORT jlong |
| JNICALL |
| Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nCreateProxy(JNIEnv *env, jobject clazz, |
| jlong runner, jobject is, |
| jbyteArray storage) { |
| |
| if (!runner) { |
| return 0; |
| } |
| SkottieRunner *skottieRunner = reinterpret_cast<SkottieRunner*>(runner); |
| std::unique_ptr<SkStream> stream(CopyJavaInputStream(env, is, storage)); |
| if (!stream.get()) { |
| // Cannot create a stream |
| return 0; |
| } |
| |
| SkottieAnimation* skottieAnimation = new SkottieAnimation(); |
| skottieAnimation->mRunner = skottieRunner; |
| skottieAnimation->mStream = std::move(stream); |
| |
| skottieAnimation->mAnimation = skottie::Animation::Make(skottieAnimation->mStream.get()); |
| skottieAnimation->mTimeBase = 0.0f; // force a time reset |
| skottieAnimation->mDuration = 1000 * skottieAnimation->mAnimation->duration(); |
| |
| if (!skottieAnimation->mAnimation) { |
| //failed to load Bodymovin animation |
| delete skottieAnimation; |
| return 0; |
| } |
| |
| return (jlong) skottieAnimation; |
| } |
| |
| extern "C" JNIEXPORT void |
| JNICALL |
| Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDeleteProxy(JNIEnv *env, jclass clazz, |
| jlong nativeProxy) { |
| if (!nativeProxy) { |
| return; |
| } |
| SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy); |
| delete skottieAnimation; |
| } |
| |
| extern "C" JNIEXPORT void |
| JNICALL |
| Java_org_skia_skottie_SkottieRunner_00024SkottieAnimation_nDrawFrame(JNIEnv *env, jclass clazz, |
| jlong nativeProxy, jint width, |
| jint height, |
| jboolean wideColorGamut, |
| jlong frameTimeNanos) { |
| ATRACE_NAME("SkottieDrawFrame"); |
| if (!nativeProxy) { |
| return; |
| } |
| SkottieAnimation* skottieAnimation = reinterpret_cast<SkottieAnimation*>(nativeProxy); |
| |
| auto grContext = skottieAnimation->mRunner->mGrContext; |
| |
| if (!grContext) { |
| return; |
| } |
| |
| SkColorType colorType; |
| // setup surface for fbo0 |
| GrGLFramebufferInfo fboInfo; |
| fboInfo.fFBOID = 0; |
| if (wideColorGamut) { |
| fboInfo.fFormat = GL_RGBA16F; |
| colorType = kRGBA_F16_SkColorType; |
| } else { |
| fboInfo.fFormat = GL_RGBA8; |
| colorType = kN32_SkColorType; |
| } |
| GrBackendRenderTarget backendRT(width, height, 0, STENCIL_BUFFER_SIZE, fboInfo); |
| |
| SkSurfaceProps props(0, kUnknown_SkPixelGeometry); |
| |
| sk_sp<SkSurface> renderTarget(SkSurface::MakeFromBackendRenderTarget( |
| grContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, colorType, |
| nullptr, &props)); |
| |
| auto canvas = renderTarget->getCanvas(); |
| canvas->clear(SK_ColorTRANSPARENT); |
| if (skottieAnimation->mAnimation) { |
| SkMSec t = 0; |
| if (skottieAnimation->mTimeBase == 0.0f) { |
| // Reset the animation time. |
| skottieAnimation->mTimeBase = frameTimeNanos; |
| } else { |
| //convert from nanoseconds to milliseconds |
| t = (frameTimeNanos - skottieAnimation->mTimeBase) / 1000000; |
| } |
| //TODO: control repeat count |
| float intpart; |
| float animState = modff(t / skottieAnimation->mDuration, &intpart); |
| skottieAnimation->mAnimation->seek(animState); |
| |
| SkAutoCanvasRestore acr(canvas, true); |
| SkRect bounds = SkRect::MakeWH(width, height); |
| skottieAnimation->mAnimation->render(canvas, &bounds); |
| } |
| |
| canvas->flush(); |
| } |