WebAssembly

Image Converter WASM

Package konversi gambar berbasis Rust WebAssembly untuk browser dan Node.js, dipublikasikan sebagai package npm.

WebAssembly Rust Aplikasi Web

Tautan Repositori

GitHub: https://github.com/walujanle/image-converter-wasm

GitLab: https://gitlab.com/walujanle/image-converter-wasm

Tautan Package

npm: https://www.npmjs.com/package/@walujanle/image-converter-wasm

Tautan Unduh

Package: https://links.leonardwalujan.eu.org/lw/image-converter-wasm-latest-package

Checksum Package (SHA-256): https://links.leonardwalujan.eu.org/lw/image-converter-wasm-latest-package-checksum

Kode Sumber: https://links.leonardwalujan.eu.org/lw/image-converter-wasm-latest-source-code

Penjelasan Singkat

Image Converter WASM adalah engine konversi gambar berbasis Rust yang dikompilasi ke WebAssembly dan dipaketkan untuk aplikasi JavaScript. Package ini dipublikasikan di npm dengan nama @walujanle/image-converter-wasm.

Package ini menerima data gambar sebagai Uint8Array, memproses konversi di memori, lalu mengembalikan hasil encoding sebagai Uint8Array. Package ini tidak memilih file, tidak membuat tombol download, tidak mengunggah file, dan tidak menulis ke folder output. Bagian tersebut tetap menjadi tanggung jawab aplikasi yang memakai package ini, sehingga cocok dipakai pada frontend, browser worker, atau workflow Node.js yang ingin mengatur sendiri cara file dipilih, disimpan, atau dikirim.

Saat ini output yang didukung adalah JPEG, PNG, WebP, dan AVIF. Input yang dapat dibaca adalah jpg, jpeg, png, webp, avif, heic, dan heif. HEIC/HEIF hanya didukung sebagai input decode; package ini dapat mengonversi dari HEIC/HEIF, tetapi sengaja tidak menyediakan encoding HEIC.

Alasan Saya Membuat Project Ini

Saya membuat project ini untuk memahami alur nyata dari kode Rust sampai menjadi WebAssembly, lalu dari WebAssembly menjadi package npm yang bisa dipasang dan dipanggil dari aplikasi frontend.

Project ini juga menjadi cara saya mempelajari konversi gambar dengan kontrol yang lebih dekat ke engine-nya, bukan hanya memakai UI konverter biasa. Saya ingin membuat engine yang perilakunya bisa dikustomisasi langsung: format output, kualitas, kompresi PNG, mode lossless WebP, resize, crop, pengaturan aspect ratio, pelestarian metadata, dan progress conversion. Tujuannya bukan membuat layanan online converter yang generik, tetapi membuat package konversi yang bisa dipanggil website sambil tetap memberi kebebasan kepada frontend untuk mengatur file picker, preview, download, cache, atau upload.

Singkatnya, project ini menjadi jembatan praktis antara tiga hal yang ingin saya pahami lebih dalam: image processing dengan Rust, packaging WebAssembly, dan integrasi frontend melalui npm.

Tech Stack yang Digunakan

  • Rust untuk conversion engine.
  • WebAssembly melalui wasm-bindgen.
  • TypeScript declaration file untuk API JavaScript publik.
  • Entry wrapper ESM dan CommonJS.
  • Distribusi melalui npm package.
  • image untuk jalur decoding umum.
  • jpeg-encoder untuk output JPEG.
  • png untuk output PNG.
  • zenwebp untuk output WebP.
  • ravif untuk output AVIF.
  • heic untuk decode input HEIC/HEIF.
  • fast_image_resize untuk proses resize.

Package ini memakai lisensi AGPL-3.0-only. Lisensi tersebut penting karena artifact WASM yang didistribusikan menyertakan dependency dengan jalur lisensi AGPL/komersial pada encoder dan decoder yang digunakan saat ini.

Model Package

Package ini tidak dibangun sebagai satu file WebAssembly besar. Build script membuat modul WASM terpisah untuk masing-masing target output:

  • modul JPEG
  • modul PNG
  • modul WebP
  • modul AVIF

