4 pontos por GN⁺ 2024-12-04 | 1 comentários | Compartilhar no WhatsApp
  • Skia Canvas permite usar a API de desenho HTML Canvas no Node.js para renderização onscreen e offscreen; por ser baseada no Google Skia, produz uma saída muito parecida com a do <canvas> do Chrome
  • Os resultados podem ser exportados como vetores, como PDF·SVG, e bitmaps, como JPEG·PNG·WEBP, e podem ser conectados a arquivos, dataURL, Buffer e objetos Sharp
  • Também lida com áreas fora do Canvas do navegador e oferece renderização em GUI Window, framework de eventos, Canvas multipágina, operações de caminho, transformação de perspectiva 3D, CSS filter e controle tipográfico avançado
  • A renderização assíncrona e o I/O de arquivos são processados em threads nativas de um pool de workers configurado pelo usuário, sendo adequados para geração de imagens server-side e ambientes serverless
  • Os benchmarks medem separadamente os modos serial e async; no async, várias iterações são iniciadas simultaneamente para comparar o efeito de multithreading

Implementação de Canvas para uso em Node.js

  • Skia Canvas é uma implementação da API de desenho HTML Canvas para Node.js, com suporte tanto a renderização onscreen quanto offscreen
  • Usa o motor gráfico Google Skia para produzir resultados muito semelhantes aos do elemento <canvas> do Chrome
  • Inclui até recursos que o Canvas do navegador ainda não oferece

Formatos de saída e formas de exportação

  • A saída de imagem oferece suporte tanto a formatos vetoriais quanto a formatos bitmap
    • Vetores: PDF, SVG
    • Bitmap: JPEG, PNG, WEBP
  • O resultado do Canvas pode ser enviado para vários destinos
  • É possível desenhar diretamente em uma Window GUI interativa, com um framework de eventos semelhante ao do navegador

Renderização assíncrona e multipágina

  • A renderização e o I/O de arquivos são executados de forma assíncrona em um pool de workers que usa threads nativas
  • É possível criar várias páginas em um único Canvas
    • Pode ser exportado como um único PDF multipágina
    • Também pode ser salvo como uma sequência de imagens dividida em vários arquivos

Recursos gráficos além do Canvas do navegador

Controle tipográfico

  • A renderização de texto lida com múltiplas linhas e quebra automática de linha
  • Fornece métricas de texto por linha
  • Recursos OpenType como small-caps e ligature podem ser usados com a sintaxe padrão de font-variant
  • É possível controlar proporcionalmente espaçamento entre letras, espaçamento entre palavras e leading
  • Oferece suporte a variable fonts e mapeia valores de weight de forma transparente
  • Também é possível usar fontes não sistêmicas carregadas de arquivos locais

Exemplos de uso

  • O exemplo de geração de arquivo de imagem cria um Canvas, obtém um contexto 2D com getContext("2d") e desenha um retângulo usando um gradiente cônico como estilo de stroke
    • Salva uma imagem de alta densidade com saveAs("rainbox.png", {density:2})
    • canvas.png é usado como abreviação de canvas.toBuffer("png")
    • O resultado de toDataURL("png") pode ser inserido em uma string HTML
    • Também é possível salvar de forma síncrona na thread principal com saveAsSync("rainbox.pdf")
  • O exemplo de sequência multipágina cria páginas de cores diferentes com canvas.newPage() e as salva
    • Salva um PDF multipágina como all-pages.pdf
    • Cria vários arquivos, como page-01.png e page-02.png, com o padrão page-{2}.png
  • O exemplo de renderização em Window cria Window(300, 300) e desenha, no evento draw, um círculo cuja espessura da linha muda conforme o valor do frame
  • O exemplo de integração com Sharp.js mostra o fluxo de converter o resultado do Canvas em um objeto Sharp para pós-processamento, ou desenhar no Canvas com loadImage uma imagem criada pelo Sharp

