@wdio/image-comparison-core 1.3.0 → 2.0.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +65 -3
  2. package/dist/base.interfaces.d.ts +2 -2
  3. package/dist/helpers/constants.js +1 -1
  4. package/dist/helpers/options.d.ts +1 -4
  5. package/dist/helpers/options.d.ts.map +1 -1
  6. package/dist/helpers/options.interfaces.d.ts +4 -2
  7. package/dist/helpers/options.interfaces.d.ts.map +1 -1
  8. package/dist/helpers/options.js +2 -11
  9. package/dist/helpers/options.test.js +16 -1
  10. package/dist/methods/compareReport.interfaces.d.ts +1 -1
  11. package/dist/methods/compareReport.interfaces.d.ts.map +1 -1
  12. package/dist/methods/createCompareReport.test.js +4 -2
  13. package/dist/methods/images.d.ts +2 -1
  14. package/dist/methods/images.d.ts.map +1 -1
  15. package/dist/methods/images.executeImageCompare.test.js +60 -44
  16. package/dist/methods/images.js +31 -31
  17. package/dist/methods/images.test.js +80 -220
  18. package/dist/methods/processDiffPixels.d.ts +1 -1
  19. package/dist/methods/processDiffPixels.d.ts.map +1 -1
  20. package/dist/methods/processDiffPixels.js +11 -2
  21. package/dist/methods/rectangles.d.ts.map +1 -1
  22. package/dist/methods/rectangles.interfaces.d.ts +1 -1
  23. package/dist/methods/rectangles.interfaces.d.ts.map +1 -1
  24. package/dist/methods/rectangles.js +12 -9
  25. package/dist/pixelmatch/compare.interfaces.d.ts +43 -0
  26. package/dist/pixelmatch/compare.interfaces.d.ts.map +1 -0
  27. package/dist/pixelmatch/compareImages.d.ts.map +1 -0
  28. package/dist/pixelmatch/compareImages.js +178 -0
  29. package/dist/pixelmatch/compareImages.test.d.ts +2 -0
  30. package/dist/pixelmatch/compareImages.test.d.ts.map +1 -0
  31. package/dist/pixelmatch/compareImages.test.js +267 -0
  32. package/dist/utils/imageUtils.d.ts +17 -0
  33. package/dist/utils/imageUtils.d.ts.map +1 -0
  34. package/dist/utils/imageUtils.js +189 -0
  35. package/dist/utils/imageUtils.test.d.ts +2 -0
  36. package/dist/utils/imageUtils.test.d.ts.map +1 -0
  37. package/dist/utils/imageUtils.test.js +277 -0
  38. package/package.json +3 -2
  39. package/dist/resemble/compare.interfaces.d.ts +0 -136
  40. package/dist/resemble/compare.interfaces.d.ts.map +0 -1
  41. package/dist/resemble/compareImages.d.ts.map +0 -1
  42. package/dist/resemble/compareImages.js +0 -21
  43. package/dist/resemble/resemble.jimp.cjs +0 -981
  44. package/dist/resemble/resemble.jimp.d.cts +0 -46
  45. package/dist/resemble/resemble.jimp.d.cts.map +0 -1
  46. /package/dist/{resemble → pixelmatch}/compare.interfaces.js +0 -0
  47. /package/dist/{resemble → pixelmatch}/compareImages.d.ts +0 -0
