1250 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			1250 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html>
 | 
						|
<title>CanvasKit (Skia via Web Assembly)</title>
 | 
						|
<meta charset="utf-8" />
 | 
						|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
 | 
						|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
						|
 | 
						|
<style>
 | 
						|
  canvas, img {
 | 
						|
    border: 1px dashed #AAA;
 | 
						|
  }
 | 
						|
  #api5_c, #api6_c {
 | 
						|
      width: 300px;
 | 
						|
      height: 300px;
 | 
						|
  }
 | 
						|
 | 
						|
</style>
 | 
						|
 | 
						|
<h2>Drop in replacement for HTML Canvas (e.g. node.js)</h2>
 | 
						|
<img id=api1 width=300 height=300>
 | 
						|
<canvas id=api1_c width=300 height=300></canvas>
 | 
						|
<img id=api2 width=300 height=300>
 | 
						|
<canvas id=api2_c width=300 height=300></canvas>
 | 
						|
<img id=api3 width=300 height=300>
 | 
						|
<canvas id=api3_c width=300 height=300></canvas>
 | 
						|
<img id=api4 width=300 height=300>
 | 
						|
<canvas id=api4_c width=300 height=300></canvas>
 | 
						|
<img id=api5 width=300 height=300>
 | 
						|
<canvas id=api5_c width=300 height=300></canvas>
 | 
						|
<img id=api6 width=300 height=300>
 | 
						|
<canvas id=api6_c width=300 height=300></canvas>
 | 
						|
<img id=api7 width=300 height=300>
 | 
						|
<canvas id=api7_c width=300 height=300></canvas>
 | 
						|
<img id=api8 width=300 height=300>
 | 
						|
<canvas id=api8_c width=300 height=300></canvas>
 | 
						|
 | 
						|
<h2> CanvasKit expands the functionality of a stock HTML canvas</h2>
 | 
						|
<canvas id=vertex1 width=300 height=300></canvas>
 | 
						|
<canvas id=gradient1 width=300 height=300></canvas>
 | 
						|
<canvas id=patheffect width=300 height=300></canvas>
 | 
						|
<canvas id=paths width=200 height=200></canvas>
 | 
						|
<canvas id=pathperson width=300 height=300></canvas>
 | 
						|
<canvas id=ink width=300 height=300></canvas>
 | 
						|
<canvas id=surfaces width=300 height=300></canvas>
 | 
						|
<canvas id=atlas width=300 height=300></canvas>
 | 
						|
<canvas id=decode width=300 height=300></canvas>
 | 
						|
 | 
						|
<h2> CanvasKit can allow for text measurement/placement (e.g. breaking, kerning)</h2>
 | 
						|
<canvas id=textonpath width=300 height=300></canvas>
 | 
						|
<canvas id=drawGlyphs width=300 height=300></canvas>
 | 
						|
 | 
						|
<h2> Interactive drawPatch</h2>
 | 
						|
<canvas id=interdrawpatch width=512 height=512></canvas>
 | 
						|
 | 
						|
<script type="text/javascript" src="/build/canvaskit.js"></script>
 | 
						|
 | 
						|
