@napi-rs/canvas 0.1.29 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/index.d.ts +49 -2
- package/index.js +2 -46
- package/js-binding.js +65 -36
- package/load-image.js +11 -9
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ main()
|
|
|
96
96
|
const { writeFileSync } = require('fs')
|
|
97
97
|
const { join } = require('path')
|
|
98
98
|
|
|
99
|
-
const { createCanvas, GlobalFonts } = require('
|
|
99
|
+
const { createCanvas, GlobalFonts } = require('@napi-rs/canvas')
|
|
100
100
|
|
|
101
101
|
GlobalFonts.registerFromPath(join(__dirname, '..', 'fonts', 'AppleColorEmoji@2x.ttf'), 'Apple Emoji')
|
|
102
102
|
GlobalFonts.registerFromPath(join(__dirname, '..', '__test__', 'fonts', 'COLRv1.ttf'), 'COLRv1')
|
|
@@ -328,7 +328,7 @@ $ node scripts/build-skia.js
|
|
|
328
328
|
|
|
329
329
|
# Install NPM packages, build the Node.js addon:
|
|
330
330
|
$ npm install -g yarn
|
|
331
|
-
$ yarn install --mode=skip
|
|
331
|
+
$ 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`
|
|
332
332
|
$ sudo dnf install clang # https://fedora.pkgs.org/34/fedora-x86_64/clang-12.0.0-0.3.rc1.fc34.x86_64.rpm.html
|
|
333
333
|
$ yarn build
|
|
334
334
|
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/// <reference lib="dom" />
|
|
2
|
-
|
|
3
1
|
// Clear all type of caches in Skia
|
|
4
2
|
export function clearAllCache(): void
|
|
5
3
|
|
|
@@ -302,9 +300,58 @@ export interface SvgCanvas {
|
|
|
302
300
|
}
|
|
303
301
|
|
|
304
302
|
export interface AvifConfig {
|
|
303
|
+
/** 0-100 scale, 100 is lossless */
|
|
305
304
|
quality?: number
|
|
305
|
+
/** 0-100 scale */
|
|
306
|
+
alphaQuality?: number
|
|
307
|
+
/** rav1e preset 1 (slow) 10 (fast but crappy), default is 4 */
|
|
306
308
|
speed?: number
|
|
309
|
+
/** How many threads should be used (0 = match core count) */
|
|
307
310
|
threads?: number
|
|
311
|
+
/** set to '4:2:0' to use chroma subsampling, default '4:4:4' */
|
|
312
|
+
chromaSubsampling?: ChromaSubsampling
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* https://en.wikipedia.org/wiki/Chroma_subsampling#Types_of_sampling_and_subsampling
|
|
316
|
+
* https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_concepts
|
|
317
|
+
*/
|
|
318
|
+
export enum ChromaSubsampling {
|
|
319
|
+
/**
|
|
320
|
+
* 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.
|
|
321
|
+
* 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).
|
|
322
|
+
* Formats such as HDCAM SR can record 4:4:4 R'G'B' over dual-link HD-SDI.
|
|
323
|
+
*/
|
|
324
|
+
Yuv444 = 0,
|
|
325
|
+
/**
|
|
326
|
+
* 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.
|
|
327
|
+
* Many high-end digital video formats and interfaces use this scheme:
|
|
328
|
+
* - [AVC-Intra 100](https://en.wikipedia.org/wiki/AVC-Intra)
|
|
329
|
+
* - [Digital Betacam](https://en.wikipedia.org/wiki/Betacam#Digital_Betacam)
|
|
330
|
+
* - [Betacam SX](https://en.wikipedia.org/wiki/Betacam#Betacam_SX)
|
|
331
|
+
* - [DVCPRO50](https://en.wikipedia.org/wiki/DV#DVCPRO) and [DVCPRO HD](https://en.wikipedia.org/wiki/DV#DVCPRO_HD)
|
|
332
|
+
* - [Digital-S](https://en.wikipedia.org/wiki/Digital-S)
|
|
333
|
+
* - [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))
|
|
334
|
+
* - [ProRes (HQ, 422, LT, and Proxy)](https://en.wikipedia.org/wiki/Apple_ProRes)
|
|
335
|
+
* - [XDCAM HD422](https://en.wikipedia.org/wiki/XDCAM)
|
|
336
|
+
* - [Canon MXF HD422](https://en.wikipedia.org/wiki/Canon_XF-300)
|
|
337
|
+
*/
|
|
338
|
+
Yuv422 = 1,
|
|
339
|
+
/**
|
|
340
|
+
* n 4:2:0, the horizontal sampling is doubled compared to 4:1:1,
|
|
341
|
+
* but as the **Cb** and **Cr** channels are only sampled on each alternate line in this scheme, the vertical resolution is halved.
|
|
342
|
+
* The data rate is thus the same.
|
|
343
|
+
* 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).
|
|
344
|
+
* It would also fit extremely well with the [SECAM](https://en.wikipedia.org/wiki/SECAM) color encoding system,
|
|
345
|
+
* 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).
|
|
346
|
+
* However, little equipment has actually been produced that outputs a SECAM analogue video signal.
|
|
347
|
+
* 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.
|
|
348
|
+
*/
|
|
349
|
+
Yuv420 = 2,
|
|
350
|
+
/**
|
|
351
|
+
* What if the chroma subsampling model is 4:0:0?
|
|
352
|
+
* 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.
|
|
353
|
+
*/
|
|
354
|
+
Yuv400 = 3,
|
|
308
355
|
}
|
|
309
356
|
|
|
310
357
|
export class Canvas {
|
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const {
|
|
|
6
6
|
CanvasRenderingContext2D,
|
|
7
7
|
CanvasElement,
|
|
8
8
|
SVGCanvas,
|
|
9
|
-
Path2D,
|
|
9
|
+
Path: Path2D,
|
|
10
10
|
ImageData,
|
|
11
11
|
Image,
|
|
12
12
|
CanvasPattern,
|
|
@@ -48,51 +48,7 @@ Object.defineProperty(GlobalFonts, 'has', {
|
|
|
48
48
|
|
|
49
49
|
function createCanvas(width, height, flag) {
|
|
50
50
|
const isSvgBackend = typeof flag !== 'undefined'
|
|
51
|
-
|
|
52
|
-
const {
|
|
53
|
-
encode: canvasEncode,
|
|
54
|
-
encodeSync: canvasEncodeSync,
|
|
55
|
-
toBuffer: canvasToBuffer,
|
|
56
|
-
toDataURL: canvasToDataURL,
|
|
57
|
-
toDataURLAsync: canvasToDataURLAsync,
|
|
58
|
-
} = Object.getPrototypeOf(canvasElement)
|
|
59
|
-
|
|
60
|
-
canvasElement.encode = function encode(type, qualityOrConfig = {}) {
|
|
61
|
-
if (type === 'avif') {
|
|
62
|
-
return canvasEncode.call(this, type, JSON.stringify(qualityOrConfig))
|
|
63
|
-
}
|
|
64
|
-
return canvasEncode.call(this, type, qualityOrConfig)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
canvasElement.encodeSync = function encodeSync(type, qualityOrConfig = {}) {
|
|
68
|
-
if (type === 'avif') {
|
|
69
|
-
return canvasEncodeSync.call(this, type, JSON.stringify(qualityOrConfig))
|
|
70
|
-
}
|
|
71
|
-
return canvasEncodeSync.call(this, type, qualityOrConfig)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
canvasElement.toBuffer = function toBuffer(type = 'image/png', qualityOrConfig = {}) {
|
|
75
|
-
if (type === 'avif') {
|
|
76
|
-
return canvasToBuffer.call(this, type, JSON.stringify(qualityOrConfig))
|
|
77
|
-
}
|
|
78
|
-
return canvasToBuffer.call(this, type, qualityOrConfig)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
canvasElement.toDataURL = function toDataURL(type = 'image/png', qualityOrConfig = {}) {
|
|
82
|
-
if (type === 'avif') {
|
|
83
|
-
return canvasToDataURL.call(this, type, JSON.stringify(qualityOrConfig))
|
|
84
|
-
}
|
|
85
|
-
return canvasToDataURL.call(this, type, qualityOrConfig)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
canvasElement.toDataURLAsync = function toDataURLAsync(type = 'image/png', qualityOrConfig = {}) {
|
|
89
|
-
if (type === 'avif') {
|
|
90
|
-
return canvasToDataURLAsync.call(this, type, JSON.stringify(qualityOrConfig))
|
|
91
|
-
}
|
|
92
|
-
return canvasToDataURLAsync.call(this, type, qualityOrConfig)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return canvasElement
|
|
51
|
+
return isSvgBackend ? new SVGCanvas(width, height, flag) : new CanvasElement(width, height)
|
|
96
52
|
}
|
|
97
53
|
|
|
98
54
|
class Canvas {
|
package/js-binding.js
CHANGED
|
@@ -5,27 +5,54 @@ const { platform, arch } = process
|
|
|
5
5
|
|
|
6
6
|
let nativeBinding = null
|
|
7
7
|
let localFileExisted = false
|
|
8
|
-
let isMusl = false
|
|
9
8
|
let loadError = null
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
15
|
-
}
|
|
16
|
-
localFileExisted = existsSync(join(__dirname, 'skia.android-arm64.node'))
|
|
10
|
+
function isMusl() {
|
|
11
|
+
// For Node 10
|
|
12
|
+
if (!process.report || typeof process.report.getReport !== 'function') {
|
|
17
13
|
try {
|
|
18
|
-
|
|
19
|
-
nativeBinding = require('./skia.android-arm64.node')
|
|
20
|
-
} else {
|
|
21
|
-
nativeBinding = require('@napi-rs/canvas-android-arm64')
|
|
22
|
-
}
|
|
14
|
+
return readFileSync('/usr/bin/ldd', 'utf8').includes('musl')
|
|
23
15
|
} catch (e) {
|
|
24
|
-
|
|
16
|
+
return true
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
const { glibcVersionRuntime } = process.report.getReport().header
|
|
20
|
+
return !glibcVersionRuntime
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
switch (platform) {
|
|
25
|
+
case 'android':
|
|
26
|
+
switch (arch) {
|
|
27
|
+
case 'arm64':
|
|
28
|
+
localFileExisted = existsSync(join(__dirname, 'skia.android-arm64.node'))
|
|
29
|
+
try {
|
|
30
|
+
if (localFileExisted) {
|
|
31
|
+
nativeBinding = require('./skia.android-arm64.node')
|
|
32
|
+
} else {
|
|
33
|
+
nativeBinding = require('@napi-rs/canvas-android-arm64')
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {
|
|
36
|
+
loadError = e
|
|
37
|
+
}
|
|
38
|
+
break
|
|
39
|
+
case 'arm':
|
|
40
|
+
localFileExisted = existsSync(join(__dirname, 'skia.android-arm-eabi.node'))
|
|
41
|
+
try {
|
|
42
|
+
if (localFileExisted) {
|
|
43
|
+
nativeBinding = require('./skia.android-arm-eabi.node')
|
|
44
|
+
} else {
|
|
45
|
+
nativeBinding = require('@napi-rs/canvas-android-arm-eabi')
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
loadError = e
|
|
49
|
+
}
|
|
50
|
+
break
|
|
51
|
+
default:
|
|
52
|
+
throw new Error(`Unsupported architecture on Android ${arch}`)
|
|
25
53
|
}
|
|
26
54
|
break
|
|
27
55
|
case 'win32':
|
|
28
|
-
// eslint-disable-next-line sonarjs/no-nested-switch
|
|
29
56
|
switch (arch) {
|
|
30
57
|
case 'x64':
|
|
31
58
|
localFileExisted = existsSync(join(__dirname, 'skia.win32-x64-msvc.node'))
|
|
@@ -68,7 +95,6 @@ switch (platform) {
|
|
|
68
95
|
}
|
|
69
96
|
break
|
|
70
97
|
case 'darwin':
|
|
71
|
-
// eslint-disable-next-line sonarjs/no-nested-switch
|
|
72
98
|
switch (arch) {
|
|
73
99
|
case 'x64':
|
|
74
100
|
localFileExisted = existsSync(join(__dirname, 'skia.darwin-x64.node'))
|
|
@@ -114,11 +140,9 @@ switch (platform) {
|
|
|
114
140
|
}
|
|
115
141
|
break
|
|
116
142
|
case 'linux':
|
|
117
|
-
// eslint-disable-next-line sonarjs/no-nested-switch
|
|
118
143
|
switch (arch) {
|
|
119
144
|
case 'x64':
|
|
120
|
-
|
|
121
|
-
if (isMusl) {
|
|
145
|
+
if (isMusl()) {
|
|
122
146
|
localFileExisted = existsSync(join(__dirname, 'skia.linux-x64-musl.node'))
|
|
123
147
|
try {
|
|
124
148
|
if (localFileExisted) {
|
|
@@ -143,8 +167,7 @@ switch (platform) {
|
|
|
143
167
|
}
|
|
144
168
|
break
|
|
145
169
|
case 'arm64':
|
|
146
|
-
|
|
147
|
-
if (isMusl) {
|
|
170
|
+
if (isMusl()) {
|
|
148
171
|
localFileExisted = existsSync(join(__dirname, 'skia.linux-arm64-musl.node'))
|
|
149
172
|
try {
|
|
150
173
|
if (localFileExisted) {
|
|
@@ -196,33 +219,39 @@ if (!nativeBinding) {
|
|
|
196
219
|
}
|
|
197
220
|
|
|
198
221
|
const {
|
|
199
|
-
|
|
222
|
+
ChromaSubsampling,
|
|
223
|
+
SvgExportFlag,
|
|
200
224
|
CanvasRenderingContext2D,
|
|
201
|
-
|
|
202
|
-
SVGCanvas,
|
|
203
|
-
Path,
|
|
225
|
+
CanvasGradient,
|
|
204
226
|
ImageData,
|
|
205
227
|
Image,
|
|
206
|
-
CanvasPattern,
|
|
207
|
-
GlobalFonts,
|
|
208
|
-
convertSVGTextToPath,
|
|
209
228
|
PathOp,
|
|
210
229
|
FillType,
|
|
211
|
-
StrokeJoin,
|
|
212
230
|
StrokeCap,
|
|
231
|
+
StrokeJoin,
|
|
232
|
+
Path,
|
|
233
|
+
CanvasPattern,
|
|
234
|
+
convertSVGTextToPath,
|
|
235
|
+
CanvasElement,
|
|
236
|
+
SVGCanvas,
|
|
237
|
+
clearAllCache,
|
|
238
|
+
GlobalFonts,
|
|
213
239
|
} = nativeBinding
|
|
214
240
|
|
|
215
|
-
module.exports.
|
|
241
|
+
module.exports.ChromaSubsampling = ChromaSubsampling
|
|
242
|
+
module.exports.SvgExportFlag = SvgExportFlag
|
|
216
243
|
module.exports.CanvasRenderingContext2D = CanvasRenderingContext2D
|
|
217
|
-
module.exports.
|
|
218
|
-
module.exports.SVGCanvas = SVGCanvas
|
|
219
|
-
module.exports.Path2D = Path
|
|
244
|
+
module.exports.CanvasGradient = CanvasGradient
|
|
220
245
|
module.exports.ImageData = ImageData
|
|
221
246
|
module.exports.Image = Image
|
|
222
|
-
module.exports.CanvasPattern = CanvasPattern
|
|
223
|
-
module.exports.GlobalFonts = GlobalFonts
|
|
224
|
-
module.exports.convertSVGTextToPath = convertSVGTextToPath
|
|
225
247
|
module.exports.PathOp = PathOp
|
|
226
248
|
module.exports.FillType = FillType
|
|
227
|
-
module.exports.StrokeJoin = StrokeJoin
|
|
228
249
|
module.exports.StrokeCap = StrokeCap
|
|
250
|
+
module.exports.StrokeJoin = StrokeJoin
|
|
251
|
+
module.exports.Path = Path
|
|
252
|
+
module.exports.CanvasPattern = CanvasPattern
|
|
253
|
+
module.exports.convertSVGTextToPath = convertSVGTextToPath
|
|
254
|
+
module.exports.CanvasElement = CanvasElement
|
|
255
|
+
module.exports.SVGCanvas = SVGCanvas
|
|
256
|
+
module.exports.clearAllCache = clearAllCache
|
|
257
|
+
module.exports.GlobalFonts = GlobalFonts
|
package/load-image.js
CHANGED
|
@@ -60,16 +60,18 @@ function makeRequest(url, resolve, reject, redirectCount, requestOptions) {
|
|
|
60
60
|
// lazy load the lib
|
|
61
61
|
const lib = isHttps ? (!https ? (https = require('https')) : https) : !http ? (http = require('http')) : http
|
|
62
62
|
|
|
63
|
-
lib
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
lib
|
|
64
|
+
.get(url, requestOptions || {}, (res) => {
|
|
65
|
+
const shouldRedirect = REDIRECT_STATUSES.has(res.statusCode) && typeof res.headers.location === 'string'
|
|
66
|
+
if (shouldRedirect && redirectCount > 0)
|
|
67
|
+
return makeRequest(new URL(res.headers.location), resolve, reject, redirectCount - 1, requestOptions)
|
|
68
|
+
if (typeof res.statusCode === 'number' && (res.statusCode < 200 || res.statusCode >= 300)) {
|
|
69
|
+
return reject(new Error(`remote source rejected with status code ${res.statusCode}`))
|
|
70
|
+
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
consumeStream(res).then(resolve, reject)
|
|
73
|
+
})
|
|
74
|
+
.on('error', reject)
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
// use stream/consumers in the future?
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@napi-rs/canvas",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "Canvas for Node.js with skia backend",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": "https://github.com/Brooooooklyn/canvas.git",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"artifacts": "napi artifacts",
|
|
51
51
|
"bench": "node -r @swc-node/register benchmark/bench.ts",
|
|
52
|
-
"build": "napi build --platform --release",
|
|
53
|
-
"build:debug": "napi build --platform",
|
|
52
|
+
"build": "napi build --platform --release --js js-binding.js",
|
|
53
|
+
"build:debug": "napi build --platform --js js-binding.js",
|
|
54
54
|
"format": "run-p format:source format:rs",
|
|
55
55
|
"format:rs": "cargo fmt",
|
|
56
56
|
"format:source": "prettier . -w",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"ava": "^4.3.3",
|
|
76
76
|
"benny": "^3.7.1",
|
|
77
77
|
"canvas": "^2.9.3",
|
|
78
|
-
"canvaskit-wasm": "^0.
|
|
78
|
+
"canvaskit-wasm": "^0.37.0",
|
|
79
79
|
"colorette": "^2.0.19",
|
|
80
80
|
"conventional-changelog-cli": "^2.2.2",
|
|
81
81
|
"echarts": "^5.3.3",
|
|
@@ -132,14 +132,14 @@
|
|
|
132
132
|
},
|
|
133
133
|
"packageManager": "yarn@3.2.3",
|
|
134
134
|
"optionalDependencies": {
|
|
135
|
-
"@napi-rs/canvas-win32-x64-msvc": "0.1.
|
|
136
|
-
"@napi-rs/canvas-darwin-x64": "0.1.
|
|
137
|
-
"@napi-rs/canvas-linux-x64-gnu": "0.1.
|
|
138
|
-
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.
|
|
139
|
-
"@napi-rs/canvas-linux-x64-musl": "0.1.
|
|
140
|
-
"@napi-rs/canvas-linux-arm64-gnu": "0.1.
|
|
141
|
-
"@napi-rs/canvas-linux-arm64-musl": "0.1.
|
|
142
|
-
"@napi-rs/canvas-darwin-arm64": "0.1.
|
|
143
|
-
"@napi-rs/canvas-android-arm64": "0.1.
|
|
135
|
+
"@napi-rs/canvas-win32-x64-msvc": "0.1.30",
|
|
136
|
+
"@napi-rs/canvas-darwin-x64": "0.1.30",
|
|
137
|
+
"@napi-rs/canvas-linux-x64-gnu": "0.1.30",
|
|
138
|
+
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.30",
|
|
139
|
+
"@napi-rs/canvas-linux-x64-musl": "0.1.30",
|
|
140
|
+
"@napi-rs/canvas-linux-arm64-gnu": "0.1.30",
|
|
141
|
+
"@napi-rs/canvas-linux-arm64-musl": "0.1.30",
|
|
142
|
+
"@napi-rs/canvas-darwin-arm64": "0.1.30",
|
|
143
|
+
"@napi-rs/canvas-android-arm64": "0.1.30"
|
|
144
144
|
}
|
|
145
145
|
}
|