Skip to content

Commit

Permalink
fix: transform state between save/restore
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Sep 14, 2021
1 parent 36ff54f commit d313b80
Show file tree
Hide file tree
Showing 28 changed files with 1,243 additions and 1,130 deletions.
23 changes: 21 additions & 2 deletions .cargo/config.toml
@@ -1,18 +1,37 @@
[target.x86_64-apple-darwin]
# ivybridge provided by Github Actions
rustflags = ["-C", "target-cpu=ivybridge"]

[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "link-args=/NODEFAULTLIB:libcmt.lib"]
rustflags = ["-C", "link-args=/NODEFAULTLIB:libcmt.lib", "-C", "target-cpu=haswell"]

[target.x86_64-unknown-linux-gnu]
rustflags = ["-C", "target-cpu=haswell"]

[target.aarch64-apple-darwin]
rustflags = ["-C", "target-cpu=apple-a14"]

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
rustflags = ["-C", "target-cpu=cortex-a57"]

[target.armv7-unknown-linux-gnueabihf]
linker = "arm-linux-gnueabihf-gcc"
rustflags = ["-C", "target-cpu=cortex-a7"]

[target.x86_64-unknown-linux-musl]
rustflags = [
"-C",
"target-feature=-crt-static",
"-C",
"target-cpu=haswell",
]

[target.aarch64-unknown-linux-musl]
linker = "aarch64-linux-musl-gcc"
rustflags = ["-C", "target-feature=-crt-static"]
rustflags = [
"-C",
"target-feature=-crt-static",
"-C",
"target-cpu=cortex-a57",
]
2 changes: 1 addition & 1 deletion .eslintrc.yml
Expand Up @@ -26,7 +26,7 @@ rules:
'space-before-function-paren': 0
'no-useless-constructor': 0
'no-undef': 2
'no-console': [2, { allow: ['error', 'warn', 'info', 'assert'] }]
'no-console': [2, { allow: ['error', 'warn', 'info', 'assert', 'time', 'timeEnd'] }]
'comma-dangle': ['error', 'only-multiline']
'no-unused-vars': 0
'no-var': 2
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/CI.yaml
Expand Up @@ -23,7 +23,9 @@ jobs:
settings:
- host: macos-latest
target: 'x86_64-apple-darwin'
build: pnpm build
build: |
rustc --print target-cpus
pnpm build
downloadTarget: ''
- host: windows-latest
build: pnpm build
Expand Down
3 changes: 2 additions & 1 deletion __test__/canvas-class.spec.ts
@@ -1,7 +1,8 @@
import test from 'ava'
import { omit } from 'lodash'

import { createCanvas, Canvas } from '../index'

test('Canvas constructor should be equal to createCanvas', (t) => {
t.deepEqual(createCanvas(200, 100), new Canvas(200, 100))
t.deepEqual(omit(createCanvas(200, 100), 'getContext'), omit(new Canvas(200, 100), 'getContext'))
})
61 changes: 48 additions & 13 deletions __test__/regression.spec.ts
@@ -1,19 +1,8 @@
import ava, { TestInterface } from 'ava'
import test from 'ava'

import { createCanvas, Canvas, SKRSContext2D } from '../index'
import { createCanvas } from '../index'
import { snapshotImage } from './image-snapshot'

const test = ava as TestInterface<{
canvas: Canvas
ctx: SKRSContext2D
}>

test.beforeEach((t) => {
const canvas = createCanvas(512, 512)
t.context.canvas = canvas
t.context.ctx = canvas.getContext('2d')!
})

