mkbitmap को WebAssembly में कंपाइल करना

WebAssembly क्या है और यह कहां से आया? में, मैंने बताया कि आज के समय में WebAssembly कैसे काम करता है. इस लेख में, हम आपको मौजूदा C प्रोग्राम, mkbitmap को WebAssembly में कंपाइल करने का तरीका बताएंगे. यह hello world उदाहरण से ज़्यादा जटिल है. इसमें फ़ाइलों के साथ काम करना, WebAssembly और JavaScript के बीच कम्यूनिकेट करना, और कैनवस पर ड्रॉ करना शामिल है. हालांकि, यह इतना मुश्किल नहीं है कि आपको समझ न आए.

यह लेख उन वेब डेवलपर के लिए लिखा गया है जो WebAssembly के बारे में जानना चाहते हैं. इसमें सिलसिलेवार तरीके से बताया गया है कि अगर आपको mkbitmap जैसे किसी कोड को WebAssembly में कंपाइल करना है, तो आपको क्या करना होगा. पहली बार में किसी ऐप्लिकेशन या लाइब्रेरी को कंपाइल न कर पाना सामान्य बात है. इसलिए, यहां बताए गए कुछ तरीके काम नहीं करते. इसलिए, मुझे पीछे जाकर फिर से कोशिश करनी पड़ी. लेख में, फ़ाइनल कंपाइलेशन कमांड को इस तरह नहीं दिखाया गया है कि वह अचानक से आ गई हो. इसके बजाय, इसमें मेरी असल प्रोग्रेस के बारे में बताया गया है. इसमें कुछ समस्याएं भी शामिल हैं.

mkbitmap के बारे में

mkbitmap C प्रोग्राम, इमेज को पढ़ता है और उस पर यहां दिए गए क्रम में एक या उससे ज़्यादा ऑपरेशन लागू करता है: इनवर्ज़न, हाईपास फ़िल्टरिंग, स्केलिंग, और थ्रेशोल्डिंग. हर ऑपरेशन को अलग-अलग कंट्रोल किया जा सकता है. साथ ही, इसे चालू या बंद किया जा सकता है. mkbitmap का मुख्य इस्तेमाल, रंगीन या ग्रेस्केल इमेज को ऐसे फ़ॉर्मैट में बदलना है जो अन्य प्रोग्राम के लिए इनपुट के तौर पर इस्तेमाल किया जा सके. खास तौर पर, ट्रेसिंग प्रोग्राम potrace के लिए, जो SVGcode का आधार है. प्रीप्रोसेसिंग टूल के तौर पर, mkbitmap खास तौर पर स्कैन किए गए लाइन आर्ट को हाई-रिज़ॉल्यूशन वाली बाइलेवल इमेज में बदलने के लिए उपयोगी है. जैसे, कार्टून या हाथ से लिखा गया टेक्स्ट.

mkbitmap का इस्तेमाल करने के लिए, इसे कई विकल्प और एक या उससे ज़्यादा फ़ाइल के नाम दिए जाते हैं. पूरी जानकारी के लिए, टूल का मैन पेज देखें:

$ mkbitmap [options] [filename...]
रंगीन कार्टून इमेज.
ओरिजनल इमेज (सोर्स).
प्रीप्रोसेसिंग के बाद, कार्टून इमेज को ग्रेस्केल में बदल दिया गया.
पहले स्केल किया गया, फिर थ्रेशोल्ड किया गया: mkbitmap -f 2 -s 2 -t 0.48 (सोर्स).

कोड प्राप्त करें

सबसे पहले, mkbitmap का सोर्स कोड पाएं. यह आपको प्रोजेक्ट की वेबसाइट पर मिलेगा. यह लेख लिखे जाने के समय, potrace-1.16.tar.gz सबसे नया वर्शन है.

स्थानीय तौर पर कंपाइल और इंस्टॉल करना

