xref: /aosp_15_r20/external/skia/modules/canvaskit/npm_build/extra.html (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker<!DOCTYPE html>
2*c8dee2aaSAndroid Build Coastguard Worker<title>CanvasKit Extra features (Skia via Web Assembly)</title>
3*c8dee2aaSAndroid Build Coastguard Worker<meta charset="utf-8" />
4*c8dee2aaSAndroid Build Coastguard Worker<meta http-equiv="X-UA-Compatible" content="IE=edge">
5*c8dee2aaSAndroid Build Coastguard Worker<meta name="viewport" content="width=device-width, initial-scale=1.0">
6*c8dee2aaSAndroid Build Coastguard Worker
7*c8dee2aaSAndroid Build Coastguard Worker<style>
8*c8dee2aaSAndroid Build Coastguard Worker  canvas {
9*c8dee2aaSAndroid Build Coastguard Worker    border: 1px dashed #AAA;
10*c8dee2aaSAndroid Build Coastguard Worker  }
11*c8dee2aaSAndroid Build Coastguard Worker  #sk_legos,#sk_drinks,#sk_party,#sk_onboarding, #sk_animated_gif {
12*c8dee2aaSAndroid Build Coastguard Worker      width: 300px;
13*c8dee2aaSAndroid Build Coastguard Worker      height: 300px;
14*c8dee2aaSAndroid Build Coastguard Worker  }
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Worker</style>
17*c8dee2aaSAndroid Build Coastguard Worker
18*c8dee2aaSAndroid Build Coastguard Worker<h2> Skottie </h2>
19*c8dee2aaSAndroid Build Coastguard Worker<canvas id=sk_legos width=300 height=300></canvas>
20*c8dee2aaSAndroid Build Coastguard Worker<canvas id=sk_drinks width=500 height=500></canvas>
21*c8dee2aaSAndroid Build Coastguard Worker<canvas id=sk_party width=500 height=500></canvas>
22*c8dee2aaSAndroid Build Coastguard Worker<canvas id=sk_onboarding width=500 height=500></canvas>
23*c8dee2aaSAndroid Build Coastguard Worker<canvas id=sk_animated_gif width=500 height=500
24*c8dee2aaSAndroid Build Coastguard Worker        title='This is an animated gif being animated in Skottie'></canvas>
25*c8dee2aaSAndroid Build Coastguard Worker
26*c8dee2aaSAndroid Build Coastguard Worker<h2> RT Shader </h2>
27*c8dee2aaSAndroid Build Coastguard Worker<canvas id=rtshader width=300 height=300></canvas>
28*c8dee2aaSAndroid Build Coastguard Worker<canvas id=rtshader2 width=300 height=300></canvas>
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker<h2> Paragraph </h2>
31*c8dee2aaSAndroid Build Coastguard Worker<canvas id=para1 width=600 height=600></canvas>
32*c8dee2aaSAndroid Build Coastguard Worker<canvas id=para2 width=600 height=600 tabindex='-1'></canvas>
33*c8dee2aaSAndroid Build Coastguard Worker<canvas id=para3 width=600 height=600 tabindex='-1'></canvas>
34*c8dee2aaSAndroid Build Coastguard Worker``
35*c8dee2aaSAndroid Build Coastguard Worker<h2> CanvasKit can serialize/deserialize .skp files</h2>
36*c8dee2aaSAndroid Build Coastguard Worker<canvas id=skp width=500 height=500></canvas>
37*c8dee2aaSAndroid Build Coastguard Worker
38*c8dee2aaSAndroid Build Coastguard Worker<h2> 3D perspective transformations </h2>
39*c8dee2aaSAndroid Build Coastguard Worker<canvas id=glyphgame width=500 height=500></canvas>
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker<h2> Support for extended color spaces </h2>
42*c8dee2aaSAndroid Build Coastguard Worker<a href="chrome://flags/#force-color-profile">Force P3 profile</a>
43*c8dee2aaSAndroid Build Coastguard Worker<canvas id=colorsupport width=300 height=300></canvas>
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" src="/build/canvaskit.js"></script>
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" src="textapi_utils.js"></script>
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" charset="utf-8">
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker  var CanvasKit = null;
52*c8dee2aaSAndroid Build Coastguard Worker  var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker  const ckLoaded = CanvasKitInit({locateFile: (file) => '/build/'+file});
55*c8dee2aaSAndroid Build Coastguard Worker
56*c8dee2aaSAndroid Build Coastguard Worker  const loadLegoJSON = fetch(cdn + 'lego_loader.json').then((response) => response.text());
57*c8dee2aaSAndroid Build Coastguard Worker  const loadDrinksJSON = fetch(cdn + 'drinks.json').then((response) => response.text());
58*c8dee2aaSAndroid Build Coastguard Worker  const loadConfettiJSON = fetch(cdn + 'confetti.json').then((response) => response.text());
59*c8dee2aaSAndroid Build Coastguard Worker  const loadOnboardingJSON = fetch(cdn + 'onboarding.json').then((response) => response.text());
60*c8dee2aaSAndroid Build Coastguard Worker  const loadMultiframeJSON = fetch(cdn + 'skottie_sample_multiframe.json').then((response) => response.text());
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker  const loadFlightGif = fetch(cdn + 'flightAnim.gif').then((response) => response.arrayBuffer());
63*c8dee2aaSAndroid Build Coastguard Worker  const loadFont = fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer());
64*c8dee2aaSAndroid Build Coastguard Worker  const loadDog = fetch(cdn + 'dog.jpg').then((response) => response.arrayBuffer());
65*c8dee2aaSAndroid Build Coastguard Worker  const loadMandrill = fetch(cdn + 'mandrill_256.png').then((response) => response.arrayBuffer());
66*c8dee2aaSAndroid Build Coastguard Worker  const loadBrickTex = fetch(cdn + 'brickwork-texture.jpg').then((response) => response.arrayBuffer());
67*c8dee2aaSAndroid Build Coastguard Worker  const loadBrickBump = fetch(cdn + 'brickwork_normal-map.jpg').then((response) => response.arrayBuffer());
68*c8dee2aaSAndroid Build Coastguard Worker
69*c8dee2aaSAndroid Build Coastguard Worker  // Examples which only require canvaskit
70*c8dee2aaSAndroid Build Coastguard Worker  ckLoaded.then((CK) => {
71*c8dee2aaSAndroid Build Coastguard Worker    CanvasKit = CK;
72*c8dee2aaSAndroid Build Coastguard Worker    RTShaderAPI1(CanvasKit);
73*c8dee2aaSAndroid Build Coastguard Worker    ColorSupport(CanvasKit);
74*c8dee2aaSAndroid Build Coastguard Worker    SkpExample(CanvasKit);
75*c8dee2aaSAndroid Build Coastguard Worker  });
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker  // Examples requiring external resources.
78*c8dee2aaSAndroid Build Coastguard Worker  // Set bounds to fix the 4:3 resolution of the legos
79*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadLegoJSON]).then(([ck, jsonstr]) => {
80*c8dee2aaSAndroid Build Coastguard Worker    SkottieExample(ck, 'sk_legos', jsonstr, [-50, 0, 350, 300]);
81*c8dee2aaSAndroid Build Coastguard Worker  });
82*c8dee2aaSAndroid Build Coastguard Worker  // Re-size to fit
83*c8dee2aaSAndroid Build Coastguard Worker  let fullBounds = [0, 0, 500, 500];
84*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadDrinksJSON]).then(([ck, jsonstr]) => {
85*c8dee2aaSAndroid Build Coastguard Worker    SkottieExample(ck, 'sk_drinks', jsonstr, fullBounds);
86*c8dee2aaSAndroid Build Coastguard Worker  });
87*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadConfettiJSON]).then(([ck, jsonstr]) => {
88*c8dee2aaSAndroid Build Coastguard Worker    SkottieExample(ck, 'sk_party', jsonstr, fullBounds);
89*c8dee2aaSAndroid Build Coastguard Worker  });
90*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadOnboardingJSON]).then(([ck, jsonstr]) => {
91*c8dee2aaSAndroid Build Coastguard Worker    SkottieExample(ck, 'sk_onboarding', jsonstr, fullBounds);
92*c8dee2aaSAndroid Build Coastguard Worker  });
93*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadMultiframeJSON, loadFlightGif]).then(([ck, jsonstr, gif]) => {
94*c8dee2aaSAndroid Build Coastguard Worker    SkottieExample(ck, 'sk_animated_gif', jsonstr, fullBounds, {'image_0.png': gif});
95*c8dee2aaSAndroid Build Coastguard Worker  });
96*c8dee2aaSAndroid Build Coastguard Worker
97*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadFont]).then((results) => {
98*c8dee2aaSAndroid Build Coastguard Worker    ParagraphAPI1(...results);
99*c8dee2aaSAndroid Build Coastguard Worker    ParagraphAPI2(...results);
100*c8dee2aaSAndroid Build Coastguard Worker    ParagraphAPI3(...results);
101*c8dee2aaSAndroid Build Coastguard Worker    GlyphGame(...results)
102*c8dee2aaSAndroid Build Coastguard Worker  });
103*c8dee2aaSAndroid Build Coastguard Worker
104*c8dee2aaSAndroid Build Coastguard Worker  const rectLeft = 0;
105*c8dee2aaSAndroid Build Coastguard Worker  const rectTop = 1;
106*c8dee2aaSAndroid Build Coastguard Worker  const rectRight = 2;
107*c8dee2aaSAndroid Build Coastguard Worker  const rectBottom = 3;
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker  function SkottieExample(CanvasKit, id, jsonStr, bounds, assets) {
110*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit || !jsonStr) {
111*c8dee2aaSAndroid Build Coastguard Worker      return;
112*c8dee2aaSAndroid Build Coastguard Worker    }
113*c8dee2aaSAndroid Build Coastguard Worker    const animation = CanvasKit.MakeManagedAnimation(jsonStr, assets);
114*c8dee2aaSAndroid Build Coastguard Worker    const duration = animation.duration() * 1000;
115*c8dee2aaSAndroid Build Coastguard Worker    const size = animation.size();
116*c8dee2aaSAndroid Build Coastguard Worker    let c = document.getElementById(id);
117*c8dee2aaSAndroid Build Coastguard Worker    bounds = bounds || CanvasKit.LTRBRect(0, 0, size.w, size.h);
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker    // Basic managed animation test.
120*c8dee2aaSAndroid Build Coastguard Worker    if (id === 'sk_drinks') {
121*c8dee2aaSAndroid Build Coastguard Worker      animation.setColor('BACKGROUND_FILL', CanvasKit.Color(0, 163, 199, 1.0));
122*c8dee2aaSAndroid Build Coastguard Worker    }
123*c8dee2aaSAndroid Build Coastguard Worker
124*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface(id);
125*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
126*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
127*c8dee2aaSAndroid Build Coastguard Worker      return;
128*c8dee2aaSAndroid Build Coastguard Worker    }
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker    let firstFrame = Date.now();
131*c8dee2aaSAndroid Build Coastguard Worker
132*c8dee2aaSAndroid Build Coastguard Worker    function drawFrame(canvas) {
133*c8dee2aaSAndroid Build Coastguard Worker      let seek = ((Date.now() - firstFrame) / duration) % 1.0;
134*c8dee2aaSAndroid Build Coastguard Worker      let damage = animation.seek(seek);
135*c8dee2aaSAndroid Build Coastguard Worker
136*c8dee2aaSAndroid Build Coastguard Worker      if (damage[rectRight] > damage[rectLeft] && damage[rectBottom] > damage[rectTop]) {
137*c8dee2aaSAndroid Build Coastguard Worker        canvas.clear(CanvasKit.WHITE);
138*c8dee2aaSAndroid Build Coastguard Worker        animation.render(canvas, bounds);
139*c8dee2aaSAndroid Build Coastguard Worker      }
140*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
141*c8dee2aaSAndroid Build Coastguard Worker    }
142*c8dee2aaSAndroid Build Coastguard Worker    surface.requestAnimationFrame(drawFrame);
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker    return surface;
145*c8dee2aaSAndroid Build Coastguard Worker  }
146*c8dee2aaSAndroid Build Coastguard Worker
147*c8dee2aaSAndroid Build Coastguard Worker  function ParagraphAPI1(CanvasKit, fontData) {
148*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit || !fontData) {
149*c8dee2aaSAndroid Build Coastguard Worker      return;
150*c8dee2aaSAndroid Build Coastguard Worker    }
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('para1');
153*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
154*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
155*c8dee2aaSAndroid Build Coastguard Worker      return;
156*c8dee2aaSAndroid Build Coastguard Worker    }
157*c8dee2aaSAndroid Build Coastguard Worker
158*c8dee2aaSAndroid Build Coastguard Worker    const canvas = surface.getCanvas();
159*c8dee2aaSAndroid Build Coastguard Worker    const fontMgr = CanvasKit.FontMgr.FromData([fontData]);
160*c8dee2aaSAndroid Build Coastguard Worker
161*c8dee2aaSAndroid Build Coastguard Worker    const paraStyle = new CanvasKit.ParagraphStyle({
162*c8dee2aaSAndroid Build Coastguard Worker        textStyle: {
163*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.BLACK,
164*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto'],
165*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 50,
166*c8dee2aaSAndroid Build Coastguard Worker        },
167*c8dee2aaSAndroid Build Coastguard Worker        textAlign: CanvasKit.TextAlign.Left,
168*c8dee2aaSAndroid Build Coastguard Worker        maxLines: 5,
169*c8dee2aaSAndroid Build Coastguard Worker    });
170*c8dee2aaSAndroid Build Coastguard Worker
171*c8dee2aaSAndroid Build Coastguard Worker    const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
172*c8dee2aaSAndroid Build Coastguard Worker    builder.addText('The quick brown fox ate a hamburgerfons and got sick.');
173*c8dee2aaSAndroid Build Coastguard Worker    const paragraph = builder.build();
174*c8dee2aaSAndroid Build Coastguard Worker
175*c8dee2aaSAndroid Build Coastguard Worker    let wrapTo = 0;
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker    let X = 100;
178*c8dee2aaSAndroid Build Coastguard Worker    let Y = 100;
179*c8dee2aaSAndroid Build Coastguard Worker
180*c8dee2aaSAndroid Build Coastguard Worker    const fontPaint = new CanvasKit.Paint();
181*c8dee2aaSAndroid Build Coastguard Worker    fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
182*c8dee2aaSAndroid Build Coastguard Worker    fontPaint.setAntiAlias(true);
183*c8dee2aaSAndroid Build Coastguard Worker
184*c8dee2aaSAndroid Build Coastguard Worker    function drawFrame(canvas) {
185*c8dee2aaSAndroid Build Coastguard Worker      canvas.clear(CanvasKit.WHITE);
186*c8dee2aaSAndroid Build Coastguard Worker      wrapTo = 350 + 150 * Math.sin(Date.now() / 2000);
187*c8dee2aaSAndroid Build Coastguard Worker      paragraph.layout(wrapTo);
188*c8dee2aaSAndroid Build Coastguard Worker      canvas.drawParagraph(paragraph, 0, 0);
189*c8dee2aaSAndroid Build Coastguard Worker
190*c8dee2aaSAndroid Build Coastguard Worker      canvas.drawLine(wrapTo, 0, wrapTo, 400, fontPaint);
191*c8dee2aaSAndroid Build Coastguard Worker
192*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
193*c8dee2aaSAndroid Build Coastguard Worker    }
194*c8dee2aaSAndroid Build Coastguard Worker    surface.requestAnimationFrame(drawFrame);
195*c8dee2aaSAndroid Build Coastguard Worker
196*c8dee2aaSAndroid Build Coastguard Worker    let interact = (e) => {
197*c8dee2aaSAndroid Build Coastguard Worker      X = e.offsetX*2; // multiply by 2 because the canvas is 300 css pixels wide,
198*c8dee2aaSAndroid Build Coastguard Worker      Y = e.offsetY*2; // but the canvas itself is 600px wide
199*c8dee2aaSAndroid Build Coastguard Worker    };
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker    document.getElementById('para1').addEventListener('pointermove', interact);
202*c8dee2aaSAndroid Build Coastguard Worker    return surface;
203*c8dee2aaSAndroid Build Coastguard Worker  }
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Worker  function ParagraphAPI2(CanvasKit, fontData) {
206*c8dee2aaSAndroid Build Coastguard Worker      if (!CanvasKit || !fontData) {
207*c8dee2aaSAndroid Build Coastguard Worker        return;
208*c8dee2aaSAndroid Build Coastguard Worker      }
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker      const surface = CanvasKit.MakeCanvasSurface('para2');
211*c8dee2aaSAndroid Build Coastguard Worker      if (!surface) {
212*c8dee2aaSAndroid Build Coastguard Worker        console.error('Could not make surface');
213*c8dee2aaSAndroid Build Coastguard Worker        return;
214*c8dee2aaSAndroid Build Coastguard Worker      }
215*c8dee2aaSAndroid Build Coastguard Worker
216*c8dee2aaSAndroid Build Coastguard Worker      const mouse = MakeMouse();
217*c8dee2aaSAndroid Build Coastguard Worker      const cursor = MakeCursor(CanvasKit);
218*c8dee2aaSAndroid Build Coastguard Worker      const canvas = surface.getCanvas();
219*c8dee2aaSAndroid Build Coastguard Worker
220*c8dee2aaSAndroid Build Coastguard Worker      const text0 = "In a hole in the ground there lived a hobbit. Not a nasty, dirty, " +
221*c8dee2aaSAndroid Build Coastguard Worker                    "wet hole full of worms and oozy smells. This was a hobbit-hole and " +
222*c8dee2aaSAndroid Build Coastguard Worker                    "that means good food, a warm hearth, and all the comforts of home.";
223*c8dee2aaSAndroid Build Coastguard Worker      const LOC_X = 20,
224*c8dee2aaSAndroid Build Coastguard Worker            LOC_Y = 20;
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker      const bgPaint = new CanvasKit.Paint();
227*c8dee2aaSAndroid Build Coastguard Worker      bgPaint.setColor([0.965, 0.965, 0.965, 1]);
228*c8dee2aaSAndroid Build Coastguard Worker
229*c8dee2aaSAndroid Build Coastguard Worker      const editor = MakeEditor(text0, {typeface:null, size:24}, cursor, 400);
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker      editor.applyStyleToRange({size:100}, 0, 1);
232*c8dee2aaSAndroid Build Coastguard Worker      editor.applyStyleToRange({italic:true}, 38, 38+6);
233*c8dee2aaSAndroid Build Coastguard Worker      editor.applyStyleToRange({color:[1,0,0,1]}, 5, 5+4);
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker      editor.setXY(LOC_X, LOC_Y);
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker      function drawFrame(canvas) {
238*c8dee2aaSAndroid Build Coastguard Worker        const lines = editor.getLines();
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker        canvas.clear(CanvasKit.WHITE);
241*c8dee2aaSAndroid Build Coastguard Worker
242*c8dee2aaSAndroid Build Coastguard Worker        if (mouse.isActive()) {
243*c8dee2aaSAndroid Build Coastguard Worker            const pos = mouse.getPos(-LOC_X, -LOC_Y);
244*c8dee2aaSAndroid Build Coastguard Worker            const a = lines_pos_to_index(lines, pos[0], pos[1]);
245*c8dee2aaSAndroid Build Coastguard Worker            const b = lines_pos_to_index(lines, pos[2], pos[3]);
246*c8dee2aaSAndroid Build Coastguard Worker            if (a == b) {
247*c8dee2aaSAndroid Build Coastguard Worker                editor.setIndex(a);
248*c8dee2aaSAndroid Build Coastguard Worker            } else {
249*c8dee2aaSAndroid Build Coastguard Worker                editor.setIndices(a, b);
250*c8dee2aaSAndroid Build Coastguard Worker            }
251*c8dee2aaSAndroid Build Coastguard Worker        }
252*c8dee2aaSAndroid Build Coastguard Worker
253*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawRect(editor.bounds(), bgPaint);
254*c8dee2aaSAndroid Build Coastguard Worker        editor.draw(canvas);
255*c8dee2aaSAndroid Build Coastguard Worker
256*c8dee2aaSAndroid Build Coastguard Worker        surface.requestAnimationFrame(drawFrame);
257*c8dee2aaSAndroid Build Coastguard Worker      }
258*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
259*c8dee2aaSAndroid Build Coastguard Worker
260*c8dee2aaSAndroid Build Coastguard Worker      function interact(e) {
261*c8dee2aaSAndroid Build Coastguard Worker        const type = e.type;
262*c8dee2aaSAndroid Build Coastguard Worker        if (type === 'pointerup') {
263*c8dee2aaSAndroid Build Coastguard Worker            mouse.setUp(e.offsetX, e.offsetY);
264*c8dee2aaSAndroid Build Coastguard Worker        } else if (type === 'pointermove') {
265*c8dee2aaSAndroid Build Coastguard Worker            mouse.setMove(e.offsetX, e.offsetY);
266*c8dee2aaSAndroid Build Coastguard Worker        } else if (type === 'pointerdown') {
267*c8dee2aaSAndroid Build Coastguard Worker            mouse.setDown(e.offsetX, e.offsetY);
268*c8dee2aaSAndroid Build Coastguard Worker        }
269*c8dee2aaSAndroid Build Coastguard Worker      };
270*c8dee2aaSAndroid Build Coastguard Worker
271*c8dee2aaSAndroid Build Coastguard Worker      function keyhandler(e) {
272*c8dee2aaSAndroid Build Coastguard Worker          switch (e.key) {
273*c8dee2aaSAndroid Build Coastguard Worker              case 'ArrowLeft':  editor.moveDX(-1); return;
274*c8dee2aaSAndroid Build Coastguard Worker              case 'ArrowRight': editor.moveDX(1); return;
275*c8dee2aaSAndroid Build Coastguard Worker              case 'ArrowUp':
276*c8dee2aaSAndroid Build Coastguard Worker                e.preventDefault();
277*c8dee2aaSAndroid Build Coastguard Worker                editor.moveDY(-1);
278*c8dee2aaSAndroid Build Coastguard Worker                return;
279*c8dee2aaSAndroid Build Coastguard Worker              case 'ArrowDown':
280*c8dee2aaSAndroid Build Coastguard Worker                e.preventDefault();
281*c8dee2aaSAndroid Build Coastguard Worker                editor.moveDY(1);
282*c8dee2aaSAndroid Build Coastguard Worker                return;
283*c8dee2aaSAndroid Build Coastguard Worker              case 'Backspace':
284*c8dee2aaSAndroid Build Coastguard Worker                editor.deleteSelection();
285*c8dee2aaSAndroid Build Coastguard Worker                return;
286*c8dee2aaSAndroid Build Coastguard Worker              case 'Shift':
287*c8dee2aaSAndroid Build Coastguard Worker                return;
288*c8dee2aaSAndroid Build Coastguard Worker            }
289*c8dee2aaSAndroid Build Coastguard Worker            if (e.ctrlKey) {
290*c8dee2aaSAndroid Build Coastguard Worker                switch (e.key) {
291*c8dee2aaSAndroid Build Coastguard Worker                    case 'r': editor.applyStyleToSelection({color:[1,0,0,1]}); return;
292*c8dee2aaSAndroid Build Coastguard Worker                    case 'g': editor.applyStyleToSelection({color:[0,0.6,0,1]}); return;
293*c8dee2aaSAndroid Build Coastguard Worker                    case 'u': editor.applyStyleToSelection({color:[0,0,1,1]}); return;
294*c8dee2aaSAndroid Build Coastguard Worker                    case 'k': editor.applyStyleToSelection({color:[0,0,0,1]}); return;
295*c8dee2aaSAndroid Build Coastguard Worker
296*c8dee2aaSAndroid Build Coastguard Worker                    case 'i': editor.applyStyleToSelection({italic:'toggle'}); return;
297*c8dee2aaSAndroid Build Coastguard Worker                    case 'b': editor.applyStyleToSelection({bold:'toggle'}); return;
298*c8dee2aaSAndroid Build Coastguard Worker                    case 'l': editor.applyStyleToSelection({underline:'toggle'}); return;
299*c8dee2aaSAndroid Build Coastguard Worker
300*c8dee2aaSAndroid Build Coastguard Worker                    case ']': editor.applyStyleToSelection({size_add:1}); return;
301*c8dee2aaSAndroid Build Coastguard Worker                    case '[': editor.applyStyleToSelection({size_add:-1}); return;
302*c8dee2aaSAndroid Build Coastguard Worker                }
303*c8dee2aaSAndroid Build Coastguard Worker            }
304*c8dee2aaSAndroid Build Coastguard Worker            if (!e.ctrlKey && !e.metaKey) {
305*c8dee2aaSAndroid Build Coastguard Worker                e.preventDefault(); // at least needed for 'space'
306*c8dee2aaSAndroid Build Coastguard Worker                editor.insert(e.key);
307*c8dee2aaSAndroid Build Coastguard Worker            }
308*c8dee2aaSAndroid Build Coastguard Worker      }
309*c8dee2aaSAndroid Build Coastguard Worker
310*c8dee2aaSAndroid Build Coastguard Worker      document.getElementById('para2').addEventListener('pointermove', interact);
311*c8dee2aaSAndroid Build Coastguard Worker      document.getElementById('para2').addEventListener('pointerdown', interact);
312*c8dee2aaSAndroid Build Coastguard Worker      document.getElementById('para2').addEventListener('pointerup', interact);
313*c8dee2aaSAndroid Build Coastguard Worker      document.getElementById('para2').addEventListener('keydown', keyhandler);
314*c8dee2aaSAndroid Build Coastguard Worker      return surface;
315*c8dee2aaSAndroid Build Coastguard Worker    }
316*c8dee2aaSAndroid Build Coastguard Worker
317*c8dee2aaSAndroid Build Coastguard Worker  function ParagraphAPI3(CanvasKit, fontData) {
318*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit || !fontData) {
319*c8dee2aaSAndroid Build Coastguard Worker      return;
320*c8dee2aaSAndroid Build Coastguard Worker    }
321*c8dee2aaSAndroid Build Coastguard Worker
322*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('para3');
323*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
324*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
325*c8dee2aaSAndroid Build Coastguard Worker      return;
326*c8dee2aaSAndroid Build Coastguard Worker    }
327*c8dee2aaSAndroid Build Coastguard Worker
328*c8dee2aaSAndroid Build Coastguard Worker    const fontMgr = CanvasKit.FontMgr.FromData([fontData]);
329*c8dee2aaSAndroid Build Coastguard Worker
330*c8dee2aaSAndroid Build Coastguard Worker    const paraStyle = new CanvasKit.ParagraphStyle({
331*c8dee2aaSAndroid Build Coastguard Worker        textStyle: {
332*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.BLACK,
333*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto'],
334*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 50,
335*c8dee2aaSAndroid Build Coastguard Worker        },
336*c8dee2aaSAndroid Build Coastguard Worker        textAlign: CanvasKit.TextAlign.Left,
337*c8dee2aaSAndroid Build Coastguard Worker        maxLines: 5,
338*c8dee2aaSAndroid Build Coastguard Worker    });
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker    const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
341*c8dee2aaSAndroid Build Coastguard Worker    builder.addText('The quick brown fox ate a hamburgerfons and got sick.');
342*c8dee2aaSAndroid Build Coastguard Worker
343*c8dee2aaSAndroid Build Coastguard Worker    // The code below works for English-only text assuming
344*c8dee2aaSAndroid Build Coastguard Worker    // that each character is a glyph (cluster), spaces are breaks between words,
345*c8dee2aaSAndroid Build Coastguard Worker    // new lines are breaks between lines and the entire text is LTR.
346*c8dee2aaSAndroid Build Coastguard Worker
347*c8dee2aaSAndroid Build Coastguard Worker    // In case the paragraph text was constructed in a set of calls we need the text
348*c8dee2aaSAndroid Build Coastguard Worker        const text = builder.getText();
349*c8dee2aaSAndroid Build Coastguard Worker
350*c8dee2aaSAndroid Build Coastguard Worker        // Pass the entire text as one word. It's only used for the method
351*c8dee2aaSAndroid Build Coastguard Worker        // getWords
352*c8dee2aaSAndroid Build Coastguard Worker        const mallocedWords = CanvasKit.Malloc(Uint32Array, 2);
353*c8dee2aaSAndroid Build Coastguard Worker        mallocedWords.toTypedArray().set([0, text.length]);
354*c8dee2aaSAndroid Build Coastguard Worker
355*c8dee2aaSAndroid Build Coastguard Worker        // Pass each character as a separate grapheme
356*c8dee2aaSAndroid Build Coastguard Worker        const mallocedGraphemes = CanvasKit.Malloc(Uint32Array, text.length + 1);
357*c8dee2aaSAndroid Build Coastguard Worker        const graphemesArr = mallocedGraphemes.toTypedArray();
358*c8dee2aaSAndroid Build Coastguard Worker        for (let i = 0; i <= text.length; i++) {
359*c8dee2aaSAndroid Build Coastguard Worker            graphemesArr[i] = i;
360*c8dee2aaSAndroid Build Coastguard Worker        }
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker        // Pass each space as a "soft" break and each new line as a "hard" break.
363*c8dee2aaSAndroid Build Coastguard Worker        const SOFT = 0;
364*c8dee2aaSAndroid Build Coastguard Worker        const HARD = 1;
365*c8dee2aaSAndroid Build Coastguard Worker        const lineBreaks = [0, SOFT];
366*c8dee2aaSAndroid Build Coastguard Worker        for (let i = 0; i < text.length; ++i) {
367*c8dee2aaSAndroid Build Coastguard Worker          if (text[i] === ' ') {
368*c8dee2aaSAndroid Build Coastguard Worker              lineBreaks.push(i + 1, SOFT);
369*c8dee2aaSAndroid Build Coastguard Worker          }
370*c8dee2aaSAndroid Build Coastguard Worker          if (text[i] === '\n') {
371*c8dee2aaSAndroid Build Coastguard Worker              lineBreaks.push(i + 1, HARD);
372*c8dee2aaSAndroid Build Coastguard Worker          }
373*c8dee2aaSAndroid Build Coastguard Worker        }
374*c8dee2aaSAndroid Build Coastguard Worker        lineBreaks.push(text.length, SOFT);
375*c8dee2aaSAndroid Build Coastguard Worker        const mallocedLineBreaks = CanvasKit.Malloc(Uint32Array, lineBreaks.length);
376*c8dee2aaSAndroid Build Coastguard Worker        mallocedLineBreaks.toTypedArray().set(lineBreaks);
377*c8dee2aaSAndroid Build Coastguard Worker
378*c8dee2aaSAndroid Build Coastguard Worker        builder.setWordsUtf16(mallocedWords);
379*c8dee2aaSAndroid Build Coastguard Worker        builder.setGraphemeBreaksUtf16(mallocedGraphemes);
380*c8dee2aaSAndroid Build Coastguard Worker        builder.setLineBreaksUtf16(mallocedLineBreaks);
381*c8dee2aaSAndroid Build Coastguard Worker        const paragraph = builder.build();
382*c8dee2aaSAndroid Build Coastguard Worker
383*c8dee2aaSAndroid Build Coastguard Worker        paragraph.layout(600);
384*c8dee2aaSAndroid Build Coastguard Worker
385*c8dee2aaSAndroid Build Coastguard Worker    let wrapTo = 0;
386*c8dee2aaSAndroid Build Coastguard Worker
387*c8dee2aaSAndroid Build Coastguard Worker    let X = 100;
388*c8dee2aaSAndroid Build Coastguard Worker    let Y = 100;
389*c8dee2aaSAndroid Build Coastguard Worker
390*c8dee2aaSAndroid Build Coastguard Worker    const fontPaint = new CanvasKit.Paint();
391*c8dee2aaSAndroid Build Coastguard Worker    fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
392*c8dee2aaSAndroid Build Coastguard Worker    fontPaint.setAntiAlias(true);
393*c8dee2aaSAndroid Build Coastguard Worker
394*c8dee2aaSAndroid Build Coastguard Worker    function drawFrame(canvas) {
395*c8dee2aaSAndroid Build Coastguard Worker      canvas.clear(CanvasKit.WHITE);
396*c8dee2aaSAndroid Build Coastguard Worker      wrapTo = 350 + 150 * Math.sin(Date.now() / 2000);
397*c8dee2aaSAndroid Build Coastguard Worker      paragraph.layout(wrapTo);
398*c8dee2aaSAndroid Build Coastguard Worker      canvas.drawParagraph(paragraph, 0, 0);
399*c8dee2aaSAndroid Build Coastguard Worker
400*c8dee2aaSAndroid Build Coastguard Worker      canvas.drawLine(wrapTo, 0, wrapTo, 400, fontPaint);
401*c8dee2aaSAndroid Build Coastguard Worker
402*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
403*c8dee2aaSAndroid Build Coastguard Worker    }
404*c8dee2aaSAndroid Build Coastguard Worker    surface.requestAnimationFrame(drawFrame);
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker    let interact = (e) => {
407*c8dee2aaSAndroid Build Coastguard Worker      X = e.offsetX*2; // multiply by 2 because the canvas is 300 css pixels wide,
408*c8dee2aaSAndroid Build Coastguard Worker      Y = e.offsetY*2; // but the canvas itself is 600px wide
409*c8dee2aaSAndroid Build Coastguard Worker    };
410*c8dee2aaSAndroid Build Coastguard Worker
411*c8dee2aaSAndroid Build Coastguard Worker    document.getElementById('para1').addEventListener('pointermove', interact);
412*c8dee2aaSAndroid Build Coastguard Worker
413*c8dee2aaSAndroid Build Coastguard Worker    return surface;
414*c8dee2aaSAndroid Build Coastguard Worker  }
415*c8dee2aaSAndroid Build Coastguard Worker
416*c8dee2aaSAndroid Build Coastguard Worker  function RTShaderAPI1(CanvasKit) {
417*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit) {
418*c8dee2aaSAndroid Build Coastguard Worker      return;
419*c8dee2aaSAndroid Build Coastguard Worker    }
420*c8dee2aaSAndroid Build Coastguard Worker
421*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('rtshader');
422*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
423*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
424*c8dee2aaSAndroid Build Coastguard Worker      return;
425*c8dee2aaSAndroid Build Coastguard Worker    }
426*c8dee2aaSAndroid Build Coastguard Worker
427*c8dee2aaSAndroid Build Coastguard Worker    const canvas = surface.getCanvas();
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker    const effect = CanvasKit.RuntimeEffect.Make(spiralSkSL);
430*c8dee2aaSAndroid Build Coastguard Worker    const shader = effect.makeShader([
431*c8dee2aaSAndroid Build Coastguard Worker      0.5,
432*c8dee2aaSAndroid Build Coastguard Worker      150, 150,
433*c8dee2aaSAndroid Build Coastguard Worker      0, 1, 0, 1,
434*c8dee2aaSAndroid Build Coastguard Worker      1, 0, 0, 1]);
435*c8dee2aaSAndroid Build Coastguard Worker    const paint = new CanvasKit.Paint();
436*c8dee2aaSAndroid Build Coastguard Worker    paint.setShader(shader);
437*c8dee2aaSAndroid Build Coastguard Worker    canvas.drawRect(CanvasKit.LTRBRect(0, 0, 300, 300), paint);
438*c8dee2aaSAndroid Build Coastguard Worker
439*c8dee2aaSAndroid Build Coastguard Worker    surface.flush();
440*c8dee2aaSAndroid Build Coastguard Worker    shader.delete();
441*c8dee2aaSAndroid Build Coastguard Worker    paint.delete();
442*c8dee2aaSAndroid Build Coastguard Worker    effect.delete();
443*c8dee2aaSAndroid Build Coastguard Worker  }
444*c8dee2aaSAndroid Build Coastguard Worker
445*c8dee2aaSAndroid Build Coastguard Worker  // RTShader2 demo
446*c8dee2aaSAndroid Build Coastguard Worker  Promise.all([ckLoaded, loadDog, loadMandrill]).then((values) => {
447*c8dee2aaSAndroid Build Coastguard Worker    const [CanvasKit, dogData, mandrillData] = values;
448*c8dee2aaSAndroid Build Coastguard Worker    const dogImg = CanvasKit.MakeImageFromEncoded(dogData);
449*c8dee2aaSAndroid Build Coastguard Worker    if (!dogImg) {
450*c8dee2aaSAndroid Build Coastguard Worker      console.error('could not decode dog');
451*c8dee2aaSAndroid Build Coastguard Worker      return;
452*c8dee2aaSAndroid Build Coastguard Worker    }
453*c8dee2aaSAndroid Build Coastguard Worker    const mandrillImg = CanvasKit.MakeImageFromEncoded(mandrillData);
454*c8dee2aaSAndroid Build Coastguard Worker    if (!mandrillImg) {
455*c8dee2aaSAndroid Build Coastguard Worker      console.error('could not decode mandrill');
456*c8dee2aaSAndroid Build Coastguard Worker      return;
457*c8dee2aaSAndroid Build Coastguard Worker    }
458*c8dee2aaSAndroid Build Coastguard Worker    const quadrantSize = 150;
459*c8dee2aaSAndroid Build Coastguard Worker
460*c8dee2aaSAndroid Build Coastguard Worker    const dogShader = dogImg.makeShaderCubic(
461*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp,
462*c8dee2aaSAndroid Build Coastguard Worker        1/3, 1/3,
463*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.Matrix.scaled(quadrantSize/dogImg.width(),
464*c8dee2aaSAndroid Build Coastguard Worker        quadrantSize/dogImg.height()));
465*c8dee2aaSAndroid Build Coastguard Worker    const mandrillShader = mandrillImg.makeShaderCubic(
466*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp,
467*c8dee2aaSAndroid Build Coastguard Worker        1/3, 1/3,
468*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.Matrix.scaled(
469*c8dee2aaSAndroid Build Coastguard Worker            quadrantSize/mandrillImg.width(),
470*c8dee2aaSAndroid Build Coastguard Worker            quadrantSize/mandrillImg.height()));
471*c8dee2aaSAndroid Build Coastguard Worker
472*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('rtshader2');
473*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
474*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
475*c8dee2aaSAndroid Build Coastguard Worker      return;
476*c8dee2aaSAndroid Build Coastguard Worker    }
477*c8dee2aaSAndroid Build Coastguard Worker
478*c8dee2aaSAndroid Build Coastguard Worker    const prog = `
479*c8dee2aaSAndroid Build Coastguard Worker      uniform shader before_map;
480*c8dee2aaSAndroid Build Coastguard Worker      uniform shader after_map;
481*c8dee2aaSAndroid Build Coastguard Worker      uniform shader threshold_map;
482*c8dee2aaSAndroid Build Coastguard Worker
483*c8dee2aaSAndroid Build Coastguard Worker      uniform float cutoff;
484*c8dee2aaSAndroid Build Coastguard Worker      uniform float slope;
485*c8dee2aaSAndroid Build Coastguard Worker
486*c8dee2aaSAndroid Build Coastguard Worker      float smooth_cutoff(float x) {
487*c8dee2aaSAndroid Build Coastguard Worker          x = x * slope + (0.5 - slope * cutoff);
488*c8dee2aaSAndroid Build Coastguard Worker          return clamp(x, 0, 1);
489*c8dee2aaSAndroid Build Coastguard Worker      }
490*c8dee2aaSAndroid Build Coastguard Worker
491*c8dee2aaSAndroid Build Coastguard Worker      half4 main(float2 xy) {
492*c8dee2aaSAndroid Build Coastguard Worker          half4 before = before_map.eval(xy);
493*c8dee2aaSAndroid Build Coastguard Worker          half4 after = after_map.eval(xy);
494*c8dee2aaSAndroid Build Coastguard Worker
495*c8dee2aaSAndroid Build Coastguard Worker          float m = smooth_cutoff(threshold_map.eval(xy).r);
496*c8dee2aaSAndroid Build Coastguard Worker          return mix(before, after, half(m));
497*c8dee2aaSAndroid Build Coastguard Worker      }`;
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker    const canvas = surface.getCanvas();
500*c8dee2aaSAndroid Build Coastguard Worker
501*c8dee2aaSAndroid Build Coastguard Worker    const thresholdEffect = CanvasKit.RuntimeEffect.Make(prog);
502*c8dee2aaSAndroid Build Coastguard Worker    const spiralEffect = CanvasKit.RuntimeEffect.Make(spiralSkSL);
503*c8dee2aaSAndroid Build Coastguard Worker
504*c8dee2aaSAndroid Build Coastguard Worker    const draw = (x, y, shader) => {
505*c8dee2aaSAndroid Build Coastguard Worker      const paint = new CanvasKit.Paint();
506*c8dee2aaSAndroid Build Coastguard Worker      paint.setShader(shader);
507*c8dee2aaSAndroid Build Coastguard Worker      canvas.save();
508*c8dee2aaSAndroid Build Coastguard Worker      canvas.translate(x, y);
509*c8dee2aaSAndroid Build Coastguard Worker      canvas.drawRect(CanvasKit.LTRBRect(0, 0, quadrantSize, quadrantSize), paint);
510*c8dee2aaSAndroid Build Coastguard Worker      canvas.restore();
511*c8dee2aaSAndroid Build Coastguard Worker      paint.delete();
512*c8dee2aaSAndroid Build Coastguard Worker    };
513*c8dee2aaSAndroid Build Coastguard Worker
514*c8dee2aaSAndroid Build Coastguard Worker    const offscreenSurface = CanvasKit.MakeSurface(quadrantSize, quadrantSize);
515*c8dee2aaSAndroid Build Coastguard Worker    const getBlurrySpiralShader = (rad_scale) => {
516*c8dee2aaSAndroid Build Coastguard Worker      const oCanvas = offscreenSurface.getCanvas();
517*c8dee2aaSAndroid Build Coastguard Worker
518*c8dee2aaSAndroid Build Coastguard Worker      const spiralShader = spiralEffect.makeShader([
519*c8dee2aaSAndroid Build Coastguard Worker      rad_scale,
520*c8dee2aaSAndroid Build Coastguard Worker      quadrantSize/2, quadrantSize/2,
521*c8dee2aaSAndroid Build Coastguard Worker      1, 1, 1, 1,
522*c8dee2aaSAndroid Build Coastguard Worker      0, 0, 0, 1]);
523*c8dee2aaSAndroid Build Coastguard Worker
524*c8dee2aaSAndroid Build Coastguard Worker      const blur = CanvasKit.ImageFilter.MakeBlur(0.1, 0.1, CanvasKit.TileMode.Clamp, null);
525*c8dee2aaSAndroid Build Coastguard Worker
526*c8dee2aaSAndroid Build Coastguard Worker      const paint = new CanvasKit.Paint();
527*c8dee2aaSAndroid Build Coastguard Worker      paint.setShader(spiralShader);
528*c8dee2aaSAndroid Build Coastguard Worker      paint.setImageFilter(blur);
529*c8dee2aaSAndroid Build Coastguard Worker      oCanvas.drawRect(CanvasKit.LTRBRect(0, 0, quadrantSize, quadrantSize), paint);
530*c8dee2aaSAndroid Build Coastguard Worker
531*c8dee2aaSAndroid Build Coastguard Worker      paint.delete();
532*c8dee2aaSAndroid Build Coastguard Worker      blur.delete();
533*c8dee2aaSAndroid Build Coastguard Worker      spiralShader.delete();
534*c8dee2aaSAndroid Build Coastguard Worker      return offscreenSurface.makeImageSnapshot()
535*c8dee2aaSAndroid Build Coastguard Worker                             .makeShaderCubic(CanvasKit.TileMode.Clamp, CanvasKit.TileMode.Clamp,
536*c8dee2aaSAndroid Build Coastguard Worker                             1/3, 1/3);
537*c8dee2aaSAndroid Build Coastguard Worker
538*c8dee2aaSAndroid Build Coastguard Worker    };
539*c8dee2aaSAndroid Build Coastguard Worker
540*c8dee2aaSAndroid Build Coastguard Worker    const drawFrame = () => {
541*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
542*c8dee2aaSAndroid Build Coastguard Worker      const thresholdShader = getBlurrySpiralShader(Math.sin(Date.now() / 5000) / 2);
543*c8dee2aaSAndroid Build Coastguard Worker
544*c8dee2aaSAndroid Build Coastguard Worker      const blendShader = thresholdEffect.makeShaderWithChildren(
545*c8dee2aaSAndroid Build Coastguard Worker        [0.5, 10],
546*c8dee2aaSAndroid Build Coastguard Worker        [dogShader, mandrillShader, thresholdShader]);
547*c8dee2aaSAndroid Build Coastguard Worker      draw(0, 0, blendShader);
548*c8dee2aaSAndroid Build Coastguard Worker      draw(quadrantSize, 0, thresholdShader);
549*c8dee2aaSAndroid Build Coastguard Worker      draw(0, quadrantSize, dogShader);
550*c8dee2aaSAndroid Build Coastguard Worker      draw(quadrantSize, quadrantSize, mandrillShader);
551*c8dee2aaSAndroid Build Coastguard Worker
552*c8dee2aaSAndroid Build Coastguard Worker      blendShader.delete();
553*c8dee2aaSAndroid Build Coastguard Worker    };
554*c8dee2aaSAndroid Build Coastguard Worker
555*c8dee2aaSAndroid Build Coastguard Worker    surface.requestAnimationFrame(drawFrame);
556*c8dee2aaSAndroid Build Coastguard Worker  });
557*c8dee2aaSAndroid Build Coastguard Worker
558*c8dee2aaSAndroid Build Coastguard Worker  function SkpExample(CanvasKit) {
559*c8dee2aaSAndroid Build Coastguard Worker    if (!CanvasKit) {
560*c8dee2aaSAndroid Build Coastguard Worker      return;
561*c8dee2aaSAndroid Build Coastguard Worker    }
562*c8dee2aaSAndroid Build Coastguard Worker
563*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeSWCanvasSurface('skp');
564*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
565*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
566*c8dee2aaSAndroid Build Coastguard Worker      return;
567*c8dee2aaSAndroid Build Coastguard Worker    }
568*c8dee2aaSAndroid Build Coastguard Worker
569*c8dee2aaSAndroid Build Coastguard Worker    const paint = new CanvasKit.Paint();
570*c8dee2aaSAndroid Build Coastguard Worker    paint.setColor(CanvasKit.RED);
571*c8dee2aaSAndroid Build Coastguard Worker
572*c8dee2aaSAndroid Build Coastguard Worker    const textPaint = new CanvasKit.Paint();
573*c8dee2aaSAndroid Build Coastguard Worker    const textFont = new CanvasKit.Font(CanvasKit.Typeface.GetDefault(), 20);
574*c8dee2aaSAndroid Build Coastguard Worker    const pr = new CanvasKit.PictureRecorder();
575*c8dee2aaSAndroid Build Coastguard Worker    const skpCanvas = pr.beginRecording(CanvasKit.LTRBRect(0, 0, 200, 200));
576*c8dee2aaSAndroid Build Coastguard Worker    skpCanvas.drawRect(CanvasKit.LTRBRect(10, 10, 50, 50), paint);
577*c8dee2aaSAndroid Build Coastguard Worker    skpCanvas.drawText('If you see this, CanvasKit loaded!!', 5, 100, textPaint, textFont);
578*c8dee2aaSAndroid Build Coastguard Worker
579*c8dee2aaSAndroid Build Coastguard Worker    const pic = pr.finishRecordingAsPicture();
580*c8dee2aaSAndroid Build Coastguard Worker    const skpData = pic.serialize();
581*c8dee2aaSAndroid Build Coastguard Worker
582*c8dee2aaSAndroid Build Coastguard Worker    paint.delete();
583*c8dee2aaSAndroid Build Coastguard Worker    pr.delete();
584*c8dee2aaSAndroid Build Coastguard Worker
585*c8dee2aaSAndroid Build Coastguard Worker    const deserialized = CanvasKit.MakePicture(skpData);
586*c8dee2aaSAndroid Build Coastguard Worker
587*c8dee2aaSAndroid Build Coastguard Worker    function drawFrame(canvas) {
588*c8dee2aaSAndroid Build Coastguard Worker      if (deserialized) {
589*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawPicture(deserialized);
590*c8dee2aaSAndroid Build Coastguard Worker      } else {
591*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawText('SKP did not deserialize', 5, 100, textPaint, textFont);
592*c8dee2aaSAndroid Build Coastguard Worker      }
593*c8dee2aaSAndroid Build Coastguard Worker    }
594*c8dee2aaSAndroid Build Coastguard Worker    surface.drawOnce(drawFrame);
595*c8dee2aaSAndroid Build Coastguard Worker    textPaint.delete();
596*c8dee2aaSAndroid Build Coastguard Worker    textFont.delete();
597*c8dee2aaSAndroid Build Coastguard Worker  }
598*c8dee2aaSAndroid Build Coastguard Worker
599*c8dee2aaSAndroid Build Coastguard Worker  // Shows a hidden message by rotating all the characters in a kind of way that makes you
600*c8dee2aaSAndroid Build Coastguard Worker  // search with your mouse.
601*c8dee2aaSAndroid Build Coastguard Worker  function GlyphGame(canvas, robotoData) {
602*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('glyphgame');
603*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
604*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
605*c8dee2aaSAndroid Build Coastguard Worker      return;
606*c8dee2aaSAndroid Build Coastguard Worker    }
607*c8dee2aaSAndroid Build Coastguard Worker    const sizeX = document.getElementById('glyphgame').width;
608*c8dee2aaSAndroid Build Coastguard Worker    const sizeY = document.getElementById('glyphgame').height;
609*c8dee2aaSAndroid Build Coastguard Worker    const halfDim = Math.min(sizeX, sizeY) / 2;
610*c8dee2aaSAndroid Build Coastguard Worker    const margin = 50;
611*c8dee2aaSAndroid Build Coastguard Worker    const marginTop = 25;
612*c8dee2aaSAndroid Build Coastguard Worker    let rotX = 0; //  expected to be updated in interact()
613*c8dee2aaSAndroid Build Coastguard Worker    let rotY = 0;
614*c8dee2aaSAndroid Build Coastguard Worker    let pointer = [500, 450];
615*c8dee2aaSAndroid Build Coastguard Worker    const radPerPixel = 0.005; // radians of subject rotation per pixel distance moved by mouse.
616*c8dee2aaSAndroid Build Coastguard Worker
617*c8dee2aaSAndroid Build Coastguard Worker    const camAngle = Math.PI / 12;
618*c8dee2aaSAndroid Build Coastguard Worker    const cam = {
619*c8dee2aaSAndroid Build Coastguard Worker      'eye'  : [0, 0, 1 / Math.tan(camAngle/2) - 1],
620*c8dee2aaSAndroid Build Coastguard Worker      'coa'  : [0, 0, 0],
621*c8dee2aaSAndroid Build Coastguard Worker      'up'   : [0, 1, 0],
622*c8dee2aaSAndroid Build Coastguard Worker      'near' : 0.02,
623*c8dee2aaSAndroid Build Coastguard Worker      'far'  : 4,
624*c8dee2aaSAndroid Build Coastguard Worker      'angle': camAngle,
625*c8dee2aaSAndroid Build Coastguard Worker    };
626*c8dee2aaSAndroid Build Coastguard Worker
627*c8dee2aaSAndroid Build Coastguard Worker    let lastImage = null;
628*c8dee2aaSAndroid Build Coastguard Worker
629*c8dee2aaSAndroid Build Coastguard Worker    const fontMgr = CanvasKit.FontMgr.FromData([robotoData]);
630*c8dee2aaSAndroid Build Coastguard Worker
631*c8dee2aaSAndroid Build Coastguard Worker    const paraStyle = new CanvasKit.ParagraphStyle({
632*c8dee2aaSAndroid Build Coastguard Worker        textStyle: {
633*c8dee2aaSAndroid Build Coastguard Worker            color: CanvasKit.Color(105, 56, 16), // brown
634*c8dee2aaSAndroid Build Coastguard Worker            fontFamilies: ['Roboto'],
635*c8dee2aaSAndroid Build Coastguard Worker            fontSize: 28,
636*c8dee2aaSAndroid Build Coastguard Worker        },
637*c8dee2aaSAndroid Build Coastguard Worker        textAlign: CanvasKit.TextAlign.Left,
638*c8dee2aaSAndroid Build Coastguard Worker    });
639*c8dee2aaSAndroid Build Coastguard Worker    const hStyle = CanvasKit.RectHeightStyle.Max;
640*c8dee2aaSAndroid Build Coastguard Worker    const wStyle = CanvasKit.RectWidthStyle.Tight;
641*c8dee2aaSAndroid Build Coastguard Worker
642*c8dee2aaSAndroid Build Coastguard Worker    const quotes = [
643*c8dee2aaSAndroid Build Coastguard Worker      'Some activities superficially familiar to you are merely stupid and should be avoided for your safety, although they are not illegal as such. These include: giving your bank account details to the son of the Nigerian Minister of Finance; buying title to bridges, skyscrapers, spacecraft, planets, or other real assets; murder; selling your identity; and entering into financial contracts with entities running Economics 2.0 or higher.',
644*c8dee2aaSAndroid Build Coastguard Worker      // Charles Stross - Accelerando
645*c8dee2aaSAndroid Build Coastguard Worker      'If only there were evil people somewhere insidiously committing evil deeds, and it were necessary only to separate them from the rest of us and destroy them. But the line dividing good and evil cuts through the heart of every human being. And who is willing to destroy a piece of his own heart?',
646*c8dee2aaSAndroid Build Coastguard Worker      // Aleksandr Solzhenitsyn - The Gulag Archipelago
647*c8dee2aaSAndroid Build Coastguard Worker      'There is one metaphor of which the moderns are very fond; they are always saying, “You can’t put the clock back.” The simple and obvious answer is “You can.” A clock, being a piece of human construction, can be restored by the human finger to any figure or hour. In the same way society, being a piece of human construction, can be reconstructed upon any plan that has ever existed.',
648*c8dee2aaSAndroid Build Coastguard Worker      // G. K. Chesterton - What's Wrong With The World?
649*c8dee2aaSAndroid Build Coastguard Worker    ];
650*c8dee2aaSAndroid Build Coastguard Worker
651*c8dee2aaSAndroid Build Coastguard Worker    // pick one at random
652*c8dee2aaSAndroid Build Coastguard Worker    const text = quotes[Math.floor(Math.random()*3)];
653*c8dee2aaSAndroid Build Coastguard Worker    const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
654*c8dee2aaSAndroid Build Coastguard Worker    builder.addText(text);
655*c8dee2aaSAndroid Build Coastguard Worker    const paragraph = builder.build();
656*c8dee2aaSAndroid Build Coastguard Worker    const font = new CanvasKit.Font(CanvasKit.Typeface.GetDefault(), 18);
657*c8dee2aaSAndroid Build Coastguard Worker    // wrap the text to a given width.
658*c8dee2aaSAndroid Build Coastguard Worker    paragraph.layout(sizeX - margin*2);
659*c8dee2aaSAndroid Build Coastguard Worker
660*c8dee2aaSAndroid Build Coastguard Worker    // to rotate every glyph individually, calculate the bounding rect of each one,
661*c8dee2aaSAndroid Build Coastguard Worker    // construct an array of rects and paragraphs that would draw each glyph individually.
662*c8dee2aaSAndroid Build Coastguard Worker    const letters = Array(text.length);
663*c8dee2aaSAndroid Build Coastguard Worker    for (let i = 0; i < text.length; i++) {
664*c8dee2aaSAndroid Build Coastguard Worker      const r = paragraph.getRectsForRange(i, i+1, hStyle, wStyle)[0];
665*c8dee2aaSAndroid Build Coastguard Worker      // The character is drawn with drawParagraph so we can pass the paraStyle,
666*c8dee2aaSAndroid Build Coastguard Worker      // and have our character be the exact size and shape the paragraph expected
667*c8dee2aaSAndroid Build Coastguard Worker      // when it wrapped the text. canvas.drawText wouldn't cut it.
668*c8dee2aaSAndroid Build Coastguard Worker      const tmpbuilder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
669*c8dee2aaSAndroid Build Coastguard Worker      tmpbuilder.addText(text[i]);
670*c8dee2aaSAndroid Build Coastguard Worker      const para = tmpbuilder.build();
671*c8dee2aaSAndroid Build Coastguard Worker      para.layout(100);
672*c8dee2aaSAndroid Build Coastguard Worker      letters[i] = {
673*c8dee2aaSAndroid Build Coastguard Worker        'r': r,
674*c8dee2aaSAndroid Build Coastguard Worker        'para': para,
675*c8dee2aaSAndroid Build Coastguard Worker      };
676*c8dee2aaSAndroid Build Coastguard Worker    }
677*c8dee2aaSAndroid Build Coastguard Worker
678*c8dee2aaSAndroid Build Coastguard Worker    function drawFrame(canvas) {
679*c8dee2aaSAndroid Build Coastguard Worker      // persistence of vision effect is done by drawing the past frame as an image,
680*c8dee2aaSAndroid Build Coastguard Worker      // then covering with semitransparent background color.
681*c8dee2aaSAndroid Build Coastguard Worker      if (lastImage) {
682*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawImage(lastImage, 0, 0, null);
683*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawColor(CanvasKit.Color(171, 244, 255, 0.1)); // sky blue, almost transparent
684*c8dee2aaSAndroid Build Coastguard Worker      } else {
685*c8dee2aaSAndroid Build Coastguard Worker        canvas.clear(CanvasKit.Color(171, 244, 255)); // sky blue, opaque
686*c8dee2aaSAndroid Build Coastguard Worker      }
687*c8dee2aaSAndroid Build Coastguard Worker      canvas.save();
688*c8dee2aaSAndroid Build Coastguard Worker      // Set up 3D view enviroment
689*c8dee2aaSAndroid Build Coastguard Worker      canvas.concat(CanvasKit.M44.setupCamera(
690*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.LTRBRect(0, 0, sizeX, sizeY), halfDim, cam));
691*c8dee2aaSAndroid Build Coastguard Worker
692*c8dee2aaSAndroid Build Coastguard Worker      // Rotate the whole paragraph as a unit.
693*c8dee2aaSAndroid Build Coastguard Worker      const paraRotPoint = [halfDim, halfDim, 1];
694*c8dee2aaSAndroid Build Coastguard Worker      canvas.concat(CanvasKit.M44.multiply(
695*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.M44.translated(paraRotPoint),
696*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.M44.rotated([0,1,0], rotX),
697*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.M44.rotated([1,0,0], rotY * 0.2),
698*c8dee2aaSAndroid Build Coastguard Worker        CanvasKit.M44.translated(CanvasKit.Vector.mulScalar(paraRotPoint, -1)),
699*c8dee2aaSAndroid Build Coastguard Worker      ));
700*c8dee2aaSAndroid Build Coastguard Worker
701*c8dee2aaSAndroid Build Coastguard Worker      // Rotate every glyph in the paragraph individually.
702*c8dee2aaSAndroid Build Coastguard Worker      let i = 0;
703*c8dee2aaSAndroid Build Coastguard Worker      for (const letter of letters) {
704*c8dee2aaSAndroid Build Coastguard Worker        canvas.save();
705*c8dee2aaSAndroid Build Coastguard Worker        let r = letter['r'];
706*c8dee2aaSAndroid Build Coastguard Worker        // rotate about the center of the glyph's rect.
707*c8dee2aaSAndroid Build Coastguard Worker        rotationPoint = [
708*c8dee2aaSAndroid Build Coastguard Worker          margin + r[rectLeft] + (r[rectRight] - r[rectLeft]) / 2,
709*c8dee2aaSAndroid Build Coastguard Worker          marginTop + r[rectTop] + (r[rectBottom] - r[rectTop]) / 2,
710*c8dee2aaSAndroid Build Coastguard Worker          0
711*c8dee2aaSAndroid Build Coastguard Worker        ];
712*c8dee2aaSAndroid Build Coastguard Worker        distanceFromPointer = CanvasKit.Vector.dist(pointer, rotationPoint.slice(0, 2));
713*c8dee2aaSAndroid Build Coastguard Worker        // Rotate more around the Y-axis depending on the glyph's distance from the pointer.
714*c8dee2aaSAndroid Build Coastguard Worker        canvas.concat(CanvasKit.M44.multiply(
715*c8dee2aaSAndroid Build Coastguard Worker          CanvasKit.M44.translated(rotationPoint),
716*c8dee2aaSAndroid Build Coastguard Worker          // note that I'm rotating around the x axis first, undoing some of the rotation done to the whole
717*c8dee2aaSAndroid Build Coastguard Worker          // paragraph above, where x came second. If I rotated y first, a lot of letters would end up
718*c8dee2aaSAndroid Build Coastguard Worker          // upside down, which is a bit too hard to unscramble.
719*c8dee2aaSAndroid Build Coastguard Worker          CanvasKit.M44.rotated([1,0,0], rotY * -0.6),
720*c8dee2aaSAndroid Build Coastguard Worker          CanvasKit.M44.rotated([0,1,0], distanceFromPointer * -0.035),
721*c8dee2aaSAndroid Build Coastguard Worker          CanvasKit.M44.translated(CanvasKit.Vector.mulScalar(rotationPoint, -1)),
722*c8dee2aaSAndroid Build Coastguard Worker        ));
723*c8dee2aaSAndroid Build Coastguard Worker        canvas.drawParagraph(letter['para'], margin + r[rectLeft], marginTop + r[rectTop]);
724*c8dee2aaSAndroid Build Coastguard Worker        i++;
725*c8dee2aaSAndroid Build Coastguard Worker        canvas.restore();
726*c8dee2aaSAndroid Build Coastguard Worker      }
727*c8dee2aaSAndroid Build Coastguard Worker      canvas.restore();
728*c8dee2aaSAndroid Build Coastguard Worker      lastImage = surface.makeImageSnapshot();
729*c8dee2aaSAndroid Build Coastguard Worker    }
730*c8dee2aaSAndroid Build Coastguard Worker
731*c8dee2aaSAndroid Build Coastguard Worker    function interact(e) {
732*c8dee2aaSAndroid Build Coastguard Worker      pointer = [e.offsetX, e.offsetY]
733*c8dee2aaSAndroid Build Coastguard Worker      rotX = (pointer[0] - halfDim) * radPerPixel;
734*c8dee2aaSAndroid Build Coastguard Worker      rotY = (pointer[1] - halfDim) * radPerPixel * -1;
735*c8dee2aaSAndroid Build Coastguard Worker      surface.requestAnimationFrame(drawFrame);
736*c8dee2aaSAndroid Build Coastguard Worker    };
737*c8dee2aaSAndroid Build Coastguard Worker
738*c8dee2aaSAndroid Build Coastguard Worker    document.getElementById('glyphgame').addEventListener('pointermove', interact);
739*c8dee2aaSAndroid Build Coastguard Worker    surface.requestAnimationFrame(drawFrame);
740*c8dee2aaSAndroid Build Coastguard Worker  }
741*c8dee2aaSAndroid Build Coastguard Worker
742*c8dee2aaSAndroid Build Coastguard Worker  function ColorSupport(CanvasKit) {
743*c8dee2aaSAndroid Build Coastguard Worker    const surface = CanvasKit.MakeCanvasSurface('colorsupport', CanvasKit.ColorSpace.ADOBE_RGB);
744*c8dee2aaSAndroid Build Coastguard Worker    if (!surface) {
745*c8dee2aaSAndroid Build Coastguard Worker      console.error('Could not make surface');
746*c8dee2aaSAndroid Build Coastguard Worker      return;
747*c8dee2aaSAndroid Build Coastguard Worker    }
748*c8dee2aaSAndroid Build Coastguard Worker    const canvas = surface.getCanvas();
749*c8dee2aaSAndroid Build Coastguard Worker
750*c8dee2aaSAndroid Build Coastguard Worker    // If the surface is correctly initialized with a higher bit depth color type,
751*c8dee2aaSAndroid Build Coastguard Worker    // And chrome is compositing it into a buffer with the P3 color space,
752*c8dee2aaSAndroid Build Coastguard Worker    // then the inner round rect should be distinct and less saturated than the full red background.
753*c8dee2aaSAndroid Build Coastguard Worker    // Even if the monitor it is viewed on cannot accurately represent that color space.
754*c8dee2aaSAndroid Build Coastguard Worker
755*c8dee2aaSAndroid Build Coastguard Worker    let red = CanvasKit.Color4f(1, 0, 0, 1);
756*c8dee2aaSAndroid Build Coastguard Worker    let paint = new CanvasKit.Paint();
757*c8dee2aaSAndroid Build Coastguard Worker    paint.setColor(red, CanvasKit.ColorSpace.ADOBE_RGB);
758*c8dee2aaSAndroid Build Coastguard Worker    canvas.drawPaint(paint);
759*c8dee2aaSAndroid Build Coastguard Worker    paint.setColor(red, CanvasKit.ColorSpace.DISPLAY_P3);
760*c8dee2aaSAndroid Build Coastguard Worker    canvas.drawRRect(CanvasKit.RRectXY([50, 50, 250, 250], 30, 30), paint);
761*c8dee2aaSAndroid Build Coastguard Worker    paint.setColor(red, CanvasKit.ColorSpace.SRGB);
762*c8dee2aaSAndroid Build Coastguard Worker    canvas.drawRRect(CanvasKit.RRectXY([100, 100, 200, 200], 30, 30), paint);
763*c8dee2aaSAndroid Build Coastguard Worker
764*c8dee2aaSAndroid Build Coastguard Worker    surface.flush();
765*c8dee2aaSAndroid Build Coastguard Worker    surface.delete();
766*c8dee2aaSAndroid Build Coastguard Worker  }
767*c8dee2aaSAndroid Build Coastguard Worker</script>
768