Skip to content

Commit

Permalink
feat: async/sync get png data from Canvas
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Dec 24, 2020
1 parent 1b2e5d1 commit f6d8cd6
Show file tree
Hide file tree
Showing 15 changed files with 789 additions and 125 deletions.
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
skia
skia-c
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ updates:
- package-ecosystem: 'npm'
directory: '/'
schedule:
interval: 'daily'
interval: 'weekly'
# Specify labels for npm pull requests
labels:
- 'npm'
Expand All @@ -18,7 +18,7 @@ updates:
- package-ecosystem: 'cargo'
directory: '/'
schedule:
interval: 'daily'
interval: 'weekly'
labels:
- 'rust'
- 'dependencies'
102 changes: 102 additions & 0 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Benchmark

env:
DEBUG: 'napi:*'

on:
push:
branches:
- main
pull_request:

jobs:
bench:
name: Bench
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
with:
submodules: true

- name: Setup node
uses: actions/setup-node@v2-beta
with:
node-version: 14
check-latest: true

- name: Set env
run: |
echo "${PWD}/depot_tools" >> $GITHUB_PATH
shell: bash

- name: Install tools on Linux
run: |
sudo apt-get install -y ninja-build
- name: Install
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: default
override: true

- name: Generate Cargo.lock
uses: actions-rs/cargo@v1
with:
command: generate-lockfile

- name: Cache cargo registry
uses: actions/cache@v1
with:
path: ~/.cargo/registry
key: bench-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}

- name: Cache cargo index
uses: actions/cache@v1
with:
path: ~/.cargo/git
key: bench-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}

- name: Cache NPM dependencies
uses: actions/cache@v1
with:
path: node_modules
key: bench-${{ hashFiles('yarn.lock') }}

- name: 'Install dependencies'
run: yarn install --frozen-lockfile --registry https://registry.npmjs.org --network-timeout 300000

- name: Compile skia
env:
PYTHONHTTPSVERIFY: 0
run: node ./scripts/build-skia.js

- name: 'Build'
run: yarn build

- name: 'Run benchmark'
run: yarn bench

- name: Store benchmark result
uses: rhysd/github-action-benchmark@v1
if: github.ref == 'refs/heads/main'
with:
tool: 'benchmarkjs'
output-file-path: bench.txt
github-token: ${{ secrets.GH_TOKEN }}
auto-push: true

- name: Store benchmark result
uses: rhysd/github-action-benchmark@v1
if: github.ref != 'refs/heads/main'
with:
tool: 'benchmarkjs'
output-file-path: bench.txt
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-always: true

- name: Clear the cargo caches
run: |
cargo install cargo-cache --no-default-features --features ci-autoclean
cargo-cache
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,5 @@ dist
/target
Cargo.lock

*.node
*.node
bench.txt
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ crate-type = ["cdylib"]
[dependencies]
anyhow = "1"
cssparser = "0.28"
napi = "1.0.0-alpha.1"
napi = "1.0.0-alpha.6"
napi-derive = "1.0.0-alpha.0"
thiserror = "1"

Expand All @@ -19,5 +19,5 @@ cc = "1"
napi-build = "1.0.0-alpha.0"

[profile.release]
lto = true
codegen-units = 1
opt-level = 3
62 changes: 52 additions & 10 deletions benchmark/bench.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,70 @@
import { promises as fs } from 'fs'
import { join } from 'path'

import b from 'benny'
import { Summary } from 'benny/lib/internal/common-types'
import { createCanvas, Canvas } from 'canvas'

import { createCanvas as skiaCreateCanvas } from '../index'

