|  | <!DOCTYPE html> | 
|  | <html> | 
|  |  | 
|  | <!-- | 
|  | Copyright 2022 The IREE Authors | 
|  |  | 
|  | Licensed under the Apache License v2.0 with LLVM Exceptions. | 
|  | See https://llvm.org/LICENSE.txt for license information. | 
|  | SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | --> | 
|  |  | 
|  | <head> | 
|  | <meta charset="utf-8" /> | 
|  | <title>IREE Static Web Sample</title> | 
|  | <meta name="viewport" content="width=device-width, initial-scale=1"> | 
|  | <link rel="icon" href="./ghost.svg" type="image/svg+xml"> | 
|  |  | 
|  | <script src="./easeljs.min.js"></script> | 
|  | <script src="./iree_api.js"></script> | 
|  | </head> | 
|  |  | 
|  | <body style="background-color: #2b2c30; color: #ABB2BF"> | 
|  | <h1>IREE Static Web Sample</h1> | 
|  |  | 
|  | <canvas id="drawingCanvas" width="256" height="256" | 
|  | style="border:2px solid #000000; background-color: #FFFFFF;" | 
|  | oncontextmenu="return false;"> | 
|  | </canvas> | 
|  | <canvas id="rescaledCanvas" width="28" height="28" | 
|  | style="border:2px solid #000000; background-color: #FFFFFF;"> | 
|  | </canvas> | 
|  |  | 
|  | <br> | 
|  | <div style="border:2px solid #000000; background-color: #CCCCCC; padding: 8px; color: #111111; width:440px"> | 
|  | <button id="predictButton" disabled onclick="predictDigit()">Predict handwritten digit</button> | 
|  | <button id="clearCanvasButton" onclick="clearCanvas()">Clear canvas</button> | 
|  | <br> | 
|  | Prediction result: <div id="predictionResult" style="display:inline"></div> | 
|  | </div> | 
|  |  | 
|  | <script> | 
|  | // <canvas> drawing using easeljs forked from: | 
|  | //   https://createjs.com/demos/easeljs/curveto | 
|  | //   https://github.com/CreateJS/EaselJS/blob/master/examples/CurveTo.html | 
|  |  | 
|  | const predictButtonElement = document.getElementById('predictButton'); | 
|  | const predictionResultElement = document.getElementById('predictionResult'); | 
|  | const drawingCanvasElement = document.getElementById("drawingCanvas"); | 
|  | const rescaledCanvasElement = document.getElementById("rescaledCanvas"); | 
|  | const rescaledCanvasContext = rescaledCanvasElement.getContext("2d"); | 
|  | let stage; | 
|  | let drawingCanvasShape; | 
|  | let oldPt, oldMidPt; | 
|  | let titleText; | 
|  | let ireeInitialized = false; | 
|  | const primaryColor = "#000000"; | 
|  | const eraseColor = "#FFFFFF"; | 
|  | const stroke = 32; | 
|  |  | 
|  | function predictDigit() { | 
|  | // TODO(scotttodd): debounce / rate limit this? | 
|  | ireePredictDigit(getRescaledCanvasData()).then((result) => { | 
|  | predictionResultElement.innerHTML = result; | 
|  | }).catch((error) => { | 
|  | console.error('error predicting digit:', error); | 
|  | predictionResultElement.innerHTML = "<b>" + error + "</b>"; | 
|  | }); | 
|  | } | 
|  |  | 
|  | function clearCanvas() { | 
|  | stage.clear(); | 
|  | stage.removeAllChildren(); | 
|  |  | 
|  | drawingCanvasShape = new createjs.Shape(); | 
|  | stage.addChild(drawingCanvasShape); | 
|  | stage.update(); | 
|  |  | 
|  | updateRescaledCanvas(); | 
|  | } | 
|  |  | 
|  | function initDrawing() { | 
|  | rescaledCanvasContext.imageSmoothingEnabled = false; | 
|  | rescaledCanvasContext.mozImageSmoothingEnabled = false; | 
|  | rescaledCanvasContext.webkitImageSmoothingEnabled = false; | 
|  | rescaledCanvasContext.msImageSmoothingEnabled = false; | 
|  |  | 
|  | stage = new createjs.Stage(drawingCanvasElement); | 
|  | stage.autoClear = false; | 
|  | stage.enableDOMEvents(true); | 
|  |  | 
|  | createjs.Touch.enable(stage); | 
|  | createjs.Ticker.framerate = 24; | 
|  |  | 
|  | stage.addEventListener("stagemousedown", handleMouseDown); | 
|  | stage.addEventListener("stagemouseup", handleMouseUp); | 
|  |  | 
|  | drawingCanvasShape = new createjs.Shape(); | 
|  | stage.addChild(drawingCanvasShape); | 
|  |  | 
|  | // Add instruction text. | 
|  | titleText = new createjs.Text("Click and Drag to draw", "18px Arial", "#000000"); | 
|  | titleText.x = 30; | 
|  | titleText.y = 100; | 
|  | stage.addChild(titleText); | 
|  |  | 
|  | stage.update(); | 
|  | } | 
|  |  | 
|  | function handleMouseDown(event) { | 
|  | if (!event.primary && !event.secondary) { return; } | 
|  |  | 
|  | if (stage.contains(titleText)) { | 
|  | stage.clear(); | 
|  | stage.removeChild(titleText); | 
|  | } | 
|  |  | 
|  | oldPt = new createjs.Point(stage.mouseX, stage.mouseY); | 
|  | oldMidPt = oldPt.clone(); | 
|  | stage.addEventListener("stagemousemove", handleMouseMove); | 
|  | } | 
|  |  | 
|  | function handleMouseMove(event) { | 
|  | if (!event.primary && !event.secondary) { return; } | 
|  |  | 
|  | const midPt = new createjs.Point( | 
|  | oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1); | 
|  |  | 
|  | const color = event.nativeEvent.which == 1 ? primaryColor : eraseColor; | 
|  | drawingCanvasShape.graphics.clear() | 
|  | .setStrokeStyle(stroke, 'round', 'round') | 
|  | .beginStroke(color).moveTo(midPt.x, midPt.y) | 
|  | .curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y); | 
|  |  | 
|  | oldPt.x = stage.mouseX; | 
|  | oldPt.y = stage.mouseY; | 
|  | oldMidPt.x = midPt.x; | 
|  | oldMidPt.y = midPt.y; | 
|  |  | 
|  | stage.update(); | 
|  | updateRescaledCanvas(); | 
|  |  | 
|  | if (ireeInitialized) { | 
|  | predictDigit(); | 
|  | } | 
|  | } | 
|  |  | 
|  | function handleMouseUp(event) { | 
|  | if (!event.primary && !event.default) { return; } | 
|  | stage.removeEventListener("stagemousemove", handleMouseMove); | 
|  | } | 
|  |  | 
|  | function updateRescaledCanvas() { | 
|  | rescaledCanvasContext.clearRect( | 
|  | 0, 0, rescaledCanvasElement.width, rescaledCanvasElement.height); | 
|  | rescaledCanvasContext.drawImage( | 
|  | drawingCanvasElement, | 
|  | /*sx=*/0, /*sy=*/0, | 
|  | /*sWidth=*/256, /*sHeight=*/256, | 
|  | /*dx=*/0, /*dy=*/0, | 
|  | /*dWidth=*/28, /*dHeight=*/28); | 
|  | } | 
|  |  | 
|  | function getRescaledCanvasData() { | 
|  | return rescaledCanvasContext.getImageData(0, 0, 28, 28); | 
|  | } | 
|  |  | 
|  | initDrawing(); | 
|  |  | 
|  | ireeInitializeWorker().then((result) => { | 
|  | predictButtonElement.disabled = false; | 
|  | ireeInitialized = true; | 
|  | }).catch((error) => { | 
|  | console.error("Failed to initialize IREE, error: '" + error + "'"); | 
|  | }); | 
|  | </script> | 
|  | </body> | 
|  |  | 
|  | </html> |