Método e resultados dos benchmarks

  • Os benchmarks testam o Skia Canvas em dois modos
    • serial: espera cada tarefa de renderização antes de avançar para a próxima iteração
    • async: inicia todas as iterações do teste de uma vez e as executa em paralelo com suporte a multithreading
  • Os resultados completos podem ser vistos na página de resultados do canvas-benchmarks
  • No teste de latência de inicialização, com 100 iterações, o skia-canvas registrou menos de 1 ms por iteração e 33 ms no total
    • canvaskit-wasm: 25 ms por iteração, 2,47 s no total
    • canvas: 88 ms por iteração, 8,77 s no total
    • @napi-rs/canvas: 69 ms por iteração, 6,87 s no total
  • O teste de curvas Bézier, com 20 iterações, evidencia a diferença do processamento paralelo no modo async
    • skia-canvas serial: 137 ms por iteração, 2,74 s no total
    • skia-canvas async: 28 ms por iteração, 558 ms no total
    • Os comparativos foram canvaskit-wasm com 790 ms, canvas com 486 ms e @napi-rs/canvas com 230 ms
  • No teste SVG to PNG, com 100 iterações, o canvaskit-wasm não tinha suporte, e o skia-canvas registrou 58 ms por iteração no serial e 11 ms por iteração no async
    • skia-canvas serial: 5,83 s no total
    • skia-canvas async: 1,08 s no total
  • Resultados do teste de escala e rotação de imagens, com 50 iterações
    • skia-canvas serial: 100 ms por iteração, 5,00 s no total
    • skia-canvas async: 19 ms por iteração, 935 ms no total
  • Resultados do teste de texto básico, com 200 iterações
    • skia-canvas serial: 21 ms por iteração, 4,26 s no total
    • skia-canvas async: 4 ms por iteração, 819 ms no total

