blob: 29de2da4cb100c9c68532d95032e62dc0710889f [file] [log] [blame]
Mike Reed69ace2a2020-01-11 15:57:14 -05001/*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/core/SkCanvas.h"
Mike Reed46f5c5f2020-02-20 15:42:29 -05009#include "include/core/SkM44.h"
Mike Reed69ace2a2020-01-11 15:57:14 -050010#include "include/core/SkPaint.h"
11#include "include/core/SkRRect.h"
Mike Reed69ace2a2020-01-11 15:57:14 -050012#include "include/utils/SkRandom.h"
13#include "samplecode/Sample.h"
14#include "tools/Resources.h"
15
Mike Reede5809952020-01-25 20:42:51 -050016struct VSphere {
Mike Reed415cace2020-02-21 12:03:49 -050017 SkV2 fCenter;
Mike Reede5809952020-01-25 20:42:51 -050018 SkScalar fRadius;
19
Mike Reed415cace2020-02-21 12:03:49 -050020 VSphere(SkV2 center, SkScalar radius) : fCenter(center), fRadius(radius) {}
Mike Reede5809952020-01-25 20:42:51 -050021
Mike Reed415cace2020-02-21 12:03:49 -050022 bool contains(SkV2 v) const {
Mike Reede5809952020-01-25 20:42:51 -050023 return (v - fCenter).length() <= fRadius;
24 }
25
Mike Reed415cace2020-02-21 12:03:49 -050026 SkV2 pinLoc(SkV2 p) const {
Mike Reed2d4a28e2020-01-25 22:39:43 -050027 auto v = p - fCenter;
28 if (v.length() > fRadius) {
29 v *= (fRadius / v.length());
30 }
31 return fCenter + v;
32 }
33
Mike Reed415cace2020-02-21 12:03:49 -050034 SkV3 computeUnitV3(SkV2 v) const {
Mike Reede5809952020-01-25 20:42:51 -050035 v = (v - fCenter) * (1 / fRadius);
36 SkScalar len2 = v.lengthSquared();
37 if (len2 > 1) {
Mike Reede1a81ba2020-02-19 17:40:32 -050038 v = v.normalize();
Mike Reede5809952020-01-25 20:42:51 -050039 len2 = 1;
40 }
41 SkScalar z = SkScalarSqrt(1 - len2);
42 return {v.x, v.y, z};
43 }
44
Mike Reed52638372020-02-19 13:30:06 -050045 struct RotateInfo {
46 SkV3 fAxis;
47 SkScalar fAngle;
48 };
49
Mike Reed415cace2020-02-21 12:03:49 -050050 RotateInfo computeRotationInfo(SkV2 a, SkV2 b) const {
Mike Reede5809952020-01-25 20:42:51 -050051 SkV3 u = this->computeUnitV3(a);
52 SkV3 v = this->computeUnitV3(b);
53 SkV3 axis = u.cross(v);
Mike Reed23823662020-02-19 17:07:04 -050054 SkScalar length = axis.length();
Mike Reede5809952020-01-25 20:42:51 -050055
Mike Reed23823662020-02-19 17:07:04 -050056 if (!SkScalarNearlyZero(length)) {
57 return {axis * (1.0f / length), acos(u.dot(v))};
Mike Reede5809952020-01-25 20:42:51 -050058 }
Mike Reed52638372020-02-19 13:30:06 -050059 return {{0, 0, 0}, 0};
60 }
61
Mike Reed415cace2020-02-21 12:03:49 -050062 SkM44 computeRotation(SkV2 a, SkV2 b) const {
Mike Reed52638372020-02-19 13:30:06 -050063 auto [axis, angle] = this->computeRotationInfo(a, b);
64 return SkM44::Rotate(axis, angle);
Mike Reede5809952020-01-25 20:42:51 -050065 }
66};
67
Mike Reedee3216d2020-01-17 17:35:04 -050068static SkM44 inv(const SkM44& m) {
69 SkM44 inverse;
70 SkAssertResult(m.invert(&inverse));
71 return inverse;
72}
73
Mike Reed69ace2a2020-01-11 15:57:14 -050074class Sample3DView : public Sample {
75protected:
76 float fNear = 0.05f;
77 float fFar = 4;
78 float fAngle = SK_ScalarPI / 12;
79
Mike Reed00a97642020-01-25 18:42:23 -050080 SkV3 fEye { 0, 0, 1.0f/tan(fAngle/2) - 1 };
81 SkV3 fCOA { 0, 0, 0 };
82 SkV3 fUp { 0, 1, 0 };
Mike Reed69ace2a2020-01-11 15:57:14 -050083
Mike Reed69ace2a2020-01-11 15:57:14 -050084public:
Mike Reedee0a03a2020-01-14 16:44:47 -050085 void saveCamera(SkCanvas* canvas, const SkRect& area, SkScalar zscale) {
Mike Reed00a97642020-01-25 18:42:23 -050086 SkM44 camera = Sk3LookAt(fEye, fCOA, fUp),
87 perspective = Sk3Perspective(fNear, fFar, fAngle),
88 viewport = SkM44::Translate(area.centerX(), area.centerY(), 0) *
89 SkM44::Scale(area.width()*0.5f, area.height()*0.5f, zscale);
Mike Reed69ace2a2020-01-11 15:57:14 -050090
Mike Reedee3216d2020-01-17 17:35:04 -050091 // want "world" to be in our big coordinates (e.g. area), so apply this inverse
92 // as part of our "camera".
Mike Reedc43f2a02020-01-16 14:54:34 -050093 canvas->experimental_saveCamera(viewport * perspective, camera * inv(viewport));
Mike Reed69ace2a2020-01-11 15:57:14 -050094 }
Mike Reed69ace2a2020-01-11 15:57:14 -050095};
96
Mike Reed69ace2a2020-01-11 15:57:14 -050097struct Face {
98 SkScalar fRx, fRy;
Mike Reedee3216d2020-01-17 17:35:04 -050099 SkColor fColor;
Mike Reed69ace2a2020-01-11 15:57:14 -0500100
Mike Reed00a97642020-01-25 18:42:23 -0500101 static SkM44 T(SkScalar x, SkScalar y, SkScalar z) {
102 return SkM44::Translate(x, y, z);
Mike Reed78184a32020-01-25 03:20:18 +0000103 }
104
Mike Reed00a97642020-01-25 18:42:23 -0500105 static SkM44 R(SkV3 axis, SkScalar rad) {
106 return SkM44::Rotate(axis, rad);
Mike Reed78184a32020-01-25 03:20:18 +0000107 }
108
Mike Reed00a97642020-01-25 18:42:23 -0500109 SkM44 asM44(SkScalar scale) const {
110 return R({0,1,0}, fRy) * R({1,0,0}, fRx) * T(0, 0, scale);
Mike Reed69ace2a2020-01-11 15:57:14 -0500111 }
112};
113
Mike Reed75435872020-01-13 21:15:06 -0500114static bool front(const SkM44& m) {
Florin Malitad5899162020-02-04 10:06:24 -0500115 SkM44 m2(SkM44::kUninitialized_Constructor);
116 if (!m.invert(&m2)) {
117 m2.setIdentity();
118 }
Mike Reed75435872020-01-13 21:15:06 -0500119 /*
120 * Classically we want to dot the transpose(inverse(ctm)) with our surface normal.
121 * In this case, the normal is known to be {0, 0, 1}, so we only actually need to look
122 * at the z-scale of the inverse (the transpose doesn't change the main diagonal, so
123 * no need to actually transpose).
124 */
Mike Reed07d32b42020-01-23 11:06:20 -0500125 return m2.rc(2,2) > 0;
Mike Reed69ace2a2020-01-11 15:57:14 -0500126}
127
128const Face faces[] = {
Mike Reedee3216d2020-01-17 17:35:04 -0500129 { 0, 0, SK_ColorRED }, // front
130 { 0, SK_ScalarPI, SK_ColorGREEN }, // back
Mike Reed69ace2a2020-01-11 15:57:14 -0500131
Mike Reedee3216d2020-01-17 17:35:04 -0500132 { SK_ScalarPI/2, 0, SK_ColorBLUE }, // top
133 {-SK_ScalarPI/2, 0, SK_ColorCYAN }, // bottom
Mike Reed69ace2a2020-01-11 15:57:14 -0500134
Mike Reedee3216d2020-01-17 17:35:04 -0500135 { 0, SK_ScalarPI/2, SK_ColorMAGENTA }, // left
136 { 0,-SK_ScalarPI/2, SK_ColorYELLOW }, // right
Mike Reed69ace2a2020-01-11 15:57:14 -0500137};
138
Mike Reedee3216d2020-01-17 17:35:04 -0500139#include "include/effects/SkRuntimeEffect.h"
140
Mike Reed2d4a28e2020-01-25 22:39:43 -0500141struct LightOnSphere {
Mike Reed415cace2020-02-21 12:03:49 -0500142 SkV2 fLoc;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500143 SkScalar fDistance;
144 SkScalar fRadius;
145
146 SkV3 computeWorldPos(const VSphere& s) const {
147 return s.computeUnitV3(fLoc) * fDistance;
148 }
149
150 void draw(SkCanvas* canvas) const {
151 SkPaint paint;
152 paint.setAntiAlias(true);
153 paint.setColor(SK_ColorWHITE);
154 canvas->drawCircle(fLoc.x, fLoc.y, fRadius + 2, paint);
155 paint.setColor(SK_ColorBLACK);
156 canvas->drawCircle(fLoc.x, fLoc.y, fRadius, paint);
157 }
158};
159
Mike Reed52638372020-02-19 13:30:06 -0500160#include "include/core/SkTime.h"
161
162class RotateAnimator {
163 SkV3 fAxis = {0, 0, 0};
164 SkScalar fAngle = 0,
165 fPrevAngle = 1234567;
166 double fNow = 0,
167 fPrevNow = 0;
168
169 SkScalar fAngleSpeed = 0,
170 fAngleSign = 1;
171
172 static constexpr double kSlowDown = 4;
173 static constexpr SkScalar kMaxSpeed = 16;
174
175public:
176 void update(SkV3 axis, SkScalar angle) {
177 if (angle != fPrevAngle) {
178 fPrevAngle = fAngle;
179 fAngle = angle;
180
181 fPrevNow = fNow;
182 fNow = SkTime::GetSecs();
183
184 fAxis = axis;
185 }
186 }
187
188 SkM44 rotation() {
189 if (fAngleSpeed > 0) {
190 double now = SkTime::GetSecs();
191 double dtime = now - fPrevNow;
192 fPrevNow = now;
193 double delta = fAngleSign * fAngleSpeed * dtime;
194 fAngle += delta;
195 fAngleSpeed -= kSlowDown * dtime;
196 if (fAngleSpeed < 0) {
197 fAngleSpeed = 0;
198 }
199 }
200 return SkM44::Rotate(fAxis, fAngle);
201
202 }
203
204 void start() {
205 if (fPrevNow != fNow) {
206 fAngleSpeed = (fAngle - fPrevAngle) / (fNow - fPrevNow);
207 fAngleSign = fAngleSpeed < 0 ? -1 : 1;
208 fAngleSpeed = std::min(kMaxSpeed, std::abs(fAngleSpeed));
209 } else {
210 fAngleSpeed = 0;
211 }
212 fPrevNow = SkTime::GetSecs();
213 fAngle = 0;
214 }
215
216 void reset() {
217 fAngleSpeed = 0;
218 fAngle = 0;
219 fPrevAngle = 1234567;
220 }
Mike Reedfab61982020-02-21 13:25:12 -0500221
222 bool isAnimating() const { return fAngleSpeed != 0; }
Mike Reed52638372020-02-19 13:30:06 -0500223};
224
Mike Reedcfee8ee2020-02-18 13:05:45 -0500225class SampleCubeBase : public Sample3DView {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500226 enum {
227 DX = 400,
228 DY = 300
229 };
230
Mike Reedf0b7edf2020-01-18 14:21:12 -0500231 SkM44 fWorldToClick,
232 fClickToWorld;
233
Mike Reed52638372020-02-19 13:30:06 -0500234 SkM44 fRotation; // part of model
235
236 RotateAnimator fRotateAnimator;
Mike Reede5809952020-01-25 20:42:51 -0500237
Mike Reedcfee8ee2020-02-18 13:05:45 -0500238protected:
239 enum Flags {
240 kCanRunOnCPU = 1 << 0,
241 kShowLightDome = 1 << 1,
242 };
243
244 LightOnSphere fLight = {{200 + DX, 200 + DY}, 800, 12};
245
246 VSphere fSphere;
247 Flags fFlags;
248
Mike Reede5809952020-01-25 20:42:51 -0500249public:
Mike Reedcfee8ee2020-02-18 13:05:45 -0500250 SampleCubeBase(Flags flags)
251 : fSphere({200 + DX, 200 + DY}, 400)
252 , fFlags(flags)
253 {}
Mike Reedf0b7edf2020-01-18 14:21:12 -0500254
255 bool onChar(SkUnichar uni) override {
256 switch (uni) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500257 case 'Z': fLight.fDistance += 10; return true;
258 case 'z': fLight.fDistance -= 10; return true;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500259 }
260 return this->Sample3DView::onChar(uni);
261 }
262
Mike Reedcfee8ee2020-02-18 13:05:45 -0500263 virtual void drawContent(SkCanvas* canvas, SkColor, int index, bool drawFront) = 0;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500264
265 void setClickToWorld(SkCanvas* canvas, const SkM44& clickM) {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500266 auto l2d = canvas->getLocalToDevice();
Mike Reedf0b7edf2020-01-18 14:21:12 -0500267 fWorldToClick = inv(clickM) * l2d;
268 fClickToWorld = inv(fWorldToClick);
269 }
270
271 void onDrawContent(SkCanvas* canvas) override {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500272 if (!canvas->getGrContext() && !(fFlags & kCanRunOnCPU)) {
Mike Reedf0b7edf2020-01-18 14:21:12 -0500273 return;
274 }
Mike Reed46f5c5f2020-02-20 15:42:29 -0500275 SkM44 clickM = canvas->getLocalToDevice();
Mike Reedf0b7edf2020-01-18 14:21:12 -0500276
277 canvas->save();
Mike Reed2d4a28e2020-01-25 22:39:43 -0500278 canvas->translate(DX, DY);
Mike Reedf0b7edf2020-01-18 14:21:12 -0500279
280 this->saveCamera(canvas, {0, 0, 400, 400}, 200);
281
282 this->setClickToWorld(canvas, clickM);
283
Mike Reedcfee8ee2020-02-18 13:05:45 -0500284 for (bool drawFront : {false, true}) {
285 int index = 0;
286 for (auto f : faces) {
287 SkAutoCanvasRestore acr(canvas, true);
288
289 SkM44 trans = SkM44::Translate(200, 200, 0); // center of the rotation
Mike Reed52638372020-02-19 13:30:06 -0500290 SkM44 m = fRotateAnimator.rotation() * fRotation * f.asM44(200);
Mike Reedcfee8ee2020-02-18 13:05:45 -0500291
Mike Reed46f5c5f2020-02-20 15:42:29 -0500292 canvas->concat44(trans * m * inv(trans));
Mike Reedcfee8ee2020-02-18 13:05:45 -0500293 this->drawContent(canvas, f.fColor, index++, drawFront);
294 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500295 }
296
Mike Reede5809952020-01-25 20:42:51 -0500297 canvas->restore(); // camera
298 canvas->restore(); // center the content in the window
299
Mike Reedcfee8ee2020-02-18 13:05:45 -0500300 if (fFlags & kShowLightDome){
301 fLight.draw(canvas);
302
Mike Reede5809952020-01-25 20:42:51 -0500303 SkPaint paint;
Mike Reed2d4a28e2020-01-25 22:39:43 -0500304 paint.setAntiAlias(true);
Mike Reede5809952020-01-25 20:42:51 -0500305 paint.setStyle(SkPaint::kStroke_Style);
Mike Reed2d4a28e2020-01-25 22:39:43 -0500306 paint.setColor(0x40FF0000);
Mike Reede5809952020-01-25 20:42:51 -0500307 canvas->drawCircle(fSphere.fCenter.x, fSphere.fCenter.y, fSphere.fRadius, paint);
308 canvas->drawLine(fSphere.fCenter.x, fSphere.fCenter.y - fSphere.fRadius,
309 fSphere.fCenter.x, fSphere.fCenter.y + fSphere.fRadius, paint);
310 canvas->drawLine(fSphere.fCenter.x - fSphere.fRadius, fSphere.fCenter.y,
311 fSphere.fCenter.x + fSphere.fRadius, fSphere.fCenter.y, paint);
312 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500313 }
314
315 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
Mike Reed415cace2020-02-21 12:03:49 -0500316 SkV2 p = fLight.fLoc - SkV2{x, y};
Mike Reed2d4a28e2020-01-25 22:39:43 -0500317 if (p.length() <= fLight.fRadius) {
Mike Reede5809952020-01-25 20:42:51 -0500318 Click* c = new Click();
319 c->fMeta.setS32("type", 0);
320 return c;
321 }
322 if (fSphere.contains({x, y})) {
323 Click* c = new Click();
324 c->fMeta.setS32("type", 1);
Mike Reed52638372020-02-19 13:30:06 -0500325
326 fRotation = fRotateAnimator.rotation() * fRotation;
327 fRotateAnimator.reset();
Mike Reede5809952020-01-25 20:42:51 -0500328 return c;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500329 }
330 return nullptr;
331 }
332 bool onClick(Click* click) override {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500333#if 0
334 auto L = fWorldToClick * fLight.fPos;
335 SkPoint c = project(fClickToWorld, {click->fCurr.fX, click->fCurr.fY, L.z/L.w, 1});
336 fLight.update(c.fX, c.fY);
337#endif
Mike Reede5809952020-01-25 20:42:51 -0500338 if (click->fMeta.hasS32("type", 0)) {
Mike Reed2d4a28e2020-01-25 22:39:43 -0500339 fLight.fLoc = fSphere.pinLoc({click->fCurr.fX, click->fCurr.fY});
Mike Reede5809952020-01-25 20:42:51 -0500340 return true;
341 }
342 if (click->fMeta.hasS32("type", 1)) {
343 if (click->fState == skui::InputState::kUp) {
Mike Reed52638372020-02-19 13:30:06 -0500344 fRotation = fRotateAnimator.rotation() * fRotation;
345 fRotateAnimator.start();
Mike Reede5809952020-01-25 20:42:51 -0500346 } else {
Mike Reed52638372020-02-19 13:30:06 -0500347 auto [axis, angle] = fSphere.computeRotationInfo(
348 {click->fOrig.fX, click->fOrig.fY},
349 {click->fCurr.fX, click->fCurr.fY});
350 fRotateAnimator.update(axis, angle);
Mike Reede5809952020-01-25 20:42:51 -0500351 }
352 return true;
353 }
Mike Reedf0b7edf2020-01-18 14:21:12 -0500354 return true;
355 }
Mike Reed52638372020-02-19 13:30:06 -0500356
357 bool onAnimate(double nanos) override {
Mike Reedfab61982020-02-21 13:25:12 -0500358 return fRotateAnimator.isAnimating();
Mike Reed52638372020-02-19 13:30:06 -0500359 }
360
361private:
362 typedef Sample3DView INHERITED;
Mike Reedf0b7edf2020-01-18 14:21:12 -0500363};
Mike Reedcfee8ee2020-02-18 13:05:45 -0500364
365class SampleBump3D : public SampleCubeBase {
366 sk_sp<SkShader> fBmpShader, fImgShader;
367 sk_sp<SkRuntimeEffect> fEffect;
368 SkRRect fRR;
369
370public:
371 SampleBump3D() : SampleCubeBase(kShowLightDome) {}
372
373 SkString name() override { return SkString("bump3d"); }
374
375 void onOnceBeforeDraw() override {
376 fRR = SkRRect::MakeRectXY({20, 20, 380, 380}, 50, 50);
377 auto img = GetResourceAsImage("images/brickwork-texture.jpg");
378 fImgShader = img->makeShader(SkMatrix::MakeScale(2, 2));
379 img = GetResourceAsImage("images/brickwork_normal-map.jpg");
380 fBmpShader = img->makeShader(SkMatrix::MakeScale(2, 2));
381
382 const char code[] = R"(
383 in fragmentProcessor color_map;
384 in fragmentProcessor normal_map;
385
386 uniform float4x4 localToWorld;
387 uniform float4x4 localToWorldAdjInv;
388 uniform float3 lightPos;
389
390 float3 convert_normal_sample(half4 c) {
391 float3 n = 2 * c.rgb - 1;
392 n.y = -n.y;
393 return n;
394 }
395
396 void main(float2 p, inout half4 color) {
397 float3 norm = convert_normal_sample(sample(normal_map, p));
398 float3 plane_norm = normalize(localToWorld * float4(norm, 0)).xyz;
399
400 float3 plane_pos = (localToWorld * float4(p, 0, 1)).xyz;
401 float3 light_dir = normalize(lightPos - plane_pos);
402
403 float ambient = 0.2;
404 float dp = dot(plane_norm, light_dir);
405 float scale = min(ambient + max(dp, 0), 1);
406
407 color = sample(color_map, p) * half4(float4(scale, scale, scale, 1));
408 }
409 )";
410 auto [effect, error] = SkRuntimeEffect::Make(SkString(code));
411 if (!effect) {
412 SkDebugf("runtime error %s\n", error.c_str());
413 }
414 fEffect = effect;
415 }
416
417 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500418 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500419 return;
420 }
421
422 auto adj_inv = [](const SkM44& m) {
423 SkM44 inv;
424 SkAssertResult(m.invert(&inv));
425 return inv.transpose();
426 };
427
428 struct Uniforms {
429 SkM44 fLocalToWorld;
430 SkM44 fLocalToWorldAdjInv;
431 SkV3 fLightPos;
432 } uni;
433 uni.fLocalToWorld = canvas->experimental_getLocalToWorld();
434 uni.fLocalToWorldAdjInv = adj_inv(uni.fLocalToWorld);
435 uni.fLightPos = fLight.computeWorldPos(fSphere);
436
437 sk_sp<SkData> data = SkData::MakeWithCopy(&uni, sizeof(uni));
438 sk_sp<SkShader> children[] = { fImgShader, fBmpShader };
439
440 SkPaint paint;
441 paint.setColor(color);
442 paint.setShader(fEffect->makeShader(data, children, 2, nullptr, true));
443
444 canvas->drawRRect(fRR, paint);
445 }
446};
Mike Reede5809952020-01-25 20:42:51 -0500447DEF_SAMPLE( return new SampleBump3D; )
Mike Reedcfee8ee2020-02-18 13:05:45 -0500448
449#include "modules/skottie/include/Skottie.h"
450
451class SampleSkottieCube : public SampleCubeBase {
452 sk_sp<skottie::Animation> fAnim[6];
453
454public:
455 SampleSkottieCube() : SampleCubeBase(kCanRunOnCPU) {}
456
457 SkString name() override { return SkString("skottie3d"); }
458
459 void onOnceBeforeDraw() override {
460 const char* files[] = {
461 "skottie/skottie-chained-mattes.json",
462 "skottie/skottie-gradient-ramp.json",
463 "skottie/skottie_sample_2.json",
464 "skottie/skottie-3d-3planes.json",
465 "skottie/skottie-text-animator-4.json",
466 "skottie/skottie-motiontile-effect-phase.json",
467
468 };
469 for (unsigned i = 0; i < SK_ARRAY_COUNT(files); ++i) {
470 if (auto stream = GetResourceAsStream(files[i])) {
471 fAnim[i] = skottie::Animation::Make(stream.get());
472 }
473 }
474 }
475
476 void drawContent(SkCanvas* canvas, SkColor color, int index, bool drawFront) override {
Mike Reed46f5c5f2020-02-20 15:42:29 -0500477 if (!drawFront || !front(canvas->getLocalToDevice())) {
Mike Reedcfee8ee2020-02-18 13:05:45 -0500478 return;
479 }
480
481 SkPaint paint;
482 paint.setColor(color);
483 SkRect r = {0, 0, 400, 400};
484 canvas->drawRect(r, paint);
485 fAnim[index]->render(canvas, &r);
486 }
487
488 bool onAnimate(double nanos) override {
489 for (auto& anim : fAnim) {
490 SkScalar dur = anim->duration();
491 SkScalar t = fmod(1e-9 * nanos, dur) / dur;
492 anim->seek(t);
493 }
494 return true;
495 }
496};
497DEF_SAMPLE( return new SampleSkottieCube; )