[canvaskit] Add support for external fonts

We ignore the url for web fonts, as we assume the names
will not collide.

Bug: skia:
Change-Id: Ifaaa0d27e0de2c5088bc0885c9299bb83ee78a5d
Reviewed-on: https://skia-review.googlesource.com/c/196426
Commit-Queue: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Kevin Lubick 2019-02-28 16:05:09 -05:00 committed by Skia Commit-Bot
parent d31b5e7489
commit 88aff5fecd
6 changed files with 97 additions and 68 deletions

View File

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

View File

@ -52,7 +52,10 @@
<canvas id=sk_drinks width=500 height=500></canvas>
<canvas id=sk_party width=500 height=500></canvas>
<canvas id=sk_onboarding width=500 height=500></canvas>
<canvas id=sk_animated_gif width=500 height=500></canvas>
<canvas id=sk_animated_gif width=500 height=500
title='This is an animated gif being animated in Skottie'></canvas>
<canvas id=sk_webfont width=500 height=500
title='This shows loading of a custom font (e.g. WebFon'></canvas>
<script type="text/javascript" src="/node_modules/canvaskit/bin/canvaskit.js"></script>
@ -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);

View File

@ -561,8 +561,8 @@ EMSCRIPTEN_BINDINGS(Skia) {
function("_decodeImage", optional_override([](uintptr_t /* uint8_t* */ iptr,
size_t length)->sk_sp<SkImage> {
uint8_t* imgData = reinterpret_cast<uint8_t*>(iptr);
sk_sp<SkData> bytes = SkData::MakeWithoutCopy(imgData, length);
return SkImage::MakeFromEncoded(bytes);
sk_sp<SkData> 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,

View File

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

View File

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

View File

@ -44,7 +44,7 @@ public:
}
void addFont(const SkString& name, sk_sp<SkData> fontData) {
// TODO(kjlubick)
fFonts.emplace_back(std::make_pair(name, std::move(fontData)));
}
sk_sp<skottie::ImageAsset> loadImageAsset(const char path[],
@ -61,6 +61,16 @@ public:
return nullptr;
}
sk_sp<SkData> 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<std::pair<SkString, sk_sp<skottie_utils::MultiFrameImageAsset>>> fImgs;
std::vector<std::pair<SkString, sk_sp<SkData>>> fFonts;
};
@ -220,6 +231,16 @@ EMSCRIPTEN_BINDINGS(Skottie) {
assetProvider->addImage(name, std::move(bytes));
}
char** fontNames = reinterpret_cast<char**>(fnptr);
uint8_t** fontDatas = reinterpret_cast<uint8_t**>(fdptr);
size_t* fontSizes = reinterpret_cast<size_t*>(fsptr);
for (int i = 0; i < fontCount; i++) {
SkString name = SkString(fontNames[i]);
sk_sp<SkData> bytes = SkData::MakeFromMalloc(fontDatas[i],
fontSizes[i]);
assetProvider->addFont(name, std::move(bytes));
}
return ManagedAnimation::Make(json, std::move(assetProvider));
}));
constant("managed_skottie", true);