@vizzly-testing/honeydiff 0.0.2 → 0.0.3
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/README.md
CHANGED
|
@@ -5,25 +5,9 @@
|
|
|
5
5
|
[](https://github.com/vizzly-testing/honeydiff/actions)
|
|
6
6
|
[](../../LICENSE)
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## Features
|
|
13
|
-
|
|
14
|
-
- **🚀 Native Performance** - Rust-powered via Neon bindings (1.1B pixels/sec for Full HD)
|
|
15
|
-
- **📦 Dual Input** - Works with file paths or Buffers
|
|
16
|
-
- **⚡ Async + Sync APIs** - Choose between non-blocking promises or synchronous operations
|
|
17
|
-
- **🎯 Full TypeScript Support** - Complete type definitions included
|
|
18
|
-
- **🔍 Advanced Analysis**:
|
|
19
|
-
- Anti-aliasing detection
|
|
20
|
-
- SSIM perceptual similarity scoring
|
|
21
|
-
- Spatial clustering of diff regions
|
|
22
|
-
- Intensity statistics (min, max, mean, median, std dev)
|
|
23
|
-
- Bounding box detection
|
|
24
|
-
- **📸 Variable Height Support** - Compare scrollable screenshots with different heights
|
|
25
|
-
- **🎨 Visual Artifacts** - Generate diff, mask, and overlay images
|
|
26
|
-
- **⚙️ Flexible Options** - Pixel tolerance, color thresholds, custom diff colors
|
|
8
|
+
Fast image diffing for Node.js with native Rust performance (1.1B pixels/sec). Supports anti-aliasing detection, SSIM, clustering, and variable-height screenshots.
|
|
9
|
+
|
|
10
|
+
**Features:** File paths or Buffers • Async + sync APIs • TypeScript types • Pre-built binaries (macOS/Linux/Windows)
|
|
27
11
|
|
|
28
12
|
## Installation
|
|
29
13
|
|
|
@@ -31,25 +15,15 @@ Blazing fast image comparison with support for anti-aliasing detection, perceptu
|
|
|
31
15
|
npm install @vizzly-testing/honeydiff
|
|
32
16
|
```
|
|
33
17
|
|
|
34
|
-
|
|
35
|
-
- Node.js 22+
|
|
36
|
-
|
|
37
|
-
**Pre-built Binaries:**
|
|
38
|
-
This package ships with pre-built native binaries for:
|
|
39
|
-
- macOS (Apple Silicon arm64)
|
|
40
|
-
- Linux (x64 and arm64)
|
|
41
|
-
- Windows (x64)
|
|
42
|
-
|
|
43
|
-
No Rust toolchain required! The correct binary is automatically loaded for your platform.
|
|
18
|
+
Requires Node.js 22+. Ships with pre-built binaries for macOS (ARM64), Linux (x64/ARM64), and Windows (x64). No Rust toolchain needed.
|
|
44
19
|
|
|
45
20
|
## Quick Start
|
|
46
21
|
|
|
47
|
-
|
|
48
|
-
|
|
22
|
+
**JavaScript**
|
|
49
23
|
```javascript
|
|
50
24
|
const { compare, quickCompare } = require('@vizzly-testing/honeydiff');
|
|
51
25
|
|
|
52
|
-
// Quick
|
|
26
|
+
// Quick check
|
|
53
27
|
if (await quickCompare('baseline.png', 'current.png')) {
|
|
54
28
|
console.log('Visual regression detected!');
|
|
55
29
|
}
|
|
@@ -60,112 +34,62 @@ const result = await compare('baseline.png', 'current.png', {
|
|
|
60
34
|
includeSSIM: true
|
|
61
35
|
});
|
|
62
36
|
|
|
63
|
-
console.log(`Diff: ${result.diffPercentage.toFixed(2)}
|
|
64
|
-
console.log(`SSIM: ${result.perceptualScore}`);
|
|
37
|
+
console.log(`Diff: ${result.diffPercentage.toFixed(2)}%, SSIM: ${result.perceptualScore}`);
|
|
65
38
|
```
|
|
66
39
|
|
|
67
|
-
|
|
40
|
+
**TypeScript**
|
|
68
41
|
|
|
69
42
|
```typescript
|
|
70
43
|
import { compare, CompareOptions, DiffResult } from '@vizzly-testing/honeydiff';
|
|
71
44
|
|
|
72
|
-
const
|
|
45
|
+
const result: DiffResult = await compare('img1.png', 'img2.png', {
|
|
73
46
|
pixelTolerance: 5,
|
|
74
47
|
includeSSIM: true,
|
|
75
48
|
includeClusters: true
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const result: DiffResult = await compare('img1.png', 'img2.png', options);
|
|
49
|
+
});
|
|
79
50
|
|
|
80
51
|
if (result.diffClusters) {
|
|
81
52
|
console.log(`Found ${result.diffClusters.length} diff regions`);
|
|
82
53
|
}
|
|
83
54
|
```
|
|
84
55
|
|
|
85
|
-
## API
|
|
86
|
-
|
|
87
|
-
### Async API (Recommended)
|
|
88
|
-
|
|
89
|
-
#### `compare(img1, img2, options?): Promise<DiffResult>`
|
|
90
|
-
|
|
91
|
-
Compare two images asynchronously. Non-blocking operation.
|
|
92
|
-
|
|
93
|
-
**Parameters:**
|
|
94
|
-
- `img1`: `string | Buffer` - First image (file path or Buffer)
|
|
95
|
-
- `img2`: `string | Buffer` - Second image (file path or Buffer)
|
|
96
|
-
- `options`: `CompareOptions` (optional) - Comparison configuration
|
|
97
|
-
|
|
98
|
-
**Returns:** `Promise<DiffResult>` - Detailed diff results
|
|
99
|
-
|
|
100
|
-
**Example:**
|
|
101
|
-
```javascript
|
|
102
|
-
const result = await compare('baseline.png', 'current.png', {
|
|
103
|
-
pixelTolerance: 10,
|
|
104
|
-
antialiasing: true,
|
|
105
|
-
includeSSIM: true,
|
|
106
|
-
diffPath: '/tmp/diff.png'
|
|
107
|
-
});
|
|
108
|
-
```
|
|
56
|
+
## API
|
|
109
57
|
|
|
110
|
-
|
|
58
|
+
**Async (recommended)**
|
|
59
|
+
- `compare(img1, img2, options?): Promise<DiffResult>` - Full comparison
|
|
60
|
+
- `quickCompare(img1, img2): Promise<boolean>` - Quick check
|
|
111
61
|
|
|
112
|
-
|
|
62
|
+
**Sync (blocks event loop)**
|
|
63
|
+
- `compareSync(img1, img2, options?): DiffResult`
|
|
64
|
+
- `quickCompareSync(img1, img2): boolean`
|
|
113
65
|
|
|
114
|
-
|
|
66
|
+
Inputs: `string` (file path) or `Buffer`
|
|
115
67
|
|
|
116
|
-
|
|
68
|
+
## Options & Results
|
|
117
69
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
Synchronous comparison (blocks event loop). Use only when necessary.
|
|
121
|
-
|
|
122
|
-
#### `quickCompareSync(img1, img2): boolean`
|
|
123
|
-
|
|
124
|
-
Synchronous quick comparison returning a boolean.
|
|
125
|
-
|
|
126
|
-
## Options
|
|
70
|
+
<details>
|
|
71
|
+
<summary><strong>CompareOptions</strong></summary>
|
|
127
72
|
|
|
128
73
|
```typescript
|
|
129
74
|
interface CompareOptions {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
/** Max differences to find before stopping (default: unlimited) */
|
|
143
|
-
maxDiffs?: number;
|
|
144
|
-
|
|
145
|
-
/** Include detailed pixel list (default: false) */
|
|
146
|
-
includeDiffPixels?: boolean;
|
|
147
|
-
|
|
148
|
-
/** Include spatial clusters (default: false) */
|
|
149
|
-
includeClusters?: boolean;
|
|
150
|
-
|
|
151
|
-
/** Calculate SSIM score (default: false) */
|
|
152
|
-
includeSSIM?: boolean;
|
|
153
|
-
|
|
154
|
-
/** Path to save diff image */
|
|
155
|
-
diffPath?: string;
|
|
156
|
-
|
|
157
|
-
/** Path to save binary mask */
|
|
158
|
-
maskPath?: string;
|
|
159
|
-
|
|
160
|
-
/** Path to save side-by-side overlay */
|
|
161
|
-
overlayPath?: string;
|
|
162
|
-
|
|
163
|
-
/** Overwrite existing files (default: false) */
|
|
164
|
-
overwrite?: boolean;
|
|
75
|
+
pixelTolerance?: number; // 0-255 (default: 0)
|
|
76
|
+
colorThreshold?: number; // 0.0-1.0 for YIQ mode (default: 0.0 = RGB)
|
|
77
|
+
antialiasing?: boolean; // Enable AA detection (default: true)
|
|
78
|
+
ignoreColors?: boolean; // Brightness only (default: false)
|
|
79
|
+
maxDiffs?: number; // Stop after N diffs (default: unlimited)
|
|
80
|
+
includeDiffPixels?: boolean; // Detailed pixel list (default: false)
|
|
81
|
+
includeClusters?: boolean; // Spatial clusters (default: false)
|
|
82
|
+
includeSSIM?: boolean; // SSIM score (default: false)
|
|
83
|
+
diffPath?: string; // Save diff image
|
|
84
|
+
maskPath?: string; // Save mask image
|
|
85
|
+
overlayPath?: string; // Save overlay image
|
|
86
|
+
overwrite?: boolean; // Overwrite files (default: false)
|
|
165
87
|
}
|
|
166
88
|
```
|
|
89
|
+
</details>
|
|
167
90
|
|
|
168
|
-
|
|
91
|
+
<details>
|
|
92
|
+
<summary><strong>DiffResult</strong></summary>
|
|
169
93
|
|
|
170
94
|
```typescript
|
|
171
95
|
interface DiffResult {
|
|
@@ -180,148 +104,79 @@ interface DiffResult {
|
|
|
180
104
|
diffPixelsList: DiffPixel[] | null;
|
|
181
105
|
diffClusters: DiffCluster[] | null;
|
|
182
106
|
intensityStats: IntensityStats | null;
|
|
183
|
-
perceptualScore: number | null;
|
|
107
|
+
perceptualScore: number | null; // SSIM 0.0-1.0
|
|
184
108
|
}
|
|
185
109
|
```
|
|
110
|
+
</details>
|
|
186
111
|
|
|
187
|
-
##
|
|
188
|
-
|
|
189
|
-
### With Buffers
|
|
112
|
+
## Examples
|
|
190
113
|
|
|
114
|
+
**Buffers**
|
|
191
115
|
```javascript
|
|
192
116
|
const fs = require('fs');
|
|
193
117
|
const { compare } = require('@vizzly-testing/honeydiff');
|
|
194
118
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const result = await compare(img1, img2, {
|
|
199
|
-
pixelTolerance: 5
|
|
200
|
-
});
|
|
119
|
+
let img1 = fs.readFileSync('image1.png');
|
|
120
|
+
let img2 = fs.readFileSync('image2.png');
|
|
121
|
+
let result = await compare(img1, img2, { pixelTolerance: 5 });
|
|
201
122
|
```
|
|
202
123
|
|
|
203
|
-
|
|
124
|
+
**Diff Images**
|
|
204
125
|
|
|
205
126
|
```javascript
|
|
206
|
-
|
|
207
|
-
diffPath: './
|
|
208
|
-
maskPath: './
|
|
209
|
-
overlayPath: './
|
|
127
|
+
let result = await compare('baseline.png', 'current.png', {
|
|
128
|
+
diffPath: './diff.png',
|
|
129
|
+
maskPath: './mask.png',
|
|
130
|
+
overlayPath: './overlay.png',
|
|
210
131
|
overwrite: true
|
|
211
132
|
});
|
|
212
|
-
|
|
213
|
-
console.log(`Diff images saved with ${result.diffPercentage}% difference`);
|
|
214
133
|
```
|
|
215
134
|
|
|
216
|
-
|
|
217
|
-
|
|
135
|
+
**Clustering & Metrics**
|
|
218
136
|
```javascript
|
|
219
|
-
|
|
220
|
-
includeClusters: true
|
|
137
|
+
let result = await compare('img1.png', 'img2.png', {
|
|
138
|
+
includeClusters: true,
|
|
139
|
+
includeSSIM: true,
|
|
140
|
+
includeDiffPixels: true
|
|
221
141
|
});
|
|
222
142
|
|
|
143
|
+
// Spatial clusters
|
|
223
144
|
if (result.diffClusters) {
|
|
224
|
-
result.diffClusters.forEach((
|
|
225
|
-
console.log(`
|
|
226
|
-
console.log(` Pixels: ${cluster.pixelCount}`);
|
|
227
|
-
console.log(` Center: (${cluster.centerOfMass[0]}, ${cluster.centerOfMass[1]})`);
|
|
228
|
-
console.log(` Bounds: ${cluster.boundingBox.width}x${cluster.boundingBox.height}`);
|
|
145
|
+
result.diffClusters.forEach((c, i) => {
|
|
146
|
+
console.log(`Region ${i + 1}: ${c.pixelCount} pixels at (${c.centerOfMass[0]}, ${c.centerOfMass[1]})`);
|
|
229
147
|
});
|
|
230
148
|
}
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Perceptual Similarity (SSIM)
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
const result = await compare('img1.png', 'img2.png', {
|
|
237
|
-
includeSSIM: true
|
|
238
|
-
});
|
|
239
149
|
|
|
150
|
+
// SSIM score
|
|
240
151
|
if (result.perceptualScore !== null) {
|
|
241
|
-
|
|
242
|
-
console.log('Perceptually very similar');
|
|
243
|
-
} else if (result.perceptualScore > 0.80) {
|
|
244
|
-
console.log('Somewhat similar');
|
|
245
|
-
} else {
|
|
246
|
-
console.log('Perceptually different');
|
|
247
|
-
}
|
|
152
|
+
console.log(`SSIM: ${result.perceptualScore.toFixed(3)}`);
|
|
248
153
|
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Intensity Statistics
|
|
252
|
-
|
|
253
|
-
```javascript
|
|
254
|
-
const result = await compare('img1.png', 'img2.png', {
|
|
255
|
-
includeDiffPixels: true // Enables intensity stats
|
|
256
|
-
});
|
|
257
154
|
|
|
155
|
+
// Intensity stats
|
|
258
156
|
if (result.intensityStats) {
|
|
259
|
-
console.log(`
|
|
260
|
-
console.log(`Mean: ${result.intensityStats.mean.toFixed(1)}`);
|
|
261
|
-
console.log(`Median: ${result.intensityStats.median}`);
|
|
262
|
-
console.log(`Std Dev: ${result.intensityStats.stdDev.toFixed(1)}`);
|
|
157
|
+
console.log(`Range: ${result.intensityStats.min}-${result.intensityStats.max}, Mean: ${result.intensityStats.mean.toFixed(1)}`);
|
|
263
158
|
}
|
|
264
159
|
```
|
|
265
160
|
|
|
266
161
|
## Performance
|
|
267
162
|
|
|
268
|
-
Benchmarks on M-series Mac:
|
|
269
|
-
|
|
270
163
|
| Image Size | Time | Throughput |
|
|
271
164
|
|------------|------|------------|
|
|
272
165
|
| 1920x1080 (Full HD) | ~2ms | 1.1B pixels/sec |
|
|
273
166
|
| 3840x2160 (4K) | ~3ms | 3.0B pixels/sec |
|
|
274
167
|
|
|
275
|
-
With
|
|
276
|
-
|
|
277
|
-
## Variable Height Support
|
|
278
|
-
|
|
279
|
-
Honeydiff can compare images with different heights (same width) - perfect for scrollable screenshots:
|
|
280
|
-
|
|
281
|
-
```javascript
|
|
282
|
-
const result = await compare('short.png', 'tall.png');
|
|
283
|
-
|
|
284
|
-
if (result.heightDiff) {
|
|
285
|
-
console.log(`Height diff: ${result.heightDiff.height1}px vs ${result.heightDiff.height2}px`);
|
|
286
|
-
console.log(`Extra pixels: ${result.heightDiff.extraPixels}`);
|
|
287
|
-
}
|
|
288
|
-
```
|
|
168
|
+
With artifacts: ~30-35ms for Full HD. Benchmarked on Apple M-series Mac.
|
|
289
169
|
|
|
290
170
|
## Building from Source
|
|
291
171
|
|
|
292
|
-
Only needed
|
|
293
|
-
|
|
294
|
-
**Prerequisites:**
|
|
295
|
-
- Rust toolchain (install from https://rustup.rs)
|
|
296
|
-
- Node.js 22+
|
|
172
|
+
Only needed for contributing or modifying native code. Requires Rust toolchain and Node.js 22+.
|
|
297
173
|
|
|
298
174
|
```bash
|
|
299
|
-
# Clone the repository
|
|
300
175
|
git clone https://github.com/vizzly-testing/honeydiff
|
|
301
176
|
cd honeydiff/crates/honeydiff-node
|
|
302
|
-
|
|
303
|
-
# Install dependencies
|
|
304
|
-
npm install
|
|
305
|
-
|
|
306
|
-
# Build for your platform
|
|
307
|
-
npm run build
|
|
308
|
-
|
|
309
|
-
# Run examples
|
|
310
|
-
node examples/basic.js
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
### Cross-Platform Builds
|
|
314
|
-
|
|
315
|
-
```bash
|
|
316
|
-
# Build for all supported platforms (requires cross-compilation setup)
|
|
317
|
-
npm run cross-build
|
|
177
|
+
npm install && npm run build
|
|
318
178
|
```
|
|
319
179
|
|
|
320
|
-
## Related Projects
|
|
321
|
-
|
|
322
|
-
- [honeydiff](https://github.com/vizzly-testing/honeydiff) - The core Rust library
|
|
323
|
-
- [Vizzly](https://github.com/vizzly-testing/vizzly) - Visual regression testing platform
|
|
324
|
-
|
|
325
180
|
## License
|
|
326
181
|
|
|
327
|
-
MIT - See [LICENSE](../../LICENSE) for details
|
|
182
|
+
MIT - See [LICENSE](../../LICENSE) for details. Built for [Vizzly](https://github.com/vizzly-testing/vizzly) visual regression testing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/honeydiff",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "High-performance image diffing for Node.js - Native bindings to Honeydiff Rust library",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -45,6 +45,11 @@
|
|
|
45
45
|
"index.d.ts",
|
|
46
46
|
"load-platform.js",
|
|
47
47
|
"platforms/*.node",
|
|
48
|
+
"README.md",
|
|
49
|
+
"index.js",
|
|
50
|
+
"index.d.ts",
|
|
51
|
+
"load-platform.js",
|
|
52
|
+
"platforms/*.node",
|
|
48
53
|
"README.md"
|
|
49
54
|
],
|
|
50
55
|
"engines": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|