@vizzly-testing/honeydiff 0.11.1 → 0.13.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 CHANGED
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.0] - 2026-07-23
11
+
12
+ ### Added
13
+ - Exposed `effectiveDiffPixels`, `effectiveMaskComplete`,
14
+ `maskSemanticsVersion`, and the installed package `version` for auditable
15
+ mask consumers.
16
+
17
+ ### Fixed
18
+ - Kept binary mask alpha opaque even when a transparent display diff color is
19
+ requested.
20
+ - Kept requested pixel and cluster diagnostics from changing the effective mask
21
+ or variable-height classification.
22
+
23
+ **Full Changelog**: https://github.com/vizzly-testing/honeydiff/compare/v0.12.0...v0.13.0
24
+
25
+ ## [0.12.0] - 2026-07-19
26
+
27
+ ### Fixed
28
+ - Prevented AA filtering from hiding thin border and divider recoloring by
29
+ requiring cross-image raster displacement.
30
+ - Compared rendered RGBA appearance, including opacity changes, while ignoring
31
+ hidden RGB under full transparency.
32
+ - Kept zero-threshold results and artifact masks consistent for low-alpha RGBA.
33
+ - Kept optional metrics independent from threshold and AA-filtered diff counts.
34
+ - Corrected small-image SSIM/MS-SSIM, GMSD boundaries, and exact artifact masks.
35
+ - Matched the authors' MS-SSIM pyramid, pooling, and direct weight semantics,
36
+ plus GMSD's exact normalized constant and sample-deviation pooling.
37
+ - Made capped comparisons classify from observed pixel evidence regardless of
38
+ cluster policy, preventing early exit from reporting false identity.
39
+ - Kept diff pixels and clusters null unless callers request them.
40
+ - Added Node boundary coverage for transparency, evidence privacy, exact RGBA,
41
+ and metric independence.
42
+
43
+ **Full Changelog**: https://github.com/vizzly-testing/honeydiff/compare/v0.11.1...v0.12.0
44
+
10
45
  ## [0.11.1] - 2026-06-20
11
46
 
12
47
  ## What's Changed
package/README.md CHANGED
@@ -53,8 +53,11 @@ Most image diff packages stop at basic pixel comparison. Honeydiff gives you the
53
53
  pieces visual regression systems usually need once screenshots get real:
54
54
 
55
55
  - CIEDE2000 perceptual color thresholds, with `2.0` as the default.
56
- - Strict exact matching when you set `threshold: 0`.
56
+ - Zero perceptual tolerance with `threshold: 0`; disable AA and set
57
+ `minClusterSize: 1` for strict rendered-pixel matching.
57
58
  - Conservative anti-aliasing detection for font and sub-pixel rendering noise.
59
+ - RGBA-aware comparison that detects opacity changes and ignores RGB payload
60
+ hidden behind full transparency.
58
61
  - Variable-height screenshot support for full-page comparisons.
59
62
  - Diff, mask, and overlay image artifacts for debugging failures.
60
63
  - Spatial clusters, intensity stats, SSIM, GMSD, and diff fingerprints.
