@napi-rs/canvas
Advanced tools
Comparing version 0.0.3 to 0.0.4
@@ -216,4 +216,25 @@ export interface DOMMatrix2DInit { | ||
rect(x: number, y: number, w: number, h: number): void | ||
// PathKit methods | ||
op(path: Path2D, operation: PathOp): Path2D | ||
toSVGString(): string | ||
getFillType(): FillType | ||
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 | ||
equals(path: Path2D): boolean | ||
} | ||
export interface StrokeOptions { | ||
width?: number | ||
miterLimit?: number | ||
cap?: StrokeCap | ||
join?: StrokeJoin | ||
} | ||
export interface SKRSContext2D extends Omit<CanvasRenderingContext2D, 'drawImage' | 'createPattern'> { | ||
@@ -246,1 +267,28 @@ drawImage(image: Image, dx: number, dy: number): void | ||
export function createCanvas(width: number, height: number): Canvas | ||
export const 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 const enum FillType { | ||
Winding = 0, | ||
EvenOdd = 1, | ||
InverseWinding = 2, | ||
InverseEvenOdd = 3, | ||
} | ||
export const enum StrokeJoin { | ||
Miter = 0, | ||
Round = 1, | ||
Bevel = 2, | ||
} | ||
export const enum StrokeCap { | ||
Butt = 0, | ||
Round = 1, | ||
Square = 2, | ||
} |
40
index.js
@@ -19,2 +19,14 @@ const { loadBinding } = require('@node-rs/helper') | ||
const StrokeJoin = { | ||
Miter: 0, | ||
Round: 1, | ||
Bevel: 2, | ||
} | ||
const StrokeCap = { | ||
Butt: 0, | ||
Round: 1, | ||
Square: 2, | ||
} | ||
CanvasRenderingContext2D.prototype.createPattern = function createPattern(image, repetition) { | ||
@@ -41,2 +53,11 @@ if (image instanceof ImageData) { | ||
Path2D.prototype.stroke = function stroke(strokeOptions = {}) { | ||
const width = typeof strokeOptions.width === 'undefined' ? 1 : strokeOptions.width | ||
const miterLimit = typeof strokeOptions.miterLimit === 'undefined' ? 1 : strokeOptions.miterLimit | ||
const join = typeof strokeOptions.join === 'undefined' ? StrokeJoin.Miter : strokeOptions.join | ||
const cap = typeof strokeOptions.cap === 'undefined' ? StrokeCap.Butt : strokeOptions.cap | ||
return this._stroke(width, miterLimit, join, cap) | ||
} | ||
function createCanvas(width, height) { | ||
@@ -84,2 +105,17 @@ const canvasElement = new CanvasElement(width, height) | ||
const 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 | ||
} | ||
const FillType = { | ||
Winding: 0, | ||
EvenOdd: 1, | ||
InverseWinding: 2, | ||
InverseEvenOdd: 3, | ||
} | ||
module.exports = { | ||
@@ -90,3 +126,7 @@ createCanvas, | ||
Image, | ||
PathOp, | ||
FillType, | ||
StrokeCap, | ||
StrokeJoin, | ||
...Geometry, | ||
} |
{ | ||
"name": "@napi-rs/canvas", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"description": "Canvas for Node.js with skia backend", | ||
@@ -67,8 +67,8 @@ "main": "index.js", | ||
"@jimp/png": "^0.16.1", | ||
"@napi-rs/cli": "^1.0.3", | ||
"@octokit/rest": "^18.3.5", | ||
"@napi-rs/cli": "^1.0.4", | ||
"@octokit/rest": "^18.5.2", | ||
"@swc-node/register": "^1.0.5", | ||
"@types/chalk": "^2.2.0", | ||
"@typescript-eslint/eslint-plugin": "^4.17.0", | ||
"@typescript-eslint/parser": "^4.17.0", | ||
"@typescript-eslint/eslint-plugin": "^4.22.0", | ||
"@typescript-eslint/parser": "^4.22.0", | ||
"ava": "^3.15.0", | ||
@@ -78,3 +78,3 @@ "benny": "^3.6.15", | ||
"chalk": "^4.1.0", | ||
"eslint": "^7.22.0", | ||
"eslint": "^7.24.0", | ||
"eslint-config-prettier": "^8.1.0", | ||
@@ -84,3 +84,3 @@ "eslint-plugin-import": "^2.22.1", | ||
"eslint-plugin-sonarjs": "^0.6.0", | ||
"husky": "^5.1.3", | ||
"husky": "^6.0.0", | ||
"lint-staged": "^10.5.4", | ||
@@ -92,3 +92,3 @@ "npm-run-all": "^4.1.5", | ||
"putasset": "^5.0.3", | ||
"typescript": "^4.2.3" | ||
"typescript": "^4.2.4" | ||
}, | ||
@@ -139,11 +139,11 @@ "dependencies": { | ||
"optionalDependencies": { | ||
"@napi-rs/canvas-win32-x64-msvc": "^0.0.3", | ||
"@napi-rs/canvas-darwin-x64": "^0.0.3", | ||
"@napi-rs/canvas-linux-x64-gnu": "^0.0.3", | ||
"@napi-rs/canvas-linux-arm-gnueabihf": "^0.0.3", | ||
"@napi-rs/canvas-linux-x64-musl": "^0.0.3", | ||
"@napi-rs/canvas-linux-arm64-gnu": "^0.0.3", | ||
"@napi-rs/canvas-darwin-arm64": "^0.0.3", | ||
"@napi-rs/canvas-android-arm64": "^0.0.3" | ||
"@napi-rs/canvas-win32-x64-msvc": "^0.0.4", | ||
"@napi-rs/canvas-darwin-x64": "^0.0.4", | ||
"@napi-rs/canvas-linux-x64-gnu": "^0.0.4", | ||
"@napi-rs/canvas-linux-arm-gnueabihf": "^0.0.4", | ||
"@napi-rs/canvas-linux-x64-musl": "^0.0.4", | ||
"@napi-rs/canvas-linux-arm64-gnu": "^0.0.4", | ||
"@napi-rs/canvas-darwin-arm64": "^0.0.4", | ||
"@napi-rs/canvas-android-arm64": "^0.0.4" | ||
} | ||
} |
@@ -5,2 +5,4 @@ # `@napi-rs/canvas` | ||
> π 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 `N-API`. | ||
@@ -11,2 +13,4 @@ | ||
[δΈζζζ‘£](./README-zh.md) | ||
# Support matrix | ||
@@ -109,7 +113,84 @@ | ||
rect(x: number, y: number, w: number, h: number): void | ||
// PathKit methods | ||
op(path: Path2D, operation: PathOp): Path2D | ||
toSVGString(): string | ||
getFillType(): FillType | ||
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 | ||
equals(path: Path2D): boolean | ||
} | ||
``` | ||
### [Example](./example/tiger.js) | ||
## 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 | ||
![boolean-operations](./docs/imgs/boolean-operations.svg) | ||
### 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( | ||
'M50 87.5776C70.7536 87.5776 87.5776 70.7536 87.5776 50C87.5776 29.2464 70.7536 12.4224 50 12.4224C29.2464 12.4224 12.4224 29.2464 12.4224 50C12.4224 70.7536 29.2464 87.5776 50 87.5776ZM50 100C77.6142 100 100 77.6142 100 50C100 22.3858 77.6142 0 50 0C22.3858 0 0 22.3858 0 50C0 77.6142 22.3858 100 50 100Z', | ||
) | ||
pathCircle.setFillType(FillType.EvenOdd) | ||
pathCircle.asWinding().toSVGString() | ||
// => "M50 87.5776C29.2464 87.5776 12.4224 70.7536 12.4224 50C12.4224 29.2464 29.2464 12.4224 50 12.4224C70.7536 12.4224 87.5776 29.2464 87.5776 50C87.5776 70.7536 70.7536 87.5776 50 87.5776ZM50 100C77.6142 100 100 77.6142 100 50C100 22.3858 77.6142 0 50 0C22.3858 0 0 22.3858 0 50C0 77.6142 22.3858 100 50 100Z" | ||
``` | ||
### 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) | ||
@@ -142,3 +223,3 @@ | ||
## Pull pre-build skia binary from Github | ||
## Pull pre-build skia binary from GitHub | ||
@@ -145,0 +226,0 @@ You can pull skia pre-build binaries if you just care the `Rust` part: |
43888
1109
241