<script type="text/javascript" charset="utf-8">
 | 
						|
 | 
						|
  var CanvasKit = null;
 | 
						|
  var cdn = 'https://storage.googleapis.com/skia-cdn/misc/';
 | 
						|
 | 
						|
  const ckLoaded = CanvasKitInit({locateFile: (file) => '/build/'+file});
 | 
						|
 | 
						|
  const loadRoboto = fetch(cdn + 'Roboto-Regular.ttf').then((response) => response.arrayBuffer());
 | 
						|
  const loadNotoSerif = fetch(cdn + 'NotoSerif-Regular.ttf').then((response) => response.arrayBuffer());
 | 
						|
  const loadTestImage = fetch(cdn + 'test.png').then((response) => response.arrayBuffer());
 | 
						|
 | 
						|
  // Examples which only require canvaskit
 | 
						|
  ckLoaded.then((CK) => {
 | 
						|
    CanvasKit = CK;
 | 
						|
    PathExample(CanvasKit);
 | 
						|
    InkExample(CanvasKit);
 | 
						|
    PathPersonExample(CanvasKit);
 | 
						|
    VertexAPI1(CanvasKit);
 | 
						|
    GradiantAPI1(CanvasKit);
 | 
						|
    TextOnPathAPI1(CanvasKit);
 | 
						|
    DrawGlyphsAPI1(CanvasKit);
 | 
						|
    SurfaceAPI1(CanvasKit);
 | 
						|
    if (CanvasKit.MakeCanvas){
 | 
						|
      CanvasAPI1(CanvasKit);
 | 
						|
      CanvasAPI2(CanvasKit);
 | 
						|
      CanvasAPI3(CanvasKit);
 | 
						|
      CanvasAPI4(CanvasKit);
 | 
						|
      CanvasAPI5(CanvasKit);
 | 
						|
      CanvasAPI6(CanvasKit);
 | 
						|
      CanvasAPI7(CanvasKit);
 | 
						|
      CanvasAPI8(CanvasKit);
 | 
						|
    } else {
 | 
						|
      console.log("Skipping CanvasAPI1 because it's not compiled in");
 | 
						|
    }
 | 
						|
    InteractivePatch(CanvasKit);
 | 
						|
  });
 | 
						|
 | 
						|
  // Examples requiring external resources
 | 
						|
  Promise.all([ckLoaded, loadRoboto]).then((results) => {DrawingExample(...results)});
 | 
						|
  Promise.all([ckLoaded, loadTestImage]).then((results) => {AtlasAPI1(...results)});
 | 
						|
  Promise.all([ckLoaded, loadTestImage]).then((results) => {DecodeAPI(...results)});
 | 
						|
 | 
						|
  function DrawingExample(CanvasKit, robotoData) {
 | 
						|
    if (!robotoData || !CanvasKit) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('patheffect');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    const paint = new CanvasKit.Paint();
 | 
						|
    const roboto = CanvasKit.Typeface.MakeFreeTypeFaceFromData(robotoData);
 | 
						|
 | 
						|
    const textPaint = new CanvasKit.Paint();
 | 
						|
    textPaint.setColor(CanvasKit.RED);
 | 
						|
    textPaint.setAntiAlias(true);
 | 
						|
 | 
						|
    const textFont = new CanvasKit.Font(roboto, 30);
 | 
						|
 | 
						|
    let i = 0;
 | 
						|
 | 
						|
    let X = 128;
 | 
						|
    let Y = 128;
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      const path = starPath(CanvasKit, X, Y);
 | 
						|
      // Some animations see performance improvements by marking their
 | 
						|
      // paths as volatile.
 | 
						|
      path.setIsVolatile(true);
 | 
						|
      const dpe = CanvasKit.PathEffect.MakeDash([15, 5, 5, 10], i/5);
 | 
						|
      i++;
 | 
						|
 | 
						|
      paint.setPathEffect(dpe);
 | 
						|
      paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
      paint.setStrokeWidth(5.0 + -3 * Math.cos(i/30));
 | 
						|
      paint.setAntiAlias(true);
 | 
						|
      paint.setColor(CanvasKit.Color(66, 129, 164, 1.0));
 | 
						|
 | 
						|
      canvas.clear(CanvasKit.TRANSPARENT);
 | 
						|
 | 
						|
      canvas.drawPath(path, paint);
 | 
						|
      canvas.drawText('Try Clicking!', 10, 280, textPaint, textFont);
 | 
						|
 | 
						|
      dpe.delete();
 | 
						|
      path.delete();
 | 
						|
      surface.requestAnimationFrame(drawFrame);
 | 
						|
    }
 | 
						|
    surface.requestAnimationFrame(drawFrame);
 | 
						|
 | 
						|
    // Make animation interactive
 | 
						|
    let interact = (e) => {
 | 
						|
      if (!e.pressure) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      X = e.offsetX;
 | 
						|
      Y = e.offsetY;
 | 
						|
    };
 | 
						|
    document.getElementById('patheffect').addEventListener('pointermove', interact);
 | 
						|
    document.getElementById('patheffect').addEventListener('pointerdown', interact);
 | 
						|
    preventScrolling(document.getElementById('patheffect'));
 | 
						|
    // A client would need to delete this if it didn't go on for ever.
 | 
						|
    // paint.delete();
 | 
						|
    // textPaint.delete();
 | 
						|
    // textFont.delete();
 | 
						|
  }
 | 
						|
 | 
						|
   function InteractivePatch(CanvasKit) {
 | 
						|
     const ELEM = 'interdrawpatch';
 | 
						|
     const surface = CanvasKit.MakeCanvasSurface(ELEM);
 | 
						|
     if (!surface) {
 | 
						|
       console.error('Could not make surface');
 | 
						|
       return;
 | 
						|
     }
 | 
						|
 | 
						|
     let live_corner, live_index;
 | 
						|
 | 
						|
     const paint = new CanvasKit.Paint();
 | 
						|
     const pts_paint = new CanvasKit.Paint();
 | 
						|
     pts_paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
     pts_paint.setStrokeWidth(9);
 | 
						|
     pts_paint.setStrokeCap(CanvasKit.StrokeCap.Round);
 | 
						|
 | 
						|
     const line_paint = new CanvasKit.Paint();
 | 
						|
     line_paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
     line_paint.setStrokeWidth(2);
 | 
						|
 | 
						|
     const colors = [CanvasKit.RED, CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.CYAN];
 | 
						|
 | 
						|
     const patch = [
 | 
						|
          [ 10,170,   10, 10,  170, 10],  // prev_vector, point, next_vector
 | 
						|
          [340, 10,  500, 10,  500,170],
 | 
						|
          [500,340,  500,500,  340,500],
 | 
						|
          [170,500,   10,500,   10,340],
 | 
						|
      ];
 | 
						|
 | 
						|
      function get_corner(corner, index) {
 | 
						|
          return [corner[index*2+0], corner[index*2+1]];
 | 
						|
      }
 | 
						|
 | 
						|
      function push_xy(array, xy) {
 | 
						|
          array.push(xy[0], xy[1]);
 | 
						|
      }
 | 
						|
 | 
						|
      function patch_to_cubics(patch) {
 | 
						|
          const array = [];
 | 
						|
          push_xy(array, get_corner(patch[0],1));
 | 
						|
          push_xy(array, get_corner(patch[0],2));
 | 
						|
          for (let i = 1; i < 4; ++i) {
 | 
						|
              push_xy(array, get_corner(patch[i],0));
 | 
						|
              push_xy(array, get_corner(patch[i],1));
 | 
						|
              push_xy(array, get_corner(patch[i],2));
 | 
						|
          }
 | 
						|
          push_xy(array, get_corner(patch[0],0));
 | 
						|
 | 
						|
          return array;
 | 
						|
      }
 | 
						|
 | 
						|
     function drawFrame(canvas) {
 | 
						|
         const cubics = patch_to_cubics(patch);
 | 
						|
 | 
						|
         canvas.drawColor(CanvasKit.WHITE);
 | 
						|
         canvas.drawPatch(cubics, colors, null, CanvasKit.BlendMode.Dst, paint);
 | 
						|
         if (live_corner) {
 | 
						|
             canvas.drawPoints(CanvasKit.PointMode.Polygon, live_corner, line_paint);
 | 
						|
         }
 | 
						|
         canvas.drawPoints(CanvasKit.PointMode.Points, cubics, pts_paint);
 | 
						|
 | 
						|
         surface.requestAnimationFrame(drawFrame);
 | 
						|
     }
 | 
						|
 | 
						|
     surface.requestAnimationFrame(drawFrame);
 | 
						|
 | 
						|
     function length2(x, y) {
 | 
						|
         return x*x + y*y;
 | 
						|
     }
 | 
						|
     function hit_test(x,y, x1,y1) {
 | 
						|
         return length2(x-x1, y-y1) <= 10*10;
 | 
						|
     }
 | 
						|
     function pointer_up(e) {
 | 
						|
         live_corner = null;
 | 
						|
         live_index = null;
 | 
						|
      }
 | 
						|
 | 
						|
     function pointer_down(e) {
 | 
						|
         live_corner = null;
 | 
						|
         live_index = null;
 | 
						|
         for (p of patch) {
 | 
						|
             for (let i = 0; i < 6; i += 2) {
 | 
						|
                 if (hit_test(p[i], p[i+1], e.offsetX, e.offsetY)) {
 | 
						|
                     live_corner = p;
 | 
						|
                     live_index = i;
 | 
						|
                 }
 | 
						|
             }
 | 
						|
         }
 | 
						|
      }
 | 
						|
 | 
						|
     function pointer_move(e) {
 | 
						|
       if (e.pressure && live_corner) {
 | 
						|
           if (live_index == 2) {
 | 
						|
               // corner
 | 
						|
               const dx = e.offsetX - live_corner[2];
 | 
						|
               const dy = e.offsetY - live_corner[3];
 | 
						|
               for  (let i = 0; i < 3; ++i) {
 | 
						|
                   live_corner[i*2+0] += dx;
 | 
						|
                   live_corner[i*2+1] += dy;
 | 
						|
               }
 | 
						|
           } else {
 | 
						|
               // control-point
 | 
						|
               live_corner[live_index+0] = e.offsetX;
 | 
						|
               live_corner[live_index+1] = e.offsetY;
 | 
						|
            }
 | 
						|
        }
 | 
						|
     }
 | 
						|
     document.getElementById(ELEM).addEventListener('pointermove', pointer_move);
 | 
						|
     document.getElementById(ELEM).addEventListener('pointerdown', pointer_down);
 | 
						|
     document.getElementById(ELEM).addEventListener('pointerup', pointer_up);
 | 
						|
     preventScrolling(document.getElementById(ELEM));
 | 
						|
   }
 | 
						|
 | 
						|
  function PathPersonExample(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeSWCanvasSurface('pathperson');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      const paint = new CanvasKit.Paint();
 | 
						|
      paint.setStrokeWidth(1.0);
 | 
						|
      paint.setAntiAlias(true);
 | 
						|
      paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
 | 
						|
      paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
 | 
						|
      const path = new CanvasKit.Path();
 | 
						|
      path.moveTo(10, 10);
 | 
						|
      path.lineTo(100, 10);
 | 
						|
      path.moveTo(10, 10);
 | 
						|
      path.lineTo(10, 200);
 | 
						|
      path.moveTo(10, 100);
 | 
						|
      path.lineTo(100,100);
 | 
						|
      path.moveTo(10, 200);
 | 
						|
      path.lineTo(100, 200);
 | 
						|
 | 
						|
      canvas.drawPath(path, paint);
 | 
						|
      path.delete();
 | 
						|
      paint.delete();
 | 
						|
    }
 | 
						|
    // Intentionally just draw frame once
 | 
						|
    surface.drawOnce(drawFrame);
 | 
						|
  }
 | 
						|
 | 
						|
  function PathExample(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeSWCanvasSurface('paths');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      const paint = new CanvasKit.Paint();
 | 
						|
      paint.setStrokeWidth(1.0);
 | 
						|
      paint.setAntiAlias(true);
 | 
						|
      paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
 | 
						|
      paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
 | 
						|
      const path = new CanvasKit.Path();
 | 
						|
      path.moveTo(20, 5);
 | 
						|
      path.lineTo(30, 20);
 | 
						|
      path.lineTo(40, 10);
 | 
						|
      path.lineTo(50, 20);
 | 
						|
      path.lineTo(60, 0);
 | 
						|
      path.lineTo(20, 5);
 | 
						|
 | 
						|
      path.moveTo(20, 80);
 | 
						|
      path.cubicTo(90, 10, 160, 150, 190, 10);
 | 
						|
 | 
						|
      path.moveTo(36, 148);
 | 
						|
      path.quadTo(66, 188, 120, 136);
 | 
						|
      path.lineTo(36, 148);
 | 
						|
 | 
						|
      path.moveTo(150, 180);
 | 
						|
      path.arcToTangent(150, 100, 50, 200, 20);
 | 
						|
      path.lineTo(160, 160);
 | 
						|
 | 
						|
      path.moveTo(20, 120);
 | 
						|
      path.lineTo(20, 120);
 | 
						|
 | 
						|
      canvas.drawPath(path, paint);
 | 
						|
 | 
						|
      const rrect = CanvasKit.RRectXY([100, 10, 140, 62], 10, 4);
 | 
						|
 | 
						|
      const rrectPath = new CanvasKit.Path().addRRect(rrect, true);
 | 
						|
 | 
						|
      canvas.drawPath(rrectPath, paint);
 | 
						|
 | 
						|
      rrectPath.delete();
 | 
						|
      path.delete();
 | 
						|
      paint.delete();
 | 
						|
    }
 | 
						|
    // Intentionally just draw frame once
 | 
						|
    surface.drawOnce(drawFrame);
 | 
						|
  }
 | 
						|
 | 
						|
  function preventScrolling(canvas) {
 | 
						|
    canvas.addEventListener('touchmove', (e) => {
 | 
						|
      // Prevents touch events in the canvas from scrolling the canvas.
 | 
						|
      e.preventDefault();
 | 
						|
      e.stopPropagation();
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  function InkExample(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('ink');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    let paint = new CanvasKit.Paint();
 | 
						|
    paint.setAntiAlias(true);
 | 
						|
    paint.setColor(CanvasKit.Color(0, 0, 0, 1.0));
 | 
						|
    paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
    paint.setStrokeWidth(4.0);
 | 
						|
    paint.setPathEffect(CanvasKit.PathEffect.MakeCorner(50));
 | 
						|
 | 
						|
    // Draw I N K
 | 
						|
    let path = new CanvasKit.Path();
 | 
						|
    path.moveTo(80, 30);
 | 
						|
    path.lineTo(80, 80);
 | 
						|
 | 
						|
    path.moveTo(100, 80);
 | 
						|
    path.lineTo(100, 15);
 | 
						|
    path.lineTo(130, 95);
 | 
						|
    path.lineTo(130, 30);
 | 
						|
 | 
						|
    path.moveTo(150, 30);
 | 
						|
    path.lineTo(150, 80);
 | 
						|
    path.moveTo(170, 30);
 | 
						|
    path.lineTo(150, 55);
 | 
						|
    path.lineTo(170, 80);
 | 
						|
 | 
						|
    let paths = [path];
 | 
						|
    let paints = [paint];
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      canvas.clear(CanvasKit.Color(255, 255, 255, 1.0));
 | 
						|
 | 
						|
      for (let i = 0; i < paints.length && i < paths.length; i++) {
 | 
						|
        canvas.drawPath(paths[i], paints[i]);
 | 
						|
      }
 | 
						|
 | 
						|
      surface.requestAnimationFrame(drawFrame);
 | 
						|
    }
 | 
						|
 | 
						|
    let hold = false;
 | 
						|
    let interact = (e) => {
 | 
						|
      let type = e.type;
 | 
						|
      if (type === 'lostpointercapture' || type === 'pointerup' || !e.pressure ) {
 | 
						|
        hold = false;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      if (hold) {
 | 
						|
        path.lineTo(e.offsetX, e.offsetY);
 | 
						|
      } else {
 | 
						|
        paint = paint.copy();
 | 
						|
        paint.setColor(CanvasKit.Color(Math.random() * 255, Math.random() * 255, Math.random() * 255, Math.random() + .2));
 | 
						|
        paints.push(paint);
 | 
						|
        path = new CanvasKit.Path();
 | 
						|
        paths.push(path);
 | 
						|
        path.moveTo(e.offsetX, e.offsetY);
 | 
						|
      }
 | 
						|
      hold = true;
 | 
						|
    };
 | 
						|
    document.getElementById('ink').addEventListener('pointermove', interact);
 | 
						|
    document.getElementById('ink').addEventListener('pointerdown', interact);
 | 
						|
    document.getElementById('ink').addEventListener('lostpointercapture', interact);
 | 
						|
    document.getElementById('ink').addEventListener('pointerup', interact);
 | 
						|
    preventScrolling(document.getElementById('ink'));
 | 
						|
    surface.requestAnimationFrame(drawFrame);
 | 
						|
  }
 | 
						|
 | 
						|
  function starPath(CanvasKit, X=128, Y=128, R=116) {
 | 
						|
    let p = new CanvasKit.Path();
 | 
						|
    p.moveTo(X + R, Y);
 | 
						|
    for (let i = 1; i < 8; i++) {
 | 
						|
      let a = 2.6927937 * i;
 | 
						|
      p.lineTo(X + R * Math.cos(a), Y + R * Math.sin(a));
 | 
						|
    }
 | 
						|
    return p;
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI1(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api1_c');
 | 
						|
 | 
						|
    let skPromise   = fetch(cdn + 'test.png')
 | 
						|
                        // if clients want to use a Blob, they are responsible
 | 
						|
                        // for reading it themselves.
 | 
						|
                        .then((response) => response.arrayBuffer())
 | 
						|
                        .then((buffer) => {
 | 
						|
                          skcanvas._img = skcanvas.decodeImage(buffer);
 | 
						|
                        });
 | 
						|
    let realPromise = fetch(cdn + 'test.png')
 | 
						|
                        .then((response) => response.blob())
 | 
						|
                        .then((blob) => createImageBitmap(blob))
 | 
						|
                        .then((bitmap) => {
 | 
						|
                          realCanvas._img = bitmap;
 | 
						|
                        });
 | 
						|
 | 
						|
    let realFontLoaded = new FontFace('Bungee', 'url(/tests/assets/Bungee-Regular.ttf)', {
 | 
						|
      'family': 'Bungee',
 | 
						|
      'style': 'normal',
 | 
						|
      'weight': '400',
 | 
						|
    }).load().then((font) => {
 | 
						|
      document.fonts.add(font);
 | 
						|
    });
 | 
						|
 | 
						|
    let skFontLoaded = fetch('/tests/assets/Bungee-Regular.ttf').then(
 | 
						|
                             (response) => response.arrayBuffer()).then(
 | 
						|
                             (buffer) => {
 | 
						|
                                // loadFont is synchronous
 | 
						|
                                skcanvas.loadFont(buffer, {
 | 
						|
                                  'family': 'Bungee',
 | 
						|
                                  'style': 'normal',
 | 
						|
                                  'weight': '400',
 | 
						|
                                });
 | 
						|
                              });
 | 
						|
 | 
						|
    Promise.all([realPromise, skPromise, realFontLoaded, skFontLoaded]).then(() => {
 | 
						|
      for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
        let ctx = canvas.getContext('2d');
 | 
						|
        ctx.fillStyle = '#EEE';
 | 
						|
        ctx.fillRect(0, 0, 300, 300);
 | 
						|
        ctx.fillStyle = 'black';
 | 
						|
        ctx.font = '26px Bungee';
 | 
						|
        ctx.rotate(.1);
 | 
						|
        let text = ctx.measureText('Awesome');
 | 
						|
        ctx.fillText('Awesome ', 25, 100);
 | 
						|
        ctx.strokeText('Groovy!', 35 + text.width, 100);
 | 
						|
 | 
						|
        // Draw line under Awesome
 | 
						|
        ctx.strokeStyle = 'rgba(125,0,0,0.5)';
 | 
						|
        ctx.beginPath();
 | 
						|
        ctx.lineWidth = 6;
 | 
						|
        ctx.moveTo(25, 105);
 | 
						|
        ctx.lineTo(200, 105);
 | 
						|
        ctx.stroke();
 | 
						|
 | 
						|
        // squished vertically
 | 
						|
        ctx.globalAlpha = 0.7;
 | 
						|
        ctx.imageSmoothingQuality = 'medium';
 | 
						|
        ctx.drawImage(canvas._img, 150, 150, 150, 100);
 | 
						|
        ctx.rotate(-.2);
 | 
						|
        ctx.imageSmoothingEnabled = false;
 | 
						|
        ctx.drawImage(canvas._img, 100, 150, 400, 350, 10, 200, 150, 100);
 | 
						|
 | 
						|
        let idata = ctx.getImageData(80, 220, 40, 45);
 | 
						|
        ctx.putImageData(idata, 250, 10);
 | 
						|
        ctx.putImageData(idata, 200, 10, 20, 10, 20, 30);
 | 
						|
        ctx.resetTransform();
 | 
						|
        ctx.strokeStyle = 'black';
 | 
						|
        ctx.lineWidth = 1;
 | 
						|
        ctx.strokeRect(200, 10, 40, 45);
 | 
						|
 | 
						|
        idata = ctx.createImageData(10, 20);
 | 
						|
        ctx.putImageData(idata, 10, 10);
 | 
						|
      }
 | 
						|
 | 
						|
      document.getElementById('api1').src = skcanvas.toDataURL();
 | 
						|
      skcanvas.dispose();
 | 
						|
    });
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI2(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api2_c');
 | 
						|
    realCanvas.width = 300;
 | 
						|
    realCanvas.height = 300;
 | 
						|
 | 
						|
    // svg data for a clock
 | 
						|
    skcanvas._path = skcanvas.makePath2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
 | 
						|
    realCanvas._path = new Path2D('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
      ctx.scale(1.5, 1.5);
 | 
						|
      ctx.moveTo(20, 5);
 | 
						|
      ctx.lineTo(30, 20);
 | 
						|
      ctx.lineTo(40, 10);
 | 
						|
      ctx.lineTo(50, 20);
 | 
						|
      ctx.lineTo(60, 0);
 | 
						|
      ctx.lineTo(20, 5);
 | 
						|
 | 
						|
      ctx.moveTo(20, 80);
 | 
						|
      ctx.bezierCurveTo(90, 10, 160, 150, 190, 10);
 | 
						|
 | 
						|
      ctx.moveTo(36, 148);
 | 
						|
      ctx.quadraticCurveTo(66, 188, 120, 136);
 | 
						|
      ctx.lineTo(36, 148);
 | 
						|
 | 
						|
      ctx.rect(5, 170, 20, 25);
 | 
						|
 | 
						|
      ctx.moveTo(150, 180);
 | 
						|
      ctx.arcTo(150, 100, 50, 200, 20);
 | 
						|
      ctx.lineTo(160, 160);
 | 
						|
 | 
						|
      ctx.moveTo(20, 120);
 | 
						|
      ctx.arc(20, 120, 18, 0, 1.75 * Math.PI);
 | 
						|
      ctx.lineTo(20, 120);
 | 
						|
 | 
						|
      ctx.moveTo(150, 5);
 | 
						|
      ctx.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI);
 | 
						|
 | 
						|
      ctx.lineWidth = 4/3;
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      // make a clock
 | 
						|
      ctx.stroke(canvas._path);
 | 
						|
 | 
						|
      // Test edgecases and draw direction
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.arc(50, 100, 10, Math.PI, -Math.PI/2);
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.arc(75, 100, 10, Math.PI, -Math.PI/2, true);
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.arc(100, 100, 10, Math.PI, 100.1 * Math.PI, true);
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.arc(125, 100, 10, Math.PI, 100.1 * Math.PI, false);
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.ellipse(155, 100, 10, 15, Math.PI/8, 100.1 * Math.PI, Math.PI, true);
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.ellipse(180, 100, 10, 15, Math.PI/8, Math.PI, 100.1 * Math.PI, true);
 | 
						|
      ctx.stroke();
 | 
						|
    }
 | 
						|
    document.getElementById('api2').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI3(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api3_c');
 | 
						|
    realCanvas.width = 300;
 | 
						|
    realCanvas.height = 300;
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
      ctx.rect(10, 10, 20, 20);
 | 
						|
 | 
						|
      ctx.scale(2.0, 4.0);
 | 
						|
      ctx.rect(30, 10, 20, 20);
 | 
						|
      ctx.resetTransform();
 | 
						|
 | 
						|
      ctx.rotate(Math.PI / 3);
 | 
						|
      ctx.rect(50, 10, 20, 20);
 | 
						|
      ctx.resetTransform();
 | 
						|
 | 
						|
      ctx.translate(30, -2);
 | 
						|
      ctx.rect(70, 10, 20, 20);
 | 
						|
      ctx.resetTransform();
 | 
						|
 | 
						|
      ctx.translate(60, 0);
 | 
						|
      ctx.rotate(Math.PI / 6);
 | 
						|
      ctx.transform(1.5, 0, 0, 0.5, 0, 0); // effectively scale
 | 
						|
      ctx.rect(90, 10, 20, 20);
 | 
						|
      ctx.resetTransform();
 | 
						|
 | 
						|
      ctx.save();
 | 
						|
      ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
 | 
						|
      ctx.rect(110, 10, 20, 20);
 | 
						|
      ctx.lineTo(110, 0);
 | 
						|
      ctx.restore();
 | 
						|
      ctx.lineTo(220, 120);
 | 
						|
 | 
						|
      ctx.scale(3.0, 3.0);
 | 
						|
      ctx.font = '6pt Noto Mono';
 | 
						|
      ctx.fillText('This text should be huge', 10, 80);
 | 
						|
      ctx.resetTransform();
 | 
						|
 | 
						|
      ctx.strokeStyle = 'black';
 | 
						|
      ctx.lineWidth = 2;
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.moveTo(250, 30);
 | 
						|
      ctx.lineTo(250, 80);
 | 
						|
      ctx.scale(3.0, 3.0);
 | 
						|
      ctx.lineTo(280/3, 90/3);
 | 
						|
      ctx.closePath();
 | 
						|
      ctx.strokeStyle = 'black';
 | 
						|
      ctx.lineWidth = 5;
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
    }
 | 
						|
    document.getElementById('api3').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI4(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api4_c');
 | 
						|
    realCanvas.width = 300;
 | 
						|
    realCanvas.height = 300;
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
 | 
						|
      ctx.strokeStyle = '#000';
 | 
						|
      ctx.fillStyle = '#CCC';
 | 
						|
      ctx.shadowColor = 'rebeccapurple';
 | 
						|
      ctx.shadowBlur = 1;
 | 
						|
      ctx.shadowOffsetX = 3;
 | 
						|
      ctx.shadowOffsetY = -8;
 | 
						|
      ctx.rect(10, 10, 30, 30);
 | 
						|
 | 
						|
      ctx.save();
 | 
						|
      ctx.strokeStyle = '#C00';
 | 
						|
      ctx.fillStyle = '#00C';
 | 
						|
      ctx.shadowBlur = 0;
 | 
						|
      ctx.shadowColor = 'transparent';
 | 
						|
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      ctx.restore();
 | 
						|
      ctx.fill();
 | 
						|
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.moveTo(36, 148);
 | 
						|
      ctx.quadraticCurveTo(66, 188, 120, 136);
 | 
						|
      ctx.closePath();
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.shadowColor = '#993366AA';
 | 
						|
      ctx.shadowOffsetX = 8;
 | 
						|
      ctx.shadowBlur = 5;
 | 
						|
      ctx.setTransform(2, 0, -.5, 2.5, -40, 120);
 | 
						|
      ctx.rect(110, 10, 20, 20);
 | 
						|
      ctx.lineTo(110, 0);
 | 
						|
      ctx.resetTransform();
 | 
						|
      ctx.lineTo(220, 120);
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      ctx.fillStyle = 'green';
 | 
						|
      ctx.font = '16pt Noto Mono';
 | 
						|
      ctx.fillText('This should be shadowed', 20, 80);
 | 
						|
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.lineWidth = 6;
 | 
						|
      ctx.ellipse(10, 290, 30, 30, 0, 0, Math.PI * 2);
 | 
						|
      ctx.scale(2, 1);
 | 
						|
      ctx.moveTo(10, 290);
 | 
						|
      ctx.ellipse(10, 290, 30, 60, 0, 0, Math.PI * 2);
 | 
						|
      ctx.resetTransform();
 | 
						|
      ctx.scale(3, 1);
 | 
						|
      ctx.moveTo(10, 290);
 | 
						|
      ctx.ellipse(10, 290, 30, 90, 0, 0, Math.PI * 2);
 | 
						|
      ctx.stroke();
 | 
						|
    }
 | 
						|
    document.getElementById('api4').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI5(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(600, 600);
 | 
						|
    let realCanvas = document.getElementById('api5_c');
 | 
						|
    realCanvas.width = 600;
 | 
						|
    realCanvas.height = 600;
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
      ctx.scale(1.1, 1.1);
 | 
						|
      ctx.translate(10, 10);
 | 
						|
      // Shouldn't impact the fillRect calls
 | 
						|
      ctx.setLineDash([5, 3]);
 | 
						|
 | 
						|
      ctx.fillStyle = 'rgba(200, 0, 100, 0.81)';
 | 
						|
      ctx.fillRect(20, 30, 100, 100);
 | 
						|
 | 
						|
      ctx.globalAlpha = 0.81;
 | 
						|
      ctx.fillStyle = 'rgba(200, 0, 100, 1.0)';
 | 
						|
      ctx.fillRect(120, 30, 100, 100);
 | 
						|
      // This shouldn't do anything
 | 
						|
      ctx.globalAlpha = 0.1;
 | 
						|
 | 
						|
      ctx.fillStyle = 'rgba(200, 0, 100, 0.9)';
 | 
						|
      ctx.globalAlpha = 0.9;
 | 
						|
      // Intentional no-op to check ordering
 | 
						|
      ctx.clearRect(220, 30, 100, 100);
 | 
						|
      ctx.fillRect(220, 30, 100, 100);
 | 
						|
 | 
						|
      ctx.fillRect(320, 30, 100, 100);
 | 
						|
      ctx.clearRect(330, 40, 80, 80);
 | 
						|
 | 
						|
      ctx.strokeStyle = 'blue';
 | 
						|
      ctx.lineWidth = 3;
 | 
						|
      ctx.setLineDash([5, 3]);
 | 
						|
      ctx.strokeRect(20, 150, 100, 100);
 | 
						|
      ctx.setLineDash([50, 30]);
 | 
						|
      ctx.strokeRect(125, 150, 100, 100);
 | 
						|
      ctx.lineDashOffset = 25;
 | 
						|
      ctx.strokeRect(230, 150, 100, 100);
 | 
						|
      ctx.setLineDash([2, 5, 9]);
 | 
						|
      ctx.strokeRect(335, 150, 100, 100);
 | 
						|
 | 
						|
      ctx.setLineDash([5, 2]);
 | 
						|
      ctx.moveTo(336, 400);
 | 
						|
      ctx.quadraticCurveTo(366, 488, 120, 450);
 | 
						|
      ctx.lineTo(300, 400);
 | 
						|
      ctx.stroke();
 | 
						|
 | 
						|
      ctx.font = '36pt Noto Mono';
 | 
						|
      ctx.strokeText('Dashed', 20, 350);
 | 
						|
      ctx.fillText('Not Dashed', 20, 400);
 | 
						|
 | 
						|
    }
 | 
						|
    document.getElementById('api5').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI6(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(600, 600);
 | 
						|
    let realCanvas = document.getElementById('api6_c');
 | 
						|
    realCanvas.width = 600;
 | 
						|
    realCanvas.height = 600;
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
 | 
						|
      let rgradient = ctx.createRadialGradient(200, 300, 10, 100, 100, 300);
 | 
						|
 | 
						|
      // Add three color stops
 | 
						|
      rgradient.addColorStop(0, 'red');
 | 
						|
      rgradient.addColorStop(0.7, 'white');
 | 
						|
      rgradient.addColorStop(1, 'blue');
 | 
						|
 | 
						|
      ctx.fillStyle = rgradient;
 | 
						|
      ctx.globalAlpha = 0.7;
 | 
						|
      ctx.fillRect(0, 0, 600, 600);
 | 
						|
      ctx.globalAlpha = 0.95;
 | 
						|
 | 
						|
      ctx.beginPath();
 | 
						|
      ctx.arc(300, 100, 90, 0, Math.PI*1.66);
 | 
						|
      ctx.closePath();
 | 
						|
      ctx.strokeStyle = 'yellow';
 | 
						|
      ctx.lineWidth = 5;
 | 
						|
      ctx.stroke();
 | 
						|
      ctx.save();
 | 
						|
      ctx.clip();
 | 
						|
 | 
						|
      let lgradient = ctx.createLinearGradient(200, 20, 420, 40);
 | 
						|
 | 
						|
      // Add three color stops
 | 
						|
      lgradient.addColorStop(0, 'green');
 | 
						|
      lgradient.addColorStop(0.5, 'cyan');
 | 
						|
      lgradient.addColorStop(1, 'orange');
 | 
						|
 | 
						|
      ctx.fillStyle = lgradient;
 | 
						|
 | 
						|
      ctx.fillRect(200, 30, 200, 300);
 | 
						|
 | 
						|
      ctx.restore();
 | 
						|
      ctx.fillRect(550, 550, 40, 40);
 | 
						|
 | 
						|
    }
 | 
						|
    document.getElementById('api6').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI7(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api7_c');
 | 
						|
 | 
						|
    let skPromise   = fetch(cdn + 'test.png')
 | 
						|
                        // if clients want to use a Blob, they are responsible
 | 
						|
                        // for reading it themselves.
 | 
						|
                        .then((response) => response.arrayBuffer())
 | 
						|
                        .then((buffer) => {
 | 
						|
                          skcanvas._img = skcanvas.decodeImage(buffer);
 | 
						|
                        });
 | 
						|
    let realPromise = fetch(cdn + 'test.png')
 | 
						|
                        .then((response) => response.blob())
 | 
						|
                        .then((blob) => createImageBitmap(blob))
 | 
						|
                        .then((bitmap) => {
 | 
						|
                          realCanvas._img = bitmap;
 | 
						|
                        });
 | 
						|
 | 
						|
 | 
						|
    Promise.all([realPromise, skPromise]).then(() => {
 | 
						|
      for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
        let ctx = canvas.getContext('2d');
 | 
						|
        ctx.fillStyle = '#EEE';
 | 
						|
        ctx.fillRect(0, 0, 300, 300);
 | 
						|
        ctx.lineWidth = 20;
 | 
						|
        ctx.scale(0.1, 0.2);
 | 
						|
 | 
						|
        let pattern = ctx.createPattern(canvas._img, 'repeat');
 | 
						|
        ctx.fillStyle = pattern;
 | 
						|
        ctx.fillRect(0, 0, 1500, 750);
 | 
						|
 | 
						|
        pattern = ctx.createPattern(canvas._img, 'repeat-x');
 | 
						|
        ctx.fillStyle = pattern;
 | 
						|
        ctx.fillRect(1500, 0, 3000, 750);
 | 
						|
 | 
						|
        ctx.globalAlpha = 0.7;
 | 
						|
        pattern = ctx.createPattern(canvas._img, 'repeat-y');
 | 
						|
        ctx.fillStyle = pattern;
 | 
						|
        ctx.fillRect(0, 750, 1500, 1500);
 | 
						|
        ctx.strokeRect(0, 750, 1500, 1500);
 | 
						|
 | 
						|
        pattern = ctx.createPattern(canvas._img, 'no-repeat');
 | 
						|
        ctx.fillStyle = pattern;
 | 
						|
        pattern.setTransform({a: 1, b: -.1, c:.1, d: 0.5, e: 1800, f:800});
 | 
						|
        ctx.fillRect(0, 0, 3000, 1500);
 | 
						|
      }
 | 
						|
 | 
						|
      document.getElementById('api7').src = skcanvas.toDataURL();
 | 
						|
      skcanvas.dispose();
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  function CanvasAPI8(CanvasKit) {
 | 
						|
    let skcanvas = CanvasKit.MakeCanvas(300, 300);
 | 
						|
    let realCanvas = document.getElementById('api8_c');
 | 
						|
 | 
						|
    function drawPoint(ctx, x, y, color) {
 | 
						|
      ctx.fillStyle = color;
 | 
						|
      ctx.fillRect(x, y, 1, 1);
 | 
						|
    }
 | 
						|
    const IN = 'purple';
 | 
						|
    const OUT = 'orange';
 | 
						|
    const SCALE = 4;
 | 
						|
 | 
						|
    const pts = [[3, 3], [4, 4], [5, 5], [10, 10], [8, 10], [6, 10],
 | 
						|
                 [6.5, 9], [15, 10], [17, 10], [17, 11], [24, 24],
 | 
						|
                 [25, 25], [26, 26], [27, 27]];
 | 
						|
 | 
						|
    const tests = [
 | 
						|
      {
 | 
						|
        xOffset: 0,
 | 
						|
        yOffset: 0,
 | 
						|
        fillType: 'nonzero',
 | 
						|
        strokeWidth: 0,
 | 
						|
        testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'nonzero'),
 | 
						|
      },
 | 
						|
      {
 | 
						|
        xOffset: 30,
 | 
						|
        yOffset: 0,
 | 
						|
        fillType: 'evenodd',
 | 
						|
        strokeWidth: 0,
 | 
						|
        testFn: (ctx, x, y) => ctx.isPointInPath(x * SCALE, y * SCALE, 'evenodd'),
 | 
						|
      },
 | 
						|
      {
 | 
						|
        xOffset: 0,
 | 
						|
        yOffset: 30,
 | 
						|
        fillType: null,
 | 
						|
        strokeWidth: 1,
 | 
						|
        testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
 | 
						|
      },
 | 
						|
      {
 | 
						|
        xOffset: 30,
 | 
						|
        yOffset: 30,
 | 
						|
        fillType: null,
 | 
						|
        strokeWidth: 2,
 | 
						|
        testFn: (ctx, x, y) => ctx.isPointInStroke(x * SCALE, y * SCALE),
 | 
						|
      },
 | 
						|
    ];
 | 
						|
 | 
						|
    for (let canvas of [skcanvas, realCanvas]) {
 | 
						|
      let ctx = canvas.getContext('2d');
 | 
						|
      ctx.font = '11px Noto Mono';
 | 
						|
      // Draw some visual aids
 | 
						|
      ctx.fillText('path-nonzero', 30, 15);
 | 
						|
      ctx.fillText('path-evenodd', 150, 15);
 | 
						|
      ctx.fillText('stroke-1px-wide', 30, 130);
 | 
						|
      ctx.fillText('stroke-2px-wide', 150, 130);
 | 
						|
      ctx.fillText('purple is IN, orange is OUT', 10, 280);
 | 
						|
 | 
						|
      // Scale up to make single pixels easier to see
 | 
						|
      ctx.scale(SCALE, SCALE);
 | 
						|
      for (let test of tests) {
 | 
						|
        ctx.beginPath();
 | 
						|
        let xOffset = test.xOffset;
 | 
						|
        let yOffset = test.yOffset;
 | 
						|
 | 
						|
        ctx.fillStyle = '#AAA';
 | 
						|
        ctx.lineWidth = test.strokeWidth;
 | 
						|
        ctx.rect(5+xOffset, 5+yOffset, 20, 20);
 | 
						|
        ctx.arc(15+xOffset, 15+yOffset, 8, 0, Math.PI*2, false);
 | 
						|
        if (test.fillType) {
 | 
						|
          ctx.fill(test.fillType);
 | 
						|
        } else {
 | 
						|
          ctx.stroke();
 | 
						|
        }
 | 
						|
 | 
						|
        for (let pt of pts) {
 | 
						|
          let [x, y] = pt;
 | 
						|
          x += xOffset;
 | 
						|
          y += yOffset;
 | 
						|
          // naively apply transform when querying because the points queried
 | 
						|
          // ignore the CTM.
 | 
						|
          if (test.testFn(ctx, x, y)) {
 | 
						|
            drawPoint(ctx, x, y, IN);
 | 
						|
          } else {
 | 
						|
            drawPoint(ctx, x, y, OUT);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    document.getElementById('api8').src = skcanvas.toDataURL();
 | 
						|
    skcanvas.dispose();
 | 
						|
  }
 | 
						|
 | 
						|
  function VertexAPI1(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('vertex1');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const canvas = surface.getCanvas();
 | 
						|
    let paint = new CanvasKit.Paint();
 | 
						|
 | 
						|
    // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
 | 
						|
    // for original c++ version.
 | 
						|
    let points = [0, 0,  250, 0,  100, 100,  0, 250];
 | 
						|
    let colors = [CanvasKit.RED, CanvasKit.BLUE,
 | 
						|
                  CanvasKit.YELLOW, CanvasKit.CYAN];
 | 
						|
    let vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
 | 
						|
                                            points, null, colors,
 | 
						|
                                            false /*isVolatile*/);
 | 
						|
 | 
						|
    canvas.drawVertices(vertices, CanvasKit.BlendMode.Dst, paint);
 | 
						|
 | 
						|
    vertices.delete();
 | 
						|
 | 
						|
    // See https://fiddle.skia.org/c/e8bdae9bea3227758989028424fcac3d
 | 
						|
    // for original c++ version.
 | 
						|
    points   = [300, 300,  50, 300,  200, 200,  300, 50 ];
 | 
						|
    let texs = [  0,   0,   0, 250,  250, 250,  250,  0 ];
 | 
						|
    vertices = CanvasKit.MakeVertices(CanvasKit.VertexMode.TriangleFan,
 | 
						|
                                            points, texs, colors);
 | 
						|
 | 
						|
    let shader = CanvasKit.Shader.MakeLinearGradient([0, 0], [250, 0],
 | 
						|
            colors, null, CanvasKit.TileMode.Clamp);
 | 
						|
    paint.setShader(shader);
 | 
						|
 | 
						|
    canvas.drawVertices(vertices, CanvasKit.BlendMode.Darken, paint);
 | 
						|
    surface.flush();
 | 
						|
 | 
						|
    shader.delete();
 | 
						|
    paint.delete();
 | 
						|
    surface.delete();
 | 
						|
  }
 | 
						|
 | 
						|
  function GradiantAPI1(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeSWCanvasSurface('gradient1');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const canvas = surface.getCanvas();
 | 
						|
    let paint = new CanvasKit.Paint();
 | 
						|
 | 
						|
    // See https://fiddle.skia.org/c/f48b22eaad1bb7adcc3faaa321754af6
 | 
						|
    // for original c++ version.
 | 
						|
    let colors = [CanvasKit.BLUE, CanvasKit.YELLOW, CanvasKit.RED];
 | 
						|
    let pos =    [0, .7, 1.0];
 | 
						|
    let transform = [2, 0, 0,
 | 
						|
                     0, 2, 0,
 | 
						|
                     0, 0, 1];
 | 
						|
    let shader = CanvasKit.Shader.MakeRadialGradient([150, 150], 130, colors,
 | 
						|
                              pos, CanvasKit.TileMode.Mirror, transform);
 | 
						|
 | 
						|
    paint.setShader(shader);
 | 
						|
    const textFont = new CanvasKit.Font(null, 75);
 | 
						|
    const textBlob = CanvasKit.TextBlob.MakeFromText('Radial', textFont);
 | 
						|
 | 
						|
    canvas.drawTextBlob(textBlob, 10, 200, paint);
 | 
						|
    paint.delete();
 | 
						|
    textFont.delete();
 | 
						|
    textBlob.delete();
 | 
						|
    surface.flush();
 | 
						|
  }
 | 
						|
 | 
						|
  function TextOnPathAPI1(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeSWCanvasSurface('textonpath');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const canvas = surface.getCanvas();
 | 
						|
    const paint = new CanvasKit.Paint();
 | 
						|
    paint.setStyle(CanvasKit.PaintStyle.Stroke);
 | 
						|
    paint.setAntiAlias(true);
 | 
						|
 | 
						|
    const font = new CanvasKit.Font(null, 24);
 | 
						|
    const fontPaint = new CanvasKit.Paint();
 | 
						|
    fontPaint.setStyle(CanvasKit.PaintStyle.Fill);
 | 
						|
    fontPaint.setAntiAlias(true);
 | 
						|
 | 
						|
    const arc = new CanvasKit.Path();
 | 
						|
    arc.arcToOval(CanvasKit.LTRBRect(20, 40, 280, 300), -160, 140, true);
 | 
						|
    arc.lineTo(210, 140);
 | 
						|
    arc.arcToOval(CanvasKit.LTRBRect(20, 0, 280, 260), 160, -140, true);
 | 
						|
 | 
						|
    const str = 'This téxt should follow the curve across contours...';
 | 
						|
    const textBlob = CanvasKit.TextBlob.MakeOnPath(str, arc, font);
 | 
						|
 | 
						|
    canvas.drawPath(arc, paint);
 | 
						|
    canvas.drawTextBlob(textBlob, 0, 0, fontPaint);
 | 
						|
 | 
						|
    surface.flush();
 | 
						|
 | 
						|
    textBlob.delete();
 | 
						|
    arc.delete();
 | 
						|
    paint.delete();
 | 
						|
    font.delete();
 | 
						|
    fontPaint.delete();
 | 
						|
  }
 | 
						|
 | 
						|
    function DrawGlyphsAPI1(CanvasKit) {
 | 
						|
        const surface = CanvasKit.MakeSWCanvasSurface('drawGlyphs');
 | 
						|
        if (!surface) {
 | 
						|
            console.error('Could not make surface');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const canvas = surface.getCanvas();
 | 
						|
        const paint = new CanvasKit.Paint();
 | 
						|
        const font = new CanvasKit.Font(null, 16);
 | 
						|
        paint.setAntiAlias(true);
 | 
						|
 | 
						|
        let glyphs = [];
 | 
						|
        let positions = [];
 | 
						|
        for (let i = 0; i < 256; ++i) {
 | 
						|
            glyphs.push(i);
 | 
						|
            positions.push((i % 16) * 16);
 | 
						|
            positions.push(Math.round(i/16) * 16);
 | 
						|
        }
 | 
						|
        canvas.drawGlyphs(glyphs, positions, 16, 20, font, paint);
 | 
						|
 | 
						|
        surface.flush();
 | 
						|
 | 
						|
        paint.delete();
 | 
						|
        font.delete();
 | 
						|
    }
 | 
						|
 | 
						|
  function SurfaceAPI1(CanvasKit) {
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('surfaces');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // create a subsurface as a temporary workspace.
 | 
						|
    const subSurface = surface.makeSurface({
 | 
						|
      width: 50,
 | 
						|
      height: 50,
 | 
						|
      alphaType: CanvasKit.AlphaType.Premul,
 | 
						|
      colorType: CanvasKit.ColorType.RGBA_8888,
 | 
						|
      colorSpace: CanvasKit.ColorSpace.SRGB,
 | 
						|
    });
 | 
						|
 | 
						|
    if (!subSurface) {
 | 
						|
      console.error('Could not make subsurface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    // draw a small "scene"
 | 
						|
    const paint = new CanvasKit.Paint();
 | 
						|
    paint.setColor(CanvasKit.Color(139, 228, 135, 0.95)); // greenish
 | 
						|
    paint.setStyle(CanvasKit.PaintStyle.Fill);
 | 
						|
    paint.setAntiAlias(true);
 | 
						|
 | 
						|
    const subCanvas = subSurface.getCanvas();
 | 
						|
    subCanvas.clear(CanvasKit.BLACK);
 | 
						|
    subCanvas.drawRect(CanvasKit.LTRBRect(5, 15, 45, 40), paint);
 | 
						|
 | 
						|
    paint.setColor(CanvasKit.Color(214, 93, 244)); // purplish
 | 
						|
    for (let i = 0; i < 10; i++) {
 | 
						|
      const x = Math.random() * 50;
 | 
						|
      const y = Math.random() * 50;
 | 
						|
 | 
						|
      subCanvas.drawOval(CanvasKit.XYWHRect(x, y, 6, 6), paint);
 | 
						|
    }
 | 
						|
 | 
						|
    // Snap it off as an Image - this image will be in the form the
 | 
						|
    // parent surface prefers (e.g. Texture for GPU / Raster for CPU).
 | 
						|
    const img = subSurface.makeImageSnapshot();
 | 
						|
 | 
						|
    // clean up the temporary surface (which also cleans up subCanvas)
 | 
						|
    subSurface.delete();
 | 
						|
    paint.delete();
 | 
						|
 | 
						|
    // Make it repeat a bunch with a shader
 | 
						|
    const pattern = img.makeShaderCubic(CanvasKit.TileMode.Repeat, CanvasKit.TileMode.Mirror,
 | 
						|
                                        1/3, 1/3);
 | 
						|
    const patternPaint = new CanvasKit.Paint();
 | 
						|
    patternPaint.setShader(pattern);
 | 
						|
 | 
						|
    let i = 0;
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      i++;
 | 
						|
      canvas.clear(CanvasKit.WHITE);
 | 
						|
 | 
						|
      canvas.drawOval(CanvasKit.LTRBRect(i % 60, i % 60, 300 - (i% 60), 300 - (i % 60)), patternPaint);
 | 
						|
      surface.requestAnimationFrame(drawFrame);
 | 
						|
    }
 | 
						|
    surface.requestAnimationFrame(drawFrame);
 | 
						|
  }
 | 
						|
 | 
						|
  function AtlasAPI1(CanvasKit, imgData) {
 | 
						|
    if (!CanvasKit || !imgData) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('atlas');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const img = CanvasKit.MakeImageFromEncoded(imgData);
 | 
						|
 | 
						|
    const paint = new CanvasKit.Paint();
 | 
						|
    paint.setColor(CanvasKit.Color(0, 0, 0, 0.8));
 | 
						|
 | 
						|
    // Allocate space for 2 rectangles.
 | 
						|
    const srcs = CanvasKit.Malloc(Float32Array, 8);
 | 
						|
    srcs.toTypedArray().set([
 | 
						|
      0, 0, 250, 250, // LTRB
 | 
						|
      250, 0, 500, 250
 | 
						|
    ]);
 | 
						|
 | 
						|
    // Allocate space for 2 RSXForms
 | 
						|
    const dsts = CanvasKit.Malloc(Float32Array, 8);
 | 
						|
    dsts.toTypedArray().set([
 | 
						|
      .5, 0, 0, 0,  // scos, ssin, tx, ty
 | 
						|
      0, .8, 200, 100
 | 
						|
    ]);
 | 
						|
 | 
						|
   // Allocate space for 4 colors.
 | 
						|
    const colors = new CanvasKit.Malloc(Uint32Array, 2);
 | 
						|
    colors.toTypedArray().set([
 | 
						|
      CanvasKit.ColorAsInt( 85, 170,  10, 128), // light green
 | 
						|
      CanvasKit.ColorAsInt( 51,  51, 191, 128), // light blue
 | 
						|
    ]);
 | 
						|
 | 
						|
    let i = 0;
 | 
						|
 | 
						|
    function drawFrame(canvas) {
 | 
						|
      canvas.clear(CanvasKit.WHITE);
 | 
						|
      i++;
 | 
						|
      let scale = 0.5 + Math.sin(i/40)/4;
 | 
						|
 | 
						|
      // update the coordinates of existing sprites - note that this
 | 
						|
      // does not require a full re-copy of the full array; they are
 | 
						|
      // updated in-place.
 | 
						|
      dsts.toTypedArray().set([0.5, 0, (2*i)%200, (5*Math.round(i/200)) % 200], 0);
 | 
						|
      dsts.toTypedArray().set([scale*Math.sin(i/20), scale*Math.cos(i/20), 200, 100], 4);
 | 
						|
 | 
						|
      canvas.drawAtlas(img, srcs, dsts, paint, CanvasKit.BlendMode.Plus, colors,
 | 
						|
                       {filter: CanvasKit.FilterMode.Nearest});
 | 
						|
      surface.requestAnimationFrame(drawFrame);
 | 
						|
    }
 | 
						|
    surface.requestAnimationFrame(drawFrame);
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  async function DecodeAPI(CanvasKit, imgData) {
 | 
						|
    if (!CanvasKit || !imgData) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const surface = CanvasKit.MakeCanvasSurface('decode');
 | 
						|
    if (!surface) {
 | 
						|
      console.error('Could not make surface');
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const blob = new Blob([ imgData ]);
 | 
						|
    // ImageBitmap is not supported in Safari
 | 
						|
    const imageBitmap = await createImageBitmap(blob);
 | 
						|
    const img = await CanvasKit.MakeImageFromCanvasImageSource(imageBitmap);
 | 
						|
 | 
						|
    surface.drawOnce((canvas) => {
 | 
						|
      canvas.drawImage(img, 0, 0, null);
 | 
						|
    });
 | 
						|
  }
 | 
						|
</script>
 |