4 puntos por GN⁺ 2024-12-04 | 1 comentarios | Compartir por WhatsApp
  • Skia Canvas permite usar en Node.js la API de dibujo de HTML Canvas para renderizado en pantalla y fuera de pantalla, y como está basado en Google Skia, produce una salida muy similar a la de <canvas> de Chrome
  • Los resultados pueden exportarse como vectores, como PDF y SVG, y como mapas de bits, como JPEG, PNG y WEBP, además de conectarse con archivos, dataURL, Buffer y objetos Sharp
  • También cubre áreas fuera del Canvas del navegador, con renderizado de Window GUI, framework de eventos, Canvas multipágina, operaciones de rutas, transformaciones de perspectiva 3D, filtros CSS y control tipográfico avanzado
  • El renderizado asíncrono y la E/S de archivos se procesan en hilos nativos de un pool de workers configurable por el usuario, por lo que encaja bien con generación de imágenes del lado del servidor y entornos serverless
  • Los benchmarks se miden por separado en modos serial y async; en async se inician varias tareas repetidas al mismo tiempo para comparar el efecto del multithreading

Implementación de Canvas para usar en Node.js

  • Skia Canvas es una implementación de la API de dibujo HTML Canvas para Node.js, con soporte tanto para renderizado en pantalla como fuera de pantalla
  • Usa el motor gráfico de Google Skia para producir resultados muy similares al elemento <canvas> de Chrome
  • Incluye incluso funciones que el Canvas del navegador todavía no ofrece

Formatos de salida y formas de exportación

  • La salida de imágenes admite tanto formatos vectoriales como formatos de mapa de bits
    • Vectoriales: PDF, SVG
    • Mapas de bits: JPEG, PNG, WEBP
  • El resultado del Canvas puede enviarse a varios destinos
  • Se puede dibujar directamente en una Window GUI interactiva y ofrece un framework de eventos similar al del navegador

Renderizado asíncrono y multipágina

  • El renderizado y la E/S de archivos se ejecutan de forma asíncrona en un pool de workers que usa hilos nativos
  • Se pueden crear varias páginas dentro de un mismo Canvas
    • Es posible exportarlas como un solo PDF multipágina
    • También pueden guardarse como una secuencia de imágenes dividida en varios archivos

Funciones gráficas más allá del Canvas del navegador

Control tipográfico

  • El renderizado de texto maneja múltiples líneas y ajuste automático de línea
  • Proporciona métricas de texto por línea
  • Funciones OpenType como small-caps y ligature pueden usarse con la sintaxis estándar de font-variant
  • Es posible controlar proporcionalmente el espaciado entre letras, el espaciado entre palabras y el leading
  • Es compatible con variable fonts y mapea de forma transparente los valores de weight
  • También se pueden usar fuentes no incluidas en el sistema cargadas desde archivos locales mediante load

Ejemplos de uso

  • El ejemplo de creación de archivo de imagen crea un Canvas, obtiene un contexto 2D con getContext("2d") y luego dibuja un rectángulo usando un gradiente cónico como estilo de trazo
    • Guarda una imagen de alta densidad con saveAs("rainbox.png", {density:2})
    • canvas.png se usa como forma abreviada de canvas.toBuffer("png")
    • El resultado de toDataURL("png") puede insertarse dentro de una cadena HTML
    • También es posible guardar de forma síncrona en el hilo principal con saveAsSync("rainbox.pdf")
  • El ejemplo de secuencia multipágina crea páginas de distintos colores con canvas.newPage() y luego las guarda
    • Guarda un PDF multipágina como all-pages.pdf
    • Genera varios archivos como page-01.png, page-02.png con el patrón page-{2}.png
  • El ejemplo de renderizado con Window crea Window(300, 300) y en el evento draw dibuja un círculo cuyo grosor de línea cambia según el valor del frame
  • El ejemplo de integración con Sharp.js muestra el flujo para convertir el resultado del Canvas en un objeto Sharp para postprocesarlo, o dibujar en Canvas una imagen creada con Sharp usando loadImage

