[canvaskit] Add demo on how to decode images in web worker
Change-Id: I3c9107799c5df2c39a8ea8ddf106978786de79f7 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/317389 Reviewed-by: Yegor Jbanov <yjbanov@google.com> Reviewed-by: Nathaniel Nifong <nifong@google.com>
This commit is contained in:
parent
952f8f17e4
commit
c21dc07a78
44
demos.skia.org/demos/image_decode_web_worker/index.html
Normal file
44
demos.skia.org/demos/image_decode_web_worker/index.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<title>Image Decoding Demo</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 {
|
||||
border: 1px dashed grey;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<h1>CanvasKit loading images in a webworker (using browser-based decoders)</h1>
|
||||
<p>NOTE: this demo currently only works in chromium-based browsers, where
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas#Browser_compatibility">
|
||||
Offscreen Canvas
|
||||
</a>
|
||||
is supported.
|
||||
</p>
|
||||
|
||||
<div class="canvas-container">
|
||||
<h2>Decoding on main thread</h2>
|
||||
<canvas id="main-thread-canvas" width=500 height=500></canvas>
|
||||
<div>
|
||||
<button id="load-button-main">Decode Image on Main Thread</button>
|
||||
<button id="load-button-web">Decode Image with Web Worker</button>
|
||||
<button id="clear-button">Clear Image</button>
|
||||
</div>
|
||||
<p>
|
||||
Notice that decoding the image on the main thread pauses the circle animation until the
|
||||
image is ready to be drawn, where as decoding it in a webworker does not have this pause
|
||||
(or at least not as drastic a pause). You may want to reload the page, as browsers are
|
||||
smart enough to not have to re-decode the image on subsequent requests.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<script type="text/javascript" src="https://particles.skia.org/static/canvaskit.js"></script>
|
||||
<script type="text/javascript" src="main.js"></script>
|
77
demos.skia.org/demos/image_decode_web_worker/main.js
Normal file
77
demos.skia.org/demos/image_decode_web_worker/main.js
Normal file
@ -0,0 +1,77 @@
|
||||
// Inspired by https://gist.github.com/ahem/d19ee198565e20c6f5e1bcd8f87b3408
|
||||
const worker = new Worker('worker.js');
|
||||
|
||||
const canvasKitInitPromise =
|
||||
CanvasKitInit({locateFile: (file) => 'https://particles.skia.org/static/'+file});
|
||||
|
||||
const bigImagePromise =
|
||||
fetch('https://upload.wikimedia.org/wikipedia/commons/3/30/Large_Gautama_Buddha_statue_in_Buddha_Park_of_Ravangla%2C_Sikkim.jpg')
|
||||
.then((response) => response.blob());
|
||||
|
||||
Promise.all([
|
||||
canvasKitInitPromise,
|
||||
bigImagePromise
|
||||
]).then(([
|
||||
CanvasKit,
|
||||
imageBlob
|
||||
]) => {
|
||||
const surface = CanvasKit.MakeWebGLCanvasSurface('main-thread-canvas', null);
|
||||
if (!surface) {
|
||||
throw 'Could not make main thread canvas surface';
|
||||
}
|
||||
|
||||
const paint = new CanvasKit.SkPaint();
|
||||
paint.setColor(CanvasKit.RED);
|
||||
|
||||
let decodedImage;
|
||||
// This animation draws a red circle oscillating from the center of the canvas.
|
||||
// It is there to show the lag introduced by decoding the image on the main
|
||||
// thread.
|
||||
const drawFrame = (canvas) => {
|
||||
canvas.clear(CanvasKit.WHITE);
|
||||
|
||||
if (decodedImage) {
|
||||
canvas.drawImageRect(decodedImage,
|
||||
CanvasKit.LTRBRect(0, 0, 3764, 5706), // original size of the image
|
||||
CanvasKit.LTRBRect(0, 0, 500, 800), // scaled down
|
||||
null); // no paint needed
|
||||
}
|
||||
canvas.drawCircle(250, 250, 200 * Math.abs(Math.sin(Date.now() / 1000)), paint);
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
};
|
||||
surface.requestAnimationFrame(drawFrame);
|
||||
|
||||
|
||||
document.getElementById('load-button-main').addEventListener('click', () => {
|
||||
if (decodedImage) {
|
||||
decodedImage.delete();
|
||||
decodedImage = null;
|
||||
}
|
||||
const imgBitmapPromise = createImageBitmap(imageBlob);
|
||||
imgBitmapPromise.then((imgBitmap) => {
|
||||
decodedImage = CanvasKit.MakeImageFromCanvasImageSource(imgBitmap);
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('load-button-web').addEventListener('click', () => {
|
||||
if (decodedImage) {
|
||||
decodedImage.delete();
|
||||
decodedImage = null;
|
||||
}
|
||||
worker.postMessage(imageBlob);
|
||||
});
|
||||
worker.addEventListener('message', (e) => {
|
||||
const decodedBuffer = e.data.decodedArrayBuffer;
|
||||
const pixels = new Uint8Array(decodedBuffer);
|
||||
decodedImage = CanvasKit.MakeImage(pixels, e.data.width, e.data.height,
|
||||
CanvasKit.AlphaType.Unpremul, CanvasKit.ColorType.RGBA_8888, CanvasKit.SkColorSpace.SRGB);
|
||||
});
|
||||
document.getElementById('clear-button').addEventListener('click', () => {
|
||||
if (decodedImage) {
|
||||
decodedImage.delete();
|
||||
decodedImage = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
23
demos.skia.org/demos/image_decode_web_worker/worker.js
Normal file
23
demos.skia.org/demos/image_decode_web_worker/worker.js
Normal file
@ -0,0 +1,23 @@
|
||||
// This worker listens for a message that is the blob of data that is an encoded image.
|
||||
// In principle, this worker could also load the image, but I didn't want to introduce
|
||||
// network lag in this comparison. When it has decoded the image and converted it into
|
||||
// unpremul bytes, it returns the width, height, and the pixels in an array buffer via
|
||||
// a worker message.
|
||||
self.addEventListener('message', (e) => {
|
||||
const blob = e.data;
|
||||
createImageBitmap(blob).then((bitmap) => {
|
||||
const oCanvas = new OffscreenCanvas(bitmap.width, bitmap.height);
|
||||
const ctx2d = oCanvas.getContext('2d');
|
||||
ctx2d.drawImage(bitmap, 0, 0);
|
||||
|
||||
const imageData = ctx2d.getImageData(0, 0, bitmap.width, bitmap.height);
|
||||
const arrayBuffer = imageData.data.buffer;
|
||||
self.postMessage({
|
||||
width: bitmap.width,
|
||||
height: bitmap.height,
|
||||
decodedArrayBuffer: arrayBuffer
|
||||
}, [
|
||||
arrayBuffer // give up ownership of this object
|
||||
]);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user