अगला चरण, टूल को स्थानीय तौर पर कंपाइल और इंस्टॉल करना है, ताकि यह पता चल सके कि यह कैसे काम करता है. INSTALL फ़ाइल में ये निर्देश शामिल होते हैं:

  1. cd को पैकेज के सोर्स कोड वाली डायरेक्ट्री में बदलें और अपने सिस्टम के लिए पैकेज को कॉन्फ़िगर करने के लिए, ./configure टाइप करें.

    configure को चालू होने में कुछ समय लग सकता है. चलते समय, यह कुछ मैसेज प्रिंट करता है. इनसे पता चलता है कि यह किन सुविधाओं की जांच कर रहा है.

  2. पैकेज को कंपाइल करने के लिए, make टाइप करें.

  3. इसके अलावा, पैकेज के साथ आने वाले किसी भी सेल्फ़-टेस्ट को चलाने के लिए, make check टाइप करें. आम तौर पर, इसके लिए अभी-अभी बनाए गए और अनइंस्टॉल किए गए बाइनरी का इस्तेमाल किया जाता है.

  4. प्रोग्राम, डेटा फ़ाइलों, और दस्तावेज़ों को इंस्टॉल करने के लिए, make install टाइप करें. जब रूट के मालिकाना हक वाले किसी प्रीफ़िक्स में पैकेज इंस्टॉल किया जाता है, तो हमारा सुझाव है कि पैकेज को सामान्य उपयोगकर्ता के तौर पर कॉन्फ़िगर और बनाया जाए. साथ ही, सिर्फ़ make install फ़ेज़ को रूट के विशेषाधिकारों के साथ लागू किया जाए.

इन चरणों को पूरा करने के बाद, आपको दो एक्ज़ीक्यूटेबल मिलेंगे: potrace और mkbitmap. इस लेख में, mkbitmap पर फ़ोकस किया गया है. mkbitmap --version चलाकर, यह पुष्टि की जा सकती है कि यह सही तरीके से काम कर रहा है या नहीं. यहां मेरे कंप्यूटर पर, चारों चरणों का आउटपुट दिया गया है. इसे छोटा करके दिखाया गया है:

पहला चरण, ./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

दूसरा चरण, 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'.

तीसरा चरण, 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'.

चौथा चरण, 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, दो आसान स्क्रिप्ट उपलब्ध कराता है. ये स्क्रिप्ट, आपके मेकफ़ाइल को कॉन्फ़िगर करती हैं, ताकि gcc की जगह emcc का इस्तेमाल किया जा सके. ज़्यादातर मामलों में, आपके प्रोजेक्ट के मौजूदा बिल्ड सिस्टम में कोई बदलाव नहीं होता.

इसके बाद, दस्तावेज़ में यह जानकारी दी गई है (इसे छोटा करने के लिए, इसमें थोड़ा बदलाव किया गया है):

मान लें कि आम तौर पर, इन निर्देशों का इस्तेमाल करके बनाया जाता है:

./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

पहला चरण, 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

दूसरा चरण, 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 एक्सटेंशन नहीं है, इसलिए यह थोड़ा भ्रमित करने वाला है. हालांकि, ये 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 में कंपाइल कर लिया है. अब अगला चरण, इसे ब्राउज़र में काम करने लायक बनाना है.

ब्राउज़र में WebAssembly के साथ mkbitmap

mkbitmap.js और mkbitmap.wasm फ़ाइलों को mkbitmap नाम की नई डायरेक्ट्री में कॉपी करें. इसके बाद, index.html एचटीएमएल बॉयलरप्लेट फ़ाइल बनाएं, जो 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 ऐप्लिकेशन में, इनपुट के लिए प्रॉम्प्ट दिखाया गया है.

अपने-आप लागू होने से रोकना

mkbitmap को तुरंत लागू होने से रोकने और उपयोगकर्ता के इनपुट का इंतज़ार करने के लिए, आपको Emscripten के Module ऑब्जेक्ट के बारे में समझना होगा. Module एक ग्लोबल JavaScript ऑब्जेक्ट है. इसमें ऐसे एट्रिब्यूट होते हैं जिन्हें Emscripten से जनरेट किया गया कोड, एक्ज़ीक्यूशन के दौरान अलग-अलग पॉइंट पर कॉल करता है. कोड के एक्ज़ीक्यूशन को कंट्रोल करने के लिए, Module को लागू किया जा सकता है. जब कोई Emscripten ऐप्लिकेशन शुरू होता है, तो वह Module ऑब्जेक्ट में मौजूद वैल्यू देखता है और उन्हें लागू करता है.

mkbitmap के मामले में, Module.noInitialRun को true पर सेट करें, ताकि पहली बार इस्तेमाल करने पर प्रॉम्प्ट न दिखे. script.js नाम की एक स्क्रिप्ट बनाएं. इसे index.html में <script src="mkbitmap.js"></script> के पहले शामिल करें. इसके बाद, script.js में यह कोड जोड़ें. अब ऐप्लिकेशन को फिर से लोड करने पर, आपको यह प्रॉम्प्ट नहीं दिखेगा.

