Skip to content

Commit 55efa4a

Browse files
committed
Merge branch 'master' into dev
2 parents ea0052e + 93b33c0 commit 55efa4a

35 files changed

Lines changed: 1471 additions & 274 deletions

.github/workflows/codacy-analysis.yml

Lines changed: 0 additions & 49 deletions
This file was deleted.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ old/
33
coverage/
44
.DS_Store
55
.nyc_output/
6-
.node_modules/
6+
.node_modules/
7+
bench/node_modules/
8+
bench/package-lock.json

ImageScript.js

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -693,48 +693,11 @@ class Image {
693693
* @returns {Image}
694694
*/
695695
composite(source, x = 0, y = 0) {
696-
x = ~~x;
697-
y = ~~y;
698-
699-
for (let yy = 0; yy < source.height; yy++) {
700-
let y_offset = y + yy;
701-
if (y_offset < 0) continue;
702-
if (y_offset >= this.height) break;
703-
704-
for (let xx = 0; xx < source.width; xx++) {
705-
let x_offset = x + xx;
706-
if (x_offset < 0) continue;
707-
if (x_offset >= this.width) break;
708-
709-
const offset = 4 * (x_offset + y_offset * this.width);
710-
const fg = source.__view__.getUint32(4 * (xx + yy * source.width), false);
711-
const bg = this.__view__.getUint32(offset, false);
712-
713-
if ((fg & 0xff) === 0xff) this.__view__.setUint32(offset, fg, false);
714-
else if ((fg & 0xff) === 0x00) this.__view__.setUint32(offset, bg, false);
715-
else this.__view__.setUint32(offset, Image.__alpha_blend__(fg, bg), false);
716-
}
717-
}
696+
new v2(this.width, this.height, this.bitmap).overlay(new v2(source.width, source.height, source.bitmap), x, y);
718697

719698
return this;
720699
}
721700

722-
/**
723-
* @private
724-
* @param {number} fg
725-
* @param {number} bg
726-
* @returns {number}
727-
*/
728-
static __alpha_blend__(fg, bg) {
729-
const fa = fg & 0xff;
730-
const alpha = fa + 1;
731-
const inv_alpha = 256 - fa;
732-
const r = (alpha * (fg >>> 24) + inv_alpha * (bg >>> 24)) >> 8;
733-
const b = (alpha * (fg >> 8 & 0xff) + inv_alpha * (bg >> 8 & 0xff)) >> 8;
734-
const g = (alpha * (fg >> 16 & 0xff) + inv_alpha * (bg >> 16 & 0xff)) >> 8;
735-
return (((r & 0xff) << 24) | ((g & 0xff) << 16) | ((b & 0xff) << 8) | (Math.max(fa, bg & 0xff) & 0xff));
736-
}
737-
738701
/**
739702
* Inverts the images colors
740703
* @returns {Image}

bench/_.mjs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// https://gist.github.com/evanwashere/7ee592870e46f80405b9776dcd56e1e8#file-bench-js
2+
3+
const now = performance.now.bind(performance);
4+
5+
function sort(a, b) {
6+
if (a > b) return 1;
7+
if (a < b) return -1;
8+
9+
return 0;
10+
};
11+
12+
function stats(n, avg, min, max, jit, all) {
13+
return {
14+
min: Math.ceil(min * 1e6),
15+
max: Math.ceil(max * 1e6),
16+
avg: Math.ceil(avg / n * 1e6),
17+
jit: jit.map(x => Math.ceil(x * 1e6)),
18+
'50th': Math.ceil(1e6 * all[Math.ceil(n * (50 / 100)) - 1]),
19+
'75th': Math.ceil(1e6 * all[Math.ceil(n * (75 / 100)) - 1]),
20+
'99th': Math.ceil(1e6 * all[Math.ceil(n * (99 / 100)) - 1]),
21+
'99.5th': Math.ceil(1e6 * all[Math.ceil(n * (99.5 / 100)) - 1]),
22+
'99.9th': Math.ceil(1e6 * all[Math.ceil(n * (99.9 / 100)) - 1]),
23+
};
24+
}
25+
26+
export function sync(n, fn) {
27+
let avg = 0;
28+
let min = Infinity;
29+
let max = -Infinity;
30+
const all = new Array(n);
31+
const jit = new Array(10);
32+
33+
warmup: {
34+
let offset = 0;
35+
let iterations = 10;
36+
while (iterations--) {
37+
const t1 = now();
38+
39+
fn();
40+
jit[offset++] = now() - t1;
41+
}
42+
43+
iterations = 1e3 - 10;
44+
while (iterations--) fn();
45+
}
46+
47+
measure: {
48+
let offset = 0;
49+
let iterations = n;
50+
while (iterations--) {
51+
const t1 = now();
52+
53+
fn();
54+
const t2 = now() - t1;
55+
if (t2 < min) min = t2;
56+
if (t2 > max) max = t2;
57+
avg += (all[offset++] = t2);
58+
}
59+
}
60+
61+
all.sort(sort);
62+
return stats(n, avg, min, max, jit, all);
63+
}
64+
65+
export async function async(n, fn) {
66+
let avg = 0;
67+
let min = Infinity;
68+
let max = -Infinity;
69+
const all = new Array(n);
70+
const jit = new Array(10);
71+
72+
warmup: {
73+
let offset = 0;
74+
let iterations = 10;
75+
while (iterations--) {
76+
const t1 = now();
77+
78+
await fn();
79+
jit[offset++] = now() - t1;
80+
}
81+
82+
iterations = 1e3 - 10;
83+
while (iterations--) await fn();
84+
}
85+
86+
measure: {
87+
let offset = 0;
88+
let iterations = n;
89+
while (iterations--) {
90+
const t1 = now();
91+
92+
await fn();
93+
const t2 = now() - t1;
94+
if (t2 < min) min = t2;
95+
if (t2 > max) max = t2;
96+
avg += (all[offset++] = t2);
97+
}
98+
}
99+
100+
all.sort(sort);
101+
return stats(n, avg, min, max, jit, all);
102+
}
103+
104+
export function format({ results, title = '', unit = 'ns', percentiles = true }) {
105+
const h = '─';
106+
const v = '│';
107+
108+
let s = '';
109+
unit = `${unit}/iter`;
110+
const rk = Object.keys(results);
111+
const rv = Object.values(results);
112+
const ra = rv.map(x => x.avg.toLocaleString('en-us'));
113+
const r50 = rv.map(x => x['50th'].toLocaleString('en-us'));
114+
const r75 = rv.map(x => x['75th'].toLocaleString('en-us'));
115+
const r99 = rv.map(x => x['99th'].toLocaleString('en-us'));
116+
const r995 = rv.map(x => x['99.5th'].toLocaleString('en-us'));
117+
const r999 = rv.map(x => x['99.9th'].toLocaleString('en-us'));
118+
const rmm = rv.map(x => [x.min.toLocaleString('en-us'), x.max.toLocaleString('en-us')]);
119+
120+
const us = unit.length;
121+
const rks = Math.max(...rk.map(x => x.length));
122+
const ras = Math.max(...ra.map(x => x.length));
123+
const r50s = Math.max(...r50.map(x => x.length));
124+
const r75s = Math.max(...r75.map(x => x.length));
125+
const r99s = Math.max(...r99.map(x => x.length));
126+
const r995s = Math.max(...r995.map(x => x.length));
127+
const r999s = Math.max(...r999.map(x => x.length));
128+
const rmns = Math.max(...rmm.map(x => x[0].length));
129+
const rmxs = Math.max(...rmm.map(x => x[1].length));
130+
131+
const bks = 1 + rks + 1;
132+
const b50s = 1 + r50s + 1 + us + 1;
133+
const b75s = 1 + r75s + 1 + us + 1;
134+
const b99s = 1 + r99s + 1 + us + 1;
135+
const b995s = 1 + r995s + 1 + us + 1;
136+
const b999s = 1 + r999s + 1 + us + 1;
137+
const bimms = 1 + rmns + 2 + rmxs + 1 + us + 1;
138+
const bas = 1 + (ras + 1 + us) + 1 + bimms + 1;
139+
const ls = 1 + bks + 1 + bas + 1 + b50s + 1 + b75s + 1 + b99s + 1 + b995s + 1 + b999s + 1;
140+
141+
if (!percentiles) s += title ? ` ${title}` : '';
142+
143+
else {
144+
s += ' '.repeat(3 + bks + bas);
145+
s += '┌' + h.repeat(ls - 4 - bks - bas) + '┐';
146+
s += '\n' + ` ${title.padEnd(2 + bks + bas, ' ')}`;
147+
}
148+
149+
if (percentiles) s += v
150+
+ '50th'.padEnd((b50s + 4) / 2, ' ').padStart(b50s, ' ') + v
151+
+ '75th'.padEnd((b75s + 4) / 2, ' ').padStart(b75s, ' ') + v
152+
+ '99th'.padEnd((b99s + 4) / 2, ' ').padStart(b99s, ' ') + v
153+
+ '99.5th'.padEnd((b995s + 6) / 2, ' ').padStart(b995s, ' ') + v
154+
+ '99.9th'.padEnd((b999s + 6) / 2, ' ').padStart(b999s, ' ') + v;
155+
156+
s += (title || percentiles ? '\n' : '') + '┌' + h.repeat(1 + bks + bas) + '┐' + (percentiles ? '├' + h.repeat(ls - 4 - bks - bas) + '┤' : '');
157+
158+
for (let i = 0; i < rk.length; i++) {
159+
s += '\n' + v + ` ${rk[i].padEnd(rks, ' ')} ` + v + ` ${ra[i].padStart(ras, ' ')} ${unit} ${`(${rmm[i][0]}..${rmm[i][1]} ${unit})`.padStart(bimms, ' ')} ` + v
160+
if (percentiles) s += v + ` ${r50[i].padStart(r50s, ' ')} ${unit} ` + v + ` ${r75[i].padStart(r75s, ' ')} ${unit} ` + v + ` ${r99[i].padStart(r99s, ' ')} ${unit} ` + v + ` ${r995[i].padStart(r995s, ' ')} ${unit} ` + v + ` ${r999[i].padStart(r999s, ' ')} ${unit} ` + v;
161+
}
162+
163+
s += '\n' + '└' + h.repeat(1 + bks + bas) + '┘' + (percentiles ? '└' + h.repeat(ls - 4 - bks - bas) + '┘' : '');
164+
165+
return s;
166+
}

bench/all.mjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import './flip.mjs';
2+
import './blur.mjs';
3+
import './resize.mjs';
4+
import './overlay.mjs';

bench/blur.mjs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { sync, async, format } from './_.mjs';
2+
3+
import jimp from 'jimp';
4+
import sharp from 'sharp';
5+
import v2 from '../v2/framebuffer.mjs';
6+
import * as imagers from '@evan/wasm/target/image/node.mjs';
7+
import * as v2_wasm from '@evan/wasm/target/imagescript/node.mjs';
8+
9+
{
10+
const rgba = new Uint8Array(4 * 256 * 256).map((_, i) => i % 256);
11+
12+
const images = {
13+
v2: new v2(256, 256, rgba),
14+
imagers: imagers.framebuffer.from(256, 256, rgba),
15+
v2_wasm: () => v2_wasm.framebuffer.from(256, 256, rgba),
16+
sharp: sharp(rgba, { raw: { width: 256, height: 256, channels: 4 } }),
17+
jimp: await new Promise(r => new jimp({ width: 256, height: 256, data: rgba }, (_, i) => r(i))),
18+
};
19+
20+
console.log(format({
21+
percentiles: false,
22+
title: 'blur (5.0f/256x256)',
23+
24+
results: {
25+
jimp: sync(1e4, () => images.jimp.clone().blur(5)),
26+
imagescript: sync(1e4, () => images.v2.clone().blur('gaussian')),
27+
sharp: await async(1e4, () => images.sharp.clone().blur(5).toBuffer()),
28+
'image-rs (wasm)': sync(1e4, () => imagers.blur(images.imagers, 5).drop()),
29+
'imagescript-rs (wasm)': sync(1e4, () => { let i = images.v2_wasm(); v2_wasm.blur(i, 5); i.drop() }),
30+
},
31+
}));
32+
33+
images.imagers.drop();
34+
}
35+
36+
{
37+
const rgba = new Uint8Array(4 * 1024 * 1024).map((_, i) => i % 256);
38+
39+
const images = {
40+
v2: new v2(1024, 1024, rgba),
41+
imagers: imagers.framebuffer.from(1024, 1024, rgba),
42+
v2_wasm: () => v2_wasm.framebuffer.from(1024, 1024, rgba),
43+
sharp: sharp(rgba, { raw: { width: 1024, height: 1024, channels: 4 } }),
44+
jimp: await new Promise(r => new jimp({ width: 1024, height: 1024, data: rgba }, (_, i) => r(i))),
45+
};
46+
47+
console.log(format({
48+
percentiles: false,
49+
title: 'blur (5.0f/1024x1024)',
50+
51+
results: {
52+
jimp: sync(1e4, () => images.jimp.clone().blur(5)),
53+
imagescript: sync(1e4, () => images.v2.clone().blur('gaussian')),
54+
sharp: await async(1e4, () => images.sharp.clone().blur(5).toBuffer()),
55+
'image-rs (wasm)': sync(1e4, () => imagers.blur(images.imagers, 5).drop()),
56+
'imagescript-rs (wasm)': sync(1e4, () => { let i = images.v2_wasm(); v2_wasm.blur(i, 5); i.drop() }),
57+
},
58+
}));
59+
}

0 commit comments

Comments
 (0)