@@ -10,6 +10,7 @@ import open from 'open';
1010import { filterTargets } from './config.mjs' ;
1111import { readManifest , detectStaleControls } from './manifest.mjs' ;
1212import { copyAssets , buildHtmlDiff , generateReport } from './report.mjs' ;
13+ import { downscaleToDisplay } from './downscale.mjs' ;
1314
1415// Skip the inline HTML line-diff above this snapshot size; line-diffing a
1516// multi-MB (often minified) document is slow and produces no useful result.
@@ -77,21 +78,21 @@ export function compareSlug(config, target, viewport) {
7778 return null ;
7879 }
7980
80- let img1 = PNG . sync . read ( fs . readFileSync ( controlImage ) ) ;
81- let img2 = PNG . sync . read ( fs . readFileSync ( captureImage ) ) ;
81+ const control = PNG . sync . read ( fs . readFileSync ( controlImage ) ) ;
82+ const capture = PNG . sync . read ( fs . readFileSync ( captureImage ) ) ;
8283
8384 // Pad both onto a common canvas so width changes (a real layout
8485 // regression) surface as a large diff instead of dropping the slug from
8586 // the report. Height was already handled this way; width is too now.
86- const maxWidth = Math . max ( img1 . width , img2 . width ) ;
87- const maxHeight = Math . max ( img1 . height , img2 . height ) ;
88- img1 = padImage ( img1 , maxWidth , maxHeight ) ;
89- img2 = padImage ( img2 , maxWidth , maxHeight ) ;
87+ const maxWidth = Math . max ( control . width , capture . width ) ;
88+ const maxHeight = Math . max ( control . height , capture . height ) ;
89+ const padded1 = padImage ( control , maxWidth , maxHeight ) ;
90+ const padded2 = padImage ( capture , maxWidth , maxHeight ) ;
9091
9192 const diff = new PNG ( { width : maxWidth , height : maxHeight } ) ;
9293 const numDiffPixels = pixelmatch (
93- img1 . data ,
94- img2 . data ,
94+ padded1 . data ,
95+ padded2 . data ,
9596 diff . data ,
9697 maxWidth ,
9798 maxHeight ,
@@ -101,6 +102,44 @@ export function compareSlug(config, target, viewport) {
101102 const diffImage = path . join ( dirs . compares , `${ slug } -diff.png` ) ;
102103 writeArtifact ( diffImage , PNG . sync . write ( diff ) ) ;
103104
105+ // pixelmatch runs at full resolution above; the report, however, is viewed
106+ // in a browser that can't decode an image past ~32,767px on a side. Hand
107+ // the report a downscaled copy of any oversized image (and remember the
108+ // original size so it can say so), leaving the full-res artifact in place
109+ // as the source of truth and the baseline pixelmatch compares against.
110+ const displayOf = ( source , original , name ) => {
111+ const result = downscaleToDisplay ( source ) ;
112+ if ( ! result . downscaled ) {
113+ return { image : original , scaled : null } ;
114+ }
115+ const displayImage = path . join ( dirs . display , `${ slug } -${ name } .png` ) ;
116+ writeArtifact ( displayImage , PNG . sync . write ( result . png ) ) ;
117+ return {
118+ image : displayImage ,
119+ scaled : {
120+ from : [ result . originalWidth , result . originalHeight ] ,
121+ to : [ result . width , result . height ] ,
122+ } ,
123+ } ;
124+ } ;
125+
126+ // control/capture display at their own native size in the report; the diff
127+ // is shown at the padded common size.
128+ const controlDisp = displayOf ( control , controlImage , 'control' ) ;
129+ const captureDisp = displayOf ( capture , captureImage , 'capture' ) ;
130+ const diffDisp = displayOf ( diff , diffImage , 'diff' ) ;
131+
132+ const scaled = { } ;
133+ if ( controlDisp . scaled ) {
134+ scaled . control = controlDisp . scaled ;
135+ }
136+ if ( captureDisp . scaled ) {
137+ scaled . capture = captureDisp . scaled ;
138+ }
139+ if ( diffDisp . scaled ) {
140+ scaled . diff = diffDisp . scaled ;
141+ }
142+
104143 const totalPixels = maxWidth * maxHeight ;
105144 const diffPercentage = ( numDiffPixels / totalPixels ) * 100 ;
106145
@@ -143,6 +182,12 @@ export function compareSlug(config, target, viewport) {
143182 controlImage,
144183 captureImage,
145184 diffImage,
185+ // Browser-safe paths the report links to (the original when it already
186+ // fits, a downscaled copy when it didn't).
187+ controlDisplay : controlDisp . image ,
188+ captureDisplay : captureDisp . image ,
189+ diffDisplay : diffDisp . image ,
190+ scaled : Object . keys ( scaled ) . length ? scaled : null ,
146191 diffPercentage,
147192 htmlAdd : htmlResult . add ,
148193 htmlDel : htmlResult . del ,
@@ -337,6 +382,7 @@ export async function compare(config, options = {}) {
337382 const startedAt = Date . now ( ) ;
338383
339384 fs . mkdirSync ( dirs . compares , { recursive : true } ) ;
385+ fs . mkdirSync ( dirs . display , { recursive : true } ) ;
340386 fs . mkdirSync ( dirs . reports , { recursive : true } ) ;
341387 copyAssets ( config ) ;
342388
0 commit comments