- Skia Canvas, Node.js’te HTML Canvas çizim API’sinin ekranda ve ekran dışında render edilmesini sağlar; Google Skia tabanlı olduğu için Chrome
<canvas>ile çok benzer çıktılar üretir - Çıktılar PDF·SVG gibi vektör ve JPEG·PNG·WEBP gibi bitmap formatlarında dışa aktarılabilir; dosya·dataURL·Buffer·Sharp nesnesi olarak kullanılabilir
- Tarayıcı Canvas’ının ötesindeki alanları da kapsar; GUI Window render etme, olay çerçevesi, çok sayfalı Canvas, yol işlemleri, 3D perspektif dönüşümleri, CSS filter, gelişmiş tipografi denetimi sunar
- Asenkron render alma ve dosya G/Ç işlemleri, kullanıcının yapılandırdığı worker pool içindeki yerel iş parçacıklarında yürütülür; bu da sunucu tarafı görsel üretimi ve serverless ortamlar için uygundur
- Benchmark’lar serial ve async modlarını ayrı ölçer; async modunda birden çok tekrar görevi aynı anda başlatılarak multithreading etkisi karşılaştırılır
Node.js’te kullanılan Canvas uygulaması
- Skia Canvas, Node.js için bir HTML Canvas çizim API’si uygulamasıdır ve hem ekranda hem ekran dışında render almayı destekler
- Google’ın Skia grafik motorunu kullanarak Chrome
<canvas>öğesine çok benzer sonuçlar üretir - Tarayıcı Canvas’ının henüz sunmadığı özellikleri de içerir
Çıktı biçimleri ve dışa aktarma yöntemleri
- Görsel çıktıda hem vektör formatları hem de bitmap formatları desteklenir
- Vektör: PDF, SVG
- Bitmap: JPEG, PNG, WEBP
- Canvas sonucu farklı hedeflere gönderilebilir
- Etkileşimli GUI Window üzerine doğrudan çizim yapılabilir ve tarayıcıya benzer bir event çerçevesi sunulur
Asenkron render alma ve çoklu sayfa
- Render alma ve dosya G/Ç, yerel iş parçacıkları kullanan bir worker pool içinde asenkron olarak çalıştırılır
- Worker pool kullanıcı tarafından yapılandırılabilir
- Tek bir Canvas içinde birden fazla sayfa oluşturulabilir
- Tek bir çok sayfalı PDF olarak çıktı alınabilir
- Birden fazla dosyaya bölünen görsel dizisi olarak da kaydedilebilir
Tarayıcı Canvas’ının ötesine geçen grafik özellikleri
- Bézier yolları boolean işlemleri ve nokta düzeyinde enterpolasyonu destekler
- Dönüşüm özellikleri, temel Canvas’ın ölçekleme·döndürme·taşıma işlemlerine ek olarak 3D perspektif dönüşümleri sunar
- Şekil doldurmada bitmap tabanlı Pattern’in yanı sıra vektör tabanlı Texture da kullanılabilir
- Çizgiler için özel marker eklenebilir
- Görsel işleme tarafında tüm CSS filter operatör seti desteklenir
Tipografi denetimi
- Metin render etme, çok satırlı metni ve otomatik satır kaydırmayı işler
- Satır bazında metin metrikleri sunar
- small-caps, ligature gibi OpenType özellikleri standart font-variant söz dizimiyle kullanılabilir
- Harf aralığı, kelime aralığı ve leading orantılı olarak denetlenebilir
- variable fonts desteklenir ve weight değerleri şeffaf biçimde eşlenir
- Yerel dosyalardan yüklenen sistem dışı fontlar da kullanılabilir
Kullanım örnekleri
- Görsel dosyası oluşturma örneğinde
CanvasoluşturulupgetContext("2d")ile 2D context alındıktan sonra, konik gradient stroke stili olarak kullanılarak bir dikdörtgen çizilirsaveAs("rainbox.png", {density:2})ile yüksek yoğunluklu görsel kaydedilircanvas.png,canvas.toBuffer("png")için kısaltma olarak kullanılırtoDataURL("png")sonucu HTML dizesi içine yerleştirilebilirsaveAsSync("rainbox.pdf")ile ana iş parçacığında senkron kayıt da mümkündür
- Çok sayfalı dizi örneğinde
canvas.newPage()ile farklı renklerde sayfalar oluşturulup kaydedilirall-pages.pdfolarak çok sayfalı PDF kaydedilirpage-{2}.pngdeseniylepage-01.png,page-02.pnggibi birden çok dosya üretilir
- Window render örneğinde
Window(300, 300)oluşturulur vedrawevent’inde frame değerine göre çizgi kalınlığı değişen bir daire çizilir - Sharp.js entegrasyon örneği, Canvas sonucunu Sharp nesnesine dönüştürerek sonradan işlemeyi ya da Sharp ile üretilen görselin
loadImageile Canvas üzerine çizilmesini gösterir
Benchmark yöntemi ve sonuçlar
- Benchmark’lar Skia Canvas’ı iki modda test eder
- serial: Her render görevini bekledikten sonra bir sonraki tekrara geçilir
- async: Tüm test tekrarları tek seferde başlatılır ve multithreading desteğiyle paralel yürütülür
- Tüm sonuçlar canvas-benchmarks sonuç sayfasında görülebilir
- Başlangıç gecikmesi testinde, 100 tekrar ölçümünde skia-canvas tekrar başına 1ms’nin altında ve toplam 33ms kaydetti
- canvaskit-wasm: tekrar başına 25ms, toplam 2.47 saniye
- canvas: tekrar başına 88ms, toplam 8.77 saniye
- @napi-rs/canvas: tekrar başına 69ms, toplam 6.87 saniye
- Bézier curves testinde, 20 tekrar ölçümünde async moddaki paralel işleme farkı belirginleşti
- skia-canvas serial: tekrar başına 137ms, toplam 2.74 saniye
- skia-canvas async: tekrar başına 28ms, toplam 558ms
- Karşılaştırma için canvaskit-wasm 790ms, canvas 486ms, @napi-rs/canvas 230ms idi
- SVG to PNG testinde, 100 tekrar ölçümünde canvaskit-wasm bunu desteklemedi; skia-canvas serial modda tekrar başına 58ms, async modda tekrar başına 11ms kaydetti
- skia-canvas serial: toplam 5.83 saniye
- skia-canvas async: toplam 1.08 saniye
- Görsel ölçekleme·döndürme testinde 50 tekrar sonucu
- skia-canvas serial: tekrar başına 100ms, toplam 5.00 saniye
- skia-canvas async: tekrar başına 19ms, toplam 935ms
- Temel metin testinde 200 tekrar sonucu
- skia-canvas serial: tekrar başına 21ms, toplam 4.26 saniye
- skia-canvas async: tekrar başına 4ms, toplam 819ms
1 yorum
Hacker News yorumları
https://windowjs.org da çok benzer bir konsept
Skia'yı sarmalayıp Canvas API olarak sunuyor, ancak Node yerine v8'i gömülü kullanarak çok küçük bir runtime ile çalışıyor
Yaklaşık 3 yıl önce yayımladığım ilk açık kaynak projemdi; WebGL, ses vb. özellikleri sunup masaüstü için bir JavaScript oyun platformuna dönüştürme planım da vardı, ancak başka işler ve projeler yüzünden geliştirme durdu
Canvas'ı tarayıcı dışında da kullanılabilir kılan böyle projelerin çıkmasına seviniyorum
Bazen sadece ağ isteği yapmak isteyebiliyorsunuz
Node.js yerine yalnızca V8 gömülü olunca ikili dosya boyutunun ne kadar küçüldüğünü merak ediyorum
Node.js de V8 kullanıyor; Node ikili dosyasının boyutunun büyük kısmı V8'den değil de runtime'dan mı geliyor, onu da merak ediyorum
Web sitesine ve GitHub deposuna baktım ama Window.js ikili dosyasının genelde kaç kB ya da MB olduğunu bulamadım
Meraktan soruyorum, bu tür kütüphaneler nerede kullanılıyor acaba
Masaüstünde şekil çizmek için daha iyi yerel kütüphaneler yok mu diye düşünüyorum
Sunucu tarafında da vektörel PDF üretmemiz gerektiği için HTML Canvas API'sini kullanıp PDF oluşturabilen bir Node kütüphanesi kullanıyoruz
Bu sayede sonuçların web tarayıcısında render edilen notalarla birebir aynı olması güzel oluyor
Sonuç olarak bu tür kütüphaneler, tarayıcı dışı ortamlarda da kodun yeniden kullanılmasını sağlıyor
Skia'nın en büyük avantajı yerel grafikleri etkili biçimde kullanmasında ve zaten tarayıcı motorlarında kullanılmasının nedeni de bu
Üstelik hedef API'nin tek olması da bir avantaj
Bir yandan bu daha düşük seviyeli, ama diğer yandan Puppeteer'ı 2D grafik kütüphanesi gibi kullandığım da oldu
Canvas 2D, WebGL, WebGPU ve HTML/CSS'in tamamını elde ediyorsunuz; metin efektleri, arka plan görselleri ve CSS dönüşümlerini de kullanabiliyorsunuz
Görsel ve video yüklemek de mümkün; ekran görüntüsü özelliğiyle birleştirdiğiniz sonucu görsele dönüştürebiliyorsunuz
Abartılı ama benim kullanımım için sorun değildi; çalışıyor, çapraz platform ve problemi çözüyor
Skia, Direct2D'den biraz daha hızlı ve Cairo'dan çok daha hızlı
Son derece optimize edilmiş durumda ve GPU hızlandırmalı backend'leri de var
Canvas aynı zamanda çok yaygın bir 2D çizim API'si
iOS'ta da yerel bir sürümü var, Android'de de yerel bir sürümü var; çizim işlemleri de genel olarak benzer
Bundan yararlanarak çok şey kazanılabiliyor
Skia'da Node'u destekleyen bir WASM derlemesi olan CanvasKit de var: https://www.npmjs.com/package/canvaskit-wasm; bu modül ise bir Rust binding'i gibi görünüyor
İki yaklaşımın artı ve eksilerini merak ediyorum
Sadece içerde Rust/C++ karışık bir kod tabanından üretilmiş
Dolayısıyla temel fark, WASM'in ayrı bir runtime'a ihtiyaç duyması, Node ikili dosyasının ise duymaması
Bu, Rust crate'ini saran bir wrapper gibi bir şey mi?
https://rust-skia.github.io/doc/skia_safe/canvas/struct.Canvas.html
Node'a özel pek bir şey göremiyorum
Bunu uzun zamandır bekliyordum
Daha önce benzer şeyler yapmaya çalışan girişimler Node-Gyp kurulumu gerektiriyordu ve gerçekten kâbus gibiydi
Basit bir render API'sinden fazlasıymış: “OS'in yerel grafik pipeline'ı ile pencerelere render edebiliyor ve tarayıcı benzeri bir UI olay framework'ü sağlıyor” diye bir ifade var
WebGPU desteği için wgpu, WebGL desteği için de ANGLE eklenebilir gibi
WebGPU ve ANGLE dahil pek çok render hedefini destekliyor
QPainter ham görselleri ve pencereleri ele alıyor; olay desteği olan daha gelişmiş widget'lar da var
Ayrıca bir süredir aradığım bir özellik olarak, Qt'de pencereden daha büyük bir grafik düzleminde milyonlarca nesneyi çözünürlükten bağımsız biçimde render edip olayları da işleyebilen graphicsview var
Node uyumlu Canvas implementasyonlarıyla ilgileniyorsanız birkaç seçenek var
Skia projesindeki canvaskit-wasm GPU hızlandırmalı değil gibi görünüyor: https://github.com/google/skia/tree/main/modules/canvaskit/npm_build
@napi-rs/canvas en hızlı binding: https://github.com/Brooooooklyn/canvas?tab=readme-ov-file#performance
node-canvas ise Skia yerine Cairo kullanıyor: https://github.com/Automattic/node-canvas
Advent of Code'da basit bir grafik penceresine ihtiyaç duyduğumda şu anda Deno ile birlikte kullanıyorum
window-example da var: https://skia-canvas.org/#rendering-to-a-window
deno add npm:skia-canvasile ekleyip,deno.jsoniçine"nodeModulesDir": "auto"koyduktan sonradeno install --allow-scriptsçalıştırmanız yeterliBunu görünce gerçekten heyecanlandım
Hem bağımsız çalışabilen hem de web ile uyumlu grafikleri çok daha rahat yazmayı sağlayacak gibi görünüyor
İlk betik dili için bir kütüphaneyi derlemek üzere ikinci bir betik diliyle kaynaktan build edip kod üretmeye de gerek kalmayacak
VS Code gibi yerlerde Canvas kodunu gerçek zamanlı render etmek ya da web Canvas'ta çalıştırmak için dönüşüm katmanı olan kodları çalıştırmak açısından ne kadar uygun olacağını görmek de ilginç olacak