LoginSignup
16
14

More than 5 years have passed since last update.

libpngをemscriptenでコンパイルしてJSから使う

Last updated at Posted at 2016-04-15

動機

現状のブラウザ環境では、
pngからピクセル情報を得るためには
一旦canvasに書き込んでおいてからgetImageDataする必要がある。

このときpngをデコードするためにUIスレッドでdeflateが呼ばれるため、
大量の画像ファイルのピクセル情報にアクセスしようとすると非常に時間がかかる。

WebWorkerスレッドで並列的にpngをデコードしてピクセル処理したいが、
WebWorkerではcanvasが使えない(CanvasProxyというのが策定中のようだ?)(OffscreenCanvasというのもあるらしい)

そこで、WebWorker上でpng.jsなどのJSライブラリでデコードする必要があるのだが、ネイティブのデコードに比べれば遅い。
wasmが実用化すればlibpngをwasm化してデコードできるようになるので、
今回はとりあえずlibpngをemscriptenしてみた。

作業環境

emsdkで作った

$ emcc --version
emcc (Emscripten gcc/clang-like replacement) 1.35.0 ()
Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang --version
clang version 3.7.0 (https://github.com/kripken/emscripten-fastcomp-clang dbe68fecd03d6f646bd075963c3cc0e7130e5767) (https://github.com/kripken/emscripten-fastcomp 4e83be90903250ec5142edc57971ed4c633c5e25)
Target: x86_64-apple-darwin15.4.0
Thread model: posix

$ where clang
/Users/***/emsdk_portable/clang/e1.35.0_64bit/clang
/usr/bin/clang

$ where emcc
/Users/***/emsdk_portable/emscripten/1.35.0/emcc

zlib と libpng のダウンロード

libpngはzlibを使用しているので両方のソースをダウンロードする

wget http://zlib.net/zlib-1.2.8.tar.gz
wget http://download.sourceforge.net/libpng/libpng-1.6.21.tar.gz
tar zxvf zlib-1.2.8.tar.gz
tar zxvf libpng-1.6.21.tar.gz
rm zlib-1.2.8.tar.gz libpng-1.6.21.tar.gz

zlib のllvm bitcode化

コンパイル&リンクして ひとつのllvm bitcodeファイルにする

cd zlib-1.2.8
emconfigure ./configure
sed -i -e "s/^AR=.*$/AR=emcc/" Makefile
sed -i -e "s/libz\.a/libz.bc/" Makefile
emmake make

emcc は .a 拡張子でllvm bcを出力するのを許さないので .bc に変更する

libtool ではllvm bcをリンクできないのでemccに書き換える

これで libz.bc ができた

libpng のllvm bitcode化

cd libpng-1.6.21
sed -i -e "s/\#define PNG_ZLIB_VERNUM 0x1250/#define PNG_ZLIB_VERNUM 0x1280/g" ./pnglibconf.h
sed -i -e "s/ZPREFIX\=\'z\_\'/ZPREFIX='..\/zlib-1.2.8\/'/g" ./configure
emconfigure ./configure --with-zlib-prefix='../zlib-1.2.8/'
sed -i -e "s/^DEFAULT_INCLUDES \= \-I\./DEFAULT_INCLUDES = -I. -I..\/zlib-1.2.8\//g" ./Makefile
sed -i -e "s/^LIBS \= \-lz/LIBS = -L..\/zlib-1.2.8\//g" ./Makefile
emmake make --include-dir=../zlib-1.2.8/
emcc -static  -fno-common -DPIC  .libs/png.o .libs/pngerror.o .libs/pngget.o .libs/pngmem.o .libs/pngpread.o .libs/pngread.o .libs/pngrio.o .libs/pngrtran.o .libs/pngrutil.o .libs/pngset.o .libs/pngtrans.o .libs/pngwio.o .libs/pngwrite.o .libs/pngwtran.o .libs/pngwutil.o   -L../zlib-1.2.8/ -lc    -Wl,-soname -Wl,libpng16.16.dylib -Wl,-retain-symbols-file -Wl,libpng.sym -o .libs/libpng16.16.bc

zlib の zlib.hZLIB_VERNUM
libpng の pnglibconf.hPNG_ZLIB_VERNUM が一致していなければ無理やり一致させる(API変わっていたらダメなので本来は一致するzlib | libpngのバージョンを用意する)

また、configure の --with-zlib-prefix オプションが emconfigure を使うと何故か効かなくなるので ZPREFIX へ直接 zlib へのパスを書き込む

libpngの Makefile では共有ライブラリしか作れず、emccは共有ライブラリに対応していない(将来的にできるかもねとのこと)ので、改めてスタティックライブラリを作る

これで .libs/libpng16.16.bc ができる

自分のコードに組み込む

emcc -std=c11 -Wall -I./zlib-1.2.8 -I./libpng-1.6.21 -o ./obj/main.o -c ./src/main.c
emcc -O1 -o ./bin/a.out.js --pre-js ./src/em-pre.js --post-js ./src/em-post.js ./zlib-1.2.8/libz.bc ./libpng-1.6.21/.libs/libpng16.16.bc ./obj/main.o

コンパイルは普通に zlib と libpng のヘッダファイルのディレクトリを -I で参照させる

リンクでは ./zlib-1.2.8/libz.bc./libpng-1.6.21/.libs/libpng16.16.bc を含めること

JSから呼ぶ

emscripten の ccall などを利用し、JS側から呼ぶ。

libpngのAPIはファイルポインタを必要とするため、
FileSystem API を用いて事前にpngファイルのバッファを仮想FSに書き込んでおく必要がある。

サンプル

参考

16
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
14