ในหัวข้อ WebAssembly คืออะไรและมาจากไหน เราได้อธิบายวิธีที่ Google ทำ WebAssembly ในวันนี้ ในบทความนี้ เราจะแสดงวิธีการรวบรวมโปรแกรม C ที่มีอยู่ mkbitmap
ไปยัง WebAssembly สิ่งนี้ซับซ้อนกว่าตัวอย่าง สวัสดีทุกคน เช่น การทำงานกับไฟล์ การสื่อสารระหว่าง WebAssembly และ JavaScript และการวาดภาพไปยัง Canvas แต่ก็ยังคงจัดการได้มากพอที่จะไม่ทำให้คุณรู้สึกว่าเยอะเกินไป
บทความนี้เขียนขึ้นสำหรับนักพัฒนาเว็บที่ต้องการเรียนรู้ WebAssembly และแสดงวิธีที่คุณอาจดำเนินการต่อ หากต้องการคอมไพล์เนื้อหาอย่างเช่น mkbitmap
ไปยัง WebAssembly ขอเตือนไว้ก่อนว่า การไม่ได้รับแอปหรือไลบรารีที่คอมไพล์เมื่อเรียกใช้ครั้งแรกนั้นเป็นเรื่องปกติ จึงเป็นเหตุผลที่ขั้นตอนบางขั้นตอนที่อธิบายไว้ด้านล่างใช้งานไม่ได้ เราจึงจำเป็นต้องย้อนกลับและลองอีกครั้ง บทความนี้ไม่ได้แสดงคำสั่งการคอมไพล์ครั้งสุดท้ายราวกับว่าเครื่องตกลงไปจากท้องฟ้า แต่จะอธิบายความคืบหน้าที่แท้จริงของข้าพเจ้า ซึ่งทำให้เกิดความไม่พอใจ
เกี่ยวกับ mkbitmap
โปรแกรม mkbitmap
C จะอ่านอิมเมจและดำเนินการตามอย่างน้อย 1 อย่างต่อไปนี้กับอิมเมจดังกล่าว โดยเรียงตามลำดับดังนี้ การกลับ การกรอง High Pass, การปรับขนาด และการกำหนดเกณฑ์ ควบคุมและเปิดหรือปิดการดำเนินการแต่ละอย่างได้ การใช้งานหลัก mkbitmap
คือการแปลงรูปภาพสีหรือโทนสีเทาให้อยู่ในรูปแบบที่เหมาะสำหรับโปรแกรมอื่นๆ โดยเฉพาะโปรแกรมการติดตามภาพ potrace
ที่เป็นพื้นฐานของ SVGcode เนื่องจากเป็นเครื่องมือประมวลผลล่วงหน้า mkbitmap
จึงมีประโยชน์อย่างยิ่งสำหรับการแปลงภาพลายเส้นที่สแกนมา เช่น การ์ตูนหรือข้อความที่เขียนด้วยลายมือให้เป็นรูปภาพ 2 ระดับความละเอียดสูง
คุณใช้ mkbitmap
ได้โดยส่งตัวเลือกจำนวนหนึ่งและชื่อไฟล์เดียวหรือหลายรายการ โปรดดูรายละเอียดทั้งหมดที่หน้าหน้าแมนของเครื่องมือ
$ mkbitmap [options] [filename...]
![รูปภาพการ์ตูนสี](https://web.dev/static/articles/compiling-mkbitmap-to-webassembly/image/cartoon-image-color.png?authuser=6&hl=th)
![แปลงรูปภาพการ์ตูนเป็นโทนสีเทาหลังจากประมวลผลล่วงหน้า](https://web.dev/static/articles/compiling-mkbitmap-to-webassembly/image/cartoon-image-converted-97142bc66e12e.png?authuser=6&hl=th)
mkbitmap -f 2 -s 2 -t 0.48
(แหล่งที่มา)รับโค้ด
ขั้นตอนแรกคือการได้รับซอร์สโค้ดของ mkbitmap
ซึ่งมีอยู่ในเว็บไซต์ของโปรเจ็กต์ ในขณะที่เขียนบทความนี้ potrace-1.16.tar.gz เป็นเวอร์ชันล่าสุด
คอมไพล์และติดตั้งในเครื่อง
ขั้นตอนถัดไปคือคอมไพล์และติดตั้งเครื่องมือในเครื่องเพื่อสัมผัสการทำงานของเครื่องมือ ไฟล์ INSTALL
มีวิธีการต่อไปนี้
cd
ไปยังไดเรกทอรีที่มีซอร์สโค้ดของแพ็กเกจและประเภท./configure
เพื่อกำหนดค่าแพ็กเกจสำหรับระบบของคุณการเรียกใช้
configure
อาจใช้เวลาสักครู่ ขณะทำงาน เครื่องจะพิมพ์ข้อความบางข้อความ ที่บอกว่ากำลังตรวจหาคุณลักษณะใดอยู่พิมพ์
make
เพื่อรวบรวมแพ็กเกจหรือเลือกพิมพ์
make check
เพื่อเรียกใช้การทดสอบด้วยตนเองทั้งหมดที่มาพร้อมกับแพ็กเกจ ซึ่งโดยปกติจะใช้ไบนารีที่ถอนการติดตั้งซึ่งเพิ่งสร้างขึ้นพิมพ์
make install
เพื่อติดตั้งโปรแกรม ไฟล์ข้อมูล และเอกสารประกอบ เมื่อติดตั้งลงในคำนำหน้าที่เป็นของรูท ขอแนะนำให้กำหนดค่าและสร้างแพ็กเกจให้เป็นผู้ใช้ทั่วไป และเฉพาะเฟสmake install
ที่ดำเนินการด้วยสิทธิ์รูทเท่านั้น
เมื่อทำตามขั้นตอนเหล่านี้แล้ว คุณควรได้ไฟล์ปฏิบัติการ 2 ไฟล์ ได้แก่ potrace
และ mkbitmap
ซึ่งอย่างหลังคือจุดโฟกัสของบทความนี้ คุณสามารถยืนยันว่าโค้ดทำงานถูกต้องโดยเรียกใช้ mkbitmap --version
นี่คือเอาต์พุตของทั้ง 4 ขั้นตอนจากเครื่องของฉันซึ่งถูกตัดออกบางส่วนเพื่อความสั้นกระชับ
ขั้นตอนที่ 1 ./configure
:
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... ./install-sh -c -d
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
[…]
config.status: executing libtool commands
ขั้นตอนที่ 2 make
:
$ make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
Making all in src
clang -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
[…]
make[2]: Nothing to be done for `all-am'.
ขั้นตอนที่ 3 make check
:
$ make check
Making check in src
make[1]: Nothing to be done for `check'.
Making check in doc
make[1]: Nothing to be done for `check'.
[…]
============================================================================
Testsuite summary for potrace 1.16
============================================================================
# TOTAL: 8
# PASS: 8
# SKIP: 0
# XFAIL: 0
# FAIL: 0
# XPASS: 0
# ERROR: 0
============================================================================
make[1]: Nothing to be done for `check-am'.
ขั้นตอนที่ 4 sudo make install
:
$ sudo make install
Password:
Making install in src
.././install-sh -c -d '/usr/local/bin'
/bin/sh ../libtool --mode=install /usr/bin/install -c potrace mkbitmap '/usr/local/bin'
[…]
make[2]: Nothing to be done for `install-data-am'.
หากต้องการตรวจสอบว่าใช้งานได้หรือไม่ ให้เรียกใช้ mkbitmap --version
$ mkbitmap --version
mkbitmap 1.16. Copyright (C) 2001-2019 Peter Selinger.
หากได้รับรายละเอียดเวอร์ชัน แสดงว่าคุณคอมไพล์และติดตั้ง mkbitmap
สำเร็จแล้ว ขั้นตอนถัดไปทำให้ขั้นตอนที่เทียบเท่ากันใช้ได้กับ WebAssembly
คอมไพล์ mkbitmap
ไปยัง WebAssembly
Emscripten เป็นเครื่องมือสำหรับคอมไพล์โปรแกรม C/C++ ไปยัง WebAssembly เอกสารประกอบโครงการก่อสร้างของ Emscripten ระบุข้อมูลต่อไปนี้
การสร้างโครงการขนาดใหญ่ด้วย Emscripten นั้นง่ายมาก Emscripten มีสคริปต์ง่ายๆ 2 รายการที่กำหนดค่าไฟล์ให้คุณใช้
emcc
เพื่อแทนที่gcc
โดยมากแล้ว ส่วนที่เหลือของระบบบิลด์ปัจจุบันจะยังคงเหมือนเดิม
จากนั้นเอกสารก็จะมีต่อไป (แก้ไขเล็กน้อยเพื่อความกระชับ)
ลองพิจารณากรณีที่ปกติคุณสร้างด้วยคำสั่งต่อไปนี้
./configure
make
หากต้องการสร้างด้วย Emscripten คุณจะใช้คำสั่งต่อไปนี้แทน
emconfigure ./configure
emmake make
ดังนั้น ./configure
จะกลายเป็น emconfigure ./configure
และ make
จะกลายเป็น emmake make
รายการต่อไปนี้แสดงวิธีดำเนินการด้วย mkbitmap
ขั้นตอนที่ 0 make clean
:
$ make clean
Making clean in src
rm -f potrace mkbitmap
test -z "" || rm -f
rm -rf .libs _libs
[…]
rm -f *.lo
ขั้นตอนที่ 1 emconfigure ./configure
:
$ emconfigure ./configure
configure: ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... ./install-sh -c -d
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
[…]
config.status: executing libtool commands
ขั้นตอนที่ 2 emmake make
:
$ emmake make
make: make
/Applications/Xcode.app/Contents/Developer/usr/bin/make all-recursive
Making all in src
/opt/homebrew/Cellar/emscripten/3.1.36/libexec/emcc -DHAVE_CONFIG_H -I. -I.. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
[…]
make[2]: Nothing to be done for `all'.
หากทุกอย่างดูดีแล้ว ตอนนี้ควรมีไฟล์ .wasm
ไฟล์อยู่ที่บางไฟล์ในไดเรกทอรี คุณสามารถค้นหาได้โดยเรียกใช้ find . -name "*.wasm"
:
$ find . -name "*.wasm"
./a.wasm
./src/mkbitmap.wasm
./src/potrace.wasm
2 รายการสุดท้ายดูน่าสนใจดี ดังนั้น cd
จะอยู่ในไดเรกทอรี src/
นอกจากนี้ยังมีไฟล์ใหม่ที่เกี่ยวข้องอีก 2 ไฟล์ ได้แก่ mkbitmap
และ potrace
สำหรับบทความนี้ มีเพียง mkbitmap
เท่านั้นที่เกี่ยวข้อง การไม่มีส่วนขยาย .js
จึงค่อนข้างน่าสับสน แต่ที่จริงแล้วเป็นไฟล์ JavaScript ที่ยืนยันได้ด้วยการเรียก head
สั้นๆ ดังนี้
$ cd src/
$ head -n 20 mkbitmap
// include: shell.js
// The Module object: Our interface to the outside world. We import
// and export values on it. There are various ways Module can be used:
// 1. Not defined. We create it here
// 2. A function parameter, function(Module) { ..generated code.. }
// 3. pre-run appended it, var Module = {}; ..generated code..
// 4. External script tag defines var Module.
// We need to check if Module already exists (e.g. case 3 above).
// Substitution will be replaced with actual code on later stage of the build,
// this way Closure Compiler will not mangle it (e.g. case 4. above).
// Note that if you want to run closure, and also to use Module
// after the generated code, you will need to define var Module = {};
// before the code. Then that object will be used in the code, and you
// can continue to use Module afterwards as well.
var Module = typeof Module != 'undefined' ? Module : {};
// --pre-jses are emitted after the Module integration code, so that they can
// refer to Module (if they choose; they can also define Module)
เปลี่ยนชื่อไฟล์ JavaScript เป็น mkbitmap.js
โดยเรียกใช้ mv mkbitmap mkbitmap.js
(และ mv potrace potrace.js
ตามลำดับ หากต้องการ)
ตอนนี้ถึงเวลาสำหรับการทดสอบครั้งแรกเพื่อดูว่าไฟล์ทำงานได้โดยการเรียกใช้ไฟล์ด้วย Node.js บนบรรทัดคำสั่งหรือไม่โดยเรียกใช้ node mkbitmap.js --version
:
$ node mkbitmap.js --version
mkbitmap 1.16. Copyright (C) 2001-2019 Peter Selinger.
คุณคอมไพล์ mkbitmap
ไปยัง WebAssembly เรียบร้อยแล้ว ขั้นตอนถัดไปคือการทำให้เบราว์เซอร์ทำงานในเบราว์เซอร์
mkbitmap
ที่ใช้ WebAssembly ในเบราว์เซอร์
คัดลอกไฟล์ mkbitmap.js
และ mkbitmap.wasm
ไปยังไดเรกทอรีใหม่ที่ชื่อ mkbitmap
แล้วสร้างไฟล์ต้นแบบ HTML index.html
ที่โหลดไฟล์ JavaScript mkbitmap.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>mkbitmap</title>
</head>
<body>
<script src="mkbitmap.js"></script>
</body>
</html>
เริ่มต้นเซิร์ฟเวอร์ภายในที่แสดงไดเรกทอรี mkbitmap
และเปิดในเบราว์เซอร์ คุณจะเห็นข้อความแจ้งที่ขอให้คุณป้อนข้อมูล ซึ่งก็เป็นไปตามที่คาดไว้อยู่แล้ว เนื่องจากตามหน้า man ของเครื่องมือ "[i]หากไม่มีอาร์กิวเมนต์ชื่อไฟล์ mkbitmap จะทำหน้าที่เป็นตัวกรองโดยอ่านค่าจากอินพุตมาตรฐาน" ซึ่งโดยค่าเริ่มต้น Emscripten จะเป็น prompt()
ป้องกันการดำเนินการโดยอัตโนมัติ
หากต้องการหยุดไม่ให้ mkbitmap
ดำเนินการทันทีและต้องรออินพุตของผู้ใช้ คุณต้องเข้าใจออบเจ็กต์ Module
ของ Emscripten Module
เป็นออบเจ็กต์ JavaScript ร่วมที่มีแอตทริบิวต์ที่ Emscripten เรียกใช้โค้ด ณ จุดต่างๆ ในการดำเนินการ
คุณสามารถติดตั้งใช้งาน Module
เพื่อควบคุมการดำเนินการของโค้ดได้
เมื่อแอปพลิเคชัน Emscripten เริ่มทำงาน แอปจะดูค่าในออบเจ็กต์ Module
แล้วนำไปใช้
ในกรณีของ mkbitmap
ให้ตั้งค่า Module.noInitialRun
เป็น true
เพื่อป้องกันการเรียกใช้ครั้งแรกที่ทำให้ข้อความแจ้งปรากฏขึ้น สร้างสคริปต์ชื่อ script.js
โดยระบุก่อน <script src="mkbitmap.js"></script>
ใน index.html
และเพิ่มโค้ดต่อไปนี้ลงใน script.js
เมื่อคุณโหลดแอปซ้ำ ข้อความแจ้งจะหายไป
var Module = {
// Don't run main() at page load
noInitialRun: true,
};
สร้างบิลด์แบบแยกส่วนด้วย Flag บิลด์เพิ่มเติม
คุณใช้การสนับสนุนระบบไฟล์ของ Emscripten ได้ในModule.FS
เพื่อป้อนข้อมูลให้กับแอป ส่วนรวมการรองรับระบบไฟล์ของเอกสารจะระบุข้อมูลต่อไปนี้
Emscripten ตัดสินใจว่าจะรวมการรองรับระบบไฟล์โดยอัตโนมัติหรือไม่ โปรแกรมจำนวนมากไม่ต้องใช้ไฟล์ และการสนับสนุนระบบไฟล์ก็ไม่ได้มีความสำคัญในเรื่องขนาด ด้วยเหตุนี้ Emscripten จึงหลีกเลี่ยงการรวมไฟล์เมื่อไม่เห็นเหตุผล ซึ่งหมายความว่าหากโค้ด C/C++ ของคุณไม่ได้เข้าถึงไฟล์ ออบเจ็กต์
FS
และ API ระบบไฟล์อื่นๆ จะไม่รวมอยู่ในเอาต์พุต และในทางกลับกัน หากโค้ด C/C++ ใช้ไฟล์ ระบบจะรวมการรองรับระบบไฟล์ให้โดยอัตโนมัติ
ขออภัย mkbitmap
เป็นหนึ่งในกรณีที่ Emscripten ไม่รวมการรองรับระบบไฟล์โดยอัตโนมัติ คุณจึงต้องแจ้งให้ดำเนินการอย่างชัดเจน ซึ่งหมายความว่าคุณต้องทำตามขั้นตอน emconfigure
และ emmake
ที่อธิบายไว้ก่อนหน้านี้ โดยมีการตั้งค่าแฟล็กเพิ่มเติม 2-3 รายการผ่านอาร์กิวเมนต์ CFLAGS
แฟล็กต่อไปนี้อาจเป็นประโยชน์สำหรับโปรเจ็กต์อื่นๆ ด้วย
- ตั้งค่า
-sFILESYSTEM=1
เพื่อให้มีการรองรับระบบไฟล์ - ตั้งค่า
-sEXPORTED_RUNTIME_METHODS=FS,callMain
เพื่อให้ระบบส่งออกModule.FS
และModule.callMain
- ตั้งค่า
-sMODULARIZE=1
และ-sEXPORT_ES6
เพื่อสร้างโมดูล ES6 ที่ทันสมัย - ตั้งค่า
-sINVOKE_RUN=0
เพื่อป้องกันการเรียกใช้ครั้งแรกที่ทำให้ข้อความแจ้งปรากฏขึ้น
นอกจากนี้ในกรณีนี้ คุณจะต้องตั้งค่าแฟล็ก --host
เป็น wasm32
เพื่อบอกสคริปต์ configure
ว่าคุณกำลังคอมไพล์สำหรับ WebAssembly
คำสั่ง emconfigure
สุดท้ายมีลักษณะดังนี้
$ emconfigure ./configure --host=wasm32 CFLAGS='-sFILESYSTEM=1 -sEXPORTED_RUNTIME_METHODS=FS,callMain -sMODULARIZE=1 -sEXPORT_ES6 -sINVOKE_RUN=0'
อย่าลืมเรียกใช้ emmake make
อีกครั้งและคัดลอกไฟล์ที่สร้างขึ้นใหม่ไปยังโฟลเดอร์ mkbitmap
แก้ไข index.html
ให้โหลดเฉพาะโมดูล ES script.js
ซึ่งคุณจะนำเข้าโมดูล mkbitmap.js
ในภายหลัง
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>mkbitmap</title>
</head>
<body>
<!-- No longer load `mkbitmap.js` here -->
<script src="script.js" type="module"></script>
</body>
</html>
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
console.log(Module);
};
run();
เมื่อเปิดแอปในเบราว์เซอร์ตอนนี้ คุณจะเห็นออบเจ็กต์ Module
บันทึกไว้ในคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บและข้อความแจ้งจะหายไปเนื่องจากไม่มีการเรียกใช้ฟังก์ชัน main()
ของ mkbitmap
ในตอนเริ่มต้นแล้ว
เรียกใช้ฟังก์ชันหลักด้วยตนเอง
ขั้นตอนถัดไปคือการเรียกฟังก์ชัน main()
ของ mkbitmap
ด้วยตนเองโดยการเรียกใช้ Module.callMain()
ฟังก์ชัน callMain()
จะใช้อาร์เรย์ของอาร์กิวเมนต์ ซึ่งตรงกับสิ่งที่คุณจะส่งผ่านบรรทัดคำสั่งไปทีละรายการ หากในบรรทัดคำสั่งที่คุณจะเรียกใช้ mkbitmap -v
คุณจะเรียกใช้ Module.callMain(['-v'])
ในเบราว์เซอร์ โดยจะบันทึกหมายเลขเวอร์ชัน mkbitmap
ไปยังคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
Module.callMain(['-v']);
};
run();
เปลี่ยนเส้นทางเอาต์พุตมาตรฐาน
เอาต์พุตมาตรฐาน (stdout
) ตามค่าเริ่มต้นคือคอนโซล แต่คุณสามารถเปลี่ยนเส้นทางไปยังฟังก์ชันอื่นได้ เช่น ฟังก์ชันที่จัดเก็บเอาต์พุตไปยังตัวแปร ซึ่งหมายความว่าคุณจะเพิ่มเอาต์พุตลงใน HTML ได้โดยการตั้งค่าพร็อพเพอร์ตี้ Module.print
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
let consoleOutput = 'Powered by ';
const Module = await loadWASM({
print: (text) => (consoleOutput += text),
});
Module.callMain(['-v']);
document.body.textContent = consoleOutput;
};
run();
นำไฟล์อินพุตเข้าสู่ระบบไฟล์หน่วยความจำ
หากต้องการรับไฟล์อินพุตในระบบไฟล์หน่วยความจำ คุณต้องมี mkbitmap filename
ที่เทียบเท่ากันในบรรทัดคำสั่ง เพื่อให้เข้าใจวิธีที่เราดำเนินการนี้ ก่อนอื่น มาดูข้อมูลเบื้องต้นเกี่ยวกับวิธีที่ mkbitmap
คาดหวังอินพุตและสร้างเอาต์พุตของตัวเอง
รูปแบบอินพุตที่รองรับของ mkbitmap
ได้แก่ PNM (PBM, PGM, PPM) และ BMP รูปแบบเอาต์พุตคือ PBM สำหรับบิตแมป และ PGM สำหรับแผนที่สีเทา หากระบุอาร์กิวเมนต์ filename
mkbitmap
จะสร้างไฟล์เอาต์พุตที่มีชื่อมาจากชื่อไฟล์อินพุตโดยค่าเริ่มต้นด้วยการเปลี่ยนคำต่อท้ายเป็น .pbm
ตัวอย่างเช่น สำหรับชื่อไฟล์อินพุต example.bmp
ชื่อไฟล์เอาต์พุตจะเป็น example.pbm
Emscripten มีระบบไฟล์เสมือนที่จำลองระบบไฟล์ในเครื่อง เพื่อให้โค้ดแบบเนทีฟที่ใช้ API ไฟล์แบบซิงโครนัสสามารถคอมไพล์และเรียกใช้ได้โดยมีการเปลี่ยนแปลงเพียงเล็กน้อยหรือไม่มีเลย
หากต้องการให้ mkbitmap
อ่านไฟล์อินพุตเสมือนว่ามีการส่งไฟล์เป็นอาร์กิวเมนต์บรรทัดคำสั่ง filename
คุณจะต้องใช้ออบเจ็กต์ FS
ที่ Emscripten มีให้
ออบเจ็กต์ FS
ได้รับการสนับสนุนโดยระบบไฟล์ในหน่วยความจำ (โดยทั่วไปจะเรียกว่า MEMFS) และมีฟังก์ชัน writeFile()
ที่คุณใช้ในการเขียนไฟล์ไปยังระบบไฟล์เสมือน คุณใช้ writeFile()
ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้
หากต้องการยืนยันว่าการเขียนไฟล์ใช้งานได้ ให้เรียกใช้ฟังก์ชัน readdir()
ของออบเจ็กต์ FS
ที่มีพารามิเตอร์ '/'
คุณจะเห็น example.bmp
และไฟล์เริ่มต้นจำนวนหนึ่งที่สร้างขึ้นโดยอัตโนมัติเสมอ
โปรดทราบว่าการโทรไปยัง Module.callMain(['-v'])
เพื่อพิมพ์หมายเลขเวอร์ชันก่อนหน้านี้ถูกนำออกแล้ว อันเนื่องมาจากการที่ Module.callMain()
เป็นฟังก์ชันที่โดยทั่วไปแล้วคาดว่าจะทำงานเพียงครั้งเดียว
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
const buffer = await fetch('https://example.com/example.bmp').then((res) => res.arrayBuffer());
Module.FS.writeFile('example.bmp', new Uint8Array(buffer));
console.log(Module.FS.readdir('/'));
};
run();
การดำเนินการจริงครั้งแรก
เมื่อทุกอย่างพร้อมแล้ว ให้ใช้ mkbitmap
โดยเรียกใช้ Module.callMain(['example.bmp'])
บันทึกเนื้อหาของโฟลเดอร์ '/'
ของ MEMFS และคุณควรเห็นไฟล์เอาต์พุต example.pbm
ที่สร้างใหม่อยู่ข้างไฟล์อินพุต example.bmp
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
const buffer = await fetch('https://example.com/example.bmp').then((res) => res.arrayBuffer());
Module.FS.writeFile('example.bmp', new Uint8Array(buffer));
Module.callMain(['example.bmp']);
console.log(Module.FS.readdir('/'));
};
run();
นำไฟล์เอาต์พุตออกจากระบบไฟล์หน่วยความจำ
ฟังก์ชัน readFile()
ของออบเจ็กต์ FS
ช่วยให้สร้าง example.pbm
ในขั้นตอนสุดท้ายของระบบไฟล์หน่วยความจำได้ ฟังก์ชันนี้แสดงผล Uint8Array
ที่คุณแปลงเป็นออบเจ็กต์ File
แล้วบันทึกลงในดิสก์ เนื่องจากเบราว์เซอร์มักไม่รองรับไฟล์ PBM สำหรับการดูในเบราว์เซอร์โดยตรง
(มีวิธีที่สะดวกในการบันทึกไฟล์มากกว่า แต่การใช้ <a download>
ที่สร้างขึ้นแบบไดนามิกเป็นวิธีที่สนับสนุนอย่างกว้างขวางที่สุด) เมื่อบันทึกไฟล์แล้ว คุณสามารถเปิดในโปรแกรมดูรูปภาพที่ชอบ
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
const buffer = await fetch('https://example.com/example.bmp').then((res) => res.arrayBuffer());
Module.FS.writeFile('example.bmp', new Uint8Array(buffer));
Module.callMain(['example.bmp']);
const output = Module.FS.readFile('example.pbm', { encoding: 'binary' });
const file = new File([output], 'example.pbm', {
type: 'image/x-portable-bitmap',
});
const a = document.createElement('a');
a.href = URL.createObjectURL(file);
a.download = file.name;
a.click();
};
run();
เพิ่ม UI แบบอินเทอร์แอกทีฟ
ในจุดนี้ ไฟล์อินพุตจะฮาร์ดโค้ดและ mkbitmap
ทำงานโดยใช้พารามิเตอร์เริ่มต้น ขั้นตอนสุดท้ายคือให้ผู้ใช้เลือกไฟล์อินพุตแบบไดนามิก ปรับแต่งพารามิเตอร์ mkbitmap
แล้วเรียกใช้เครื่องมือตามตัวเลือกที่เลือกไว้
// Corresponds to `mkbitmap -o output.pbm input.bmp -s 8 -3 -f 4 -t 0.45`.
Module.callMain(['-o', 'output.pbm', 'input.bmp', '-s', '8', '-3', '-f', '4', '-t', '0.45']);
รูปแบบรูปภาพ PBM นั้นไม่ยากในการแยกวิเคราะห์ ดังนั้นด้วยโค้ด JavaScript บางรายการ คุณจะแสดงตัวอย่างรูปภาพเอาต์พุตได้ด้วย โปรดดูซอร์สโค้ดของการสาธิตแบบฝังที่ด้านล่าง
บทสรุป
ยินดีด้วย คุณคอมไพล์ mkbitmap
ไปยัง WebAssembly สำเร็จและทำงานในเบราว์เซอร์ได้แล้ว ก่อนหน้านี้ก็มีทางตันและคุณต้องคอมไพล์เครื่องมือมากกว่า 1 ครั้งจนกว่าเครื่องมือจะได้ผล แต่ตามที่ฉันได้เขียนไว้ข้างต้น นั่นเป็นส่วนหนึ่งของประสบการณ์ นอกจากนี้ อย่าลืมแท็ก webassembly
ของสแต็คโอเวอร์โฟลว์ หากพบปัญหา ขอให้สนุกกับการคอมไพล์
กิตติกรรมประกาศ
บทความนี้ได้รับการตรวจสอบโดย Sam Clegg และ Rachel Andrew