1 comentários

 
GN⁺ 2024-12-04
Opiniões no Hacker News
  • https://windowjs.org também é um conceito muito parecido
    Ele envolve o Skia e o expõe como Canvas API, mas, em vez de Node, incorpora o v8 e funciona como um runtime bem pequeno
    Foi o primeiro projeto open source que publiquei, cerca de 3 anos atrás, e havia planos de expor WebGL, áudio etc. para transformá-lo em uma plataforma de jogos em JavaScript para desktop, mas o desenvolvimento foi interrompido por causa de outros trabalhos e projetos
    Fico feliz em ver surgir um projeto assim, que permite usar Canvas também fora do navegador

    • Bem legal, mas, se isolamento não for estritamente necessário, um ambiente só com v8 deixa a desejar
      Às vezes você só quer fazer uma requisição de rede
    • Projeto legal
      Fico curioso para saber quanto o tamanho do binário diminui ao incorporar apenas o V8 em vez do Node.js
      O Node.js também usa V8, então também fico curioso se a maior parte do tamanho do binário do Node vem do runtime, e não do V8
      Dei uma olhada no site e no repositório do GitHub, mas não consegui encontrar quantos kB ou MB costuma ter o binário do Window.js
  • Por curiosidade, onde esse tipo de biblioteca é usado?
    No desktop, imagino que existam bibliotecas nativas melhores para desenhar formas

    • No Soundslice, usamos uma biblioteca gráfica personalizada de renderização de partituras feita com JavaScript/Canvas no frontend
      Como também precisamos gerar PDFs vetoriais no lado do servidor, usamos uma biblioteca Node que permite usar a HTML Canvas API e criar PDFs
      Isso é bom porque o resultado fica exatamente igual às partituras renderizadas no navegador
      Em resumo, esse tipo de biblioteca permite reutilizar código em ambientes que não são navegadores
    • Em desenho 2D, praticamente não há muita coisa tão poderosa quanto o Skia que se possa chamar comumente de “nativa”
      A grande vantagem do Skia está em aproveitar gráficos nativos de forma eficaz, e é justamente por isso que ele é usado em engines de navegador
      Além disso, ter uma única API-alvo também é uma vantagem
    • Portabilidade não é a vantagem? Fico curioso para saber que outras bibliotecas de renderização 2D aceleradas por GPU existem
      Por um lado, isto é de mais baixo nível; por outro, já usei o Puppeteer como se fosse uma biblioteca gráfica 2D
      Você obtém Canvas 2D, WebGL, WebGPU e HTML/CSS completos, além de poder usar efeitos de texto, imagens de fundo e transformações CSS
      Também é possível carregar imagens e vídeos, e usar a função de screenshot para transformar o resultado combinado em uma imagem
      É exagerado, mas para o meu caso de uso não importava; funcionava, era multiplataforma e resolvia o problema
    • Eu ficaria surpreso se alguém conseguisse citar uma plataforma com uma API de desenho gráfico 2D melhor que Canvas
      Skia é um pouco mais rápido que Direct2D e muito mais rápido que Cairo
      É altamente otimizado e também tem backends com aceleração por GPU
    • Poder executar o mesmo código em várias plataformas é um grande bônus
      Canvas também é uma API de desenho 2D muito comum
      O iOS tem uma versão nativa e o Android também tem uma versão nativa, e as operações de desenho são, em geral, parecidas
      Aproveitar isso traz muitos benefícios
  • O Skia inclui o CanvasKit, uma build WASM com suporte a Node: https://www.npmjs.com/package/canvaskit-wasm, e este módulo parece ser um binding em Rust
    Fico curioso sobre os prós e contras das duas abordagens

    • Em vez de “binding em Rust”, está mais para um binário Node pré-compilado
      Ele apenas é gerado internamente a partir de uma base de código mista em Rust/C++
      Então a diferença principal é que o WASM precisa de um runtime separado, enquanto o binário Node não precisa
  • Isto é algo como um wrapper em torno de um crate Rust?
    https://rust-skia.github.io/doc/skia_safe/canvas/struct.Canvas.html

    • Pelo que entendo, o Skia Canvas pode funcionar em praticamente qualquer linguagem de programação, desde que haja bindings https://www.swig.org/
      Não vejo nada muito especial no Node
    • Sim, é gerado com napi-rs
  • Eu estava esperando por algo assim há muito tempo
    Tentativas anteriores de fazer algo parecido exigiam instalação via Node-Gyp, o que era um verdadeiro pesadelo

  • É mais do que uma simples API de renderização: há a afirmação de que “pode renderizar em uma janela usando o pipeline gráfico nativo do SO e oferece um framework de eventos de UI semelhante ao do navegador”
    Também seria possível adicionar wgpu para suporte a WebGPU e ANGLE para suporte a WebGL

    • Skia é uma biblioteca amplamente usada; só de cabeça, Chrome, Firefox e LibreOffice a utilizam
      Ela oferece suporte a muitos alvos de renderização, incluindo WebGPU e ANGLE
    • É bem parecido com o que o Qt já oferece
      QPainter lida com imagens brutas e janelas, e também há widgets mais sofisticados com suporte a eventos
      Além disso, existe uma funcionalidade que eu vinha procurando havia um tempo: no Qt, há o graphicsview, que consegue renderizar independentemente da resolução milhões de objetos em um plano gráfico maior que a janela e ainda tratar eventos
  • Se você tem interesse em implementações de Canvas compatíveis com Node, há algumas opções
    O canvaskit-wasm do projeto Skia não parece ter aceleração por GPU: https://github.com/google/skia/tree/main/modules/canvaskit/npm_build
    @napi-rs/canvas é o binding mais rápido: https://github.com/Brooooooklyn/canvas?tab=readme-ov-file#performance
    node-canvas usa Cairo em vez de Skia: https://github.com/Automattic/node-canvas

  • Estou usando isto atualmente com Deno quando preciso de uma janela gráfica simples no Advent of Code
    Também há um window-example: https://skia-canvas.org/#rendering-to-a-window
    Basta adicionar com deno add npm:skia-canvas, colocar "nodeModulesDir": "auto" no deno.json e então executar deno install --allow-scripts

  • Ver isto me deixa realmente animado
    Parece que vai tornar muito mais fácil escrever gráficos compatíveis com a web e que também podem rodar de forma independente
    Também elimina a necessidade de compilar a partir do código-fonte e gerar código em uma segunda linguagem de script para compilar uma biblioteca para a primeira linguagem de script
    Também é interessante pensar em quão bem isso se encaixaria para renderização em tempo real de código Canvas em lugares como o VS Code, ou para executar código que tenha uma camada de conversão para rodar no Canvas da web