Merge pull request #14 from zenozeng/test
test: rendering test for svgcanvas
This commit is contained in:
commit
519719ef2d
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
node-version: [10.x, 12.x, 14.x, 16.x]
|
||||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@ -27,3 +27,4 @@ jobs:
|
|||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm run build --if-present
|
- run: npm run build --if-present
|
||||||
|
- run: npm test
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
|
coverage
|
@ -1,32 +1,27 @@
|
|||||||
|
process.env.CHROME_BIN = require('puppeteer').executablePath();
|
||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
config.set({
|
config.set({
|
||||||
basePath: '',
|
basePath: '',
|
||||||
frameworks: ['mocha', 'chai'],
|
frameworks: ['mocha'],
|
||||||
plugins: [
|
|
||||||
'karma-mocha',
|
|
||||||
'karma-chai',
|
|
||||||
'karma-mocha-reporter',
|
|
||||||
'karma-chrome-launcher',
|
|
||||||
'karma-firefox-launcher'
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
},
|
|
||||||
files: [
|
files: [
|
||||||
'node_modules/resemblejs/resemble.js',
|
'dist/rendering.test.js',
|
||||||
'canvas2svg.js',
|
|
||||||
'test/globals.js',
|
|
||||||
'test/example/*.js',
|
|
||||||
'test/unit.spec.js',
|
|
||||||
'test/example.spec.js'
|
|
||||||
],
|
],
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
|
'**/*.js': ['sourcemap']
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'coverage', 'mocha'],
|
||||||
|
coverageReporter: {
|
||||||
|
type: 'lcovonly',
|
||||||
|
dir : 'coverage/',
|
||||||
|
subdir: '.',
|
||||||
|
file: 'lcov.info'
|
||||||
},
|
},
|
||||||
reporters: ['mocha'],
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_DISABLE,
|
logLevel: config.LOG_INFO,
|
||||||
autoWatch: false,
|
autoWatch: false,
|
||||||
browsers: ['Firefox', 'Chrome'],
|
browsers: ['ChromeHeadless'],
|
||||||
singleRun: true
|
singleRun: true // output all logs to stdout instead of click debug button
|
||||||
});
|
});
|
||||||
};
|
};
|
6193
package-lock.json
generated
6193
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@ -4,8 +4,10 @@
|
|||||||
"description": "svgcanvas",
|
"description": "svgcanvas",
|
||||||
"main": "dist/svgcanvas.js",
|
"main": "dist/svgcanvas.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup index.js -o dist/svgcanvas.js -f cjs && rollup index.js -o dist/svgcanvas.esm.js -f es && rollup test/index.js -o dist/test.js -f iife",
|
"watch": "rollup -c -w",
|
||||||
"prepublishOnly": "npm run build"
|
"build": "rollup -c",
|
||||||
|
"prepublishOnly": "npm run build",
|
||||||
|
"test": "karma start"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -19,6 +21,17 @@
|
|||||||
"author": "Zeno Zeng",
|
"author": "Zeno Zeng",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^21.0.2",
|
||||||
|
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||||
|
"chai": "^4.3.6",
|
||||||
|
"karma": "^6.3.17",
|
||||||
|
"karma-chrome-launcher": "^3.1.0",
|
||||||
|
"karma-coverage": "^2.2.0",
|
||||||
|
"karma-mocha": "^2.0.1",
|
||||||
|
"karma-mocha-reporter": "^2.2.5",
|
||||||
|
"karma-sourcemap-loader": "^0.3.8",
|
||||||
|
"mocha": "^9.2.1",
|
||||||
|
"puppeteer": "^13.5.0",
|
||||||
"rollup": "^2.67.0"
|
"rollup": "^2.67.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
rollup.config.js
Normal file
35
rollup.config.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
input: 'index.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/svgcanvas.js',
|
||||||
|
format: 'cjs'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'index.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/svgcanvas.esm.js',
|
||||||
|
format: 'esm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'test/index.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/test.js',
|
||||||
|
format: 'iife'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: 'test/rendering.test.js',
|
||||||
|
output: {
|
||||||
|
file: 'dist/rendering.test.js',
|
||||||
|
format: 'iife',
|
||||||
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
plugins: [nodeResolve(), commonjs()]
|
||||||
|
},
|
||||||
|
]
|
192
test/rendering.test.js
Normal file
192
test/rendering.test.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { Element } from '../index'
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import arc from './tests/arc'
|
||||||
|
import arcTo from './tests/arcTo'
|
||||||
|
import arcTo2 from './tests/arcTo2'
|
||||||
|
import emptyArc from './tests/emptyArc'
|
||||||
|
import fillstyle from './tests/fillstyle'
|
||||||
|
import globalAlpha from './tests/globalalpha'
|
||||||
|
import gradient from './tests/gradient'
|
||||||
|
import linecap from './tests/linecap'
|
||||||
|
import linewidth from './tests/linewidth'
|
||||||
|
import rgba from './tests/rgba'
|
||||||
|
import rotate from './tests/rotate'
|
||||||
|
import saveandrestore from './tests/saveandrestore'
|
||||||
|
import setLineDash from './tests/setLineDash'
|
||||||
|
import text from './tests/text'
|
||||||
|
import tiger from './tests/tiger'
|
||||||
|
import transform from './tests/transform'
|
||||||
|
import pattern from "./tests/pattern";
|
||||||
|
|
||||||
|
const tests = {
|
||||||
|
tiger,
|
||||||
|
arc,
|
||||||
|
arcTo,
|
||||||
|
arcTo2,
|
||||||
|
emptyArc,
|
||||||
|
fillstyle,
|
||||||
|
globalAlpha,
|
||||||
|
gradient,
|
||||||
|
linecap,
|
||||||
|
linewidth,
|
||||||
|
rgba,
|
||||||
|
rotate,
|
||||||
|
saveandrestore,
|
||||||
|
setLineDash,
|
||||||
|
text,
|
||||||
|
transform,
|
||||||
|
pattern
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
pixelDensity: 3 // for 200% and 150%
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenderingTester {
|
||||||
|
constructor(name, fn) {
|
||||||
|
this.name = name;
|
||||||
|
this.fn = fn;
|
||||||
|
this.width = 600;
|
||||||
|
this.height = 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
async test() {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const svgcanvas = new Element();
|
||||||
|
|
||||||
|
[canvas, svgcanvas].forEach((canvas) => {
|
||||||
|
canvas.width = this.width
|
||||||
|
canvas.height = this.height
|
||||||
|
const ctx = canvas.getContext('2d')
|
||||||
|
this.fn(ctx)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pixels
|
||||||
|
const svg = svgcanvas.toDataURL("image/svg+xml");
|
||||||
|
const svgImage = await new Promise((resolve) => {
|
||||||
|
var svgImage = new Image();
|
||||||
|
svgImage.onload = function () {
|
||||||
|
resolve(svgImage)
|
||||||
|
}
|
||||||
|
svgImage.src = svg;
|
||||||
|
})
|
||||||
|
const svgPixels = this.getPixels(svgImage);
|
||||||
|
const canvasPixels = this.getPixels(canvas);
|
||||||
|
const diffPixels = this.diffPixels(svgPixels, canvasPixels);
|
||||||
|
const removeThinLinesPixels = this.removeThinLines(this.removeThinLines(diffPixels));
|
||||||
|
const svgPixelsCount = this.countPixels(svgPixels);
|
||||||
|
const canvasPixelsCount = this.countPixels(canvasPixels)
|
||||||
|
const count = Math.max(svgPixelsCount, canvasPixelsCount);
|
||||||
|
const diffPixelsCount = this.countPixels(removeThinLinesPixels);
|
||||||
|
console.log({ fn: this.name, count, diffCount: diffPixelsCount, svgPixelsCount, canvasPixelsCount })
|
||||||
|
if (count === 0 && diffPixelsCount === 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
const diffRate = diffPixelsCount / count;
|
||||||
|
return diffRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPixels(image) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const width = this.width;
|
||||||
|
const height = this.height;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(image, 0, 0, width, height);
|
||||||
|
return ctx.getImageData(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// count non transparent pixels
|
||||||
|
countPixels(imgData) {
|
||||||
|
var count = 0;
|
||||||
|
for (var i = 3; i < imgData.data.length; i += 4) {
|
||||||
|
if (imgData.data[i] > 0) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
diffPixels(imgData1, imgData2) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const width = this.width;
|
||||||
|
const height = this.height;
|
||||||
|
const diffImgData = canvas.getContext('2d').getImageData(0, 0, width, height);
|
||||||
|
for (var i = 0; i < imgData1.data.length; i += 4) {
|
||||||
|
var indexes = [i, i + 1, i + 2, i + 3];
|
||||||
|
indexes.forEach(function (i) {
|
||||||
|
diffImgData.data[i] = 0;
|
||||||
|
});
|
||||||
|
if (indexes.some(function (i) {
|
||||||
|
return Math.abs(imgData1.data[i] - imgData2.data[i]) > 0;
|
||||||
|
})) {
|
||||||
|
diffImgData.data[i + 3] = 255; // set black
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diffImgData;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeThinLines(imageData) {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const width = this.width;
|
||||||
|
const height = this.height;
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
var imgDataCopy = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
var getPixelIndex = function (x, y) {
|
||||||
|
return (y * width + x) * 4 + 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
var getPixel = function (x, y) {
|
||||||
|
var alphaIndex = getPixelIndex(x, y);
|
||||||
|
return imgDataCopy.data[alphaIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
var setPixel = function (x, y, value) {
|
||||||
|
imgData.data[getPixelIndex(x, y)] = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var x = 1; x < width - 1; x++) {
|
||||||
|
for (var y = 1; y < height - 1; y++) {
|
||||||
|
if (getPixel(x, y) == 0) {
|
||||||
|
continue; // ignore transparents
|
||||||
|
}
|
||||||
|
var links = [
|
||||||
|
{ x: x - 1, y: y - 1 },
|
||||||
|
{ x: x, y: y - 1 },
|
||||||
|
{ x: x + 1, y: y - 1 },
|
||||||
|
{ x: x - 1, y: y },
|
||||||
|
{ x: x + 1, y: y },
|
||||||
|
{ x: x - 1, y: y + 1 },
|
||||||
|
{ x: x, y: y + 1 },
|
||||||
|
{ x: x + 1, y: y + 1 }
|
||||||
|
].map(function (p) {
|
||||||
|
return getPixel(p.x, p.y);
|
||||||
|
}).filter(function (val) {
|
||||||
|
return val > 0; // not transparent?
|
||||||
|
}).length;
|
||||||
|
|
||||||
|
if (links < 5) { // is a thin line
|
||||||
|
setPixel(x, y, 0); // make it transparent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return imgData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('RenderTest', () => {
|
||||||
|
for (let fn of Object.keys(tests)) {
|
||||||
|
it(`should render same results for ${fn}`, async () => {
|
||||||
|
const tester = new RenderingTester(fn, tests[fn]);
|
||||||
|
const diffRate = await tester.test();
|
||||||
|
expect(diffRate).to.lessThan(0.05);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
@ -1,8 +1,5 @@
|
|||||||
export default function text(ctx) {
|
export default function text(ctx) {
|
||||||
ctx.font = "normal 36px Times";
|
ctx.font = "normal 120px Arial";
|
||||||
ctx.fillStyle = "#000000";
|
ctx.fillStyle = "#000000";
|
||||||
ctx.fillText("A Text Example", 50, 50);
|
ctx.fillText("Hello", 0, 200);
|
||||||
ctx.font = "normal 36px Arial";
|
|
||||||
ctx.strokeStyle = "#000000";
|
|
||||||
ctx.strokeText("A Text Example", 50, 90);
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user