@wdio/image-comparison-core 1.2.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/dist/helpers/options.d.ts +1 -4
- package/dist/helpers/options.d.ts.map +1 -1
- package/dist/helpers/options.js +2 -11
- package/dist/methods/compareReport.interfaces.d.ts +1 -1
- package/dist/methods/compareReport.interfaces.d.ts.map +1 -1
- package/dist/methods/createCompareReport.test.js +4 -2
- package/dist/methods/images.d.ts +2 -1
- package/dist/methods/images.d.ts.map +1 -1
- package/dist/methods/images.executeImageCompare.test.js +39 -44
- package/dist/methods/images.js +31 -31
- package/dist/methods/images.test.js +80 -220
- package/dist/methods/processDiffPixels.d.ts +1 -1
- package/dist/methods/processDiffPixels.d.ts.map +1 -1
- package/dist/methods/processDiffPixels.js +11 -2
- package/dist/methods/rectangles.d.ts.map +1 -1
- package/dist/methods/rectangles.interfaces.d.ts +1 -1
- package/dist/methods/rectangles.interfaces.d.ts.map +1 -1
- package/dist/methods/rectangles.js +12 -9
- package/dist/pixelmatch/compare.interfaces.d.ts +43 -0
- package/dist/pixelmatch/compare.interfaces.d.ts.map +1 -0
- package/dist/pixelmatch/compareImages.d.ts.map +1 -0
- package/dist/pixelmatch/compareImages.js +173 -0
- package/dist/pixelmatch/compareImages.test.d.ts +2 -0
- package/dist/pixelmatch/compareImages.test.d.ts.map +1 -0
- package/dist/pixelmatch/compareImages.test.js +262 -0
- package/dist/utils/imageUtils.d.ts +17 -0
- package/dist/utils/imageUtils.d.ts.map +1 -0
- package/dist/utils/imageUtils.js +189 -0
- package/dist/utils/imageUtils.test.d.ts +2 -0
- package/dist/utils/imageUtils.test.d.ts.map +1 -0
- package/dist/utils/imageUtils.test.js +277 -0
- package/package.json +6 -5
- package/dist/resemble/compare.interfaces.d.ts +0 -136
- package/dist/resemble/compare.interfaces.d.ts.map +0 -1
- package/dist/resemble/compareImages.d.ts.map +0 -1
- package/dist/resemble/compareImages.js +0 -21
- package/dist/resemble/resemble.jimp.cjs +0 -981
- package/dist/resemble/resemble.jimp.d.cts +0 -46
- package/dist/resemble/resemble.jimp.d.cts.map +0 -1
- /package/dist/{resemble → pixelmatch}/compare.interfaces.js +0 -0
- /package/dist/{resemble → pixelmatch}/compareImages.d.ts +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { decode, encode } from 'fast-png';
|
|
2
|
+
// Convert any channel count to RGBA 8-bit. fast-png may decode RGB, grayscale, or clamped PNGs.
|
|
3
|
+
function toRGBA(data, channels, width, height) {
|
|
4
|
+
if (channels === 4 && (data instanceof Uint8Array || data instanceof Uint8ClampedArray)) {
|
|
5
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
6
|
+
}
|
|
7
|
+
const pixels = width * height;
|
|
8
|
+
const out = new Uint8Array(pixels * 4);
|
|
9
|
+
for (let i = 0; i < pixels; i++) {
|
|
10
|
+
if (channels === 3) {
|
|
11
|
+
out[i * 4] = data[i * 3];
|
|
12
|
+
out[i * 4 + 1] = data[i * 3 + 1];
|
|
13
|
+
out[i * 4 + 2] = data[i * 3 + 2];
|
|
14
|
+
out[i * 4 + 3] = 255;
|
|
15
|
+
}
|
|
16
|
+
else if (channels === 1) {
|
|
17
|
+
const v = data[i];
|
|
18
|
+
out[i * 4] = v;
|
|
19
|
+
out[i * 4 + 1] = v;
|
|
20
|
+
out[i * 4 + 2] = v;
|
|
21
|
+
out[i * 4 + 3] = 255;
|
|
22
|
+
}
|
|
23
|
+
else if (channels === 2) {
|
|
24
|
+
const v = data[i * 2];
|
|
25
|
+
out[i * 4] = v;
|
|
26
|
+
out[i * 4 + 1] = v;
|
|
27
|
+
out[i * 4 + 2] = v;
|
|
28
|
+
out[i * 4 + 3] = data[i * 2 + 1];
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// 4-channel Uint16Array: downsample 16-bit → 8-bit
|
|
32
|
+
out[i * 4] = data[i * 4] >> 8;
|
|
33
|
+
out[i * 4 + 1] = data[i * 4 + 1] >> 8;
|
|
34
|
+
out[i * 4 + 2] = data[i * 4 + 2] >> 8;
|
|
35
|
+
out[i * 4 + 3] = data[i * 4 + 3] >> 8;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
export function decodeImage(buffer) {
|
|
41
|
+
const png = decode(buffer);
|
|
42
|
+
return {
|
|
43
|
+
data: toRGBA(png.data, png.channels, png.width, png.height),
|
|
44
|
+
width: png.width,
|
|
45
|
+
height: png.height,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export function encodeImage(img) {
|
|
49
|
+
return Buffer.from(encode({ data: img.data, width: img.width, height: img.height, channels: 4, depth: 8 }));
|
|
50
|
+
}
|
|
51
|
+
export function toBase64Png(img) {
|
|
52
|
+
return encodeImage(img).toString('base64');
|
|
53
|
+
}
|
|
54
|
+
export function createCanvas(width, height, r = 0, g = 0, b = 0, a = 0) {
|
|
55
|
+
const data = new Uint8Array(width * height * 4);
|
|
56
|
+
if (r !== 0 || g !== 0 || b !== 0 || a !== 0) {
|
|
57
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
58
|
+
data[i] = r;
|
|
59
|
+
data[i + 1] = g;
|
|
60
|
+
data[i + 2] = b;
|
|
61
|
+
data[i + 3] = a;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { data, width, height };
|
|
65
|
+
}
|
|
66
|
+
export function cropImage(img, x, y, w, h) {
|
|
67
|
+
const data = new Uint8Array(w * h * 4);
|
|
68
|
+
const rowBytes = w * 4;
|
|
69
|
+
for (let row = 0; row < h; row++) {
|
|
70
|
+
const srcOffset = ((y + row) * img.width + x) * 4;
|
|
71
|
+
data.set(img.data.subarray(srcOffset, srcOffset + rowBytes), row * rowBytes);
|
|
72
|
+
}
|
|
73
|
+
return { data, width: w, height: h };
|
|
74
|
+
}
|
|
75
|
+
// Porter-Duff "over" compositing. Mutates base in place.
|
|
76
|
+
export function compositeImage(base, overlay, offsetX, offsetY, opacity = 1) {
|
|
77
|
+
for (let oy = 0; oy < overlay.height; oy++) {
|
|
78
|
+
const by = oy + offsetY;
|
|
79
|
+
if (by < 0 || by >= base.height) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
for (let ox = 0; ox < overlay.width; ox++) {
|
|
83
|
+
const bx = ox + offsetX;
|
|
84
|
+
if (bx < 0 || bx >= base.width) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const si = (oy * overlay.width + ox) * 4;
|
|
88
|
+
const di = (by * base.width + bx) * 4;
|
|
89
|
+
const srcA = (overlay.data[si + 3] / 255) * opacity;
|
|
90
|
+
const dstA = base.data[di + 3] / 255;
|
|
91
|
+
const outA = srcA + dstA * (1 - srcA);
|
|
92
|
+
if (outA === 0) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
base.data[di] = Math.round((overlay.data[si] * srcA + base.data[di] * dstA * (1 - srcA)) / outA);
|
|
96
|
+
base.data[di + 1] = Math.round((overlay.data[si + 1] * srcA + base.data[di + 1] * dstA * (1 - srcA)) / outA);
|
|
97
|
+
base.data[di + 2] = Math.round((overlay.data[si + 2] * srcA + base.data[di + 2] * dstA * (1 - srcA)) / outA);
|
|
98
|
+
base.data[di + 3] = Math.round(outA * 255);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function resizeBilinear(img, newW, newH) {
|
|
103
|
+
const data = new Uint8Array(newW * newH * 4);
|
|
104
|
+
const xRatio = img.width / newW;
|
|
105
|
+
const yRatio = img.height / newH;
|
|
106
|
+
for (let dy = 0; dy < newH; dy++) {
|
|
107
|
+
const sy = dy * yRatio;
|
|
108
|
+
const y0 = Math.floor(sy);
|
|
109
|
+
const y1 = Math.min(y0 + 1, img.height - 1);
|
|
110
|
+
const yFrac = sy - y0;
|
|
111
|
+
for (let dx = 0; dx < newW; dx++) {
|
|
112
|
+
const sx = dx * xRatio;
|
|
113
|
+
const x0 = Math.floor(sx);
|
|
114
|
+
const x1 = Math.min(x0 + 1, img.width - 1);
|
|
115
|
+
const xFrac = sx - x0;
|
|
116
|
+
const i00 = (y0 * img.width + x0) * 4;
|
|
117
|
+
const i10 = (y0 * img.width + x1) * 4;
|
|
118
|
+
const i01 = (y1 * img.width + x0) * 4;
|
|
119
|
+
const i11 = (y1 * img.width + x1) * 4;
|
|
120
|
+
const di = (dy * newW + dx) * 4;
|
|
121
|
+
const w00 = (1 - xFrac) * (1 - yFrac);
|
|
122
|
+
const w10 = xFrac * (1 - yFrac);
|
|
123
|
+
const w01 = (1 - xFrac) * yFrac;
|
|
124
|
+
const w11 = xFrac * yFrac;
|
|
125
|
+
data[di] = Math.round(img.data[i00] * w00 + img.data[i10] * w10 + img.data[i01] * w01 + img.data[i11] * w11);
|
|
126
|
+
data[di + 1] = Math.round(img.data[i00 + 1] * w00 + img.data[i10 + 1] * w10 + img.data[i01 + 1] * w01 + img.data[i11 + 1] * w11);
|
|
127
|
+
data[di + 2] = Math.round(img.data[i00 + 2] * w00 + img.data[i10 + 2] * w10 + img.data[i01 + 2] * w01 + img.data[i11 + 2] * w11);
|
|
128
|
+
data[di + 3] = Math.round(img.data[i00 + 3] * w00 + img.data[i10 + 3] * w10 + img.data[i01 + 3] * w01 + img.data[i11 + 3] * w11);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { data, width: newW, height: newH };
|
|
132
|
+
}
|
|
133
|
+
// 90° clockwise: new width = srcHeight, new height = srcWidth
|
|
134
|
+
// dst(dx, dy) ← src(col=dy, row=srcH-1-dx)
|
|
135
|
+
export function rotate90CW(img) {
|
|
136
|
+
const { width: srcW, height: srcH } = img;
|
|
137
|
+
const data = new Uint8Array(srcW * srcH * 4);
|
|
138
|
+
const newW = srcH;
|
|
139
|
+
const newH = srcW;
|
|
140
|
+
for (let dy = 0; dy < newH; dy++) {
|
|
141
|
+
for (let dx = 0; dx < newW; dx++) {
|
|
142
|
+
const si = ((srcH - 1 - dx) * srcW + dy) * 4;
|
|
143
|
+
const di = (dy * newW + dx) * 4;
|
|
144
|
+
data[di] = img.data[si];
|
|
145
|
+
data[di + 1] = img.data[si + 1];
|
|
146
|
+
data[di + 2] = img.data[si + 2];
|
|
147
|
+
data[di + 3] = img.data[si + 3];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return { data, width: newW, height: newH };
|
|
151
|
+
}
|
|
152
|
+
// 90° counter-clockwise: new width = srcHeight, new height = srcWidth
|
|
153
|
+
// dst(dx, dy) ← src(col=srcW-1-dy, row=dx)
|
|
154
|
+
export function rotate90CCW(img) {
|
|
155
|
+
const { width: srcW, height: srcH } = img;
|
|
156
|
+
const data = new Uint8Array(srcW * srcH * 4);
|
|
157
|
+
const newW = srcH;
|
|
158
|
+
const newH = srcW;
|
|
159
|
+
for (let dy = 0; dy < newH; dy++) {
|
|
160
|
+
for (let dx = 0; dx < newW; dx++) {
|
|
161
|
+
const si = (dx * srcW + (srcW - 1 - dy)) * 4;
|
|
162
|
+
const di = (dy * newW + dx) * 4;
|
|
163
|
+
data[di] = img.data[si];
|
|
164
|
+
data[di + 1] = img.data[si + 1];
|
|
165
|
+
data[di + 2] = img.data[si + 2];
|
|
166
|
+
data[di + 3] = img.data[si + 3];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { data, width: newW, height: newH };
|
|
170
|
+
}
|
|
171
|
+
export function rotate180(img) {
|
|
172
|
+
const data = new Uint8Array(img.data.length);
|
|
173
|
+
const total = img.width * img.height;
|
|
174
|
+
for (let i = 0; i < total; i++) {
|
|
175
|
+
const si = i * 4;
|
|
176
|
+
const di = (total - 1 - i) * 4;
|
|
177
|
+
data[di] = img.data[si];
|
|
178
|
+
data[di + 1] = img.data[si + 1];
|
|
179
|
+
data[di + 2] = img.data[si + 2];
|
|
180
|
+
data[di + 3] = img.data[si + 3];
|
|
181
|
+
}
|
|
182
|
+
return { data, width: img.width, height: img.height };
|
|
183
|
+
}
|
|
184
|
+
// Multiply every pixel's alpha channel by opacity (0–1). Mutates in place.
|
|
185
|
+
export function setOpacity(img, opacity) {
|
|
186
|
+
for (let i = 3; i < img.data.length; i += 4) {
|
|
187
|
+
img.data[i] = Math.round(img.data[i] * opacity);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"imageUtils.test.d.ts","sourceRoot":"","sources":["../../src/utils/imageUtils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { encode } from 'fast-png';
|
|
3
|
+
import { decodeImage, encodeImage, toBase64Png, createCanvas, cropImage, compositeImage, resizeBilinear, rotate90CW, rotate90CCW, rotate180, setOpacity, } from './imageUtils.js';
|
|
4
|
+
// Build a minimal 2x2 RGBA PNG buffer for use as test input
|
|
5
|
+
function makePng(pixels) {
|
|
6
|
+
// pixels: [[r,g,b,a], ...] in row-major order, width = sqrt(pixels.length)
|
|
7
|
+
const side = Math.sqrt(pixels.length);
|
|
8
|
+
const data = new Uint8Array(pixels.length * 4);
|
|
9
|
+
pixels.forEach(([r, g, b, a], i) => {
|
|
10
|
+
data[i * 4] = r;
|
|
11
|
+
data[i * 4 + 1] = g;
|
|
12
|
+
data[i * 4 + 2] = b;
|
|
13
|
+
data[i * 4 + 3] = a;
|
|
14
|
+
});
|
|
15
|
+
return Buffer.from(encode({ data, width: side, height: side, channels: 4, depth: 8 }));
|
|
16
|
+
}
|
|
17
|
+
describe('decodeImage', () => {
|
|
18
|
+
it('decodes an RGBA PNG buffer into a RawImage', () => {
|
|
19
|
+
const buf = makePng([[255, 0, 0, 255], [0, 255, 0, 255], [0, 0, 255, 255], [255, 255, 0, 255]]);
|
|
20
|
+
const img = decodeImage(buf);
|
|
21
|
+
expect(img.width).toBe(2);
|
|
22
|
+
expect(img.height).toBe(2);
|
|
23
|
+
expect(img.data[0]).toBe(255); // R
|
|
24
|
+
expect(img.data[1]).toBe(0); // G
|
|
25
|
+
expect(img.data[2]).toBe(0); // B
|
|
26
|
+
expect(img.data[3]).toBe(255); // A
|
|
27
|
+
});
|
|
28
|
+
it('converts an RGB (3-channel) PNG to RGBA by adding full alpha', () => {
|
|
29
|
+
const data = new Uint8Array([100, 150, 200, 50, 60, 70]);
|
|
30
|
+
const buf = Buffer.from(encode({ data, width: 2, height: 1, channels: 3, depth: 8 }));
|
|
31
|
+
const img = decodeImage(buf);
|
|
32
|
+
expect(img.width).toBe(2);
|
|
33
|
+
expect(img.height).toBe(1);
|
|
34
|
+
expect(img.data[0]).toBe(100); // R
|
|
35
|
+
expect(img.data[1]).toBe(150); // G
|
|
36
|
+
expect(img.data[2]).toBe(200); // B
|
|
37
|
+
expect(img.data[3]).toBe(255); // A added
|
|
38
|
+
expect(img.data[4]).toBe(50);
|
|
39
|
+
expect(img.data[7]).toBe(255); // A added for second pixel
|
|
40
|
+
});
|
|
41
|
+
it('converts a grayscale (1-channel) PNG to RGBA', () => {
|
|
42
|
+
const data = new Uint8Array([128]);
|
|
43
|
+
const buf = Buffer.from(encode({ data, width: 1, height: 1, channels: 1, depth: 8 }));
|
|
44
|
+
const img = decodeImage(buf);
|
|
45
|
+
expect(img.width).toBe(1);
|
|
46
|
+
expect(img.data[0]).toBe(128); // R = gray value
|
|
47
|
+
expect(img.data[1]).toBe(128); // G = gray value
|
|
48
|
+
expect(img.data[2]).toBe(128); // B = gray value
|
|
49
|
+
expect(img.data[3]).toBe(255); // A = full
|
|
50
|
+
});
|
|
51
|
+
it('converts a grayscale+alpha (2-channel) PNG to RGBA', () => {
|
|
52
|
+
const data = new Uint8Array([200, 128]); // gray=200, alpha=128
|
|
53
|
+
const buf = Buffer.from(encode({ data, width: 1, height: 1, channels: 2, depth: 8 }));
|
|
54
|
+
const img = decodeImage(buf);
|
|
55
|
+
expect(img.data[0]).toBe(200); // R = gray
|
|
56
|
+
expect(img.data[1]).toBe(200); // G = gray
|
|
57
|
+
expect(img.data[2]).toBe(200); // B = gray
|
|
58
|
+
expect(img.data[3]).toBe(128); // A preserved
|
|
59
|
+
});
|
|
60
|
+
it('converts a 16-bit RGBA PNG by downsampling to 8-bit', () => {
|
|
61
|
+
// 16-bit value 0xFF00 >> 8 = 255, 0x8000 >> 8 = 128
|
|
62
|
+
const data = new Uint16Array([0xFF00, 0x8000, 0x0000, 0xFF00]);
|
|
63
|
+
const buf = Buffer.from(encode({ data, width: 1, height: 1, channels: 4, depth: 16 }));
|
|
64
|
+
const img = decodeImage(buf);
|
|
65
|
+
expect(img.data[0]).toBe(255); // R: 0xFF00 >> 8
|
|
66
|
+
expect(img.data[1]).toBe(128); // G: 0x8000 >> 8
|
|
67
|
+
expect(img.data[2]).toBe(0); // B: 0x0000 >> 8
|
|
68
|
+
expect(img.data[3]).toBe(255); // A: 0xFF00 >> 8
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('encodeImage / toBase64Png', () => {
|
|
72
|
+
it('round-trips a RawImage through encode → decode', () => {
|
|
73
|
+
const original = createCanvas(2, 2, 100, 150, 200, 255);
|
|
74
|
+
const buf = encodeImage(original);
|
|
75
|
+
const decoded = decodeImage(buf);
|
|
76
|
+
expect(decoded.width).toBe(2);
|
|
77
|
+
expect(decoded.height).toBe(2);
|
|
78
|
+
expect(decoded.data[0]).toBe(100);
|
|
79
|
+
expect(decoded.data[1]).toBe(150);
|
|
80
|
+
expect(decoded.data[2]).toBe(200);
|
|
81
|
+
expect(decoded.data[3]).toBe(255);
|
|
82
|
+
});
|
|
83
|
+
it('toBase64Png returns a valid base64 string that decodes back correctly', () => {
|
|
84
|
+
const img = createCanvas(1, 1, 10, 20, 30, 255);
|
|
85
|
+
const b64 = toBase64Png(img);
|
|
86
|
+
expect(typeof b64).toBe('string');
|
|
87
|
+
const decoded = decodeImage(Buffer.from(b64, 'base64'));
|
|
88
|
+
expect(decoded.data[0]).toBe(10);
|
|
89
|
+
expect(decoded.data[1]).toBe(20);
|
|
90
|
+
expect(decoded.data[2]).toBe(30);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('createCanvas', () => {
|
|
94
|
+
it('creates a zero-filled canvas by default', () => {
|
|
95
|
+
const img = createCanvas(3, 3);
|
|
96
|
+
expect(img.width).toBe(3);
|
|
97
|
+
expect(img.height).toBe(3);
|
|
98
|
+
expect(img.data.every(v => v === 0)).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
it('fills all pixels with the provided RGBA color', () => {
|
|
101
|
+
const img = createCanvas(2, 2, 57, 170, 86, 255);
|
|
102
|
+
for (let i = 0; i < 4; i++) {
|
|
103
|
+
expect(img.data[i * 4]).toBe(57);
|
|
104
|
+
expect(img.data[i * 4 + 1]).toBe(170);
|
|
105
|
+
expect(img.data[i * 4 + 2]).toBe(86);
|
|
106
|
+
expect(img.data[i * 4 + 3]).toBe(255);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('cropImage', () => {
|
|
111
|
+
it('extracts the correct rectangular region', () => {
|
|
112
|
+
// 4x1 image: red, green, blue, yellow
|
|
113
|
+
const img = {
|
|
114
|
+
data: new Uint8Array([
|
|
115
|
+
255, 0, 0, 255, // red
|
|
116
|
+
0, 255, 0, 255, // green
|
|
117
|
+
0, 0, 255, 255, // blue
|
|
118
|
+
255, 255, 0, 255, // yellow
|
|
119
|
+
]),
|
|
120
|
+
width: 4,
|
|
121
|
+
height: 1,
|
|
122
|
+
};
|
|
123
|
+
const cropped = cropImage(img, 1, 0, 2, 1);
|
|
124
|
+
expect(cropped.width).toBe(2);
|
|
125
|
+
expect(cropped.height).toBe(1);
|
|
126
|
+
// First pixel = green
|
|
127
|
+
expect(cropped.data[0]).toBe(0);
|
|
128
|
+
expect(cropped.data[1]).toBe(255);
|
|
129
|
+
expect(cropped.data[2]).toBe(0);
|
|
130
|
+
// Second pixel = blue
|
|
131
|
+
expect(cropped.data[4]).toBe(0);
|
|
132
|
+
expect(cropped.data[5]).toBe(0);
|
|
133
|
+
expect(cropped.data[6]).toBe(255);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('compositeImage', () => {
|
|
137
|
+
it('copies an opaque overlay exactly onto the base', () => {
|
|
138
|
+
const base = createCanvas(2, 2, 0, 0, 0, 255);
|
|
139
|
+
const overlay = createCanvas(1, 1, 255, 0, 0, 255);
|
|
140
|
+
compositeImage(base, overlay, 1, 1);
|
|
141
|
+
const di = (1 * 2 + 1) * 4;
|
|
142
|
+
expect(base.data[di]).toBe(255);
|
|
143
|
+
expect(base.data[di + 1]).toBe(0);
|
|
144
|
+
expect(base.data[di + 2]).toBe(0);
|
|
145
|
+
expect(base.data[di + 3]).toBe(255);
|
|
146
|
+
// Top-left should be untouched
|
|
147
|
+
expect(base.data[0]).toBe(0);
|
|
148
|
+
});
|
|
149
|
+
it('blends a semi-transparent overlay with opacity', () => {
|
|
150
|
+
const base = createCanvas(1, 1, 0, 0, 0, 255); // black opaque
|
|
151
|
+
const overlay = createCanvas(1, 1, 255, 0, 0, 255); // red opaque
|
|
152
|
+
compositeImage(base, overlay, 0, 0, 0.5);
|
|
153
|
+
// src_a = 0.5, dst_a = 1, out_a = 1
|
|
154
|
+
// R = round((255 * 0.5 + 0 * 1 * 0.5) / 1) = 128 (approximately)
|
|
155
|
+
expect(base.data[0]).toBeCloseTo(128, 0);
|
|
156
|
+
expect(base.data[3]).toBe(255);
|
|
157
|
+
});
|
|
158
|
+
it('skips blending when both src and dst alpha are zero', () => {
|
|
159
|
+
const base = createCanvas(1, 1, 255, 0, 0, 0); // red but fully transparent
|
|
160
|
+
const overlay = createCanvas(1, 1, 0, 255, 0, 0); // green but fully transparent
|
|
161
|
+
compositeImage(base, overlay, 0, 0);
|
|
162
|
+
// outA = 0 → pixel unchanged: base stays (255, 0, 0, 0)
|
|
163
|
+
expect(base.data[0]).toBe(255);
|
|
164
|
+
expect(base.data[3]).toBe(0);
|
|
165
|
+
});
|
|
166
|
+
it('skips pixels outside the base bounds', () => {
|
|
167
|
+
const base = createCanvas(2, 2, 0, 0, 0, 255);
|
|
168
|
+
const overlay = createCanvas(3, 3, 255, 0, 0, 255);
|
|
169
|
+
// Offset so overlay extends beyond base - should not throw
|
|
170
|
+
expect(() => compositeImage(base, overlay, 1, 1)).not.toThrow();
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
describe('resizeBilinear', () => {
|
|
174
|
+
it('doubles a 1x1 image to 2x2 with the same color', () => {
|
|
175
|
+
const img = createCanvas(1, 1, 100, 200, 50, 255);
|
|
176
|
+
const resized = resizeBilinear(img, 2, 2);
|
|
177
|
+
expect(resized.width).toBe(2);
|
|
178
|
+
expect(resized.height).toBe(2);
|
|
179
|
+
expect(resized.data[0]).toBe(100);
|
|
180
|
+
expect(resized.data[1]).toBe(200);
|
|
181
|
+
expect(resized.data[2]).toBe(50);
|
|
182
|
+
});
|
|
183
|
+
it('halves a 4x4 image to 2x2', () => {
|
|
184
|
+
const img = createCanvas(4, 4, 200, 100, 50, 255);
|
|
185
|
+
const resized = resizeBilinear(img, 2, 2);
|
|
186
|
+
expect(resized.width).toBe(2);
|
|
187
|
+
expect(resized.height).toBe(2);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
describe('rotate90CW', () => {
|
|
191
|
+
it('swaps width and height', () => {
|
|
192
|
+
const img = createCanvas(4, 2, 0, 0, 0, 255);
|
|
193
|
+
const rotated = rotate90CW(img);
|
|
194
|
+
expect(rotated.width).toBe(2);
|
|
195
|
+
expect(rotated.height).toBe(4);
|
|
196
|
+
});
|
|
197
|
+
it('moves top-left pixel to top-right', () => {
|
|
198
|
+
// 2x1: [red | green]
|
|
199
|
+
const img = {
|
|
200
|
+
data: new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255]),
|
|
201
|
+
width: 2,
|
|
202
|
+
height: 1,
|
|
203
|
+
};
|
|
204
|
+
const rotated = rotate90CW(img);
|
|
205
|
+
// 90° CW: new 1x2 - top pixel comes from bottom-left of src (only 1 row, so left=red)
|
|
206
|
+
expect(rotated.width).toBe(1);
|
|
207
|
+
expect(rotated.height).toBe(2);
|
|
208
|
+
// top of rotated should be red (was left in src)
|
|
209
|
+
expect(rotated.data[0]).toBe(255); // R
|
|
210
|
+
expect(rotated.data[1]).toBe(0); // G
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe('rotate90CCW', () => {
|
|
214
|
+
it('swaps width and height', () => {
|
|
215
|
+
const img = createCanvas(4, 2, 0, 0, 0, 255);
|
|
216
|
+
const rotated = rotate90CCW(img);
|
|
217
|
+
expect(rotated.width).toBe(2);
|
|
218
|
+
expect(rotated.height).toBe(4);
|
|
219
|
+
});
|
|
220
|
+
it('is the inverse of rotate90CW', () => {
|
|
221
|
+
const img = createCanvas(3, 2, 0, 0, 0, 255);
|
|
222
|
+
img.data[0] = 42; // mark top-left
|
|
223
|
+
const cw = rotate90CW(img);
|
|
224
|
+
const back = rotate90CCW(cw);
|
|
225
|
+
expect(back.width).toBe(3);
|
|
226
|
+
expect(back.height).toBe(2);
|
|
227
|
+
expect(back.data[0]).toBe(42);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
describe('rotate180', () => {
|
|
231
|
+
it('preserves dimensions', () => {
|
|
232
|
+
const img = createCanvas(3, 4, 0, 0, 0, 255);
|
|
233
|
+
const rotated = rotate180(img);
|
|
234
|
+
expect(rotated.width).toBe(3);
|
|
235
|
+
expect(rotated.height).toBe(4);
|
|
236
|
+
});
|
|
237
|
+
it('flips the pixel order', () => {
|
|
238
|
+
const img = {
|
|
239
|
+
data: new Uint8Array([255, 0, 0, 255, 0, 255, 0, 255]),
|
|
240
|
+
width: 2,
|
|
241
|
+
height: 1,
|
|
242
|
+
};
|
|
243
|
+
const rotated = rotate180(img);
|
|
244
|
+
// First pixel should now be green (was last)
|
|
245
|
+
expect(rotated.data[0]).toBe(0);
|
|
246
|
+
expect(rotated.data[1]).toBe(255);
|
|
247
|
+
expect(rotated.data[2]).toBe(0);
|
|
248
|
+
// Second pixel should be red
|
|
249
|
+
expect(rotated.data[4]).toBe(255);
|
|
250
|
+
expect(rotated.data[5]).toBe(0);
|
|
251
|
+
});
|
|
252
|
+
it('is its own inverse', () => {
|
|
253
|
+
const img = createCanvas(2, 2, 0, 0, 0, 255);
|
|
254
|
+
img.data[0] = 77;
|
|
255
|
+
const twice = rotate180(rotate180(img));
|
|
256
|
+
expect(twice.data[0]).toBe(77);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
describe('setOpacity', () => {
|
|
260
|
+
it('halves the alpha channel of each pixel', () => {
|
|
261
|
+
const img = createCanvas(2, 2, 57, 170, 86, 255);
|
|
262
|
+
setOpacity(img, 0.5);
|
|
263
|
+
for (let i = 0; i < 4; i++) {
|
|
264
|
+
expect(img.data[i * 4 + 3]).toBe(128);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
it('sets full opacity to 255', () => {
|
|
268
|
+
const img = createCanvas(1, 1, 0, 0, 0, 128);
|
|
269
|
+
setOpacity(img, 1);
|
|
270
|
+
expect(img.data[3]).toBe(128); // unchanged - 128 * 1 = 128
|
|
271
|
+
});
|
|
272
|
+
it('sets zero opacity to 0', () => {
|
|
273
|
+
const img = createCanvas(1, 1, 0, 0, 0, 200);
|
|
274
|
+
setOpacity(img, 0);
|
|
275
|
+
expect(img.data[3]).toBe(0);
|
|
276
|
+
});
|
|
277
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wdio/image-comparison-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"author": "Wim Selles - wswebcreation",
|
|
5
5
|
"description": "Image comparison core module for @wdio/visual-service - WebdriverIO visual testing framework",
|
|
6
6
|
"keywords": [
|
|
@@ -26,12 +26,13 @@
|
|
|
26
26
|
"url": "https://github.com/webdriverio/visual-testing/issues"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"@wdio/
|
|
29
|
+
"fast-png": "^8.0.0",
|
|
30
|
+
"pixelmatch": "^7.2.0",
|
|
31
|
+
"@wdio/logger": "^9.29.1",
|
|
32
|
+
"@wdio/types": "^9.29.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"webdriverio": "^9.
|
|
35
|
+
"webdriverio": "^9.29.1"
|
|
35
36
|
},
|
|
36
37
|
"publishConfig": {
|
|
37
38
|
"access": "public"
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import type { BaseBoundingBox, BaseCoordinates, BaseDimensions } from '../base.interfaces.js';
|
|
2
|
-
export interface CompareData {
|
|
3
|
-
/** The mismatch percentage like 0.12345567 */
|
|
4
|
-
rawMisMatchPercentage: number;
|
|
5
|
-
/** The mismatch percentage like 0.12 */
|
|
6
|
-
misMatchPercentage: number;
|
|
7
|
-
/** The image buffer */
|
|
8
|
-
getBuffer: () => Buffer;
|
|
9
|
-
/** The bounds of the diff area */
|
|
10
|
-
diffBounds: BaseBoundingBox;
|
|
11
|
-
/** The analysis time in milliseconds */
|
|
12
|
-
analysisTime: number;
|
|
13
|
-
/** The diff pixels location(s) and color(s) */
|
|
14
|
-
diffPixels: BaseCoordinates[];
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Src: @types/resemblejs
|
|
18
|
-
*/
|
|
19
|
-
type OutputSettings = {
|
|
20
|
-
/** Color to use for highlighting errors */
|
|
21
|
-
errorColor?: {
|
|
22
|
-
/** Red color component (0-255) */
|
|
23
|
-
red: number;
|
|
24
|
-
/** Green color component (0-255) */
|
|
25
|
-
green: number;
|
|
26
|
-
/** Blue color component (0-255) */
|
|
27
|
-
blue: number;
|
|
28
|
-
} | undefined;
|
|
29
|
-
/** Type of error highlighting to use */
|
|
30
|
-
errorType?: OutputErrorType | undefined;
|
|
31
|
-
/** Custom error pixel processing function */
|
|
32
|
-
errorPixel?: ((px: number[], offset: number, d1: Color, d2: Color) => void) | undefined;
|
|
33
|
-
/** Transparency level for the output image */
|
|
34
|
-
transparency?: number | undefined;
|
|
35
|
-
/** Threshold for large image processing */
|
|
36
|
-
largeImageThreshold?: number | undefined;
|
|
37
|
-
/** Whether to use cross-origin for image loading */
|
|
38
|
-
useCrossOrigin?: boolean | undefined;
|
|
39
|
-
/** Bounding box to focus comparison on */
|
|
40
|
-
boundingBox?: Box | undefined;
|
|
41
|
-
/** Box area to ignore during comparison */
|
|
42
|
-
ignoredBox?: Box | undefined;
|
|
43
|
-
/** Multiple bounding boxes to focus comparison on */
|
|
44
|
-
boundingBoxes?: Box[] | undefined;
|
|
45
|
-
/** Multiple box areas to ignore during comparison */
|
|
46
|
-
ignoredBoxes?: Box[] | undefined;
|
|
47
|
-
/** Color to ignore during comparison */
|
|
48
|
-
ignoreAreasColoredWith?: Color | undefined;
|
|
49
|
-
};
|
|
50
|
-
type Box = {
|
|
51
|
-
/** Left boundary of the box */
|
|
52
|
-
left: number;
|
|
53
|
-
/** Top boundary of the box */
|
|
54
|
-
top: number;
|
|
55
|
-
/** Right boundary of the box */
|
|
56
|
-
right: number;
|
|
57
|
-
/** Bottom boundary of the box */
|
|
58
|
-
bottom: number;
|
|
59
|
-
};
|
|
60
|
-
type Color = {
|
|
61
|
-
/** Red color component (0-255) */
|
|
62
|
-
r: number;
|
|
63
|
-
/** Green color component (0-255) */
|
|
64
|
-
g: number;
|
|
65
|
-
/** Blue color component (0-255) */
|
|
66
|
-
b: number;
|
|
67
|
-
/** Alpha transparency component (0-255) */
|
|
68
|
-
a: number;
|
|
69
|
-
};
|
|
70
|
-
type Tolerance = {
|
|
71
|
-
/** Tolerance for red color component */
|
|
72
|
-
red?: number;
|
|
73
|
-
/** Tolerance for green color component */
|
|
74
|
-
green?: number;
|
|
75
|
-
/** Tolerance for blue color component */
|
|
76
|
-
blue?: number;
|
|
77
|
-
/** Tolerance for alpha transparency component */
|
|
78
|
-
alpha?: number;
|
|
79
|
-
/** Minimum brightness tolerance */
|
|
80
|
-
minBrightness?: number;
|
|
81
|
-
/** Maximum brightness tolerance */
|
|
82
|
-
maxBrightness?: number;
|
|
83
|
-
};
|
|
84
|
-
type OutputErrorType = 'flat' | 'movement' | 'flatDifferenceIntensity' | 'movementDifferenceIntensity' | 'diffOnly';
|
|
85
|
-
export type ComparisonIgnoreOption = 'nothing' | 'less' | 'antialiasing' | 'colors' | 'alpha';
|
|
86
|
-
export interface ComparisonOptions {
|
|
87
|
-
/** Output settings for the comparison */
|
|
88
|
-
output?: OutputSettings | undefined;
|
|
89
|
-
/** Threshold to return early if mismatch exceeds this value */
|
|
90
|
-
returnEarlyThreshold?: number | undefined;
|
|
91
|
-
/** Whether to scale images to the same size before comparison */
|
|
92
|
-
scaleToSameSize?: boolean | undefined;
|
|
93
|
-
/** What aspects to ignore during comparison */
|
|
94
|
-
ignore?: ComparisonIgnoreOption | ComparisonIgnoreOption[] | undefined;
|
|
95
|
-
/** Tolerance settings for color differences */
|
|
96
|
-
tolerance?: Tolerance | undefined;
|
|
97
|
-
}
|
|
98
|
-
export interface ComparisonResult {
|
|
99
|
-
/**
|
|
100
|
-
* Error information if error encountered
|
|
101
|
-
*
|
|
102
|
-
* Note: If error encountered, other properties will be undefined
|
|
103
|
-
*/
|
|
104
|
-
error?: unknown | undefined;
|
|
105
|
-
/**
|
|
106
|
-
* Time consumed by the comparison (in milliseconds)
|
|
107
|
-
*/
|
|
108
|
-
analysisTime: number;
|
|
109
|
-
/**
|
|
110
|
-
* Do the two images have the same dimensions?
|
|
111
|
-
*/
|
|
112
|
-
isSameDimensions: boolean;
|
|
113
|
-
/**
|
|
114
|
-
* The difference in width and height between the dimensions of the two compared images
|
|
115
|
-
*/
|
|
116
|
-
dimensionDifference: BaseDimensions;
|
|
117
|
-
/**
|
|
118
|
-
* The percentage of pixels which do not match between the images
|
|
119
|
-
*/
|
|
120
|
-
rawMisMatchPercentage: number;
|
|
121
|
-
/**
|
|
122
|
-
* Same as `rawMisMatchPercentage` but fixed to 2-digit after the decimal point
|
|
123
|
-
*/
|
|
124
|
-
misMatchPercentage: number;
|
|
125
|
-
diffBounds: Box;
|
|
126
|
-
/**
|
|
127
|
-
* Get a data URL for the comparison image
|
|
128
|
-
*/
|
|
129
|
-
getImageDataUrl(): string;
|
|
130
|
-
/**
|
|
131
|
-
* Get data buffer
|
|
132
|
-
*/
|
|
133
|
-
getBuffer?: (includeOriginal: boolean) => Buffer;
|
|
134
|
-
}
|
|
135
|
-
export {};
|
|
136
|
-
//# sourceMappingURL=compare.interfaces.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compare.interfaces.d.ts","sourceRoot":"","sources":["../../src/resemble/compare.interfaces.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"compareImages.d.ts","sourceRoot":"","sources":["../../src/resemble/compareImages.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAE7E,wBAA8B,aAAa,CACvC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,iBAAiB,GAC3B,OAAO,CAAC,WAAW,CAAC,CAmBtB"}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
// @ts-ignore: Ignoring type checking for this module import
|
|
2
|
-
import * as resembleJimp from './resemble.jimp.cjs';
|
|
3
|
-
export default async function compareImages(image1, image2, options) {
|
|
4
|
-
/**
|
|
5
|
-
* Resemble.js implemented in the way that scales 2nd images to the size of 1st.
|
|
6
|
-
* Experimentally proven that downscaling images produces more accurate result than upscaling
|
|
7
|
-
*/
|
|
8
|
-
const { imageToCompare1, imageToCompare2 } = options.scaleToSameSize && image1.length > image2.length
|
|
9
|
-
? {
|
|
10
|
-
imageToCompare1: image2,
|
|
11
|
-
imageToCompare2: image1,
|
|
12
|
-
}
|
|
13
|
-
: { imageToCompare1: image1, imageToCompare2: image2 };
|
|
14
|
-
try {
|
|
15
|
-
const data = await resembleJimp.default.compare(imageToCompare1, imageToCompare2, options);
|
|
16
|
-
return data;
|
|
17
|
-
}
|
|
18
|
-
catch (err) {
|
|
19
|
-
throw err;
|
|
20
|
-
}
|
|
21
|
-
}
|