var Module = {
  // Don't run main() at page load
  noInitialRun: true,
};

कुछ और बिल्ड फ़्लैग के साथ मॉड्यूलर बिल्ड बनाना

ऐप्लिकेशन को इनपुट देने के लिए, Module.FS में Emscripten के फ़ाइल सिस्टम का इस्तेमाल किया जा सकता है. दस्तावेज़ के फ़ाइल सिस्टम के साथ काम करने की सुविधा शामिल है सेक्शन में यह बताया गया है:

Emscripten यह तय करता है कि फ़ाइल सिस्टम के साथ काम करने की सुविधा को अपने-आप शामिल करना है या नहीं. कई प्रोग्राम को फ़ाइलों की ज़रूरत नहीं होती. साथ ही, फ़ाइल सिस्टम का साइज़ भी काफ़ी बड़ा होता है. इसलिए, Emscripten इसे तब तक शामिल नहीं करता, जब तक उसे ऐसा करने की कोई वजह नहीं दिखती. इसका मतलब है कि अगर आपके C/C++ कोड में फ़ाइलों को ऐक्सेस नहीं किया जाता है, तो आउटपुट में FS ऑब्जेक्ट और अन्य फ़ाइल सिस्टम एपीआई शामिल नहीं किए जाएंगे. इसके अलावा, अगर आपके C/C++ कोड में फ़ाइलों का इस्तेमाल किया जाता है, तो फ़ाइल सिस्टम का सपोर्ट अपने-आप शामिल हो जाएगा.

माफ़ करें, mkbitmap एक ऐसा मामला है जहां Emscripten, फ़ाइल सिस्टम के साथ काम करने की सुविधा को अपने-आप शामिल नहीं करता है. इसलिए, आपको इसे साफ़ तौर पर ऐसा करने के लिए कहना होगा. इसका मतलब है कि आपको पहले बताए गए emconfigure और emmake चरणों का पालन करना होगा. साथ ही, CFLAGS आर्ग्युमेंट के ज़रिए कुछ और फ़्लैग सेट करने होंगे. ये फ़्लैग, अन्य प्रोजेक्ट के लिए भी काम आ सकते हैं.

  • -sFILESYSTEM=1 को सेट करें, ताकि फ़ाइल सिस्टम का सपोर्ट शामिल किया जा सके.
  • -sEXPORTED_RUNTIME_METHODS=FS,callMain को सेट करें, ताकि Module.FS और Module.callMain एक्सपोर्ट किए जा सकें.
  • मॉडर्न ES6 मॉड्यूल जनरेट करने के लिए, -sMODULARIZE=1 और -sEXPORT_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();

अब ब्राउज़र में ऐप्लिकेशन खोलने पर, आपको DevTools कंसोल में Module ऑब्जेक्ट लॉग किया हुआ दिखेगा. साथ ही, प्रॉम्प्ट नहीं दिखेगा, क्योंकि अब mkbitmap का main() फ़ंक्शन शुरू में कॉल नहीं किया जाता है.

mkbitmap ऐप्लिकेशन की व्हाइट स्क्रीन पर, DevTools कंसोल में लॉग किया गया मॉड्यूल ऑब्जेक्ट दिखाया गया है.

मुख्य फ़ंक्शन को मैन्युअल तरीके से लागू करना

अगला चरण, Module.callMain() को चलाकर mkbitmap के main() फ़ंक्शन को मैन्युअल तरीके से कॉल करना है. 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();

mkbitmap ऐप्लिकेशन की व्हाइट स्क्रीन दिख रही है. इसमें DevTools कंसोल में लॉग किया गया mkbitmap का वर्शन नंबर दिख रहा है.

स्टैंडर्ड आउटपुट को रीडायरेक्ट करना

डिफ़ॉल्ट रूप से स्टैंडर्ड आउटपुट (stdout), कंसोल होता है. हालांकि, इसे किसी दूसरी चीज़ पर रीडायरेक्ट किया जा सकता है. उदाहरण के लिए, किसी ऐसे फ़ंक्शन पर जो आउटपुट को किसी वैरिएबल में सेव करता है. इसका मतलब है कि 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 ऐप्लिकेशन में mkbitmap का वर्शन नंबर दिखाया गया है.

इनपुट फ़ाइल को मेमोरी फ़ाइल सिस्टम में पाना