@@ -162,9 +165,9 @@ await saveColorBlindnessSimulation(
162
165
 
163
166
  | Option | Default | Notes |
164
167
  | --- | --- | --- |
165
- | `threshold` | `2.0` | CIEDE2000 Delta E threshold. Use `0` for exact matching. |
168
+ | `threshold` | `2.0` | CIEDE2000 Delta E threshold. Use `0` for zero perceptual tolerance. |
166
169
  | `antialiasing` | `true` | Ignore likely anti-aliased pixels. |
167
- | `maxDiffs` | unlimited | Stop after a maximum number of differing pixels. |
170
+ | `maxDiffs` | unlimited | Stop after a maximum number of differing pixels. Capped results classify from the pixels observed before early exit, without cluster filtering. |
168
171
  | `includeDiffPixels` | `false` | Return individual differing pixel positions and intensities. |
169
172
  | `includeClusters` | `false` | Return connected regions of visual change. |
170
173
  | `minClusterSize` | `2` | Filter tiny isolated clusters as noise. |
@@ -184,6 +187,9 @@ interface DiffResult {
184
187
  diffPercentage: number;
185
188
  totalPixels: number;
186
189
  diffPixels: number;
190
+ effectiveDiffPixels: number;
191
+ effectiveMaskComplete: boolean;
192
+ maskSemanticsVersion: string;
187
193
  aaPixelsIgnored: number;
188
194
  aaPercentage: number;
189
195
  boundingBox: BoundingBox | null;
@@ -196,6 +202,16 @@ interface DiffResult {
196
202
  }
197
203
  ```
198
204
 
205
+ `diffPixels` is the raw post-threshold/anti-aliasing count.
206
+ `effectiveDiffPixels` is the retained count represented by the binary mask after
207
+ `minClusterSize` filtering. It always matches the mask's nontransparent pixel
208
+ count. If `effectiveMaskComplete` is `false`, `maxDiffs` stopped the scan early;
209
+ the partial mask is useful for diagnostics but not complete spatial proof.
210
+ Requesting `diffPixelsList` or `diffClusters` never changes that retained result.
211
+
212
+ The package also exports `version` from its installed manifest and
213
+ `maskSemanticsVersion` from the native comparison engine.
214
+
199
215
  See [index.d.ts](./index.d.ts) for the full API surface.
200
216
 
201
217
  ## Thresholds
@@ -204,7 +220,7 @@ Honeydiff uses CIEDE2000 Delta E for perceptual color difference.
204
220
 
205
221
  | Threshold | Meaning |
206
222
  | --- | --- |
207
- | `0` | Exact pixel matching. |
223
+ | `0` | No Delta E tolerance; AA and cluster filtering still apply if enabled. |
208
224
  | `1` | Barely noticeable color changes. |
209
225
  | `2` | Recommended default for UI screenshots. |
210
226
  | `3+` | More tolerant of rendering differences. |
@@ -213,7 +229,7 @@ The default is intentionally practical for browser and app screenshots: it
213
229
  filters tiny rendering variance while still catching meaningful UI changes.
214
230
 
215
231
  The cited algorithms and standards are listed in the repo's
216
- [References](../../docs/REFERENCES.md), including CIEDE2000, SSIM, MS-SSIM,
232
+ [References](https://github.com/vizzly-testing/honeydiff/blob/main/docs/REFERENCES.md), including CIEDE2000, SSIM, MS-SSIM,
217
233
  GMSD, Brettel CVD simulation, sRGB, and WCAG contrast math.
218
234
 
219
235
  ## Performance
@@ -222,12 +238,12 @@ Current local benchmark snapshots:
222
238
 
223
239
  | Scenario | Result |
224
240
  | --- | ---: |
225
- | Full HD default comparison | ~31.6ms |
226
- | Full HD strict threshold with AA | ~103ms |
227
- | Identical image fast path | ~0.34ms |
228
- | 4K default comparison | ~131ms |
229
- | Tall screenshot default comparison | ~192ms |
230
- | Tall screenshot exact/no-AA comparison | ~4.1ms |
241
+ | Vizzly screenshot default comparison | ~2.81ms |
242
+ | Vizzly screenshot strict/no-AA comparison | ~2.18ms |
243
+ | Tall screenshot default comparison | ~147ms |
244
+ | Tall screenshot strict/no-AA comparison | ~4.34ms |
245
+ | 1080p isolated SSIM | ~14.7ms |
246
+ | 1080p isolated GMSD | ~10.9ms |
231
247
 
232
248
  See [benchmarks/BENCHMARK_RESULTS.md](https://github.com/vizzly-testing/honeydiff/blob/main/benchmarks/BENCHMARK_RESULTS.md)
233
249
  for the current benchmark notes.
package/index.d.ts CHANGED
@@ -75,6 +75,12 @@ export interface DiffResult {
75
75
  * Note: This may be > 0 even when isDifferent is false if small clusters were filtered as noise
76
76
  */
77
77
  diffPixels: number;
78
+ /** Number of retained pixels represented by the effective diff mask */
79
+ effectiveDiffPixels: number;
80
+ /** False when maxDiffs stopped the scan before the full image was evaluated */
81
+ effectiveMaskComplete: boolean;
82
+ /** Version of the pixel-retention semantics used by the effective mask */
83
+ maskSemanticsVersion: string;
78
84
  /** Number of pixels ignored due to anti-aliasing detection */
79
85
  aaPixelsIgnored: number;
80
86
  /** Percentage of pixels ignored as anti-aliasing (0.0 - 100.0) */
@@ -89,7 +95,7 @@ export interface DiffResult {
89
95
  diffClusters: DiffCluster[] | null;
90
96
  /** Statistical metrics about difference intensities (null unless includeDiffPixels is enabled) */
91
97
  intensityStats: IntensityStats | null;
92
- /** SSIM (Structural Similarity Index) perceptual score 0.0-1.0 (null unless includeSSIM is enabled) */
98
+ /** SSIM score, approximately -1.0 to 1.0 (null unless includeSSIM is enabled) */
93
99
  perceptualScore: number | null;
94
100
  /**
95
101
  * GMSD (Gradient Magnitude Similarity Deviation) score (null unless includeGMSD is enabled)
@@ -99,6 +105,12 @@ export interface DiffResult {
99
105
  gmsdScore: number | null;
100
106
  }
101
107
 
108
+ /** Installed @vizzly-testing/honeydiff package version */
109
+ export const version: string;
110
+
111
+ /** Native effective-mask semantics implemented by this package */
112
+ export const maskSemanticsVersion: string;
113
+
102
114
  // ============================================================================
103
115
  // Options
104
116
  // ============================================================================
@@ -157,7 +169,7 @@ export interface CompareOptions {
157
169
  * Perceptual color difference threshold using CIEDE2000 (Delta E units)
158
170
  *
159
171
  * The CIEDE2000 standard provides intuitive, perceptually uniform thresholds:
160
- * - 0.0 = Exact pixel matching (strictest)
172
+ * - 0.0 = No Delta E tolerance; AA and cluster filtering remain independent
161
173
  * - 1.0 = Just Noticeable Difference (JND) - barely perceptible to trained observers
162
174
  * - 2.0 = Recommended default - ignores sub-pixel rendering variance, catches real differences
163
175
  * - 3.0+ = Permissive - high tolerance for rendering variations
@@ -168,12 +180,17 @@ export interface CompareOptions {
168
180
 
169
181
  /**
170
182
  * Enable anti-aliasing detection to ignore AA differences
183
+ *
184
+ * Suppression requires a luminance ramp plus strict cross-image evidence that
185
+ * raster coverage moved to a neighboring pixel. Stable edge recolors remain differences.
171
186
  * @default true
172
187
  */
173
188
  antialiasing?: boolean;
174
189
 
175
190
  /**
176
191
  * Maximum number of differences to detect before stopping
192
+ * Capped comparisons classify from observed pixel evidence because early
193
+ * exit cannot determine whether a complete cluster meets minClusterSize.
177
194
  * @default undefined (unlimited)
178
195
  */
179
196
  maxDiffs?: number;
@@ -194,7 +211,10 @@ export interface CompareOptions {
194
211
 
195
212
  /**
196
213
  * Calculate SSIM (Structural Similarity Index) perceptual score
197
- * WARNING: SSIM can be computationally expensive on large images
214
+ * This metric measures the source images independently of threshold and AA
215
+ * filtering. Transparent inputs are evaluated over black and white mattes,
216
+ * keeping the more conservative score.
217
+ * WARNING: SSIM can use substantial memory on large images.
198
218
  * @default false
199
219
  */
200
220
  includeSSIM?: boolean;
@@ -205,6 +225,8 @@ export interface CompareOptions {
205
225
  * GMSD is very fast and highly sensitive to edge/structural changes.
206
226
  * Useful for detecting border thickness changes, font weight shifts,
207
227
  * and icon updates.
228
+ * A score of 0 does not prove image identity; uniform changes can have no
229
+ * spatial variation in gradient degradation.
208
230
  *
209
231
  * **Note:** For variable-height comparisons, GMSD is calculated on the
210
232
  * overlapping region shared by both images.
@@ -223,9 +245,9 @@ export interface CompareOptions {
223
245
  * This helps ignore run-to-run rendering variance (scattered single pixels)
224
246
  * while still catching real visual changes (grouped pixel differences).
225
247
  *
226
- * - 1 = Exact matching - any different pixel counts
248
+ * - 1 = Any surviving diff pixel counts
227
249
  * - 2 = Default - filters single isolated pixels as noise
228
- * - 3+ = More permissive - only larger clusters detected
250
+ * - 3+ = More aggressive noise filtering
229
251
  *
230
252
  * When set to a value > 1, clustering is automatically enabled.
231
253
  * @default 2
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * @vizzly-testing/honeydiff - Native Node.js bindings for Honeydiff
3
3
  */
4
4
 
5
- import { existsSync } from 'node:fs';
5
+ import { existsSync, readFileSync } from 'node:fs';
6
6
  import { createRequire } from 'node:module';
7
7
  import { dirname, join } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
@@ -11,6 +11,7 @@ const require = createRequire(import.meta.url);
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
 
13
13
  let addon;
14
+ let packageMetadata = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
14
15
 
15
16
  // Try to load the native addon:
16
17
  // 1. First try load-platform.cjs (exists in published npm package with multi-platform binaries)
@@ -41,6 +42,8 @@ export const compareSync = addon.compareSync;
41
42
  export const quickCompareSync = addon.quickCompareSync;
42
43
  export const getDimensions = addon.getDimensions;
43
44
  export const getDimensionsSync = addon.getDimensionsSync;
45
+ export const maskSemanticsVersion = addon.maskSemanticsVersion;
46
+ export const version = packageMetadata.version;
44
47
 
45
48
  // Image Metadata API
46
49
  export const getImageMetadata = addon.getImageMetadata;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vizzly-testing/honeydiff",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "High-performance image diffing for Node.js - Native bindings to Honeydiff Rust library",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -59,6 +59,7 @@
59
59
  "devDependencies": {
60
60
  "@biomejs/biome": "2.3.8",
61
61
  "@types/node": "^24.10.1",
62
+ "pngjs": "^7.0.0",
62
63
  "typescript": "^5.9.3"
63
64
  },
64
65
  "bugs": {