Método y resultados de benchmark

  • El benchmark prueba Skia Canvas en dos modos
    • serial: espera a que termine cada tarea de renderizado antes de pasar a la siguiente repetición
    • async: inicia de una vez todas las repeticiones de la prueba y las ejecuta en paralelo con soporte de multithreading
  • Los resultados completos pueden revisarse en la página de resultados de canvas-benchmarks
  • En la prueba de latencia de arranque, con 100 repeticiones, skia-canvas registró menos de 1 ms por ejecución y 33 ms en total
    • canvaskit-wasm: 25 ms por ejecución, 2.47 s en total
    • canvas: 88 ms por ejecución, 8.77 s en total
    • @napi-rs/canvas: 69 ms por ejecución, 6.87 s en total
  • En la prueba de curvas Bézier, con 20 repeticiones, se hizo visible la diferencia del procesamiento en paralelo en modo async
    • skia-canvas serial: 137 ms por ejecución, 2.74 s en total
    • skia-canvas async: 28 ms por ejecución, 558 ms en total
    • Los comparados fueron canvaskit-wasm con 790 ms, canvas con 486 ms y @napi-rs/canvas con 230 ms
  • En la prueba de SVG a PNG, con 100 repeticiones, canvaskit-wasm no era compatible, y skia-canvas registró 58 ms por ejecución en serial y 11 ms por ejecución en async
    • skia-canvas serial: 5.83 s en total
    • skia-canvas async: 1.08 s en total
  • Resultados de 50 repeticiones en la prueba de escalado y rotación de imagen
    • skia-canvas serial: 100 ms por ejecución, 5.00 s en total
    • skia-canvas async: 19 ms por ejecución, 935 ms en total
  • Resultados de 200 repeticiones en la prueba básica de texto
    • skia-canvas serial: 21 ms por ejecución, 4.26 s en total
    • skia-canvas async: 4 ms por ejecución, 819 ms en total

