Skip to content

Commit 54fe17a

Browse files
committed
Fixes #34 (Diff swallows black color in added areas): adds options.ligthness, options.rgb and options.stack to customize the diff image.
Updates Jasmine ImageDiffSpec with new diff options
1 parent e92cccf commit 54fe17a

File tree

3 files changed

+140
-19
lines changed

3 files changed

+140
-19
lines changed

imagediff.js

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,12 @@
194194
i, j, k, v;
195195

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

203205
return c;
@@ -231,7 +233,6 @@
231233
cData[i+0] = aData[j+0]; // r
232234
cData[i+1] = aData[j+1]; // g
233235
cData[i+2] = aData[j+2]; // b
234-
// cData[i+3] = aData[j+3]; // a
235236
}
236237
}
237238

@@ -241,9 +242,12 @@
241242
for (column = b.width; column--;) {
242243
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
243244
j = 4 * (row * b.width + column);
244-
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
245-
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
246-
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
245+
var pixelA = Array.prototype.slice.call(cData, i, i+3);
246+
var pixelB = Array.prototype.slice.call(bData, j, j+3);
247+
var diffPixel = diffPixels(pixelA, pixelB, options);
248+
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
249+
cData[i+rgbIndex] = diffPixel[rgbIndex];
250+
}
247251
}
248252
}
249253

@@ -261,6 +265,40 @@
261265
return c;
262266
}
263267

268+
/**
269+
* Differentiates two rgb pixels by subtracting color values.
270+
* The light value for each color marks the difference gap.
271+
*
272+
* @see https://github.com/HumbleSoftware/js-imagediff/issues/34
273+
* @param {Object} options
274+
* options.lightness: light added to color value, increasing differences visibility
275+
* options.rgb : array used to specify rgb balance instead of lightness
276+
* options.stack: stack differences on top of the original image, preserving common pixels
277+
*
278+
* @returns {Array} pixel rgba values between 0 and 255.
279+
*/
280+
function diffPixels(pixelA, pixelB, options) {
281+
var lightness = options && options.lightness || 25;
282+
if (options && options.lightness === 0) lightness = 0;
283+
var diffColor = options && options.rgb || [lightness,lightness,lightness];
284+
var stack = options && options.stack;
285+
// diffPixel = [r,g,b,a]
286+
var diffPixel = [0,0,0,255];
287+
for (var rgbIndex = 0; rgbIndex < 3 ; rgbIndex++) {
288+
diffPixel[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]);
289+
if (diffPixel[rgbIndex] > 0) {
290+
// Optionally colors area of difference, adds visibility with lightness.
291+
diffPixel[rgbIndex] += diffColor[rgbIndex];
292+
diffPixel[rgbIndex] = Math.min(diffPixel[rgbIndex], 255);
293+
}
294+
else if (stack) {
295+
// If options.stack and no pixel differences are found,
296+
// print original pixel instead of a black (0) pixel.
297+
diffPixel[rgbIndex] = pixelA[rgbIndex];
298+
}
299+
}
300+
return diffPixel;
301+
}
264302

265303
// Validation
266304
function checkType () {

js/imagediff.js

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,12 @@
187187
i, j, k, v;
188188

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

196198
return c;
@@ -224,7 +226,6 @@
224226
cData[i+0] = aData[j+0]; // r
225227
cData[i+1] = aData[j+1]; // g
226228
cData[i+2] = aData[j+2]; // b
227-
// cData[i+3] = aData[j+3]; // a
228229
}
229230
}
230231

@@ -234,9 +235,12 @@
234235
for (column = b.width; column--;) {
235236
i = 4 * ((row + rowOffset) * width + (column + columnOffset));
236237
j = 4 * (row * b.width + column);
237-
cData[i+0] = Math.abs(cData[i+0] - bData[j+0]); // r
238-
cData[i+1] = Math.abs(cData[i+1] - bData[j+1]); // g
239-
cData[i+2] = Math.abs(cData[i+2] - bData[j+2]); // b
238+
var pixelA = Array.prototype.slice.call(cData, i, i+3);
239+
var pixelB = Array.prototype.slice.call(bData, j, j+3);
240+
var diffPixel = diffPixels(pixelA, pixelB, options);
241+
for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) {
242+
cData[i+rgbIndex] = diffPixel[rgbIndex];
243+
}
240244
}
241245
}
242246

