创建包含了一个
Canvas
的页面,Canvas
上将绘制一个直径周而复始变大变小的红色的圆。其中:
- C代码负责图像数据的管理以及圆的绘制;
- JavaScript代码负责图像数据向
Canvas
的更新及动画调度。
创建canvas.cc:
#ifndef EM_PORT_API
#if defined(__EMSCRIPTEN__)
#include <emscripten.h>
#if defined(__cplusplus)
#define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
#else
#define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
#endif
#else
#if defined(__cplusplus)
#define EM_PORT_API(rettype) extern "C" rettype
#else
#define EM_PORT_API(rettype) rettype
#endif
#endif
#endif
#include <stdio.h>
#include <malloc.h>
#include <stdint.h>
uint8_t *img_buf = NULL;
int img_width = 0, img_height = 0;
EM_PORT_API(uint8_t*) get_img_buf(int w, int h) {
if (img_buf == NULL || w != img_width || h != img_height) {
if (img_buf) {
free(img_buf);
}
img_buf = (uint8_t*)malloc(w * h * 4);
img_width = w;
img_height = h;
}
return img_buf;
}
EM_PORT_API(void) draw_circle(int cx, int cy, int radii) {
int sq = radii * radii;
for (int y = 0; y < img_height; y++) {
for (int x = 0; x < img_width; x++) {
int d = (y - cy) * (y - cy) + (x - cx) * (x - cx);
if (d < sq) {
img_buf[(y * img_width + x) * 4] = 255; //r
img_buf[(y * img_width + x) * 4 + 1] = 0; //g
img_buf[(y * img_width + x) * 4 + 2] = 0; //b
img_buf[(y * img_width + x) * 4 + 3] = 255; //a
}
else {
img_buf[(y * img_width + x) * 4] = 0; //r
img_buf[(y * img_width + x) * 4 + 1] = 255; //g
img_buf[(y * img_width + x) * 4 + 2] = 255; //b
img_buf[(y * img_width + x) * 4 + 3] = 255; //a
}
}
}
}
img_buf
指向用于保存位图数据的缓冲区,get_img_buf()
函数将根据传入的参数判断是否需要重新创建缓冲区。draw_circle()
函数将在指定位置以指定半径填充绘制圆。
创建canvas.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
Module = {};
Module.onRuntimeInitialized = function () {
var canvas = document.getElementById('myCanvas');
canvas.width = 400;
canvas.height = 400;
window.requestAnimationFrame(update);
}
var radii = 0, delta = 1;
function update() {
var buf_addr = Module._get_img_buf(400, 400);
Module._draw_circle(200, 200, radii);
radii += delta;
if (radii > 200 || radii < 0) delta = -delta;
var u8o = new Uint8ClampedArray(Module.HEAPU8.subarray(buf_addr, buf_addr + 400 * 400 * 4));
var imgData = new ImageData(u8o, 400, 400);
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.putImageData(imgData, 0, 0);
window.requestAnimationFrame(update);
}
</script>
<script src="canvas.js"></script>
</body>
</html>
html中声明了id为
myCanvas
的Canvas
元素,在每帧更新时,从Emscripten运行时Module
中取出图像数据,创建ImageData
对象imgData
,并将imgData
通过CanvasRenderingContext2D
对象ctx
更新到Canvas
上去。
编译canvas.cc:
emcc code/1209/canvas.cc -o code/1209/canvas.js
运行canvas.html后得到一个不断变化的红色圆。
Canvas
对象已经内置包括圆、矩形、扇形等多种几何图形的绘制方法,上述例程使用自定义的draw_circle()
函数更多是为了演示在如何在C语言中操作位图图像,以及如何提取图像并更新到Canvas
。Canvas
、ImageData
、CanvasRenderingContext2D
等相关对象的详细资料,可参考MDN。