@@ -1,981 +0,0 @@
1
- "use strict";
2
- /*
3
- James Cryer / Huddle
4
- URL: https://github.com/Huddle/Resemble.js
5
- Modified by: @wswebcreation
6
- Reason: The node-canvas library was producing a lot of issues due to system dependency errors.
7
- The Jimp library is a pure JavaScript image processing library that can be used in a Node.js environment.
8
- The browser is not needed anymore, so the API is deliberately broken to only work in a Node.js environment.
9
- The old code is still in the file, but commented out. This way, the code can easily be compared when needed with
10
- the original resemble.js file.
11
- */
12
- const { Jimp, JimpMime } = require('jimp');
13
- const naiveFallback = function () {
14
- // ISC (c) 2011-2019 https://github.com/medikoo/es5-ext/blob/master/global.js
15
- if (typeof self === 'object' && self) {
16
- return self;
17
- }
18
- if (typeof window === 'object' && window) {
19
- return window;
20
- }
21
- throw new Error('Unable to resolve global `this`');
22
- };
23
- const getGlobalThis = function () {
24
- // ISC (c) 2011-2019 https://github.com/medikoo/es5-ext/blob/master/global.js
25
- // Fallback to standard globalThis if available
26
- if (typeof globalThis === 'object' && globalThis) {
27
- return globalThis;
28
- }
29
- try {
30
- Object.defineProperty(Object.prototype, '__global__', {
31
- get: function () {
32
- return this;
33
- },
34
- configurable: true,
35
- });
36
- }
37
- catch (error) {
38
- return naiveFallback();
39
- }
40
- try {
41
- // eslint-disable-next-line no-undef
42
- if (!__global__) {
43
- return naiveFallback();
44
- }
45
- return __global__; // eslint-disable-line no-undef
46
- }
47
- finally {
48
- delete Object.prototype.__global__;
49
- }
50
- };
51
- const isNode = function () {
52
- const globalPolyfill = getGlobalThis();
53
- return (typeof globalPolyfill.process !== 'undefined' &&
54
- globalPolyfill.process.versions &&
55
- globalPolyfill.process.versions.node);
56
- };
57
- (function (root, factory) {
58
- 'use strict';
59
- if (typeof define === 'function' && define.amd) {
60
- define([], factory);
61
- }
62
- else if (typeof module === 'object' && module.exports) {
63
- module.exports = factory();
64
- }
65
- else {
66
- root.resemble = factory();
67
- }
68
- })(this /* eslint-disable-line no-invalid-this*/, function () {
69
- 'use strict';
70
- let Img;
71
- // var Canvas;
72
- // var loadNodeCanvasImage;
73
- // if (isNode()) {
74
- // Canvas = require("canvas"); // eslint-disable-line global-require
75
- // Img = Canvas.Image;
76
- // loadNodeCanvasImage = Canvas.loadImage;
77
- // } else {
78
- // Img = Image;
79
- // }
80
- function createCanvas(width, height) {
81
- // if (isNode()) {
82
- // return Canvas.createCanvas(width, height);
83
- // }
84
- // var cnvs = document.createElement("canvas");
85
- // cnvs.width = width;
86
- // cnvs.height = height;
87
- // return cnvs;
88
- return new Jimp({ width, height });
89
- }
90
- const oldGlobalSettings = {};
91
- let globalOutputSettings = oldGlobalSettings;
92
- const resemble = function (fileData) {
93
- let pixelTransparency = 1;
94
- const errorPixelColor = {
95
- // Color for Error Pixels. Between 0 and 255.
96
- red: 255,
97
- green: 0,
98
- blue: 255,
99
- alpha: 255,
100
- };
101
- const targetPix = { r: 0, g: 0, b: 0, a: 0 }; // isAntialiased
102
- const errorPixelTransform = {
103
- flat: function (px, offset) {
104
- px[offset] = errorPixelColor.red;
105
- px[offset + 1] = errorPixelColor.green;
106
- px[offset + 2] = errorPixelColor.blue;
107
- px[offset + 3] = errorPixelColor.alpha;
108
- },
109
- movement: function (px, offset, d1, d2) {
110
- px[offset] = (d2.r * (errorPixelColor.red / 255) + errorPixelColor.red) / 2;
111
- px[offset + 1] = (d2.g * (errorPixelColor.green / 255) + errorPixelColor.green) / 2;
112
- px[offset + 2] = (d2.b * (errorPixelColor.blue / 255) + errorPixelColor.blue) / 2;
113
- px[offset + 3] = d2.a;
114
- },
115
- flatDifferenceIntensity: function (px, offset, d1, d2) {
116
- px[offset] = errorPixelColor.red;
117
- px[offset + 1] = errorPixelColor.green;
118
- px[offset + 2] = errorPixelColor.blue;
119
- px[offset + 3] = colorsDistance(d1, d2);
120
- },
121
- movementDifferenceIntensity: function (px, offset, d1, d2) {
122
- const ratio = (colorsDistance(d1, d2) / 255) * 0.8;
123
- px[offset] =
124
- (1 - ratio) * (d2.r * (errorPixelColor.red / 255)) +
125
- ratio * errorPixelColor.red;
126
- px[offset + 1] =
127
- (1 - ratio) * (d2.g * (errorPixelColor.green / 255)) +
128
- ratio * errorPixelColor.green;
129
- px[offset + 2] =
130
- (1 - ratio) * (d2.b * (errorPixelColor.blue / 255)) +
131
- ratio * errorPixelColor.blue;
132
- px[offset + 3] = d2.a;
133
- },
134
- diffOnly: function (px, offset, d1, d2) {
135
- px[offset] = d2.r;
136
- px[offset + 1] = d2.g;
137
- px[offset + 2] = d2.b;
138
- px[offset + 3] = d2.a;
139
- },
140
- };
141
- let errorPixel = errorPixelTransform.flat;
142
- let errorType;
143
- let boundingBoxes;
144
- let ignoredBoxes;
145
- let ignoreAreasColoredWith;
146
- let largeImageThreshold = 1200;
147
- let useCrossOrigin = true;
148
- let data = {};
149
- let images = [];
150
- const updateCallbackArray = [];
151
- const tolerance = {
152
- // between 0 and 255
153
- red: 16,
154
- green: 16,
155
- blue: 16,
156
- alpha: 16,
157
- minBrightness: 16,
158
- maxBrightness: 240,
159
- };
160
- let ignoreAntialiasing = false;
161
- let ignoreColors = false;
162
- let scaleToSameSize = false;
163
- let compareOnly = false;
164
- let returnEarlyThreshold;
165
- function colorsDistance(c1, c2) {
166
- return ((Math.abs(c1.r - c2.r) + Math.abs(c1.g - c2.g) + Math.abs(c1.b - c2.b)) / 3);
167
- }
168
- function withinBoundingBox(x, y, width, height, box) {
169
- return (x >= (box.left || 0) &&
170
- x <= (box.right || width) &&
171
- y >= (box.top || 0) &&
172
- y <= (box.bottom || height));
173
- }
174
- function withinComparedArea(x, y, width, height, pixel2) {
175
- let isIncluded = true;
176
- let i;
177
- let boundingBox;
178
- let ignoredBox;
179
- let selected;
180
- let ignored;
181
- if (boundingBoxes instanceof Array) {
182
- selected = false;
183
- for (i = 0; i < boundingBoxes.length; i++) {
184
- boundingBox = boundingBoxes[i];
185
- if (withinBoundingBox(x, y, width, height, boundingBox)) {
186
- selected = true;
187
- break;
188
- }
189
- }
190
- }
191
- if (ignoredBoxes instanceof Array) {
192
- ignored = true;
193
- for (i = 0; i < ignoredBoxes.length; i++) {
194
- ignoredBox = ignoredBoxes[i];
195
- if (withinBoundingBox(x, y, width, height, ignoredBox)) {
196
- ignored = false;
197
- break;
198
- }
199
- }
200
- }
201
- if (ignoreAreasColoredWith) {
202
- return colorsDistance(pixel2, ignoreAreasColoredWith) !== 0;
203
- }
204
- if (selected === undefined && ignored === undefined) {
205
- return true;
206
- }
207
- if (selected === false && ignored === true) {
208
- return false;
209
- }
210
- if (selected === true || ignored === true) {
211
- isIncluded = true;
212
- }
213
- if (selected === false || ignored === false) {
214
- isIncluded = false;
215
- }
216
- return isIncluded;
217
- }
218
- function triggerDataUpdate() {
219
- const len = updateCallbackArray.length;
220
- let i;
221
- for (i = 0; i < len; i++) {
222
- if (typeof updateCallbackArray[i] === 'function') {
223
- updateCallbackArray[i](data);
224
- }
225
- }
226
- }
227
- function loop(w, h, callback) {
228
- let x;
229
- let y;
230
- for (x = 0; x < w; x++) {
231
- for (y = 0; y < h; y++) {
232
- callback(x, y);
233
- }
234
- }
235
- }
236
- function parseImage(sourceImageData, width, height) {
237
- let pixelCount = 0;
238
- let redTotal = 0;
239
- let greenTotal = 0;
240
- let blueTotal = 0;
241
- let alphaTotal = 0;
242
- let brightnessTotal = 0;
243
- let whiteTotal = 0;
244
- let blackTotal = 0;
245
- loop(width, height, function (horizontalPos, verticalPos) {
246
- const offset = (verticalPos * width + horizontalPos) * 4;
247
- const red = sourceImageData[offset];
248
- const green = sourceImageData[offset + 1];
249
- const blue = sourceImageData[offset + 2];
250
- const alpha = sourceImageData[offset + 3];
251
- const brightness = getBrightness(red, green, blue);
252
- if (red === green && red === blue && alpha) {
253
- if (red === 0) {
254
- blackTotal++;
255
- }
256
- else if (red === 255) {
257
- whiteTotal++;
258
- }
259
- }
260
- pixelCount++;
261
- redTotal += (red / 255) * 100;
262
- greenTotal += (green / 255) * 100;
263
- blueTotal += (blue / 255) * 100;
264
- alphaTotal += ((255 - alpha) / 255) * 100;
265
- brightnessTotal += (brightness / 255) * 100;
266
- });
267
- data.red = Math.floor(redTotal / pixelCount);
268
- data.green = Math.floor(greenTotal / pixelCount);
269
- data.blue = Math.floor(blueTotal / pixelCount);
270
- data.alpha = Math.floor(alphaTotal / pixelCount);
271
- data.brightness = Math.floor(brightnessTotal / pixelCount);
272
- data.white = Math.floor((whiteTotal / pixelCount) * 100);
273
- data.black = Math.floor((blackTotal / pixelCount) * 100);
274
- triggerDataUpdate();
275
- }
276
- function onLoadImage(hiddenImage, callback) {
277
- // don't assign to hiddenImage, see https://github.com/Huddle/Resemble.js/pull/87/commits/300d43352a2845aad289b254bfbdc7cd6a37e2d7
278
- let width = hiddenImage.bitmap.width;
279
- let height = hiddenImage.bitmap.height;
280
- if (scaleToSameSize && images.length === 1) {
281
- width = images[0].bitmap.width;
282
- height = images[0].bitmap.height;
283
- }
284
- // var hiddenCanvas = createCanvas(width, height);
285
- // var imageData;
286
- // hiddenCanvas.getContext("2d").drawImage(hiddenImage, 0, 0, width, height);
287
- // imageData = hiddenCanvas
288
- // .getContext("2d")
289
- // .getImageData(0, 0, width, height);
290
- // images.push(imageData);
291
- images.push(hiddenImage);
292
- callback(hiddenImage, width, height);
293
- }
294
- function loadImageData(fileDataForImage, callback) {
295
- // var fileReader;
296
- // var hiddenImage = new Img();
297
- // if (!hiddenImage.setAttribute) {
298
- // hiddenImage.setAttribute = function setAttribute() {};
299
- // }
300
- // if (useCrossOrigin) {
301
- // hiddenImage.setAttribute("crossorigin", "anonymous");
302
- // }
303
- // hiddenImage.onerror = function (event) {
304
- // hiddenImage.onload = null;
305
- // hiddenImage.onerror = null; // fixes pollution between calls
306
- // const error = event ? event + "" : "Unknown error";
307
- // images.push({
308
- // error: `Failed to load image '${fileDataForImage}'. ${error}`,
309
- // });
310
- // callback();
311
- // };
312
- // hiddenImage.onload = function () {
313
- // hiddenImage.onload = null; // fixes pollution between calls
314
- // hiddenImage.onerror = null;
315
- // onLoadImage(hiddenImage, callback);
316
- // };
317
- // if (typeof fileDataForImage === "string") {
318
- // hiddenImage.src = fileDataForImage;
319
- // if (!isNode() && hiddenImage.complete && hiddenImage.naturalWidth > 0) {
320
- // hiddenImage.onload();
321
- // }
322
- // } else if (
323
- // typeof fileDataForImage.data !== "undefined" &&
324
- // typeof fileDataForImage.width === "number" &&
325
- // typeof fileDataForImage.height === "number"
326
- // ) {
327
- // images.push(fileDataForImage);
328
- // callback(
329
- // fileDataForImage,
330
- // fileDataForImage.width,
331
- // fileDataForImage.height
332
- // );
333
- // } else if (
334
- // typeof Buffer !== "undefined" &&
335
- // fileDataForImage instanceof Buffer
336
- // ) {
337
- // // If we have Buffer, assume we're on Node+Canvas and its supported
338
- // // hiddenImage.src = fileDataForImage;
339
- // loadNodeCanvasImage(fileDataForImage)
340
- // .then(function (image) {
341
- // hiddenImage.onload = null; // fixes pollution between calls
342
- // hiddenImage.onerror = null;
343
- // onLoadImage(image, callback);
344
- // })
345
- // .catch(function (err) {
346
- // images.push({
347
- // error: err ? err + "" : "Image load error.",
348
- // });
349
- // callback();
350
- // });
351
- // } else {
352
- // fileReader = new FileReader();
353
- // fileReader.onload = function (event) {
354
- // hiddenImage.src = event.target.result;
355
- // };
356
- // fileReader.readAsDataURL(fileDataForImage);
357
- // }
358
- Jimp.read(fileDataForImage)
359
- .then((image) => {
360
- onLoadImage(image, callback);
361
- })
362
- .catch((err) => {
363
- images.push({
364
- error: `Failed to load image '${fileDataForImage}'. ${err}`,
365
- });
366
- callback();
367
- });
368
- }
369
- function isColorSimilar(a, b, color) {
370
- const absDiff = Math.abs(a - b);
371
- if (typeof a === 'undefined') {
372
- return false;
373
- }
374
- if (typeof b === 'undefined') {
375
- return false;
376
- }
377
- if (a === b) {
378
- return true;
379
- }
380
- else if (absDiff < tolerance[color]) {
381
- return true;
382
- }
383
- return false;
384
- }
385
- function isPixelBrightnessSimilar(d1, d2) {
386
- const alpha = isColorSimilar(d1.a, d2.a, 'alpha');
387
- const brightness = isColorSimilar(d1.brightness, d2.brightness, 'minBrightness');
388
- return brightness && alpha;
389
- }
390
- function getBrightness(r, g, b) {
391
- return 0.3 * r + 0.59 * g + 0.11 * b;
392
- }
393
- function isRGBSame(d1, d2) {
394
- const red = d1.r === d2.r;
395
- const green = d1.g === d2.g;
396
- const blue = d1.b === d2.b;
397
- return red && green && blue;
398
- }
399
- function isRGBSimilar(d1, d2) {
400
- const red = isColorSimilar(d1.r, d2.r, 'red');
401
- const green = isColorSimilar(d1.g, d2.g, 'green');
402
- const blue = isColorSimilar(d1.b, d2.b, 'blue');
403
- const alpha = isColorSimilar(d1.a, d2.a, 'alpha');
404
- return red && green && blue && alpha;
405
- }
406
- function isContrasting(d1, d2) {
407
- return Math.abs(d1.brightness - d2.brightness) > tolerance.maxBrightness;
408
- }
409
- function getHue(red, green, blue) {
410
- const r = red / 255;
411
- const g = green / 255;
412
- const b = blue / 255;
413
- const max = Math.max(r, g, b);
414
- const min = Math.min(r, g, b);
415
- let h;
416
- let d;
417
- if (max === min) {
418
- h = 0; // achromatic
419
- }
420
- else {
421
- d = max - min;
422
- switch (max) {
423
- case r:
424
- h = (g - b) / d + (g < b ? 6 : 0);
425
- break;
426
- case g:
427
- h = (b - r) / d + 2;
428
- break;
429
- case b:
430
- h = (r - g) / d + 4;
431
- break;
432
- default:
433
- h /= 6;
434
- }
435
- }
436
- return h;
437
- }
438
- function isAntialiased(sourcePix, pix, cacheSet, verticalPos, horizontalPos, width) {
439
- let offset;
440
- const distance = 1;
441
- let i;
442
- let j;
443
- let hasHighContrastSibling = 0;
444
- let hasSiblingWithDifferentHue = 0;
445
- let hasEquivalentSibling = 0;
446
- addHueInfo(sourcePix);
447
- for (i = distance * -1; i <= distance; i++) {
448
- for (j = distance * -1; j <= distance; j++) {
449
- if (i === 0 && j === 0) {
450
- // ignore source pixel
451
- continue;
452
- }
453
- else {
454
- offset = ((verticalPos + j) * width + (horizontalPos + i)) * 4;
455
- if (!getPixelInfo(targetPix, pix, offset, cacheSet)) {
456
- continue;
457
- }
458
- addBrightnessInfo(targetPix);
459
- addHueInfo(targetPix);
460
- if (isContrasting(sourcePix, targetPix)) {
461
- hasHighContrastSibling++;
462
- }
463
- if (isRGBSame(sourcePix, targetPix)) {
464
- hasEquivalentSibling++;
465
- }
466
- if (Math.abs(targetPix.h - sourcePix.h) > 0.3) {
467
- hasSiblingWithDifferentHue++;
468
- }
469
- if (hasSiblingWithDifferentHue > 1 || hasHighContrastSibling > 1) {
470
- return true;
471
- }
472
- }
473
- }
474
- }
475
- if (hasEquivalentSibling < 2) {
476
- return true;
477
- }
478
- return false;
479
- }
480
- function copyPixel(px, offset, pix) {
481
- if (errorType === 'diffOnly') {
482
- return;
483
- }
484
- px[offset] = pix.r; // r
485
- px[offset + 1] = pix.g; // g
486
- px[offset + 2] = pix.b; // b
487
- px[offset + 3] = pix.a * pixelTransparency; // a
488
- }
489
- function copyGrayScalePixel(px, offset, pix) {
490
- if (errorType === 'diffOnly') {
491
- return;
492
- }
493
- px[offset] = pix.brightness; // r
494
- px[offset + 1] = pix.brightness; // g
495
- px[offset + 2] = pix.brightness; // b
496
- px[offset + 3] = pix.a * pixelTransparency; // a
497
- }
498
- function getPixelInfo(dst, pix, offset) {
499
- if (pix.length > offset) {
500
- dst.r = pix[offset];
501
- dst.g = pix[offset + 1];
502
- dst.b = pix[offset + 2];
503
- dst.a = pix[offset + 3];
504
- return true;
505
- }
506
- return false;
507
- }
508
- function addBrightnessInfo(pix) {
509
- pix.brightness = getBrightness(pix.r, pix.g, pix.b); // 'corrected' lightness
510
- }
511
- function addHueInfo(pix) {
512
- pix.h = getHue(pix.r, pix.g, pix.b);
513
- }
514
- function analyseImages(img1, img2, width, height) {
515
- const data1 = img1.bitmap.data;
516
- const data2 = img2.bitmap.data;
517
- let hiddenCanvas;
518
- // var context;
519
- // var imgd;
520
- let pix;
521
- if (!compareOnly) {
522
- hiddenCanvas = createCanvas(width, height);
523
- // context = hiddenCanvas.getContext("2d");
524
- // imgd = context.createImageData(width, height);
525
- pix = hiddenCanvas.bitmap.data;
526
- }
527
- let mismatchCount = 0;
528
- const diffBounds = {
529
- top: height,
530
- left: width,
531
- bottom: 0,
532
- right: 0,
533
- };
534
- const diffPixels = [];
535
- const updateBounds = function (x, y) {
536
- // Update the big box
537
- diffBounds.left = Math.min(x, diffBounds.left);
538
- diffBounds.right = Math.max(x, diffBounds.right);
539
- diffBounds.top = Math.min(y, diffBounds.top);
540
- diffBounds.bottom = Math.max(y, diffBounds.bottom);
541
- // Update the diffPixels array
542
- diffPixels.push({ x, y });
543
- };
544
- const time = Date.now();
545
- let skip;
546
- if (!!largeImageThreshold &&
547
- ignoreAntialiasing &&
548
- (width > largeImageThreshold || height > largeImageThreshold)) {
549
- skip = 6;
550
- }
551
- const pixel1 = { r: 0, g: 0, b: 0, a: 0 };
552
- const pixel2 = { r: 0, g: 0, b: 0, a: 0 };
553
- let skipTheRest = false;
554
- loop(width, height, function (horizontalPos, verticalPos) {
555
- if (skipTheRest) {
556
- return;
557
- }
558
- if (skip) {
559
- // only skip if the image isn't small
560
- if (verticalPos % skip === 0 || horizontalPos % skip === 0) {
561
- return;
562
- }
563
- }
564
- const offset = (verticalPos * width + horizontalPos) * 4;
565
- if (!getPixelInfo(pixel1, data1, offset, 1) ||
566
- !getPixelInfo(pixel2, data2, offset, 2)) {
567
- return;
568
- }
569
- const isWithinComparedArea = withinComparedArea(horizontalPos, verticalPos, width, height, pixel2);
570
- if (ignoreColors) {
571
- addBrightnessInfo(pixel1);
572
- addBrightnessInfo(pixel2);
573
- if (isPixelBrightnessSimilar(pixel1, pixel2) || !isWithinComparedArea) {
574
- if (!compareOnly) {
575
- copyGrayScalePixel(pix, offset, pixel2);
576
- }
577
- }
578
- else {
579
- if (!compareOnly) {
580
- errorPixel(pix, offset, pixel1, pixel2);
581
- }
582
- mismatchCount++;
583
- updateBounds(horizontalPos, verticalPos);
584
- }
585
- return;
586
- }
587
- if (isRGBSimilar(pixel1, pixel2) || !isWithinComparedArea) {
588
- if (!compareOnly) {
589
- copyPixel(pix, offset, pixel1);
590
- }
591
- }
592
- else if (ignoreAntialiasing &&
593
- (addBrightnessInfo(pixel1), // jit pixel info augmentation looks a little weird, sorry.
594
- addBrightnessInfo(pixel2),
595
- isAntialiased(pixel1, data1, 1, verticalPos, horizontalPos, width) ||
596
- isAntialiased(pixel2, data2, 2, verticalPos, horizontalPos, width))) {
597
- if (isPixelBrightnessSimilar(pixel1, pixel2) || !isWithinComparedArea) {
598
- if (!compareOnly) {
599
- copyGrayScalePixel(pix, offset, pixel2);
600
- }
601
- }
602
- else {
603
- if (!compareOnly) {
604
- errorPixel(pix, offset, pixel1, pixel2);
605
- }
606
- mismatchCount++;
607
- updateBounds(horizontalPos, verticalPos);
608
- }
609
- }
610
- else {
611
- if (!compareOnly) {
612
- errorPixel(pix, offset, pixel1, pixel2);
613
- }
614
- mismatchCount++;
615
- updateBounds(horizontalPos, verticalPos);
616
- }
617
- if (compareOnly) {
618
- const currentMisMatchPercent = (mismatchCount / (height * width)) * 100;
619
- if (currentMisMatchPercent > returnEarlyThreshold) {
620
- skipTheRest = true;
621
- }
622
- }
623
- });
624
- data.rawMisMatchPercentage = (mismatchCount / (height * width)) * 100;
625
- data.misMatchPercentage = data.rawMisMatchPercentage.toFixed(2);
626
- data.diffBounds = diffBounds;
627
- data.analysisTime = Date.now() - time;
628
- // Add diffPixels array to the data object
629
- data.diffPixels = diffPixels;
630
- data.getImageDataUrl = function (text) {
631
- if (compareOnly) {
632
- throw Error('No diff image available - ran in compareOnly mode');
633
- }
634
- let barHeight = 0;
635
- if (text) {
636
- barHeight = addLabel(text, hiddenCanvas);
637
- }
638
- // context.putImageData(imgd, 0, barHeight);
639
- return hiddenCanvas.getBase64(JimpMime.png);
640
- };
641
- if (!compareOnly) {
642
- data.getBuffer = function (includeOriginal) {
643
- if (includeOriginal) {
644
- const imageWidth = hiddenCanvas.bitmap.width + 2;
645
- hiddenCanvas.resize(imageWidth * 3, hiddenCanvas.bitmap.height);
646
- hiddenCanvas.composite(img1, 0, 0);
647
- hiddenCanvas.composite(img2, imageWidth, 0);
648
- hiddenCanvas.composite(hiddenCanvas, imageWidth * 2, 0);
649
- }
650
- return hiddenCanvas.getBuffer(JimpMime.png);
651
- };
652
- }
653
- }
654
- function addLabel(text, hiddenCanvas) {
655
- const textPadding = 2;
656
- // context.font = "12px sans-serif";
657
- // var textWidth = context.measureText(text).width + textPadding * 2;
658
- // var barHeight = 22;
659
- // if (textWidth > hiddenCanvas.width) {
660
- // hiddenCanvas.width = textWidth;
661
- // }
662
- // hiddenCanvas.height += barHeight;
663
- // context.fillStyle = "#666";
664
- // context.fillRect(0, 0, hiddenCanvas.width, barHeight - 4);
665
- // context.fillStyle = "#fff";
666
- // context.fillRect(0, barHeight - 4, hiddenCanvas.width, 4);
667
- // context.fillStyle = "#fff";
668
- // context.textBaseline = "top";
669
- // context.font = "12px sans-serif";
670
- // context.fillText(text, textPadding, 1);
671
- // return barHeight;
672
- return Jimp.loadFont(Jimp.FONT_SANS_12_WHITE).then((font) => {
673
- const textWidth = Jimp.measureText(font, text) + textPadding * 2;
674
- const barHeight = 22;
675
- if (textWidth > hiddenCanvas.bitmap.width) {
676
- hiddenCanvas.resize(textWidth, hiddenCanvas.bitmap.height);
677
- }
678
- const context = hiddenCanvas.clone();
679
- context.print(font, textPadding, 1, text);
680
- return barHeight;
681
- });
682
- }
683
- function normalise(img, w, h) {
684
- // var c;
685
- // var context;
686
- if (img.bitmap.height < h || img.bitmap.width < w) {
687
- // c = createCanvas(w, h);
688
- // context = c.getContext("2d");
689
- // context.putImageData(img, 0, 0);
690
- return img.contain({ w, h });
691
- }
692
- return img;
693
- }
694
- function outputSettings(options) {
695
- let key;
696
- if (options.errorColor) {
697
- for (key in options.errorColor) {
698
- if (options.errorColor.hasOwnProperty(key)) {
699
- errorPixelColor[key] = options.errorColor[key] === void 0
700
- ? errorPixelColor[key]
701
- : options.errorColor[key];
702
- }
703
- }
704
- }
705
- if (options.errorType && errorPixelTransform[options.errorType]) {
706
- errorPixel = errorPixelTransform[options.errorType];
707
- errorType = options.errorType;
708
- }
709
- if (options.errorPixel && typeof options.errorPixel === 'function') {
710
- errorPixel = options.errorPixel;
711
- }
712
- pixelTransparency = isNaN(Number(options.transparency))
713
- ? pixelTransparency
714
- : options.transparency;
715
- if (options.largeImageThreshold !== undefined) {
716
- largeImageThreshold = options.largeImageThreshold;
717
- }
718
- if (options.useCrossOrigin !== undefined) {
719
- useCrossOrigin = options.useCrossOrigin;
720
- }
721
- if (options.boundingBox !== undefined) {
722
- boundingBoxes = [options.boundingBox];
723
- }
724
- if (options.ignoredBox !== undefined) {
725
- ignoredBoxes = [options.ignoredBox];
726
- }
727
- if (options.boundingBoxes !== undefined) {
728
- boundingBoxes = options.boundingBoxes;
729
- }
730
- if (options.ignoredBoxes !== undefined) {
731
- ignoredBoxes = options.ignoredBoxes;
732
- }
733
- if (options.ignoreAreasColoredWith !== undefined) {
734
- ignoreAreasColoredWith = options.ignoreAreasColoredWith;
735
- }
736
- }
737
- function compare(one, two) {
738
- if (globalOutputSettings !== oldGlobalSettings) {
739
- outputSettings(globalOutputSettings);
740
- }
741
- function onceWeHaveBoth() {
742
- let width;
743
- let height;
744
- if (images.length === 2) {
745
- if (images[0].error || images[1].error) {
746
- data = {};
747
- data.error = images[0].error ? images[0].error : images[1].error;
748
- triggerDataUpdate();
749
- return;
750
- }
751
- width = images[0].bitmap.width > images[1].bitmap.width
752
- ? images[0].bitmap.width
753
- : images[1].bitmap.width;
754
- height = images[0].bitmap.height > images[1].bitmap.height
755
- ? images[0].bitmap.height
756
- : images[1].bitmap.height;
757
- data.isSameDimensions = images[0].bitmap.width === images[1].bitmap.width &&
758
- images[0].bitmap.height === images[1].bitmap.height ? true : false;
759
- data.dimensionDifference = {
760
- width: images[0].bitmap.width - images[1].bitmap.width,
761
- height: images[0].bitmap.height - images[1].bitmap.height,
762
- };
763
- analyseImages(normalise(images[0], width, height), normalise(images[1], width, height), width, height);
764
- triggerDataUpdate();
765
- }
766
- }
767
- images = [];
768
- loadImageData(one, onceWeHaveBoth);
769
- loadImageData(two, onceWeHaveBoth);
770
- }
771
- function getCompareApi(param) {
772
- let secondFileData;
773
- const hasMethod = typeof param === 'function';
774
- if (!hasMethod) {
775
- // assume it's file data
776
- secondFileData = param;
777
- }
778
- var self = {
779
- setReturnEarlyThreshold: function (threshold) {
780
- if (threshold) {
781
- compareOnly = true;
782
- returnEarlyThreshold = threshold;
783
- }
784
- return self;
785
- },
786
- scaleToSameSize: function () {
787
- scaleToSameSize = true;
788
- if (hasMethod) {
789
- param();
790
- }
791
- return self;
792
- },
793
- useOriginalSize: function () {
794
- scaleToSameSize = false;
795
- if (hasMethod) {
796
- param();
797
- }
798
- return self;
799
- },
800
- ignoreNothing: function () {
801
- tolerance.red = 0;
802
- tolerance.green = 0;
803
- tolerance.blue = 0;
804
- tolerance.alpha = 0;
805
- tolerance.minBrightness = 0;
806
- tolerance.maxBrightness = 255;
807
- ignoreAntialiasing = false;
808
- ignoreColors = false;
809
- if (hasMethod) {
810
- param();
811
- }
812
- return self;
813
- },
814
- ignoreLess: function () {
815
- tolerance.red = 16;
816
- tolerance.green = 16;
817
- tolerance.blue = 16;
818
- tolerance.alpha = 16;
819
- tolerance.minBrightness = 16;
820
- tolerance.maxBrightness = 240;
821
- ignoreAntialiasing = false;
822
- ignoreColors = false;
823
- if (hasMethod) {
824
- param();
825
- }
826
- return self;
827
- },
828
- ignoreAntialiasing: function () {
829
- tolerance.red = 32;
830
- tolerance.green = 32;
831
- tolerance.blue = 32;
832
- tolerance.alpha = 32;
833
- tolerance.minBrightness = 64;
834
- tolerance.maxBrightness = 96;
835
- ignoreAntialiasing = true;
836
- ignoreColors = false;
837
- if (hasMethod) {
838
- param();
839
- }
840
- return self;
841
- },
842
- ignoreColors: function () {
843
- tolerance.alpha = 16;
844
- tolerance.minBrightness = 16;
845
- tolerance.maxBrightness = 240;
846
- ignoreAntialiasing = false;
847
- ignoreColors = true;
848
- if (hasMethod) {
849
- param();
850
- }
851
- return self;
852
- },
853
- ignoreAlpha: function () {
854
- tolerance.red = 16;
855
- tolerance.green = 16;
856
- tolerance.blue = 16;
857
- tolerance.alpha = 255;
858
- tolerance.minBrightness = 16;
859
- tolerance.maxBrightness = 240;
860
- ignoreAntialiasing = false;
861
- ignoreColors = false;
862
- if (hasMethod) {
863
- param();
864
- }
865
- return self;
866
- },
867
- repaint: function () {
868
- if (hasMethod) {
869
- param();
870
- }
871
- return self;
872
- },
873
- outputSettings: function (options) {
874
- outputSettings(options);
875
- return self;
876
- },
877
- onComplete: function (callback) {
878
- updateCallbackArray.push(callback);
879
- const wrapper = function () {
880
- compare(fileData, secondFileData);
881
- };
882
- wrapper();
883
- return getCompareApi(wrapper);
884
- },
885
- setupCustomTolerance: function (customSettings) {
886
- for (const property in tolerance) {
887
- if (!customSettings.hasOwnProperty(property)) {
888
- continue;
889
- }
890
- tolerance[property] = customSettings[property];
891
- }
892
- },
893
- };
894
- return self;
895
- }
896
- var rootSelf = {
897
- onComplete: function (callback) {
898
- updateCallbackArray.push(callback);
899
- loadImageData(fileData, function (imageData, width, height) {
900
- parseImage(imageData, width, height);
901
- });
902
- },
903
- compareTo: function (secondFileData) {
904
- return getCompareApi(secondFileData);
905
- },
906
- outputSettings: function (options) {
907
- outputSettings(options);
908
- return rootSelf;
909
- },
910
- };
911
- return rootSelf;
912
- };
913
- function setGlobalOutputSettings(settings) {
914
- globalOutputSettings = settings;
915
- return resemble;
916
- }
917
- function applyIgnore(api, ignore, customTolerance) {
918
- switch (ignore) {
919
- case 'nothing':
920
- api.ignoreNothing();
921
- break;
922
- case 'less':
923
- api.ignoreLess();
924
- break;
925
- case 'antialiasing':
926
- api.ignoreAntialiasing();
927
- break;
928
- case 'colors':
929
- api.ignoreColors();
930
- break;
931
- case 'alpha':
932
- api.ignoreAlpha();
933
- break;
934
- default:
935
- throw new Error('Invalid ignore: ' + ignore);
936
- }
937
- api.setupCustomTolerance(customTolerance);
938
- }
939
- resemble.compare = function (image1, image2, options) {
940
- return new Promise((resolve, reject) => {
941
- let opt;
942
- if (typeof options !== 'object') {
943
- opt = {};
944
- }
945
- else {
946
- opt = options;
947
- }
948
- const res = resemble(image1);
949
- let compare;
950
- if (opt.output) {
951
- res.outputSettings(opt.output);
952
- }
953
- compare = res.compareTo(image2);
954
- if (opt.returnEarlyThreshold) {
955
- compare.setReturnEarlyThreshold(opt.returnEarlyThreshold);
956
- }
957
- if (opt.scaleToSameSize) {
958
- compare.scaleToSameSize();
959
- }
960
- const toleranceSettings = opt.tolerance || {};
961
- if (typeof opt.ignore === 'string') {
962
- applyIgnore(compare, opt.ignore, toleranceSettings);
963
- }
964
- else if (opt.ignore && opt.ignore.forEach) {
965
- opt.ignore.forEach(function (v) {
966
- applyIgnore(compare, v, toleranceSettings);
967
- });
968
- }
969
- compare.onComplete(function (data) {
970
- if (data.error) {
971
- reject(data.error);
972
- }
973
- else {
974
- resolve(data);
975
- }
976
- });
977
- });
978
- };
979
- resemble.outputSettings = setGlobalOutputSettings;
980
- return resemble;
981
- });