From 134be1d9ba75dd29fa74d3f3b497c871ed822adc Mon Sep 17 00:00:00 2001 From: Kevin Lubick Date: Tue, 30 Oct 2018 15:05:04 -0400 Subject: [PATCH] [canvaskit] Expose nima animation as POC Still to come, actually exposing drawVertices, and the other APIs needed. Had to re-make all the jsfiddles because of an API change in a previous CL. Docs-Preview: https://skia.org/?cl=166444 Bug: skia: Change-Id: I4d4825f6e7b073d6792ab8d99d5117df860d4815 Reviewed-on: https://skia-review.googlesource.com/c/166444 Reviewed-by: Brian Osman Commit-Queue: Kevin Lubick --- BUILD.gn | 5 + experimental/canvaskit/canvaskit/example.html | 74 ++++++++++++++- experimental/canvaskit/canvaskit/package.json | 2 +- experimental/canvaskit/canvaskit_bindings.cpp | 37 +++++++- experimental/canvaskit/compile.sh | 20 +++- experimental/canvaskit/externs.js | 20 +++- experimental/canvaskit/interface.js | 9 ++ site/user/modules/canvaskit.md | 95 ++++++++++++++++--- third_party/Nima-Cpp/BUILD.gn | 2 - 9 files changed, 239 insertions(+), 25 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index d34286ca9c..3bbc95eb2f 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -47,6 +47,7 @@ declare_args() { skia_enable_effect_deserialization = !skia_enable_flutter_defines skia_enable_fontmgr_empty = false skia_enable_gpu = true + skia_enable_nima = false skia_enable_pdf = true skia_enable_spirv_validation = is_skia_dev_build && is_debug skia_enable_skpicture = true @@ -891,6 +892,10 @@ component("skia") { ":xml", ] + if (skia_enable_nima) { + deps += [ "//third_party/Nima-Cpp" ] + } + # This file (and all GN files in Skia) are designed to work with an # empty sources assignment filter; we handle all that explicitly. # We clear the filter here for clients who may have set up a global filter. diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html index ec2a18c921..8df01c7be5 100644 --- a/experimental/canvaskit/canvaskit/example.html +++ b/experimental/canvaskit/canvaskit/example.html @@ -30,6 +30,10 @@ +

Nima

+ + +

Drop in replacement for HTML Canvas (e.g. node.js)

