Skip to content

Commit

Permalink
feat: testing for context2d functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jan 5, 2021
1 parent 2d2016c commit 107a650
Show file tree
Hide file tree
Showing 32 changed files with 769 additions and 178 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,36 @@ Cargo.lock

*.node
bench.txt

# Created by https://www.toptal.com/developers/gitignore/api/macos
# Edit at https://www.toptal.com/developers/gitignore?templates=macos

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# End of https://www.toptal.com/developers/gitignore/api/macos
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ crate-type = ["cdylib"]
[dependencies]
anyhow = "1"
cssparser = "0.28"
napi = "1.0.0"
napi-derive = "1.0.0"
napi = "1"
napi-derive = "1"
thiserror = "1"

[target.'cfg(all(unix, not(target_env = "musl"), not(target_arch = "aarch64")))'.dependencies]
Expand Down
257 changes: 257 additions & 0 deletions __test__/draw.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import ava, { TestInterface } from 'ava'

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

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

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

test('arc', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.arc(100, 75, 50, 0, 2 * Math.PI)
ctx.stroke()
await snapshotImage(t)
})

test('arcTo', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.moveTo(180, 90)
ctx.arcTo(180, 130, 110, 130, 130)
ctx.lineTo(110, 130)
ctx.stroke()
await snapshotImage(t)
})

test('arcTo-colorful', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.strokeStyle = 'gray'
ctx.moveTo(200, 20)
ctx.lineTo(200, 130)
ctx.lineTo(50, 20)
ctx.stroke()

// Arc
ctx.beginPath()
ctx.strokeStyle = 'black'
ctx.lineWidth = 5
ctx.moveTo(200, 20)
ctx.arcTo(200, 130, 50, 20, 40)
ctx.stroke()

// Start point
ctx.beginPath()
ctx.fillStyle = 'blue'
ctx.arc(200, 20, 5, 0, 2 * Math.PI)
ctx.fill()

// Control points
ctx.beginPath()
ctx.fillStyle = 'red'
ctx.arc(200, 130, 5, 0, 2 * Math.PI) // Control point one
ctx.arc(50, 20, 5, 0, 2 * Math.PI) // Control point two
ctx.fill()
await snapshotImage(t)
})

test('beginPath', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.strokeStyle = 'blue'
ctx.moveTo(20, 20)
ctx.lineTo(200, 20)
ctx.stroke()

// Second path
ctx.beginPath()
ctx.strokeStyle = 'green'
ctx.moveTo(20, 20)
ctx.lineTo(120, 120)
ctx.stroke()
await snapshotImage(t)
})

test('bezierCurveTo', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.moveTo(30, 30)
ctx.bezierCurveTo(120, 160, 180, 10, 220, 140)
ctx.stroke()
await snapshotImage(t)
})

test('bezierCurveTo-colorful', async (t) => {
const { ctx } = t.context
const start = { x: 50, y: 20 }
const cp1 = { x: 230, y: 30 }
const cp2 = { x: 150, y: 80 }
const end = { x: 250, y: 100 }

// Cubic Bézier curve
ctx.beginPath()
ctx.moveTo(start.x, start.y)
ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y)
ctx.stroke()

// Start and end points
ctx.fillStyle = 'blue'
ctx.beginPath()
ctx.arc(start.x, start.y, 5, 0, 2 * Math.PI) // Start point
ctx.arc(end.x, end.y, 5, 0, 2 * Math.PI) // End point
ctx.fill()

// Control points
ctx.fillStyle = 'red'
ctx.beginPath()
ctx.arc(cp1.x, cp1.y, 5, 0, 2 * Math.PI) // Control point one
ctx.arc(cp2.x, cp2.y, 5, 0, 2 * Math.PI) // Control point two
ctx.fill()
await snapshotImage(t)
})

test('clearRect', async (t) => {
const { ctx, canvas } = t.context
ctx.beginPath()
ctx.fillStyle = '#ff6'
ctx.fillRect(0, 0, canvas.width, canvas.height)

// Draw blue triangle
ctx.beginPath()
ctx.fillStyle = 'blue'
ctx.moveTo(20, 20)
ctx.lineTo(180, 20)
ctx.lineTo(130, 130)
ctx.closePath()
ctx.fill()

// Clear part of the canvas
ctx.clearRect(10, 10, 120, 100)
await snapshotImage(t)
})

test('clip', async (t) => {
const { ctx, canvas } = t.context
// Create circular clipping region
ctx.beginPath()
ctx.arc(100, 75, 50, 0, Math.PI * 2)
ctx.clip()

// Draw stuff that gets clipped
ctx.fillStyle = 'blue'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = 'orange'
ctx.fillRect(0, 0, 100, 100)
await snapshotImage(t)
})

test('closePath', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.moveTo(20, 140) // Move pen to bottom-left corner
ctx.lineTo(120, 10) // Line to top corner
ctx.lineTo(220, 140) // Line to bottom-right corner
ctx.closePath() // Line to bottom-left corner
ctx.stroke()
await snapshotImage(t)
})

