SVGcode: PWA untuk mengonversi gambar raster menjadi grafik vektor SVG

SVGcode adalah Progressive Web App yang memungkinkan Anda mengonversi gambar raster seperti JPG, PNG, GIF, WebP, AVIF, dll. ke grafik vektor dalam format SVG. API ini menggunakan File System Access API, Async Clipboard API, File Handling API, dan Penyesuaian Overlay Kontrol Jendela.

(Jika Anda lebih suka menonton daripada membaca, artikel ini juga tersedia sebagai video.)

Dari raster ke vektor

Pernahkah Anda menskalakan gambar dan hasilnya menjadi pecah dan tidak memuaskan? Jika demikian, Anda mungkin pernah berurusan dengan format gambar raster seperti WebP, PNG, atau JPG.

Menskalakan gambar raster akan membuatnya terlihat pecah.

Sebaliknya, grafik vektor adalah gambar yang ditentukan oleh titik dalam sistem koordinat. Titik-titik ini dihubungkan oleh garis dan kurva untuk membentuk poligon dan bentuk lainnya. Grafik vektor memiliki kelebihan daripada grafis raster, yaitu dapat ditingkatkan atau diturunkan skalanya ke resolusi tanpa pikselasi.

Meningkatkan skala gambar vektor tanpa menurunkan kualitas.

Memperkenalkan SVGcode

Saya telah membuat PWA bernama SVGcode yang dapat membantu Anda mengonversi gambar raster menjadi vektor. Kredit yang diberikan: Saya bukan yang menciptakannya. Dengan SVGcode, saya hanya perlu menggunakan alat command line bernama Potrace oleh Peter Selinger yang telah dikonversi ke Web Assembly, sehingga dapat digunakan dalam aplikasi Web.

screenshot aplikasi SVGcode.
Aplikasi SVGcode.

Menggunakan SVGcode

Pertama, saya ingin menunjukkan cara menggunakan aplikasi. Saya mulai dengan gambar teaser Chrome Dev Summit yang saya download dari saluran Twitter ChromiumDev. Ini adalah gambar raster PNG yang kemudian saya tarik ke aplikasi SVGcode. Saat saya melepas file, aplikasi akan melacak warna gambar berdasarkan warna, hingga versi vektor input muncul. Sekarang saya dapat memperbesar gambar, dan seperti yang Anda lihat, tepiannya tetap tajam. Namun, dengan memperbesar logo Chrome, Anda dapat melihat bahwa pelacakannya tidak sempurna, dan terutama garis luar logo terlihat sedikit berbintik. Saya bisa memperbaiki hasilnya dengan menghapus spektrum pelacakan dengan menyembunyikan hingga, lima piksel,

Mengonversi gambar yang dilepas ke SVG.

Posterisasi dalam SVGcode

Langkah penting dalam vektorisasi, terutama untuk gambar fotografi, adalah melakukan posterisasi gambar input untuk mengurangi jumlah warna. SVGcode memungkinkan saya melakukan ini per saluran warna, dan melihat SVG yang dihasilkan saat saya membuat perubahan. Jika puas dengan hasilnya, saya bisa menyimpan SVG ke {i>hard disk<i} dan menggunakannya di mana pun yang saya suka.

Membuat poster gambar untuk mengurangi jumlah warna.

API yang digunakan dalam SVGcode

Setelah Anda melihat kemampuan aplikasi, mari kita lihat beberapa API yang membantu mewujudkan keajaiban.

Progressive Web App

SVGcode adalah Progressive Web App yang dapat diinstal dan oleh karena itu diaktifkan sepenuhnya secara offline. Aplikasi ini didasarkan pada template Vanilla JS untuk Vite.js dan menggunakan PWA plugin Viite populer, yang membuat pekerja layanan yang menggunakan Workbox.js di balik layar. Workbox adalah sekumpulan library yang dapat mendukung pekerja layanan siap-produksi untuk Progressive Web App. Pola ini mungkin belum tentu berfungsi untuk semua aplikasi, namun untuk kasus penggunaan SVGcode, pola ini sangat bagus.

Overlay Kontrol Jendela

Untuk memaksimalkan real estate layar yang tersedia, SVGcode menggunakan penyesuaian Window Controls Overlay dengan memindahkan menu utamanya ke atas ke area judul. Anda dapat melihat proses ini diaktifkan pada akhir alur penginstalan.

Menginstal SVGcode dan mengaktifkan penyesuaian Overlay Kontrol Jendela.

