@vizzly-testing/honeydiff 0.0.3 → 0.1.1
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 +141 -18
- package/index.d.ts +47 -0
- package/package.json +11 -1
- package/platforms/index-aarch64-apple-darwin.node +0 -0
- package/platforms/index-aarch64-unknown-linux-gnu.node +0 -0
- package/platforms/index-x86_64-pc-windows-msvc.node +0 -0
- package/platforms/index-x86_64-unknown-linux-gnu.node +0 -0
package/README.md
CHANGED
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@vizzly-testing/honeydiff)
|
|
4
4
|
[](https://www.npmjs.com/package/@vizzly-testing/honeydiff)
|
|
5
|
-
[](../../LICENSE)
|
|
5
|
+
[](https://github.com/vizzly-testing/honeydiff/blob/main/LICENSE)
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
Blazingly fast image comparison for Node.js. Perfect for visual regression testing, screenshot
|
|
8
|
+
validation, and detecting visual changes.
|
|
9
9
|
|
|
10
|
-
**
|
|
10
|
+
**Why Honeydiff?**
|
|
11
|
+
- **Fast:** Native performance processing 230M+ pixels/sec
|
|
12
|
+
- **Smart:** Intelligent anti-aliasing detection filters rendering artifacts
|
|
13
|
+
- **Flexible:** File paths or Buffers, async or sync APIs
|
|
14
|
+
- **Complete:** TypeScript types, pre-built binaries for all platforms
|
|
15
|
+
- **Advanced:** SSIM perceptual scoring, spatial clustering, variable-height screenshots
|
|
11
16
|
|
|
12
17
|
## Installation
|
|
13
18
|
|
|
@@ -15,7 +20,10 @@ Fast image diffing for Node.js with native Rust performance (1.1B pixels/sec). S
|
|
|
15
20
|
npm install @vizzly-testing/honeydiff
|
|
16
21
|
```
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
**Requirements:** Node.js 22+
|
|
24
|
+
|
|
25
|
+
Pre-built binaries included for macOS (ARM64), Linux (x64/ARM64), and Windows (x64). No compilation
|
|
26
|
+
required.
|
|
19
27
|
|
|
20
28
|
## Quick Start
|
|
21
29
|
|
|
@@ -35,6 +43,7 @@ const result = await compare('baseline.png', 'current.png', {
|
|
|
35
43
|
});
|
|
36
44
|
|
|
37
45
|
console.log(`Diff: ${result.diffPercentage.toFixed(2)}%, SSIM: ${result.perceptualScore}`);
|
|
46
|
+
// Output: Diff: 3.55%, SSIM: 0.9234
|
|
38
47
|
```
|
|
39
48
|
|
|
40
49
|
**TypeScript**
|
|
@@ -51,6 +60,7 @@ const result: DiffResult = await compare('img1.png', 'img2.png', {
|
|
|
51
60
|
if (result.diffClusters) {
|
|
52
61
|
console.log(`Found ${result.diffClusters.length} diff regions`);
|
|
53
62
|
}
|
|
63
|
+
// Output: Found 3 diff regions
|
|
54
64
|
```
|
|
55
65
|
|
|
56
66
|
## API
|
|
@@ -58,10 +68,12 @@ if (result.diffClusters) {
|
|
|
58
68
|
**Async (recommended)**
|
|
59
69
|
- `compare(img1, img2, options?): Promise<DiffResult>` - Full comparison
|
|
60
70
|
- `quickCompare(img1, img2): Promise<boolean>` - Quick check
|
|
71
|
+
- `getDimensions(img): Promise<{ width: number, height: number }>` - Get image dimensions
|
|
61
72
|
|
|
62
73
|
**Sync (blocks event loop)**
|
|
63
74
|
- `compareSync(img1, img2, options?): DiffResult`
|
|
64
75
|
- `quickCompareSync(img1, img2): boolean`
|
|
76
|
+
- `getDimensionsSync(img): { width: number, height: number }` - Get image dimensions
|
|
65
77
|
|
|
66
78
|
Inputs: `string` (file path) or `Buffer`
|
|
67
79
|
|
|
@@ -111,6 +123,24 @@ interface DiffResult {
|
|
|
111
123
|
|
|
112
124
|
## Examples
|
|
113
125
|
|
|
126
|
+
**Get Image Dimensions**
|
|
127
|
+
```javascript
|
|
128
|
+
const { getDimensions, getDimensionsSync } = require('@vizzly-testing/honeydiff');
|
|
129
|
+
|
|
130
|
+
// Async
|
|
131
|
+
let dims = await getDimensions('screenshot.png');
|
|
132
|
+
console.log(`Image is ${dims.width}x${dims.height} pixels`);
|
|
133
|
+
// Output: Image is 1920x1080 pixels
|
|
134
|
+
|
|
135
|
+
// Sync
|
|
136
|
+
let dims = getDimensionsSync('screenshot.png');
|
|
137
|
+
console.log(`Image is ${dims.width}x${dims.height} pixels`);
|
|
138
|
+
|
|
139
|
+
// Works with buffers too
|
|
140
|
+
let buffer = fs.readFileSync('image.png');
|
|
141
|
+
let dims = await getDimensions(buffer);
|
|
142
|
+
```
|
|
143
|
+
|
|
114
144
|
**Buffers**
|
|
115
145
|
```javascript
|
|
116
146
|
const fs = require('fs');
|
|
@@ -146,37 +176,130 @@ if (result.diffClusters) {
|
|
|
146
176
|
console.log(`Region ${i + 1}: ${c.pixelCount} pixels at (${c.centerOfMass[0]}, ${c.centerOfMass[1]})`);
|
|
147
177
|
});
|
|
148
178
|
}
|
|
179
|
+
// Output:
|
|
180
|
+
// Region 1: 12450 pixels at (245, 189)
|
|
181
|
+
// Region 2: 3891 pixels at (672, 401)
|
|
182
|
+
// Region 3: 1023 pixels at (1156, 823)
|
|
149
183
|
|
|
150
184
|
// SSIM score
|
|
151
185
|
if (result.perceptualScore !== null) {
|
|
152
186
|
console.log(`SSIM: ${result.perceptualScore.toFixed(3)}`);
|
|
153
187
|
}
|
|
188
|
+
// Output: SSIM: 0.923
|
|
154
189
|
|
|
155
190
|
// Intensity stats
|
|
156
191
|
if (result.intensityStats) {
|
|
157
192
|
console.log(`Range: ${result.intensityStats.min}-${result.intensityStats.max}, Mean: ${result.intensityStats.mean.toFixed(1)}`);
|
|
158
193
|
}
|
|
194
|
+
// Output: Range: 12-255, Mean: 87.3
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Complete Result Example**
|
|
198
|
+
```javascript
|
|
199
|
+
let result = await compare('baseline.png', 'current.png', {
|
|
200
|
+
pixelTolerance: 10,
|
|
201
|
+
includeClusters: true
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
console.log(JSON.stringify(result, null, 2));
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
<details>
|
|
208
|
+
<summary>Example output (click to expand)</summary>
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"isDifferent": true,
|
|
213
|
+
"diffPercentage": 3.55,
|
|
214
|
+
"totalPixels": 18309000,
|
|
215
|
+
"diffPixels": 650086,
|
|
216
|
+
"aaPixelsIgnored": 1952904,
|
|
217
|
+
"aaPercentage": 10.67,
|
|
218
|
+
"boundingBox": {
|
|
219
|
+
"x": 124,
|
|
220
|
+
"y": 89,
|
|
221
|
+
"width": 1456,
|
|
222
|
+
"height": 2301
|
|
223
|
+
},
|
|
224
|
+
"heightDiff": {
|
|
225
|
+
"height1": 24162,
|
|
226
|
+
"height2": 24412,
|
|
227
|
+
"extraPixels": 187500
|
|
228
|
+
},
|
|
229
|
+
"diffPixelsList": null,
|
|
230
|
+
"diffClusters": [
|
|
231
|
+
{
|
|
232
|
+
"pixelCount": 12450,
|
|
233
|
+
"centerOfMass": [245, 189]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"pixelCount": 3891,
|
|
237
|
+
"centerOfMass": [672, 401]
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"intensityStats": {
|
|
241
|
+
"min": 12,
|
|
242
|
+
"max": 255,
|
|
243
|
+
"mean": 87.3,
|
|
244
|
+
"median": 76,
|
|
245
|
+
"stdDev": 45.2
|
|
246
|
+
},
|
|
247
|
+
"perceptualScore": null
|
|
248
|
+
}
|
|
159
249
|
```
|
|
250
|
+
</details>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Use Cases
|
|
254
|
+
|
|
255
|
+
- **Visual Regression Testing:** Detect unintended UI changes in your test suite
|
|
256
|
+
- **Screenshot Validation:** Compare generated screenshots against baselines
|
|
257
|
+
- **Design QA:** Validate UI implementations match designs
|
|
258
|
+
- **Cross-browser Testing:** Detect rendering differences across browsers
|
|
259
|
+
- **Scrollable Content:** Compare full-page screenshots with variable heights
|
|
160
260
|
|
|
161
261
|
## Performance
|
|
162
262
|
|
|
163
|
-
|
|
164
|
-
|------------|------|------------|
|
|
165
|
-
| 1920x1080 (Full HD) | ~2ms | 1.1B pixels/sec |
|
|
166
|
-
| 3840x2160 (4K) | ~3ms | 3.0B pixels/sec |
|
|
263
|
+
Tested on real-world tall screenshots (750 x 24,162 pixels = 18.3M pixels):
|
|
167
264
|
|
|
168
|
-
|
|
265
|
+
| Operation | Time | Throughput |
|
|
266
|
+
|-----------|------|------------|
|
|
267
|
+
| Basic comparison | ~79ms | 231.8M pixels/sec |
|
|
268
|
+
| With artifacts (diff/mask/overlay) | ~432ms | 42.4M pixels/sec |
|
|
169
269
|
|
|
170
|
-
|
|
270
|
+
**6x faster than odiff** on large screenshots with excellent multi-core parallelization.
|
|
171
271
|
|
|
172
|
-
|
|
272
|
+
*Benchmarked on Apple M-series Mac.*
|
|
173
273
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
274
|
+
## Advanced Features
|
|
275
|
+
|
|
276
|
+
**Variable Height Screenshots**
|
|
277
|
+
Compare scrollable content with different heights. Overlapping regions are compared pixel-by-pixel,
|
|
278
|
+
extra rows counted as differences.
|
|
279
|
+
|
|
280
|
+
**Color Spaces**
|
|
281
|
+
- RGB mode (default): Exact matching, fastest performance
|
|
282
|
+
- YIQ mode: Perceptual matching weighted for human vision
|
|
283
|
+
|
|
284
|
+
**Spatial Clustering**
|
|
285
|
+
Group nearby differences into regions using 8-connectivity clustering. Useful for identifying distinct changed areas.
|
|
286
|
+
|
|
287
|
+
**SSIM Scoring**
|
|
288
|
+
Structural Similarity Index (0.0-1.0) provides perceptual similarity measurement beyond pixel differences.
|
|
289
|
+
|
|
290
|
+
## Troubleshooting
|
|
291
|
+
|
|
292
|
+
**Module not found errors**
|
|
293
|
+
Ensure you're on Node.js 22+ and your platform is supported (macOS ARM64, Linux x64/ARM64, Windows x64).
|
|
294
|
+
|
|
295
|
+
**Large memory usage with `includeDiffPixels`**
|
|
296
|
+
This option stores every different pixel's position and intensity. Disable for large images with many differences.
|
|
297
|
+
|
|
298
|
+
**Slow performance with `includeSSIM`**
|
|
299
|
+
SSIM calculation is O(width × height × window²). Only enable when perceptual scoring is needed.
|
|
179
300
|
|
|
180
301
|
## License
|
|
181
302
|
|
|
182
|
-
MIT
|
|
303
|
+
MIT License
|
|
304
|
+
|
|
305
|
+
Built with native Rust for maximum performance.
|
package/index.d.ts
CHANGED
|
@@ -274,3 +274,50 @@ export function quickCompareSync(
|
|
|
274
274
|
img1: ImageInput,
|
|
275
275
|
img2: ImageInput
|
|
276
276
|
): boolean;
|
|
277
|
+
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// Dimensions API
|
|
280
|
+
// ============================================================================
|
|
281
|
+
|
|
282
|
+
export interface ImageDimensions {
|
|
283
|
+
/** Image width in pixels */
|
|
284
|
+
width: number;
|
|
285
|
+
/** Image height in pixels */
|
|
286
|
+
height: number;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get image dimensions asynchronously (recommended)
|
|
291
|
+
*
|
|
292
|
+
* Fast utility to retrieve image width and height without loading the full image data.
|
|
293
|
+
* Useful for pre-flight checks before comparison.
|
|
294
|
+
*
|
|
295
|
+
* @param img - Image (file path or Buffer)
|
|
296
|
+
* @returns Promise resolving to { width, height } in pixels
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* ```typescript
|
|
300
|
+
* const dims = await getDimensions('screenshot.png');
|
|
301
|
+
* console.log(`Image is ${dims.width}x${dims.height} pixels`);
|
|
302
|
+
* // Output: Image is 1920x1080 pixels
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
export function getDimensions(img: ImageInput): Promise<ImageDimensions>;
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Get image dimensions synchronously (blocks event loop)
|
|
309
|
+
*
|
|
310
|
+
* Fast utility to retrieve image width and height without loading the full image data.
|
|
311
|
+
* Use this only when you need blocking behavior.
|
|
312
|
+
*
|
|
313
|
+
* @param img - Image (file path or Buffer)
|
|
314
|
+
* @returns { width, height } in pixels
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* const dims = getDimensionsSync('screenshot.png');
|
|
319
|
+
* console.log(`Image is ${dims.width}x${dims.height} pixels`);
|
|
320
|
+
* // Output: Image is 1920x1080 pixels
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export function getDimensionsSync(img: ImageInput): ImageDimensions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizzly-testing/honeydiff",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
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",
|
|
@@ -50,6 +50,16 @@
|
|
|
50
50
|
"index.d.ts",
|
|
51
51
|
"load-platform.js",
|
|
52
52
|
"platforms/*.node",
|
|
53
|
+
"README.md",
|
|
54
|
+
"index.js",
|
|
55
|
+
"index.d.ts",
|
|
56
|
+
"load-platform.js",
|
|
57
|
+
"platforms/*.node",
|
|
58
|
+
"README.md",
|
|
59
|
+
"index.js",
|
|
60
|
+
"index.d.ts",
|
|
61
|
+
"load-platform.js",
|
|
62
|
+
"platforms/*.node",
|
|
53
63
|
"README.md"
|
|
54
64
|
],
|
|
55
65
|
"engines": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|