@@ -43,6 +47,9 @@ var confettiJSON = null; var onboardingJSON = null; var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; + + var nimaFile = null; + var nimaTexture = null; CanvasKitInit({ locateFile: (file) => '/node_modules/canvaskit/bin/'+file, }).then((CK) => { @@ -58,6 +65,7 @@ SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds); SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds); SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds); + NimaExample(CanvasKit, nimaFile, nimaTexture); CanvasAPI1(CanvasKit); }); @@ -91,6 +99,28 @@ }); }); + fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima').then((resp) => { + resp.blob().then((blob) => { + let reader = new FileReader(); + reader.addEventListener("loadend", function() { + nimaFile = reader.result; + NimaExample(CanvasKit, nimaFile, nimaTexture); + }); + reader.readAsArrayBuffer(blob); + }); + }); + + fetch('https://storage.googleapis.com/skia-cdn/misc/robot.nima.png').then((resp) => { + resp.blob().then((blob) => { + let reader = new FileReader(); + reader.addEventListener("loadend", function() { + nimaTexture = reader.result; + NimaExample(CanvasKit, nimaFile, nimaTexture); + }); + reader.readAsArrayBuffer(blob); + }); + }); + function addScreenshotListener(surface) { if (!surface) { return; @@ -345,11 +375,10 @@ const context = CanvasKit.currentContext(); const canvas = surface.getCanvas(); - let firstFrame = new Date().getTime(); + let firstFrame = Date.now(); function drawFrame() { - let now = new Date().getTime(); - let seek = ((now - firstFrame) / duration) % 1.0; + let seek = ((Date.now() - firstFrame) / duration) % 1.0; CanvasKit.setCurrentContext(context); animation.seek(seek); canvas.clear(CanvasKit.Color(255, 255, 255, 1.0)); @@ -364,6 +393,9 @@ } function CanvasAPI1(CanvasKit) { + if (CanvasKit.gpu) { + return; + } let canvas = CanvasKit.MakeCanvas(300, 300); let ctx = canvas.getContext('2d'); @@ -384,4 +416,40 @@ document.getElementById('api1').src = canvas.toDataURL(); } + function NimaExample(CanvasKit, nimaFile, nimaTexture) { + if (!CanvasKit || !nimaFile || !nimaTexture) { + return; + } + const animation = CanvasKit.MakeNimaActor(nimaFile, nimaTexture); + if (!animation) { + console.error('could not make animation'); + return; + } + + const surface = CanvasKit.MakeCanvasSurface('nima_example'); + if (!surface) { + console.error('Could not make surface'); + return; + } + + const context = CanvasKit.currentContext(); + const canvas = surface.getCanvas(); + canvas.translate(125, 275); + canvas.scale(0.4, -0.4); + + let firstFrame = Date.now(); + animation.setAnimationByName('attack'); + + function drawFrame() { + let seek = ((Date.now() - firstFrame) / 1000.0); + CanvasKit.setCurrentContext(context); + canvas.clear(CanvasKit.Color(255, 255, 255, 0.0)); + animation.seek(seek); + animation.render(canvas); + surface.flush(); + window.requestAnimationFrame(drawFrame); + } + window.requestAnimationFrame(drawFrame); + } + diff --git a/experimental/canvaskit/canvaskit/package.json b/experimental/canvaskit/canvaskit/package.json index 76dd8e9922..affb6b8cc3 100644 --- a/experimental/canvaskit/canvaskit/package.json +++ b/experimental/canvaskit/canvaskit/package.json @@ -1,6 +1,6 @@ { "name": "canvaskit-wasm", - "version": "0.1.0", + "version": "0.1.1", "description": "A WASM version of Skia's Canvas API", "main": "bin/canvaskit.js", "homepage": "https://github.com/google/skia/tree/master/experimental/canvaskit", diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp index 1d91667864..129ee50c8d 100644 --- a/experimental/canvaskit/canvaskit_bindings.cpp +++ b/experimental/canvaskit/canvaskit_bindings.cpp @@ -28,6 +28,9 @@ #if SK_INCLUDE_SKOTTIE #include "Skottie.h" #endif +#if SK_INCLUDE_NIMA +#include "nima/NimaActor.h" +#endif #include #include @@ -41,9 +44,10 @@ using namespace emscripten; +// Self-documenting types +using JSArray = emscripten::val; using JSColor = int32_t; - void EMSCRIPTEN_KEEPALIVE initFonts() { gSkFontMgr_DefaultFactory = &sk_tool_utils::MakePortableFontMgr; } @@ -376,4 +380,35 @@ EMSCRIPTEN_BINDINGS(Skia) { function("MakeAnimation", &MakeAnimation); constant("skottie", true); #endif + +#if SK_INCLUDE_NIMA + class_("NimaActor") + .function("duration", &NimaActor::duration) + .function("getAnimationNames", optional_override([](NimaActor& self)->JSArray { + JSArray names = emscripten::val::array(); + auto vNames = self.getAnimationNames(); + for (size_t i = 0; i < vNames.size(); i++) { + names.call("push", vNames[i]); + } + return names; + }), allow_raw_pointers()) + .function("render", optional_override([](NimaActor& self, SkCanvas* canvas)->void { + self.render(canvas, 0); + }), allow_raw_pointers()) + .function("seek", &NimaActor::seek) + .function("setAnimationByIndex", select_overload(&NimaActor::setAnimation)) + .function("setAnimationByName" , select_overload(&NimaActor::setAnimation)); + + function("_MakeNimaActor", optional_override([](uintptr_t /* uint8_t* */ nptr, int nlen, + uintptr_t /* uint8_t* */ tptr, int tlen)->NimaActor* { + // See comment above for uintptr_t explanation + const uint8_t* nimaBytes = reinterpret_cast(nptr); + const uint8_t* textureBytes = reinterpret_cast(tptr); + + auto nima = SkData::MakeWithoutCopy(nimaBytes, nlen); + auto texture = SkData::MakeWithoutCopy(textureBytes, tlen); + return new NimaActor(nima, texture); + }), allow_raw_pointers()); + constant("nima", true); +#endif } diff --git a/experimental/canvaskit/compile.sh b/experimental/canvaskit/compile.sh index e264617cd4..026ca326fa 100755 --- a/experimental/canvaskit/compile.sh +++ b/experimental/canvaskit/compile.sh @@ -25,7 +25,7 @@ EXTRA_CFLAGS="\"-DSK_RELEASE\"" if [[ $@ == *debug* ]]; then echo "Building a Debug build" EXTRA_CFLAGS="\"-DSK_DEBUG\"" - RELEASE_CONF="-O0 --js-opts 0 -s DEMANGLE_SUPPORT=1 -s SAFE_HEAP=1 -s ASSERTIONS=1 -s GL_ASSERTIONS=1 -g3 -DPATHKIT_TESTING -DSK_DEBUG" + RELEASE_CONF="-O0 --js-opts 0 -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=1 -s GL_ASSERTIONS=1 -g3 -DPATHKIT_TESTING -DSK_DEBUG" BUILD_DIR=${BUILD_DIR:="out/canvaskit_wasm_debug"} else BUILD_DIR=${BUILD_DIR:="out/canvaskit_wasm"} @@ -67,6 +67,15 @@ if [[ $@ == *no_skottie* ]]; then WASM_SKOTTIE="-DSK_INCLUDE_SKOTTIE=0" fi +GN_NIMA="skia_enable_nima=true" +WASM_NIMA="-DSK_INCLUDE_NIMA=1 \ + experimental/nima/NimaActor.cpp" +if [[ $@ == *no_nima* ]]; then + echo "Omitting Nima" + GN_NIMA="skia_enable_nima=false" + WASM_NIMA="-DSK_INCLUDE_NIMA=0" +fi + HTML_CANVAS_API="--pre-js $BASE_DIR/htmlcanvas/canvas2d.js" if [[ $@ == *no_canvas* ]]; then echo "Omitting bindings for HTML Canvas API" @@ -119,6 +128,7 @@ echo "Compiling bitcode" skia_enable_ccpr=false \ skia_enable_nvpr=false \ skia_enable_skpicture=false \ + ${GN_NIMA} \ skia_enable_effect_deserialization = false \ ${GN_GPU} \ skia_enable_fontmgr_empty=false \ @@ -137,6 +147,7 @@ echo "Generating final wasm" # Emscripten will use LLD, which may relax this requirement. ${EMCXX} \ $RELEASE_CONF \ + -Iexperimental \ -Iinclude/c \ -Iinclude/codec \ -Iinclude/config \ @@ -150,10 +161,12 @@ ${EMCXX} \ -Imodules/skottie/include \ -Imodules/sksg/include \ -Isrc/core/ \ - -Isrc/utils/ \ -Isrc/sfnt/ \ - -Itools/fonts \ + -Isrc/utils/ \ -Itools \ + -Itools/fonts \ + -I$BUILD_DIR/gen/third_party/Nima-Cpp/Nima-Cpp \ + -I$BUILD_DIR/gen/third_party/Nima-Cpp/Nima-Math-Cpp \ -DSK_DISABLE_READBUFFER \ -DSK_DISABLE_AAA \ -DSK_DISABLE_DAA \ @@ -166,6 +179,7 @@ ${EMCXX} \ $BASE_DIR/canvaskit_bindings.cpp \ tools/fonts/SkTestFontMgr.cpp \ tools/fonts/SkTestTypeface.cpp \ + $WASM_NIMA \ $WASM_SKOTTIE \ $BUILD_DIR/libskia.a \ -s ALLOW_MEMORY_GROWTH=1 \ diff --git a/experimental/canvaskit/externs.js b/experimental/canvaskit/externs.js index 4377561a1d..c11b4caac9 100644 --- a/experimental/canvaskit/externs.js +++ b/experimental/canvaskit/externs.js @@ -29,21 +29,35 @@ var CanvasKit = { LTRBRect: function() {}, MakeCanvas: function() {}, MakeCanvasSurface: function() {}, + MakeNimaActor: function() {}, MakeSkDashPathEffect: function() {}, MakeSurface: function() {}, currentContext: function() {}, + getSkDataBytes: function() {}, initFonts: function() {}, setCurrentContext: function() {}, - getSkDataBytes: function() {}, // private API (i.e. things declared in the bindings that we use // in the pre-js file) - _getWebGLSurface: function() {}, - _getRasterN32PremulSurface: function() {}, + _MakeNimaActor: function() {}, _MakeSkDashPathEffect: function() {}, + _getRasterN32PremulSurface: function() {}, + _getWebGLSurface: function() {}, // Objects and properties on CanvasKit + NimaActor: { + // public API (from C++ bindings) + duration: function() {}, + getAnimationNames: function() {}, + render: function() {}, + seek: function() {}, + setAnimationByIndex: function() {}, + setAnimationByName: function() {}, + + // private API + }, + SkCanvas: { // public API (from C++ bindings) clear: function() {}, diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js index 885d7e2528..d74f380e4d 100644 --- a/experimental/canvaskit/interface.js +++ b/experimental/canvaskit/interface.js @@ -141,4 +141,13 @@ return CanvasKit._MakeSkDashPathEffect(ptr, intervals.length, phase); } + CanvasKit.MakeNimaActor = function(nimaFile, nimaTexture) { + var nptr = CanvasKit._malloc(nimaFile.byteLength); + CanvasKit.HEAPU8.set(new Uint8Array(nimaFile), nptr); + var tptr = CanvasKit._malloc(nimaTexture.byteLength); + CanvasKit.HEAPU8.set(new Uint8Array(nimaTexture), tptr); + + return CanvasKit._MakeNimaActor(nptr, nimaFile.byteLength, tptr, nimaTexture.byteLength); + } + }(Module)); // When this file is loaded in, the high level object is "Module"; diff --git a/site/user/modules/canvaskit.md b/site/user/modules/canvaskit.md index be30cc23ad..d2c554b9e1 100644 --- a/site/user/modules/canvaskit.md +++ b/site/user/modules/canvaskit.md @@ -55,7 +55,7 @@ Samples
- Star JSFiddle
@@ -63,30 +63,36 @@ Samples
- Ink JSFiddle