test('transform-with-state', async (t) => {
const canvas = createCanvas(256, 256)
const ctx = canvas.getContext('2d')
Expand All @@ -40,3 +29,49 @@ test('transform-with-state', async (t) => {
ctx.restore()
await snapshotImage(t, { canvas, ctx })
})

test('transform-with-radial-gradient', async (t) => {
const canvas = createCanvas(256, 256)
const ctx = canvas.getContext('2d')
ctx.translate(128.5, 128.5)
ctx.scale(1, 1)
ctx.clearRect(-128, -128, 256, 256)
ctx.beginPath()
ctx.save()
ctx.transform(1, 0, 0, 0.9090909090909091, 0, 0)
ctx.arc(0, 0, 110, 0, 6.283185307179586, false)
ctx.restore()
ctx.save()
const p = ctx.createRadialGradient(0.5, 0.5, 0, 0.2, 0.4, 0.5)
p.addColorStop(1, 'rgba(0, 0, 255, 1)')
p.addColorStop(0, 'rgba(200, 200, 200, 0)')
ctx.fillStyle = p
ctx.transform(220, 0, 0, 200, -110, -100)
ctx.transform(1, 0, 0, 1, 0, 0)
ctx.fill()
ctx.restore()
await snapshotImage(t, { canvas, ctx })
})

test('transform-with-radial-gradient-x', async (t) => {
const canvas = createCanvas(400, 282)
const ctx = canvas.getContext('2d')
ctx.translate(200.5, 141.5)
ctx.scale(1, 1)
ctx.clearRect(-181.5, -128, 363, 256)
ctx.beginPath()
ctx.save()
ctx.transform(1, 0, 0, 0.5555555555555556, 0, 0)
ctx.arc(0, 0, 180, 0, 6.283185307179586, false)
ctx.restore()
ctx.save()
const p = ctx.createRadialGradient(0.5, 0.5, 0, 0.5, 0.5, 0.5)
p.addColorStop(1, 'rgba(0, 0, 255, 1)')
p.addColorStop(0, 'rgba(200, 200, 200, 0)')
ctx.fillStyle = p
ctx.transform(360, 0, 0, 200, -180, -100)
ctx.transform(1, 0, 0, 1, 0, 0)
ctx.fill()
ctx.restore()
await snapshotImage(t, { canvas, ctx })
})
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion depot_tools
Submodule depot_tools updated 255 files
25 changes: 24 additions & 1 deletion index.html
Expand Up @@ -5,6 +5,7 @@
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/node_modules/canvaskit-wasm/bin/canvaskit.js"></script>
<title>Canvas</title>
</head>

Expand All @@ -27,6 +28,7 @@
<canvas width="1024" height="768" id="canvas-text"></canvas>

<canvas width="256" height="256" id="regression"></canvas>
<img src="/__test__/failure/transform-with-radial-gradient.png" width="256" height="256" alt="">

<script>
setTimeout(() => {
Expand Down Expand Up @@ -75,8 +77,29 @@
ctx.stroke()
ctx.fillText('Abcdefghijklmnop (' + baseline + ')', 0, y)
})
}, 300)
}, 300);

(function () {
const canvas = document.getElementById('regression')
const ctx = canvas.getContext('2d')
ctx.translate(128.5, 128.5)
ctx.scale(1, 1)
ctx.clearRect(-128, -128, 256, 256)
ctx.beginPath()
ctx.save()
ctx.transform(1, 0, 0, 0.9090909090909091, 0, 0)
ctx.arc(0, 0, 110, 0, 6.283185307179586, false)
ctx.restore()
ctx.save()
const p = ctx.createRadialGradient(0.5, 0.5, 0, 0.2, 0.4, 0.5)
p.addColorStop(1, 'rgba(0, 0, 255, 1)')
p.addColorStop(0, 'rgba(200, 200, 200, 0)')
ctx.fillStyle = p
ctx.transform(220, 0, 0, 200, -110, -100)
ctx.transform(1, 0, 0, 1, 0, 0)
ctx.fill()
ctx.restore()
})()
</script>
</body>

