baab5aff09
Only text string + size for now, we can surface more if needed. Change-Id: I4b40c1bb5e27ea5fc4ed20a1214c4eeb04bf1ca8 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/382279 Commit-Queue: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@google.com> Reviewed-by: Jorge Betancourt <jmbetancourt@google.com> Reviewed-by: Kevin Lubick <kjlubick@google.com>
328 lines
11 KiB
JavaScript
328 lines
11 KiB
JavaScript
describe('Skottie behavior', () => {
|
|
let container;
|
|
|
|
beforeEach(async () => {
|
|
await LoadCanvasKit;
|
|
container = document.createElement('div');
|
|
container.innerHTML = `
|
|
<canvas width=600 height=600 id=test></canvas>
|
|
<canvas width=600 height=600 id=report></canvas>`;
|
|
document.body.appendChild(container);
|
|
});
|
|
|
|
afterEach(() => {
|
|
document.body.removeChild(container);
|
|
});
|
|
|
|
const expectArrayCloseTo = (a, b, precision) => {
|
|
precision = precision || 14; // digits of precision in base 10
|
|
expect(a.length).toEqual(b.length);
|
|
for (let i=0; i<a.length; i++) {
|
|
expect(a[i]).toBeCloseTo(b[i], precision);
|
|
}
|
|
};
|
|
|
|
const imgPromise = fetch('/assets/flightAnim.gif')
|
|
.then((response) => response.arrayBuffer());
|
|
const jsonPromise = fetch('/assets/animated_gif.json')
|
|
.then((response) => response.text());
|
|
const washPromise = fetch('/assets/map-shield.json')
|
|
.then((response) => response.text());
|
|
|
|
gm('skottie_animgif', (canvas, promises) => {
|
|
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
|
|
console.warn('Skipping test because not compiled with skottie');
|
|
return;
|
|
}
|
|
expect(promises[1]).not.toBe('NOT FOUND');
|
|
const animation = CanvasKit.MakeManagedAnimation(promises[1], {
|
|
'flightAnim.gif': promises[0],
|
|
});
|
|
expect(animation).toBeTruthy();
|
|
const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
|
|
|
|
const size = animation.size();
|
|
expectArrayCloseTo(size, Float32Array.of(800, 600), 4);
|
|
|
|
canvas.clear(CanvasKit.WHITE);
|
|
animation.render(canvas, bounds);
|
|
|
|
// We intentionally make the length of this array 5 and add a sentinel value
|
|
// of 999 so we can make sure the bounds are copied into this rect and a new
|
|
// one is not allocated.
|
|
const damageRect = Float32Array.of(0, 0, 0, 0, 999);
|
|
|
|
// There was a bug, fixed in https://skia-review.googlesource.com/c/skia/+/241757
|
|
// that seeking again and drawing again revealed.
|
|
animation.seek(0.5, damageRect);
|
|
expectArrayCloseTo(damageRect, Float32Array.of(0, 0, 800, 600, 999), 4);
|
|
|
|
canvas.clear(CanvasKit.WHITE);
|
|
animation.render(canvas, bounds);
|
|
animation.delete();
|
|
}, imgPromise, jsonPromise);
|
|
|
|
gm('skottie_setcolor', (canvas, promises) => {
|
|
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
|
|
console.warn('Skipping test because not compiled with skottie');
|
|
return;
|
|
}
|
|
expect(promises[0]).not.toBe('NOT FOUND');
|
|
const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
|
|
canvas.clear(CanvasKit.WHITE);
|
|
|
|
const animation = CanvasKit.MakeManagedAnimation(promises[0]);
|
|
expect(animation).toBeTruthy();
|
|
animation.setColor('$Icon Fill', CanvasKit.RED);
|
|
animation.seek(0.5);
|
|
animation.render(canvas, bounds);
|
|
animation.delete();
|
|
}, washPromise);
|
|
|
|
it('can load audio assets', (done) => {
|
|
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
|
|
console.warn('Skipping test because not compiled with skottie');
|
|
return;
|
|
}
|
|
const mockSoundMap = {
|
|
map : new Map(),
|
|
getPlayer : function(name) {return this.map.get(name)},
|
|
setPlayer : function(name, player) {this.map.set(name, player)},
|
|
};
|
|
function mockPlayer(name) {
|
|
this.name = name;
|
|
this.wasPlayed = false,
|
|
this.seek = function(t) {
|
|
this.wasPlayed = true;
|
|
}
|
|
}
|
|
for (let i = 0; i < 20; i++) {
|
|
var name = 'audio_' + i;
|
|
mockSoundMap.setPlayer(name, new mockPlayer(name));
|
|
}
|
|
fetch('/assets/audio_external.json')
|
|
.then((response) => response.text())
|
|
.then((lottie) => {
|
|
const animation = CanvasKit.MakeManagedAnimation(lottie, null, null, mockSoundMap);
|
|
expect(animation).toBeTruthy();
|
|
// 190 frames in sample lottie
|
|
for (let t = 0; t < 190; t++) {
|
|
animation.seekFrame(t);
|
|
}
|
|
animation.delete();
|
|
for(const player of mockSoundMap.map.values()) {
|
|
expect(player.wasPlayed).toBeTrue(player.name + " was not played");
|
|
}
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can get logs', (done) => {
|
|
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
|
|
console.warn('Skipping test because not compiled with skottie');
|
|
return;
|
|
}
|
|
|
|
const logger = {
|
|
errors: [],
|
|
warnings: [],
|
|
|
|
reset: function() { this.errors = []; this.warnings = []; },
|
|
|
|
// Logger API
|
|
onError: function(err) { this.errors.push(err) },
|
|
onWarning: function(wrn) { this.warnings.push(wrn) }
|
|
};
|
|
|
|
{
|
|
const json = `{
|
|
"v": "5.2.1",
|
|
"w": 100,
|
|
"h": 100,
|
|
"fr": 10,
|
|
"ip": 0,
|
|
"op": 100,
|
|
"layers": [{
|
|
"ty": 3,
|
|
"nm": "null",
|
|
"ind": 0,
|
|
"ip": 0
|
|
}]
|
|
}`;
|
|
const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
|
|
expect(animation).toBeTruthy();
|
|
expect(logger.errors.length).toEqual(0);
|
|
expect(logger.warnings.length).toEqual(0);
|
|
}
|
|
|
|
{
|
|
const json = `{
|
|
"v": "5.2.1",
|
|
"w": 100,
|
|
"h": 100,
|
|
"fr": 10,
|
|
"ip": 0,
|
|
"op": 100,
|
|
"layers": [{
|
|
"ty": 2,
|
|
"nm": "image",
|
|
"ind": 0,
|
|
"ip": 0
|
|
}]
|
|
}`;
|
|
const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
|
|
expect(animation).toBeTruthy();
|
|
expect(logger.errors.length).toEqual(1);
|
|
expect(logger.warnings.length).toEqual(0);
|
|
|
|
// Image layer missing refID
|
|
expect(logger.errors[0].includes('missing ref'));
|
|
logger.reset();
|
|
}
|
|
|
|
{
|
|
const json = `{
|
|
"v": "5.2.1",
|
|
"w": 100,
|
|
"h": 100,
|
|
"fr": 10,
|
|
"ip": 0,
|
|
"op": 100,
|
|
"layers": [{
|
|
"ty": 1,
|
|
"nm": "solid",
|
|
"sw": 100,
|
|
"sh": 100,
|
|
"sc": "#aabbcc",
|
|
"ind": 0,
|
|
"ip": 0,
|
|
"ef": [{
|
|
"mn": "FOO"
|
|
}]
|
|
}]
|
|
}`;
|
|
const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
|
|
expect(animation).toBeTruthy();
|
|
expect(logger.errors.length).toEqual(0);
|
|
expect(logger.warnings.length).toEqual(1);
|
|
|
|
// Unsupported effect FOO
|
|
expect(logger.warnings[0].includes('FOO'));
|
|
logger.reset();
|
|
}
|
|
|
|
done();
|
|
});
|
|
|
|
it('can access dynamic props', () => {
|
|
if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
|
|
console.warn('Skipping test because not compiled with skottie');
|
|
return;
|
|
}
|
|
|
|
const json = `{
|
|
"v": "5.2.1",
|
|
"w": 100,
|
|
"h": 100,
|
|
"fr": 10,
|
|
"ip": 0,
|
|
"op": 100,
|
|
"fonts": {
|
|
"list": [{
|
|
"fName": "test_font",
|
|
"fFamily": "test-family",
|
|
"fStyle": "TestFontStyle"
|
|
}]
|
|
},
|
|
"layers": [
|
|
{
|
|
"ty": 4,
|
|
"nm": "__shape_layer",
|
|
"ind": 0,
|
|
"ip": 0,
|
|
"shapes": [
|
|
{
|
|
"ty": "el",
|
|
"p": { "a": 0, "k": [ 50, 50 ] },
|
|
"s": { "a": 0, "k": [ 50, 50 ] }
|
|
},{
|
|
"ty": "fl",
|
|
"nm": "__shape_fill",
|
|
"c": { "a": 0, "k": [ 1, 0, 0] }
|
|
},{
|
|
"ty": "tr",
|
|
"nm": "__shape_opacity",
|
|
"o": { "a": 0, "k": 50 }
|
|
}
|
|
]
|
|
},{
|
|
"ty": 5,
|
|
"nm": "__text_layer",
|
|
"ip": 0,
|
|
"t": {
|
|
"d": {
|
|
"k": [{
|
|
"t": 0,
|
|
"s": {
|
|
"f": "test_font",
|
|
"s": 100,
|
|
"t": "Foo Bar Baz",
|
|
"lh": 120,
|
|
"ls": 12
|
|
}
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}`;
|
|
|
|
const animation = CanvasKit.MakeManagedAnimation(json, null, '__');
|
|
expect(animation).toBeTruthy();
|
|
|
|
{
|
|
const colors = animation.getColorProps();
|
|
expect(colors.length).toEqual(1);
|
|
expect(colors[0].key).toEqual('__shape_fill');
|
|
expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(255,0,0,255));
|
|
|
|
const opacities = animation.getOpacityProps();
|
|
expect(opacities.length).toEqual(1);
|
|
expect(opacities[0].key).toEqual('__shape_opacity');
|
|
expect(opacities[0].value).toEqual(50);
|
|
|
|
const texts = animation.getTextProps();
|
|
expect(texts.length).toEqual(1);
|
|
expect(texts[0].key).toEqual('__text_layer');
|
|
expect(texts[0].value.text).toEqual('Foo Bar Baz');
|
|
expect(texts[0].value.size).toEqual(100);
|
|
}
|
|
|
|
expect(animation.setColor('__shape_fill', [0,1,0,1])).toEqual(true);
|
|
expect(animation.setOpacity('__shape_opacity', 100)).toEqual(true);
|
|
expect(animation.setText('__text_layer', 'baz bar foo', 10)).toEqual(true);
|
|
|
|
{
|
|
const colors = animation.getColorProps();
|
|
expect(colors.length).toEqual(1);
|
|
expect(colors[0].key).toEqual('__shape_fill');
|
|
expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(0,255,0,255));
|
|
|
|
const opacities = animation.getOpacityProps();
|
|
expect(opacities.length).toEqual(1);
|
|
expect(opacities[0].key).toEqual('__shape_opacity');
|
|
expect(opacities[0].value).toEqual(100);
|
|
|
|
const texts = animation.getTextProps();
|
|
expect(texts.length).toEqual(1);
|
|
expect(texts[0].key).toEqual('__text_layer');
|
|
expect(texts[0].value.text).toEqual('baz bar foo');
|
|
expect(texts[0].value.size).toEqual(10);
|
|
}
|
|
|
|
expect(animation.setColor('INVALID_KEY', [0,1,0,1])).toEqual(false);
|
|
expect(animation.setOpacity('INVALID_KEY', 100)).toEqual(false);
|
|
expect(animation.setText('INVALID KEY', '', 10)).toEqual(false);
|
|
});
|
|
});
|