Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

looks-same

Package Overview
Dependencies
Maintainers
9
Versions
42
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

looks-same - npm Package Compare versions

Comparing version 8.1.0 to 8.2.0

7

CHANGELOG.md

@@ -5,2 +5,9 @@ # Changelog

## [8.2.0](https://github.com/gemini-testing/looks-same/compare/v8.1.0...v8.2.0) (2023-08-02)
### Features
* calc equality and build diff simultaneously ([abbe9ed](https://github.com/gemini-testing/looks-same/commit/abbe9ed29f18d656317097053e74afe11bacda44))
## [8.1.0](https://github.com/gemini-testing/looks-same/compare/v8.0.0...v8.1.0) (2022-11-21)

@@ -7,0 +14,0 @@

42

index.js
'use strict';
const _ = require('lodash');
const parseColor = require('parse-color');
const colorDiff = require('color-diff');

@@ -25,2 +24,5 @@ const img = require('./lib/image');

function makeCIEDE2000Comparator(tolerance) {
const upperBound = tolerance * 6.2; // cie76 <= 6.2 * ciede2000
const lowerBound = tolerance * 0.695; // cie76 >= 0.695 * ciede2000
return function doColorsLookSame(data) {

@@ -34,2 +36,16 @@ if (areColorsSame(data)) {

const cie76 = Math.sqrt(
(lab1.L - lab2.L) * (lab1.L - lab2.L) +
(lab1.a - lab2.a) * (lab1.a - lab2.a) +
(lab1.b - lab2.b) * (lab1.b - lab2.b)
);
if (cie76 >= upperBound) {
return false;
}
if (cie76 <= lowerBound) {
return true;
}
return colorDiff.diff(lab1, lab2) < tolerance;

@@ -100,3 +116,3 @@ };

if (!options.comparator({color1, color2, img1, img2, x, y, width, height})) {
if (!options.comparator({color1, color2, img1, img2, x, y, width, height, minWidth, minHeight})) {
setPixel(resultBuffer, x, y, highlightColor);

@@ -111,12 +127,2 @@ } else {

const parseColorString = (str) => {
const parsed = parseColor(str || '#ff00ff');
return {
R: parsed.rgb[0],
G: parsed.rgb[1],
B: parsed.rgb[2]
};
};
const getToleranceFromOpts = (opts) => {

@@ -169,6 +175,6 @@ if (!_.hasIn(opts, 'tolerance')) {

return {equal: true, metaInfo, diffBounds, diffClusters: [diffBounds]};
return {equal: true, metaInfo, diffBounds, diffClusters: [diffBounds], diffImage: null};
}
if (first.width !== second.width || first.height !== second.height) {
if (!opts.createDiffImage && (first.width !== second.width || first.height !== second.height)) {
const diffBounds = getMaxDiffBounds(first, second);

@@ -186,4 +192,8 @@

const comparator = createComparator(img1, img2, opts);
const {stopOnFirstFail, shouldCluster, clustersSize} = opts;
const {stopOnFirstFail, shouldCluster, clustersSize, createDiffImage, highlightColor} = opts;
if (createDiffImage) {
return utils.calcDiffImage(img1, img2, comparator, {highlightColor, shouldCluster, clustersSize});
}
const {diffArea, diffClusters} = await utils.getDiffPixelsCoords(img1, img2, comparator, {stopOnFirstFail, shouldCluster, clustersSize});

@@ -224,3 +234,3 @@ const diffBounds = diffArea.area;

const diffImage = await buildDiffImage(first, second, {
highlightColor: parseColorString(opts.highlightColor),
highlightColor: utils.parseColorString(opts.highlightColor),
comparator: createComparator(first, second, opts)

@@ -227,0 +237,0 @@ });

@@ -7,3 +7,4 @@ 'use strict';

REQUIRED_BOUNDING_BOX_FIELDS: ['left', 'top', 'right', 'bottom'],
CLUSTERS_SIZE: 10
CLUSTERS_SIZE: 10,
DIFF_IMAGE_CHANNELS: 3
};
'use strict';
const _ = require('lodash');
const STATES = {

@@ -34,3 +32,3 @@ InitState: require('./states/init'),

_checkIsCaret(data) {
return this._state.validate(_.pick(data, ['x', 'y']), _.pick(data, ['img1', 'img2']));
return this._state.validate(data);
}

@@ -37,0 +35,0 @@

'use strict';
const _ = require('lodash');
const State = require('./state');

@@ -8,10 +7,10 @@ const areColorsSame = require('../../same-colors');

module.exports = class InitState extends State {
validate(firstCaretPoint, imgs) {
const lastCaretPoint = this._getLastCaretPoint(firstCaretPoint, imgs);
validate(data) {
const lastCaretPoint = this._getLastCaretPoint(data);
if (!this._looksLikeCaret(firstCaretPoint, lastCaretPoint)) {
if (!this._looksLikeCaret(data, lastCaretPoint)) {
return false;
}
this.caretTopLeft = firstCaretPoint;
this.caretTopLeft = data;
this.caretBottomRight = lastCaretPoint;

@@ -24,10 +23,10 @@

_getLastCaretPoint(firstCaretPoint, imgs) {
let currPoint = firstCaretPoint;
_getLastCaretPoint(data) {
let currPoint = data;
/* eslint-disable-next-line no-constant-condition */
while (true) {
const nextPoint = this._getNextCaretPoint(firstCaretPoint, currPoint);
const nextPoint = this._getNextCaretPoint(data, currPoint);
if (this._isPointOutsideImages(nextPoint, imgs) || this._areColorsSame(nextPoint, imgs)) {
if (this._isPointOutsideImages(nextPoint, data) || this._areColorsSame(nextPoint, data)) {
return currPoint;

@@ -39,9 +38,9 @@ }

_isPointOutsideImages(point, imgs) {
return _.some(imgs, (img) => point.x >= img.width || point.y >= img.height);
_isPointOutsideImages(point, data) {
return point.x >= data.minWidth || point.y >= data.minHeight;
}
_areColorsSame(point, imgs) {
const color1 = imgs.img1.getPixel(point.x, point.y);
const color2 = imgs.img2.getPixel(point.x, point.y);
_areColorsSame(point, data) {
const color1 = data.img1.getPixel(point.x, point.y);
const color2 = data.img2.getPixel(point.x, point.y);

@@ -48,0 +47,0 @@ return areColorsSame({color1, color2});

@@ -21,2 +21,10 @@ 'use strict';

async initMeta() {
const {width, height, channels} = await this._img.metadata();
this._width = width;
this._height = height;
this._channels = channels;
}
getPixel(x, y) {

@@ -23,0 +31,0 @@ const idx = this._getIdx(x, y);

'use strict';
const _ = require('lodash');
const parseColor = require('parse-color');
const img = require('./image');

@@ -9,2 +10,4 @@ const buffer = require('./img-buffer');

const validators = require('./validators');
const areColorsSame = require('./same-colors');
const {DIFF_IMAGE_CHANNELS} = require('./constants');

@@ -100,1 +103,98 @@ exports.readImgCb = async ({source, ...opts}) => {

};
exports.parseColorString = (str) => {
const parsed = parseColor(str || '#ff00ff');
return {
R: parsed.rgb[0],
G: parsed.rgb[1],
B: parsed.rgb[2]
};
};
exports.calcDiffImage = async (img1, img2, comparator, {highlightColor, shouldCluster, clustersSize}) => {
const diffColor = exports.parseColorString(highlightColor);
const minHeight = Math.min(img1.height, img2.height);
const minWidth = Math.min(img1.width, img2.width);
const maxHeight = Math.max(img1.height, img2.height);
const maxWidth = Math.max(img1.width, img2.width);
const totalPixels = maxHeight * maxWidth;
const metaInfo = {refImg: {size: {width: img1.width, height: img1.height}}};
const diffBuffer = Buffer.alloc(maxHeight * maxWidth * DIFF_IMAGE_CHANNELS);
const diffArea = new DiffArea();
const diffClusters = new DiffClusters(clustersSize);
let differentPixels = 0;
let diffBufferPos = 0;
const markDiff = (x, y) => {
diffBuffer[diffBufferPos++] = diffColor.R;
diffBuffer[diffBufferPos++] = diffColor.G;
diffBuffer[diffBufferPos++] = diffColor.B;
differentPixels++;
diffArea.update(x, y);
if (shouldCluster) {
diffClusters.update(x, y);
}
};
for (let y = 0; y < maxHeight; y++) {
for (let x = 0; x < maxWidth; x++) {
if (y > minHeight || x > minWidth) {
markDiff(x, y); // Out of bounds pixels considered as diff
continue;
}
const color1 = img1.getPixel(x, y);
const color2 = img2.getPixel(x, y);
const areSame = areColorsSame({color1, color2}) || comparator({
img1,
img2,
x,
y,
color1,
color2,
width: maxWidth,
height: maxHeight,
minWidth,
minHeight
});
if (areSame) {
diffBuffer[diffBufferPos++] = color2.R;
diffBuffer[diffBufferPos++] = color2.G;
diffBuffer[diffBufferPos++] = color2.B;
} else {
markDiff(x, y);
}
}
// eslint-disable-next-line no-bitwise
if (!(y & 0xff)) { // Release event queue every 256 rows
await new Promise(setImmediate);
}
}
let diffImage = null;
if (differentPixels) {
diffImage = await img.fromBuffer(diffBuffer, {raw: {width: maxWidth, height: maxHeight, channels: DIFF_IMAGE_CHANNELS}});
await diffImage.initMeta();
}
return {
equal: !differentPixels,
metaInfo,
diffImage,
differentPixels,
totalPixels,
diffBounds: diffArea.area,
diffClusters: diffClusters.clusters
};
};
{
"name": "looks-same",
"version": "8.1.0",
"version": "8.2.0",
"description": "Pure node.js library for comparing PNG-images, taking into account human color perception.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -130,2 +130,22 @@ # LooksSame

## Comparing images and creating diff image simultaneously
If you need both co compare images and create diff image, you can pass option `createDiffImage: true`,
it would work faster than two separate function calls:
```javascript
const {
equal,
diffImage,
differentPixels,
totalPixels,
diffBounds,
diffClusters
} = await looksSame('image1.png', 'image2.png', {createDiffImage: true});
if (!equal) {
await diffImage.save('diffImage.png');
}
```
## Comparing colors

@@ -132,0 +152,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc