From 9e2275d2b87ae17f3d5b576d594c3fb3edc7d97b Mon Sep 17 00:00:00 2001 From: Martin Stanek Date: Sat, 13 Apr 2013 18:55:35 +0200 Subject: [PATCH 1/2] Added Base64 to ImageData feature --- examples/image-type-base64.js | 12 ++++++++++++ imagediff.js | 27 +++++++++++++++++++++++++++ imagediff.min.js | 2 +- js/imagediff.js | 27 +++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 examples/image-type-base64.js diff --git a/examples/image-type-base64.js b/examples/image-type-base64.js new file mode 100644 index 0000000..4c5afef --- /dev/null +++ b/examples/image-type-base64.js @@ -0,0 +1,12 @@ +var imagediff = require('../js/imagediff.js'), + fs = require('fs'); + +var source = fs.readFileSync('1_normal_a.jpg'); +var target = fs.readFileSync('1_normal_b.jpg'); + +source = "data:image/jpg;base64," + source.toString('base64'); +target = "data:image/jpg;base64," + target.toString('base64'); + +var isEqual = imagediff.equal(source, target, 0); + +console.log("Images are equal: " + isEqual); diff --git a/imagediff.js b/imagediff.js index 8d20d9d..0c64790 100644 --- a/imagediff.js +++ b/imagediff.js @@ -49,6 +49,16 @@ // Type Checking + function isBase64Image (string) { + var base64Regex = /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/, + imageRegex = /^data:image\/(png|jpg);base64,/, + imageType = string.match(imageRegex); + + if (imageType == null) { + return false; + } + return base64Regex.test(string.replace(imageRegex, "")); + } function isImage (object) { return isType(object, TYPE_IMAGE); } @@ -67,6 +77,7 @@ } function isImageType (object) { return ( + isBase64Image(object) || isImage(object) || isCanvas(object) || isContext(object) || @@ -98,11 +109,26 @@ return newImageData; } function toImageData (object) { + if (isBase64Image(object)) { return toImageDataFromBase64(object); } if (isImage(object)) { return toImageDataFromImage(object); } if (isCanvas(object)) { return toImageDataFromCanvas(object); } if (isContext(object)) { return toImageDataFromContext(object); } if (isImageData(object)) { return object; } } + + function toImageDataFromBase64 (string) { + var image = new Canvas.Image; + image.src = string; + + var height = image.height, + width = image.width; + + canvas.width = width; + canvas.height = height; + context.clearRect(0, 0, width, height); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, width, height); + } function toImageDataFromImage (image) { var height = image.height, @@ -340,6 +366,7 @@ createCanvas : getCanvas, createImageData : getImageData, + isBase64Image : isBase64Image, isImage : isImage, isCanvas : isCanvas, isContext : isContext, diff --git a/imagediff.min.js b/imagediff.min.js index 5610794..01c91e6 100644 --- a/imagediff.min.js +++ b/imagediff.min.js @@ -4,4 +4,4 @@ // For original source and documentation visit: // http://www.github.com/HumbleSoftware/js-imagediff -(function(a,b){var c=this;if(typeof module!="undefined"){var d=require("canvas");module.exports=b(c,a,d)}else typeof define=="function"&&typeof define.amd=="object"?define(b):c[a]=b(c,a)})("imagediff",function(a,b,c){function o(a,b){var d=c?new c:document.createElement("canvas");return a&&(d.width=a),b&&(d.height=b),d}function p(a,b){return j.width=a,j.height=b,k.clearRect(0,0,a,b),k.createImageData(a,b)}function q(a){return v(a,g)}function r(a){return v(a,e)}function s(a){return v(a,f)}function t(a){return!!a&&!!v(a,h)&&typeof a.width!==i&&typeof a.height!==i&&typeof a.data!==i}function u(a){return q(a)||r(a)||s(a)||t(a)}function v(a,b){return typeof a=="object"&&!!Object.prototype.toString.apply(a).match(b)}function w(a){var b=a.height,c=a.width,d=a.data,e,f,g;j.width=c,j.height=b,e=k.getImageData(0,0,c,b),f=e.data;for(g=a.data.length;g--;)f[g]=d[g];return e}function x(a){if(q(a))return y(a);if(r(a))return z(a);if(s(a))return A(a);if(t(a))return a}function y(a){var b=a.height,c=a.width;return j.width=c,j.height=b,k.clearRect(0,0,c,b),k.drawImage(a,0,0),k.getImageData(0,0,c,b)}function z(a){var b=a.height,c=a.width,d=a.getContext("2d");return d.getImageData(0,0,c,b)}function A(a){var b=a.canvas,c=b.height,d=b.width;return a.getImageData(0,0,d,c)}function B(a){var b=x(a),c=o(b.width,b.height),d=c.getContext("2d");return d.putImageData(b,0,0),c}function C(a,b){return a.width===b.width}function D(a,b){return a.height===b.height}function E(a,b){return D(a,b)&&C(a,b)}function F(a,b,c){var d=a.data,e=b.data,f=d.length,g;c=c||0;if(!E(a,b))return!1;for(g=f;g--;)if(d[g]!==e[g]&&Math.abs(d[g]-e[g])>c)return!1;return!0}function G(a,b){return(E(a,b)?H:I)(a,b)}function H(a,b){var c=a.height,d=a.width,e=p(d,c),f=a.data,g=b.data,h=e.data,i=h.length,j,k,l,m,n,o;for(l=0;l0;m-=4)h[m]=255;r(a);for(k=a.height;k--;)for(l=a.width;l--;)m=4*((k+i)*d+(l+j)),n=4*(k*a.width+l),h[m+0]=f[n+0],h[m+1]=f[n+1],h[m+2]=f[n+2];r(b);for(k=b.height;k--;)for(l=b.width;l--;)m=4*((k+i)*d+(l+j)),n=4*(k*b.width+l),h[m+0]=Math.abs(h[m+0]-g[n+0]),h[m+1]=Math.abs(h[m+1]-g[n+1]),h[m+2]=Math.abs(h[m+2]-g[n+2]);return e}function J(){var a;for(a=0;aActual:"),d=K("div","
Expected:
"),e=K("div","
Diff:
"),f=m.diff(this.actual,a),g=o(),h;return g.height=f.height,g.width=f.width,b.style.overflow="hidden",c.style.float="left",d.style.float="left",e.style.float="left",h=g.getContext("2d"),h.putImageData(f,0,0),c.appendChild(B(this.actual)),d.appendChild(B(a)),e.appendChild(g),b.appendChild(c),b.appendChild(d),b.appendChild(e),[b,"Expected not to be equal."]}),m.equal(this.actual,a,b)}},m={createCanvas:o,createImageData:p,isImage:q,isCanvas:r,isContext:s,isImageData:t,isImageType:u,toImageData:function(a){return J(a),t(a)?w(a):x(a)},equal:function(a,b,c){return J(a,b),a=x(a),b=x(b),F(a,b,c)},diff:function(a,b){return J(a,b),a=x(a),b=x(b),G(a,b)},jasmine:n,noConflict:function(){return a[b]=l,m}},typeof module!="undefined"&&(m.imageDataToPNG=L),m}) \ No newline at end of file +(function(e,t){var n=this;if(typeof module!="undefined"){var r=require("canvas");module.exports=t(n,e,r)}else typeof define=="function"&&typeof define.amd=="object"?define(t):n[e]=t(n,e)})("imagediff",function(e,t,n){function d(e,t){var r=n?new n:document.createElement("canvas");return e&&(r.width=e),t&&(r.height=t),r}function v(e,t){return f.width=e,f.height=t,l.clearRect(0,0,e,t),l.createImageData(e,t)}function m(e){var t=/^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/,n=/^data:image\/(png|jpg);base64,/,r=e.match(n);return r==null?!1:t.test(e.replace(n,""))}function g(e){return S(e,o)}function y(e){return S(e,i)}function b(e){return S(e,s)}function w(e){return!!e&&!!S(e,u)&&typeof e.width!==a&&typeof e.height!==a&&typeof e.data!==a}function E(e){return m(e)||g(e)||y(e)||b(e)||w(e)}function S(e,t){return typeof e=="object"&&!!Object.prototype.toString.apply(e).match(t)}function x(e){var t=e.height,n=e.width,r=e.data,i,s,o;f.width=n,f.height=t,i=l.getImageData(0,0,n,t),s=i.data;for(o=e.data.length;o--;)s[o]=r[o];return i}function T(e){if(m(e))return N(e);if(g(e))return C(e);if(y(e))return k(e);if(b(e))return L(e);if(w(e))return e}function N(e){var t=new n.Image;t.src=e;var r=t.height,i=t.width;return f.width=i,f.height=r,l.clearRect(0,0,i,r),l.drawImage(t,0,0),l.getImageData(0,0,i,r)}function C(e){var t=e.height,n=e.width;return f.width=n,f.height=t,l.clearRect(0,0,n,t),l.drawImage(e,0,0),l.getImageData(0,0,n,t)}function k(e){var t=e.height,n=e.width,r=e.getContext("2d");return r.getImageData(0,0,n,t)}function L(e){var t=e.canvas,n=t.height,r=t.width;return e.getImageData(0,0,r,n)}function A(e){var t=T(e),n=d(t.width,t.height),r=n.getContext("2d");return r.putImageData(t,0,0),n}function O(e,t){return e.width===t.width}function M(e,t){return e.height===t.height}function _(e,t){return M(e,t)&&O(e,t)}function D(e,t,n){var r=e.data,i=t.data,s=r.length,o;n=n||0;if(!_(e,t))return!1;for(o=s;o--;)if(r[o]!==i[o]&&Math.abs(r[o]-i[o])>n)return!1;return!0}function P(e,t){return(_(e,t)?H:B)(e,t)}function H(e,t){var n=e.height,r=e.width,i=v(r,n),s=e.data,o=t.data,u=i.data,a=u.length,f,l,c,h,p,d;for(c=0;c0;h-=4)u[h]=255;g(e);for(l=e.height;l--;)for(c=e.width;c--;)h=4*((l+a)*r+(c+f)),p=4*(l*e.width+c),u[h+0]=s[p+0],u[h+1]=s[p+1],u[h+2]=s[p+2];g(t);for(l=t.height;l--;)for(c=t.width;c--;)h=4*((l+a)*r+(c+f)),p=4*(l*t.width+c),u[h+0]=Math.abs(u[h+0]-o[p+0]),u[h+1]=Math.abs(u[h+1]-o[p+1]),u[h+2]=Math.abs(u[h+2]-o[p+2]);return i}function j(){var e;for(e=0;eActual:"),r=F("div","
Expected:
"),i=F("div","
Diff:
"),s=h.diff(this.actual,e),o=d(),u;return o.height=s.height,o.width=s.width,t.style.overflow="hidden",n.style.float="left",r.style.float="left",i.style.float="left",u=o.getContext("2d"),u.putImageData(s,0,0),n.appendChild(A(this.actual)),r.appendChild(A(e)),i.appendChild(o),t.appendChild(n),t.appendChild(r),t.appendChild(i),[t,"Expected not to be equal."]}),h.equal(this.actual,e,t)}},h={createCanvas:d,createImageData:v,isBase64Image:m,isImage:g,isCanvas:y,isContext:b,isImageData:w,isImageType:E,toImageData:function(e){return j(e),w(e)?x(e):T(e)},equal:function(e,t,n){return j(e,t),e=T(e),t=T(t),D(e,t,n)},diff:function(e,t){return j(e,t),e=T(e),t=T(t),P(e,t)},jasmine:p,noConflict:function(){return e[t]=c,h}},typeof module!="undefined"&&(h.imageDataToPNG=I),h}) \ No newline at end of file diff --git a/js/imagediff.js b/js/imagediff.js index a084c62..4b84085 100644 --- a/js/imagediff.js +++ b/js/imagediff.js @@ -43,6 +43,16 @@ // Type Checking + function isBase64Image (string) { + var base64Regex = /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/, + imageRegex = /^data:image\/(png|jpg);base64,/, + imageType = string.match(imageRegex); + + if (imageType == null) { + return false; + } + return base64Regex.test(string.replace(imageRegex, "")); + } function isImage (object) { return isType(object, TYPE_IMAGE); } @@ -61,6 +71,7 @@ } function isImageType (object) { return ( + isBase64Image(object) || isImage(object) || isCanvas(object) || isContext(object) || @@ -92,11 +103,26 @@ return newImageData; } function toImageData (object) { + if (isBase64Image(object)) { return toImageDataFromBase64(object); } if (isImage(object)) { return toImageDataFromImage(object); } if (isCanvas(object)) { return toImageDataFromCanvas(object); } if (isContext(object)) { return toImageDataFromContext(object); } if (isImageData(object)) { return object; } } + + function toImageDataFromBase64 (string) { + var image = new Canvas.Image; + image.src = string; + + var height = image.height, + width = image.width; + + canvas.width = width; + canvas.height = height; + context.clearRect(0, 0, width, height); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, width, height); + } function toImageDataFromImage (image) { var height = image.height, @@ -334,6 +360,7 @@ createCanvas : getCanvas, createImageData : getImageData, + isBase64Image : isBase64Image, isImage : isImage, isCanvas : isCanvas, isContext : isContext, From 59979c3328755372dda3f045a36a29990a47a2b0 Mon Sep 17 00:00:00 2001 From: Carl Sutherland Date: Wed, 27 Nov 2013 21:47:18 -0500 Subject: [PATCH 2/2] Curate the base64 PR and add some specs. --- js/imagediff.js | 52 +++++++++++++++++++++++-------------------- spec/ImageDiffSpec.js | 25 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/js/imagediff.js b/js/imagediff.js index 4b84085..a102c5d 100644 --- a/js/imagediff.js +++ b/js/imagediff.js @@ -44,14 +44,14 @@ // Type Checking function isBase64Image (string) { - var base64Regex = /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/, - imageRegex = /^data:image\/(png|jpg);base64,/, - imageType = string.match(imageRegex); - - if (imageType == null) { - return false; - } - return base64Regex.test(string.replace(imageRegex, "")); + var + base64Regex = /^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$/, + imageRegex = /^data:image\/(png|jpg);base64,/; + return ( + typeof string === 'string' && + imageRegex.test(string) && + base64Regex.test(string.replace(imageRegex, "")) + ); } function isImage (object) { return isType(object, TYPE_IMAGE); @@ -71,11 +71,11 @@ } function isImageType (object) { return ( - isBase64Image(object) || isImage(object) || isCanvas(object) || isContext(object) || - isImageData(object) + isImageData(object) || + isBase64Image(object) ); } function isType (object, type) { @@ -103,25 +103,29 @@ return newImageData; } function toImageData (object) { - if (isBase64Image(object)) { return toImageDataFromBase64(object); } if (isImage(object)) { return toImageDataFromImage(object); } if (isCanvas(object)) { return toImageDataFromCanvas(object); } if (isContext(object)) { return toImageDataFromContext(object); } if (isImageData(object)) { return object; } + if (isBase64Image(object)) { return toImageDataFromBase64.apply(null, arguments); } } - function toImageDataFromBase64 (string) { - var image = new Canvas.Image; + function toImageDataFromBase64 (string, callback) { + var + image = new Image; image.src = string; - - var height = image.height, + image.onload = function () { + var + height = image.height, width = image.width; - - canvas.width = width; - canvas.height = height; - context.clearRect(0, 0, width, height); - context.drawImage(image, 0, 0); - return context.getImageData(0, 0, width, height); + canvas.width = width; + canvas.height = height; + context.clearRect(0, 0, width, height); + context.drawImage(image, 0, 0); + if (callback) { + callback(context.getImageData(0, 0, width, height)); + } + }; } function toImageDataFromImage (image) { var @@ -370,16 +374,16 @@ toImageData : function (object) { checkType(object); if (isImageData(object)) { return copyImageData(object); } - return toImageData(object); + return toImageData.apply(null, arguments); }, - equal : function (a, b, tolerance) { + equal : function (a, b, tolerance, callback) { checkType(a, b); a = toImageData(a); b = toImageData(b); return equal(a, b, tolerance); }, - diff : function (a, b) { + diff : function (a, b, callback) { checkType(a, b); a = toImageData(a); b = toImageData(b); diff --git a/spec/ImageDiffSpec.js b/spec/ImageDiffSpec.js index 2506654..7d07f9f 100644 --- a/spec/ImageDiffSpec.js +++ b/spec/ImageDiffSpec.js @@ -104,6 +104,7 @@ describe('ImageUtils', function() { it('should check Canvas type', testType('isCanvas', canvas)); it('should check 2DContext type', testType('isContext', context)); it('should check ImageData type', testType('isImageData', imageData)); + it('should check Base 64 type', testType('isBase64Image', canvas.toDataURL())); it('should check for any of the image types', function () { expect(imagediff.isImageType(null)).toEqual(false); expect(imagediff.isImageType('')).toEqual(false); @@ -112,6 +113,7 @@ describe('ImageUtils', function() { expect(imagediff.isImageType(canvas)).toEqual(true); expect(imagediff.isImageType(context)).toEqual(true); expect(imagediff.isImageType(imageData)).toEqual(true); + expect(imagediff.isImageType(canvas.toDataURL())).toEqual(true); }); }); @@ -179,6 +181,29 @@ describe('ImageUtils', function() { expect(result).toBeImageData(); }); + it('should convert Base64 to ImageData', function () { + var + canvas = imagediff.createCanvas(), + context = canvas.getContext('2d'), + result; + + canvas.height = image.height; + canvas.width = image.width; + context.drawImage(image, 0, 0); + + imagediff.toImageData(canvas.toDataURL(), function (image) { + result = image; + }); + + waitsFor(function () { + return !!result; + }, 'async image data not set', 100); + + runs(function () { + expect(result).toBeImageData(); + }); + }); + it('should copy ImageData to new ImageData', function () { result = imagediff.toImageData(imageData); expect(result).toBeImageData();