Expand Down
93 changes: 55 additions & 38 deletions index.js
Expand Up @@ -14,6 +14,7 @@ const { loadBinding } = require('@node-rs/helper')
const {
CanvasRenderingContext2D,
CanvasElement,
createContext,
SVGCanvas,
Path2D,
ImageData,
Expand Down Expand Up @@ -160,44 +161,60 @@ Path2D.prototype.getFillTypeString = function getFillTypeString() {
function createCanvas(width, height, flag) {
const isSvgBackend = typeof flag !== 'undefined'
const canvasElement = isSvgBackend ? new SVGCanvas(width, height) : new CanvasElement(width, height)
const ctx = isSvgBackend
? new CanvasRenderingContext2D(width, height, GlobalFontsSingleton, flag)
: new CanvasRenderingContext2D(width, height, GlobalFontsSingleton)

// napi can not define writable: true but enumerable: false property
Object.defineProperty(ctx, '_fillStyle', {
value: '#000',
configurable: false,
enumerable: false,
writable: true,
})

Object.defineProperty(ctx, '_strokeStyle', {
value: '#000',
configurable: false,
enumerable: false,
writable: true,
})

Object.defineProperty(ctx, 'createImageData', {
value: function createImageData(widthOrImage, height) {
if (widthOrImage instanceof ImageData) {
return new ImageData(widthOrImage.width, widthOrImage.height)
}
return new ImageData(widthOrImage, height)
},
configurable: false,
enumerable: false,
writable: false,
})

Object.defineProperty(canvasElement, 'ctx', {
value: ctx,
enumerable: false,
configurable: false,
})

ctx.canvas = canvasElement

let ctx
canvasElement.getContext = function getContext(type, attr) {
if (type !== '2d') {
throw new Error('Unsupported type')
}
ctx = ctx
? ctx
: isSvgBackend
? new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton, flag)
: new CanvasRenderingContext2D(this.width, this.height, GlobalFontsSingleton)
if (attr) {
createContext(ctx, this.width, this.height, attr)
} else {
createContext(ctx, this.width, this.height)
}

// napi can not define writable: true but enumerable: false property
Object.defineProperty(ctx, '_fillStyle', {
value: '#000',
configurable: false,
enumerable: false,
writable: true,
})

Object.defineProperty(ctx, '_strokeStyle', {
value: '#000',
configurable: false,
enumerable: false,
writable: true,
})

Object.defineProperty(ctx, 'createImageData', {
value: function createImageData(widthOrImage, height) {
if (widthOrImage instanceof ImageData) {
return new ImageData(widthOrImage.width, widthOrImage.height)
}
return new ImageData(widthOrImage, height)
},
configurable: false,
enumerable: false,
writable: false,
})

Object.defineProperty(canvasElement, 'ctx', {
value: ctx,
enumerable: false,
configurable: false,
})

ctx.canvas = canvasElement

return ctx
}

return canvasElement
}
Expand Down
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -69,11 +69,13 @@
"@napi-rs/cli": "^1.2.0",
"@octokit/rest": "^18.9.0",
"@swc-node/register": "^1.3.3",
"@types/lodash": "^4.14.172",
"@typescript-eslint/eslint-plugin": "^4.29.0",
"@typescript-eslint/parser": "^4.29.0",
"ava": "^3.15.0",
"benny": "^3.6.15",
"canvas": "^2.8.0",
"canvaskit-wasm": "^0.29.0",
"chalk": "^4.1.2",
"conventional-changelog-cli": "^2.1.1",
"eslint": "^7.32.0",
Expand All @@ -83,6 +85,7 @@
"eslint-plugin-sonarjs": "^0.10.0",
"husky": "^7.0.1",
"lint-staged": "^11.1.2",
"lodash": "^4.17.21",
"npm-run-all": "^4.1.5",
"pinst": "^2.1.6",
"png.js": "^0.2.1",
Expand Down

1 comment on commit d313b80

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: d313b80 Previous: 36ff54f Ratio
Draw house#skia-canvas 25.21 ops/sec (±1.25%) 31 ops/sec (±0.41%) 1.23
Draw house#node-canvas 22.94 ops/sec (±0.51%) 27 ops/sec (±0.47%) 1.18
Draw house#@napi-rs/skia 25.2 ops/sec (±1%) 29 ops/sec (±0.3%) 1.15
Draw gradient#skia-canvas 23 ops/sec (±1.1%) 31 ops/sec (±0.3%) 1.35
Draw gradient#node-canvas 22 ops/sec (±0.28%) 26 ops/sec (±1.72%) 1.18
Draw gradient#@napi-rs/skia 25 ops/sec (±0.08%) 28 ops/sec (±0.47%) 1.12

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.