Skottie (click for fiddles)

- - - - +

Nima (click for fiddle)

+ + + + Lottie files courtesy of the lottiefiles.com community: -[Lego Loader](https://www.lottiefiles.com/410-lego-loader), [I'm -thirsty](https://www.lottiefiles.com/77-im-thirsty), +[Lego Loader](https://www.lottiefiles.com/410-lego-loader), +[I'm thirsty](https://www.lottiefiles.com/77-im-thirsty), [Confetti](https://www.lottiefiles.com/1370-confetti), [Onboarding](https://www.lottiefiles.com/1134-onboarding-1) +Nima files courtesy of 2dimensions.com: +[Robot](https://www.2dimensions.com/s/281-robot) + Test server ----------- diff --git a/third_party/Nima-Cpp/BUILD.gn b/third_party/Nima-Cpp/BUILD.gn index f3dc8b2309..82243aa075 100644 --- a/third_party/Nima-Cpp/BUILD.gn +++ b/third_party/Nima-Cpp/BUILD.gn @@ -86,8 +86,6 @@ third_party("Nima-Cpp") { "../externals/Nima-Math-Cpp/Source/Vec2D.cpp", ] - testonly = true - cflags_cc = [] if (is_win) { defines = [ "_USE_MATH_DEFINES" ]