Skip to content

Commit

Permalink
feat: ImageData relates API and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Jan 7, 2021
1 parent 1f11d7d commit 24c7990
Show file tree
Hide file tree
Showing 25 changed files with 748 additions and 37 deletions.
265 changes: 264 additions & 1 deletion __test__/draw.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,23 @@ test('closePath-arc', async (t) => {
await snapshotImage(t)
})

test.todo('createImageData')
test('createImageData', async (t) => {
const { ctx } = t.context
const imageData = ctx.createImageData(256, 256)

// Iterate through every pixel
for (let i = 0; i < imageData.data.length; i += 4) {
// Modify pixel data
imageData.data[i + 0] = 190 // R value
imageData.data[i + 1] = 0 // G value
imageData.data[i + 2] = 210 // B value
imageData.data[i + 3] = 255 // A value
}

// Draw image data to the canvas
ctx.putImageData(imageData, 20, 20)
await snapshotImage(t)
})

test('createLinearGradient', async (t) => {
const { ctx } = t.context
Expand Down Expand Up @@ -255,3 +271,250 @@ test('fillRect', async (t) => {
ctx.fillRect(20, 10, 150, 100)
await snapshotImage(t)
})

test.todo('fillText')

test.todo('getContextAttributes')

test('getImageData', async (t) => {
const { ctx } = t.context
ctx.rect(10, 10, 100, 100)
ctx.fill()
const imageData = ctx.getImageData(60, 60, 200, 100)
ctx.putImageData(imageData, 150, 10)
await snapshotImage(t)
})

test.todo('isPointInPath')

test.todo('isPointInStroke')

test('lineTo', async (t) => {
const { ctx } = t.context
ctx.beginPath() // Start a new path
ctx.moveTo(30, 50) // Move the pen to (30, 50)
ctx.lineTo(150, 100) // Draw a line to (150, 100)
ctx.stroke() // Render the path
await snapshotImage(t)
})

test.todo('measureText')

test('moveTo', async (t) => {
const { ctx } = t.context
ctx.beginPath()
ctx.moveTo(50, 50) // Begin first sub-path
ctx.lineTo(200, 50)
ctx.moveTo(50, 90) // Begin second sub-path
ctx.lineTo(280, 120)
ctx.stroke()
await snapshotImage(t)
})

test('putImageData', async (t) => {
const { ctx } = t.context
function putImageData(
imageData: ImageData,
dx: number,
dy: number,
dirtyX: number,
dirtyY: number,
dirtyWidth: number,
dirtyHeight: number,
) {
const data = imageData.data
const height = imageData.height
const width = imageData.width
dirtyX = dirtyX || 0
dirtyY = dirtyY || 0
dirtyWidth = dirtyWidth !== undefined ? dirtyWidth : width
dirtyHeight = dirtyHeight !== undefined ? dirtyHeight : height
const limitBottom = dirtyY + dirtyHeight
const limitRight = dirtyX + dirtyWidth
for (let y = dirtyY; y < limitBottom; y++) {
for (let x = dirtyX; x < limitRight; x++) {
const pos = y * width + x
ctx.fillStyle =
'rgba(' +
data[pos * 4 + 0] +
',' +
data[pos * 4 + 1] +
',' +
data[pos * 4 + 2] +
',' +
data[pos * 4 + 3] / 255 +
')'
ctx.fillRect(x + dx, y + dy, 1, 1)
}
}
}

// Draw content onto the canvas
ctx.fillRect(0, 0, 100, 100)
// Create an ImageData object from it
const imagedata = ctx.getImageData(0, 0, 100, 100)
// use the putImageData function that illustrates how putImageData works
putImageData(imagedata, 150, 0, 50, 50, 25, 25)

await snapshotImage(t)
})

test('quadraticCurveTo', async (t) => {
const { ctx } = t.context
// Quadratic Bézier curve
ctx.beginPath()
ctx.moveTo(50, 20)
ctx.quadraticCurveTo(230, 30, 50, 100)
ctx.stroke()

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

// Control point
ctx.fillStyle = 'red'
ctx.beginPath()
ctx.arc(230, 30, 5, 0, 2 * Math.PI)
ctx.fill()
await snapshotImage(t)
})

test('rect', async (t) => {
const { ctx } = t.context
ctx.fillStyle = 'yellow'
ctx.rect(10, 20, 150, 100)
ctx.fill()
await snapshotImage(t)
})

test.todo('resetTransform')

test('save-restore', async (t) => {
const { ctx } = t.context
// Save the default state
ctx.save()

ctx.fillStyle = 'green'
ctx.fillRect(10, 10, 100, 100)

// Restore the default state
ctx.restore()

ctx.fillRect(150, 40, 100, 100)

await snapshotImage(t)
})

test.todo('rotate')

test.todo('scale')

test('setLineDash', async (t) => {
const { ctx } = t.context
// Dashed line
ctx.beginPath()
ctx.setLineDash([5, 15])
ctx.moveTo(0, 50)
ctx.lineTo(300, 50)
ctx.stroke()

// Solid line
ctx.beginPath()
ctx.setLineDash([])
ctx.moveTo(0, 100)
ctx.lineTo(300, 100)
ctx.stroke()
await snapshotImage(t)
})

test('setTransform', async (t) => {
const { ctx } = t.context
ctx.setTransform(1, 0.2, 0.8, 1, 0, 0)
ctx.fillRect(0, 0, 100, 100)
await snapshotImage(t)
})

test('stroke', async (t) => {
const { ctx } = t.context
// First sub-path
ctx.lineWidth = 26
ctx.strokeStyle = 'orange'
ctx.moveTo(20, 20)
ctx.lineTo(160, 20)
ctx.stroke()

// Second sub-path
ctx.lineWidth = 14
ctx.strokeStyle = 'green'
ctx.moveTo(20, 80)
ctx.lineTo(220, 80)
ctx.stroke()

// Third sub-path
ctx.lineWidth = 4
ctx.strokeStyle = 'pink'
ctx.moveTo(20, 140)
ctx.lineTo(280, 140)
ctx.stroke()
await snapshotImage(t)
})

test('stroke-and-filling', async (t) => {
const { ctx } = t.context
ctx.lineWidth = 16
ctx.strokeStyle = 'red'

// Stroke on top of fill
ctx.beginPath()
ctx.rect(25, 25, 100, 100)
ctx.fill()
ctx.stroke()

// Fill on top of stroke
ctx.beginPath()
ctx.rect(175, 25, 100, 100)
ctx.stroke()
ctx.fill()
await snapshotImage(t)
})

test('strokeRect', async (t) => {
const { ctx } = t.context
ctx.shadowColor = '#d53'
ctx.shadowBlur = 20
ctx.lineJoin = 'bevel'
ctx.lineWidth = 15
ctx.strokeStyle = '#38f'
ctx.strokeRect(30, 30, 160, 90)
await snapshotImage(t)
})

test.todo('strokeText')

test('transform', async (t) => {
const { ctx } = t.context
ctx.transform(1, 0.2, 0.8, 1, 0, 0)
ctx.fillRect(0, 0, 100, 100)
ctx.resetTransform()
ctx.fillRect(220, 0, 100, 100)
await snapshotImage(t)
})

test('translate', async (t) => {
const { ctx } = t.context
// Moved square
ctx.translate(110, 30)
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, 80, 80)

// Reset current transformation matrix to the identity matrix
ctx.setTransform(1, 0, 0, 1, 0, 0)

// Unmoved square
ctx.fillStyle = 'gray'
ctx.fillRect(0, 0, 80, 80)
await snapshotImage(t)
})
Binary file added __test__/failure/transform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions __test__/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ test('globalCompositeOperation state should be ok', (t) => {
t.is(ctx.globalCompositeOperation, 'xor')
})

test('imageSmoothingEnabled state should be ok', (t) => {
const { ctx } = t.context
t.is(ctx.imageSmoothingEnabled, true)
ctx.imageSmoothingEnabled = false
t.is(ctx.imageSmoothingEnabled, false)
})

test('imageSmoothingQuality state should be ok', (t) => {
const { ctx } = t.context
t.is(ctx.imageSmoothingQuality, 'low')
ctx.imageSmoothingQuality = 'high'
t.is(ctx.imageSmoothingQuality, 'high')
})

test('lineCap state should be ok', (t) => {
const { ctx } = t.context
t.is(ctx.lineCap, 'butt')
Expand Down Expand Up @@ -114,3 +128,12 @@ test('shadowOffsetY state should be ok', (t) => {
ctx.shadowOffsetY = 10
t.is(ctx.shadowOffsetY, 10)
})

test('lineDash state should be ok', (t) => {
const { ctx } = t.context
const lineDash = [1, 2, 4.5, 7]
ctx.setLineDash(lineDash)
t.deepEqual(ctx.getLineDash(), lineDash)
})

test.todo('getTransform')
Binary file added __test__/snapshots/createImageData.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/getImageData.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/lineTo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/moveTo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/putImageData.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/quadraticCurveTo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/rect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/save-restore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/setLineDash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/setTransform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/stroke-and-filling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/stroke.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/strokeRect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/transform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __test__/snapshots/translate.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ const { loadBinding } = require('@node-rs/helper')
*/
const { CanvasRenderingContext2D, CanvasElement, Path2D, ImageData } = loadBinding(__dirname, 'skia', '@napi-rs/skia')

CanvasRenderingContext2D.prototype.getImageData = function getImageData(x, y, w, h) {
const data = this._getImageData(x, y, w, h)
return new ImageData(data, w, h)
}

function createCanvas(width, height) {
const canvasElement = new CanvasElement(width, height)
const ctx = new CanvasRenderingContext2D(width, height)
Expand All @@ -29,6 +34,18 @@ function createCanvas(width, height) {
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,
Expand Down
2 changes: 1 addition & 1 deletion skia
Submodule skia updated 1510 files
23 changes: 23 additions & 0 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,13 @@ extern "C"
}
}

bool skiac_surface_read_pixels_rect(skiac_surface *c_surface, uint8_t *data, int x, int y, int w, int h)
{
auto image_info = SkImageInfo::Make(w, h, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB());
auto result = SURFACE_CAST->readPixels(image_info, data, w * 4, x, y);
return result;
}

void skiac_surface_png_data(skiac_surface *c_surface, skiac_sk_data *data)
{
auto image = SURFACE_CAST->makeImageSnapshot();
Expand Down Expand Up @@ -285,6 +292,22 @@ extern "C"
CANVAS_CAST->restore();
}

void skiac_canvas_write_pixels(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, int x, int y)
{
auto info = SkImageInfo::Make(width, height, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType);
CANVAS_CAST->writePixels(info, pixels, row_bytes, x, y);
}

void skiac_canvas_write_pixels_dirty(skiac_canvas *c_canvas, int width, int height, uint8_t *pixels, size_t row_bytes, size_t length, float x, float y, float dirty_x, float dirty_y, float dirty_width, float dirty_height)
{
auto info = SkImageInfo::Make(width, height, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kUnpremul_SkAlphaType);
auto data = SkData::MakeFromMalloc(pixels, length);
auto image = SkImage::MakeRasterData(info, data, row_bytes);
auto src_rect = SkRect::MakeXYWH(dirty_x, dirty_y, dirty_width, dirty_height);
auto dst_rect = SkRect::MakeXYWH(x + dirty_x, y + dirty_y, dirty_width, dirty_height);
CANVAS_CAST->drawImageRect(image, src_rect, dst_rect, nullptr);
}

// Paint

skiac_paint *skiac_paint_create()
Expand Down
Loading

1 comment on commit 24c7990

@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: 24c7990 Previous: 1f11d7d Ratio
Draw house#@napi-rs/skia 23 ops/sec (±0.98%) 27 ops/sec (±0.11%) 1.17
Draw house#node-canvas 19 ops/sec (±0.85%) 24 ops/sec (±0.35%) 1.26
Draw gradient#@napi-rs/skia 23 ops/sec (±0.15%) 26 ops/sec (±0.04%) 1.13
Draw gradient#node-canvas 19 ops/sec (±0.25%) 23 ops/sec (±0.17%) 1.21

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

Please sign in to comment.