diff --git a/experimental/canvaskit/CHANGELOG.md b/experimental/canvaskit/CHANGELOG.md index 1cd6916775..2f04cb6fd9 100644 --- a/experimental/canvaskit/CHANGELOG.md +++ b/experimental/canvaskit/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - - Optional arguments to `MakeManagedAnimation` for supplying external assets (like images). + - Optional arguments to `MakeManagedAnimation` for supplying external assets (like images, fonts). ## [0.4.0] - 2019-02-25 diff --git a/experimental/canvaskit/canvaskit/example.html b/experimental/canvaskit/canvaskit/example.html index 0f207a2142..7c017ff5cf 100644 --- a/experimental/canvaskit/canvaskit/example.html +++ b/experimental/canvaskit/canvaskit/example.html @@ -52,7 +52,10 @@ - + + @@ -64,6 +67,7 @@ var confettiJSON = null; var onboardingJSON = null; var multiFrameJSON = null; + var webfontJSON = null; var fullBounds = {fLeft: 0, fTop: 0, fRight: 500, fBottom: 500}; var robotoData = null; @@ -85,7 +89,12 @@ SkottieExample(CanvasKit, 'sk_drinks', drinksJSON, fullBounds); SkottieExample(CanvasKit, 'sk_party', confettiJSON, fullBounds); SkottieExample(CanvasKit, 'sk_onboarding', onboardingJSON, fullBounds); - SkottieAssetExample(CanvasKit, multiFrameJSON, flightAnimGif); + SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, { + 'images/image_0.png': flightAnimGif, + }, null); + SkottieExample(CanvasKit, 'sk_webfont', webfontJSON, fullBounds, null, { + 'Roboto-Regular': robotoData, + }); CanvasAPI1(CanvasKit); CanvasAPI2(CanvasKit); @@ -137,7 +146,18 @@ fetch('https://storage.googleapis.com/skia-cdn/misc/skottie_sample_multiframe.json').then((resp) => { resp.text().then((str) => { multiFrameJSON = str; - SkottieAssetExample(CanvasKit, multiFrameJSON, flightAnimGif); + SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, { + 'images/image_0.png': flightAnimGif, + }); + }); + }); + + fetch('https://storage.googleapis.com/skia-cdn/misc/skottie_sample_webfont.json').then((resp) => { + resp.text().then((str) => { + webfontJSON = str; + SkottieExample(CanvasKit, 'sk_webfont', webfontJSON, fullBounds, null, { + 'Roboto-Regular': robotoData, + }); }); }); @@ -151,7 +171,9 @@ fetch('https://storage.googleapis.com/skia-cdn/misc/flightAnim.gif').then((resp) => { resp.arrayBuffer().then((buffer) => { flightAnimGif = buffer; - SkottieAssetExample(CanvasKit, multiFrameJSON, flightAnimGif); + SkottieExample(CanvasKit, 'sk_animated_gif', multiFrameJSON, fullBounds, { + 'images/image_0.png': flightAnimGif, + }); }); }); @@ -159,6 +181,9 @@ resp.arrayBuffer().then((buffer) => { robotoData = buffer; DrawingExample(CanvasKit, robotoData); + SkottieExample(CanvasKit, 'sk_webfont', webfontJSON, fullBounds, null, { + 'Roboto-Regular': robotoData, + }); }); }); @@ -403,11 +428,11 @@ return frameTimes.length / total; } - function SkottieExample(CanvasKit, id, jsonStr, bounds) { + function SkottieExample(CanvasKit, id, jsonStr, bounds, images, fonts) { if (!CanvasKit || !jsonStr) { return; } - const animation = CanvasKit.MakeManagedAnimation(jsonStr); + const animation = CanvasKit.MakeManagedAnimation(jsonStr, images, fonts); const duration = animation.duration() * 1000; const size = animation.size(); let c = document.getElementById(id); @@ -443,49 +468,6 @@ return surface; } - function SkottieAssetExample(CanvasKit, jsonStr, imgdata) { - if (!CanvasKit || !jsonStr || !imgdata) { - return; - } - const animation = CanvasKit.MakeManagedAnimation(jsonStr, { - 'images/image_0.png': imgdata, - }); - const duration = animation.duration() * 1000; - const size = animation.size(); - let c = document.getElementById('sk_animated_gif'); - - const surface = CanvasKit.MakeCanvasSurface('sk_animated_gif'); - if (!surface) { - console.error('Could not make surface'); - return; - } - const context = CanvasKit.currentContext(); - const canvas = surface.getCanvas(); - - const textPaint = new CanvasKit.SkPaint(); - const textFont = new CanvasKit.SkFont(null, 20); - - let firstFrame = Date.now(); - - function drawFrame() { - let seek = ((Date.now() - firstFrame) / duration) % 1.0; - CanvasKit.setCurrentContext(context); - animation.seek(seek); - canvas.clear(CanvasKit.WHITE); - animation.render(canvas, fullBounds); - canvas.drawText('Embed an animated gif and transforming it', - 5, 20, textPaint, textFont) - surface.flush(); - - window.requestAnimationFrame(drawFrame); - } - window.requestAnimationFrame(drawFrame); - - // animation.delete(); - // assetProvider.delete(); // This will clean up anything we passed in. - return surface; - } - function CanvasAPI1(CanvasKit) { let skcanvas = CanvasKit.MakeCanvas(300, 300); let realCanvas = document.getElementById('api1_c'); @@ -1249,8 +1231,8 @@ const TEXT = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ac leo vitae ipsum hendrerit euismod quis rutrum nibh. Quisque non suscipit urna. Donec enim urna, facilisis vitae volutpat in, mattis at elit. Sed quis augue et dolor dignissim fringilla. Sed non massa eu neque tristique malesuada. '; - let X = 80; - let Y = 90; + let X = 240; + let Y = 190; function drawFrame() { CanvasKit.setCurrentContext(context); diff --git a/experimental/canvaskit/canvaskit_bindings.cpp b/experimental/canvaskit/canvaskit_bindings.cpp index 2d7f41dd79..249c613944 100644 --- a/experimental/canvaskit/canvaskit_bindings.cpp +++ b/experimental/canvaskit/canvaskit_bindings.cpp @@ -561,8 +561,8 @@ EMSCRIPTEN_BINDINGS(Skia) { function("_decodeImage", optional_override([](uintptr_t /* uint8_t* */ iptr, size_t length)->sk_sp { uint8_t* imgData = reinterpret_cast(iptr); - sk_sp bytes = SkData::MakeWithoutCopy(imgData, length); - return SkImage::MakeFromEncoded(bytes); + sk_sp bytes = SkData::MakeFromMalloc(imgData, length); + return SkImage::MakeFromEncoded(std::move(bytes)); }), allow_raw_pointers()); function("_getRasterDirectSurface", optional_override([](const SimpleImageInfo ii, uintptr_t /* uint8_t* */ pPtr, diff --git a/experimental/canvaskit/interface.js b/experimental/canvaskit/interface.js index 91456d5094..52cb33e5d3 100644 --- a/experimental/canvaskit/interface.js +++ b/experimental/canvaskit/interface.js @@ -546,14 +546,8 @@ CanvasKit.MakeImageFromEncoded = function(data) { var img = CanvasKit._decodeImage(iptr, data.byteLength); if (!img) { SkDebug('Could not decode image'); - CanvasKit._free(iptr); return null; } - var realDelete = img.delete.bind(img); - img.delete = function() { - CanvasKit._free(iptr); - realDelete(); - } return img; } diff --git a/experimental/canvaskit/skottie.js b/experimental/canvaskit/skottie.js index a5ac1ff37a..30c646178d 100644 --- a/experimental/canvaskit/skottie.js +++ b/experimental/canvaskit/skottie.js @@ -14,9 +14,9 @@ CanvasKit.MakeManagedAnimation = function(json, imgs, fonts) { var imgDataPtrs = []; var imgSizes = []; - var keys = Object.keys(imgs); - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; + var imgKeys = Object.keys(imgs || {}); + for (var i = 0; i < imgKeys.length; i++) { + var key = imgKeys[i]; var buffer = imgs[key]; var data = new Uint8Array(buffer); @@ -35,20 +35,52 @@ CanvasKit.MakeManagedAnimation = function(json, imgs, fonts) { imgNamePtrs.push(strPtr); } + var fontNamePtrs = []; + var fontDataPtrs = []; + var fontSizes = []; + + var fontKeys = Object.keys(fonts || {}); + for (var i = 0; i < fontKeys.length; i++) { + var key = fontKeys[i]; + var buffer = fonts[key]; + var data = new Uint8Array(buffer); + + var iptr = CanvasKit._malloc(data.byteLength); + CanvasKit.HEAPU8.set(data, iptr); + fontDataPtrs.push(iptr); + fontSizes.push(data.byteLength); + + // lengthBytesUTF8 and stringToUTF8Array are defined in the emscripten + // JS. See https://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html#stringToUTF8 + // Add 1 for null terminator + var strLen = lengthBytesUTF8(key) + 1; + var strPtr = CanvasKit._malloc(strLen); + + stringToUTF8(key, strPtr, strLen); + fontNamePtrs.push(strPtr); + } + // Not entirely sure if it matters, but the uintptr_t are 32 bits // we want to copy our array of uintptr_t into the right size memory. - var namesPtr = copy1dArray(imgNamePtrs, CanvasKit.HEAPU32); + var iNamesPtr = copy1dArray(imgNamePtrs, CanvasKit.HEAPU32); var imgsPtr = copy1dArray(imgDataPtrs, CanvasKit.HEAPU32); var imgSizesPtr = copy1dArray(imgSizes, CanvasKit.HEAPU32); - var anim = CanvasKit._MakeManagedAnimation(json, keys.length, namesPtr, imgsPtr, imgSizesPtr, - 0, nullptr, nullptr, nullptr); + var fNamesPtr = copy1dArray(fontNamePtrs, CanvasKit.HEAPU32); + var fontsPtr = copy1dArray(fontDataPtrs, CanvasKit.HEAPU32); + var fontSizesPtr = copy1dArray(fontSizes, CanvasKit.HEAPU32); + + var anim = CanvasKit._MakeManagedAnimation(json, imgKeys.length, iNamesPtr, imgsPtr, imgSizesPtr, + fontKeys.length, fNamesPtr, fontsPtr, fontSizesPtr); // We leave the image data arrays and string data live and assume // it is now owned by the C++ code - CanvasKit._free(namesPtr); + CanvasKit._free(iNamesPtr); CanvasKit._free(imgsPtr); CanvasKit._free(imgSizesPtr); + CanvasKit._free(fNamesPtr); + CanvasKit._free(fontsPtr); + CanvasKit._free(fontSizesPtr); return anim; }; diff --git a/experimental/canvaskit/skottie_bindings.cpp b/experimental/canvaskit/skottie_bindings.cpp index 67ee4d3f75..8992869973 100644 --- a/experimental/canvaskit/skottie_bindings.cpp +++ b/experimental/canvaskit/skottie_bindings.cpp @@ -44,7 +44,7 @@ public: } void addFont(const SkString& name, sk_sp fontData) { - // TODO(kjlubick) + fFonts.emplace_back(std::make_pair(name, std::move(fontData))); } sk_sp loadImageAsset(const char path[], @@ -61,6 +61,16 @@ public: return nullptr; } + sk_sp loadFont(const char name[], const char url[]) const override { + for(int i = 0; i < fFonts.size(); i++) { + if (fFonts[i].first.equals(name)) { + return fFonts[i].second; + } + } + SkDebugf("Could not find %s (%s)\n", name, url); + return nullptr; + } + private: SkottieAssetProvider() = default; @@ -69,6 +79,7 @@ private: // Not entirely sure why, but perhaps the iterator in the map was // confusing enscripten. std::vector>> fImgs; + std::vector>> fFonts; }; @@ -220,6 +231,16 @@ EMSCRIPTEN_BINDINGS(Skottie) { assetProvider->addImage(name, std::move(bytes)); } + char** fontNames = reinterpret_cast(fnptr); + uint8_t** fontDatas = reinterpret_cast(fdptr); + size_t* fontSizes = reinterpret_cast(fsptr); + for (int i = 0; i < fontCount; i++) { + SkString name = SkString(fontNames[i]); + sk_sp bytes = SkData::MakeFromMalloc(fontDatas[i], + fontSizes[i]); + assetProvider->addFont(name, std::move(bytes)); + } + return ManagedAnimation::Make(json, std::move(assetProvider)); })); constant("managed_skottie", true);