Skip to content

Commit

Permalink
Fixes HumbleSoftware#34 (Diff swallows black color in added areas): a…
Browse files Browse the repository at this point in the history
…dds options.ligthness, options.rgb and options.stack to customize the diff image.

Updates Jasmine ImageDiffSpec with new diff options
  • Loading branch information
B2F committed Apr 23, 2015
1 parent e92cccf commit 54fe17a
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 19 deletions.
54 changes: 46 additions & 8 deletions imagediff.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,10 +194,12 @@
i, j, k, v;

for (i = 0; i < length; i += 4) {
cData[i] = Math.abs(aData[i] - bData[i]);
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3]));
var pixelA = Array.prototype.slice.call(aData, i, i+3);
var pixelB = Array.prototype.slice.call(bData, i, i+3);
var diffPixel = diffPixels(pixelA, pixelB, options);
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
cData[i+rgbIndex] = diffPixel[rgbIndex];
}
}

return c;
Expand Down Expand Up @@ -231,7 +233,6 @@
cData[i+0] = aData[j+0]; // r
cData[i+1] = aData[j+1]; // g
cData[i+2] = aData[j+2]; // b
// cData[i+3] = aData[j+3]; // a
}
}

Expand All @@ -241,9 +242,12 @@
for (column = b.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * b.width + column);
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
var pixelA = Array.prototype.slice.call(cData, i, i+3);
var pixelB = Array.prototype.slice.call(bData, j, j+3);
var diffPixel = diffPixels(pixelA, pixelB, options);
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
cData[i+rgbIndex] = diffPixel[rgbIndex];
}
}
}

Expand All @@ -261,6 +265,40 @@
return c;
}

/**
* Differentiates two rgb pixels by subtracting color values.
* The light value for each color marks the difference gap.
*
* @see https://github.com/HumbleSoftware/js-imagediff/issues/34
* @param {Object} options
* options.lightness: light added to color value, increasing differences visibility
* options.rgb : array used to specify rgb balance instead of lightness
* options.stack: stack differences on top of the original image, preserving common pixels
*
* @returns {Array} pixel rgba values between 0 and 255.
*/
function diffPixels(pixelA, pixelB, options) {
var lightness = options && options.lightness || 25;
if (options && options.lightness === 0) lightness = 0;
var diffColor = options && options.rgb || [lightness,lightness,lightness];
var stack = options && options.stack;
// diffPixel = [r,g,b,a]
var diffPixel = [0,0,0,255];
for (var rgbIndex = 0; rgbIndex < 3 ; rgbIndex++) {
diffPixel[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]);
if (diffPixel[rgbIndex] > 0) {
// Optionally colors area of difference, adds visibility with lightness.
diffPixel[rgbIndex] += diffColor[rgbIndex];
diffPixel[rgbIndex] = Math.min(diffPixel[rgbIndex], 255);
}
else if (stack) {
// If options.stack and no pixel differences are found,
// print original pixel instead of a black (0) pixel.
diffPixel[rgbIndex] = pixelA[rgbIndex];
}
}
return diffPixel;
}

// Validation
function checkType () {
Expand Down
54 changes: 46 additions & 8 deletions js/imagediff.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,12 @@
i, j, k, v;

for (i = 0; i < length; i += 4) {
cData[i] = Math.abs(aData[i] - bData[i]);
cData[i+1] = Math.abs(aData[i+1] - bData[i+1]);
cData[i+2] = Math.abs(aData[i+2] - bData[i+2]);
cData[i+3] = Math.abs(255 - Math.abs(aData[i+3] - bData[i+3]));
var pixelA = Array.prototype.slice.call(aData, i, i+3);
var pixelB = Array.prototype.slice.call(bData, i, i+3);
var diffPixel = diffPixels(pixelA, pixelB, options);
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
cData[i+rgbIndex] = diffPixel[rgbIndex];
}
}

return c;
Expand Down Expand Up @@ -224,7 +226,6 @@
cData[i+0] = aData[j+0]; // r
cData[i+1] = aData[j+1]; // g
cData[i+2] = aData[j+2]; // b
// cData[i+3] = aData[j+3]; // a
}
}

Expand All @@ -234,9 +235,12 @@
for (column = b.width; column--;) {
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
j = 4 * (row * b.width + column);
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
var pixelA = Array.prototype.slice.call(cData, i, i+3);
var pixelB = Array.prototype.slice.call(bData, j, j+3);
var diffPixel = diffPixels(pixelA, pixelB, options);
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
cData[i+rgbIndex] = diffPixel[rgbIndex];
}
}
}

