[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 <brianosman@google.com>
Commit-Queue: Kevin Lubick <kjlubick@google.com>
This commit is contained in:
Kevin Lubick 2018-10-30 15:05:04 -04:00 committed by Skia Commit-Bot
parent 936fe7d1f2
commit 134be1d9ba
9 changed files with 239 additions and 25 deletions

View File

@ -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.

View File

@ -30,6 +30,10 @@
<!-- Doesn't work yet. -->
<button id=lego_btn>Take a picture of the legos</button>
<h2> Nima </h2>
<canvas id=nima_example width=300 height=300></canvas>
<h2>Drop in replacement for HTML Canvas (e.g. node.js)</h2>
<img id=api1 width=300 height=300/>
@ -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);
}
</script>

View File

@ -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",

View File

@ -28,6 +28,9 @@
#if SK_INCLUDE_SKOTTIE
#include "Skottie.h"
#endif
#if SK_INCLUDE_NIMA
#include "nima/NimaActor.h"
#endif
#include <iostream>
#include <string>
@ -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>("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<void>("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<void(uint8_t )>(&NimaActor::setAnimation))
.function("setAnimationByName" , select_overload<void(std::string)>(&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<const uint8_t*>(nptr);
const uint8_t* textureBytes = reinterpret_cast<const uint8_t*>(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
}

View File

@ -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 \

View File

@ -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() {},

View File

@ -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";

View File

@ -55,7 +55,7 @@ Samples
<figure>
<canvas id=patheffect width=400 height=400></canvas>
<figcaption>
<a href="https://jsfiddle.skia.org/canvaskit/79bc0e7a670ef4aa45254acfcd537ffe787b5b3333c45b4107e1ab8c898fc834"
<a href="https://jsfiddle.skia.org/canvaskit/89604becd6101263113cb7c35156432b8b5c2f7857eed4a18df06b577f431dfa"
target=_blank rel=noopener>
Star JSFiddle</a>
</figcaption>
@ -63,30 +63,36 @@ Samples
<figure>
<canvas id=ink width=400 height=400></canvas>
<figcaption>
<a href="https://jsfiddle.skia.org/canvaskit/4279ce869f9a08b04288a81d740eef5b3d54191f30a4aea510a64596118a5d62"
<a href="https://jsfiddle.skia.org/canvaskit/aca160254b841c6d5aed7f49a244200cf55282b898a7d503a27bbb69685cecf6"
target=_blank rel=noopener>
Ink JSFiddle</a>
</figcaption>
</figure>
<h3>Skottie (click for fiddles)</h3>
<a href="https://jsfiddle.skia.org/canvaskit/00ad983919d3925499345202c2e8e28da1c127093593ae86e268e519c6c2b1bc"
<a href="https://jsfiddle.skia.org/canvaskit/092690b273b41076d2f00f0d43d004893d6bb9992c387c0385efa8e6f6bc83d7"
target=_blank rel=noopener>
<canvas id=sk_legos width=300 height=300></canvas>
</a>
<a href="https://jsfiddle.skia.org/canvaskit/93a4d65d8b467053fbed26a6bc08968f9ff9f5986528ad9583e7fe2a0d98192f"
<a href="https://jsfiddle.skia.org/canvaskit/e7ac983d9859f89aff1b6d385190919202c2eb53d028a79992892cacceffd209"
target=_blank rel=noopener>
<canvas id=sk_drinks width=500 height=500></canvas>
</a>
<a href="https://jsfiddle.skia.org/canvaskit/9d2ce26e5e14b6d72701466ee46c60aadecc3650ed709a57e35a04fc8f98366e"
<a href="https://jsfiddle.skia.org/canvaskit/0e06547181759731e7369d3e3613222a0826692f48c41b16504ed68d671583e1"
target=_blank rel=noopener>
<canvas id=sk_party width=500 height=500></canvas>
</a>
<a href="https://jsfiddle.skia.org/canvaskit/13d92f4a7238425dcb68211010a1c313e18e429aae3a81ff630788307e31771e"
<a href="https://jsfiddle.skia.org/canvaskit/be3fc1c5c351e7f43cc2840033f80b44feb3475925264808f321bb9e2a21174a"
target=_blank rel=noopener>
<canvas id=sk_onboarding width=500 height=500></canvas>
</a>
<h3>Nima (click for fiddle)</h3>
<a href="https://jsfiddle.skia.org/canvaskit/8f72aa124b91d28c77ef38c849ad7b3b0a4c207010ecca6dccba2b273329895c"
target=_blank rel=noopener>
<canvas id=nima_robot width=300 height=300></canvas>
</a>
</div>
<script type="text/javascript" charset="utf-8">
@ -96,7 +102,7 @@ Samples
var locate_file = '';
if (window.WebAssembly && typeof window.WebAssembly.compile === 'function') {
console.log('WebAssembly is supported!');
locate_file = 'https://storage.googleapis.com/skia-cdn/canvaskit-wasm/0.1.0/bin/';
locate_file = 'https://storage.googleapis.com/skia-cdn/canvaskit-wasm/0.1.1/bin/';
} else {
console.log('WebAssembly is not supported (yet) on this browser.');
document.getElementById('demo').innerHTML = "<div>WASM not supported by your browser. Try a recent version of Chrome, Firefox, Edge, or Safari.</div>";
@ -109,6 +115,8 @@ Samples
var drinksJSON = null;
var confettiJSON = null;
var onboardingJSON = null;
var nimaFile = null;
var nimaTexture = null;
var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500};
CanvasKitInit({
locateFile: (file) => locate_file + file,
@ -123,6 +131,8 @@ Samples
SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds);
SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds);
SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds);
NimaExample(CanvasKit, nimaFile, nimaTexture);
});
fetch('https://storage.googleapis.com/skia-cdn/misc/lego_loader.json').then((resp) => {
@ -153,6 +163,28 @@ Samples
});
});
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 preventScrolling(canvas) {
canvas.addEventListener('touchmove', (e) => {
// Prevents touch events in the canvas from scrolling the canvas.
@ -162,7 +194,7 @@ Samples
}
function DrawingExample(CanvasKit) {
const surface = CanvasKit.getWebGLSurface('patheffect');
const surface = CanvasKit.MakeCanvasSurface('patheffect');
if (!surface) {
console.log('Could not make surface');
}
@ -222,7 +254,7 @@ Samples
}
function InkExample(CanvasKit) {
const surface = CanvasKit.getWebGLSurface('ink');
const surface = CanvasKit.MakeCanvasSurface('ink');
if (!surface) {
console.log('Could not make surface');
}
@ -315,7 +347,7 @@ Samples
let c = document.getElementById(id);
bounds = bounds || {fLeft: 0, fTop: 0, fRight: size.w, fBottom: size.h};
const surface = CanvasKit.getWebGLSurface(id);
const surface = CanvasKit.MakeCanvasSurface(id);
if (!surface) {
console.log('Could not make surface');
}
@ -337,17 +369,56 @@ Samples
window.requestAnimationFrame(drawFrame);
//animation.delete();
}
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_robot');
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);
}
}
document.head.appendChild(s);
})();
</script>
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
-----------

View File

@ -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" ]