Wrapper JavaScript melakukan lazy loading terhadap modul yang sesuai dengan format output yang diminta. Dengan cara ini, API publik tetap sederhana, tetapi package tidak harus memuat satu binary besar untuk semua jalur konversi.

Package npm yang dihasilkan menyediakan dua gaya module:

  • index.js untuk ESM import.
  • index.cjs untuk CommonJS require.
  • index.d.ts untuk pengguna TypeScript.
  • formats/<format>/esm/index_bg.wasm untuk penggunaan ESM di browser dan bundler.
  • formats/<format>/cjs/index_bg.wasm untuk penggunaan CommonJS.

Pada Node.js ESM, wrapper membaca byte WASM dari file yang ikut dipaketkan. Pada browser build, wrapper memakai URL asset WASM yang dihasilkan, kecuali aplikasi memberi wasmSources secara eksplisit.

API Publik

API JavaScript publik dibuat kecil dan langsung.

init(options?)

Mengatur konfigurasi wrapper dan dapat melakukan preload untuk modul output tertentu. API ini juga mendukung custom WASM source ketika framework membutuhkan file WASM disajikan dari folder public atau static.

convertImage(fileBytes, ext, options)

Mengonversi satu buffer input dan mengembalikan byte output.

convertImageWithInfo(fileBytes, ext, options, onProgress?)

Mengonversi satu buffer input dan mengembalikan byte output bersama lebar dan tinggi final. Callback progress mengembalikan progress berbasis tahap dari 0 sampai 1.

getImageDimensions(fileBytes, ext, format?)

Membaca dimensi gambar tanpa menjalankan konversi penuh.

extractMetadata(fileBytes, ext, format?)

Mengekstrak metadata EXIF, XMP, IPTC, dan ICC yang tersedia dari buffer gambar di memori.

getProjectVersion(format?)

Mengembalikan versi project yang tertanam di modul WASM.

Opsi Konversi

Opsi yang tersedia di sisi frontend berfokus pada hal yang berguna untuk caller browser atau Node.js:

  • format: format output, yaitu Jpeg, Png, WebP, atau Avif.
  • quality: input kualitas untuk JPEG, WebP lossy, dan AVIF.
  • pngCompressed: mode kompresi PNG yang lebih tinggi.
  • lossless: mode lossless untuk WebP.
  • resize: mengaktifkan resize.
  • targetWidth dan targetHeight: ukuran target.
  • resizeLockAspectRatio: menjaga aspect ratio asli saat resize.
  • crop: mengaktifkan crop berbasis persen.
  • cropTop, cropBottom, cropLeft, dan cropRight: persentase crop.
  • keepMetadata: meminta encoder mempertahankan metadata yang didukung.

Beberapa field state konversi masih ada secara internal karena struktur Rust-nya berbagi bentuk dengan logic konversi yang lebih luas, tetapi wrapper npm hanya mengekspos fitur byte-in, byte-out yang masuk akal untuk package WebAssembly.

Cara Kerja

Alur konversi dimulai dari wrapper JavaScript. Wrapper menormalkan format output, menormalkan extension input, memetakan nama opsi JavaScript ke bentuk JSON yang dipakai Rust, lalu memuat modul WASM yang sesuai.

Di sisi Rust, pipeline konversi memvalidasi ukuran file, extension, dan magic bytes sebelum decode. Batas keras yang dipakai saat ini adalah ukuran file maksimal 256 MB, dimensi maksimal 16384 pixel per sisi gambar, dan timeout konversi 300 detik.

Setelah validasi, gambar sumber di-decode ke memori. JPEG, PNG, WebP, dan AVIF memakai jalur decoding image umum. Input HEIC/HEIF memakai decoder HEIC berbasis Rust ketika fitur tersebut aktif.

Untuk input non-HEIC, orientasi EXIF dapat diterapkan agar gambar dari kamera yang tersimpan dalam keadaan rotasi tertentu menjadi benar sebelum diproses lebih lanjut. Output decode HEIC/HEIF dianggap sudah mengikuti orientasi, sehingga pipeline menghindari penerapan orientasi yang sama dua kali.

Jika metadata preservation diminta, pipeline mengekstrak metadata sebelum transformasi gambar. ICC color profile juga ditangani terpisah agar data profil warna dapat dibawa ketika jalur output mendukungnya.

