### Approve Skia Canvas Builds with pnpm Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md Manages the installation of skia-canvas with pnpm, which requires explicit approval for native builds. Includes interactive and non-interactive methods, as well as configuration via `package.json`. ```bash pnpm install skia-canvas pnpm approve-builds ``` ```bash pnpm install skia-canvas --allow-build=skia-canvas ``` ```json { "pnpm": { "onlyBuiltDependencies": ["skia-canvas"] } } ``` -------------------------------- ### Install Skia Canvas with npm Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md Installs the skia-canvas package using npm. This command downloads a pre-compiled native library from the project's latest release, simplifying setup on supported platforms. ```bash npm install skia-canvas ``` -------------------------------- ### Basic Dockerfile for Skia Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md Sets up a Docker environment for Skia Canvas using a Node.js base image. Supports both standard Debian-derived images and Alpine Linux. ```dockerfile FROM node ``` ```dockerfile FROM node:alpine ``` -------------------------------- ### Demonstrate Text Baselines with Skia-Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md This example visualizes different text baselines ('top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom') by rendering text at each baseline and drawing corresponding indicator lines. It uses `ctx.measureText` to get metrics like `actualBoundingBoxLeft`, `actualBoundingBoxAscent`, `actualBoundingBoxRight`, `actualBoundingBoxDescent`, and specific baseline offsets. The output is saved to 'out.png'. ```javascript const BASELINES = ["top", "hanging", "middle", "alphabetic", "ideographic", "bottom"] let [x, y] = [20, 60] // set the location of the first column let spacing = 25 // space between columns let padding = 7 // amount to outdent the gray lines for hanging & alphabetical ctx.font = 'italic 30px serif' ctx.textWrap = true ctx.translate(x, y) for (const baseline of BASELINES){ ctx.textBaseline = baseline let msg = baseline // single-line example // uncomment to try adding an additional line of text: // msg = baseline + "\nbaseline" let m = ctx.measureText(msg) // use the `actualBoundingBox` to draw a white rectangle behind the text run let left = -m.actualBoundingBoxLeft let top = -m.actualBoundingBoxAscent let right = m.actualBoundingBoxRight let bottom = m.actualBoundingBoxDescent ctx.fillStyle = 'white' ctx.fillRect(left, top, right - left, bottom - top) for (const line of m.lines){ // draw selected `textBaseline` in red ctx.beginPath() ctx.moveTo(left, line.baseline) ctx.lineTo(right, line.baseline) ctx.strokeStyle = 'red' ctx.stroke() // draw alphabetic baseline as a solid gray line ctx.beginPath() ctx.moveTo(left-padding, line.baseline - m.alphabeticBaseline) ctx.lineTo(right+padding, line.baseline - m.alphabeticBaseline) ctx.strokeStyle = 'rgba(0,0,0, 0.3)' ctx.stroke() // draw hanging baseline as a dotted line ctx.beginPath() ctx.moveTo(left-padding, line.baseline - m.hangingBaseline) ctx.lineTo(right+padding, line.baseline - m.hangingBaseline) ctx.setLineDash([1]) ctx.stroke() ctx.setLineDash([]) // NB: you can also use the m.ideographicBaseline offset } // typeset the text itself ctx.fillStyle = 'black' ctx.fillText(msg, 0, 0) // move to next column ctx.translate(right - left + spacing, 0) } await canvas.toFile('out.png', {density:2, matte:'#d9d9d9'}) } baselinesDemo() ``` -------------------------------- ### Measure Text Metrics and Draw Bounds with Skia-Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md This example demonstrates how to use the `measureText` method in skia-canvas to obtain detailed metrics about rendered text, including bounding boxes and line information. It then uses these metrics to draw visual guides and bounds around the text, illustrating concepts like actual bounding box, font bounding box, and different text baselines. The code also handles text wrapping and saves the output to a file. ```javascript import {Canvas} from 'skia-canvas' const canvas = new Canvas(750, 300), ctx = canvas.getContext("2d") async function metricsDemo(){ // try customizing these and seeing what does (or doesn't) change in the graphic let msg = "Twas brillig and the slithy toves did gyre and gimble in the wabe" let [x, y] = [40, 75] // set the location of the text let maxWidth = 680 // set the point at which the text will wrap ctx.font = 'italic 64px/1.4 serif' // try adjusting lineHeight and note dotted lines ctx.textBaseline = 'alphabetic' // the red line will correspond to this selection ctx.textAlign = 'left' ctx.textWrap = true // print the font metrics to the console let m = ctx.measureText(msg, maxWidth) console.log(m) // set the origin point for the drawing the text to screen ctx.translate(x, y) // use the `actualBoundingBox` to draw a white rectangle behind the entire multi-line run let left = -m.actualBoundingBoxLeft let top = -m.actualBoundingBoxAscent let right = m.actualBoundingBoxRight let bottom = m.actualBoundingBoxDescent ctx.fillStyle = 'white' ctx.fillRect(left, top, right - left, bottom - top) // draw a red circle at the origin point (i.e., the position passed to fillText) ctx.fillStyle = 'red' ctx.beginPath() ctx.ellipse(0, 0, 2.5, 2.5, 0, 0, 2*Math.PI) ctx.fill() // step through each object in the `lines` array for (const [i, line] of m.lines.entries()) { // print out the substring that's typeset on this line console.log(`line ${i+1}: "${msg.slice(line.startIndex, line.endIndex)}"`) // use the line's rect dimensions to enclose just the area occupied by glyphs ctx.fillStyle = 'rgba(0,204,255, 0.3)' ctx.fillRect(line.x, line.y, line.width, line.height) // draw the baseline chosen as `textBaseline` as a red line ctx.fillStyle = 'red' ctx.strokeStyle = 'red' ctx.beginPath() ctx.moveTo(left, line.baseline) ctx.lineTo(right, line.baseline) ctx.stroke() // draw dotted lines around the 1em tall text block (i.e., the non-leading portions of the lineHeight) let textTop = line.baseline + m.alphabeticBaseline - m.fontBoundingBoxAscent let textBottom = line.baseline + m.alphabeticBaseline + m.fontBoundingBoxDescent ctx.beginPath() ctx.setLineDash([2]) ctx.strokeStyle = 'rgba(0,50,50, 0.4)' ctx.moveTo(left, textTop) ctx.lineTo(right, textTop) ctx.moveTo(left, textBottom) ctx.lineTo(right, textBottom) ctx.stroke() ctx.setLineDash([]) } // typeset the text itself ctx.fillStyle = 'black' ctx.fillText(msg, 0, 0, maxWidth) await canvas.toFile('out.png', {density:2, matte:'#d9d9d9'}) } metricsDemo() ``` -------------------------------- ### Setup UI with Cookies and URL Parameters (JavaScript) Source: https://github.com/samizdatco/skia-canvas/blob/main/tests/visual/index.html Initializes the user interface by checking for saved rendering options in cookies and applying them. It also parses URL query parameters to override default settings. Finally, it sets the background color based on options and populates the form. ```javascript function setupUi() { // check cookie for runtime options let renderOptions = getCookie('renderOptions'); if (renderOptions) { try { renderOptions = JSON.parse(renderOptions); Object.assign(opt, renderOptions); } catch (e) { console.log("Options cookie parse error:", e); } } // some options are set only via URL params const params = new URL(location.href).searchParams; opt.threshold = parseFloat(params.get('threshold')) || opt.threshold; opt.diffMask = params.has('diffMask'); document.getElementsByTagName('main')[0].style.backgroundColor = !opt.bc_default && opt.bc ? opt.bc : 'unset'; populateForm(); } ``` -------------------------------- ### Measure Text Lines and Runs with Skia-Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md This example demonstrates how to measure multi-line text and individual text runs within those lines using `ctx.measureText`. It renders text with wrapping and then draws bounding boxes and baseline indicators for both the entire line and each font run within the line. The output is saved to 'out.png'. ```javascript import {Canvas} from 'skia-canvas' const canvas = new Canvas(750, 340), ctx = canvas.getContext("2d") async function lineMetricsDemo(){ let msg = "ABC ДЖЯ xyz ឦ ючл 地水炎空" // text with many glyphs not present in Avenir… let [x, y] = [75, 75] // set the location of the text let maxWidth = 315 // set the point at which the text will wrap ctx.font = '56px/2 Avenir' ctx.textWrap = true // set the origin point for the drawing the text to screen ctx.translate(x, y) // obtain the font metrics we'll be using to draw boxes let m = ctx.measureText(msg, maxWidth) for (const column of ['left', 'right']){ // use the `actualBoundingBox` to draw a white rectangle behind the entire multi-line run let left = -m.actualBoundingBoxLeft let top = -m.actualBoundingBoxAscent let right = m.actualBoundingBoxRight let bottom = m.actualBoundingBoxDescent ctx.fillStyle = 'white' ctx.fillRect(left, top, right - left, bottom - top) // draw a red circle at the origin point (i.e., the position passed to fillText) ctx.fillStyle = 'red' ctx.beginPath() ctx.ellipse(0, 0, 2.5, 2.5, 0, 0, 2*Math.PI) ctx.fill() for (const line of m.lines){ // on the left, draw green boxes around the line bounds and stroke the line's ascent & descent if (column=='left'){ ctx.fillStyle = '#00dc0033' ctx.fillRect(line.x, line.y, line.width, line.height) ctx.strokeStyle = '#007700' ctx.beginPath() ctx.moveTo(0, line.ascent) ctx.lineTo(m.width, line.ascent) ctx.moveTo(0, line.descent) ctx.lineTo(m.width, line.descent) ctx.stroke() } // on the right, draw blue boxes around each single-font run's bounds and stroke the // ascent & descent lines specific to that font run if (column=='right'){ for (const run of line.runs){ ctx.fillStyle = '#09f3' ctx.fillRect(run.x, run.y, run.width, run.height) ctx.strokeStyle = '#07c' ctx.beginPath() ctx.moveTo(run.x, run.ascent) ctx.lineTo(run.x + run.width, run.ascent) ctx.moveTo(run.x, run.descent) ctx.lineTo(run.x + run.width, run.descent) ctx.stroke() } } } // draw the text on top ctx.fillStyle = 'black' ctx.fillText(msg, 0, 0, maxWidth) // shift rightward for next column ctx.translate(maxWidth + 5, 0) } await canvas.toFile('out.png', {density:2, matte:'#d9d9d9'}) } lineMetricsDemo() ``` -------------------------------- ### Create Skia Canvas AWS Lambda Layer using AWS CLI Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md Automates the creation of an AWS Lambda layer for Skia Canvas using the AWS CLI. This script fetches a specific version of the library and publishes it as a layer, compatible with specified Node.js runtimes and architectures. ```bash #!/usr/bin/env bash VERSION=3.0.8 # the skia-canvas version to include PLATFORM=arm64 # arm64 or x64 curl -sLO https://github.com/samizdatco/skia-canvas/releases/download/v${VERSION}/aws-lambda-${PLATFORM}.zip aws lambda publish-layer-version \ --layer-name "skia-canvas" \ --description "Skia Canvas ${VERSION} layer" \ --zip-file "fileb://aws-lambda-${PLATFORM}.zip" \ --compatible-runtimes "nodejs20.x" "nodejs22.x" \ --compatible-architectures "${X/#x/x86_}" ``` -------------------------------- ### ImageData Constructor Options in JavaScript Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/imagedata.md Provides examples of the various ways to construct an ImageData object, including creating empty buffers, copying existing buffers, copying other ImageData objects, and decoding pixels from an Image object. ```javascript // create an empty buffer filled with transparent pixels new ImageData(width, height) new ImageData(width, height, {colorType='rgba', colorSpace='srgb'}) // copy an existing buffer into an ImageData container new ImageData(buffer, width) new ImageData(buffer, width, height) new ImageData(buffer, width, height, {colorType='rgba', colorSpace='srgb'}) new ImageData(imageData) // create a copy from another ImageData new ImageData(image, {colorType, colorSpace}) // decode the pixels from a bitmap Image ``` -------------------------------- ### Handle Mouse and Keyboard Events for Drawing Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Provides an example of responding to user interface events, specifically mouse movement and key presses, to dynamically alter the canvas content. It demonstrates how to use `win.on()` to attach event listeners for drawing with the mouse and clearing the canvas with the Escape key. ```javascript let win = new Window(400, 300, {background:'rgba(16, 16, 16, 0.35)'}), {canvas, ctx} = win // use the canvas & context created by the window win.on('mousemove', ({button, x, y}) => { if (button == 0){ // a left click ctx.fillStyle = `rgb(${Math.floor(255 * Math.random())},0,0)` ctx.beginPath() ctx.arc(x, y, 10 + 30 * Math.random(), 0, 2 * Math.PI) ctx.fill() } win.cursor = button === 0 ? 'none' : 'crosshair' }) win.on('keydown', ({key}) => { if (key == 'Escape'){ ctx.clearRect(0, 0, canvas.width, canvas.height) } }) ``` -------------------------------- ### Set Skia Canvas Threads Environment Variable Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md This example demonstrates how to limit the number of simultaneous asynchronous tasks for Skia Canvas rendering. By setting the SKIA_CANVAS_THREADS environment variable, you can control the thread pool size managed by the rayon library, which is used for background rendering tasks. ```bash SKIA_CANVAS_THREADS=2 node my-canvas-script.js ``` -------------------------------- ### Enable Skia Canvas Strict Argument Validation Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md This example shows how to enable stricter argument validation for Skia Canvas. By setting the SKIA_CANVAS_STRICT environment variable to '1' or 'true', you can configure Skia Canvas to throw TypeErrors for invalid arguments, which can be helpful for debugging and ensuring correct API usage. ```javascript ctx.fillRect(0, 0, 100, "october") ctx.lineTo(NaN, 0) ``` -------------------------------- ### Create Window with Options Object Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Shows how to create a Window using an options object to customize properties like background color or title. The window size can be specified alongside the options object or defaulted. ```javascript let orange = new Window(1024, 768, {background:"orange"}) let titled = new Window({title:"Canvas Window"}) ``` -------------------------------- ### Configure Next.js/Webpack for Skia Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/getting-started.md This configuration is necessary for frameworks like Next.js that use Webpack to bundle server-side code. It marks 'skia-canvas' as an external package to prevent its platform-native binary file from being excluded from the final build. This ensures that Skia Canvas functions correctly in the bundled application. ```typescript const nextConfig: NextConfig = { serverExternalPackages: ['skia-canvas'], webpack: (config, options) => { if (options.isServer){ config.externals = [ ...config.externals, {'skia-canvas': 'commonjs skia-canvas'}, ] } return config } }; ``` -------------------------------- ### Create Canvas Textures with Path2D and Options Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md Demonstrates creating various CanvasTextures using Path2D objects and options like color, line width, and angle. It also shows how to use these textures as fill and stroke styles for different shapes and text. ```javascript async function texturesDemo(){ let canvas = new Canvas(512, 256), ctx = canvas.getContext("2d") // define Path2Ds to use as repeating patterns let n = 10 let nylonPath = new Path2D() nylonPath.moveTo(0, n/4) nylonPath.lineTo(n/4, n/4) nylonPath.lineTo(n/4, 0) nylonPath.moveTo(n*3/4, n) nylonPath.lineTo(n*3/4, n*3/4) nylonPath.lineTo(n, n*3/4) nylonPath.moveTo(n/4, n/2) nylonPath.lineTo(n/4, n*3/4) nylonPath.lineTo(n/2, n*3/4) nylonPath.moveTo(n/2, n/4) nylonPath.lineTo(n*3/4, n/4) nylonPath.lineTo(n*3/4, n/2) let d = 1 let dotPath = new Path2D() dotPath.arc(0, 0, d, 0, 2*Math.PI) let w = 16 let wavePath = new Path2D() wavePath.moveTo(-w/2, w/2) wavePath.bezierCurveTo(-w*3/8, w*3/4, -w/8, w*3/4, 0, w/2) wavePath.bezierCurveTo( w/8, w/4, w*3/8, w/4, w/2, w/2) wavePath.bezierCurveTo( w*5/8, w*3/4, w*7/8, w*3/4, w, w/2) wavePath.bezierCurveTo( w*9/8, w/4, w*11/8, w/4, w*3/2, w/2) // create CanvasTextures using the Path2D objects let dots = ctx.createTexture(d*4, {path:dotPath, color:'teal', angle:Math.PI/4}), // path is filled if `line` omitted waves = ctx.createTexture([w, w/2], {path:wavePath, color:'red', line:3, angle:Math.PI/7}), nylon = ctx.createTexture(n, {path:nylonPath, color:'skyblue', line:1, cap:'round', angle:Math.PI/8}), lines = ctx.createTexture(5, {line:2, color:'orange'}) // no `path` required for parallel lines pattern // draw using CanvasTextures as fill & stroke styles for (let [i, texture] of Object.entries([dots, waves, nylon, lines])){ let x = 80 + i*120 let y = 60 // stroke path ctx.lineWidth = 30 ctx.strokeStyle = texture ctx.beginPath() ctx.ellipse(x, y, 30, 30, 0, Math.PI, 2*Math.PI) ctx.stroke() // fill path ctx.fillStyle = texture ctx.beginPath() ctx.ellipse(x, y+50, 40, 40, 0, 0, Math.PI*2) ctx.fill() // fill text ctx.textAlign = 'center' ctx.font = '900 100px sans-serif' ctx.fillText("Z", x, y+170) } await canvas.toFile('out.png', {density:2, matte:'white'}) } texturesDemo() ``` -------------------------------- ### Configure Font Rendering Options Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Illustrates how to configure font rendering for a Window by passing `textContrast` and `textGamma` options to the constructor. These settings affect the shading and blending of text glyphs. ```javascript new Window(800, 600, {textContrast:1, textGamma: 0.8}) ``` -------------------------------- ### Get Cookie Value (JavaScript) Source: https://github.com/samizdatco/skia-canvas/blob/main/tests/visual/index.html Retrieves the value of a specified cookie from the document's cookies. It parses the `document.cookie` string to find the cookie by its name and returns its decoded value, or null if not found. ```javascript function getCookie(name) { const nameEQ = name + '='; for (const cookie of document.cookie.split('; ')) { if (cookie.indexOf(nameEQ) === 0) { const value = cookie.substring(nameEQ.length); return decodeURIComponent(value); } } return null; } ``` -------------------------------- ### Create Window with Existing Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Demonstrates creating a Window and assigning an existing Canvas object to it. The window's dimensions will match the provided canvas if no size is specified. The output shows the initial window dimensions and the dimensions of the assigned canvas. ```javascript let bigCanvas = new Canvas(1024, 1024) let win = new Window({canvas:bigCanvas}) console.log([win.width, win.height]) ``` -------------------------------- ### Convert Skia-Canvas to Sharp Image Object (JavaScript) Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/imagedata.md The `toSharp()` method allows copying the canvas content into a Sharp image object. This requires the optional Sharp library to be installed separately and enables advanced image processing. ```javascript toSharp() ``` -------------------------------- ### Projection and Transformations Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md This section details how to create and apply projections and transformations to the canvas context. It covers the `createProjection` method for setting up perspective transforms and the `transform` and `setTransform` methods for applying matrix transformations in various formats. ```APIDOC ## POST /createProjection ### Description Creates a projection matrix that maps a source quadrilateral (basis) to a destination quadrilateral (quad). If no basis is specified, the canvas's bounding box is used. ### Method POST ### Endpoint ctx.createProjection(quad, basis?) ### Parameters #### Path Parameters - **quad** (array of numbers) - Required - The destination quadrilateral corners [x1, y1, x2, y2, x3, y3, x4, y4]. - **basis** (array of numbers or null) - Optional - The source quadrilateral. Can be [width, height], [left, top, right, bottom], or [x1, y1, x2, y2, x3, y3, x4, y4]. Defaults to canvas bounding box if not provided. ### Request Example ```js let quad = [ w*.33, h/2, // upper left w*.66, h/2, // upper right w, h*.9, // bottom right 0, h*.9 // bottom left ] let matrix = ctx.createProjection(quad) // use default basis ``` ### Response #### Success Response (200) - **matrix** (DOMMatrix or array of numbers) - The projection matrix. #### Response Example ```json { "matrix": [-0.75, 0, 0, -0.5, 256, 256] } ``` ## PUT /transform ### Description Applies a transformation matrix to the current canvas state. Accepts the matrix in various formats. ### Method PUT ### Endpoint ctx.transform(...matrix) ### Parameters #### Path Parameters - **matrix** (DOMMatrix, string, object, array, or numbers) - Required - The transformation matrix or its representation. ### Request Example ```js ctx.transform({a:-2, b:0, c:0, d:-0.5, e:-20, f:-40}) // matrix-like object ctx.transform([-2, 0, 0, -0.5, -20, -40]) // array ctx.transform(-2, 0, 0, -0.5, -20, -40) // numeric arguments ``` ## PUT /setTransform ### Description Resets the current transformation matrix to the specified matrix. Accepts the matrix in various formats. ### Method PUT ### Endpoint ctx.setTransform(...matrix) ### Parameters #### Path Parameters - **matrix** (DOMMatrix, string, object, array, or numbers) - Required - The transformation matrix or its representation. ### Request Example ```js ctx.setTransform('scale(2, 50%) rotate(180deg) translate(10, 10)') // CSS transform ctx.setTransform(new DOMMatrix().scale(2, .5).rotate(180).translate(10, 10)) // DOMMatrix ``` ``` -------------------------------- ### Check Font Availability - skia-canvas FontLibrary Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/font-library.md Checks if a given font family is currently available, either installed on the system or loaded dynamically via FontLibrary.use(). This method returns a boolean value, true if the font is available, and false otherwise. ```javascript import {FontLibrary} from 'skia-canvas' const isAvailable = FontLibrary.has("Arial") console.log(isAvailable) ``` -------------------------------- ### Get Font Details - skia-canvas FontLibrary Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/font-library.md Retrieves detailed information about a specific font family, including available weights, widths, and styles. If the font family is not found, it returns undefined. This method is useful for understanding the variations of a font that can be used. ```javascript import {FontLibrary} from 'skia-canvas' const fontInfo = FontLibrary.family("Avenir Next") console.log(fontInfo) ``` -------------------------------- ### Create Interactive GUI Window with Skia Canvas Source: https://context7.com/samizdatco/skia-canvas/llms.txt Demonstrates creating a native OS window to display Skia Canvas interactively. Supports mouse/keyboard events, animation loops, and window customization. Requires the 'skia-canvas' package. ```javascript import { Window, Canvas, App } from 'skia-canvas' // Create a window with custom options let win = new Window(800, 600, { title: 'Interactive Canvas', background: '#1a1a2e', resizable: true }) let { ctx, canvas } = win let particles = [] // Initialize particles on setup win.on('setup', () => { for (let i = 0; i < 100; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 4, vy: (Math.random() - 0.5) * 4, radius: Math.random() * 5 + 2, color: `hsl(${Math.random() * 360}, 70%, 60%)` }) } }) // Animation loop - 'draw' event clears canvas automatically win.on('draw', ({ frame }) => { // Update and draw particles particles.forEach(p => { // Update position p.x += p.vx p.y += p.vy // Bounce off walls if (p.x < 0 || p.x > canvas.width) p.vx *= -1 if (p.y < 0 || p.y > canvas.height) p.vy *= -1 // Draw particle ctx.fillStyle = p.color ctx.beginPath() ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2) ctx.fill() }) // Display frame count ctx.fillStyle = 'white' ctx.font = '16px sans-serif' ctx.fillText(`Frame: ${frame}`, 10, 30) }) // Mouse interaction win.on('mousemove', ({ x, y, button }) => { if (button === 0) { // Left button held particles.push({ x, y, vx: (Math.random() - 0.5) * 8, vy: (Math.random() - 0.5) * 8, radius: Math.random() * 8 + 4, color: `hsl(${Date.now() % 360}, 80%, 60%)` }) } }) // Keyboard controls win.on('keydown', ({ key }) => { if (key === 'Escape') win.close() if (key === 'f') win.fullscreen = !win.fullscreen if (key === 'c') particles.length = 0 // Clear particles }) // Window resize handling win.on('resize', () => { console.log(`Window resized to ${win.width}x${win.height}`) }) // Set frame rate (default is 60) App.fps = 60 // Launch returns when all windows are closed await App.launch() console.log('Application closed') ``` -------------------------------- ### Set SKIA_CANVAS_THREADS Environment Variable Source: https://github.com/samizdatco/skia-canvas/blob/main/README.md This example shows how to set the SKIA_CANVAS_THREADS environment variable to control the number of threads used for asynchronous rendering tasks. This is useful for managing CPU resources when using methods like toFile or toBuffer. ```bash SKIA_CANVAS_THREADS=2 node my-canvas-script.js ``` -------------------------------- ### Manage Multiple Canvas Pages and Navigation Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Illustrates creating multiple pages within a single canvas and navigating between them using window properties. It shows how to set the initial page and dynamically change it using keyboard input, along with logging the current page status. ```javascript let canvas = new Canvas(32, 32), colors = ['orange', 'yellow', 'green', 'skyblue', 'purple'] for (var c of colors){ ctx = canvas.newPage(canvas.width * 2, canvas.height * 2) ctx.fillStyle = c ctx.fillRect(0,0, canvas.width, canvas.height) ctx.fillStyle = 'white' ctx.arc(canvas.width/2, canvas.height/2, 40, 0, 2 * Math.PI) ctx.fill() } let win = new Window({canvas, page:-2}) win.on('keydown', e => { if (e.key=='ArrowLeft') win.page-- if (e.key=='ArrowRight') win.page++ console.log(`page ${win.page}/${canvas.pages.length}: ${canvas.width} × ${canvas.height}`) }) ``` -------------------------------- ### Create Projection with Quad and Default Basis Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md Demonstrates creating a projection matrix using a quadrilateral and the default canvas basis. This method transforms drawing elements onto a trapezoidal shape. It uses the canvas dimensions as the default basis if none is provided. ```javascript let canvas = new Canvas(512, 512), ctx = canvas.getContext("2d"), {width:w, height:h} = canvas; ctx.font = '900 480px Times' ctx.textAlign = 'center' ctx.fillStyle = '#aaa' ctx.fillRect(0, 0, w, h) let quad = [ w*.33, h/2, // upper left w*.66, h/2, // upper right w, h*.9, // bottom right 0, h*.9, // bottom left ] let matrix = ctx.createProjection(quad) // use default basis ctx.setTransform(matrix) ctx.fillStyle = 'white' ctx.fillRect(10, 10, w-20, h-20) ctx.fillStyle = '#900' ctx.fillText("@", w/2, h-40) ``` -------------------------------- ### Load Sharp Images in JavaScript Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/imagedata.md Demonstrates loading an image using the Sharp library. This function assumes Sharp is installed and imported. It initializes a Sharp object and then passes it to `loadImageData` for processing. The output color type is always RGBA. ```javascript import sharp from 'sharp' let sharpImage = sharp({ create: {width:2, height:2, channels:3, background:"#f00"} }) await loadImageData(sharpImage) ``` -------------------------------- ### Create Path2D Objects Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/path2d.md Demonstrates various ways to instantiate Path2D objects. This includes creating an empty path, initializing from an SVG string, and cloning an existing Path2D object. These methods allow for flexible path creation and management. ```javascript let p1 = new Path2D("M 10,10 h 100 v 100 h -100 Z") let p2 = new Path2D(p1) let p3 = new Path2D() p3.rect(10, 10, 100, 100) ``` -------------------------------- ### Trim Path Segments by Percentage Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/path2d.md The trim method extracts a portion of a path based on start and end percentages (0.0 to 1.0). It can also invert the selection. Single numbers or negative values are supported for shorthand notation. The original path is not modified. ```javascript let orig = new Path2D(); orig.arc(100, 100, 50, Math.PI, 0); let middle = orig.trim(.25, .75); let endpoints = orig.trim(.25, .75, true); let left = orig.trim(.25); let right = orig.trim(-.25); ``` -------------------------------- ### Window Methods Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Methods for managing the window lifecycle and event handling. ```APIDOC ## Window Methods ### `close()` - **Description**: Removes the window from the screen and prepares it to be garbage collected. The `Window` object remains valid after closing and its `.canvas` can still be used. - **Returns**: `Window` ### `open()` - **Description**: Re-opens a closed window. Has no effect on an already open window or a newly created window. - **Returns**: `Window` ### `on(eventType, handlerFunction)` - **Description**: Adds an event listener for the specified event type. - **Parameters**: - `eventType` (string) - The type of event to listen for. - `handlerFunction` (function) - The function to call when the event is triggered. - **Returns**: `Window` ### `off(eventType, handlerFunction)` - **Description**: Removes an event listener for the specified event type. - **Parameters**: - `eventType` (string) - The type of event to remove the listener from. - `handlerFunction` (function) - The handler function to remove. - **Returns**: `Window` ### `once(eventType, handlerFunction)` - **Description**: Adds a one-time event listener that will be called only the next time the event is fired. - **Parameters**: - `eventType` (string) - The type of event to listen for. - `handlerFunction` (function) - The function to call when the event is triggered. - **Returns**: `Window` ``` -------------------------------- ### Convert ImageData to Sharp Image and Save Grayscale (JavaScript) Source: https://github.com/samizdatco/skia-canvas/blob/main/README.md This example demonstrates converting Skia Canvas ImageData to a Sharp object and saving it as a PNG in grayscale. It depends on 'sharp' and 'skia-canvas'. Input is ImageData from a canvas context, output is a PNG file. ```javascript import sharp from 'sharp' import {Canvas, loadImage} from 'skia-canvas' let canvas = new Canvas(400, 400), ctx = canvas.getContext("2d"), {width, height} = canvas, [x, y] = [width/2, height/2] ctx.fillStyle = 'red' ctx.fillRect(0, 0, x, y) ctx.fillStyle = 'orange' ctx.fillRect(x, y, x, y) // Convert an ImageData to a Sharp object and save a grayscale version let imgData = ctx.getImageData(0, 0, width, height, {matte:'white', density:2}) await imgData.toSharp().grayscale().png().toFile("black-and-white.png") ``` -------------------------------- ### createProjection Method Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md Generates a DOMMatrix for simulating perspective or other distortions by mapping canvas corners to an arbitrary quadrilateral. ```APIDOC ## `createProjection()` ### Description This method returns a `DOMMatrix` object which can be used to simulate perspective effects or other distortions in which the four corners of the canvas are mapped to an arbitrary quadrilateral (four sided polygon). The matrix must be passed to the context's `setTransform()` method for it take effect. ### Method `createProjection(quad, [basis])` ### Parameters #### Path Parameters - **quad** (Array | Array>) - Required - The `quad` argument defines the **target** of the transformation. It specifies four points that establish where the four corners of the source coordinate space will be positioned within the viewport. If these points form a polygon other than a rectangle, lines drawn along the x & y axes of the source space will no longer be perpendicular—trapezoids allow for ‘vanishing point’ effects and parallelograms create ‘skew’. The geometry of the quadrilateral should be described as an Array of either 8 or 4 numbers specifying an arbitrary polygon or rectangle respectively: ```js [x1, y1, x2, y2, x3, y3, x4, y4] // four corner points [left, top, right, bottom] // four edges of a rectangle // internal arrays for grouping are also allowed [[x1, y1], [x2, y2], [x3, y3], [x4, y4]] ``` - **basis** (DOMMatrix) - Optional - The basis matrix to apply the projection to. Defaults to the identity matrix. ``` -------------------------------- ### Choose Rendering Engine (CPU/GPU) Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/canvas.md Demonstrates how to initialize a Skia canvas, optionally disabling GPU acceleration. GPU rendering is generally faster for complex scenes, but CPU rendering can be preferable when frequently accessing bitmap data. ```javascript new Canvas(512, 512, {gpu:false}) // use CPU-based rendering ``` -------------------------------- ### Create and Export Images with Skia Canvas Source: https://context7.com/samizdatco/skia-canvas/llms.txt Demonstrates creating a new Canvas instance, drawing content like gradients and text, and exporting the result to various formats (PNG, JPEG, PDF) as files, buffers, or data URLs. It highlights both asynchronous (recommended) and synchronous export methods, as well as shorthand properties for direct buffer access. ```javascript import { Canvas } from 'skia-canvas' // Create a new canvas with dimensions let canvas = new Canvas(400, 400) let ctx = canvas.getContext("2d") // Draw a gradient-filled rectangle let gradient = ctx.createLinearGradient(0, 0, 400, 400) gradient.addColorStop(0, "red") gradient.addColorStop(0.5, "yellow") gradient.addColorStop(1, "green") ctx.fillStyle = gradient ctx.fillRect(0, 0, 400, 400) // Add text ctx.fillStyle = 'white' ctx.font = 'bold 48px sans-serif' ctx.textAlign = 'center' ctx.fillText('Hello Canvas', 200, 220) // Export asynchronously (recommended for production) await canvas.toFile('output.png') // Save to file await canvas.toFile('retina@2x.png') // Save at 2x density await canvas.toFile('output.pdf') // Save as PDF let buffer = await canvas.toBuffer('png') // Get as Buffer let dataUrl = await canvas.toURL('jpeg', {quality: 0.85}) // Get as data URL // Export synchronously (blocks main thread) canvas.toFileSync('sync-output.png') let syncBuffer = canvas.toBufferSync('png') // Shorthand properties return Promises let pngBuffer = await canvas.png let pdfBuffer = await canvas.pdf let jpgBuffer = await canvas.jpg ``` -------------------------------- ### Create and Get Image Data with Skia Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/context.md These methods mirror standard Canvas API functions but extend createImageData and getImageData to accept an optional colorType for pixel array arrangement. The colorType defaults to 'rgba' but can be set to any supported type. getImageData also accepts rendering options like density, matte, and msaa. ```javascript createImageData(width, height) createImageData(width, height, {colorType="rgba", colorSpace="srgb"}) createImageData(imagedata) getImageData(sx, sy, sw, sh) getImageData(sx, sy, sw, sh, {colorType="rgba", colorSpace="srgb", density, matte, msaa}) ``` -------------------------------- ### Create Window with Custom Size Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Demonstrates creating a Window with specified width and height. These dimensions are applied to both the window and its automatically created canvas. ```javascript let smaller = new Window(256, 128) ``` -------------------------------- ### Create Default Window and Canvas Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Creates a new Window object with default dimensions (512x512) and a white background. It automatically initializes a Canvas of the same size, accessible via the `.canvas` property. The output shows the default canvas properties. ```javascript let win = new Window() console.log(win.canvas) ``` -------------------------------- ### toSharp() Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/canvas.md Copies the canvas content into a Sharp image object, enabling the use of Sharp's extensive image processing and optimization features. This method is optional and requires the Sharp library to be installed separately. Note that while the method returns synchronously, subsequent operations on the Sharp object may need to be awaited. ```APIDOC ## `toSharp()` ### Description Copies the canvas content into a [Sharp][sharp] image object. This allows you to leverage Sharp's advanced image processing and optimization capabilities. The Sharp library is an optional dependency and must be installed separately. ### Method `toSharp(options)` (sync) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **options** (object) - Optional configuration object: - **page** (number) - For multi-page formats like PDF, specifies the page number (1-based). - **matte** (boolean) - Whether to include a matte background (default: false). - **msaa** (number) - The number of samples per pixel for multi-sample anti-aliasing (MSAA). - **density** (number) - The resolution of the output image in dots per inch (DPI). ### Request Example ```javascript // Get a Sharp image object let sharpImg = canvas.toSharp({ density: 300 }); // Use Sharp's methods (note the await for operations) await sharpImg.heif({ compression: 'hevc' }).toFile('output.heif'); // Method chaining with await await canvas.toSharp().png().toFile('output.png'); ``` ### Response #### Success Response (200) - **Sharp** - A Sharp image object instance. #### Response Example ```javascript // Example of a Sharp object (actual object is complex) Sharp { ... } ``` ``` -------------------------------- ### Combine paths using boolean operations Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/path2d.md Methods like `complement()`, `difference()`, `intersect()`, `union()`, and `xor()` allow combining two Path2D objects based on their overlapping areas. These operations generate a new Path2D object representing the result of the boolean logic. Example shapes include ovals and rectangles. ```javascript let oval = new Path2D() oval.arc(100, 100, 100, 0, 2*Math.PI) let rect = new Path2D() rect.rect(0, 100, 100, 100) let knockout = rect.complement(oval), overlap = rect.intersect(oval), footprint = rect.union(oval) ``` -------------------------------- ### Draw to Window Canvas using ctx Shortcut Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/window.md Demonstrates drawing directly to a window's canvas using the convenient `.ctx` property, which is a shortcut for `win.canvas.getContext('2d')`. This method simplifies the process of accessing the 2D rendering context for immediate drawing operations. ```javascript let win = new Window({background:"olive", fit:"contain-y"}) console.log(win.ctx === win.canvas.getContext("2d")) // true let {canvas, ctx} = win ctx.fillStyle = 'lightskyblue' ctx.fillRect(10, 10, canvas.width-20, canvas.height-20) ``` -------------------------------- ### Load Fonts using Glob Patterns - skia-canvas FontLibrary Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/font-library.md Loads font files from a directory using glob patterns. This is useful for loading multiple fonts that follow a naming convention. Note that glob support is no longer built-in and requires installing external packages like 'glob' or 'fast-glob'. Paths should use forward slashes, even on Windows. ```javascript import {FontLibrary} from 'skia-canvas' import {globSync as glob} from 'fast-glob' // Load fonts with default family name FontLibrary.use(glob('fonts/Crimson_Pro/*.ttf')) // Load fonts with an alias FontLibrary.use("Stinson", glob('fonts/Crimson_Pro/*.ttf')) ``` -------------------------------- ### Integrate with Sharp Image Processing Library (JS) Source: https://github.com/samizdatco/skia-canvas/blob/main/docs/api/canvas.md Allows copying canvas content into a Sharp image object for advanced image manipulation. This method is synchronous, but subsequent operations on the Sharp object often require `await`. The Sharp library is an optional dependency and must be installed separately. Optional arguments control page, matte, msaa, and density. ```javascript toSharp({page, matte, msaa, density}) ``` ```javascript let sharpImg = canvas.toSharp() await sharpImg.heif({compression:'hevc'}).toFile("image.heif") ``` ```javascript await canvas.toSharp().heif({compression:'hevc'}).toFile("image.heif") ```