编译 zbar 到 WebAssembly

工作上需要前端实现扫描二维码的功能,这部分属于图像识别的工作,图像识别相关的库基本都是 C 语言库,所以需要将这些库编译到 WebAssembly,然后在前端调用。

# 胶水函数

我们需要将 C 语言的函数暴露出来,这样前端才能调用这些函数,这部分代码称为胶水函数。下面列举了一个简单的例子。

#include <emscripten.h>
#include <zbar.h>
#include <stdio.h>
#include <stdlib.h>

#define EXPORT EMSCRIPTEN_KEEPALIVE
typedef int int32_t;
typedef unsigned int uint32_t;

EXPORT zbar_image_scanner_t* ImageScanner_create() {
  return zbar_image_scanner_create();
}
1
2
3
4
5
6
7
8
9
10
11
12

# 编写 Makefile

ZBAR_VERSION = 0.23.90
ZBAR_SRC = zbar-$(ZBAR_VERSION)

SRC = src
BUILD = build
DIST = dist

EM_VERSION = 3.1.44

# See https://emscripten.org/docs/tools_reference/emcc.html
EMCC = emcc
EMMAKE = emmake
EMCONFIG = emconfigure

ZBAR_DEPS = $(ZBAR_SRC)/make.done
ZBAR_OBJS = $(ZBAR_SRC)/zbar/*.o $(ZBAR_SRC)/zbar/*/*.o
ZBAR_INC = -I $(ZBAR_SRC)/include/ -I $(ZBAR_SRC)/

# See https://github.com/emscripten-core/emscripten/blob/main/src/settings.js
EMCC_FLAGS = -Oz -Wall -Werror -s ALLOW_MEMORY_GROWTH=1 \
	-s EXPORTED_FUNCTIONS="['_malloc','_free']" \
	-s MODULARIZE=1 -s EXPORT_NAME=zbarWasm

.PHONY: all build clean-build clean

all: build

build: $(BUILD)/zbar.js $(BUILD)/zbar.wasm

clean-build:
	-rm -rf $(DIST) $(BUILD)

clean: clean-build
	-rm $(ZBAR_SRC).tar.gz
	-rm -rf $(ZBAR_SRC)

$(BUILD)/zbar.wasm $(BUILD)/zbar.js: $(ZBAR_DEPS) $(SRC)/export.c
	mkdir -p $(BUILD)/
	$(EMCC) $(EMCC_FLAGS) -o $(BUILD)/zbar.js -sEXPORT_ES6 $(SRC)/export.c $(ZBAR_INC) $(ZBAR_OBJS)

$(ZBAR_DEPS): $(ZBAR_SRC)/Makefile
	cd $(ZBAR_SRC) && $(EMMAKE) make CFLAGS=-Os CXXFLAGS=-Os \
		DEFS="-DZNO_MESSAGES -DHAVE_CONFIG_H"
	touch -m $(ZBAR_DEPS)

$(ZBAR_SRC)/Makefile: $(ZBAR_SRC)/configure
	cd $(ZBAR_SRC) && $(EMCONFIG) ./configure --without-x --without-xshm \
		--without-xv --without-jpeg --without-libiconv-prefix \
		--without-imagemagick --without-npapi --without-gtk \
		--without-python --without-qt --without-xshm --disable-video \
		--disable-pthread --disable-assert --host=x86_64-linux-gnu

$(ZBAR_SRC)/configure: $(ZBAR_SRC).tar.gz
	tar zxvf $(ZBAR_SRC).tar.gz
	touch -m $(ZBAR_SRC)/configure

$(ZBAR_SRC).tar.gz:
	curl -L -o $(ZBAR_SRC).tar.gz https://linuxtv.org/downloads/zbar/zbar-$(ZBAR_VERSION).tar.gz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

Makefile 中定义了编译 zbar 的流程,我们首先使用 curl 下载 zbar 的源码,然后解压,然后配置编译参数,最后编译生成 zbar 的调用 JS 和 WASM 文件。注意在配置参数中我们禁用了一些功能,因为这些功能在 WebAssembly 中不需要,并且在指定 host 的时候需要根据运行环境设置正确的参数,如果是运行在 Github Actions 中,需要设置为 x86_64-linux-gnu。如果是 M1 芯片的 Mac,需要设置为 arm

# 函数封装

虽然我们从 zbar 暴露出来关键的图像处理相关的函数,但是用起来还不是那么方便,所以我们需要写一点 Typescript 的代码来封装这些函数。然后使用 rollup 打包成 JS 文件,并生成对应的 TS 类型文件。这部分实现细节就不赘述了。

# 总结

编译 zbar 到 WebAssembly 的过程并不复杂,但是需要注意一些细节,比如编译参数的设置,胶水函数的编写等。这部分工作需要一定的 C 语言基础,如果没有的话,可以参考一些现成的例子,然后根据自己的需求进行修改。我也是参考网上的例子,然后根据自己的需求进行修改的。

# 参考

undecaf/zbar-wasm (opens new window)

acgotaku/zbar-wasm (opens new window)