function draw(factory: (width: number, height: number) => Canvas) {
const canvas = factory(1024, 768)

const ctx = canvas.getContext('2d')!

ctx.lineWidth = 10
ctx.strokeStyle = '#03a9f4'
ctx.fillStyle = '#03a9f4'

// Wall
ctx.strokeRect(75, 140, 150, 110)

// Door
ctx.fillRect(130, 190, 40, 60)

import { sync } from '../index'
// Roof
ctx.beginPath()
ctx.moveTo(50, 140)
ctx.lineTo(150, 60)
ctx.lineTo(250, 140)
ctx.closePath()
ctx.stroke()

function add(a: number) {
return a + 100
canvas.toBuffer('image/png')
}

async function run() {
await b.suite(
'Add 100',
function house() {
return b.suite(
'Draw house',

b.add('Native a + 100', () => {
sync(10)
b.add('@napi-rs/skia', () => {
// @ts-expect-error
draw(skiaCreateCanvas)
}),

b.add('JavaScript a + 100', () => {
add(10)
b.add('node-canvas', () => {
draw(createCanvas)
}),

b.cycle(),
b.complete(),
)
}

async function run() {
const output = [await house()].map(formatSummary).join('\n')
await fs.writeFile(join(process.cwd(), 'bench.txt'), output, 'utf8')
}

run().catch((e) => {
console.error(e)
})

function formatSummary(summary: Summary): string {
return summary.results
.map(
(result) =>
`${summary.name}#${result.name} x ${result.ops} ops/sec ±${result.margin}% (${result.samples} runs sampled)`,
)
.join('\n')
}
17 changes: 15 additions & 2 deletions example/simple.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { writeFileSync } = require('fs')
const { promises } = require('fs')
const { join } = require('path')

const { createCanvas } = require('../index')
Expand All @@ -25,4 +25,17 @@ ctx.lineTo(250, 140)
ctx.closePath()
ctx.stroke()

writeFileSync(join(__dirname, 'simple.png'), canvas.toBuffer())
console.info(process.memoryUsage())

async function main() {
for (const _ of Array.from({ length: 10000 }).fill(0)) {
await canvas
.png()
.then((data) => promises.writeFile(join(__dirname, 'simple.png'), data))
.then(() => {
console.info(process.memoryUsage())
})
}
}

main()
7 changes: 6 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
export function createCanvas(width: number, height: number): HTMLCanvasElement
export function createCanvas(
width: number,
height: number,
): HTMLCanvasElement & {
png(): Promise<Buffer>
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@typescript-eslint/parser": "^4.11.0",
"ava": "^3.14.0",
"benny": "^3.6.15",
"canvas": "^2.6.1",
"chalk": "^4.1.0",
"eslint": "^7.16.0",
"eslint-config-prettier": "^7.1.0",
Expand Down
36 changes: 21 additions & 15 deletions skia-c/skia_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ extern "C"

bool skiac_surface_save(skiac_surface *c_surface, const char *path)
{
sk_sp<SkImage> image = SURFACE_CAST->makeImageSnapshot();
sk_sp<SkData> data = image->encodeToData(SkEncodedImageFormat::kPNG, 0);
auto image = SURFACE_CAST->makeImageSnapshot();
auto data = image->encodeToData(SkEncodedImageFormat::kPNG, 0);
if (data)
{
SkFILEWStream stream(path);
Expand All @@ -93,19 +93,6 @@ extern "C"
return false;
}

void skiac_surface_png_data(skiac_surface *c_surface, skiac_surface_data *data)
{
auto image = SURFACE_CAST->makeImageSnapshot();
auto png_data = image->encodeToData();
data->ptr = nullptr;
data->size = 0;
if (png_data)
{
data->ptr = const_cast<uint8_t *>(png_data->bytes());
data->size = png_data->size();
}
}

void skiac_surface_destroy(skiac_surface *c_surface)
{
// SkSurface is ref counted.
Expand Down Expand Up @@ -161,6 +148,18 @@ extern "C"
}
}

void skiac_surface_png_data(skiac_surface *c_surface, skiac_sk_data *data)
{
auto image = SURFACE_CAST->makeImageSnapshot();
auto png_data = image->encodeToData().release();
if (png_data)
{
data->ptr = const_cast<uint8_t *>(png_data->bytes());
data->size = png_data->size();
data->data = reinterpret_cast<skiac_data *>(png_data);
}
}

int skiac_surface_get_alpha_type(skiac_surface *c_surface)
{
return SURFACE_CAST->imageInfo().alphaType();
Expand Down Expand Up @@ -675,4 +674,11 @@ extern "C"
auto mask_filter = MASK_FILTER_CAST;
SkSafeUnref(mask_filter);
}

// SkData
void skiac_sk_data_destroy(skiac_data *c_data)
{
auto sk_data = reinterpret_cast<SkData *>(c_data);
SkSafeUnref(sk_data);
}
}
13 changes: 12 additions & 1 deletion skia-c/skia_c.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef struct skiac_shader skiac_shader;
typedef struct skiac_path_effect skiac_path_effect;
typedef struct skiac_matrix skiac_matrix;
typedef struct skiac_mask_filter skiac_mask_filter;
typedef struct skiac_data skiac_data;

struct skiac_transform
{
Expand All @@ -44,6 +45,13 @@ struct skiac_surface_data
size_t size;
};

struct skiac_sk_data
{
uint8_t *ptr;
size_t size;
skiac_data *data;
};

extern "C"
{

Expand All @@ -61,7 +69,7 @@ extern "C"
int skiac_surface_get_width(skiac_surface *c_surface);
int skiac_surface_get_height(skiac_surface *c_surface);
void skiac_surface_read_pixels(skiac_surface *c_surface, skiac_surface_data *data);
void skiac_surface_png_data(skiac_surface *c_surface, skiac_surface_data *data);
void skiac_surface_png_data(skiac_surface *c_surface, skiac_sk_data *data);
int skiac_surface_get_alpha_type(skiac_surface *c_surface);
bool skiac_surface_save(skiac_surface *c_surface, const char *path);

Expand Down Expand Up @@ -194,6 +202,9 @@ extern "C"
skiac_mask_filter *skiac_mask_filter_make_blur(float radius);

void skiac_mask_filter_destroy(skiac_mask_filter *c_mask_filter);

// SkData
void skiac_sk_data_destroy(skiac_data *c_data);
}

#endif // SKIA_CAPI_H
Loading

0 comments on commit f6d8cd6

Please sign in to comment.