### Quick Start: Clone and Build Native Bindings Source: https://github.com/brooooooklyn/canvas/blob/main/CONTRIBUTING.md Steps to clone the repository, download pre-built Skia binaries, install dependencies, and build the native Node-API bindings for your host machine. ```bash # 1. Clone with submodules (Skia lives in ./skia) git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git cd canvas # 2. Download a matching *pre-built* Skia binary node scripts/release-skia-binary.mjs --download # 3. Install JS deps (skip heavy bench deps) yarn install --immutable --mode=skip-build # 4. Build the native Node-API bindings for **your** host machine yarn build # => builds .node in the project root # 5. Run the full test-suite & code quality checks yarn test ``` -------------------------------- ### Run Tiger Example Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Executes the tiger example script. ```shell node example/tiger.js ``` -------------------------------- ### Run Anime Girl Example Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Executes the anime-girl example script. ```shell node example/anime-girl.js ``` -------------------------------- ### Build Skia from Source Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Instructions for cloning the repository, building Skia from source, installing dependencies, and building the Node.js addon. Requires clang. ```shell $ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git $ cd canvas # Build Skia: $ node scripts/build-skia.js # Install NPM packages, build the Node.js addon: $ npm install -g yarn $ yarn install --mode=skip-build # Here are modules that are used for benchmarking and are hard to install, you can skip it by specifying `--mode=skip-build` $ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html $ yarn build # All done! Run test cases or examples now: $ yarn test $ node example/tiger.js ``` -------------------------------- ### Install @napi-rs/canvas Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Install the library using either yarn or npm. ```bash yarn add @napi-rs/canvas npm install @napi-rs/canvas ``` -------------------------------- ### Local Build: Rebuild Bindings for macOS/Apple Silicon Source: https://github.com/brooooooklyn/canvas/blob/main/CONTRIBUTING.md Example commands to set up the environment, build Skia for a specific target (macOS/Apple Silicon), and then rebuild the Node-API bindings against the newly built Skia. ```bash # Example: Rebuild for macOS/Apple Silicon export MACOSX_DEPLOYMENT_TARGET=11.0 clang --version node scripts/build-skia.js --target=aarch64-apple-darwin # If you only need static libraries locally you can stop here. # Re-build the bindings against your fresh Skia node scripts/release-skia-binary.mjs --download --target=aarch64-apple-darwin # (above command will pick the libs you just built) yarn build --target aarch64-apple-darwin ``` -------------------------------- ### Pull Pre-built Skia Binary Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Instructions for cloning the repository, downloading pre-built Skia binaries, installing dependencies, and building the Node.js addon. Requires clang. ```shell # Clone the code: $ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git $ cd canvas # Download Skia binaries: # It will pull the binaries match the git hash in `./skia` submodule $ node scripts/release-skia-binary.mjs --download # Install NPM packages, build the Node.js addon: $ npm install -g yarn $ yarn install --mode=skip-build $ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html $ yarn build # All done! Run test cases or examples now: $ yarn test $ node example/tiger.js ``` -------------------------------- ### Create Raster and SVG Canvases Source: https://context7.com/brooooooklyn/canvas/llms.txt Demonstrates creating both raster and SVG canvases, drawing on them, and encoding the raster output to PNG. For SVG, it shows how to get the raw SVG bytes. ```typescript import { promises as fs } from 'node:fs' import { createCanvas, SvgExportFlag } from '@napi-rs/canvas' // Raster canvas const canvas = createCanvas(800, 600) const ctx = canvas.getContext('2d') ctx.fillStyle = '#1a1a2e' ctx.fillRect(0, 0, 800, 600) ctx.strokeStyle = '#e94560' ctx.lineWidth = 4 ctx.beginPath() ctx.arc(400, 300, 200, 0, Math.PI * 2) ctx.stroke() // Async encode (runs in libuv thread pool, non-blocking) const png = await canvas.encode('png') await fs.writeFile('circle.png', png) // Synchronous encode (blocks event loop) const jpeg = canvas.encodeSync('jpeg', 90) // quality 0-100 const webp = canvas.encodeSync('webp', 80) // SVG canvas β€” vector output const svgCanvas = createCanvas(400, 300, SvgExportFlag.NoPrettyXML) const svgCtx = svgCanvas.getContext('2d') svgCtx.fillStyle = 'tomato' svgCtx.fillRect(10, 10, 100, 100) const svgBytes = svgCanvas.getContent() // Buffer containing SVG XML await fs.writeFile('output.svg', svgBytes) ``` -------------------------------- ### Trim SVG Path Source: https://github.com/brooooooklyn/canvas/blob/main/__test__/pathkit.spec.ts.md This example shows how to trim an SVG path. It takes an SVG path string as input and returns a modified SVG path string. ```svg ``` -------------------------------- ### Encode Lottie Animation to Video Frame Source: https://github.com/brooooooklyn/canvas/blob/main/README.md This example demonstrates how to render each frame of a Lottie animation to a canvas and prepare it for video encoding using @napi-rs/webcodecs. It includes setting a background color and rendering the animation frame. ```javascript import { createCanvas, LottieAnimation } from '@napi-rs/canvas' import { VideoEncoder, VideoFrame, Mp4Muxer, type EncodedVideoChunk, type EncodedVideoChunkMetadata, } from '@napi-rs/webcodecs' const animation = LottieAnimation.loadFromFile('animation.json') const canvas = createCanvas(animation.width, animation.height) const ctx = canvas.getContext('2d') for (let frame = 0; frame < animation.frames; frame++) { animation.seekFrame(frame) ctx.fillStyle = '#ffffff' ctx.fillRect(0, 0, canvas.width, canvas.height) animation.render(ctx) // Encode frame to video... } ``` -------------------------------- ### Drawing Emoji and Custom Fonts Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Shows how to register custom fonts, including emoji fonts, and use them to render text on the canvas. Ensure the font files are correctly located and accessible. This example uses 'Apple Emoji' and 'COLRv1' fonts. ```javascript const { writeFileSync } = require('fs') const { join } = require('path') const { createCanvas, GlobalFonts } = require('@napi-rs/canvas') GlobalFonts.registerFromPath(join(__dirname, '..', 'fonts', 'AppleColorEmoji@2x.ttf'), 'Apple Emoji') GlobalFonts.registerFromPath(join(__dirname, '..', '__test__', 'fonts', 'COLRv1.ttf'), 'COLRv1') console.info(GlobalFonts.families) const canvas = createCanvas(760, 360) const ctx = canvas.getContext('2d') ctx.font = '50px Apple Emoji' ctx.strokeText('πŸ˜€πŸ˜ƒπŸ˜„πŸ˜πŸ˜†πŸ˜…πŸ˜‚πŸ€£β˜ΊοΈπŸ˜ŠπŸ˜ŠπŸ˜‡', 50, 150) ctx.font = '100px COLRv1' ctx.fillText('abc', 50, 300) const b = canvas.toBuffer('image/png') writeFileSync(join(__dirname, 'draw-emoji.png'), b) ``` -------------------------------- ### Local Build: Rebuild Skia using Docker for Linux Source: https://github.com/brooooooklyn/canvas/blob/main/CONTRIBUTING.md Demonstrates how to use a Docker container to reproduce the CI environment for building Skia on Linux, targeting a specific architecture. ```bash docker pull ghcr.io/brooooooklyn/canvas/ubuntu-builder:jammy docker run --rm -v $(pwd):/canvas -w /canvas ghcr.io/brooooooklyn/canvas/ubuntu-builder:jammy \ node scripts/build-skia.js --target=x86_64-unknown-linux-gnu ``` -------------------------------- ### Path2D Constructors Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Demonstrates the different ways to instantiate a Path2D object, including with no arguments, another Path2D object, or a string representing a path. ```APIDOC ## Path2D Constructors ### Description Instantiates a new Path2D object. ### Usage ```typescript new Path2D() new Path2D(path: Path2D) new Path2D(path: string) ``` ``` -------------------------------- ### Benchmark Results: Draw a House and export to PNG Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Compares the performance of '@napi-rs/skia', 'skia-canvas', and 'node-canvas' for drawing a house and exporting to PNG. Shows average and median latency and throughput. ```text ❯ yarn bench Draw a House and export to PNG β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ (index) β”‚ Task name β”‚ Latency average (ns) β”‚ Latency median (ns) β”‚ Throughput average (ops/s) β”‚ Throughput median (ops/s) β”‚ Samples β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ 0 β”‚ '@napi-rs/skia' β”‚ '14676992.14 Β± 0.68%' β”‚ '14602333.00' β”‚ '68 Β± 0.59%' β”‚ '68' β”‚ 69 β”‚ β”‚ 1 β”‚ 'skia-canvas' β”‚ '21167809.17 Β± 2.05%' β”‚ '20960021.00 Β± 13646.00' β”‚ '47 Β± 1.31%' β”‚ '48' β”‚ 64 β”‚ β”‚ 2 β”‚ 'node-canvas' β”‚ '16552027.42 Β± 0.70%' β”‚ '16451291.50 Β± 2208.50' β”‚ '60 Β± 0.62%' β”‚ '61' β”‚ 64 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -------------------------------- ### Benchmark Results: Draw Gradient and export to PNG Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Compares the performance of '@napi-rs/skia', 'skia-canvas', and 'node-canvas' for drawing a gradient and exporting to PNG. Shows average and median latency and throughput. ```text Draw Gradient and export to PNG β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ (index) β”‚ Task name β”‚ Latency average (ns) β”‚ Latency median (ns) β”‚ Throughput average (ops/s) β”‚ Throughput median (ops/s) β”‚ Samples β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ 0 β”‚ '@napi-rs/skia' β”‚ '15228495.58 Β± 0.53%' β”‚ '15146312.50 Β± 1187.50' β”‚ '66 Β± 0.48%' β”‚ '66' β”‚ 66 β”‚ β”‚ 1 β”‚ 'skia-canvas' β”‚ '21725564.41 Β± 2.20%' β”‚ '21412520.50 Β± 2104.50' β”‚ '46 Β± 1.39%' β”‚ '47' β”‚ 64 β”‚ β”‚ 2 β”‚ 'node-canvas' β”‚ '17976022.14 Β± 1.53%' β”‚ '17563479.50 Β± 5104.50' β”‚ '56 Β± 1.38%' β”‚ '57' β”‚ 64 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` -------------------------------- ### Basic Canvas Drawing and Image Export Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Demonstrates creating a canvas, drawing basic shapes (rectangles, paths), loading images from disk and URL, drawing images onto the canvas, and exporting the canvas content as a PNG file. Ensure images are accessible and the output path is writable. ```javascript const { promises } = require('node:fs') const { join } = require('node:path') const { createCanvas, loadImage } = require('@napi-rs/canvas') const canvas = createCanvas(300, 320) const ctx = canvas.getContext('2d') ctx.lineWidth = 10 ctx.strokeStyle = '#03a9f4' ctx.fillStyle = '#03a9f4' // Wall ctx.strokeRect(75, 140, 150, 110) // Door ctx.fillRect(130, 190, 40, 60) // Roof ctx.beginPath() ctx.moveTo(50, 140) ctx.lineTo(150, 60) ctx.lineTo(250, 140) ctx.closePath() ctx.stroke() async function main() { // load images from disk or from a URL const catImage = await loadImage('path/to/cat.png') const dogImage = await loadImage('https://example.com/path/to/dog.jpg') ctx.drawImage(catImage, 0, 0, catImage.width, catImage.height) ctx.drawImage(dogImage, canvas.width / 2, canvas.height / 2, dogImage.width, dogImage.height) // export canvas as image const pngData = await canvas.encode('png') // JPEG, AVIF and WebP are also supported // encoding in libuv thread pool, non-blocking await promises.writeFile(join(__dirname, 'simple.png'), pngData) } main() ``` -------------------------------- ### Load Lottie Animation with @napi-rs/canvas Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Load Lottie animations from a file or JSON string using `LottieAnimation.loadFromFile` or `LottieAnimation.loadFromData`. The latter supports external assets via the `resourcePath` option. ```javascript const { LottieAnimation } = require('@napi-rs/canvas') // Load from file const animation = LottieAnimation.loadFromFile('animation.json') // Load from JSON string with resource path for external assets const animation = LottieAnimation.loadFromData(jsonString, { resourcePath: '/path/to/assets', }) ``` -------------------------------- ### Path2D Constructor Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Demonstrates the different ways to construct a Path2D object, including from another Path2D object or a string representation. ```typescript new Path2D() new Path2D(path: Path2D) // new Path2D('M108.956,403.826c0,0,0.178,3.344-1.276,3.311 c-1.455-0.033-30.507-84.917-66.752-80.957C40.928,326.18,72.326,313.197,108.956,403.826z') new Path2D(path: string) ``` -------------------------------- ### Path2D Utility Methods Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Lists utility methods for Path2D objects, including stroke, transform, bounds calculation, and equality checks. ```APIDOC ## Path2D Utility Methods ### Description Utility methods for Path2D objects. ### Methods - `stroke(stroke?: StrokeOptions): Path2D` - `transform(transform: DOMMatrix2DInit): Path2D` - `getBounds(): [left: number, top: number, right: number, bottom: number]` - `computeTightBounds(): [left: number, top: number, right: number, bottom: number]` - `trim(start: number, end: number, isComplement?: boolean): Path2D` - `round(radius: number): Path2D` - `equals(path: Path2D): boolean` ``` -------------------------------- ### Encode Canvas to Various Image Formats Source: https://context7.com/brooooooklyn/canvas/llms.txt Shows how to encode a canvas to PNG, JPEG, WebP, AVIF, and GIF using both asynchronous and synchronous methods. Also demonstrates converting to a data URL, Blob, and raw pixel buffer. ```typescript import { createCanvas, ChromaSubsampling } from '@napi-rs/canvas' const canvas = createCanvas(512, 512) const ctx = canvas.getContext('2d') const grad = ctx.createRadialGradient(256, 256, 0, 256, 256, 256) grad.addColorStop(0, 'gold') grad.addColorStop(1, 'crimson') ctx.fillStyle = grad ctx.fillRect(0, 0, 512, 512) // PNG β€” async const png = await canvas.encode('png') // JPEG β€” async with quality const jpeg = await canvas.encode('jpeg', 85) // WebP β€” synchronous const webp = canvas.encodeSync('webp', 75) // AVIF β€” with detailed config const avif = await canvas.encode('avif', { quality: 80, // 0-100, 100 = lossless alphaQuality: 100, speed: 6, // 1 (slow) - 10 (fast) threads: 4, chromaSubsampling: ChromaSubsampling.Yuv420, }) // GIF β€” single frame const gif = await canvas.encode('gif', 10) // quality 1-30 // data URL const dataUrl = canvas.toDataURL('image/png') const jpegDataUrl = canvas.toDataURL('image/jpeg', 90) // Blob (for Web API compatibility) const blob = await canvas.convertToBlob({ mime: 'image/webp', quality: 80 }) // Raw RGBA pixel buffer const rawPixels = canvas.data() // Buffer of width*height*4 bytes ``` -------------------------------- ### Node-canvas Compatibility Layer Migration Source: https://context7.com/brooooooklyn/canvas/llms.txt Shows how to import compatibility exports from `@napi-rs/canvas/node-canvas` to migrate existing `node-canvas` codebases with minimal changes. Includes font registration, stream creation, and `createImageData` usage. ```typescript // Replace: import { createCanvas } from 'canvas' // With: import { createCanvas, createImageData, registerFont, deregisterAllFonts, loadImage, Canvas, Image, ImageData, PNGStream, JPEGStream, CanvasRenderingContext2D, Context2d, // alias for CanvasRenderingContext2D DOMPoint, DOMMatrix, GlobalFonts, } from '@napi-rs/canvas/node-canvas' // Works exactly like node-canvas registerFont('./fonts/OpenSans.ttf', { family: 'Open Sans' }) const canvas = createCanvas(400, 200) const ctx = canvas.getContext('2d') ctx.font = '30px Open Sans' ctx.fillText('Migration complete!', 20, 50) // node-canvas stream API const pngStream: PNGStream = canvas.createPNGStream() const jpegStream: JPEGStream = canvas.createJPEGStream({ quality: 0.9 }) // createImageData (node-canvas variant) const imgData = createImageData(new Uint8ClampedArray(4 * 4 * 4).fill(255), 4, 4) // Cleanup deregisterAllFonts() ``` -------------------------------- ### Working with ImageData in @napi-rs/canvas Source: https://context7.com/brooooooklyn/canvas/llms.txt Demonstrates creating, manipulating, and reading raw pixel data using the ImageData class. Supports various typed array sources and allows for pixel manipulation before putting back onto a canvas. ```typescript import { ImageData, createCanvas } from '@napi-rs/canvas' // Create blank ImageData const blank = new ImageData(100, 100) // blank.data is Uint8ClampedArray of 100*100*4 = 40,000 zeros // Create from typed array β€” 4Γ—4 fully opaque green pixels const data = new Uint8ClampedArray(4 * 4 * 4) for (let i = 0; i < 4 * 4; i++) { data[i * 4 + 0] = 0 // R data[i * 4 + 1] = 200 // G data[i * 4 + 2] = 50 // B data[i * 4 + 3] = 255 // A } const greenTile = new ImageData(data, 4, 4) console.log(greenTile.width, greenTile.height) // 4 4 // Read pixels from a canvas const canvas = createCanvas(200, 200) const ctx = canvas.getContext('2d') ctx.fillStyle = 'red' ctx.fillRect(0, 0, 100, 100) const pixels = ctx.getImageData(0, 0, 200, 200) console.log(pixels.data[0], pixels.data[1], pixels.data[2], pixels.data[3]) // 255 0 0 255 (red) // Manipulate and put back β€” invert colors for (let i = 0; i < pixels.data.length; i += 4) { pixels.data[i] = 255 - pixels.data[i] // R pixels.data[i + 1] = 255 - pixels.data[i + 1] // G pixels.data[i + 2] = 255 - pixels.data[i + 2] // B // leave Alpha unchanged } ctx.putImageData(pixels, 0, 0) // create from an existing ImageData (copies the buffer) const copy = new ImageData(pixels) ``` -------------------------------- ### Path2D Methods Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Provides a reference for the various methods available on the Path2D object for drawing and manipulating paths. ```APIDOC ## Path2D Methods ### Description Methods for drawing and manipulating paths. ### Methods - `addPath(path: Path2D, transform?: DOMMatrix2DInit): void` - `arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void` - `arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void` - `bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void` - `closePath(): void` - `ellipse( x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number, endAngle: number, anticlockwise?: boolean, ): void` - `lineTo(x: number, y: number): void` - `moveTo(x: number, y: number): void` - `quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void` - `rect(x: number, y: number, w: number, h: number): void` ``` -------------------------------- ### Render Lottie Animations with LottieAnimation Source: https://context7.com/brooooooklyn/canvas/llms.txt Loads and renders Lottie JSON animations using Skia's Skottie module. Supports embedded images, fonts, external assets, and `.lottie` ZIP format. Animations can be sought by frame, time, or normalized position. ```typescript import { LottieAnimation, createCanvas } from '@napi-rs/canvas' import { promises as fs } from 'node:fs' // Load from file path (assets resolved relative to JSON directory) const animation = LottieAnimation.loadFromFile('./animations/confetti.json') console.log(`Duration: ${animation.duration}s`) console.log(`FPS: ${animation.fps}, Total frames: ${animation.frames}`) console.log(`Size: ${animation.width}x${animation.height}`) console.log(`Version: ${animation.version}`) // Create canvas matching animation dimensions const canvas = createCanvas(animation.width, animation.height) const ctx = canvas.getContext('2d') // Render specific frame and export to PNG animation.seekFrame(0) animation.render(ctx) await fs.writeFile('frame0.png', await canvas.encode('png')) // Render scaled to a custom rect animation.seekFrame(10) animation.render(ctx, { x: 0, y: 0, width: 800, height: 600 }) // Seek by time animation.seekTime(1.5) // 1.5 seconds into the animation animation.render(ctx) // Seek by normalized position (0.0 = start, 1.0 = end) animation.seek(0.5) // middle of animation animation.render(ctx) // Export every frame as PNG strip const strip = createCanvas(animation.width * animation.frames, animation.height) const stripCtx = strip.getContext('2d') for (let i = 0; i < animation.frames; i++) { animation.seekFrame(i) animation.render(stripCtx, { x: i * animation.width, y: 0, width: animation.width, height: animation.height }) } await fs.writeFile('sprite-strip.png', await strip.encode('png')) // Load from JSON data with external resource path const jsonData = await fs.readFile('./animations/hero.json', 'utf8') const anim2 = LottieAnimation.loadFromData(jsonData, { resourcePath: './animations/assets' }) ``` -------------------------------- ### Hardware Information Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Displays system hardware and software details relevant to performance testing. This information is useful for understanding the environment in which benchmarks were run. ```text ,MMMM. Host - xxxxxxxxxxxxxxxxxxxxxxx .MMMMMM Machine - Mac15,9 MMMMM, Kernel - 24.0.0 .;MMMMM:' MMMMMMMMMM;. OS - macOS 15.0.1 Sequoia MMMMMMMMMMMMNWMMMMMMMMMMM: DE - Aqua .MMMMMMMMMMMMMMMMMMMMMMMMWM. WM - Quartz Compositor MMMMMMMMMMMMMMMMMMMMMMMMM. Packages - 194 (Homebrew), 32 (cargo) ;MMMMMMMMMMMMMMMMMMMMMMMM: Shell - zsh :MMMMMMMMMMMMMMMMMMMMMMMM: Terminal - warpterminal (Version v0.2024.10.23.14.49.stable_00) .MMMMMMMMMMMMMMMMMMMMMMMMM. Resolution - 5120x2880@160fps (as 2560x1440) MMMMMMMMMMMMMMMMMMMMMMMMMMM. 2992x1934@120fps (as 1496x967) .MMMMMMMMMMMMMMMMMMMMMMMMMM. 2232x1512@60fps (as 1116x756) MMMMMMMMMMMMMMMMMMMMMMMM Uptime - 1d 2h 32m ;MMMMMMMMMMMMMMMMMMMM. CPU - Apple M3 Max (16) .MMMM,. .MMMM,. CPU Load - 16% Memory - 50.1 GB / 134.2 GB Battery - 78% & Discharging Disk Space - 624.0 GB / 994.7 GB ``` -------------------------------- ### Measure Text Metrics Source: https://github.com/brooooooklyn/canvas/blob/main/index.html Measures text metrics for a given string and font, and logs them to the console. Also demonstrates rendering text with different baselines and measuring their metrics. ```javascript setTimeout(() => { const canvas = document.getElementById('canvas') /** * @type {CanvasRenderingContext2D} */ const ctx = canvas.getContext('2d') ctx.fillStyle = 'yellow' ctx.fillRect(0, 0, canvas.width, canvas.height) ctx.strokeStyle = 'black' ctx.lineWidth = 3 ctx.font = '50px Iosevka Slab' const metrics = ctx.measureText('@napi-rs/canvas') console.info(metrics) const baselines = ['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'] baselines.forEach(function (baseline) { ctx.textBaseline = baseline const metrics = ctx.measureText('@napi-rs/canvas') console.info(baseline, metrics) }) ctx.strokeText('skr canvas', 50, 150) ctx.strokeText('@napi-rs/canvas', 50, 300) }, 300) ``` -------------------------------- ### createCanvas Source: https://context7.com/brooooooklyn/canvas/llms.txt Creates a new Canvas instance backed by Skia. Supports both raster and SVG canvas creation. ```APIDOC ## createCanvas Creates a new `Canvas` instance backed by Skia. Passing a `SvgExportFlag` returns an `SvgCanvas` whose `getContent()` returns raw SVG bytes instead of a bitmap. ### Usage ```typescript import { createCanvas, SvgExportFlag } from '@napi-rs/canvas' // Raster canvas const canvas = createCanvas(800, 600) const ctx = canvas.getContext('2d') // ... drawing operations ... // Async encode (runs in libuv thread pool, non-blocking) const png = await canvas.encode('png') // Synchronous encode (blocks event loop) const jpeg = canvas.encodeSync('jpeg', 90) // SVG canvas β€” vector output const svgCanvas = createCanvas(400, 300, SvgExportFlag.NoPrettyXML) const svgCtx = svgCanvas.getContext('2d') svgCtx.fillStyle = 'tomato' svgCtx.fillRect(10, 10, 100, 100) const svgBytes = svgCanvas.getContent() // Buffer containing SVG XML ``` ``` -------------------------------- ### Manual Image Loading with `Image` Class Source: https://context7.com/brooooooklyn/canvas/llms.txt The `Image` class provides a browser-style interface with `onload` and `onerror` callbacks, and a `decode()` promise. It supports various sources including Buffers, file paths, data URLs, HTTP URLs, and SVG buffers. Use `decode()` to wait for the bitmap to be fully decoded. ```typescript import { Image, createCanvas } from '@napi-rs/canvas' import { promises as fs } from 'node:fs' // Load with Promise-based API using decode() const image = new Image() image.src = await fs.readFile('./photo.jpg') await image.decode() // waits until bitmap is fully decoded console.log(image.naturalWidth, image.naturalHeight) // actual dimensions // Draw immediately after decode const canvas = createCanvas(image.naturalWidth, image.naturalHeight) const ctx = canvas.getContext('2d') ctx.drawImage(image, 0, 0) // Load via callback const img2 = new Image() img2.onload = () => { console.log(`Loaded: ${img2.currentSrc} ${img2.width}x${img2.height}`) } img2.onerror = (err) => { console.error('Failed:', err.message) } img2.src = 'https://example.com/image.png' // URL string triggers async fetch // SVG with explicit viewport const svgImg = new Image(200, 150) // width/height sets SVG viewport svgImg.src = Buffer.from('') // SVG loads synchronously β€” onload fires synchronously console.log(svgImg.complete) // true ``` -------------------------------- ### Load Image from Various Sources with `loadImage` Source: https://context7.com/brooooooklyn/canvas/llms.txt Use `loadImage` to load images from file paths, URLs, Buffers, ArrayBuffers, data URLs, Uint8Arrays, or Node.js Readable streams. EXIF orientation is automatically applied. Options like `maxRedirects` and `alt` text can be provided. ```typescript import { createCanvas, loadImage } from '@napi-rs/canvas' import { createReadStream } from 'node:fs' import { promises as fs } from 'node:fs' const canvas = createCanvas(800, 600) const ctx = canvas.getContext('2d') // From file path const localImg = await loadImage('./photo.png') ctx.drawImage(localImg, 0, 0) // From HTTPS URL (follows redirects) const remoteImg = await loadImage('https://example.com/banner.jpg', { maxRedirects: 3, }) ctx.drawImage(remoteImg, 0, 0, 400, 300) // From Buffer const buf = await fs.readFile('./sprite.png') const fromBuf = await loadImage(buf) // From stream const fromStream = await loadImage(createReadStream('./texture.jpg')) // From data URL const fromDataUrl = await loadImage( 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=', ) // With alt text const imgWithAlt = await loadImage('./logo.svg', { alt: 'Company logo' }) console.log(imgWithAlt.alt) // 'Company logo' ``` -------------------------------- ### Apply Image Filters (Contrast and Brightness) Source: https://github.com/brooooooklyn/canvas/blob/main/index.html Draws an image onto a canvas and applies contrast and brightness filters. Requires a canvas element with id 'regression' and an image element with id 'firefox'. ```javascript setTimeout(() => { const canvas = document.getElementById('regression') const ctx = canvas.getContext('2d') ctx.filter = 'contrast(175%) brightness(103%)' const image = document.querySelector('#firefox') ctx.drawImage(image, 0, 0) }, 1000) ``` -------------------------------- ### `LottieAnimation` β€” Lottie animation rendering Source: https://context7.com/brooooooklyn/canvas/llms.txt Loads and renders Lottie JSON animations using Skia's Skottie module. Supports embedded images, fonts, external asset directories, and the `.lottie` ZIP format. ```APIDOC ## `LottieAnimation` β€” Lottie animation rendering Loads and renders Lottie JSON animations using Skia's Skottie module. Supports embedded images (base64), embedded fonts, external asset directories, and the `.lottie` ZIP format. ```typescript import { LottieAnimation, createCanvas } from '@napi-rs/canvas' import { promises as fs } from 'node:fs' // Load from file path (assets resolved relative to JSON directory) const animation = LottieAnimation.loadFromFile('./animations/confetti.json') console.log(`Duration: ${animation.duration}s`) console.log(`FPS: ${animation.fps}, Total frames: ${animation.frames}`) console.log(`Size: ${animation.width}x${animation.height}`) console.log(`Version: ${animation.version}`) // Create canvas matching animation dimensions const canvas = createCanvas(animation.width, animation.height) const ctx = canvas.getContext('2d') // Render specific frame and export to PNG animation.seekFrame(0) animation.render(ctx) await fs.writeFile('frame0.png', await canvas.encode('png')) // Render scaled to a custom rect animation.seekFrame(10) animation.render(ctx, { x: 0, y: 0, width: 800, height: 600 }) // Seek by time animation.seekTime(1.5) // 1.5 seconds into the animation animation.render(ctx) // Seek by normalized position (0.0 = start, 1.0 = end) animation.seek(0.5) // middle of animation animation.render(ctx) // Export every frame as PNG strip const strip = createCanvas(animation.width * animation.frames, animation.height) const stripCtx = strip.getContext('2d') for (let i = 0; i < animation.frames; i++) { animation.seekFrame(i) animation.render(stripCtx, { x: i * animation.width, y: 0, width: animation.width, height: animation.height }) } await fs.writeFile('sprite-strip.png', await strip.encode('png')) // Load from JSON data with external resource path const jsonData = await fs.readFile('./animations/hero.json', 'utf8') const anim2 = LottieAnimation.loadFromData(jsonData, { resourcePath: './animations/assets' }) ``` ``` -------------------------------- ### FillType Conversion Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Explains how to convert the fill type of a path, specifically from 'evenodd' to 'nonzero', which is useful for SVG and OpenType font compatibility. ```APIDOC ## FillType Conversion ### Description Converts the fill type of a path. ### Method `.asWinding(): Path2D` ### Usage This method is useful for converting `fill-rule="evenodd"` to `fill-rule="nonzero"` in SVG, as `nonzero` is the only fill rule supported by OpenType fonts. ### Example ```js const pathCircle = new Path2D( 'M24.2979 13.6364H129.394V40.9091H24.2979L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455H70.4673V68.1818H16.073C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727V113.636C7.00728 118.657 11.0661 122.727 16.073 122.727H70.4673V150H84.0658V122.727H128.041C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818H84.0658V54.5455H133.927C138.934 54.5455 142.993 50.4755 142.993 45.4545V9.09091C142.993 4.07014 138.934 0 133.927 0H21.9592ZM125.702 109.091H20.6058V81.8182H125.702L135.372 95.4545L125.702 109.091Z', ) pathCircle.setFillType(FillType.EvenOdd) pathCircle.asWinding().toSVGString() // => "M24.2979 13.6364L129.394 13.6364L129.394 40.9091L24.2979 40.9091L14.6278 27.2727L24.2979 13.6364ZM21.9592 0C19.0246 0 16.2716 1.42436 14.571 3.82251L1.67756 22.0043C-0.559186 25.1585 -0.559186 29.387 1.67756 32.5411L14.571 50.7227C16.2716 53.1209 19.0246 54.5455 21.9592 54.5455L70.4673 54.5455L70.4673 68.1818L16.073 68.1818C11.0661 68.1818 7.00728 72.2518 7.00728 77.2727L7.00728 113.636C7.00728 118.657 11.0661 122.727 16.073 122.727L70.4673 122.727L70.4673 150L84.0658 150L84.0658 122.727L128.041 122.727C130.975 122.727 133.729 121.303 135.429 118.905L148.323 100.723C150.559 97.5686 150.559 93.3405 148.323 90.1864L135.429 72.0045C133.729 69.6064 130.975 68.1818 128.041 68.1818L84.0658 68.1818L84.0658 54.5455L133.927 54.5455C138.934 54.5455 142.993 50.4755 142.993 45.4545L142.993 9.09091C142.993 4.07014 138.934 0 133.927 0L21.9592 0ZM125.702 109.091L20.6058 109.091L20.6058 81.8182L125.702 81.8182L135.372 95.4545L125.702 109.091Z" ``` ``` -------------------------------- ### Render Lottie Animation to Canvas Source: https://github.com/brooooooklyn/canvas/blob/main/README.md Render a loaded Lottie animation onto an @napi-rs/canvas. The animation can be rendered at its original size or scaled to fit a custom destination rectangle. ```javascript const { createCanvas, LottieAnimation } = require('@napi-rs/canvas') const animation = LottieAnimation.loadFromFile('animation.json') const canvas = createCanvas(animation.width, animation.height) const ctx = canvas.getContext('2d') // Render at original size animation.render(ctx) // Render with custom destination rect animation.render(ctx, { x: 0, y: 0, width: 800, height: 600 }) ```