From ec55b3a5ad4d905f060ddf0aa7d047849369bb82 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Mon, 16 Aug 2021 20:30:21 +0800 Subject: [PATCH] fix: pass the fabric.js visual tests - Fix addColorStop sort issue in CanvasGradient - Fix Transform::inverse method - No need map_points in CanvasGradient::get_shader - CSS Color now allowed to be empty string - Allow createPattern from another Canvas - toDataURL should be STANDARD base64 - swap Path in beginPath for better performance --- CHANGELOG.md | 8 +- Cargo.toml | 4 +- __test__/canvas-class.spec.ts | 7 + __test__/draw.spec.ts | 56 +- __test__/image-snapshot.ts | 5 +- __test__/notoemoji-person.svg | 80 + __test__/regression.spec.ts | 42 + .../snapshots/draw-image-svg-noto-emoji.png | Bin 0 -> 19181 bytes .../snapshots/drawImage-another-Canvas.png | Bin 0 -> 5893 bytes __test__/snapshots/transform-with-state.png | Bin 0 -> 784 bytes .../snapshots/translate-with-transform.png | Bin 0 -> 1305 bytes index.d.ts | 16 +- index.html | 2 + index.js | 9 + npm/android-arm64/package.json | 14 +- npm/darwin-arm64/package.json | 14 +- npm/darwin-x64/package.json | 14 +- npm/linux-arm-gnueabihf/package.json | 14 +- npm/linux-arm64-gnu/package.json | 14 +- npm/linux-arm64-musl/package.json | 14 +- npm/linux-x64-gnu/package.json | 14 +- npm/linux-x64-musl/package.json | 14 +- npm/win32-x64-msvc/package.json | 15 +- package.json | 31 +- pnpm-lock.yaml | 2811 ++++++++++------- renovate.json | 4 +- skia-c/skia_c.cpp | 54 +- skia-c/skia_c.hpp | 26 +- src/ctx.rs | 197 +- src/gradient.rs | 88 +- src/image.rs | 69 +- src/image_pattern.rs | 31 +- src/lib.rs | 19 +- src/pattern.rs | 2 +- src/sk.rs | 174 +- 35 files changed, 2392 insertions(+), 1470 deletions(-) create mode 100644 __test__/canvas-class.spec.ts create mode 100644 __test__/notoemoji-person.svg create mode 100644 __test__/regression.spec.ts create mode 100644 __test__/snapshots/draw-image-svg-noto-emoji.png create mode 100644 __test__/snapshots/drawImage-another-Canvas.png create mode 100644 __test__/snapshots/transform-with-state.png create mode 100644 __test__/snapshots/translate-with-transform.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 2df93bcc..03d32a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,12 @@ ## [0.1.5](https://github.com/Brooooooklyn/canvas/compare/v0.1.4...v0.1.5) (2021-08-13) - ### Bug Fixes -* default value of FilterQuality in SamplingOptions should be high ([389aa26](https://github.com/Brooooooklyn/canvas/commit/389aa26fda79dfca15b6f2d87fbd03fe92f28758)) - +- default value of FilterQuality in SamplingOptions should be high ([389aa26](https://github.com/Brooooooklyn/canvas/commit/389aa26fda79dfca15b6f2d87fbd03fe92f28758)) ### Features -* add resize SVG demo ([bf8388d](https://github.com/Brooooooklyn/canvas/commit/bf8388ddab06474a01362829437bcfc7df4bd248)) - - +- add resize SVG demo ([bf8388d](https://github.com/Brooooooklyn/canvas/commit/bf8388ddab06474a01362829437bcfc7df4bd248)) ## [0.1.4](https://github.com/Brooooooklyn/canvas/compare/v0.1.3...v0.1.4) (2021-08-11) diff --git a/Cargo.toml b/Cargo.toml index 20818397..4a778058 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,12 +23,14 @@ serde_json = "1" thiserror = "1.0" [target.'cfg(all(target_arch = "x86_64", not(target_env = "musl")))'.dependencies] -mimalloc = {version = "0.1"} +mimalloc-rust = "0.1" [build-dependencies] cc = "1" napi-build = "1" [profile.release] +codegen-units = 1 lto = true +panic = "unwind" strip = 'symbols' diff --git a/__test__/canvas-class.spec.ts b/__test__/canvas-class.spec.ts new file mode 100644 index 00000000..01bb9183 --- /dev/null +++ b/__test__/canvas-class.spec.ts @@ -0,0 +1,7 @@ +import test from 'ava' + +import { createCanvas, Canvas } from '../index' + +test('Canvas constructor should be equal to createCanvas', (t) => { + t.deepEqual(createCanvas(200, 100), new Canvas(200, 100)) +}) diff --git a/__test__/draw.spec.ts b/__test__/draw.spec.ts index 498b3132..ccf1633f 100644 --- a/__test__/draw.spec.ts +++ b/__test__/draw.spec.ts @@ -364,6 +364,38 @@ test('drawImage-svg without width height should be empty image', async (t) => { t.deepEqual(outputData.data, Buffer.alloc(outputData.width * outputData.height * 4, 0)) }) +test('draw-image-svg-noto-emoji', async (t) => { + const { ctx } = t.context + const filePath = './notoemoji-person.svg' + const file = await promises.readFile(join(__dirname, filePath)) + const image = new Image() + image.src = file + ctx.drawImage(image, 0, 0) + await snapshotImage(t) +}) + +test('drawImage-another-Canvas', async (t) => { + const { ctx } = t.context + + ctx.fillStyle = 'hotpink' + ctx.fillRect(10, 10, 100, 100) + + const anotherCanvas = createCanvas(200, 200) + const anotherContext = anotherCanvas.getContext('2d') + anotherContext.beginPath() + anotherContext.ellipse(80, 80, 50, 75, Math.PI / 4, 0, 2 * Math.PI) + anotherContext.stroke() + + // Draw the ellipse's line of reflection + anotherContext.beginPath() + anotherContext.setLineDash([5, 5]) + anotherContext.moveTo(10, 150) + anotherContext.lineTo(150, 10) + anotherContext.stroke() + ctx.drawImage(anotherCanvas, 150, 150) + await snapshotImage(t) +}) + test('ellipse', async (t) => { const { ctx } = t.context // Draw the ellipse @@ -836,19 +868,29 @@ test('transform', async (t) => { test('translate', async (t) => { const { ctx } = t.context - drawHouse(ctx) + drawTranslate(ctx) + await snapshotImage(t) +}) + +test('translate-with-transform', async (t) => { + const { ctx } = t.context + ctx.translate(110, 30) + ctx.transform(1, 0, 0, 1, -20, -10) + ctx.transform(1, 0, 0, 1, 0, 0) + ctx.fillStyle = 'red' + ctx.fillRect(-30, -10, 80, 80) await snapshotImage(t) }) test('webp-output', async (t) => { const { ctx } = t.context - drawHouse(ctx) + drawTranslate(ctx) await snapshotImage(t, t.context, 'webp') }) test('raw output', async (t) => { const { ctx, canvas } = t.context - drawHouse(ctx) + drawTranslate(ctx) const output = canvas.data() const pngFromCanvas = await canvas.encode('png') @@ -858,7 +900,7 @@ test('raw output', async (t) => { test('toDataURL', async (t) => { const { ctx, canvas } = t.context - drawHouse(ctx) + drawTranslate(ctx) const output = canvas.toDataURL() const prefix = 'data:image/png;base64,' @@ -870,7 +912,7 @@ test('toDataURL', async (t) => { test('toDataURL with quality', async (t) => { const { ctx, canvas } = t.context - drawHouse(ctx) + drawTranslate(ctx) const output = canvas.toDataURL('image/jpeg', 20) const prefix = 'data:image/jpeg;base64,' @@ -882,7 +924,7 @@ test('toDataURL with quality', async (t) => { test('toDataURLAsync', async (t) => { const { ctx, canvas } = t.context - drawHouse(ctx) + drawTranslate(ctx) const output = await canvas.toDataURLAsync() const prefix = 'data:image/png;base64,' t.true(output.startsWith(prefix)) @@ -891,7 +933,7 @@ test('toDataURLAsync', async (t) => { t.deepEqual(pngBuffer, await canvas.encode('png')) }) -function drawHouse(ctx: SKRSContext2D) { +function drawTranslate(ctx: SKRSContext2D) { // Moved square ctx.translate(110, 30) ctx.fillStyle = 'red' diff --git a/__test__/image-snapshot.ts b/__test__/image-snapshot.ts index 20e1f2d7..e7babe6a 100644 --- a/__test__/image-snapshot.ts +++ b/__test__/image-snapshot.ts @@ -1,5 +1,6 @@ import { promises as fs } from 'fs' import { join } from 'path' +import { arch } from 'os' import PNG from '@jimp/png' import JPEG from '@jimp/jpeg' @@ -7,12 +8,13 @@ import { ExecutionContext } from 'ava' const png = PNG() const jpeg = JPEG() +const ARCH_NAME = arch() export async function snapshotImage( t: ExecutionContext, context = t.context, type: 'png' | 'jpeg' | 'webp' = 'png', - differentRatio = 0.015, + differentRatio = ARCH_NAME === 'x64' ? 0.015 : 0.3, ) { // @ts-expect-error const { canvas } = context @@ -53,7 +55,6 @@ export async function snapshotImage( diffCount++ } }) - // diff ratio greater than 0.01% if (diffCount / existedPixels.length > differentRatio / 100) { await writeFailureImage() t.fail(`Image bytes is not equal, different ratio is ${((diffCount / existedPixels.length) * 100).toFixed(2)}%`) diff --git a/__test__/notoemoji-person.svg b/__test__/notoemoji-person.svg new file mode 100644 index 00000000..0b8fe051 --- /dev/null +++ b/__test__/notoemoji-person.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/__test__/regression.spec.ts b/__test__/regression.spec.ts new file mode 100644 index 00000000..7ff15290 --- /dev/null +++ b/__test__/regression.spec.ts @@ -0,0 +1,42 @@ +import ava, { TestInterface } from 'ava' + +import { createCanvas, Canvas, SKRSContext2D } 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') + ctx.translate(128.5, 128.5) + ctx.scale(1, 1) + ctx.clearRect(-128, -128, 256, 256) + ctx.beginPath() + ctx.moveTo(-52.5, -38.5) + ctx.lineTo(52.5, -38.5) + ctx.lineTo(52.5, 38.5) + ctx.lineTo(-52.5, 38.5) + ctx.lineTo(-52.5, -38.5) + ctx.closePath() + ctx.save() + const p = ctx.createLinearGradient(0, 0, 0, 77) + p.addColorStop(1, 'rgba(0, 128, 128, 1)') + p.addColorStop(0.6, 'rgba(0, 255, 255, 1)') + p.addColorStop(0.3, 'rgba(176, 199, 45, 1)') + p.addColorStop(0.0, 'rgba(204, 82, 51, 1)') + ctx.fillStyle = p + ctx.transform(1, 0, 0, 1, -52.5, -38.5) + ctx.transform(1, 0, 0, 1, 0, 0) + ctx.fill() + ctx.restore() + await snapshotImage(t, { canvas, ctx }) +}) diff --git a/__test__/snapshots/draw-image-svg-noto-emoji.png b/__test__/snapshots/draw-image-svg-noto-emoji.png new file mode 100644 index 0000000000000000000000000000000000000000..f00c955dc438e6a6e073a0a284f840503b6a246a GIT binary patch literal 19181 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 literal 0 HcmV?d00001 diff --git a/__test__/snapshots/drawImage-another-Canvas.png b/__test__/snapshots/drawImage-another-Canvas.png new file mode 100644 index 0000000000000000000000000000000000000000..0527029ff641ae4e714c5d464a7815dfca7c18a3 GIT binary patch literal 5893 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 Bjimqp literal 0 HcmV?d00001 diff --git a/__test__/snapshots/transform-with-state.png b/__test__/snapshots/transform-with-state.png new file mode 100644 index 0000000000000000000000000000000000000000..d351b423f990dc2036a83f0dabd11cd7c08372df GIT binary patch literal 784 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$6%N?Bp530R%N1DIE+9OvgQ4 z978JRyuI6)8SNKMI^+0$Ba>5@}Zgf8{>^iJVx;@7BoZ4`Vk$*z5SziV9u&+$lxj_2LwAKZ(H@3UjP)5qX)U7VT0nnl5gaf1tk z5@SLqgNrz0LI;CO8H0{s!wJy_RVD#W25%OHP0S!UbkQPKkm$4qpxPI|p1u%1@Rq|N zh5djKW5*kY2qlJzoFM&?Oq+ma3nNTXW0=O_@a5kd|6_dd#U*yNw!b(EjF|-X+Hu02 zvS>4BgCbC97J~%Ln3-S`RTY4afmpkKdA_?mW1bt#Y?c(5TOjV<$prEflD&2OAm<>N zV`X2-b%u|zBZe{JQ^gm?cjulyvTv>_TR!>yuVx>HtG|C=RBxCq*x&_n#MRrkA-q#8 z2Lu>bumGJGz{%juvOtKTjl)5efd{H?DuaOwgVuYHmZ=~=;86q$=#YDiAoapHO#&-o q0$I6-8Khnf$vWDk8wQ4k|8MoT2A17l@v2M}WQV7#pUXO@geCxw5x77A literal 0 HcmV?d00001 diff --git a/__test__/snapshots/translate-with-transform.png b/__test__/snapshots/translate-with-transform.png new file mode 100644 index 0000000000000000000000000000000000000000..43d57bb00558455a82ae17b6d208786d46affd46 GIT binary patch literal 1305 zcmeAS@N?(olHy`uVBq!ia0y~yU;;9k7&zE~)R&4Yzkn1=v6E*A2N2Y7q;vqe7d%}Y zLn`LHy|z*CfPuha1MA=K-{gGU*K&YGE2Kadr~w@$oZDxca4YiloqHcrt$#>1*jMH8 zGcY{kWMJSBVqg$ZVPH`3U|?{Vz`)R;z{tSh2y{Xd3(%rb1^B}OJ)B4RqaiRF0;GmO Ya$E=x(*nkKWtAYEp00i_>zopr0DMzNX8-^I literal 0 HcmV?d00001 diff --git a/index.d.ts b/index.d.ts index 73c51a1a..44e0853b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -238,17 +238,21 @@ export interface StrokeOptions { join?: StrokeJoin } -export interface SKRSContext2D extends Omit { +export interface SKRSContext2D + extends Omit< + CanvasRenderingContext2D, + 'drawImage' | 'createPattern' | 'getTransform' | 'drawFocusIfNeeded' | 'scrollPathIntoView' + > { /** * @param startAngle The angle at which to begin the gradient, in radians. Angle measurements start vertically above the centre and move around clockwise. * @param x The x-axis coordinate of the centre of the gradient. * @param y The y-axis coordinate of the centre of the gradient. */ createConicGradient(startAngle: number, x: number, y: number): CanvasGradient - drawImage(image: Image, dx: number, dy: number): void - drawImage(image: Image, dx: number, dy: number, dw: number, dh: number): void + drawImage(image: Image | Canvas, dx: number, dy: number): void + drawImage(image: Image | Canvas, dx: number, dy: number, dw: number, dh: number): void drawImage( - image: Image, + image: Image | Canvas, sx: number, sy: number, sw: number, @@ -281,7 +285,9 @@ export interface SvgCanvas { getContent(): Buffer } -export interface Canvas { +export class Canvas { + constructor(width: number, height: number, flag?: SvgExportFlag) + width: number height: number getContext(contextType: '2d', contextAttributes?: { alpha: boolean }): SKRSContext2D diff --git a/index.html b/index.html index fec0c48a..2da4b37f 100644 --- a/index.html +++ b/index.html @@ -26,6 +26,8 @@ + +