فهرست منبع

Puppeteer: Replaced jimp with jpeg-js and pngjs. (#32652)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
mrdoob 1 هفته پیش
والد
کامیت
3ada1b3cc2
4فایلهای تغییر یافته به همراه174 افزوده شده و 819 حذف شده
  1. 14 805
      package-lock.json
  2. 2 4
      package.json
  3. 148 0
      test/e2e/image.js
  4. 10 10
      test/e2e/puppeteer.js

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 14 - 805
package-lock.json


+ 2 - 4
package.json

@@ -103,8 +103,9 @@
     "eslint-plugin-html": "^8.1.3",
     "eslint-plugin-jsdoc": "^61.4.1",
     "globals": "^16.5.0",
-    "jimp": "^1.6.0",
+    "jpeg-js": "^0.4.4",
     "jsdoc": "^4.0.5",
+    "pngjs": "^7.0.0",
     "magic-string": "^0.30.0",
     "pixelmatch": "^7.0.0",
     "puppeteer": "^24.25.0",
@@ -112,9 +113,6 @@
     "rollup": "^4.6.0",
     "servez": "^2.2.4"
   },
-  "overrides": {
-    "jpeg-js": "^0.4.4"
-  },
   "jspm": {
     "files": [
       "package.json",

+ 148 - 0
test/e2e/image.js

@@ -0,0 +1,148 @@
+import { PNG } from 'pngjs';
+import jpeg from 'jpeg-js';
+import * as fs from 'fs/promises';
+
+class Image {
+
+	constructor( width, height, data ) {
+
+		this.width = width;
+		this.height = height;
+		this.data = data;
+
+	}
+
+	get bitmap() {
+
+		return { width: this.width, height: this.height, data: this.data };
+
+	}
+
+	clone() {
+
+		return new Image( this.width, this.height, Buffer.from( this.data ) );
+
+	}
+
+	scale( factor ) {
+
+		if ( factor >= 1 ) {
+
+			console.warn( 'Image.scale() is optimized for downscaling only.' );
+
+		}
+
+		const newWidth = Math.round( this.width * factor );
+		const newHeight = Math.round( this.height * factor );
+		const newData = Buffer.alloc( newWidth * newHeight * 4 );
+
+		// Box filter for downscaling (averages all source pixels in the region)
+		const scaleX = this.width / newWidth;
+		const scaleY = this.height / newHeight;
+
+		for ( let y = 0; y < newHeight; y ++ ) {
+
+			for ( let x = 0; x < newWidth; x ++ ) {
+
+				// Calculate source region
+				const srcX0 = x * scaleX;
+				const srcY0 = y * scaleY;
+				const srcX1 = ( x + 1 ) * scaleX;
+				const srcY1 = ( y + 1 ) * scaleY;
+
+				const x0 = Math.floor( srcX0 );
+				const y0 = Math.floor( srcY0 );
+				const x1 = Math.min( Math.ceil( srcX1 ), this.width );
+				const y1 = Math.min( Math.ceil( srcY1 ), this.height );
+
+				const dstIdx = ( y * newWidth + x ) * 4;
+				const sums = [ 0, 0, 0, 0 ];
+				let totalWeight = 0;
+
+				// Average all pixels in the source region with proper weighting
+				for ( let sy = y0; sy < y1; sy ++ ) {
+
+					for ( let sx = x0; sx < x1; sx ++ ) {
+
+						// Calculate coverage weight for edge pixels
+						const wx0 = Math.max( 0, Math.min( 1, sx + 1 - srcX0 ) );
+						const wx1 = Math.max( 0, Math.min( 1, srcX1 - sx ) );
+						const wy0 = Math.max( 0, Math.min( 1, sy + 1 - srcY0 ) );
+						const wy1 = Math.max( 0, Math.min( 1, srcY1 - sy ) );
+						const weight = Math.min( wx0, wx1 ) * Math.min( wy0, wy1 );
+
+						const srcIdx = ( sy * this.width + sx ) * 4;
+						for ( let c = 0; c < 4; c ++ ) {
+
+							sums[ c ] += this.data[ srcIdx + c ] * weight;
+
+						}
+
+						totalWeight += weight;
+
+					}
+
+				}
+
+				for ( let c = 0; c < 4; c ++ ) {
+
+					newData[ dstIdx + c ] = Math.round( sums[ c ] / totalWeight );
+
+				}
+
+			}
+
+		}
+
+		this.width = newWidth;
+		this.height = newHeight;
+		this.data = newData;
+
+		return this;
+
+	}
+
+	async write( filepath, quality = 95 ) {
+
+		const rawImageData = {
+			data: this.data,
+			width: this.width,
+			height: this.height
+		};
+
+		const encoded = jpeg.encode( rawImageData, quality );
+		await fs.writeFile( filepath, encoded.data );
+
+	}
+
+	static async read( input ) {
+
+		let buffer;
+
+		if ( typeof input === 'string' ) {
+
+			buffer = await fs.readFile( input );
+
+		} else {
+
+			buffer = input;
+
+		}
+
+		// Check if PNG (starts with PNG signature)
+		if ( buffer[ 0 ] === 0x89 && buffer[ 1 ] === 0x50 && buffer[ 2 ] === 0x4E && buffer[ 3 ] === 0x47 ) {
+
+			const png = PNG.sync.read( buffer );
+			return new Image( png.width, png.height, png.data );
+
+		}
+
+		// Otherwise assume JPEG
+		const decoded = jpeg.decode( buffer, { useTArray: true } );
+		return new Image( decoded.width, decoded.height, Buffer.from( decoded.data ) );
+
+	}
+
+}
+
+export { Image };

+ 10 - 10
test/e2e/puppeteer.js

@@ -1,6 +1,6 @@
 import puppeteer from 'puppeteer';
 import pixelmatch from 'pixelmatch';
-import { Jimp } from 'jimp';
+import { Image } from './image.js';
 import * as fs from 'fs/promises';
 import server from './server.js';
 
@@ -486,7 +486,7 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
 
 		}
 
-		const screenshot = ( await Jimp.read( await page.screenshot(), { quality: jpgQuality } ) ).scale( 1 / viewScale );
+		const screenshot = ( await Image.read( await page.screenshot() ) ).scale( 1 / viewScale );
 
 		if ( page.error !== undefined ) throw new Error( page.error );
 
@@ -494,7 +494,7 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
 
 			/* Make screenshots */
 
-			await screenshot.write( `examples/screenshots/${ file }.jpg` );
+			await screenshot.write( `examples/screenshots/${ file }.jpg`, jpgQuality );
 
 			console.green( `Screenshot generated for file ${ file }` );
 
@@ -506,11 +506,11 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
 
 			try {
 
-				expected = ( await Jimp.read( `examples/screenshots/${ file }.jpg`, { quality: jpgQuality } ) );
+				expected = await Image.read( `examples/screenshots/${ file }.jpg` );
 
 			} catch ( e ) {
 
-				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg` );
+				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg`, jpgQuality );
 				throw new Error( `Screenshot does not exist: ${ file }` );
 
 			}
@@ -529,8 +529,8 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
 
 			} catch ( e ) {
 
-				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg` );
-				await expected.write( `test/e2e/output-screenshots/${ file }-expected.jpg` );
+				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg`, jpgQuality );
+				await expected.write( `test/e2e/output-screenshots/${ file }-expected.jpg`, jpgQuality );
 				throw new Error( `Image sizes does not match in file: ${ file }` );
 
 			}
@@ -545,9 +545,9 @@ async function makeAttempt( page, failedScreenshots, cleanPage, isMakeScreenshot
 
 			} else {
 
-				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg` );
-				await expected.write( `test/e2e/output-screenshots/${ file }-expected.jpg` );
-				await diff.write( `test/e2e/output-screenshots/${ file }-diff.jpg` );
+				await screenshot.write( `test/e2e/output-screenshots/${ file }-actual.jpg`, jpgQuality );
+				await expected.write( `test/e2e/output-screenshots/${ file }-expected.jpg`, jpgQuality );
+				await diff.write( `test/e2e/output-screenshots/${ file }-diff.jpg`, jpgQuality );
 				throw new Error( `Diff wrong in ${ differentPixels.toFixed( 1 ) }% of pixels in file: ${ file }` );
 
 			}

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است

粤ICP备19079148号