Gathering detailed insights and metrics for @napi-rs/canvas-linux-x64-musl
Gathering detailed insights and metrics for @napi-rs/canvas-linux-x64-musl
Gathering detailed insights and metrics for @napi-rs/canvas-linux-x64-musl
Gathering detailed insights and metrics for @napi-rs/canvas-linux-x64-musl
@napi-rs/nice-linux-x64-musl
https://linux.die.net/man/2/nice binding for Node.js
@napi-rs/snappy-linux-x64-musl
Fastest Snappy compression library in Node.js
@napi-rs/simple-git-linux-x64-musl
This is the **x86_64-unknown-linux-musl** binary for `@napi-rs/simple-git`
@napi-rs/canvas-linux-x64-gnu
Canvas for Node.js with skia backend
npm install @napi-rs/canvas-linux-x64-musl
98.6
Supply Chain
35.6
Quality
95.2
Maintenance
100
Vulnerability
100
License
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
1,784 Stars
1,025 Commits
76 Forks
20 Watching
16 Branches
28 Contributors
Updated on 28 Nov 2024
Rust (51.34%)
TypeScript (18.74%)
C++ (14.84%)
JavaScript (13.3%)
Dockerfile (1.02%)
HTML (0.71%)
CMake (0.05%)
Shell (0.01%)
Cumulative downloads
Total Downloads
Last day
-23.1%
9,042
Compared to previous day
Last week
2.4%
61,383
Compared to previous week
Last month
11.1%
233,358
Compared to previous month
Last year
164.5%
2,558,088
Compared to previous year
No dependencies detected.
skr canvas
🚀 Help me to become a full-time open-source developer by sponsoring me on Github
Google Skia binding to Node.js via Node-API, 0 System dependencies!
⚠️ This project is in pre-release stage. And there may be some bugs.
For details on planned features and future direction please refer to the Roadmap.
1yarn add @napi-rs/canvas 2npm install @napi-rs/canvas
arm64
cortex-a57 or newer CPU architecture on Linux.
All Apple M chips on macOS.
armv7
cortex-a7 or newer CPU architecture.
Since Skia relies on the glibc 2.18 API, you need to have at least glibc version >= 2.18 on your system.
To use this library on Lambda you will need to use a Lambda layer.
You can simply attach a lambda layer by getting an ARN from Canvas-Lambda-Layer
Make sure to exclude
@napi-rs/canvas
while bundling your Lambda.
1const { promises } = require('node:fs')
2const { join } = require('node:path')
3const { createCanvas, loadImage } = require('@napi-rs/canvas')
4
5const canvas = createCanvas(300, 320)
6const ctx = canvas.getContext('2d')
7
8ctx.lineWidth = 10
9ctx.strokeStyle = '#03a9f4'
10ctx.fillStyle = '#03a9f4'
11
12// Wall
13ctx.strokeRect(75, 140, 150, 110)
14
15// Door
16ctx.fillRect(130, 190, 40, 60)
17
18// Roof
19ctx.beginPath()
20ctx.moveTo(50, 140)
21ctx.lineTo(150, 60)
22ctx.lineTo(250, 140)
23ctx.closePath()
24ctx.stroke()
25
26async function main() {
27 // load images from disk or from a URL
28 const catImage = await loadImage('path/to/cat.png')
29 const dogImage = await loadImage('https://example.com/path/to/dog.jpg')
30
31 ctx.drawImage(catImage, 0, 0, catImage.width, catImage.height)
32
33 ctx.drawImage(dogImage, canvas.width / 2, canvas.height / 2, dogImage.width, dogImage.height)
34
35 // export canvas as image
36 const pngData = await canvas.encode('png') // JPEG, AVIF and WebP are also supported
37 // encoding in libuv thread pool, non-blocking
38 await promises.writeFile(join(__dirname, 'simple.png'), pngData)
39}
40
41main()
1const { writeFileSync } = require('fs') 2const { join } = require('path') 3 4const { createCanvas, GlobalFonts } = require('@napi-rs/canvas') 5 6GlobalFonts.registerFromPath(join(__dirname, '..', 'fonts', 'AppleColorEmoji@2x.ttf'), 'Apple Emoji') 7GlobalFonts.registerFromPath(join(__dirname, '..', '__test__', 'fonts', 'COLRv1.ttf'), 'COLRv1') 8 9console.info(GlobalFonts.families) 10 11const canvas = createCanvas(760, 360) 12const ctx = canvas.getContext('2d') 13 14ctx.font = '50px Apple Emoji' 15ctx.strokeText('😀😃😄😁😆😅😂🤣☺️😊😊😇', 50, 150) 16 17ctx.font = '100px COLRv1' 18ctx.fillText('abc', 50, 300) 19 20const b = canvas.toBuffer('image/png') 21 22writeFileSync(join(__dirname, 'draw-emoji.png'), b)
See benchmark for benchmark code.
Hardware info:
,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
❯ 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 │
└─────────┴─────────────────┴───────────────────────┴──────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
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 │
└─────────┴─────────────────┴───────────────────────┴─────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
1new Path2D() 2new Path2D(path: Path2D) 3// 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') 4new Path2D(path: string)
1export interface DOMMatrix2DInit { 2 a: number 3 b: number 4 c: number 5 d: number 6 e: number 7 f: number 8} 9 10export class Path2D { 11 constructor(path?: Path2D | string) 12 13 addPath(path: Path2D, transform?: DOMMatrix2DInit): void 14 arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, anticlockwise?: boolean): void 15 arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void 16 bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void 17 closePath(): void 18 ellipse( 19 x: number, 20 y: number, 21 radiusX: number, 22 radiusY: number, 23 rotation: number, 24 startAngle: number, 25 endAngle: number, 26 anticlockwise?: boolean, 27 ): void 28 lineTo(x: number, y: number): void 29 moveTo(x: number, y: number): void 30 quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void 31 rect(x: number, y: number, w: number, h: number): void 32 33 // PathKit methods 34 op(path: Path2D, operation: PathOp): Path2D 35 toSVGString(): string 36 getFillType(): FillType 37 getFillTypeString(): string 38 setFillType(type: FillType): void 39 simplify(): Path2D 40 asWinding(): Path2D 41 stroke(stroke?: StrokeOptions): Path2D 42 transform(transform: DOMMatrix2DInit): Path2D 43 getBounds(): [left: number, top: number, right: number, bottom: number] 44 computeTightBounds(): [left: number, top: number, right: number, bottom: number] 45 trim(start: number, end: number, isComplement?: boolean): Path2D 46 equals(path: Path2D): boolean 47}
PathKit
is a toolset for manipulating Path in Skia
, supporting quadratic beziers, cubic beziers and conics.
The main features are.
.op(path, PathOp)
1const pathOne = new Path2D( 2 'M8 50H92C96.4183 50 100 53.5817 100 58V142C100 146.418 96.4183 150 92 150H8C3.58172 150 0 146.418 0 142V58C0 53.5817 3.58172 50 8 50Z', 3) 4const pathTwo = new Path2D( 5 '"M58 0H142C146.418 0 150 3.58172 150 8V92C150 96.4183 146.418 100 142 100H58C53.5817 100 50 96.4183 50 92V8C50 3.58172 53.5817 0 58 0Z', 6) 7 8pathOne.op(pathTwo, PathOp.Intersect).toSVGString() 9// => "M100 100L58 100C53.5817 100 50 96.4183 50 92L50 50L92 50C96.4183 50 100 53.5817 100 58L100 100Z"
FillType
in Path.asWinding()
You can convert fill-rule="evenodd"
to fill-rule="nonzero"
in SVG.
This is useful for OpenType font-related tools, as fill-rule="nonzero"
is only supported in OpenType fonts.
1const pathCircle = new Path2D( 2 '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', 3) 4pathCircle.setFillType(FillType.EvenOdd) 5pathCircle.asWinding().toSVGString() 6// => "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"
.simplify()
Set the path to the same non-overlapping contour as the original path area, which means that it can also remove overlapping paths.
SVG with overlapping paths (Left)
1const path = 2 'M2.933,89.89 L89.005,3.818 Q90.412,2.411 92.249,1.65 Q94.087,0.889 96.076,0.889 Q98.065,0.889 99.903,1.65 Q101.741,2.411 103.147,3.818 L189.22,89.89 Q190.626,91.296 191.387,93.134 Q192.148,94.972 192.148,96.961 Q192.148,98.95 191.387,100.788 Q190.626,102.625 189.219,104.032 Q187.813,105.439 185.975,106.2 Q184.138,106.961 182.148,106.961 Q180.159,106.961 178.322,106.2 Q176.484,105.439 175.077,104.032 L89.005,17.96 L96.076,10.889 L103.147,17.96 L17.075,104.032 Q15.668,105.439 13.831,106.2 Q11.993,106.961 10.004,106.961 Q8.015,106.961 6.177,106.2 Q4.339,105.439 2.933,104.032 Q1.526,102.625 0.765,100.788 Q0.004,98.95 0.004,96.961 Q0.004,94.972 0.765,93.134 Q1.526,91.296 2.933,89.89 Z' 3 4path.simplify().toSVGString() 5// => "M89.005 3.818L2.933 89.89Q1.526 91.296 0.765 93.134Q0.004 94.972 0.004 96.961Q0.004 98.95 0.765 100.788Q1.526 102.625 2.933 104.032Q4.339 105.439 6.177 106.2Q8.015 106.961 10.004 106.961Q11.993 106.961 13.831 106.2Q15.668 105.439 17.075 104.032L96.076 25.031L175.077 104.032Q176.484 105.439 178.322 106.2Q180.159 106.961 182.148 106.961Q184.138 106.961 185.975 106.2Q187.813 105.439 189.219 104.032Q190.626 102.625 191.387 100.788Q192.148 98.95 192.148 96.961Q192.148 94.972 191.387 93.134Q190.626 91.296 189.22 89.89L103.147 3.818Q101.741 2.411 99.903 1.65Q98.065 0.889 96.076 0.889Q94.087 0.889 92.249 1.65Q90.412 2.411 89.005 3.818Z"
The tiger.json was serialized from gojs/samples/tiger
1node example/anime-girl.js
SVG | PNG |
---|---|
CC-BY-SA 3.0 by Niabot | CC-BY-SA 3.0 by Niabot |
You can build this project from source, the system requirements are here: https://skia.org/docs/user/build
1# Clone the code: 2$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git 3$ cd canvas 4 5# Build Skia: 6$ node scripts/build-skia.js 7 8# Install NPM packages, build the Node.js addon: 9$ npm install -g yarn 10$ 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` 11$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html 12$ yarn build 13 14# All done! Run test cases or examples now: 15$ yarn test 16$ node example/tiger.js
You can pull skia pre-build binaries if you just care the Rust
part:
1# Clone the code: 2$ git clone --recurse-submodules https://github.com/Brooooooklyn/canvas.git 3$ cd canvas 4 5# Download Skia binaries: 6# It will pull the binaries match the git hash in `./skia` submodule 7$ node scripts/release-skia-binary.mjs --download 8 9# Install NPM packages, build the Node.js addon: 10$ npm install -g yarn 11$ yarn install --mode=skip-build 12$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html 13$ yarn build 14 15# All done! Run test cases or examples now: 16$ yarn test 17$ node example/tiger.js
No vulnerabilities found.
Reason
30 commit(s) and 4 issue activity found in the last 90 days -- score normalized to 10
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
packaging workflow detected
Details
Reason
1 existing vulnerabilities detected
Details
Reason
Found 3/28 approved changesets -- score normalized to 1
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
Project has not signed or included provenance with any releases.
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2024-11-25
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More