Expand All @@ -254,6 +258,40 @@
return c;
}

/**
* Differentiates two rgb pixels by subtracting color values.
* The light value for each color marks the difference gap.
*
* @see https://github.com/HumbleSoftware/js-imagediff/issues/34
* @param {Object} options
* options.lightness: light added to color value, increasing differences visibility
* options.rgb : array used to specify rgb balance instead of lightness
* options.stack: stack differences on top of the original image, preserving common pixels
*
* @returns {Array} pixel rgba values between 0 and 255.
*/
function diffPixels(pixelA, pixelB, options) {
var lightness = options && options.lightness || 25;
if (options && options.lightness === 0) lightness = 0;
var diffColor = options && options.rgb || [lightness,lightness,lightness];
var stack = options && options.stack;
// diffPixel = [r,g,b,a]
var diffPixel = [0,0,0,255];
for (var rgbIndex = 0; rgbIndex < 3 ; rgbIndex++) {
diffPixel[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]);
if (diffPixel[rgbIndex] > 0) {
// Optionally colors area of difference, adds visibility with lightness.
diffPixel[rgbIndex] += diffColor[rgbIndex];
diffPixel[rgbIndex] = Math.min(diffPixel[rgbIndex], 255);
}
else if (stack) {
// If options.stack and no pixel differences are found,
// print original pixel instead of a black (0) pixel.
diffPixel[rgbIndex] = pixelA[rgbIndex];
}
}
return diffPixel;
}

// Validation
function checkType () {
Expand Down
51 changes: 48 additions & 3 deletions spec/ImageDiffSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,29 @@ describe('ImageUtils', function() {
});

it('should calculate difference', function () {
a = imagediff.createImageData(1, 1),
a.data[1] = 200,
b = imagediff.createImageData(1, 1),
b.data[1] = 158,
c = imagediff.diff(a, b, {lightness: 0}),

d = imagediff.createImageData(1, 1);
d.data[1] = 42;
d.data[3] = 255;

expect(c).toImageDiffEqual(d);
});

it('should calculate difference with adjusted lightness', function () {
a = imagediff.createImageData(1, 1),
a.data[1] = 200;
b = imagediff.createImageData(1, 1),
b.data[1] = 158;
c = imagediff.diff(a, b);

d = imagediff.createImageData(1, 1);
d.data[1] = 42;
// 42 + 25 (default increased lightness)
d.data[1] = 67;
d.data[3] = 255;

expect(c).toImageDiffEqual(d);
Expand All @@ -329,7 +344,7 @@ describe('ImageUtils', function() {
a = imagediff.createImageData(3, 3),
b = imagediff.createImageData(1, 1),
b.data[1] = 21;
c = imagediff.diff(a, b);
c = imagediff.diff(a, b, {lightness: 0});

d = imagediff.createImageData(3, 3);
// 4 * (rowPos * imageWidth + columnPos) + rgbPos
Expand All @@ -348,7 +363,7 @@ describe('ImageUtils', function() {
a = imagediff.createImageData(3, 3),
b = imagediff.createImageData(1, 1),
b.data[1] = 21;
c = imagediff.diff(a, b, {align: 'top'});
c = imagediff.diff(a, b, {lightness: 0, align: 'top'});

d = imagediff.createImageData(3, 3);
d.data[1] = 21;
Expand All @@ -361,6 +376,36 @@ describe('ImageUtils', function() {

expect(c).toImageDiffEqual(d);
});

it('should optionally color differences', function () {
a = imagediff.createImageData(1, 1),
b = imagediff.createImageData(1, 1);
// Fills a grey pixel and expects c to be tinted in light pink
Array.prototype.forEach.call(b.data, function (value, i) {
b.data[i] = 125;
});
c = imagediff.diff(a, b, {rgb: [255,0,255]});

d = imagediff.createImageData(1, 1);
d.data[0] = 255;
d.data[1] = 125;
d.data[2] = 255;
d.data[3] = 255;

expect(c).toImageDiffEqual(d);
});

it('should optionally show common pixels', function () {
a = imagediff.createImageData(1, 1);
// Fills white pixels
Array.prototype.forEach.call(a.data, function (value, i) {
a.data[i] = 255;
});
b = a;
c = imagediff.diff(a, b, {stack: true});

expect(c).toImageDiffEqual(a);
});
});

/*
Expand Down

0 comments on commit 54fe17a

Please sign in to comment.