Setelah itu engine menjalankan crop opsional, resize opsional, dan encoding sesuai format target. Hasil akhir ditulis ke memori dan dikembalikan ke JavaScript. Pada layer ini tidak ada konsep path file output.

Perilaku Metadata

Metadata extraction dan metadata preservation adalah dua perilaku yang berbeda. extractMetadata(...) membaca metadata dari byte sumber. keepMetadata: true meminta pipeline konversi menulis metadata yang didukung ke output.

Dukungan preservation output saat ini:

Format OutputEXIFXMPIPTCICC
JPEGYaYaYaYa
PNGYaYaYaYa
WebPYaYaTidakYa
AVIFYaYaTidakTidak

JPEG menulis EXIF dan XMP melalui segment APP1, IPTC melalui Photoshop APP13, dan ICC melalui APP2. PNG menulis metadata melalui chunk PNG dan juga memproyeksikan sebagian field EXIF/XMP ke text chunk agar lebih mudah terlihat oleh sistem operasi. WebP menulis EXIF, XMP, dan ICC di container RIFF. AVIF mempertahankan EXIF dan XMP, sedangkan embedding ICC belum dijanjikan oleh jalur output AVIF saat ini.

Untuk input AVIF dan HEIC, metadata dibaca dari struktur ISOBMFF ketika parser dapat menemukannya. Jalur ISOBMFF menangani item metadata untuk EXIF dan XMP, serta dapat mengekstrak ICC dari box colr dengan tipe profile prof atau rICC.

Contoh Penggunaan

import { convertImage, init } from "@walujanle/image-converter-wasm";

await init({ preload: ["Jpeg"] });

const input = new Uint8Array(await file.arrayBuffer());
const output = await convertImage(input, ".png", {
  format: "Jpeg",
  quality: 82,
  resize: true,
  targetWidth: 1600,
  resizeLockAspectRatio: true,
  keepMetadata: true,
});

const blob = new Blob([output], { type: "image/jpeg" });

Untuk aplikasi browser, kode seperti ini sebaiknya dijalankan di jalur client-side seperti client component, browser-only hook, atau web worker. Kode server-side rendering tidak boleh mengasumsikan API file browser tersedia.

Catatan Build dan Release

Build script repository adalah build_wasm.bat. Script ini mengompilasi satu modul output dalam satu waktu, menjalankan wasm-bindgen, menyusun package npm di folder pkg/, dan menyalin README npm dari js-wrapper/README.npm.md.

Repository ini sengaja memiliki dua README:

  • README.md untuk maintainer yang membaca source repository.
  • js-wrapper/README.npm.md untuk pengguna package npm.

Pemilahan ini penting karena maintainer membutuhkan detail build dan architecture, sedangkan pengguna package biasanya hanya membutuhkan instalasi, API, runtime, dan catatan bundler.

Ada juga jalur build opsional WASM_THREADS=1. Jalur tersebut hanya berguna ketika host environment sudah siap untuk kebutuhan threaded WebAssembly seperti SharedArrayBuffer, COOP, dan COEP.

Batasan

  • HEIC/HEIF hanya decode-only. Output HEIC tidak didukung.
  • TIFF bukan input atau output publik pada package ini.
  • Model konversinya berbasis memori, sehingga file yang sangat besar tetap dapat memakai memori besar.
  • File picker, saving, preview, dan download di browser harus dibuat oleh aplikasi host.
  • Progress bersifat stage-based, bukan progress byte atau scanline internal codec.
  • Output AVIF saat ini tidak menjanjikan embedding ICC.
  • Output WebP dan AVIF tidak mempertahankan IPTC karena jalur output tersebut tidak menyediakan handling IPTC native dalam package ini.
  • Build WASM threaded membutuhkan header dan dukungan runtime dari aplikasi host.

Lisensi

Image Converter WASM dirilis dengan lisensi AGPL-3.0-only.

Pengguna dapat mempelajari, memodifikasi, dan mendistribusikan ulang project ini sesuai ketentuan AGPL. Aplikasi yang mendistribusikan atau menyediakan akses jaringan ke package ini perlu mengikuti kewajiban lisensi tersebut atau memakai jalur lisensi kompatibel yang terpisah.

Proyek Terbaru