File System Access API

Untuk membuka file gambar input dan menyimpan SVG yang dihasilkan, saya menggunakan File System Access API. Dengan begitu, saya dapat menyimpan referensi ke file yang sebelumnya dibuka dan melanjutkan dari bagian terakhir yang saya tinggalkan, bahkan setelah aplikasi dimuat ulang. Setiap kali disimpan, gambar akan dioptimalkan melalui library svgo, yang mungkin memerlukan waktu beberapa saat, bergantung pada kompleksitas SVG. Menampilkan dialog penyimpanan file memerlukan gestur pengguna. Oleh karena itu, penting untuk mendapatkan handle file sebelum pengoptimalan SVG terjadi, sehingga gestur pengguna tidak dibatalkan pada saat SVG yang dioptimalkan sudah siap.

try {
  let svg = svgOutput.innerHTML;
  let handle = null;
  // To not consume the user gesture obtain the handle before preparing the
  // blob, which may take longer.
  if (supported) {
    handle = await showSaveFilePicker({
      types: [{description: 'SVG file', accept: {'image/svg+xml': ['.svg']}}],
    });
  }
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  showToast(i18n.t('savedSVG'));
  const blob = new Blob([svg], {type: 'image/svg+xml'});
  await fileSave(blob, {description: 'SVG file'}, handle);
} catch (err) {
  console.error(err.name, err.message);
  showToast(err.message);
}

Tarik lalu lepas

Untuk membuka gambar input, saya dapat menggunakan fitur buka file, atau, seperti yang telah Anda lihat di atas, cukup tarik lalu lepas file gambar ke aplikasi. Fitur buka file cukup mudah, yang lebih menarik adalah kasus tarik lalu lepas. Hal yang sangat bagus tentang hal ini adalah Anda bisa mendapatkan handle sistem file dari item transfer data melalui metode getAsFileSystemHandle(). Seperti yang disebutkan sebelumnya, saya dapat mempertahankan handle ini sehingga siap saat aplikasi dimuat ulang.

document.addEventListener('drop', async (event) => {
  event.preventDefault();
  dropContainer.classList.remove('dropenter');
  const item = event.dataTransfer.items[0];
  if (item.kind === 'file') {
    inputImage.addEventListener(
      'load',
      () => {
        URL.revokeObjectURL(blobURL);
      },
      {once: true},
    );
    const handle = await item.getAsFileSystemHandle();
    if (handle.kind !== 'file') {
      return;
    }
    const file = await handle.getFile();
    const blobURL = URL.createObjectURL(file);
    inputImage.src = blobURL;
    await set(FILE_HANDLE, handle);
  }
});

Untuk detail selengkapnya, lihat artikel tentang File System Access API dan, jika Anda tertarik, pelajari kode sumber SVGcode di src/js/filesystem.js.

Async Clipboard API

SVGcode juga terintegrasi sepenuhnya dengan clipboard sistem operasi melalui Async Clipboard API. Anda dapat menempelkan gambar dari file explorer sistem operasi ke dalam aplikasi dengan mengklik tombol tempel gambar atau dengan menekan command atau control plus v pada keyboard.

Menempelkan gambar dari file explorer ke SVGcode.

Async Clipboard API baru-baru ini juga telah mendapatkan kemampuan untuk menangani gambar SVG, sehingga Anda juga dapat menyalin gambar SVG dan menempelkannya ke aplikasi lain untuk diproses lebih lanjut.

Menyalin gambar dari SVGcode ke SVGOMG.
copyButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  showToast(i18n.t('optimizingSVG'), Infinity);
  svg = await optimizeSVG(svg);
  const textBlob = new Blob([svg], {type: 'text/plain'});
  const svgBlob = new Blob([svg], {type: 'image/svg+xml'});
  navigator.clipboard.write([
    new ClipboardItem({
      [svgBlob.type]: svgBlob,
      [textBlob.type]: textBlob,
    }),
  ]);
  showToast(i18n.t('copiedSVG'));
});

Untuk mempelajari lebih lanjut, baca artikel Papan Klip Asinkron, atau lihat file src/js/clipboard.js.

Penanganan File

Salah satu fitur favorit saya dari SVGcode adalah kemampuannya dalam menyatu dengan sistem operasi. Sebagai PWA yang diinstal, aplikasi ini dapat menjadi pengendali file, atau bahkan pengendali file default, untuk file gambar. Artinya, saat saya berada di Finder di mesin macOS, saya dapat mengklik kanan gambar dan membukanya dengan SVGcode. Fitur ini disebut Penanganan File dan bekerja berdasarkan properti file_handlers di Manifes Aplikasi Web dan antrean peluncuran, yang memungkinkan aplikasi menggunakan file yang diteruskan.

