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