1 comentarios

 
GN⁺ 2024-12-04
Opiniones en Hacker News
  • https://windowjs.org también es un concepto muy parecido
    Envuelve Skia y lo expone como Canvas API, pero en vez de Node incorpora v8 y funciona como un runtime muy pequeño
    Fue mi primer proyecto open source publicado hace unos 3 años, y también tenía planes de exponer WebGL, audio, etc. para convertirlo en una plataforma de juegos JavaScript de escritorio, pero el desarrollo quedó detenido por otros trabajos y proyectos
    Me alegra ver que aparezcan proyectos así que permiten usar Canvas fuera del navegador

    • Está bastante genial, pero si el aislamiento no es algo estrictamente necesario, un entorno con solo v8 se queda corto
      A veces uno simplemente quiere hacer una petición de red
    • Gran proyecto
      Me da curiosidad cuánto se reduce el tamaño del binario al incorporar solo V8 en lugar de Node.js
      Node.js también usa V8, y también me pregunto si la mayor parte del tamaño del binario de Node no viene de V8 sino del runtime
      Revisé el sitio web y el repositorio de GitHub, pero no encontré cuántos kB o MB suele pesar el binario de Window.js
  • Por curiosidad, ¿para qué se usan este tipo de librerías?
    Si es para escritorio, pensaría que hay librerías nativas mejores para dibujar formas

    • En Soundslice usamos una librería gráfica personalizada para renderizar partituras, hecha con JavaScript/Canvas en el frontend
      Como también necesitamos generar PDFs vectoriales del lado del servidor, usamos una librería de Node que permite usar la HTML Canvas API y crear PDFs
      Lo bueno de esto es que el resultado queda exactamente igual que la partitura renderizada en el navegador web
      En resumen, estas librerías permiten reutilizar código incluso en entornos que no son el navegador
    • En dibujo 2D, en realidad no hay muchas cosas tan potentes como Skia que además uno llamaría comúnmente “nativas”
      La gran ventaja de Skia está en aprovechar eficazmente los gráficos nativos, y por eso se usa en motores de navegador desde el principio
      Además, que el objetivo sea una sola API también es una ventaja
    • ¿No es la portabilidad la ventaja? Me pregunto qué otras librerías de renderizado 2D con aceleración por GPU existen
      Por un lado esto es de más bajo nivel, pero por otro también he usado Puppeteer como si fuera una librería de gráficos 2D
      Obtienes Canvas 2D, WebGL, WebGPU y todo HTML/CSS, y puedes usar efectos de texto, imágenes de fondo y transformaciones CSS
      También puedes cargar imágenes y videos, y con la función de screenshots convertir el resultado compuesto en una imagen
      Es exagerado, pero para mi caso no importaba; funciona, es multiplataforma y resuelve el problema
    • Me sorprendería si pudieras nombrar una plataforma con una API de dibujo de gráficos 2D mejor que Canvas
      Skia es un poco más rápida que Direct2D y mucho más rápida que Cairo
      Está muy optimizada y también tiene backends con aceleración por GPU
    • Poder ejecutar el mismo código en varias plataformas es un gran extra
      Canvas también es una API de dibujo 2D muy común
      iOS tiene una versión nativa y Android también tiene una versión nativa, y las operaciones de dibujo son en general parecidas
      Aprovechar eso aporta mucho
  • Skia incluye CanvasKit, una build WASM compatible con Node: https://www.npmjs.com/package/canvaskit-wasm, y este módulo parece ser un binding de Rust
    Me dan curiosidad las ventajas y desventajas de ambos enfoques

    • Más que un “binding de Rust”, se parece más a un binario de Node precompilado
      Internamente simplemente se genera a partir de una base de código mixta Rust/C++
      Así que la diferencia clave es que WASM necesita un runtime aparte, mientras que el binario de Node no
  • ¿Esto es como un wrapper alrededor de un crate de Rust?
    https://rust-skia.github.io/doc/skia_safe/canvas/struct.Canvas.html

    • Entiendo que Skia Canvas puede funcionar en casi cualquier lenguaje de programación siempre que haya bindings https://www.swig.org/
      No veo mucho que sea especial de Node
    • Correcto, se genera con napi-rs
  • Llevaba mucho tiempo esperando algo así
    Los intentos anteriores de hacer algo parecido requerían instalar Node-Gyp, y eso era una verdadera pesadilla

  • Es más que una simple API de renderizado: dice que “puede renderizar en una ventana mediante el pipeline gráfico nativo del OS y ofrece un framework de eventos de UI similar al de un navegador”
    También podrían agregar wgpu para soporte de WebGPU y ANGLE para soporte de WebGL

    • Skia es una librería muy usada; de los que se me vienen a la mente, la usan Chrome, Firefox y LibreOffice
      Soporta muchos destinos de renderizado, incluidos WebGPU y ANGLE
    • Es bastante parecido a lo que Qt ya ofrece
      QPainter maneja imágenes sin procesar y ventanas, y también hay widgets más sofisticados con soporte de eventos
      Además, una función que estuve buscando durante un tiempo: Qt tiene graphicsview, que puede renderizar de forma independiente de la resolución millones de objetos en un plano gráfico más grande que la ventana y también manejar eventos
  • Si te interesan las implementaciones de Canvas compatibles con Node, hay varias
    canvaskit-wasm del proyecto Skia parece no tener aceleración por GPU: https://github.com/google/skia/tree/main/modules/canvaskit/npm_build
    @napi-rs/canvas es el binding más rápido: https://github.com/Brooooooklyn/canvas?tab=readme-ov-file#performance
    node-canvas usa Cairo en lugar de Skia: https://github.com/Automattic/node-canvas

  • Actualmente lo uso con Deno cuando necesito una ventana gráfica simple para Advent of Code
    También hay un ejemplo con ventana: https://skia-canvas.org/#rendering-to-a-window
    Basta con agregarlo con deno add npm:skia-canvas, poner "nodeModulesDir": "auto" en deno.json y luego ejecutar deno install --allow-scripts

  • Ver esto me entusiasma mucho
    Parece que permitirá escribir gráficos independientes y compatibles con la web de una forma mucho más cómoda
    Ya no hará falta compilar desde el código fuente ni generar código con un segundo lenguaje de scripting para compilar una librería destinada al primer lenguaje de scripting
    También será interesante ver qué tan bien encaja para renderizar en tiempo real código de Canvas en lugares como VS Code, o para ejecutar código que tenga una capa de traducción pensada para correr sobre Web Canvas