در WebAssembly چیست و از کجا آمده است؟ ، توضیح دادم که چگونه به WebAssembly امروز رسیدیم. در این مقاله، من روش خود را برای کامپایل کردن یک برنامه C موجود، mkbitmap
، به WebAssembly به شما نشان خواهم داد. از مثال hello world پیچیدهتر است، زیرا شامل کار با فایلها، برقراری ارتباط بین WebAssembly و جاوا اسکریپت و کشیدن روی بوم میشود، اما همچنان به اندازهای قابل مدیریت است که شما را تحت تأثیر قرار ندهد.
این مقاله برای توسعه دهندگان وب نوشته شده است که می خواهند WebAssembly را یاد بگیرند و گام به گام نشان می دهد که اگر می خواهید چیزی مانند mkbitmap
را در WebAssembly کامپایل کنید چگونه می توانید ادامه دهید. به عنوان یک هشدار منصفانه، دریافت نکردن یک برنامه یا کتابخانه برای کامپایل در اولین اجرا کاملاً طبیعی است، به همین دلیل است که برخی از مراحل شرح داده شده در زیر کار نمیکنند، بنابراین من باید به عقب برگردم و دوباره امتحان کنم. این مقاله فرمان کامپایل نهایی جادویی را به گونهای نشان نمیدهد که انگار از آسمان افتاده است، بلکه پیشرفت واقعی من را توصیف میکند که شامل برخی ناامیدیها نیز میشود.
درباره mkbitmap
برنامه mkbitmap
C یک تصویر را می خواند و یک یا چند مورد از عملیات زیر را به ترتیب بر روی آن اعمال می کند: وارونگی، فیلتر بالاگذر، مقیاس گذاری و آستانه گذاری. هر عملیات را می توان به صورت جداگانه کنترل و روشن یا خاموش کرد. کاربرد اصلی mkbitmap
تبدیل تصاویر رنگی یا خاکستری به فرمتی مناسب به عنوان ورودی برای برنامه های دیگر است، به ویژه ردیابی برنامه potrace
که اساس SVGcode را تشکیل می دهد. به عنوان یک ابزار پیش پردازش، mkbitmap
به ویژه برای تبدیل هنر خط اسکن شده، مانند کارتون یا متن دست نویس، به تصاویر دوسطحی با وضوح بالا مفید است.
شما از mkbitmap
با ارسال تعدادی گزینه و یک یا چند نام فایل به آن استفاده می کنید. برای همه جزئیات، به صفحه مرد ابزار مراجعه کنید:
$ mkbitmap [options] [filename...]
کد را دریافت کنید
اولین قدم دریافت کد منبع mkbitmap
است. می توانید آن را در وب سایت پروژه پیدا کنید. در زمان نگارش این مطلب، potrace-1.16.tar.gz آخرین نسخه است.
کامپایل و به صورت محلی نصب کنید
گام بعدی این است که ابزار را به صورت محلی کامپایل و نصب کنید تا احساس کنید که چگونه رفتار می کند. فایل INSTALL
حاوی دستورالعمل های زیر است:
cd
به دایرکتوری حاوی کد منبع بسته وارد کنید و./configure
را تایپ کنید تا بسته را برای سیستم خود پیکربندی کنید.اجرای
configure
ممکن است کمی طول بکشد. در حین اجرا، پیام هایی را چاپ می کند که نشان می دهد کدام ویژگی ها را بررسی می کند.برای کامپایل کردن بسته،
make
تایپ کنید.به صورت اختیاری،
make check
را تایپ کنید تا آزمایشهای خودکاری که با بسته ارائه میشود، اجرا شود، معمولاً با استفاده از باینریهای حذفشده تازه ساختهشده.برای نصب برنامه ها و هر فایل داده و اسنادی،
make install
تایپ کنید. هنگام نصب در پیشوندی که متعلق به root است، توصیه می شود که بسته به عنوان یک کاربر معمولی پیکربندی و ساخته شود و فقط مرحلهmake install
با حقوق ریشه اجرا شود.
با دنبال کردن این مراحل، باید به دو فایل اجرایی، potrace
و mkbitmap
برسید - مورد دوم تمرکز این مقاله است. می توانید با اجرای mkbitmap --version
بررسی کنید که درست کار کرده است. در اینجا خروجی هر چهار مرحله از دستگاه من است که برای اختصار به شدت کوتاه شده است:
مرحله 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 دو اسکریپت ساده را ارائه میکند که فایلهای ساخت شما را به گونهای پیکربندی میکند که
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
دو مورد آخر امیدوارکننده به نظر می رسند، بنابراین cd
به دایرکتوری src/
وارد کنید. اکنون دو فایل متناظر جدید نیز وجود دارد، mkbitmap
و potrace
. برای این مقاله، فقط mkbitmap
مرتبط است. این واقعیت که آنها پسوند .js
ندارند کمی گیج کننده است، اما در واقع فایل های جاوا اسکریپت هستند که با یک 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)
با فراخوانی mv mkbitmap mkbitmap.js
(و در صورت تمایل به ترتیب mv potrace potrace.js
) نام فایل جاوا اسکریپت را به mkbitmap.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 boilerplate index.html
ایجاد کنید که فایل جاوا اسکریپت 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
یک شی جاوا اسکریپت جهانی با ویژگی هایی است که کد تولید شده توسط 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,
};
یک ساخت مدولار با چند پرچم ساخت دیگر ایجاد کنید
برای ارائه ورودی به برنامه، میتوانید از پشتیبانی سیستم فایل Emscripten در Module.FS
استفاده کنید. در بخش پشتیبانی از فایل سیستم در اسناد آمده است:
Emscripten تصمیم می گیرد که آیا پشتیبانی سیستم فایل را به طور خودکار شامل شود یا خیر. بسیاری از برنامهها به فایلها نیازی ندارند و پشتیبانی از سیستم فایل از نظر اندازه ناچیز نیست، بنابراین Emscripten وقتی دلیلی برای این کار نمیبیند از قرار دادن آن اجتناب میکند. این بدان معناست که اگر کد C/C++ شما به فایلها دسترسی نداشته باشد، شی
FS
و سایر APIهای سیستم فایل در خروجی گنجانده نمیشوند. و از سوی دیگر، اگر کد C/C++ شما از فایلها استفاده میکند، پشتیبانی سیستم فایل بهطور خودکار شامل میشود.
متأسفانه mkbitmap
یکی از مواردی است که Emscripten به طور خودکار پشتیبانی از سیستم فایل را شامل نمی شود، بنابراین باید صریحاً به آن بگویید که این کار را انجام دهد. این بدان معنی است که شما باید مراحل emconfigure
و emmake
را که قبلا توضیح داده شد، با چند پرچم دیگر از طریق یک آرگومان 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
را مشاهده کنید که در کنسول DevTools ثبت شده است، و اعلان از بین می رود، زیرا تابع main()
mkbitmap
دیگر در ابتدا فراخوانی نمی شود.
تابع اصلی را به صورت دستی اجرا کنید
مرحله بعدی فراخوانی دستی تابع main()
mkbitmap
با اجرای Module.callMain()
است. تابع callMain()
آرایهای از آرگومانها را میگیرد که با آنچه در خط فرمان ارسال میکنید، یک به یک مطابقت دارند. اگر در خط فرمان mkbitmap -v
را اجرا کنید، Module.callMain(['-v'])
را در مرورگر فراخوانی کنید. این شماره نسخه mkbitmap
را در کنسول DevTools ثبت می کند.
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
Module.callMain(['-v']);
};
run();
خروجی استاندارد را تغییر مسیر دهید
خروجی استاندارد ( stdout
) به طور پیش فرض کنسول است. با این حال، می توانید آن را به چیز دیگری هدایت کنید، به عنوان مثال، تابعی که خروجی را در یک متغیر ذخیره می کند. این بدان معنی است که می توانید با تنظیم ویژگی Module.print
خروجی را به HTML اضافه کنید.
// 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();
یک رابط کاربری تعاملی اضافه کنید
تا این مرحله، فایل ورودی هاردکد شده و 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 به خصوص سخت نیست، بنابراین با استفاده از کد جاوا اسکریپت ، حتی می توانید پیش نمایش تصویر خروجی را نشان دهید. کد منبع نسخه ی نمایشی تعبیه شده را برای یکی از راه های انجام این کار در زیر ببینید.
نتیجه گیری
تبریک می گوییم، شما با موفقیت mkbitmap
در WebAssembly کامپایل کردید و آن را در مرورگر کار کردید! بن بست هایی وجود داشت و شما مجبور بودید بیش از یک بار ابزار را کامپایل کنید تا زمانی که کار کند، اما همانطور که در بالا نوشتم، این بخشی از تجربه است. همچنین اگر گیر کردید ، تگ webassembly
StackOverflow را به خاطر بسپارید. تدوین مبارک!
قدردانی
این مقاله توسط سام کلگ و ریچل اندرو بررسی شده است.
،در WebAssembly چیست و از کجا آمده است؟ ، توضیح دادم که چگونه به WebAssembly امروز رسیدیم. در این مقاله، من روش خود را برای کامپایل کردن یک برنامه C موجود، mkbitmap
، به WebAssembly به شما نشان خواهم داد. از مثال hello world پیچیدهتر است، زیرا شامل کار با فایلها، برقراری ارتباط بین WebAssembly و جاوا اسکریپت و کشیدن روی بوم میشود، اما همچنان به اندازهای قابل مدیریت است که شما را تحت تأثیر قرار ندهد.
این مقاله برای توسعه دهندگان وب نوشته شده است که می خواهند WebAssembly را یاد بگیرند و گام به گام نشان می دهد که اگر می خواهید چیزی مانند mkbitmap
در WebAssembly کامپایل کنید چگونه می توانید ادامه دهید. به عنوان یک هشدار منصفانه، دریافت نکردن یک برنامه یا کتابخانه برای کامپایل در اولین اجرا کاملاً طبیعی است، به همین دلیل است که برخی از مراحل شرح داده شده در زیر کار نمیکنند، بنابراین من باید به عقب برگردم و دوباره امتحان کنم. این مقاله فرمان کامپایل نهایی جادویی را به گونهای نشان نمیدهد که انگار از آسمان افتاده است، بلکه پیشرفت واقعی من را توصیف میکند که شامل برخی ناامیدیها نیز میشود.
درباره mkbitmap
برنامه mkbitmap
C یک تصویر را می خواند و یک یا چند مورد از عملیات زیر را به ترتیب بر روی آن اعمال می کند: وارونگی، فیلتر بالاگذر، مقیاس گذاری و آستانه گذاری. هر عملیات را می توان به صورت جداگانه کنترل و روشن یا خاموش کرد. کاربرد اصلی mkbitmap
تبدیل تصاویر رنگی یا خاکستری به فرمتی مناسب به عنوان ورودی برای برنامه های دیگر است، به ویژه ردیابی برنامه potrace
که اساس SVGcode را تشکیل می دهد. به عنوان یک ابزار پیش پردازش، mkbitmap
به ویژه برای تبدیل هنر خط اسکن شده، مانند کارتون یا متن دست نویس، به تصاویر دوسطحی با وضوح بالا مفید است.
شما از mkbitmap
با ارسال تعدادی گزینه و یک یا چند نام فایل به آن استفاده می کنید. برای همه جزئیات، به صفحه مرد ابزار مراجعه کنید:
$ mkbitmap [options] [filename...]
کد را دریافت کنید
اولین قدم دریافت کد منبع mkbitmap
است. می توانید آن را در وب سایت پروژه پیدا کنید. در زمان نگارش این مطلب، potrace-1.16.tar.gz آخرین نسخه است.
کامپایل و به صورت محلی نصب کنید
گام بعدی این است که ابزار را به صورت محلی کامپایل و نصب کنید تا احساس کنید که چگونه رفتار می کند. فایل INSTALL
حاوی دستورالعمل های زیر است:
cd
به دایرکتوری حاوی کد منبع بسته وارد کنید و./configure
را تایپ کنید تا بسته را برای سیستم خود پیکربندی کنید.اجرای
configure
ممکن است کمی طول بکشد. در حین اجرا، پیام هایی را چاپ می کند که نشان می دهد کدام ویژگی ها را بررسی می کند.برای کامپایل کردن بسته،
make
تایپ کنید.به صورت اختیاری،
make check
را تایپ کنید تا آزمایشهای خودکاری که با بسته ارائه میشود، اجرا شود، معمولاً با استفاده از باینریهای حذفشده تازه ساختهشده.برای نصب برنامه ها و هر فایل داده و اسنادی،
make install
تایپ کنید. هنگام نصب در پیشوندی که متعلق به root است، توصیه می شود که بسته به عنوان یک کاربر معمولی پیکربندی و ساخته شود و فقط مرحلهmake install
با حقوق ریشه اجرا شود.
با دنبال کردن این مراحل، باید به دو فایل اجرایی، potrace
و mkbitmap
برسید - مورد دوم تمرکز این مقاله است. می توانید با اجرای mkbitmap --version
بررسی کنید که درست کار کرده است. در اینجا خروجی هر چهار مرحله از دستگاه من است که برای اختصار به شدت کوتاه شده است:
مرحله 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 دو اسکریپت ساده را ارائه میکند که فایلهای ساخت شما را به گونهای پیکربندی میکند که
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
دو مورد آخر امیدوارکننده به نظر می رسند، بنابراین cd
به دایرکتوری src/
وارد کنید. اکنون دو فایل متناظر جدید نیز وجود دارد، mkbitmap
و potrace
. برای این مقاله، فقط mkbitmap
مرتبط است. این واقعیت که آنها پسوند .js
ندارند کمی گیج کننده است، اما در واقع فایل های جاوا اسکریپت هستند که با یک 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)
با فراخوانی mv mkbitmap mkbitmap.js
(و در صورت تمایل به ترتیب mv potrace potrace.js
) نام فایل جاوا اسکریپت را به mkbitmap.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 boilerplate index.html
ایجاد کنید که فایل جاوا اسکریپت 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
یک شی جاوا اسکریپت جهانی با ویژگی هایی است که کد تولید شده توسط 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,
};
یک ساخت مدولار با چند پرچم ساخت دیگر ایجاد کنید
برای ارائه ورودی به برنامه، میتوانید از پشتیبانی سیستم فایل Emscripten در Module.FS
استفاده کنید. در بخش پشتیبانی از فایل سیستم در اسناد آمده است:
Emscripten تصمیم می گیرد که آیا پشتیبانی سیستم فایل را به طور خودکار شامل شود یا خیر. بسیاری از برنامهها به فایلها نیازی ندارند و پشتیبانی از سیستم فایل از نظر اندازه ناچیز نیست، بنابراین Emscripten وقتی دلیلی برای این کار نمیبیند از قرار دادن آن اجتناب میکند. این بدان معناست که اگر کد C/C++ شما به فایلها دسترسی نداشته باشد، شی
FS
و سایر APIهای سیستم فایل در خروجی گنجانده نمیشوند. و از سوی دیگر، اگر کد C/C++ شما از فایلها استفاده میکند، پشتیبانی سیستم فایل بهطور خودکار شامل میشود.
متأسفانه mkbitmap
یکی از مواردی است که Emscripten به طور خودکار پشتیبانی از سیستم فایل را شامل نمی شود، بنابراین باید صریحاً به آن بگویید که این کار را انجام دهد. این بدان معنی است که شما باید مراحل emconfigure
و emmake
را که قبلا توضیح داده شد، با چند پرچم دیگر از طریق یک آرگومان 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
را مشاهده کنید که در کنسول DevTools ثبت شده است، و اعلان از بین می رود، زیرا تابع main()
mkbitmap
دیگر در ابتدا فراخوانی نمی شود.
تابع اصلی را به صورت دستی اجرا کنید
مرحله بعدی فراخوانی دستی تابع main()
mkbitmap
با اجرای Module.callMain()
است. تابع callMain()
آرایهای از آرگومانها را میگیرد که با آنچه در خط فرمان ارسال میکنید، یک به یک مطابقت دارند. اگر در خط فرمان mkbitmap -v
را اجرا کنید، Module.callMain(['-v'])
را در مرورگر فراخوانی کنید. این شماره نسخه mkbitmap
را در کنسول DevTools ثبت می کند.
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
Module.callMain(['-v']);
};
run();
خروجی استاندارد را تغییر مسیر دهید
خروجی استاندارد ( stdout
) به طور پیش فرض کنسول است. با این حال، می توانید آن را به چیز دیگری هدایت کنید، به عنوان مثال، تابعی که خروجی را در یک متغیر ذخیره می کند. این بدان معنی است که می توانید با تنظیم ویژگی Module.print
خروجی را به HTML اضافه کنید.
// 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();
یک رابط کاربری تعاملی اضافه کنید
تا این مرحله، فایل ورودی هاردکد شده و 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 به خصوص سخت نیست، بنابراین با استفاده از کد جاوا اسکریپت ، حتی می توانید پیش نمایش تصویر خروجی را نشان دهید. کد منبع نسخه ی نمایشی تعبیه شده را برای یکی از راه های انجام این کار در زیر ببینید.
نتیجه گیری
تبریک می گوییم، شما با موفقیت mkbitmap
در WebAssembly کامپایل کردید و آن را در مرورگر کار کردید! بن بست هایی وجود داشت و شما مجبور بودید بیش از یک بار ابزار را کامپایل کنید تا زمانی که کار کند، اما همانطور که در بالا نوشتم، این بخشی از تجربه است. همچنین اگر گیر کردید ، تگ webassembly
StackOverflow را به خاطر بسپارید. تدوین مبارک!
قدردانی
این مقاله توسط سام کلگ و ریچل اندرو بررسی شده است.
،در WebAssembly چیست و از کجا آمده است؟ ، توضیح دادم که چگونه به WebAssembly امروز رسیدیم. در این مقاله، من روش خود را برای کامپایل کردن یک برنامه C موجود، mkbitmap
، به WebAssembly به شما نشان خواهم داد. از مثال hello world پیچیدهتر است، زیرا شامل کار با فایلها، برقراری ارتباط بین WebAssembly و جاوا اسکریپت و کشیدن روی بوم میشود، اما همچنان به اندازهای قابل مدیریت است که شما را تحت تأثیر قرار ندهد.
این مقاله برای توسعه دهندگان وب نوشته شده است که می خواهند WebAssembly را یاد بگیرند و گام به گام نشان می دهد که اگر می خواهید چیزی مانند mkbitmap
در WebAssembly کامپایل کنید چگونه می توانید ادامه دهید. به عنوان یک هشدار منصفانه، دریافت نکردن یک برنامه یا کتابخانه برای کامپایل در اولین اجرا کاملاً طبیعی است، به همین دلیل است که برخی از مراحل شرح داده شده در زیر کار نمیکنند، بنابراین من باید به عقب برگردم و دوباره امتحان کنم. این مقاله فرمان کامپایل نهایی جادویی را به گونهای نشان نمیدهد که انگار از آسمان افتاده است، بلکه پیشرفت واقعی من را توصیف میکند که شامل برخی ناامیدیها نیز میشود.
درباره mkbitmap
برنامه mkbitmap
C یک تصویر را می خواند و یک یا چند مورد از عملیات زیر را به ترتیب بر روی آن اعمال می کند: وارونگی، فیلتر بالاگذر، مقیاس گذاری و آستانه گذاری. هر عملیات را می توان به صورت جداگانه کنترل و روشن یا خاموش کرد. کاربرد اصلی mkbitmap
تبدیل تصاویر رنگی یا خاکستری به فرمتی مناسب به عنوان ورودی برای برنامه های دیگر است، به ویژه ردیابی برنامه potrace
که اساس SVGcode را تشکیل می دهد. به عنوان یک ابزار پیش پردازش، mkbitmap
به ویژه برای تبدیل هنر خط اسکن شده، مانند کارتون یا متن دست نویس، به تصاویر دوسطحی با وضوح بالا مفید است.
شما از mkbitmap
با ارسال تعدادی گزینه و یک یا چند نام فایل به آن استفاده می کنید. برای همه جزئیات، به صفحه مرد ابزار مراجعه کنید:
$ mkbitmap [options] [filename...]
کد را دریافت کنید
اولین قدم دریافت کد منبع mkbitmap
است. می توانید آن را در وب سایت پروژه پیدا کنید. در زمان نگارش این مطلب، potrace-1.16.tar.gz آخرین نسخه است.
کامپایل و به صورت محلی نصب کنید
گام بعدی این است که ابزار را به صورت محلی کامپایل و نصب کنید تا احساس کنید که چگونه رفتار می کند. فایل INSTALL
حاوی دستورالعمل های زیر است:
cd
به دایرکتوری حاوی کد منبع بسته وارد کنید و./configure
را تایپ کنید تا بسته را برای سیستم خود پیکربندی کنید.اجرای
configure
ممکن است کمی طول بکشد. در حین اجرا، پیام هایی را چاپ می کند که نشان می دهد کدام ویژگی ها را بررسی می کند.برای کامپایل کردن بسته،
make
تایپ کنید.به صورت اختیاری،
make check
را تایپ کنید تا آزمایشهای خودکاری که با بسته ارائه میشود، اجرا شود، معمولاً با استفاده از باینریهای حذفشده تازه ساختهشده.برای نصب برنامه ها و هر فایل داده و اسنادی،
make install
تایپ کنید. هنگام نصب در پیشوندی که متعلق به root است، توصیه می شود که بسته به عنوان یک کاربر معمولی پیکربندی و ساخته شود و فقط مرحلهmake install
با حقوق ریشه اجرا شود.
با دنبال کردن این مراحل، باید به دو فایل اجرایی، potrace
و mkbitmap
برسید - مورد دوم تمرکز این مقاله است. می توانید با اجرای mkbitmap --version
بررسی کنید که درست کار کرده است. در اینجا خروجی هر چهار مرحله از دستگاه من است که برای اختصار به شدت کوتاه شده است:
مرحله 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 موارد زیر را بیان می کند:
ساختن پروژه های بزرگ با Emscriptten بسیار آسان است. Emscripten دو اسکریپت ساده را ارائه می دهد که MakeFiles شما را برای استفاده از
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
این دو مورد آخر امیدوار کننده به نظر می رسند ، بنابراین cd
در src/
Directory. هم اکنون دو پرونده جدید مربوطه ، 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)
با تماس با mv mkbitmap mkbitmap.js
(و mv potrace potrace.js
به ترتیب در صورت تمایل) پرونده javaScript را به mkbitmap.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
کپی کرده و یک پرونده index.html
html boilerplate ایجاد کنید که پرونده mkbitmap.js
javaScript را بارگیری می کند.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>mkbitmap</title>
</head>
<body>
<script src="mkbitmap.js"></script>
</body>
</html>
یک سرور محلی را شروع کنید که به دایرکتوری mkbitmap
خدمت کند و آن را در مرورگر خود باز کنید. شما باید یک سریع را ببینید که از شما درخواست ورودی می کند. این همانطور که انتظار می رود ، از آنجا که ، مطابق با صفحه مرد ابزار ، "[من] هیچ آرگومان نام پرونده ارائه نمی شود ، سپس MKBitMap به عنوان یک فیلتر عمل می کند ، از ورودی استاندارد می خواند" ، که برای emscripten به طور پیش فرض prompt()
.
از اجرای خودکار جلوگیری کنید
برای جلوگیری از اجرای بلافاصله mkbitmap
و در عوض ، آن را منتظر ورودی کاربر هستید ، باید شیء Module
Emscripten را درک کنید. Module
یک شیء جهانی جاوا اسکریپت است و دارای ویژگی هایی است که کد تولید شده توسط 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,
};
با برخی از پرچم های ساخت بیشتر یک ساخت ماژولار ایجاد کنید
برای ارائه ورودی به برنامه ، می توانید از پشتیبانی سیستم فایل Emscripten در Module.FS
استفاده کنید. بخش پشتیبانی سیستم پرونده از جمله مستندات آمده است:
Emscripten تصمیم می گیرد که آیا پشتیبانی سیستم فایل را بطور خودکار درج می کند. بسیاری از برنامه ها به پرونده ها احتیاج ندارند و پشتیبانی از سیستم فایل در اندازه ناچیز نیست ، بنابراین Emscripten از جمله آن جلوگیری می کند که دلیل آن را نمی بیند. این بدان معناست که اگر کد C/C ++ شما به پرونده ها دسترسی پیدا نکند ، آنگاه
FS
Object و API های سیستم فایل دیگر در خروجی قرار نمی گیرند. و از طرف دیگر ، اگر کد C/C ++ شما از پرونده ها استفاده می کند ، پشتیبانی سیستم پرونده به طور خودکار گنجانده می شود.
متأسفانه mkbitmap
یکی از مواردی است که Emscripten به طور خودکار شامل پشتیبانی سیستم فایل نیست ، بنابراین شما باید صریحاً برای انجام این کار بگویید. این بدان معناست که شما باید مراحل emconfigure
و emmake
که قبلاً شرح داده شده بود ، دنبال کنید ، با این که چند پرچم دیگر از طریق یک آرگومان CFLAGS
تنظیم شده است. پرچم های زیر ممکن است برای سایر پروژه ها نیز مفید باشد.
- set
-sFILESYSTEM=1
بنابراین پشتیبانی سیستم فایل گنجانده شده است. - set
-sEXPORTED_RUNTIME_METHODS=FS,callMain
soModule.FS
وModule.callMain
صادر می شوند. - set
-sMODULARIZE=1
و-sEXPORT_ES6
برای تولید یک ماژول مدرن ES6. - SET
-sINVOKE_RUN=0
برای جلوگیری از اجرای اولیه که باعث ظاهر شدن سریع شد.
همچنین ، در این مورد خاص ، شما باید پرچم --host
را روی wasm32
تنظیم کنید تا به اسکریپت configure
بگویید که برای WebAnsembly تهیه می کنید.
دستور نهایی 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
به گونه ای اصلاح کنید که فقط script.js
ماژول ES را بارگذاری کند ، که از آن می توانید ماژول 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
را که به کنسول DevTools وارد شده است ، مشاهده کنید ، و سریع از بین می رود ، زیرا عملکرد main()
mkbitmap
دیگر در ابتدا فراخوانی نمی شود.
عملکرد اصلی را به صورت دستی اجرا کنید
مرحله بعدی این است که با اجرای Module.callMain()
main()
) mkbitmap
تماس بگیرید. تابع callMain()
مجموعه ای از آرگومان ها را می گیرد ، که یک به یک با یک آنچه را که می توانید از خط فرمان عبور دهید مطابقت دارد. اگر در خط فرمان mkbitmap -v
را اجرا کنید ، می توانید Module.callMain(['-v'])
را در مرورگر صدا کنید. این شماره نسخه mkbitmap
را به کنسول DevTools وارد می کند.
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
Module.callMain(['-v']);
};
run();
بازده استاندارد را هدایت کنید
خروجی استاندارد ( stdout
) به طور پیش فرض کنسول است. با این حال ، می توانید آن را به چیز دیگری هدایت کنید ، به عنوان مثال ، عملکردی که خروجی را به یک متغیر ذخیره می کند. این بدان معنی است که می توانید با تنظیم ویژگی Module.print
، خروجی را به HTML اضافه کنید.
// 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 برای Bitmaps و PGM برای Graymaps هستند. اگر یک آرگومان filename
ارائه شود ، mkbitmap
به طور پیش فرض یک فایل خروجی ایجاد می کند که نام آن از نام پرونده ورودی با تغییر پسوند خود به .pbm
بدست می آید. به عنوان مثال ، برای example.bmp
نام پرونده ورودی. BMP ، نام پرونده خروجی example.pbm
خواهد بود.
Emscripten یک سیستم فایل مجازی را ارائه می دهد که سیستم فایل محلی را شبیه سازی می کند ، به طوری که کد بومی با استفاده از API های فایل همزمان می تواند با تغییر کم و یا تغییر کند. برای اینکه mkbitmap
یک فایل ورودی را بخواند ، گویی که به عنوان یک آرگومان خط فرمان filename
منتقل شده است ، باید از شیء FS
که Emscripten ارائه می دهد استفاده کنید.
شیء FS
توسط یک سیستم پرونده در حافظه (که معمولاً به آن MEMF ها گفته می شود) پشتیبانی می شود و دارای یک تابع writeFile()
است که شما برای نوشتن پرونده ها به سیستم فایل مجازی استفاده می کنید. شما از writeFile()
همانطور که در نمونه کد زیر نشان داده شده است استفاده می کنید.
برای تأیید عملکرد نوشتن پرونده ، readdir()
FS
Object را با پارامتر '/'
اجرا کنید. 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();
پرونده خروجی را از سیستم پرونده حافظه دریافت کنید
عملکرد FS
Object's readFile()
دریافت 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 گردآوری کرده اید و آن را در مرورگر کار کرده اید! برخی از بن بست ها وجود داشت و شما باید بیش از یک بار ابزار را تا زمانی که کار کند ، کامپایل کنید ، اما همانطور که در بالا نوشتم ، این بخشی از تجربه است. همچنین اگر گیر کردید ، برچسب webassembly
StackoverFlow را به خاطر بسپارید. کامپایل مبارک!
قدردانی
این مقاله توسط سام کلگ و راشل اندرو بررسی شده است.
،در WebAssembly چیست و از کجا آمده است؟ ، من توضیح دادم که چگونه ما با WebAssembly امروز به پایان رسیدیم. در این مقاله ، من رویکرد خود را برای تهیه یک برنامه C موجود ، mkbitmap
، به WebAssembly به شما نشان خواهم داد. این پیچیده تر از مثال Hello World است ، زیرا شامل کار با پرونده ها ، برقراری ارتباط بین WebAnsembly و سرزمین های JavaScript و ترسیم به یک بوم است ، اما هنوز هم به اندازه کافی قابل کنترل است که شما را تحت الشعاع قرار ندهد.
این مقاله برای توسعه دهندگان وب نوشته شده است که می خواهند WebAssembly را یاد بگیرند و به صورت گام به گام نشان می دهد که اگر می خواهید چیزی مانند mkbitmap
را به WebAnsembly کامپایل کنید ، چگونه می توانید ادامه دهید. به عنوان یک هشدار عادلانه ، عدم دریافت برنامه یا کتابخانه برای تهیه در اولین اجرا کاملاً طبیعی است ، به همین دلیل برخی از مراحل شرح داده شده در زیر به پایان نرسیده است ، بنابراین من نیاز به عقب نشینی و دوباره امتحان کردن دوباره دارم. این مقاله دستور تالیف نهایی جادویی را نشان نمی دهد که گویی از آسمان افتاده است ، بلکه پیشرفت واقعی من را توصیف می کند ، برخی از ناامیدی ها شامل می شود.
درباره mkbitmap
برنامه mkbitmap
C یک تصویر را می خواند و یک یا چند مورد از عملیات زیر را در این ترتیب به کار می برد: وارونگی ، فیلتر Highpass ، مقیاس گذاری و آستانه. هر عملیاتی را می توان به صورت جداگانه کنترل کرد و روشن یا خاموش کرد. استفاده اصلی از mkbitmap
تبدیل تصاویر رنگ یا مقیاس خاکستری به فرمت مناسب به عنوان ورودی برای سایر برنامه ها ، به ویژه برنامه ردیابی potrace
که اساس SVGCode را تشکیل می دهد. به عنوان یک ابزار پیش پردازش ، mkbitmap
به ویژه برای تبدیل هنر خط اسکن شده مانند کارتون یا متن دست نوشته به تصاویر صفراوی با وضوح بالا مفید است.
شما با عبور از تعدادی از گزینه ها و یک یا چند نام پرونده از mkbitmap
استفاده می کنید. برای تمام جزئیات ، به صفحه مرد ابزار مراجعه کنید:
$ mkbitmap [options] [filename...]
کد را دریافت کنید
اولین قدم به دست آوردن کد منبع mkbitmap
است. می توانید آن را در وب سایت پروژه پیدا کنید. در زمان این نوشتار ، POTRACE-1.16.TAR.GZ آخرین نسخه است.
کامپایل و نصب محلی
مرحله بعدی تهیه و نصب ابزار به صورت محلی است تا احساس رفتار خود را بدست آورید. پرونده INSTALL
شامل دستورالعمل های زیر است:
cd
به فهرست حاوی کد منبع بسته و نوع./configure
برای پیکربندی بسته برای سیستم خود.configure
در حال اجرا ممکن است مدتی طول بکشد. در حین اجرای ، برخی از پیام ها را چاپ می کند که می گوید کدام ویژگی ها را بررسی می کند.برای کامپایل کردن بسته ، تایپ
make
.به صورت اختیاری ، برای اجرای هرگونه تست خود که با بسته بندی همراه است ، به طور کلی با استفاده از باینری های حذف نشده درست ساخته شده ، نوع خود
make check
.برای نصب برنامه ها و هر پرونده و اسناد داده ،
make install
. هنگام نصب در پیشوند متعلق به Root ، توصیه می شود که این بسته به عنوان یک کاربر معمولی تنظیم و ساخته شود و فقط مرحلهmake install
با امتیازات ریشه ای اجرا شود.
با دنبال کردن این مراحل ، شما باید با دو اجرایی ، potrace
و mkbitmap
به پایان برسید - دومی مورد توجه این مقاله است. می توانید با اجرای mkbitmap --version
تأیید کنید که به درستی کار کرده است. در اینجا خروجی هر چهار مرحله از دستگاه من است که به شدت برای کوتاه بودن بریده شده است:
مرحله 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 موارد زیر را بیان می کند:
ساختن پروژه های بزرگ با Emscriptten بسیار آسان است. Emscripten دو اسکریپت ساده را ارائه می دهد که MakeFiles شما را برای استفاده از
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
این دو مورد آخر امیدوار کننده به نظر می رسند ، بنابراین cd
در src/
Directory. هم اکنون دو پرونده جدید مربوطه ، 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)
با تماس با mv mkbitmap mkbitmap.js
(و mv potrace potrace.js
به ترتیب در صورت تمایل) پرونده javaScript را به mkbitmap.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
کپی کرده و یک پرونده index.html
html boilerplate ایجاد کنید که پرونده mkbitmap.js
javaScript را بارگیری می کند.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>mkbitmap</title>
</head>
<body>
<script src="mkbitmap.js"></script>
</body>
</html>
یک سرور محلی را شروع کنید که به دایرکتوری mkbitmap
خدمت کند و آن را در مرورگر خود باز کنید. شما باید یک سریع را ببینید که از شما درخواست ورودی می کند. این همانطور که انتظار می رود ، از آنجا که ، مطابق با صفحه مرد ابزار ، "[من] هیچ آرگومان نام پرونده ارائه نمی شود ، سپس MKBitMap به عنوان یک فیلتر عمل می کند ، از ورودی استاندارد می خواند" ، که برای emscripten به طور پیش فرض prompt()
.
از اجرای خودکار جلوگیری کنید
برای جلوگیری از اجرای بلافاصله mkbitmap
و در عوض ، آن را منتظر ورودی کاربر هستید ، باید شیء Module
Emscripten را درک کنید. Module
یک شیء جهانی جاوا اسکریپت است و دارای ویژگی هایی است که کد تولید شده توسط 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,
};
با برخی از پرچم های ساخت بیشتر یک ساخت ماژولار ایجاد کنید
برای ارائه ورودی به برنامه ، می توانید از پشتیبانی سیستم فایل Emscripten در Module.FS
استفاده کنید. بخش پشتیبانی سیستم پرونده از جمله مستندات آمده است:
Emscripten تصمیم می گیرد که آیا پشتیبانی سیستم فایل را بطور خودکار درج می کند. بسیاری از برنامه ها به پرونده ها احتیاج ندارند و پشتیبانی از سیستم فایل در اندازه ناچیز نیست ، بنابراین Emscripten از جمله آن جلوگیری می کند که دلیل آن را نمی بیند. این بدان معناست که اگر کد C/C ++ شما به پرونده ها دسترسی پیدا نکند ، آنگاه
FS
Object و API های سیستم فایل دیگر در خروجی قرار نمی گیرند. و از طرف دیگر ، اگر کد C/C ++ شما از پرونده ها استفاده می کند ، پشتیبانی سیستم پرونده به طور خودکار گنجانده می شود.
متأسفانه mkbitmap
یکی از مواردی است که Emscripten به طور خودکار شامل پشتیبانی سیستم فایل نیست ، بنابراین شما باید صریحاً برای انجام این کار بگویید. این بدان معناست که شما باید مراحل emconfigure
و emmake
که قبلاً شرح داده شده بود ، دنبال کنید ، با این که چند پرچم دیگر از طریق یک آرگومان CFLAGS
تنظیم شده است. پرچم های زیر ممکن است برای سایر پروژه ها نیز مفید باشد.
- set
-sFILESYSTEM=1
بنابراین پشتیبانی سیستم فایل گنجانده شده است. - set
-sEXPORTED_RUNTIME_METHODS=FS,callMain
soModule.FS
وModule.callMain
صادر می شوند. - set
-sMODULARIZE=1
و-sEXPORT_ES6
برای تولید یک ماژول مدرن ES6. - SET
-sINVOKE_RUN=0
برای جلوگیری از اجرای اولیه که باعث ظاهر شدن سریع شد.
همچنین ، در این مورد خاص ، شما باید پرچم --host
را روی wasm32
تنظیم کنید تا به اسکریپت configure
بگویید که برای WebAnsembly تهیه می کنید.
دستور نهایی 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
به گونه ای اصلاح کنید که فقط script.js
ماژول ES را بارگذاری کند ، که از آن می توانید ماژول 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
را که به کنسول DevTools وارد شده است ، مشاهده کنید ، و سریع از بین می رود ، زیرا عملکرد main()
mkbitmap
دیگر در ابتدا فراخوانی نمی شود.
عملکرد اصلی را به صورت دستی اجرا کنید
مرحله بعدی این است که با اجرای Module.callMain()
main()
) mkbitmap
تماس بگیرید. تابع callMain()
مجموعه ای از آرگومان ها را می گیرد ، که یک به یک با یک آنچه را که می توانید از خط فرمان عبور دهید مطابقت دارد. اگر در خط فرمان mkbitmap -v
را اجرا کنید ، می توانید Module.callMain(['-v'])
را در مرورگر صدا کنید. این شماره نسخه mkbitmap
را به کنسول DevTools وارد می کند.
// This is `script.js`.
import loadWASM from './mkbitmap.js';
const run = async () => {
const Module = await loadWASM();
Module.callMain(['-v']);
};
run();
بازده استاندارد را هدایت کنید
خروجی استاندارد ( stdout
) به طور پیش فرض کنسول است. با این حال ، می توانید آن را به چیز دیگری هدایت کنید ، به عنوان مثال ، عملکردی که خروجی را به یک متغیر ذخیره می کند. این بدان معنی است که می توانید با تنظیم ویژگی Module.print
، خروجی را به HTML اضافه کنید.
// 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 برای Bitmaps و PGM برای Graymaps هستند. اگر یک آرگومان filename
ارائه شود ، mkbitmap
به طور پیش فرض یک فایل خروجی ایجاد می کند که نام آن از نام پرونده ورودی با تغییر پسوند خود به .pbm
بدست می آید. به عنوان مثال ، برای example.bmp
نام پرونده ورودی. BMP ، نام پرونده خروجی example.pbm
خواهد بود.
Emscripten یک سیستم فایل مجازی را ارائه می دهد که سیستم فایل محلی را شبیه سازی می کند ، به طوری که کد بومی با استفاده از API های فایل همزمان می تواند با تغییر کم و یا تغییر کند. برای اینکه mkbitmap
یک فایل ورودی را بخواند ، گویی که به عنوان یک آرگومان خط فرمان filename
منتقل شده است ، باید از شیء FS
که Emscripten ارائه می دهد استفاده کنید.
شیء FS
توسط یک سیستم پرونده در حافظه (که معمولاً به آن MEMF ها گفته می شود) پشتیبانی می شود و دارای یک تابع writeFile()
است که شما برای نوشتن پرونده ها به سیستم فایل مجازی استفاده می کنید. شما از writeFile()
همانطور که در نمونه کد زیر نشان داده شده است استفاده می کنید.
برای تأیید عملکرد نوشتن پرونده ، readdir()
FS
Object را با پارامتر '/'
اجرا کنید. 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();
پرونده خروجی را از سیستم پرونده حافظه دریافت کنید
عملکرد FS
Object's readFile()
دریافت 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 گردآوری کرده اید و آن را در مرورگر کار کرده اید! برخی از بن بست ها وجود داشت و شما باید بیش از یک بار ابزار را تا زمانی که کار کند ، کامپایل کنید ، اما همانطور که در بالا نوشتم ، این بخشی از تجربه است. همچنین اگر گیر کردید ، برچسب webassembly
StackoverFlow را به خاطر بسپارید. کامپایل مبارک!
قدردانی
این مقاله توسط سام کلگ و راشل اندرو بررسی شده است.