ganze project
This commit is contained in:
3
node_modules/@napi-rs/canvas-win32-x64-msvc/README.md
generated
vendored
Normal file
3
node_modules/@napi-rs/canvas-win32-x64-msvc/README.md
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# `@napi-rs/canvas-win32-x64-msvc`
|
||||
|
||||
This is the **x86_64-pc-windows-msvc** binary for `@napi-rs/canvas`
|
||||
BIN
node_modules/@napi-rs/canvas-win32-x64-msvc/icudtl.dat
generated
vendored
Normal file
BIN
node_modules/@napi-rs/canvas-win32-x64-msvc/icudtl.dat
generated
vendored
Normal file
Binary file not shown.
41
node_modules/@napi-rs/canvas-win32-x64-msvc/package.json
generated
vendored
Normal file
41
node_modules/@napi-rs/canvas-win32-x64-msvc/package.json
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@napi-rs/canvas-win32-x64-msvc",
|
||||
"version": "0.1.80",
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "skia.win32-x64-msvc.node",
|
||||
"files": [
|
||||
"skia.win32-x64-msvc.node",
|
||||
"icudtl.dat"
|
||||
],
|
||||
"description": "Canvas for Node.js with skia backend",
|
||||
"keywords": [
|
||||
"napi-rs",
|
||||
"NAPI",
|
||||
"N-API",
|
||||
"Rust",
|
||||
"node-addon",
|
||||
"node-addon-api",
|
||||
"canvas",
|
||||
"image",
|
||||
"pdf",
|
||||
"svg",
|
||||
"skia"
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Brooooooklyn/canvas.git"
|
||||
}
|
||||
}
|
||||
BIN
node_modules/@napi-rs/canvas-win32-x64-msvc/skia.win32-x64-msvc.node
generated
vendored
Normal file
BIN
node_modules/@napi-rs/canvas-win32-x64-msvc/skia.win32-x64-msvc.node
generated
vendored
Normal file
Binary file not shown.
21
node_modules/@napi-rs/canvas/LICENSE
generated
vendored
Normal file
21
node_modules/@napi-rs/canvas/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 lynweklm@gmail.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
362
node_modules/@napi-rs/canvas/README.md
generated
vendored
Normal file
362
node_modules/@napi-rs/canvas/README.md
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
# `skr canvas`
|
||||
|
||||
[](https://github.com/Brooooooklyn/canvas/actions/workflows/CI.yaml)
|
||||

|
||||
[](https://packagephobia.com/result?p=@napi-rs/canvas)
|
||||
[](https://npmcharts.com/compare/@napi-rs/canvas?minimal=true)
|
||||
|
||||
> 🚀 Help me to become a full-time open-source developer by [sponsoring me on Github](https://github.com/sponsors/Brooooooklyn)
|
||||
|
||||
Google Skia binding to Node.js via [Node-API](https://napi.rs), **0 System dependencies!**
|
||||
|
||||
⚠️ This project is in pre-release stage. And there may be some bugs.<br/>
|
||||
For details on planned features and future direction please refer to the [Roadmap](https://github.com/Brooooooklyn/canvas/issues/113).
|
||||
|
||||
[中文文档](./README-zh.md)
|
||||
|
||||
# Install
|
||||
|
||||
```bash
|
||||
yarn add @napi-rs/canvas
|
||||
npm install @napi-rs/canvas
|
||||
```
|
||||
|
||||
# Support matrix
|
||||
|
||||
## System requirement
|
||||
|
||||
### `arm64`
|
||||
|
||||
[**_cortex-a57_**](https://en.wikipedia.org/wiki/ARM_Cortex-A57) or newer CPU architecture on **Linux**.
|
||||
|
||||
All Apple M chips on **macOS**.
|
||||
|
||||
### `armv7`
|
||||
|
||||
[**_cortex-a7_**](https://en.wikipedia.org/wiki/ARM_Cortex-A7) or newer CPU architecture.
|
||||
|
||||
### glibc
|
||||
|
||||
Since Skia relies on the [glibc](https://www.gnu.org/software/libc/) 2.18 API, you need to have at least glibc version >= 2.18 on your system.
|
||||
|
||||
## AWS Lambda usage
|
||||
|
||||
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](https://github.com/ShivamJoker/Canvas-Lambda-Layer)
|
||||
|
||||
> Make sure to exclude `@napi-rs/canvas` while bundling your Lambda.
|
||||
|
||||
# Usage
|
||||
|
||||
```js
|
||||
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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Emoji text
|
||||
|
||||
```js
|
||||
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)
|
||||
```
|
||||
|
||||

|
||||
|
||||
# Performance
|
||||
|
||||
See [benchmark](./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 │
|
||||
└─────────┴─────────────────┴───────────────────────┴─────────────────────────┴────────────────────────────┴───────────────────────────┴─────────┘
|
||||
```
|
||||
|
||||
# Features
|
||||
|
||||
## Path2D
|
||||
|
||||
```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)
|
||||
```
|
||||
|
||||
```typescript
|
||||
export interface DOMMatrix2DInit {
|
||||
a: number
|
||||
b: number
|
||||
c: number
|
||||
d: number
|
||||
e: number
|
||||
f: number
|
||||
}
|
||||
|
||||
export class Path2D {
|
||||
constructor(path?: Path2D | string)
|
||||
|
||||
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
|
||||
|
||||
// PathKit methods
|
||||
op(path: Path2D, operation: PathOp): Path2D
|
||||
toSVGString(): string
|
||||
getFillType(): FillType
|
||||
getFillTypeString(): string
|
||||
setFillType(type: FillType): void
|
||||
simplify(): Path2D
|
||||
asWinding(): Path2D
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
## PathKit
|
||||
|
||||
`PathKit` is a toolset for manipulating Path in `Skia`, supporting **_quadratic beziers_**, **_cubic beziers_** and **_conics_**.
|
||||
The main features are.
|
||||
|
||||
### Path Operation
|
||||
|
||||
`.op(path, PathOp)`
|
||||
|
||||
```js
|
||||
const pathOne = new Path2D(
|
||||
'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',
|
||||
)
|
||||
const pathTwo = new Path2D(
|
||||
'"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',
|
||||
)
|
||||
|
||||
pathOne.op(pathTwo, PathOp.Intersect).toSVGString()
|
||||
// => "M100 100L58 100C53.5817 100 50 96.4183 50 92L50 50L92 50C96.4183 50 100 53.5817 100 58L100 100Z"
|
||||
```
|
||||
|
||||
- **Union**, subtract the op path from the first path
|
||||
- **Difference**, intersect the two paths
|
||||
- **ReverseDifference**, union (inclusive-or) the two paths
|
||||
- **Intersect**, exclusive-or the two paths
|
||||
- **XOR**, subtract the first path from the op path
|
||||
|
||||

|
||||
|
||||
### Covert `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.
|
||||
|
||||

|
||||
|
||||
```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"
|
||||
```
|
||||
|
||||
### Simplify **_Path_**
|
||||
|
||||
`.simplify()`
|
||||
|
||||
Set the path to the same non-overlapping contour as the original path area, which means that it can also remove overlapping paths.
|
||||
|
||||
<img width="800" src="./docs/imgs/simplify.png" >
|
||||
|
||||
[SVG with overlapping paths](./docs/imgs/overlapping-path.svg) (Left)
|
||||
|
||||
```js
|
||||
const path =
|
||||
'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'
|
||||
|
||||
path.simplify().toSVGString()
|
||||
// => "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"
|
||||
```
|
||||
|
||||
# [Example](./example/tiger.js)
|
||||
|
||||
> The tiger.json was serialized from [gojs/samples/tiger](https://github.com/NorthwoodsSoftware/GoJS/blob/master/samples/tiger.html)
|
||||
|
||||
<img width="500" src="example/tiger.png">
|
||||
|
||||
```shell
|
||||
node example/anime-girl.js
|
||||
```
|
||||
|
||||
| SVG | PNG |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| <img width="500" src="example/anime-girl.svg"><br/>[CC-BY-SA 3.0](https://creativecommons.org/licenses/by/3.0) by [Niabot](https://commons.wikimedia.org/wiki/User:Niabot) | <img width="500" src="example/anime-girl.png"><br/>[CC-BY-SA 3.0](https://creativecommons.org/licenses/by/3.0) by [Niabot](https://commons.wikimedia.org/wiki/User:Niabot) |
|
||||
|
||||
# Building
|
||||
|
||||
## Build skia from source
|
||||
|
||||
You can build this project from source, the system requirements are here: https://skia.org/docs/user/build
|
||||
|
||||
```sh
|
||||
# Clone the code:
|
||||
$ 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
|
||||
```
|
||||
|
||||
## Pull pre-build skia binary from GitHub
|
||||
|
||||
You can pull skia pre-build binaries if you just care the `Rust` part:
|
||||
|
||||
```sh
|
||||
# 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
|
||||
```
|
||||
873
node_modules/@napi-rs/canvas/geometry.js
generated
vendored
Normal file
873
node_modules/@napi-rs/canvas/geometry.js
generated
vendored
Normal file
@@ -0,0 +1,873 @@
|
||||
const { inspect } = require('util')
|
||||
/*
|
||||
* vendored in order to fix its dependence on the window global [cds 2020/08/04]
|
||||
* otherwise unchanged from https://github.com/jarek-foksa/geometry-polyfill/tree/f36bbc8f4bc43539d980687904ce46c8e915543d
|
||||
*/
|
||||
|
||||
// @info
|
||||
// DOMPoint polyfill
|
||||
// @src
|
||||
// https://drafts.fxtf.org/geometry/#DOMPoint
|
||||
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_point_read_only.cc
|
||||
class DOMPoint {
|
||||
constructor(x = 0, y = 0, z = 0, w = 1) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.z = z
|
||||
this.w = w
|
||||
}
|
||||
|
||||
static fromPoint(otherPoint) {
|
||||
return new DOMPoint(
|
||||
otherPoint.x,
|
||||
otherPoint.y,
|
||||
otherPoint.z !== undefined ? otherPoint.z : 0,
|
||||
otherPoint.w !== undefined ? otherPoint.w : 1,
|
||||
)
|
||||
}
|
||||
|
||||
matrixTransform(matrix) {
|
||||
if ((matrix.is2D || matrix instanceof SVGMatrix) && this.z === 0 && this.w === 1) {
|
||||
return new DOMPoint(
|
||||
this.x * matrix.a + this.y * matrix.c + matrix.e,
|
||||
this.x * matrix.b + this.y * matrix.d + matrix.f,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
} else {
|
||||
return new DOMPoint(
|
||||
this.x * matrix.m11 + this.y * matrix.m21 + this.z * matrix.m31 + this.w * matrix.m41,
|
||||
this.x * matrix.m12 + this.y * matrix.m22 + this.z * matrix.m32 + this.w * matrix.m42,
|
||||
this.x * matrix.m13 + this.y * matrix.m23 + this.z * matrix.m33 + this.w * matrix.m43,
|
||||
this.x * matrix.m14 + this.y * matrix.m24 + this.z * matrix.m34 + this.w * matrix.m44,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
z: this.z,
|
||||
w: this.w,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @info
|
||||
// DOMRect polyfill
|
||||
// @src
|
||||
// https://drafts.fxtf.org/geometry/#DOMRect
|
||||
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_rect_read_only.cc
|
||||
|
||||
class DOMRect {
|
||||
constructor(x = 0, y = 0, width = 0, height = 0) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
|
||||
static fromRect(otherRect) {
|
||||
return new DOMRect(otherRect.x, otherRect.y, otherRect.width, otherRect.height)
|
||||
}
|
||||
|
||||
get top() {
|
||||
return this.y
|
||||
}
|
||||
|
||||
get left() {
|
||||
return this.x
|
||||
}
|
||||
|
||||
get right() {
|
||||
return this.x + this.width
|
||||
}
|
||||
|
||||
get bottom() {
|
||||
return this.y + this.height
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
top: this.top,
|
||||
left: this.left,
|
||||
right: this.right,
|
||||
bottom: this.bottom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const propertyName of ['top', 'right', 'bottom', 'left']) {
|
||||
const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMRect.prototype, propertyName)
|
||||
propertyDescriptor.enumerable = true
|
||||
Object.defineProperty(DOMRect.prototype, propertyName, propertyDescriptor)
|
||||
}
|
||||
|
||||
// @info
|
||||
// DOMMatrix polyfill (SVG 2)
|
||||
// @src
|
||||
// https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/geometry/dom_matrix_read_only.cc
|
||||
// https://github.com/tocharomera/generativecanvas/blob/master/node-canvas/lib/DOMMatrix.js
|
||||
|
||||
const M11 = 0,
|
||||
M12 = 1,
|
||||
M13 = 2,
|
||||
M14 = 3
|
||||
const M21 = 4,
|
||||
M22 = 5,
|
||||
M23 = 6,
|
||||
M24 = 7
|
||||
const M31 = 8,
|
||||
M32 = 9,
|
||||
M33 = 10,
|
||||
M34 = 11
|
||||
const M41 = 12,
|
||||
M42 = 13,
|
||||
M43 = 14,
|
||||
M44 = 15
|
||||
|
||||
const A = M11,
|
||||
B = M12
|
||||
const C = M21,
|
||||
D = M22
|
||||
const E = M41,
|
||||
F = M42
|
||||
|
||||
const DEGREE_PER_RAD = 180 / Math.PI
|
||||
const RAD_PER_DEGREE = Math.PI / 180
|
||||
|
||||
const VALUES = Symbol('values')
|
||||
const IS_2D = Symbol('is2D')
|
||||
|
||||
function parseMatrix(init) {
|
||||
let parsed = init.replace(/matrix\(/, '').split(/,/, 7)
|
||||
|
||||
if (parsed.length !== 6) {
|
||||
throw new Error(`Failed to parse ${init}`)
|
||||
}
|
||||
|
||||
parsed = parsed.map(parseFloat)
|
||||
|
||||
return [parsed[0], parsed[1], 0, 0, parsed[2], parsed[3], 0, 0, 0, 0, 1, 0, parsed[4], parsed[5], 0, 1]
|
||||
}
|
||||
|
||||
function parseMatrix3d(init) {
|
||||
const parsed = init.replace(/matrix3d\(/, '').split(/,/, 17)
|
||||
|
||||
if (parsed.length !== 16) {
|
||||
throw new Error(`Failed to parse ${init}`)
|
||||
}
|
||||
|
||||
return parsed.map(parseFloat)
|
||||
}
|
||||
|
||||
function parseTransform(tform) {
|
||||
const type = tform.split(/\(/, 1)[0]
|
||||
|
||||
if (type === 'matrix') {
|
||||
return parseMatrix(tform)
|
||||
} else if (type === 'matrix3d') {
|
||||
return parseMatrix3d(tform)
|
||||
} else {
|
||||
throw new Error(`${type} parsing not implemented`)
|
||||
}
|
||||
}
|
||||
|
||||
const setNumber2D = (receiver, index, value) => {
|
||||
if (typeof value !== 'number') {
|
||||
throw new TypeError('Expected number')
|
||||
}
|
||||
|
||||
receiver[VALUES][index] = value
|
||||
}
|
||||
|
||||
const setNumber3D = (receiver, index, value) => {
|
||||
if (typeof value !== 'number') {
|
||||
throw new TypeError('Expected number')
|
||||
}
|
||||
|
||||
if (index === M33 || index === M44) {
|
||||
if (value !== 1) {
|
||||
receiver[IS_2D] = false
|
||||
}
|
||||
} else if (value !== 0) {
|
||||
receiver[IS_2D] = false
|
||||
}
|
||||
|
||||
receiver[VALUES][index] = value
|
||||
}
|
||||
|
||||
const newInstance = (values) => {
|
||||
const instance = Object.create(DOMMatrix.prototype)
|
||||
instance.constructor = DOMMatrix
|
||||
instance[IS_2D] = true
|
||||
instance[VALUES] = values
|
||||
|
||||
return instance
|
||||
}
|
||||
|
||||
const multiply = (first, second) => {
|
||||
const dest = new Float64Array(16)
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
for (let j = 0; j < 4; j++) {
|
||||
let sum = 0
|
||||
|
||||
for (let k = 0; k < 4; k++) {
|
||||
sum += first[i * 4 + k] * second[k * 4 + j]
|
||||
}
|
||||
|
||||
dest[i * 4 + j] = sum
|
||||
}
|
||||
}
|
||||
|
||||
return dest
|
||||
}
|
||||
|
||||
class DOMMatrix {
|
||||
get m11() {
|
||||
return this[VALUES][M11]
|
||||
}
|
||||
set m11(value) {
|
||||
setNumber2D(this, M11, value)
|
||||
}
|
||||
get m12() {
|
||||
return this[VALUES][M12]
|
||||
}
|
||||
set m12(value) {
|
||||
setNumber2D(this, M12, value)
|
||||
}
|
||||
get m13() {
|
||||
return this[VALUES][M13]
|
||||
}
|
||||
set m13(value) {
|
||||
setNumber3D(this, M13, value)
|
||||
}
|
||||
get m14() {
|
||||
return this[VALUES][M14]
|
||||
}
|
||||
set m14(value) {
|
||||
setNumber3D(this, M14, value)
|
||||
}
|
||||
get m21() {
|
||||
return this[VALUES][M21]
|
||||
}
|
||||
set m21(value) {
|
||||
setNumber2D(this, M21, value)
|
||||
}
|
||||
get m22() {
|
||||
return this[VALUES][M22]
|
||||
}
|
||||
set m22(value) {
|
||||
setNumber2D(this, M22, value)
|
||||
}
|
||||
get m23() {
|
||||
return this[VALUES][M23]
|
||||
}
|
||||
set m23(value) {
|
||||
setNumber3D(this, M23, value)
|
||||
}
|
||||
get m24() {
|
||||
return this[VALUES][M24]
|
||||
}
|
||||
set m24(value) {
|
||||
setNumber3D(this, M24, value)
|
||||
}
|
||||
get m31() {
|
||||
return this[VALUES][M31]
|
||||
}
|
||||
set m31(value) {
|
||||
setNumber3D(this, M31, value)
|
||||
}
|
||||
get m32() {
|
||||
return this[VALUES][M32]
|
||||
}
|
||||
set m32(value) {
|
||||
setNumber3D(this, M32, value)
|
||||
}
|
||||
get m33() {
|
||||
return this[VALUES][M33]
|
||||
}
|
||||
set m33(value) {
|
||||
setNumber3D(this, M33, value)
|
||||
}
|
||||
get m34() {
|
||||
return this[VALUES][M34]
|
||||
}
|
||||
set m34(value) {
|
||||
setNumber3D(this, M34, value)
|
||||
}
|
||||
get m41() {
|
||||
return this[VALUES][M41]
|
||||
}
|
||||
set m41(value) {
|
||||
setNumber2D(this, M41, value)
|
||||
}
|
||||
get m42() {
|
||||
return this[VALUES][M42]
|
||||
}
|
||||
set m42(value) {
|
||||
setNumber2D(this, M42, value)
|
||||
}
|
||||
get m43() {
|
||||
return this[VALUES][M43]
|
||||
}
|
||||
set m43(value) {
|
||||
setNumber3D(this, M43, value)
|
||||
}
|
||||
get m44() {
|
||||
return this[VALUES][M44]
|
||||
}
|
||||
set m44(value) {
|
||||
setNumber3D(this, M44, value)
|
||||
}
|
||||
|
||||
get a() {
|
||||
return this[VALUES][A]
|
||||
}
|
||||
set a(value) {
|
||||
setNumber2D(this, A, value)
|
||||
}
|
||||
get b() {
|
||||
return this[VALUES][B]
|
||||
}
|
||||
set b(value) {
|
||||
setNumber2D(this, B, value)
|
||||
}
|
||||
get c() {
|
||||
return this[VALUES][C]
|
||||
}
|
||||
set c(value) {
|
||||
setNumber2D(this, C, value)
|
||||
}
|
||||
get d() {
|
||||
return this[VALUES][D]
|
||||
}
|
||||
set d(value) {
|
||||
setNumber2D(this, D, value)
|
||||
}
|
||||
get e() {
|
||||
return this[VALUES][E]
|
||||
}
|
||||
set e(value) {
|
||||
setNumber2D(this, E, value)
|
||||
}
|
||||
get f() {
|
||||
return this[VALUES][F]
|
||||
}
|
||||
set f(value) {
|
||||
setNumber2D(this, F, value)
|
||||
}
|
||||
|
||||
get is2D() {
|
||||
return this[IS_2D]
|
||||
}
|
||||
|
||||
get isIdentity() {
|
||||
const values = this[VALUES]
|
||||
|
||||
return (
|
||||
values[M11] === 1 &&
|
||||
values[M12] === 0 &&
|
||||
values[M13] === 0 &&
|
||||
values[M14] === 0 &&
|
||||
values[M21] === 0 &&
|
||||
values[M22] === 1 &&
|
||||
values[M23] === 0 &&
|
||||
values[M24] === 0 &&
|
||||
values[M31] === 0 &&
|
||||
values[M32] === 0 &&
|
||||
values[M33] === 1 &&
|
||||
values[M34] === 0 &&
|
||||
values[M41] === 0 &&
|
||||
values[M42] === 0 &&
|
||||
values[M43] === 0 &&
|
||||
values[M44] === 1
|
||||
)
|
||||
}
|
||||
|
||||
static fromMatrix(init) {
|
||||
if (init instanceof DOMMatrix) {
|
||||
return new DOMMatrix(init[VALUES])
|
||||
} else if (init instanceof SVGMatrix) {
|
||||
return new DOMMatrix([init.a, init.b, init.c, init.d, init.e, init.f])
|
||||
} else {
|
||||
throw new TypeError('Expected DOMMatrix')
|
||||
}
|
||||
}
|
||||
|
||||
static fromFloat32Array(init) {
|
||||
if (!(init instanceof Float32Array)) throw new TypeError('Expected Float32Array')
|
||||
return new DOMMatrix(init)
|
||||
}
|
||||
|
||||
static fromFloat64Array(init) {
|
||||
if (!(init instanceof Float64Array)) throw new TypeError('Expected Float64Array')
|
||||
return new DOMMatrix(init)
|
||||
}
|
||||
|
||||
// @type
|
||||
// (Float64Array) => void
|
||||
constructor(init) {
|
||||
this[IS_2D] = true
|
||||
|
||||
this[VALUES] = new Float64Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1])
|
||||
|
||||
// Parse CSS transformList
|
||||
if (typeof init === 'string') {
|
||||
if (init === '') {
|
||||
return
|
||||
} else {
|
||||
const tforms = init.split(/\)\s+/, 20).map(parseTransform)
|
||||
|
||||
if (tforms.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
init = tforms[0]
|
||||
|
||||
for (let i = 1; i < tforms.length; i++) {
|
||||
init = multiply(tforms[i], init)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let i = 0
|
||||
|
||||
if (init && init.length === 6) {
|
||||
setNumber2D(this, A, init[i++])
|
||||
setNumber2D(this, B, init[i++])
|
||||
setNumber2D(this, C, init[i++])
|
||||
setNumber2D(this, D, init[i++])
|
||||
setNumber2D(this, E, init[i++])
|
||||
setNumber2D(this, F, init[i++])
|
||||
} else if (init && init.length === 16) {
|
||||
setNumber2D(this, M11, init[i++])
|
||||
setNumber2D(this, M12, init[i++])
|
||||
setNumber3D(this, M13, init[i++])
|
||||
setNumber3D(this, M14, init[i++])
|
||||
setNumber2D(this, M21, init[i++])
|
||||
setNumber2D(this, M22, init[i++])
|
||||
setNumber3D(this, M23, init[i++])
|
||||
setNumber3D(this, M24, init[i++])
|
||||
setNumber3D(this, M31, init[i++])
|
||||
setNumber3D(this, M32, init[i++])
|
||||
setNumber3D(this, M33, init[i++])
|
||||
setNumber3D(this, M34, init[i++])
|
||||
setNumber2D(this, M41, init[i++])
|
||||
setNumber2D(this, M42, init[i++])
|
||||
setNumber3D(this, M43, init[i++])
|
||||
setNumber3D(this, M44, init[i])
|
||||
} else if (init !== undefined) {
|
||||
throw new TypeError('Expected string or array.')
|
||||
}
|
||||
}
|
||||
|
||||
dump() {
|
||||
const mat = this[VALUES]
|
||||
console.info([mat.slice(0, 4), mat.slice(4, 8), mat.slice(8, 12), mat.slice(12, 16)])
|
||||
}
|
||||
|
||||
[inspect.custom](depth) {
|
||||
if (depth < 0) return '[DOMMatrix]'
|
||||
|
||||
const { a, b, c, d, e, f, is2D, isIdentity } = this
|
||||
if (this.is2D) {
|
||||
return `DOMMatrix ${inspect({ a, b, c, d, e, f, is2D, isIdentity }, { colors: true })}`
|
||||
} else {
|
||||
const { m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, is2D, isIdentity } = this
|
||||
return `DOMMatrix ${inspect(
|
||||
{
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
m11,
|
||||
m12,
|
||||
m13,
|
||||
m14,
|
||||
m21,
|
||||
m22,
|
||||
m23,
|
||||
m24,
|
||||
m31,
|
||||
m32,
|
||||
m33,
|
||||
m34,
|
||||
m41,
|
||||
m42,
|
||||
m43,
|
||||
m44,
|
||||
is2D,
|
||||
isIdentity,
|
||||
},
|
||||
{ colors: true },
|
||||
)}`
|
||||
}
|
||||
}
|
||||
|
||||
multiply(other) {
|
||||
return newInstance(this[VALUES]).multiplySelf(other)
|
||||
}
|
||||
|
||||
multiplySelf(other) {
|
||||
this[VALUES] = multiply(other[VALUES], this[VALUES])
|
||||
|
||||
if (!other.is2D) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
preMultiplySelf(other) {
|
||||
this[VALUES] = multiply(this[VALUES], other[VALUES])
|
||||
|
||||
if (!other.is2D) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
translate(tx, ty, tz) {
|
||||
return newInstance(this[VALUES]).translateSelf(tx, ty, tz)
|
||||
}
|
||||
|
||||
translateSelf(tx = 0, ty = 0, tz = 0) {
|
||||
this[VALUES] = multiply([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, tx, ty, tz, 1], this[VALUES])
|
||||
|
||||
if (tz !== 0) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
scale(scaleX, scaleY, scaleZ, originX, originY, originZ) {
|
||||
return newInstance(this[VALUES]).scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
|
||||
}
|
||||
|
||||
scale3d(scale, originX, originY, originZ) {
|
||||
return newInstance(this[VALUES]).scale3dSelf(scale, originX, originY, originZ)
|
||||
}
|
||||
|
||||
scale3dSelf(scale, originX, originY, originZ) {
|
||||
return this.scaleSelf(scale, scale, scale, originX, originY, originZ)
|
||||
}
|
||||
|
||||
scaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ) {
|
||||
// Not redundant with translate's checks because we need to negate the values later.
|
||||
if (typeof originX !== 'number') originX = 0
|
||||
if (typeof originY !== 'number') originY = 0
|
||||
if (typeof originZ !== 'number') originZ = 0
|
||||
|
||||
this.translateSelf(originX, originY, originZ)
|
||||
|
||||
if (typeof scaleX !== 'number') scaleX = 1
|
||||
if (typeof scaleY !== 'number') scaleY = scaleX
|
||||
if (typeof scaleZ !== 'number') scaleZ = 1
|
||||
|
||||
this[VALUES] = multiply([scaleX, 0, 0, 0, 0, scaleY, 0, 0, 0, 0, scaleZ, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
this.translateSelf(-originX, -originY, -originZ)
|
||||
|
||||
if (scaleZ !== 1 || originZ !== 0) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
rotateFromVector(x, y) {
|
||||
return newInstance(this[VALUES]).rotateFromVectorSelf(x, y)
|
||||
}
|
||||
|
||||
rotateFromVectorSelf(x = 0, y = 0) {
|
||||
const theta = x === 0 && y === 0 ? 0 : Math.atan2(y, x) * DEGREE_PER_RAD
|
||||
return this.rotateSelf(theta)
|
||||
}
|
||||
|
||||
rotate(rotX, rotY, rotZ) {
|
||||
return newInstance(this[VALUES]).rotateSelf(rotX, rotY, rotZ)
|
||||
}
|
||||
|
||||
rotateSelf(rotX, rotY, rotZ) {
|
||||
if (rotY === undefined && rotZ === undefined) {
|
||||
rotZ = rotX
|
||||
rotX = rotY = 0
|
||||
}
|
||||
|
||||
if (typeof rotY !== 'number') rotY = 0
|
||||
if (typeof rotZ !== 'number') rotZ = 0
|
||||
|
||||
if (rotX !== 0 || rotY !== 0) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
rotX *= RAD_PER_DEGREE
|
||||
rotY *= RAD_PER_DEGREE
|
||||
rotZ *= RAD_PER_DEGREE
|
||||
|
||||
let c = Math.cos(rotZ)
|
||||
let s = Math.sin(rotZ)
|
||||
|
||||
this[VALUES] = multiply([c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
c = Math.cos(rotY)
|
||||
s = Math.sin(rotY)
|
||||
|
||||
this[VALUES] = multiply([c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
c = Math.cos(rotX)
|
||||
s = Math.sin(rotX)
|
||||
|
||||
this[VALUES] = multiply([1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
rotateAxisAngle(x, y, z, angle) {
|
||||
return newInstance(this[VALUES]).rotateAxisAngleSelf(x, y, z, angle)
|
||||
}
|
||||
|
||||
rotateAxisAngleSelf(x = 0, y = 0, z = 0, angle = 0) {
|
||||
const length = Math.sqrt(x * x + y * y + z * z)
|
||||
|
||||
if (length === 0) {
|
||||
return this
|
||||
}
|
||||
|
||||
if (length !== 1) {
|
||||
x /= length
|
||||
y /= length
|
||||
z /= length
|
||||
}
|
||||
|
||||
angle *= RAD_PER_DEGREE
|
||||
|
||||
const c = Math.cos(angle)
|
||||
const s = Math.sin(angle)
|
||||
const t = 1 - c
|
||||
const tx = t * x
|
||||
const ty = t * y
|
||||
|
||||
this[VALUES] = multiply(
|
||||
[
|
||||
tx * x + c,
|
||||
tx * y + s * z,
|
||||
tx * z - s * y,
|
||||
0,
|
||||
tx * y - s * z,
|
||||
ty * y + c,
|
||||
ty * z + s * x,
|
||||
0,
|
||||
tx * z + s * y,
|
||||
ty * z - s * x,
|
||||
t * z * z + c,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
],
|
||||
this[VALUES],
|
||||
)
|
||||
|
||||
if (x !== 0 || y !== 0) {
|
||||
this[IS_2D] = false
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
skewX(sx) {
|
||||
return newInstance(this[VALUES]).skewXSelf(sx)
|
||||
}
|
||||
|
||||
skewXSelf(sx) {
|
||||
if (typeof sx !== 'number') {
|
||||
return this
|
||||
}
|
||||
|
||||
const t = Math.tan(sx * RAD_PER_DEGREE)
|
||||
|
||||
this[VALUES] = multiply([1, 0, 0, 0, t, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
skewY(sy) {
|
||||
return newInstance(this[VALUES]).skewYSelf(sy)
|
||||
}
|
||||
|
||||
skewYSelf(sy) {
|
||||
if (typeof sy !== 'number') {
|
||||
return this
|
||||
}
|
||||
|
||||
const t = Math.tan(sy * RAD_PER_DEGREE)
|
||||
|
||||
this[VALUES] = multiply([1, t, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES])
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
flipX() {
|
||||
return newInstance(multiply([-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
|
||||
}
|
||||
|
||||
flipY() {
|
||||
return newInstance(multiply([1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], this[VALUES]))
|
||||
}
|
||||
|
||||
inverse() {
|
||||
return newInstance(this[VALUES].slice()).invertSelf()
|
||||
}
|
||||
|
||||
invertSelf() {
|
||||
if (this[IS_2D]) {
|
||||
const det = this[VALUES][A] * this[VALUES][D] - this[VALUES][B] * this[VALUES][C]
|
||||
|
||||
// Invertable
|
||||
if (det !== 0) {
|
||||
const newA = this[VALUES][D] / det
|
||||
const newB = -this[VALUES][B] / det
|
||||
const newC = -this[VALUES][C] / det
|
||||
const newD = this[VALUES][A] / det
|
||||
const newE = (this[VALUES][C] * this[VALUES][F] - this[VALUES][D] * this[VALUES][E]) / det
|
||||
const newF = (this[VALUES][B] * this[VALUES][E] - this[VALUES][A] * this[VALUES][F]) / det
|
||||
|
||||
this.a = newA
|
||||
this.b = newB
|
||||
this.c = newC
|
||||
this.d = newD
|
||||
this.e = newE
|
||||
this.f = newF
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// Not invertable
|
||||
else {
|
||||
this[IS_2D] = false
|
||||
|
||||
this[VALUES] = [NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN]
|
||||
|
||||
return this
|
||||
}
|
||||
} else {
|
||||
throw new Error('3D matrix inversion is not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
setMatrixValue(transformList) {
|
||||
const temp = new DOMMatrix(transformList)
|
||||
|
||||
this[VALUES] = temp[VALUES]
|
||||
this[IS_2D] = temp[IS_2D]
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
transformPoint(point) {
|
||||
const x = point.x || 0
|
||||
const y = point.y || 0
|
||||
const z = point.z || 0
|
||||
const w = point.w || 1
|
||||
|
||||
const values = this[VALUES]
|
||||
|
||||
const nx = values[M11] * x + values[M21] * y + values[M31] * z + values[M41] * w
|
||||
const ny = values[M12] * x + values[M22] * y + values[M32] * z + values[M42] * w
|
||||
const nz = values[M13] * x + values[M23] * y + values[M33] * z + values[M43] * w
|
||||
const nw = values[M14] * x + values[M24] * y + values[M34] * z + values[M44] * w
|
||||
|
||||
return new DOMPoint(nx, ny, nz, nw)
|
||||
}
|
||||
|
||||
toFloat32Array() {
|
||||
return Float32Array.from(this[VALUES])
|
||||
}
|
||||
|
||||
toFloat64Array() {
|
||||
return this[VALUES].slice(0)
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return {
|
||||
a: this.a,
|
||||
b: this.b,
|
||||
c: this.c,
|
||||
d: this.d,
|
||||
e: this.e,
|
||||
f: this.f,
|
||||
m11: this.m11,
|
||||
m12: this.m12,
|
||||
m13: this.m13,
|
||||
m14: this.m14,
|
||||
m21: this.m21,
|
||||
m22: this.m22,
|
||||
m23: this.m23,
|
||||
m24: this.m24,
|
||||
m31: this.m31,
|
||||
m32: this.m32,
|
||||
m33: this.m33,
|
||||
m34: this.m34,
|
||||
m41: this.m41,
|
||||
m42: this.m42,
|
||||
m43: this.m43,
|
||||
m44: this.m44,
|
||||
is2D: this.is2D,
|
||||
isIdentity: this.isIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
if (this.is2D) {
|
||||
return `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.e}, ${this.f})`
|
||||
} else {
|
||||
return `matrix3d(${this[VALUES].join(', ')})`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const propertyName of [
|
||||
'a',
|
||||
'b',
|
||||
'c',
|
||||
'd',
|
||||
'e',
|
||||
'f',
|
||||
'm11',
|
||||
'm12',
|
||||
'm13',
|
||||
'm14',
|
||||
'm21',
|
||||
'm22',
|
||||
'm23',
|
||||
'm24',
|
||||
'm31',
|
||||
'm32',
|
||||
'm33',
|
||||
'm34',
|
||||
'm41',
|
||||
'm42',
|
||||
'm43',
|
||||
'm44',
|
||||
'is2D',
|
||||
'isIdentity',
|
||||
]) {
|
||||
const propertyDescriptor = Object.getOwnPropertyDescriptor(DOMMatrix.prototype, propertyName)
|
||||
propertyDescriptor.enumerable = true
|
||||
Object.defineProperty(DOMMatrix.prototype, propertyName, propertyDescriptor)
|
||||
}
|
||||
|
||||
module.exports = { DOMPoint, DOMMatrix, DOMRect }
|
||||
733
node_modules/@napi-rs/canvas/index.d.ts
generated
vendored
Normal file
733
node_modules/@napi-rs/canvas/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,733 @@
|
||||
import { ReadableStream } from 'node:stream/web'
|
||||
|
||||
// Clear all type of caches in Skia
|
||||
export function clearAllCache(): void
|
||||
|
||||
interface CanvasRenderingContext2D
|
||||
extends CanvasCompositing,
|
||||
CanvasDrawPath,
|
||||
CanvasFillStrokeStyles,
|
||||
CanvasFilters,
|
||||
CanvasImageData,
|
||||
CanvasImageSmoothing,
|
||||
CanvasPath,
|
||||
CanvasPathDrawingStyles,
|
||||
CanvasRect,
|
||||
CanvasSettings,
|
||||
CanvasShadowStyles,
|
||||
CanvasState,
|
||||
CanvasText,
|
||||
CanvasTextDrawingStyles,
|
||||
CanvasTransform {}
|
||||
|
||||
interface CanvasState {
|
||||
isContextLost(): boolean
|
||||
reset(): void
|
||||
restore(): void
|
||||
save(): void
|
||||
}
|
||||
interface CanvasShadowStyles {
|
||||
shadowBlur: number
|
||||
shadowColor: string
|
||||
shadowOffsetX: number
|
||||
shadowOffsetY: number
|
||||
}
|
||||
interface CanvasRenderingContext2DSettings {
|
||||
alpha?: boolean
|
||||
colorSpace?: PredefinedColorSpace
|
||||
desynchronized?: boolean
|
||||
willReadFrequently?: boolean
|
||||
}
|
||||
interface CanvasSettings {
|
||||
getContextAttributes(): CanvasRenderingContext2DSettings
|
||||
}
|
||||
|
||||
interface CanvasRect {
|
||||
clearRect(x: number, y: number, w: number, h: number): void
|
||||
fillRect(x: number, y: number, w: number, h: number): void
|
||||
strokeRect(x: number, y: number, w: number, h: number): void
|
||||
}
|
||||
|
||||
interface TextMetrics {
|
||||
readonly actualBoundingBoxAscent: number
|
||||
readonly actualBoundingBoxDescent: number
|
||||
readonly actualBoundingBoxLeft: number
|
||||
readonly actualBoundingBoxRight: number
|
||||
readonly alphabeticBaseline: number
|
||||
readonly emHeightAscent: number
|
||||
readonly emHeightDescent: number
|
||||
readonly fontBoundingBoxAscent: number
|
||||
readonly fontBoundingBoxDescent: number
|
||||
readonly hangingBaseline: number
|
||||
readonly ideographicBaseline: number
|
||||
readonly width: number
|
||||
}
|
||||
|
||||
interface CanvasText {
|
||||
fillText(text: string, x: number, y: number, maxWidth?: number): void
|
||||
measureText(text: string): TextMetrics
|
||||
strokeText(text: string, x: number, y: number, maxWidth?: number): void
|
||||
}
|
||||
|
||||
type CanvasLineCap = 'butt' | 'round' | 'square'
|
||||
type CanvasLineJoin = 'bevel' | 'miter' | 'round'
|
||||
|
||||
interface CanvasPathDrawingStyles {
|
||||
lineCap: CanvasLineCap
|
||||
lineDashOffset: number
|
||||
lineJoin: CanvasLineJoin
|
||||
lineWidth: number
|
||||
miterLimit: number
|
||||
getLineDash(): number[]
|
||||
setLineDash(segments: number[]): void
|
||||
}
|
||||
|
||||
interface CanvasPath {
|
||||
arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: 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,
|
||||
counterclockwise?: 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
|
||||
roundRect(x: number, y: number, w: number, h: number, radii?: number | DOMPointInit | (number | DOMPointInit)[]): void
|
||||
}
|
||||
|
||||
type ImageSmoothingQuality = 'high' | 'low' | 'medium'
|
||||
|
||||
interface CanvasImageSmoothing {
|
||||
imageSmoothingEnabled: boolean
|
||||
imageSmoothingQuality: ImageSmoothingQuality
|
||||
}
|
||||
|
||||
interface CanvasTransform {
|
||||
resetTransform(): void
|
||||
rotate(angle: number): void
|
||||
scale(x: number, y: number): void
|
||||
setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void
|
||||
setTransform(transform?: DOMMatrix2DInit): void
|
||||
transform(a: number, b: number, c: number, d: number, e: number, f: number): void
|
||||
translate(x: number, y: number): void
|
||||
}
|
||||
type PredefinedColorSpace = 'display-p3' | 'srgb'
|
||||
|
||||
interface ImageDataSettings {
|
||||
colorSpace?: PredefinedColorSpace
|
||||
}
|
||||
interface CanvasImageData {
|
||||
createImageData(sw: number, sh: number, settings?: ImageDataSettings): ImageData
|
||||
createImageData(imagedata: ImageData): ImageData
|
||||
getImageData(sx: number, sy: number, sw: number, sh: number, settings?: ImageDataSettings): ImageData
|
||||
putImageData(imagedata: ImageData, dx: number, dy: number): void
|
||||
putImageData(
|
||||
imagedata: ImageData,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dirtyX: number,
|
||||
dirtyY: number,
|
||||
dirtyWidth: number,
|
||||
dirtyHeight: number,
|
||||
): void
|
||||
}
|
||||
|
||||
type CanvasDirection = 'inherit' | 'ltr' | 'rtl'
|
||||
type CanvasFontKerning = 'auto' | 'none' | 'normal'
|
||||
type CanvasFontStretch =
|
||||
| 'condensed'
|
||||
| 'expanded'
|
||||
| 'extra-condensed'
|
||||
| 'extra-expanded'
|
||||
| 'normal'
|
||||
| 'semi-condensed'
|
||||
| 'semi-expanded'
|
||||
| 'ultra-condensed'
|
||||
| 'ultra-expanded'
|
||||
type CanvasFontVariantCaps =
|
||||
| 'all-petite-caps'
|
||||
| 'all-small-caps'
|
||||
| 'normal'
|
||||
| 'petite-caps'
|
||||
| 'small-caps'
|
||||
| 'titling-caps'
|
||||
| 'unicase'
|
||||
type CanvasTextAlign = 'center' | 'end' | 'left' | 'right' | 'start'
|
||||
type CanvasTextBaseline = 'alphabetic' | 'bottom' | 'hanging' | 'ideographic' | 'middle' | 'top'
|
||||
type CanvasTextRendering = 'auto' | 'geometricPrecision' | 'optimizeLegibility' | 'optimizeSpeed'
|
||||
|
||||
interface CanvasTextDrawingStyles {
|
||||
direction: CanvasDirection
|
||||
font: string
|
||||
fontKerning: CanvasFontKerning
|
||||
fontStretch: CanvasFontStretch
|
||||
fontVariantCaps: CanvasFontVariantCaps
|
||||
letterSpacing: string
|
||||
textAlign: CanvasTextAlign
|
||||
textBaseline: CanvasTextBaseline
|
||||
textRendering: CanvasTextRendering
|
||||
wordSpacing: string
|
||||
}
|
||||
|
||||
interface CanvasFilters {
|
||||
filter: string
|
||||
}
|
||||
|
||||
interface CanvasFillStrokeStyles {
|
||||
fillStyle: string | CanvasGradient | CanvasPattern
|
||||
strokeStyle: string | CanvasGradient | CanvasPattern
|
||||
createConicGradient(startAngle: number, x: number, y: number): CanvasGradient
|
||||
createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient
|
||||
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient
|
||||
}
|
||||
|
||||
type CanvasFillRule = 'evenodd' | 'nonzero'
|
||||
|
||||
interface CanvasDrawPath {
|
||||
beginPath(): void
|
||||
clip(fillRule?: CanvasFillRule): void
|
||||
clip(path: Path2D, fillRule?: CanvasFillRule): void
|
||||
fill(fillRule?: CanvasFillRule): void
|
||||
fill(path: Path2D, fillRule?: CanvasFillRule): void
|
||||
isPointInPath(x: number, y: number, fillRule?: CanvasFillRule): boolean
|
||||
isPointInPath(path: Path2D, x: number, y: number, fillRule?: CanvasFillRule): boolean
|
||||
isPointInStroke(x: number, y: number): boolean
|
||||
isPointInStroke(path: Path2D, x: number, y: number): boolean
|
||||
stroke(): void
|
||||
stroke(path: Path2D): void
|
||||
}
|
||||
|
||||
type GlobalCompositeOperation =
|
||||
| 'color'
|
||||
| 'color-burn'
|
||||
| 'color-dodge'
|
||||
| 'copy'
|
||||
| 'darken'
|
||||
| 'destination-atop'
|
||||
| 'destination-in'
|
||||
| 'destination-out'
|
||||
| 'destination-over'
|
||||
| 'difference'
|
||||
| 'exclusion'
|
||||
| 'hard-light'
|
||||
| 'hue'
|
||||
| 'lighten'
|
||||
| 'lighter'
|
||||
| 'luminosity'
|
||||
| 'multiply'
|
||||
| 'overlay'
|
||||
| 'saturation'
|
||||
| 'screen'
|
||||
| 'soft-light'
|
||||
| 'source-atop'
|
||||
| 'source-in'
|
||||
| 'source-out'
|
||||
| 'source-over'
|
||||
| 'xor'
|
||||
|
||||
interface CanvasCompositing {
|
||||
globalAlpha: number
|
||||
globalCompositeOperation: GlobalCompositeOperation
|
||||
}
|
||||
|
||||
interface DOMPointInit {
|
||||
w?: number
|
||||
x?: number
|
||||
y?: number
|
||||
z?: number
|
||||
}
|
||||
interface CanvasPattern {
|
||||
setTransform(transform?: DOMMatrix2DInit): void
|
||||
}
|
||||
|
||||
interface CanvasGradient {
|
||||
addColorStop(offset: number, color: string): void
|
||||
}
|
||||
|
||||
interface DOMRectInit {
|
||||
height?: number
|
||||
width?: number
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
|
||||
interface DOMMatrixInit extends DOMMatrix2DInit {
|
||||
is2D?: boolean
|
||||
m13?: number
|
||||
m14?: number
|
||||
m23?: number
|
||||
m24?: number
|
||||
m31?: number
|
||||
m32?: number
|
||||
m33?: number
|
||||
m34?: number
|
||||
m43?: number
|
||||
m44?: number
|
||||
}
|
||||
|
||||
// ----------- added types
|
||||
|
||||
export interface DOMMatrix2DInit {
|
||||
a: number
|
||||
b: number
|
||||
c: number
|
||||
d: number
|
||||
e: number
|
||||
f: number
|
||||
}
|
||||
|
||||
interface DOMMatrixReadOnly {
|
||||
readonly a: number
|
||||
readonly b: number
|
||||
readonly c: number
|
||||
readonly d: number
|
||||
readonly e: number
|
||||
readonly f: number
|
||||
readonly is2D: boolean
|
||||
readonly isIdentity: boolean
|
||||
readonly m11: number
|
||||
readonly m12: number
|
||||
readonly m13: number
|
||||
readonly m14: number
|
||||
readonly m21: number
|
||||
readonly m22: number
|
||||
readonly m23: number
|
||||
readonly m24: number
|
||||
readonly m31: number
|
||||
readonly m32: number
|
||||
readonly m33: number
|
||||
readonly m34: number
|
||||
readonly m41: number
|
||||
readonly m42: number
|
||||
readonly m43: number
|
||||
readonly m44: number
|
||||
flipX(): DOMMatrix
|
||||
flipY(): DOMMatrix
|
||||
inverse(): DOMMatrix
|
||||
multiply(other?: DOMMatrixInit): DOMMatrix
|
||||
rotate(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix
|
||||
rotateAxisAngle(x?: number, y?: number, z?: number, angle?: number): DOMMatrix
|
||||
rotateFromVector(x?: number, y?: number): DOMMatrix
|
||||
scale(
|
||||
scaleX?: number,
|
||||
scaleY?: number,
|
||||
scaleZ?: number,
|
||||
originX?: number,
|
||||
originY?: number,
|
||||
originZ?: number,
|
||||
): DOMMatrix
|
||||
scale3d(scale?: number, originX?: number, originY?: number, originZ?: number): DOMMatrix
|
||||
skewX(sx?: number): DOMMatrix
|
||||
skewY(sy?: number): DOMMatrix
|
||||
toFloat32Array(): Float32Array
|
||||
toFloat64Array(): Float64Array
|
||||
transformPoint(point?: DOMPointInit): DOMPoint
|
||||
translate(tx?: number, ty?: number, tz?: number): DOMMatrix
|
||||
toString(): string
|
||||
}
|
||||
|
||||
export interface DOMMatrix extends DOMMatrixReadOnly {
|
||||
a: number
|
||||
b: number
|
||||
c: number
|
||||
d: number
|
||||
e: number
|
||||
f: number
|
||||
m11: number
|
||||
m12: number
|
||||
m13: number
|
||||
m14: number
|
||||
m21: number
|
||||
m22: number
|
||||
m23: number
|
||||
m24: number
|
||||
m31: number
|
||||
m32: number
|
||||
m33: number
|
||||
m34: number
|
||||
m41: number
|
||||
m42: number
|
||||
m43: number
|
||||
m44: number
|
||||
invertSelf(): DOMMatrix
|
||||
multiplySelf(other?: DOMMatrixInit): DOMMatrix
|
||||
preMultiplySelf(other?: DOMMatrixInit): DOMMatrix
|
||||
rotateAxisAngleSelf(x?: number, y?: number, z?: number, angle?: number): DOMMatrix
|
||||
rotateFromVectorSelf(x?: number, y?: number): DOMMatrix
|
||||
rotateSelf(rotX?: number, rotY?: number, rotZ?: number): DOMMatrix
|
||||
scale3dSelf(scale?: number, originX?: number, originY?: number, originZ?: number): DOMMatrix
|
||||
scaleSelf(
|
||||
scaleX?: number,
|
||||
scaleY?: number,
|
||||
scaleZ?: number,
|
||||
originX?: number,
|
||||
originY?: number,
|
||||
originZ?: number,
|
||||
): DOMMatrix
|
||||
setMatrixValue(transformList: string): DOMMatrix
|
||||
skewXSelf(sx?: number): DOMMatrix
|
||||
skewYSelf(sy?: number): DOMMatrix
|
||||
translateSelf(tx?: number, ty?: number, tz?: number): DOMMatrix
|
||||
toJSON(): { [K in OmitNeverOfMatrix]: DOMMatrix[K] }
|
||||
}
|
||||
|
||||
type OmitMatrixMethod = { [K in keyof DOMMatrix]: DOMMatrix[K] extends (...args: any[]) => any ? never : K }
|
||||
|
||||
type OmitNeverOfMatrix = OmitMatrixMethod[keyof OmitMatrixMethod]
|
||||
|
||||
export const DOMMatrix: {
|
||||
prototype: DOMMatrix
|
||||
new (init?: string | number[]): DOMMatrix
|
||||
fromFloat32Array(array32: Float32Array): DOMMatrix
|
||||
fromFloat64Array(array64: Float64Array): DOMMatrix
|
||||
fromMatrix(other?: DOMMatrixInit): DOMMatrix
|
||||
}
|
||||
|
||||
interface DOMRectReadOnly {
|
||||
readonly bottom: number
|
||||
readonly height: number
|
||||
readonly left: number
|
||||
readonly right: number
|
||||
readonly top: number
|
||||
readonly width: number
|
||||
readonly x: number
|
||||
readonly y: number
|
||||
}
|
||||
|
||||
export interface DOMRect extends DOMRectReadOnly {
|
||||
height: number
|
||||
width: number
|
||||
x: number
|
||||
y: number
|
||||
toJSON(): Omit<this, 'toJSON' | 'fromRect'>
|
||||
}
|
||||
|
||||
export const DOMRect: {
|
||||
prototype: DOMRect
|
||||
new (x?: number, y?: number, width?: number, height?: number): DOMRect
|
||||
fromRect(other?: DOMRectInit): DOMRect
|
||||
}
|
||||
|
||||
interface DOMPointReadOnly {
|
||||
readonly w: number
|
||||
readonly x: number
|
||||
readonly y: number
|
||||
readonly z: number
|
||||
matrixTransform(matrix?: DOMMatrixInit): DOMPoint
|
||||
}
|
||||
|
||||
export interface DOMPoint extends DOMPointReadOnly {
|
||||
w: number
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
toJSON(): Omit<DOMPoint, 'matrixTransform' | 'toJSON'>
|
||||
}
|
||||
|
||||
export const DOMPoint: {
|
||||
prototype: DOMPoint
|
||||
new (x?: number, y?: number, z?: number, w?: number): DOMPoint
|
||||
fromPoint(other?: DOMPointInit): DOMPoint
|
||||
}
|
||||
|
||||
export class ImageData {
|
||||
/**
|
||||
* Returns the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
|
||||
*/
|
||||
readonly data: Uint8ClampedArray
|
||||
/**
|
||||
* Returns the actual dimensions of the data in the ImageData object, in pixels.
|
||||
*/
|
||||
readonly height: number
|
||||
/**
|
||||
* Returns the actual dimensions of the data in the ImageData object, in pixels.
|
||||
*/
|
||||
readonly width: number
|
||||
|
||||
constructor(sw: number, sh: number, attr?: { colorSpace?: ColorSpace })
|
||||
constructor(imageData: ImageData, attr?: { colorSpace?: ColorSpace })
|
||||
constructor(data: Uint8ClampedArray, sw: number, sh?: number)
|
||||
}
|
||||
|
||||
export class Image {
|
||||
constructor()
|
||||
// attrs only affects SVG
|
||||
constructor(width: number, height: number, attrs?: { colorSpace?: ColorSpace })
|
||||
width: number
|
||||
height: number
|
||||
readonly naturalWidth: number
|
||||
readonly naturalHeight: number
|
||||
readonly complete: boolean
|
||||
alt: string
|
||||
// the src can be a Uint8Array or a string
|
||||
// if it's a string, it can be a file path, a data URL, a remote URL, or a SVG string
|
||||
src: Uint8Array | string
|
||||
onload?(): void
|
||||
onerror?(err: Error): void
|
||||
}
|
||||
|
||||
export class Path2D {
|
||||
constructor(path?: Path2D | string)
|
||||
|
||||
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
|
||||
roundRect(x: number, y: number, w: number, h: number, radii?: number | number[]): void
|
||||
|
||||
// PathKit methods
|
||||
op(path: Path2D, operation: PathOp): Path2D
|
||||
toSVGString(): string
|
||||
getFillType(): FillType
|
||||
getFillTypeString(): string
|
||||
setFillType(type: FillType): void
|
||||
simplify(): Path2D
|
||||
asWinding(): Path2D
|
||||
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
|
||||
dash(on: number, off: number, phase: number): Path2D
|
||||
round(radius: number): Path2D
|
||||
equals(path: Path2D): boolean
|
||||
}
|
||||
|
||||
export interface StrokeOptions {
|
||||
width?: number
|
||||
miterLimit?: number
|
||||
cap?: StrokeCap
|
||||
join?: StrokeJoin
|
||||
}
|
||||
|
||||
export interface SKRSContext2D extends CanvasRenderingContext2D {
|
||||
canvas: Canvas
|
||||
/**
|
||||
* @param startAngle The angle at which to begin the gradient, in radians. Angle measurements start vertically above the centre and move around clockwise.
|
||||
* @param x The x-axis coordinate of the centre of the gradient.
|
||||
* @param y The y-axis coordinate of the centre of the gradient.
|
||||
*/
|
||||
createConicGradient(startAngle: number, x: number, y: number): CanvasGradient
|
||||
drawImage(image: Image | Canvas, dx: number, dy: number): void
|
||||
drawImage(image: Image | Canvas, dx: number, dy: number, dw: number, dh: number): void
|
||||
drawImage(
|
||||
image: Image | Canvas,
|
||||
sx: number,
|
||||
sy: number,
|
||||
sw: number,
|
||||
sh: number,
|
||||
dx: number,
|
||||
dy: number,
|
||||
dw: number,
|
||||
dh: number,
|
||||
): void
|
||||
createPattern(
|
||||
image: Image | ImageData | Canvas | SvgCanvas,
|
||||
repeat: 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat' | null,
|
||||
): CanvasPattern
|
||||
getContextAttributes(): { alpha: boolean; desynchronized: boolean }
|
||||
getTransform(): DOMMatrix
|
||||
|
||||
letterSpacing: string
|
||||
wordSpacing: string
|
||||
}
|
||||
|
||||
export type ColorSpace = 'srgb' | 'display-p3'
|
||||
|
||||
export interface ContextAttributes {
|
||||
alpha?: boolean
|
||||
colorSpace?: ColorSpace
|
||||
}
|
||||
|
||||
export interface SvgCanvas {
|
||||
width: number
|
||||
height: number
|
||||
getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D
|
||||
|
||||
getContent(): Buffer
|
||||
}
|
||||
|
||||
export interface AvifConfig {
|
||||
/** 0-100 scale, 100 is lossless */
|
||||
quality?: number
|
||||
/** 0-100 scale */
|
||||
alphaQuality?: number
|
||||
/** rav1e preset 1 (slow) 10 (fast but crappy), default is 4 */
|
||||
speed?: number
|
||||
/** How many threads should be used (0 = match core count) */
|
||||
threads?: number
|
||||
/** set to '4:2:0' to use chroma subsampling, default '4:4:4' */
|
||||
chromaSubsampling?: ChromaSubsampling
|
||||
}
|
||||
/**
|
||||
* https://en.wikipedia.org/wiki/Chroma_subsampling#Types_of_sampling_and_subsampling
|
||||
* https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_concepts
|
||||
*/
|
||||
export enum ChromaSubsampling {
|
||||
/**
|
||||
* Each of the three Y'CbCr components has the same sample rate, thus there is no chroma subsampling. This scheme is sometimes used in high-end film scanners and cinematic post-production.
|
||||
* Note that "4:4:4" may instead be wrongly referring to R'G'B' color space, which implicitly also does not have any chroma subsampling (except in JPEG R'G'B' can be subsampled).
|
||||
* Formats such as HDCAM SR can record 4:4:4 R'G'B' over dual-link HD-SDI.
|
||||
*/
|
||||
Yuv444 = 0,
|
||||
/**
|
||||
* The two chroma components are sampled at half the horizontal sample rate of luma: the horizontal chroma resolution is halved. This reduces the bandwidth of an uncompressed video signal by one-third.
|
||||
* Many high-end digital video formats and interfaces use this scheme:
|
||||
* - [AVC-Intra 100](https://en.wikipedia.org/wiki/AVC-Intra)
|
||||
* - [Digital Betacam](https://en.wikipedia.org/wiki/Betacam#Digital_Betacam)
|
||||
* - [Betacam SX](https://en.wikipedia.org/wiki/Betacam#Betacam_SX)
|
||||
* - [DVCPRO50](https://en.wikipedia.org/wiki/DV#DVCPRO) and [DVCPRO HD](https://en.wikipedia.org/wiki/DV#DVCPRO_HD)
|
||||
* - [Digital-S](https://en.wikipedia.org/wiki/Digital-S)
|
||||
* - [CCIR 601](https://en.wikipedia.org/wiki/Rec._601) / [Serial Digital Interface](https://en.wikipedia.org/wiki/Serial_digital_interface) / [D1](https://en.wikipedia.org/wiki/D-1_(Sony))
|
||||
* - [ProRes (HQ, 422, LT, and Proxy)](https://en.wikipedia.org/wiki/Apple_ProRes)
|
||||
* - [XDCAM HD422](https://en.wikipedia.org/wiki/XDCAM)
|
||||
* - [Canon MXF HD422](https://en.wikipedia.org/wiki/Canon_XF-300)
|
||||
*/
|
||||
Yuv422 = 1,
|
||||
/**
|
||||
* n 4:2:0, the horizontal sampling is doubled compared to 4:1:1,
|
||||
* but as the **Cb** and **Cr** channels are only sampled on each alternate line in this scheme, the vertical resolution is halved.
|
||||
* The data rate is thus the same.
|
||||
* This fits reasonably well with the PAL color encoding system, since this has only half the vertical chrominance resolution of [NTSC](https://en.wikipedia.org/wiki/NTSC).
|
||||
* It would also fit extremely well with the [SECAM](https://en.wikipedia.org/wiki/SECAM) color encoding system,
|
||||
* since like that format, 4:2:0 only stores and transmits one color channel per line (the other channel being recovered from the previous line).
|
||||
* However, little equipment has actually been produced that outputs a SECAM analogue video signal.
|
||||
* In general, SECAM territories either have to use a PAL-capable display or a [transcoder](https://en.wikipedia.org/wiki/Transcoding) to convert the PAL signal to SECAM for display.
|
||||
*/
|
||||
Yuv420 = 2,
|
||||
/**
|
||||
* What if the chroma subsampling model is 4:0:0?
|
||||
* That says to use every pixel of luma data, but that each row has 0 chroma samples applied to it. The resulting image, then, is comprised solely of the luminance data—a greyscale image.
|
||||
*/
|
||||
Yuv400 = 3,
|
||||
}
|
||||
|
||||
export interface ConvertToBlobOptions {
|
||||
mime?: string
|
||||
quality?: number
|
||||
}
|
||||
|
||||
export class Canvas {
|
||||
constructor(width: number, height: number, flag?: SvgExportFlag)
|
||||
|
||||
width: number
|
||||
height: number
|
||||
getContext(contextType: '2d', contextAttributes?: ContextAttributes): SKRSContext2D
|
||||
encodeSync(format: 'webp' | 'jpeg', quality?: number): Buffer
|
||||
encodeSync(format: 'png'): Buffer
|
||||
encodeSync(format: 'avif', cfg?: AvifConfig): Buffer
|
||||
encode(format: 'webp' | 'jpeg', quality?: number): Promise<Buffer>
|
||||
encode(format: 'png'): Promise<Buffer>
|
||||
encode(format: 'avif', cfg?: AvifConfig): Promise<Buffer>
|
||||
encodeStream(format: 'webp' | 'jpeg', quality?: number): ReadableStream<Buffer>
|
||||
encodeStream(format: 'png'): ReadableStream<Buffer>
|
||||
toBuffer(mime: 'image/png'): Buffer
|
||||
toBuffer(mime: 'image/jpeg' | 'image/webp', quality?: number): Buffer
|
||||
toBuffer(mime: 'image/avif', cfg?: AvifConfig): Buffer
|
||||
// raw pixels
|
||||
data(): Buffer
|
||||
toDataURL(mime?: 'image/png'): string
|
||||
toDataURL(mime: 'image/jpeg' | 'image/webp', quality?: number): string
|
||||
toDataURL(mime?: 'image/jpeg' | 'image/webp' | 'image/png', quality?: number): string
|
||||
toDataURL(mime?: 'image/avif', cfg?: AvifConfig): string
|
||||
|
||||
toDataURLAsync(mime?: 'image/png'): Promise<string>
|
||||
toDataURLAsync(mime: 'image/jpeg' | 'image/webp', quality?: number): Promise<string>
|
||||
toDataURLAsync(mime?: 'image/jpeg' | 'image/webp' | 'image/png', quality?: number): Promise<string>
|
||||
toDataURLAsync(mime?: 'image/avif', cfg?: AvifConfig): Promise<string>
|
||||
|
||||
toBlob(callback: (blob: Blob | null) => void, mime?: string, quality?: number): void
|
||||
convertToBlob(options?: ConvertToBlobOptions): Promise<Blob>
|
||||
}
|
||||
|
||||
export function createCanvas(width: number, height: number): Canvas
|
||||
|
||||
export function createCanvas(width: number, height: number, svgExportFlag: SvgExportFlag): SvgCanvas
|
||||
|
||||
export declare class FontKey {
|
||||
// make it a unique type
|
||||
private readonly key: symbol
|
||||
}
|
||||
|
||||
interface IGlobalFonts {
|
||||
readonly families: { family: string; styles: { weight: number; width: string; style: string }[] }[]
|
||||
// return true if succeeded
|
||||
register(font: Buffer, nameAlias?: string): FontKey | null
|
||||
// absolute path
|
||||
registerFromPath(path: string, nameAlias?: string): boolean
|
||||
has(name: string): boolean
|
||||
loadFontsFromDir(path: string): number
|
||||
remove(key: FontKey): void
|
||||
}
|
||||
|
||||
export const GlobalFonts: IGlobalFonts
|
||||
|
||||
export enum PathOp {
|
||||
Difference = 0, // subtract the op path from the first path
|
||||
Intersect = 1, // intersect the two paths
|
||||
Union = 2, // union (inclusive-or) the two paths
|
||||
Xor = 3, // exclusive-or the two paths
|
||||
ReverseDifference = 4, // subtract the first path from the op path
|
||||
}
|
||||
|
||||
export enum FillType {
|
||||
Winding = 0,
|
||||
EvenOdd = 1,
|
||||
InverseWinding = 2,
|
||||
InverseEvenOdd = 3,
|
||||
}
|
||||
|
||||
export enum StrokeJoin {
|
||||
Miter = 0,
|
||||
Round = 1,
|
||||
Bevel = 2,
|
||||
}
|
||||
|
||||
export enum StrokeCap {
|
||||
Butt = 0,
|
||||
Round = 1,
|
||||
Square = 2,
|
||||
}
|
||||
|
||||
export enum SvgExportFlag {
|
||||
ConvertTextToPaths = 0x01,
|
||||
NoPrettyXML = 0x02,
|
||||
RelativePathEncoding = 0x04,
|
||||
}
|
||||
|
||||
export function convertSVGTextToPath(svg: Buffer | string): Buffer
|
||||
|
||||
export interface LoadImageOptions {
|
||||
alt?: string
|
||||
maxRedirects?: number
|
||||
requestOptions?: import('http').RequestOptions
|
||||
}
|
||||
|
||||
export function loadImage(
|
||||
source: string | URL | Buffer | ArrayBufferLike | Uint8Array | Image | import('stream').Readable,
|
||||
options?: LoadImageOptions,
|
||||
): Promise<Image>
|
||||
179
node_modules/@napi-rs/canvas/index.js
generated
vendored
Normal file
179
node_modules/@napi-rs/canvas/index.js
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
const { platform, homedir } = require('os')
|
||||
const { join } = require('path')
|
||||
|
||||
const {
|
||||
clearAllCache,
|
||||
CanvasRenderingContext2D,
|
||||
CanvasElement,
|
||||
SVGCanvas,
|
||||
Path: Path2D,
|
||||
ImageData,
|
||||
Image,
|
||||
FontKey,
|
||||
GlobalFonts,
|
||||
PathOp,
|
||||
FillType,
|
||||
StrokeJoin,
|
||||
StrokeCap,
|
||||
convertSVGTextToPath,
|
||||
} = require('./js-binding')
|
||||
|
||||
const { DOMPoint, DOMMatrix, DOMRect } = require('./geometry')
|
||||
|
||||
const loadImage = require('./load-image')
|
||||
|
||||
const SvgExportFlag = {
|
||||
ConvertTextToPaths: 0x01,
|
||||
NoPrettyXML: 0x02,
|
||||
RelativePathEncoding: 0x04,
|
||||
}
|
||||
|
||||
if (!('families' in GlobalFonts)) {
|
||||
Object.defineProperty(GlobalFonts, 'families', {
|
||||
get: function () {
|
||||
return JSON.parse(GlobalFonts.getFamilies().toString())
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!('has' in GlobalFonts)) {
|
||||
Object.defineProperty(GlobalFonts, 'has', {
|
||||
value: function has(name) {
|
||||
return !!JSON.parse(GlobalFonts.getFamilies().toString()).find(({ family }) => family === name)
|
||||
},
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
})
|
||||
}
|
||||
|
||||
const _toBlob = CanvasElement.prototype.toBlob
|
||||
const _convertToBlob = CanvasElement.prototype.convertToBlob
|
||||
if ('Blob' in globalThis) {
|
||||
CanvasElement.prototype.toBlob = function toBlob(callback, mimeType, quality) {
|
||||
_toBlob.call(
|
||||
this,
|
||||
function (/** @type {Uint8Array} */ imageBuffer) {
|
||||
const blob = new Blob([imageBuffer.buffer], { type: mimeType })
|
||||
callback(blob)
|
||||
},
|
||||
mimeType,
|
||||
quality,
|
||||
)
|
||||
}
|
||||
CanvasElement.prototype.convertToBlob = function convertToBlob(options) {
|
||||
return _convertToBlob.call(this, options).then((/** @type {Uint8Array} */ imageBuffer) => {
|
||||
const blob = new Blob([imageBuffer.buffer], { type: options?.mime || 'image/png' })
|
||||
return blob
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// oxlint-disable-next-line no-unused-vars
|
||||
CanvasElement.prototype.toBlob = function toBlob(callback, mimeType, quality) {
|
||||
callback(null)
|
||||
}
|
||||
// oxlint-disable-next-line no-unused-vars
|
||||
CanvasElement.prototype.convertToBlob = function convertToBlob(options) {
|
||||
return Promise.reject(new Error('Blob is not supported in this environment'))
|
||||
}
|
||||
}
|
||||
|
||||
const _getTransform = CanvasRenderingContext2D.prototype.getTransform
|
||||
|
||||
CanvasRenderingContext2D.prototype.getTransform = function getTransform() {
|
||||
const transform = _getTransform.apply(this, arguments)
|
||||
// monkey patched, skip
|
||||
if (transform instanceof DOMMatrix) {
|
||||
return transform
|
||||
}
|
||||
const { a, b, c, d, e, f } = transform
|
||||
return new DOMMatrix([a, b, c, d, e, f])
|
||||
}
|
||||
|
||||
// Workaround for webpack bundling issue with drawImage
|
||||
// Store the original drawImage method
|
||||
const _drawImage = CanvasRenderingContext2D.prototype.drawImage
|
||||
|
||||
// Override drawImage to ensure proper type recognition in bundled environments
|
||||
CanvasRenderingContext2D.prototype.drawImage = function drawImage(image, ...args) {
|
||||
// If the image is a Canvas-like object but not recognized due to bundling,
|
||||
// we need to ensure it's properly identified
|
||||
if (image && typeof image === 'object') {
|
||||
// First check if it's a wrapped canvas object
|
||||
if (image.canvas instanceof CanvasElement || image.canvas instanceof SVGCanvas) {
|
||||
image = image.canvas
|
||||
} else if (image._canvas instanceof CanvasElement || image._canvas instanceof SVGCanvas) {
|
||||
image = image._canvas
|
||||
}
|
||||
// Then check if it's a Canvas-like object by checking for getContext method
|
||||
else if (typeof image.getContext === 'function' && image.width && image.height) {
|
||||
// If it has canvas properties but isn't recognized as CanvasElement or SVGCanvas,
|
||||
// try to correct the prototype chain
|
||||
if (!(image instanceof CanvasElement) && !(image instanceof SVGCanvas)) {
|
||||
// Try to create a proper CanvasElement from the canvas-like object
|
||||
// This helps when webpack has transformed the prototype chain
|
||||
Object.setPrototypeOf(image, CanvasElement.prototype)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the original drawImage with the potentially corrected image
|
||||
return _drawImage.apply(this, [image, ...args])
|
||||
}
|
||||
|
||||
function createCanvas(width, height, flag) {
|
||||
const isSvgBackend = typeof flag !== 'undefined'
|
||||
return isSvgBackend ? new SVGCanvas(width, height, flag) : new CanvasElement(width, height)
|
||||
}
|
||||
|
||||
class Canvas {
|
||||
constructor(width, height, flag) {
|
||||
return createCanvas(width, height, flag)
|
||||
}
|
||||
|
||||
static [Symbol.hasInstance](instance) {
|
||||
return instance instanceof CanvasElement || instance instanceof SVGCanvas
|
||||
}
|
||||
}
|
||||
|
||||
if (!process.env.DISABLE_SYSTEM_FONTS_LOAD) {
|
||||
GlobalFonts.loadSystemFonts()
|
||||
const platformName = platform()
|
||||
const homedirPath = homedir()
|
||||
switch (platformName) {
|
||||
case 'win32':
|
||||
GlobalFonts.loadFontsFromDir(join(homedirPath, 'AppData', 'Local', 'Microsoft', 'Windows', 'Fonts'))
|
||||
break
|
||||
case 'darwin':
|
||||
GlobalFonts.loadFontsFromDir(join(homedirPath, 'Library', 'Fonts'))
|
||||
break
|
||||
case 'linux':
|
||||
GlobalFonts.loadFontsFromDir(join('usr', 'local', 'share', 'fonts'))
|
||||
GlobalFonts.loadFontsFromDir(join(homedirPath, '.fonts'))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
clearAllCache,
|
||||
Canvas,
|
||||
createCanvas,
|
||||
Path2D,
|
||||
ImageData,
|
||||
Image,
|
||||
PathOp,
|
||||
FillType,
|
||||
StrokeCap,
|
||||
StrokeJoin,
|
||||
SvgExportFlag,
|
||||
GlobalFonts: GlobalFonts,
|
||||
convertSVGTextToPath,
|
||||
DOMPoint,
|
||||
DOMMatrix,
|
||||
DOMRect,
|
||||
loadImage,
|
||||
FontKey,
|
||||
// Export these for better webpack compatibility
|
||||
CanvasElement,
|
||||
SVGCanvas,
|
||||
}
|
||||
368
node_modules/@napi-rs/canvas/js-binding.js
generated
vendored
Normal file
368
node_modules/@napi-rs/canvas/js-binding.js
generated
vendored
Normal file
@@ -0,0 +1,368 @@
|
||||
// prettier-ignore
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
/* auto-generated by NAPI-RS */
|
||||
|
||||
const { readFileSync } = require('fs')
|
||||
let nativeBinding = null
|
||||
const loadErrors = []
|
||||
|
||||
const isMusl = () => {
|
||||
let musl = false
|
||||
if (process.platform === 'linux') {
|
||||
musl = isMuslFromFilesystem()
|
||||
if (musl === null) {
|
||||
musl = isMuslFromReport()
|
||||
}
|
||||
if (musl === null) {
|
||||
musl = isMuslFromChildProcess()
|
||||
}
|
||||
}
|
||||
return musl
|
||||
}
|
||||
|
||||
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
|
||||
|
||||
const isMuslFromFilesystem = () => {
|
||||
try {
|
||||
return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const isMuslFromReport = () => {
|
||||
const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null
|
||||
if (!report) {
|
||||
return null
|
||||
}
|
||||
if (report.header && report.header.glibcVersionRuntime) {
|
||||
return false
|
||||
}
|
||||
if (Array.isArray(report.sharedObjects)) {
|
||||
if (report.sharedObjects.some(isFileMusl)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const isMuslFromChildProcess = () => {
|
||||
try {
|
||||
return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
|
||||
} catch (e) {
|
||||
// If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function requireNative() {
|
||||
if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
|
||||
try {
|
||||
nativeBinding = require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH)
|
||||
} catch (err) {
|
||||
loadErrors.push(err)
|
||||
}
|
||||
} else if (process.platform === 'android') {
|
||||
if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./skia.android-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-android-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 'arm') {
|
||||
try {
|
||||
return require('./skia.android-arm-eabi.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-android-arm-eabi')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'win32') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./skia.win32-x64-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-win32-x64-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 'ia32') {
|
||||
try {
|
||||
return require('./skia.win32-ia32-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-win32-ia32-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./skia.win32-arm64-msvc.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-win32-arm64-msvc')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'darwin') {
|
||||
try {
|
||||
return require('./skia.darwin-universal.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-darwin-universal')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./skia.darwin-x64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-darwin-x64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./skia.darwin-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-darwin-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'freebsd') {
|
||||
if (process.arch === 'x64') {
|
||||
try {
|
||||
return require('./skia.freebsd-x64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-freebsd-x64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
try {
|
||||
return require('./skia.freebsd-arm64.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-freebsd-arm64')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
|
||||
}
|
||||
} else if (process.platform === 'linux') {
|
||||
if (process.arch === 'x64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./skia.linux-x64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-x64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./skia.linux-x64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-x64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'arm64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./skia.linux-arm64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-arm64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./skia.linux-arm64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-arm64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'arm') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./skia.linux-arm-musleabihf.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-arm-musleabihf')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./skia.linux-arm-gnueabihf.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-arm-gnueabihf')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'riscv64') {
|
||||
if (isMusl()) {
|
||||
try {
|
||||
return require('./skia.linux-riscv64-musl.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-riscv64-musl')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return require('./skia.linux-riscv64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-riscv64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
}
|
||||
} else if (process.arch === 'ppc64') {
|
||||
try {
|
||||
return require('./skia.linux-ppc64-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-ppc64-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else if (process.arch === 's390x') {
|
||||
try {
|
||||
return require('./skia.linux-s390x-gnu.node')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
try {
|
||||
return require('@napi-rs/canvas-linux-s390x-gnu')
|
||||
} catch (e) {
|
||||
loadErrors.push(e)
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
|
||||
}
|
||||
} else {
|
||||
loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
|
||||
}
|
||||
}
|
||||
|
||||
nativeBinding = requireNative()
|
||||
|
||||
if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
|
||||
try {
|
||||
nativeBinding = require('./skia.wasi.cjs')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
loadErrors.push(err)
|
||||
}
|
||||
}
|
||||
if (!nativeBinding) {
|
||||
try {
|
||||
nativeBinding = require('@napi-rs/canvas-wasm32-wasi')
|
||||
} catch (err) {
|
||||
if (process.env.NAPI_RS_FORCE_WASI) {
|
||||
loadErrors.push(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nativeBinding) {
|
||||
if (loadErrors.length > 0) {
|
||||
// TODO Link to documentation with potential fixes
|
||||
// - The package owner could build/publish bindings for this arch
|
||||
// - The user may need to bundle the correct files
|
||||
// - The user may need to re-install node_modules to get new packages
|
||||
throw new Error('Failed to load native binding', { cause: loadErrors })
|
||||
}
|
||||
throw new Error(`Failed to load native binding`)
|
||||
}
|
||||
|
||||
module.exports.GlobalFonts = nativeBinding.GlobalFonts
|
||||
module.exports.CanvasElement = nativeBinding.CanvasElement
|
||||
module.exports.CanvasGradient = nativeBinding.CanvasGradient
|
||||
module.exports.CanvasPattern = nativeBinding.CanvasPattern
|
||||
module.exports.CanvasRenderingContext2D = nativeBinding.CanvasRenderingContext2D
|
||||
module.exports.FontKey = nativeBinding.FontKey
|
||||
module.exports.Image = nativeBinding.Image
|
||||
module.exports.ImageData = nativeBinding.ImageData
|
||||
module.exports.Path = nativeBinding.Path
|
||||
module.exports.SVGCanvas = nativeBinding.SVGCanvas
|
||||
module.exports.ChromaSubsampling = nativeBinding.ChromaSubsampling
|
||||
module.exports.clearAllCache = nativeBinding.clearAllCache
|
||||
module.exports.convertSVGTextToPath = nativeBinding.convertSVGTextToPath
|
||||
module.exports.FillType = nativeBinding.FillType
|
||||
module.exports.PathOp = nativeBinding.PathOp
|
||||
module.exports.StrokeCap = nativeBinding.StrokeCap
|
||||
module.exports.StrokeJoin = nativeBinding.StrokeJoin
|
||||
module.exports.SvgExportFlag = nativeBinding.SvgExportFlag
|
||||
141
node_modules/@napi-rs/canvas/load-image.js
generated
vendored
Normal file
141
node_modules/@napi-rs/canvas/load-image.js
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
const fs = require('fs')
|
||||
const { Readable } = require('stream')
|
||||
const { URL } = require('url')
|
||||
|
||||
const { Image } = require('./js-binding')
|
||||
|
||||
let http, https
|
||||
|
||||
const MAX_REDIRECTS = 20
|
||||
const REDIRECT_STATUSES = new Set([301, 302])
|
||||
|
||||
/**
|
||||
* Loads the given source into canvas Image
|
||||
* @param {string|URL|Image|Buffer} source The image source to be loaded
|
||||
* @param {object} options Options passed to the loader
|
||||
*/
|
||||
module.exports = async function loadImage(source, options = {}) {
|
||||
// use the same buffer without copying if the source is a buffer
|
||||
if (Buffer.isBuffer(source) || source instanceof Uint8Array) return createImage(source, options.alt)
|
||||
// load readable stream as image
|
||||
if (source instanceof Readable) return createImage(await consumeStream(source), options.alt)
|
||||
// construct a Uint8Array if the source is ArrayBuffer or SharedArrayBuffer
|
||||
if (source instanceof ArrayBuffer || source instanceof SharedArrayBuffer)
|
||||
return createImage(new Uint8Array(source), options.alt)
|
||||
// construct a buffer if the source is buffer-like
|
||||
if (isBufferLike(source)) return createImage(Buffer.from(source), options.alt)
|
||||
// if the source is Image instance, copy the image src to new image
|
||||
if (source instanceof Image) return createImage(source.src, options.alt)
|
||||
// if source is string and in data uri format, construct image using data uri
|
||||
if (typeof source === 'string' && source.trimStart().startsWith('data:')) {
|
||||
const commaIdx = source.indexOf(',')
|
||||
const encoding = source.lastIndexOf('base64', commaIdx) < 0 ? 'utf-8' : 'base64'
|
||||
const data = Buffer.from(source.slice(commaIdx + 1), encoding)
|
||||
return createImage(data, options.alt)
|
||||
}
|
||||
// if source is a string or URL instance
|
||||
if (typeof source === 'string') {
|
||||
// if the source exists as a file, construct image from that file
|
||||
if (!source.startsWith('http') && !source.startsWith('https') && (await exists(source))) {
|
||||
return createImage(source, options.alt)
|
||||
} else {
|
||||
// the source is a remote url here
|
||||
source = new URL(source)
|
||||
// attempt to download the remote source and construct image
|
||||
const data = await new Promise((resolve, reject) =>
|
||||
makeRequest(
|
||||
source,
|
||||
resolve,
|
||||
reject,
|
||||
typeof options.maxRedirects === 'number' && options.maxRedirects >= 0 ? options.maxRedirects : MAX_REDIRECTS,
|
||||
options.requestOptions,
|
||||
),
|
||||
)
|
||||
return createImage(data, options.alt)
|
||||
}
|
||||
}
|
||||
|
||||
if (source instanceof URL) {
|
||||
if (source.protocol === 'file:') {
|
||||
// remove the leading slash on windows
|
||||
return createImage(process.platform === 'win32' ? source.pathname.substring(1) : source.pathname, options.alt)
|
||||
} else {
|
||||
const data = await new Promise((resolve, reject) =>
|
||||
makeRequest(
|
||||
source,
|
||||
resolve,
|
||||
reject,
|
||||
typeof options.maxRedirects === 'number' && options.maxRedirects >= 0 ? options.maxRedirects : MAX_REDIRECTS,
|
||||
options.requestOptions,
|
||||
),
|
||||
)
|
||||
return createImage(data, options.alt)
|
||||
}
|
||||
}
|
||||
|
||||
// throw error as don't support that source
|
||||
throw new TypeError('unsupported image source')
|
||||
}
|
||||
|
||||
function makeRequest(url, resolve, reject, redirectCount, requestOptions) {
|
||||
const isHttps = url.protocol === 'https:'
|
||||
// lazy load the lib
|
||||
const lib = isHttps ? (!https ? (https = require('https')) : https) : !http ? (http = require('http')) : http
|
||||
|
||||
lib
|
||||
.get(url.toString(), requestOptions || {}, (res) => {
|
||||
try {
|
||||
const shouldRedirect = REDIRECT_STATUSES.has(res.statusCode) && typeof res.headers.location === 'string'
|
||||
if (shouldRedirect && redirectCount > 0)
|
||||
return makeRequest(
|
||||
new URL(res.headers.location, url.origin),
|
||||
resolve,
|
||||
reject,
|
||||
redirectCount - 1,
|
||||
requestOptions,
|
||||
)
|
||||
if (typeof res.statusCode === 'number' && (res.statusCode < 200 || res.statusCode >= 300)) {
|
||||
return reject(new Error(`remote source rejected with status code ${res.statusCode}`))
|
||||
}
|
||||
|
||||
consumeStream(res).then(resolve, reject)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
.on('error', reject)
|
||||
}
|
||||
|
||||
// use stream/consumers in the future?
|
||||
function consumeStream(res) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = []
|
||||
|
||||
res.on('data', (chunk) => chunks.push(chunk))
|
||||
res.on('end', () => resolve(Buffer.concat(chunks)))
|
||||
res.on('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
function createImage(src, alt) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const image = new Image()
|
||||
if (typeof alt === 'string') image.alt = alt
|
||||
image.onload = () => resolve(image)
|
||||
image.onerror = (e) => reject(e)
|
||||
image.src = src
|
||||
})
|
||||
}
|
||||
|
||||
function isBufferLike(src) {
|
||||
return (src && src.type === 'Buffer') || Array.isArray(src)
|
||||
}
|
||||
|
||||
async function exists(path) {
|
||||
try {
|
||||
await fs.promises.access(path, fs.constants.F_OK)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
157
node_modules/@napi-rs/canvas/package.json
generated
vendored
Normal file
157
node_modules/@napi-rs/canvas/package.json
generated
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"name": "@napi-rs/canvas",
|
||||
"version": "0.1.80",
|
||||
"description": "Canvas for Node.js with skia backend",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/Brooooooklyn/canvas.git"
|
||||
},
|
||||
"workspaces": [
|
||||
"e2e/*"
|
||||
],
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"napi-rs",
|
||||
"NAPI",
|
||||
"N-API",
|
||||
"Rust",
|
||||
"node-addon",
|
||||
"node-addon-api",
|
||||
"canvas",
|
||||
"image",
|
||||
"pdf",
|
||||
"svg",
|
||||
"skia"
|
||||
],
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"index.js",
|
||||
"geometry.js",
|
||||
"js-binding.js",
|
||||
"load-image.js"
|
||||
],
|
||||
"napi": {
|
||||
"binaryName": "skia",
|
||||
"targets": [
|
||||
"x86_64-unknown-linux-gnu",
|
||||
"x86_64-apple-darwin",
|
||||
"x86_64-pc-windows-msvc",
|
||||
"armv7-unknown-linux-gnueabihf",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"aarch64-unknown-linux-gnu",
|
||||
"aarch64-unknown-linux-musl",
|
||||
"aarch64-apple-darwin",
|
||||
"aarch64-linux-android",
|
||||
"riscv64-unknown-linux-gnu"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"artifacts": "napi artifacts",
|
||||
"bench": "node -r @swc-node/register benchmark/bench.ts",
|
||||
"build": "napi build --platform --release --js js-binding.js",
|
||||
"build:debug": "napi build --platform --js js-binding.js",
|
||||
"format": "run-p format:source format:rs format:toml",
|
||||
"format:rs": "cargo fmt",
|
||||
"format:source": "prettier . -w",
|
||||
"format:toml": "taplo format",
|
||||
"lint": "oxlint",
|
||||
"prepublishOnly": "pinst --disable && napi prepublish -t npm",
|
||||
"postpublish": "pinst --enable",
|
||||
"test:ci": "ava -c 1",
|
||||
"test": "ava",
|
||||
"e2e": "yarn workspace @napi-rs/canvas-e2e-webpack test",
|
||||
"version": "napi version && conventional-changelog -p angular -i CHANGELOG.md -s && git add ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jimp/core": "^1.6.0",
|
||||
"@jimp/custom": "^0.22.12",
|
||||
"@jimp/jpeg": "^0.22.12",
|
||||
"@jimp/png": "^0.22.12",
|
||||
"@napi-rs/cli": "^3.1.1",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
"@swc-node/register": "^1.10.10",
|
||||
"@swc/core": "^1.11.31",
|
||||
"@taplo/cli": "^0.7.0",
|
||||
"@types/lodash": "^4.17.17",
|
||||
"@types/node": "^22.15.30",
|
||||
"@types/semver": "^7",
|
||||
"ava": "^6.3.0",
|
||||
"canvas": "^3.1.0",
|
||||
"canvaskit-wasm": "^0.40.0",
|
||||
"colorette": "^2.0.20",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"core-js": "^3.42.0",
|
||||
"echarts": "^6.0.0",
|
||||
"electron": "^38.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^16.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"oxlint": "^1.0.0",
|
||||
"pinst": "^3.0.0",
|
||||
"png.js": "^0.2.1",
|
||||
"prettier": "^3.5.3",
|
||||
"pretty-bytes": "^7.0.0",
|
||||
"semver": "^7.7.2",
|
||||
"skia-canvas": "^3.0.0",
|
||||
"table": "^6.9.0",
|
||||
"tinybench": "^5.0.0",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.@(js|ts|tsx|yml|yaml|md|json|html)": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.@(js|ts|tsx)": [
|
||||
"oxlint --fix"
|
||||
]
|
||||
},
|
||||
"ava": {
|
||||
"require": [
|
||||
"@swc-node/register",
|
||||
"core-js/proposals/promise-with-resolvers.js"
|
||||
],
|
||||
"extensions": [
|
||||
"ts"
|
||||
],
|
||||
"files": [
|
||||
"__test__/**/*.spec.ts",
|
||||
"scripts/__test__/**/*.spec.ts"
|
||||
],
|
||||
"workerThreads": false,
|
||||
"cache": false,
|
||||
"timeout": "3m",
|
||||
"environmentVariables": {
|
||||
"SWC_NODE_PROJECT": "./tsconfig.json",
|
||||
"NODE_ENV": "ava"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"semi": false,
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"arrowParens": "always"
|
||||
},
|
||||
"packageManager": "yarn@4.9.4",
|
||||
"optionalDependencies": {
|
||||
"@napi-rs/canvas-linux-x64-gnu": "0.1.80",
|
||||
"@napi-rs/canvas-darwin-x64": "0.1.80",
|
||||
"@napi-rs/canvas-win32-x64-msvc": "0.1.80",
|
||||
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80",
|
||||
"@napi-rs/canvas-linux-x64-musl": "0.1.80",
|
||||
"@napi-rs/canvas-linux-arm64-gnu": "0.1.80",
|
||||
"@napi-rs/canvas-linux-arm64-musl": "0.1.80",
|
||||
"@napi-rs/canvas-darwin-arm64": "0.1.80",
|
||||
"@napi-rs/canvas-android-arm64": "0.1.80",
|
||||
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.80"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user