Membuka file dari desktop dengan aplikasi SVGcode yang terinstal.
window.launchQueue.setConsumer(async (launchParams) => {
  if (!launchParams.files.length) {
    return;
  }
  for (const handle of launchParams.files) {
    const file = await handle.getFile();
    if (file.type.startsWith('image/')) {
      const blobURL = URL.createObjectURL(file);
      inputImage.addEventListener(
        'load',
        () => {
          URL.revokeObjectURL(blobURL);
        },
        {once: true},
      );
      inputImage.src = blobURL;
      await set(FILE_HANDLE, handle);
      return;
    }
  }
});

Untuk mengetahui informasi selengkapnya, lihat Mengizinkan aplikasi web yang terinstal menjadi pengendali file, dan lihat kode sumbernya di src/js/filehandling.js.

Berbagi Web (File)

Contoh lain dari membaur dengan sistem operasi adalah fitur berbagi aplikasi. Dengan asumsi saya ingin mengedit SVG yang dibuat dengan SVGcode, salah satu cara untuk menanganinya adalah dengan menyimpan file, meluncurkan aplikasi pengeditan SVG, lalu membuka file SVG dari sana. Namun, alur yang lebih lancar adalah dengan menggunakan Web Share API, yang memungkinkan file dibagikan secara langsung. Jadi, jika aplikasi pengeditan SVG adalah target berbagi, aplikasi tersebut dapat langsung menerima file tanpa deviasi.

shareSVGButton.addEventListener('click', async () => {
  let svg = svgOutput.innerHTML;
  svg = await optimizeSVG(svg);
  const suggestedFileName =
    getSuggestedFileName(await get(FILE_HANDLE)) || 'Untitled.svg';
  const file = new File([svg], suggestedFileName, { type: 'image/svg+xml' });
  const data = {
    files: [file],
  };
  if (navigator.canShare(data)) {
    try {
      await navigator.share(data);
    } catch (err) {
      if (err.name !== 'AbortError') {
        console.error(err.name, err.message);
      }
    }
  }
});
Membagikan gambar SVG ke Gmail.

Target Berbagi Web (File)

Sebaliknya, SVGcode juga dapat bertindak sebagai target berbagi dan menerima file dari aplikasi lain. Agar berfungsi, aplikasi perlu memberi tahu sistem operasi melalui Web Share Target API jenis data yang dapat diterima aplikasi. Hal ini terjadi melalui kolom khusus di Manifes Aplikasi Web.

{
  "share_target": {
    "action": "https://svgco.de/share-target/",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "files": [
        {
          "name": "image",
          "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
        }
      ]
    }
  }
}

Rute action tidak benar-benar ada, tetapi ditangani sepenuhnya di pengendali fetch pekerja layanan, yang kemudian meneruskan file yang diterima untuk pemrosesan sebenarnya di aplikasi.

self.addEventListener('fetch', (fetchEvent) => {
  if (
    fetchEvent.request.url.endsWith('/share-target/') &&
    fetchEvent.request.method === 'POST'
  ) {
    return fetchEvent.respondWith(
      (async () => {
        const formData = await fetchEvent.request.formData();
        const image = formData.get('image');
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        await mediaCache.put('shared-image', new Response(image));
        return Response.redirect('./?share-target', 303);
      })(),
    );
  }
});
Membagikan screenshot ke SVGcode.

Kesimpulan

Baiklah, ini adalah tur singkat tentang beberapa fitur aplikasi lanjutan di SVGcode. Semoga aplikasi ini dapat menjadi alat penting untuk kebutuhan pemrosesan gambar Anda bersama dengan aplikasi luar biasa lainnya seperti Squoosh atau SVGOMG.

SVGcode tersedia di svgco.de. Lihat apa yang saya lakukan di sana? Anda dapat meninjau kode sumbernya di GitHub. Perhatikan bahwa karena Potrace berlisensi GPL, begitu juga SVGcode. Setelah itu, selamat melakukan vektorisasi! Saya harap kode SVG akan berguna, dan beberapa fiturnya dapat menginspirasi aplikasi Anda berikutnya.

Ucapan terima kasih

Artikel ini ditinjau oleh Joe Medley.