इनपुट फ़ाइल को मेमोरी फ़ाइल सिस्टम में लाने के लिए, आपको कमांड लाइन पर mkbitmap filename के बराबर की अनुमति चाहिए. इसे समझने के लिए, पहले यह जान लें कि mkbitmap किस तरह से इनपुट लेता है और आउटपुट तैयार करता है.

mkbitmap के लिए, इनपुट फ़ॉर्मैट इस्तेमाल किए जा सकते हैं: PNM (PBM, PGM, PPM) और BMP. आउटपुट फ़ॉर्मैट, बिटमैप के लिए PBM और ग्रेमैप के लिए PGM हैं. अगर filename आर्ग्युमेंट दिया जाता है, तो mkbitmap डिफ़ॉल्ट रूप से एक आउटपुट फ़ाइल बनाएगा. इसका नाम, इनपुट फ़ाइल के नाम से लिया जाएगा. इसके लिए, इसके सफ़िक्स को .pbm में बदल दिया जाएगा. उदाहरण के लिए, अगर इनपुट फ़ाइल का नाम example.bmp है, तो आउटपुट फ़ाइल का नाम example.pbm होगा.

Emscripten, एक वर्चुअल फ़ाइल सिस्टम उपलब्ध कराता है. यह लोकल फ़ाइल सिस्टम की तरह काम करता है, ताकि सिंक्रोनस फ़ाइल एपीआई का इस्तेमाल करने वाले नेटिव कोड को कंपाइल किया जा सके और उसमें बहुत कम या कोई बदलाव किए बिना चलाया जा सके. mkbitmap को इनपुट फ़ाइल को इस तरह से पढ़ने के लिए कि उसे filename कमांड लाइन आर्ग्युमेंट के तौर पर पास किया गया हो, आपको Emscripten के दिए गए FS ऑब्जेक्ट का इस्तेमाल करना होगा.

FS ऑब्जेक्ट, इन-मेमोरी फ़ाइल सिस्टम (इसे आम तौर पर MEMFS कहा जाता है) पर आधारित होता है. इसमें एक writeFile() फ़ंक्शन होता है. इसका इस्तेमाल, वर्चुअल फ़ाइल सिस्टम में फ़ाइलें लिखने के लिए किया जाता है. writeFile() का इस्तेमाल इस कोड सैंपल में दिखाए गए तरीके से किया जाता है.

फ़ाइल में लिखने की कार्रवाई पूरी हुई है या नहीं, इसकी पुष्टि करने के लिए, FS ऑब्जेक्ट के readdir() फ़ंक्शन को '/' पैरामीटर के साथ चलाएं. आपको 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 ऐप्लिकेशन, मेमोरी फ़ाइल सिस्टम में फ़ाइलों की एक सीरीज़ दिखा रहा है. इसमें example.bmp भी शामिल है.

पहली बार स्क्रिप्ट को चलाने का समय

सब कुछ सेट अप हो जाने के बाद, Module.callMain(['example.bmp']) को चलाने के लिए mkbitmap को लागू करें. MEMFS के '/' फ़ोल्डर के कॉन्टेंट को लॉग करें. आपको example.bmp इनपुट फ़ाइल के बगल में, नई बनाई गई example.pbm आउटपुट फ़ाइल दिखेगी.

// 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();

mkbitmap ऐप्लिकेशन, मेमोरी फ़ाइल सिस्टम में फ़ाइलों की एक सीरीज़ दिखा रहा है. इसमें example.bmp और example.pbm शामिल हैं.

मेमोरी फ़ाइल सिस्टम से आउटपुट फ़ाइल पाना

FS ऑब्जेक्ट का 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();

macOS Finder में, इनपुट .bmp फ़ाइल और आउटपुट .pbm फ़ाइल की झलक दिखाई गई है.

इंटरैक्टिव यूज़र इंटरफ़ेस (यूआई) जोड़ना

इस पॉइंट तक, इनपुट फ़ाइल को हार्डकोड किया जाता है और 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 में कंपाइल कर लिया है और इसे ब्राउज़र में काम करने लायक बना दिया है! कुछ मामलों में, आपको सही जवाब नहीं मिला और टूल को काम करने के लिए, आपको एक से ज़्यादा बार निर्देश देने पड़े. हालांकि, जैसा कि हमने ऊपर बताया है, यह भी एक तरह का अनुभव है. अगर आपको कोई समस्या आती है, तो StackOverflow के webassembly टैग का इस्तेमाल करें. संकलन बनाने के लिए शुभकामनाएं!

Acknowledgements

इस लेख की समीक्षा सैम क्लेग और राशेल ऐंड्रयू ने की है.