From 12e061cdbd2ab4849a846cef2d5077bd00793613 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 25 Dec 2020 12:18:30 +0800 Subject: [PATCH] feat: support gradient in fill/stroke style --- Cargo.toml | 2 +- benchmark/bench.ts | 51 +++---------------------------------------- benchmark/gradient.ts | 42 +++++++++++++++++++++++++++++++++++ benchmark/house.ts | 49 +++++++++++++++++++++++++++++++++++++++++ example/gradient.js | 26 ++++++++++++++++++++++ src/ctx.rs | 17 ++++++++++----- src/sk.rs | 4 ++-- 7 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 benchmark/gradient.ts create mode 100644 benchmark/house.ts create mode 100644 example/gradient.js diff --git a/Cargo.toml b/Cargo.toml index 712281a3..2c1e7902 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1" cssparser = "0.28" -napi = "1.0.0-alpha.7" +napi = "1.0.0-alpha.9" napi-derive = "1.0.0-alpha.0" thiserror = "1" diff --git a/benchmark/bench.ts b/benchmark/bench.ts index 76d925f7..02b76bc4 100644 --- a/benchmark/bench.ts +++ b/benchmark/bench.ts @@ -1,58 +1,13 @@ 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) - - // Roof - ctx.beginPath() - ctx.moveTo(50, 140) - ctx.lineTo(150, 60) - ctx.lineTo(250, 140) - ctx.closePath() - ctx.stroke() - - canvas.toBuffer('image/png') -} - -function house() { - return b.suite( - 'Draw house', - - b.add('@napi-rs/skia', () => { - // @ts-expect-error - draw(skiaCreateCanvas) - }), - - b.add('node-canvas', () => { - draw(createCanvas) - }), - - b.cycle(), - b.complete(), - ) -} +import { house } from './house' +import { gradient } from './gradient' async function run() { - const output = [await house()].map(formatSummary).join('\n') + const output = [await house(), await gradient()].map(formatSummary).join('\n') await fs.writeFile(join(process.cwd(), 'bench.txt'), output, 'utf8') } diff --git a/benchmark/gradient.ts b/benchmark/gradient.ts new file mode 100644 index 00000000..9ab83379 --- /dev/null +++ b/benchmark/gradient.ts @@ -0,0 +1,42 @@ +import b from 'benny' + +import { createCanvas, Canvas } from 'canvas' + +import { createCanvas as skiaCreateCanvas } from '../index' + +function drawGradient(factory: (width: number, height: number) => Canvas) { + const canvas = factory(1024, 768) + + const ctx = canvas.getContext('2d')! + + 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) + + canvas.toBuffer('image/png') +} + +export function gradient() { + return b.suite( + 'Draw gradient', + + b.add('@napi-rs/skia', () => { + // @ts-expect-error + drawGradient(skiaCreateCanvas) + }), + + b.add('node-canvas', () => { + drawGradient(createCanvas) + }), + + b.cycle(), + b.complete(), + ) +} diff --git a/benchmark/house.ts b/benchmark/house.ts new file mode 100644 index 00000000..ebcfc608 --- /dev/null +++ b/benchmark/house.ts @@ -0,0 +1,49 @@ +import b from 'benny' + +import { createCanvas, Canvas } from 'canvas' + +import { createCanvas as skiaCreateCanvas } from '../index' + +function drawHouse(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) + + // Roof + ctx.beginPath() + ctx.moveTo(50, 140) + ctx.lineTo(150, 60) + ctx.lineTo(250, 140) + ctx.closePath() + ctx.stroke() + + canvas.toBuffer('image/png') +} + +export function house() { + return b.suite( + 'Draw house', + + b.add('@napi-rs/skia', () => { + // @ts-expect-error + drawHouse(skiaCreateCanvas) + }), + + b.add('node-canvas', () => { + drawHouse(createCanvas) + }), + + b.cycle(), + b.complete(), + ) +} diff --git a/example/gradient.js b/example/gradient.js new file mode 100644 index 00000000..a65a3f0d --- /dev/null +++ b/example/gradient.js @@ -0,0 +1,26 @@ +const { promises } = require('fs') +const { join } = require('path') + +const { createCanvas } = require('../index') + +const canvas = createCanvas(1024, 768) + +const ctx = canvas.getContext('2d') + +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) + +canvas + .png() + .then((data) => promises.writeFile(join(__dirname, 'gradient.png'), data)) + .catch((e) => { + console.error(e) + }) diff --git a/src/ctx.rs b/src/ctx.rs index 07be5408..3be897c3 100644 --- a/src/ctx.rs +++ b/src/ctx.rs @@ -1060,13 +1060,15 @@ fn set_fill_style(ctx: CallContext) -> Result { match js_fill_style.get_type()? { ValueType::String => { let js_color = - unsafe { JsString::from_raw_unchecked(ctx.env.raw(), js_fill_style.raw()) }.into_utf8()?; + unsafe { js_fill_style.cast::() }.into_utf8()?; context_2d.set_fill_style(Pattern::from_color(js_color.as_str()?)?)?; } - // TODO, image and gradient - ValueType::External => { - todo!(); + ValueType::Object => { + let fill_object = unsafe { js_fill_style.cast::() }; + let gradient = ctx.env.unwrap::(&fill_object)?; + context_2d.set_fill_style(Pattern::Gradient(gradient.clone())); } + // todo ImagePattern _ => return Err(Error::new(Status::InvalidArg, format!("Invalid fillStyle"))), } @@ -1094,7 +1096,12 @@ fn set_stroke_style(ctx: CallContext) -> Result { .into_utf8()?; context_2d.set_stroke_style(Pattern::from_color(js_color.as_str()?)?)?; } - // TODO, image and gradient + ValueType::Object => { + let stroke_object = unsafe { js_stroke_style.cast::() }; + let gradient = ctx.env.unwrap::(&stroke_object)?; + context_2d.set_stroke_style(Pattern::Gradient(gradient.clone())); + } + // todo ImagePattern ValueType::External => {} _ => { return Err(Error::new( diff --git a/src/sk.rs b/src/sk.rs index 1c9608a2..4facfdd7 100644 --- a/src/sk.rs +++ b/src/sk.rs @@ -1597,8 +1597,8 @@ impl Transform { let x_trans = self.a * x + self.b * y + self.c; // Dx+Ey+F let y_trans = self.d * x + self.e * y + self.e; - result_arr[i] = x_trans / denom; - result_arr[i + 1] = y_trans / denom; + result_arr.push(x_trans / denom); + result_arr.push(y_trans / denom); i += 2; } result_arr