@@ -254,6 +258,40 @@
254258
return c;
255259
}
256260

261+
/**
262+
* Differentiates two rgb pixels by subtracting color values.
263+
* The light value for each color marks the difference gap.
264+
*
265+
* @see https://github.com/HumbleSoftware/js-imagediff/issues/34
266+
* @param {Object} options
267+
* options.lightness: light added to color value, increasing differences visibility
268+
* options.rgb : array used to specify rgb balance instead of lightness
269+
* options.stack: stack differences on top of the original image, preserving common pixels
270+
*
271+
* @returns {Array} pixel rgba values between 0 and 255.
272+
*/
273+
function diffPixels(pixelA, pixelB, options) {
274+
var lightness = options && options.lightness || 25;
275+
if (options && options.lightness === 0) lightness = 0;
276+
var diffColor = options && options.rgb || [lightness,lightness,lightness];
277+
var stack = options && options.stack;
278+
// diffPixel = [r,g,b,a]
279+
var diffPixel = [0,0,0,255];
280+
for (var rgbIndex = 0; rgbIndex < 3 ; rgbIndex++) {
281+
diffPixel[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]);
282+
if (diffPixel[rgbIndex] > 0) {
283+
// Optionally colors area of difference, adds visibility with lightness.
284+
diffPixel[rgbIndex] += diffColor[rgbIndex];
285+
diffPixel[rgbIndex] = Math.min(diffPixel[rgbIndex], 255);
286+
}
287+
else if (stack) {
288+
// If options.stack and no pixel differences are found,
289+
// print original pixel instead of a black (0) pixel.
290+
diffPixel[rgbIndex] = pixelA[rgbIndex];
291+
}
292+
}
293+
return diffPixel;
294+
}
257295

258296
// Validation
259297
function checkType () {

spec/ImageDiffSpec.js

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,14 +312,29 @@ describe('ImageUtils', function() {
312312
});
313313

314314
it('should calculate difference', function () {
315+
a = imagediff.createImageData(1, 1),
316+
a.data[1] = 200,
317+
b = imagediff.createImageData(1, 1),
318+
b.data[1] = 158,
319+
c = imagediff.diff(a, b, {lightness: 0}),
320+
321+
d = imagediff.createImageData(1, 1);
322+
d.data[1] = 42;
323+
d.data[3] = 255;
324+
325+
expect(c).toImageDiffEqual(d);
326+
});
327+
328+
it('should calculate difference with adjusted lightness', function () {
315329
a = imagediff.createImageData(1, 1),
316330
a.data[1] = 200;
317331
b = imagediff.createImageData(1, 1),
318332
b.data[1] = 158;
319333
c = imagediff.diff(a, b);
320334

321335
d = imagediff.createImageData(1, 1);
322-
d.data[1] = 42;
336+
// 42 + 25 (default increased lightness)
337+
d.data[1] = 67;
323338
d.data[3] = 255;
324339

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

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

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

362377
expect(c).toImageDiffEqual(d);
363378
});
379+
380+
it('should optionally color differences', function () {
381+
a = imagediff.createImageData(1, 1),
382+
b = imagediff.createImageData(1, 1);
383+
// Fills a grey pixel and expects c to be tinted in light pink
384+
Array.prototype.forEach.call(b.data, function (value, i) {
385+
b.data[i] = 125;
386+
});
387+
c = imagediff.diff(a, b, {rgb: [255,0,255]});
388+
389+
d = imagediff.createImageData(1, 1);
390+
d.data[0] = 255;
391+
d.data[1] = 125;
392+
d.data[2] = 255;
393+
d.data[3] = 255;
394+
395+
expect(c).toImageDiffEqual(d);
396+
});
397+
398+
it('should optionally show common pixels', function () {
399+
a = imagediff.createImageData(1, 1);
400+
// Fills white pixels
401+
Array.prototype.forEach.call(a.data, function (value, i) {
402+
a.data[i] = 255;
403+
});
404+
b = a;
405+
c = imagediff.diff(a, b, {stack: true});
406+
407+
expect(c).toImageDiffEqual(a);
408+
});
364409
});
365410

366411
/*

0 commit comments

Comments
 (0)