test('closePath-arc', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.arc(240, 20, 40, 0, Math.PI)
ctx.moveTo(100, 20)
ctx.arc(60, 20, 40, 0, Math.PI)
ctx.moveTo(215, 80)
ctx.arc(150, 80, 65, 0, Math.PI)
ctx.closePath()
ctx.lineWidth = 6
ctx.stroke()

await snapshotImage(t)
})

test.todo('createImageData')

test('createLinearGradient', async (t) => {
const { ctx } = t.context
const gradient = ctx.createLinearGradient(20, 0, 220, 0)

// Add three color stops
gradient.addColorStop(0, 'green')
gradient.addColorStop(0.5, 'cyan')
gradient.addColorStop(1, 'green')

// Set the fill style and draw a rectangle
ctx.fillStyle = gradient
ctx.fillRect(20, 20, 200, 100)
await snapshotImage(t)
})

test.todo('createPattern')

test('createRadialGradient', async (t) => {
const { ctx } = t.context
const gradient = ctx.createRadialGradient(110, 90, 30, 100, 100, 70)

// Add three color stops
gradient.addColorStop(0, 'pink')
gradient.addColorStop(0.9, 'white')
gradient.addColorStop(1, 'green')

// Set the fill style and draw a rectangle
ctx.fillStyle = gradient
ctx.fillRect(20, 20, 160, 160)
await snapshotImage(t)
})

test.todo('drawImage')

test('ellipse', async (t) => {
const { ctx } = t.context
// Draw the ellipse
ctx.beginPath()
ctx.ellipse(100, 100, 50, 75, Math.PI / 4, 0, 2 * Math.PI)
ctx.stroke()

// Draw the ellipse's line of reflection
ctx.beginPath()
ctx.setLineDash([5, 5])
ctx.moveTo(0, 200)
ctx.lineTo(200, 0)
ctx.stroke()
await snapshotImage(t)
})

test('fill', async (t) => {
const { ctx } = t.context
const region = new Path2D()
region.moveTo(30, 90)
region.lineTo(110, 20)
region.lineTo(240, 130)
region.lineTo(60, 130)
region.lineTo(190, 20)
region.lineTo(270, 90)
region.closePath()

// Fill path
ctx.fillStyle = 'green'
ctx.fill(region, 'evenodd')
await snapshotImage(t)
})

test('fillRect', async (t) => {
const { ctx } = t.context
ctx.fillStyle = 'hotpink'
ctx.fillRect(20, 10, 150, 100)
await snapshotImage(t)
})
73 changes: 73 additions & 0 deletions __test__/image-data.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import test from 'ava'

import { ImageData } from '../index'

test('should be able to create ImageData', (t) => {
t.notThrows(() => new ImageData(1024, 768))
})

test('should be able to create from Uint8ClampedArray', (t) => {
const pixelArray = Array.from<number>({ length: 4 * 4 * 4 }).fill(255)
const u8array = new Uint8ClampedArray(pixelArray)
const imageData = new ImageData(u8array, 4, 4)
t.not(imageData.data, u8array)
t.deepEqual(imageData.data, u8array)
t.is(imageData.width, 4)
t.is(imageData.height, 4)
})

test('should be able to create from Uint8ClampedArray without height', (t) => {
const pixelArray = Array.from<number>({ length: 4 * 4 * 4 }).fill(233)
const u8array = new Uint8ClampedArray(pixelArray)
const imageData = new ImageData(u8array, 4)
t.not(imageData.data, u8array)
t.deepEqual(imageData.data, u8array)
t.is(imageData.width, 4)
t.is(imageData.height, 4)
})

test('should throw if width * height * 4 not equal to arraybuffer length', (t) => {
const pixelArray = Array.from<number>({ length: 4 * 4 * 4 }).fill(255)
const u8array = new Uint8ClampedArray(pixelArray)
t.throws(() => new ImageData(u8array, 4, 3), {
code: 'InvalidArg',
message: 'Index or size is negative or greater than the allowed amount',
})
})

test('properties should be readonly', (t) => {
const imageData = new ImageData(1024, 768)
const fakeData = new Uint8ClampedArray()
t.throws(
() => {
// @ts-expect-error
imageData.data = fakeData
},
{
instanceOf: TypeError,
message: /Cannot assign to read only property/,
},
)

t.throws(
() => {
// @ts-expect-error
imageData.width = 1111
},
{
instanceOf: TypeError,
message: /Cannot assign to read only property/,
},
)

t.throws(
() => {
// @ts-expect-error
imageData.height = 2222
},
{
instanceOf: TypeError,
message: /Cannot assign to read only property/,
},
)
})
Loading

1 comment on commit 107a650

@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: 107a650 Previous: 5cfe667 Ratio
Draw house#@napi-rs/skia 21.3 ops/sec (±1.21%) 21 ops/sec (±1.14%) 0.99
Draw house#node-canvas 20.6 ops/sec (±2.3%) 20 ops/sec (±1.5%) 0.97
Draw gradient#@napi-rs/skia 22 ops/sec (±0.1%) 20.1 ops/sec (±1.38%) 0.91
Draw gradient#node-canvas 20 ops/sec (±1.63%) 20 ops/sec (±0.73%) 1

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

Please sign in to comment.