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 का सोर्स कोड पाएं. आपको यह प्रोजेक्ट की वेबसाइट पर मिलेगा. यह लिखे जाते समय, posrace-1.16.tar.gz सबसे नया वर्शन है.

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

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

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

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

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

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

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

इन चरणों को पूरा करने पर आपको दो एक्ज़ीक्यूटेबल फ़ाइलें मिलती हैं, potrace और 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 एक टूल है, जिसकी मदद से WebAssembly में C/C++ प्रोग्राम कंपाइल किए जा सकते हैं. 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

आखिरी दो सही लग रहे हैं, इसलिए src/ डायरेक्ट्री में cd. अब संबंधित दो नई फ़ाइलें भी हैं, 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 को कॉल करके JavaScript फ़ाइल का नाम बदलकर 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.

आपने WebAssembly में mkbitmap को कंपाइल कर लिया है. अब अगला कदम यह है कि यह ब्राउज़र में काम करे.

ब्राउज़र में 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 डायरेक्ट्री के लिए सेवा देता है. इसके बाद, उसे अपने ब्राउज़र में खोलें. आपको एक प्रॉम्प्ट दिखेगा, जिसमें आपसे इनपुट मांगा जाएगा. टूल के मैन पेज के मुताबिक, "[i]f कोई फ़ाइल नाम नहीं दिया गया है. इसलिए, mkbitmap स्टैंडर्ड इनपुट से पढ़ते हुए, फ़िल्टर के तौर पर काम करता है", जो डिफ़ॉल्ट रूप से Emscripten के लिए prompt() होता है.

mkbitmap ऐप्लिकेशन, इनपुट के लिए प्रॉम्प्ट दिखाता है.

अपने-आप एक्ज़ीक्यूशन होने से रोकें

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

साथ ही, इस मामले में, configure स्क्रिप्ट को यह बताने के लिए कि आप WebAssembly के लिए कंपाइल कर रहे हैं, आपको --host फ़्लैग को wasm32 पर सेट करना होगा.

आखिरी 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']) को कॉल किया जा सकता है. यह DevTools कंसोल में mkbitmap वर्शन नंबर लॉग करता है.

// 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 कैसे इनपुट की उम्मीद करता है और आउटपुट कैसे बनाता है.

PNM (PBM, PGM, PPM) और BMP mkbitmap के इनपुट फ़ॉर्मैट काम करते हैं. आउटपुट फ़ॉर्मैट, बिटमैप के लिए पीबीएम और ग्रेमैप के लिए पीजीएम हैं. अगर 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 टैग भी याद रखें. कंपाइल करने के लिए शुभकामनाएं!

स्वीकार की गई

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