From 54fe17a1f4266b8ca51540bfb9f11f899a1b1757 Mon Sep 17 00:00:00 2001 From: B2F Date: Sat, 18 Apr 2015 12:22:11 +0200 Subject: [PATCH 1/2] 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 --- imagediff.js | 54 ++++++++++++++++++++++++++++++++++++------- js/imagediff.js | 54 ++++++++++++++++++++++++++++++++++++------- spec/ImageDiffSpec.js | 51 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 140 insertions(+), 19 deletions(-) diff --git a/imagediff.js b/imagediff.js index 279ade0..4149ceb 100644 --- a/imagediff.js +++ b/imagediff.js @@ -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; @@ -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 } } @@ -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]; + } } } @@ -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 () { diff --git a/js/imagediff.js b/js/imagediff.js index 275d6f8..7c35206 100644 --- a/js/imagediff.js +++ b/js/imagediff.js @@ -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; @@ -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 } } @@ -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]; + } } } @@ -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 () { diff --git a/spec/ImageDiffSpec.js b/spec/ImageDiffSpec.js index 67299b4..56f9b2d 100644 --- a/spec/ImageDiffSpec.js +++ b/spec/ImageDiffSpec.js @@ -312,6 +312,20 @@ 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), @@ -319,7 +333,8 @@ describe('ImageUtils', function() { 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); @@ -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 @@ -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; @@ -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); + }); }); /* From 491f8673d5a80d5147c29f4c67045b3aff9e245c Mon Sep 17 00:00:00 2001 From: B2F Date: Thu, 14 May 2015 11:55:42 +0200 Subject: [PATCH 2/2] Replaces lightness and rgb options with lightboost and diffColor (PR 52#issuecomment-101045835) --- imagediff.js | 43 +++++++++++++++++++------------------------ js/imagediff.js | 43 +++++++++++++++++++------------------------ spec/ImageDiffSpec.js | 20 ++++++++++---------- 3 files changed, 48 insertions(+), 58 deletions(-) diff --git a/imagediff.js b/imagediff.js index 4149ceb..abd3088 100644 --- a/imagediff.js +++ b/imagediff.js @@ -196,9 +196,9 @@ for (i = 0; i < length; i += 4) { 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); + var pixelC = diffPixels(pixelA, pixelB, options); for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) { - cData[i+rgbIndex] = diffPixel[rgbIndex]; + cData[i+rgbIndex] = pixelC[rgbIndex]; } } @@ -244,9 +244,9 @@ j = 4 * (row * b.width + column); 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); + var pixelC = diffPixels(pixelA, pixelB, options); for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) { - cData[i+rgbIndex] = diffPixel[rgbIndex]; + cData[i+rgbIndex] = pixelC[rgbIndex]; } } } @@ -267,37 +267,32 @@ /** * 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/pull/52 * - * @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 + * options.lightboost: increases differences visibility with a light boost. + * options.diffColor: a rgb array used to specify differences color instead of light gap. + * options.stack: stacks 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]; + var lightboost = options && options.lightboost || 0; + var diffColor = options && options.diffColor || false; + var stack = options && options.stack || false; + // pixel = [r,g,b,a] + var pixelC = [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); + pixelC[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]); + if (pixelC[rgbIndex] > 0) { + if (diffColor) pixelC[rgbIndex] = diffColor[rgbIndex]; + pixelC[rgbIndex] = Math.min(pixelC[rgbIndex] + lightboost, 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]; + pixelC[rgbIndex] = pixelA[rgbIndex]; } } - return diffPixel; + return pixelC; } // Validation diff --git a/js/imagediff.js b/js/imagediff.js index 7c35206..9b5dbcd 100644 --- a/js/imagediff.js +++ b/js/imagediff.js @@ -189,9 +189,9 @@ for (i = 0; i < length; i += 4) { 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); + var pixelC = diffPixels(pixelA, pixelB, options); for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) { - cData[i+rgbIndex] = diffPixel[rgbIndex]; + cData[i+rgbIndex] = pixelC[rgbIndex]; } } @@ -237,9 +237,9 @@ j = 4 * (row * b.width + column); 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); + var pixelC = diffPixels(pixelA, pixelB, options); for (var rgbIndex = 0; rgbIndex < 4; rgbIndex++) { - cData[i+rgbIndex] = diffPixel[rgbIndex]; + cData[i+rgbIndex] = pixelC[rgbIndex]; } } } @@ -260,37 +260,32 @@ /** * 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/pull/52 * - * @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 + * options.lightboost: increases differences visibility with a light boost. + * options.diffColor: a rgb array used to specify differences color instead of light gap. + * options.stack: stacks 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]; + var lightboost = options && options.lightboost || 0; + var diffColor = options && options.diffColor || false; + var stack = options && options.stack || false; + // pixel = [r,g,b,a] + var pixelC = [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); + pixelC[rgbIndex] = Math.abs(pixelA[rgbIndex] - pixelB[rgbIndex]); + if (pixelC[rgbIndex] > 0) { + if (diffColor) pixelC[rgbIndex] = diffColor[rgbIndex]; + pixelC[rgbIndex] = Math.min(pixelC[rgbIndex] + lightboost, 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]; + pixelC[rgbIndex] = pixelA[rgbIndex]; } } - return diffPixel; + return pixelC; } // Validation diff --git a/spec/ImageDiffSpec.js b/spec/ImageDiffSpec.js index 56f9b2d..ad8b88a 100644 --- a/spec/ImageDiffSpec.js +++ b/spec/ImageDiffSpec.js @@ -316,7 +316,7 @@ describe('ImageUtils', function() { a.data[1] = 200, b = imagediff.createImageData(1, 1), b.data[1] = 158, - c = imagediff.diff(a, b, {lightness: 0}), + c = imagediff.diff(a, b), d = imagediff.createImageData(1, 1); d.data[1] = 42; @@ -325,16 +325,16 @@ describe('ImageUtils', function() { expect(c).toImageDiffEqual(d); }); - it('should calculate difference with adjusted lightness', function () { + it('should calculate color 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); + c = imagediff.diff(a, b, {lightboost: 155}); d = imagediff.createImageData(1, 1); - // 42 + 25 (default increased lightness) - d.data[1] = 67; + // a-b + 155 + d.data[1] = 197; d.data[3] = 255; expect(c).toImageDiffEqual(d); @@ -344,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, {lightness: 0}); + c = imagediff.diff(a, b); d = imagediff.createImageData(3, 3); // 4 * (rowPos * imageWidth + columnPos) + rgbPos @@ -363,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, {lightness: 0, align: 'top'}); + c = imagediff.diff(a, b, {align: 'top'}); d = imagediff.createImageData(3, 3); d.data[1] = 21; @@ -384,12 +384,12 @@ describe('ImageUtils', function() { Array.prototype.forEach.call(b.data, function (value, i) { b.data[i] = 125; }); - c = imagediff.diff(a, b, {rgb: [255,0,255]}); + c = imagediff.diff(a, b, {diffColor: [255,0,44], lightboost: 191}); d = imagediff.createImageData(1, 1); d.data[0] = 255; - d.data[1] = 125; - d.data[2] = 255; + d.data[1] = 191; + d.data[2] = 235; d.data[3] = 255; expect(c).toImageDiffEqual(d);