From 475d767668e9ded7afa81e7ee010b9ccd8c84a21 Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 14:28:46 -0600 Subject: [PATCH 1/6] Basic resize test --- README.md | 3 ++- image-resize-client-tests.js | 39 ++++++++++++++++++++++++++++++++++++ image-resize-client.js | 4 ++-- package.js | 20 ++++++++++++++++-- 4 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 image-resize-client-tests.js diff --git a/README.md b/README.md index ad33ef4..186d7ab 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,8 @@ This package allows you to set the size you want to upload to the service, savin var options = { width: 300, height: 300, - cropSquare: true + cropSquare: true, + quality: 0.3 // optional }; ``` diff --git a/image-resize-client-tests.js b/image-resize-client-tests.js new file mode 100644 index 0000000..828e0c0 --- /dev/null +++ b/image-resize-client-tests.js @@ -0,0 +1,39 @@ +Tinytest.addAsync('Image - Resize', function(test, done) { + var originalImg, resizedImg, originalBlob + var xmlHttp = new XMLHttpRequest(); + xmlHttp.open("GET", 'https://scontent-dfw1-1.xx.fbcdn.net/hphotos-xpt1/v/t1.0-9/12346343_871489722946736_5400293110848611387_n.jpg?oh=420c4ce953952c93b6d282898dd1061d&oe=56DBA4F4', true); // true for asynchronous + xmlHttp.responseType = 'blob'; + xmlHttp.onload = originalImgRetrieved + xmlHttp.send(); + + function originalImgRetrieved(error) { + if (this.status == 200) { + originalBlob = new Blob([this.response], {type: 'image/jpg'}) + originalBlob.name = 'blob.jpg' + + originalImg = document.createElement('img'); + originalImg.src = window.URL.createObjectURL(originalBlob); + originalImg.onload = onOriginalImgLoaded + + document.body.appendChild(originalImg) + } + } + + function onOriginalImgLoaded() { + Resizer.resize(originalBlob, {width: 300, height: 100, cropSquare: false}, function(err, file) { + resizedImg = document.createElement('img'); + resizedImg.src = window.URL.createObjectURL(file); + resizedImg.onload = onResizedImgLoaded + + document.body.appendChild(resizedImg) + }) + } + + function onResizedImgLoaded() { + test.isTrue(originalImg.height > resizedImg.height) + test.equal(resizedImg.height, 300) + test.equal(resizedImg.width, 300) + + done() + } +}) diff --git a/image-resize-client.js b/image-resize-client.js index 6a7c325..f730fad 100644 --- a/image-resize-client.js +++ b/image-resize-client.js @@ -20,7 +20,7 @@ Resizer = { }; // Get image metadata. - LoadImage.parseMetaData(file, function(data) { + loadImage.parseMetaData(file, function(data) { var orientation = 1; if (data.exif) { orientation = data.exif.get('Orientation'); @@ -30,7 +30,7 @@ Resizer = { } // Resize image with orientation metadata. - LoadImage(file, function(canvas) { + loadImage(file, function(canvas) { var resize_dataUrl = canvas.toDataURL(fileData.type); var binaryImg = atob(resize_dataUrl.slice(resize_dataUrl.indexOf('base64') + 7, resize_dataUrl.length)); diff --git a/package.js b/package.js index 9d289ba..9ce42a0 100644 --- a/package.js +++ b/package.js @@ -12,10 +12,26 @@ Package.describe({ Package.onUse(function(api) { api.versionsFrom('1.0.3.2'); - api.use(["underscore", "jquery"]); + api.use(["underscore", "jquery", 'check']); - api.use('blueimp:javascript-load-image@1.13.1', 'client'); + api.use('marvin:javascript-load-image@2.1.0', 'client'); api.addFiles('image-resize-client.js', 'client'); if (api.export) { api.export('Resizer', 'client'); } }); + +Package.onTest(function(api) { + api.use([ + 'ecmascript', + 'tinytest', + // 'underscore' + ]); + + api.use([ + 'thinksoftware:image-resize-client', + ]); + + api.addFiles([ + 'image-resize-client-tests.js', + ], 'client') +}) From b103925f5e1df7f3f3d518d6e7c44376c7cbdee8 Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 14:35:03 -0600 Subject: [PATCH 2/6] change options to be consistent with javascript-load-image --- image-resize-client-tests.js | 4 ++-- image-resize-client.js | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/image-resize-client-tests.js b/image-resize-client-tests.js index 828e0c0..8c4fdf2 100644 --- a/image-resize-client-tests.js +++ b/image-resize-client-tests.js @@ -20,7 +20,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { } function onOriginalImgLoaded() { - Resizer.resize(originalBlob, {width: 300, height: 100, cropSquare: false}, function(err, file) { + Resizer.resize(originalBlob, {maxWidth: 300, maxHeight: 100, crop: true}, function(err, file) { resizedImg = document.createElement('img'); resizedImg.src = window.URL.createObjectURL(file); resizedImg.onload = onResizedImgLoaded @@ -31,7 +31,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { function onResizedImgLoaded() { test.isTrue(originalImg.height > resizedImg.height) - test.equal(resizedImg.height, 300) + test.equal(resizedImg.height, 100) test.equal(resizedImg.width, 300) done() diff --git a/image-resize-client.js b/image-resize-client.js index f730fad..6635080 100644 --- a/image-resize-client.js +++ b/image-resize-client.js @@ -2,16 +2,14 @@ Resizer = { resize: function(file, options, callback) { check(options, Match.ObjectIncluding({ - width: Number, - height: Number, - cropSquare: Boolean + maxWidth: Number, + maxHeight: Number, + crop: Match.Optional(Boolean), + canvas: Match.Optional(Boolean) })); - // Convert to LoadImage style options. - options.maxWidth = options.width; - options.maxHeight = options.width; - options.crop = options.cropSquare; - options.canvas = true; + if(options.canvas === undefined) + options.canvas = true; var fileData = { name: file.name, From af2b62d38876e5b686c51ac5bf88fa8577ecef3a Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 15:28:40 -0600 Subject: [PATCH 3/6] Add all javascript-load-image options --- image-resize-client-tests.js | 8 +++++--- image-resize-client.js | 36 ++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/image-resize-client-tests.js b/image-resize-client-tests.js index 8c4fdf2..cca1b4a 100644 --- a/image-resize-client-tests.js +++ b/image-resize-client-tests.js @@ -12,6 +12,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { originalBlob.name = 'blob.jpg' originalImg = document.createElement('img'); + originalImg.style = 'display: none;' originalImg.src = window.URL.createObjectURL(originalBlob); originalImg.onload = onOriginalImgLoaded @@ -22,6 +23,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { function onOriginalImgLoaded() { Resizer.resize(originalBlob, {maxWidth: 300, maxHeight: 100, crop: true}, function(err, file) { resizedImg = document.createElement('img'); + resizedImg.style = 'display: none;' resizedImg.src = window.URL.createObjectURL(file); resizedImg.onload = onResizedImgLoaded @@ -30,9 +32,9 @@ Tinytest.addAsync('Image - Resize', function(test, done) { } function onResizedImgLoaded() { - test.isTrue(originalImg.height > resizedImg.height) - test.equal(resizedImg.height, 100) - test.equal(resizedImg.width, 300) + test.isTrue(originalImg.height > resizedImg.height, "Original height not greater than resized") + test.equal(resizedImg.height, 100, "Resized height incorrect") + test.equal(resizedImg.width, 300, "Resized height incorrect") done() } diff --git a/image-resize-client.js b/image-resize-client.js index 6635080..7131876 100644 --- a/image-resize-client.js +++ b/image-resize-client.js @@ -2,18 +2,32 @@ Resizer = { resize: function(file, options, callback) { check(options, Match.ObjectIncluding({ - maxWidth: Number, - maxHeight: Number, + maxWidth: Match.Optional(Number), + maxHeight: Match.Optional(Number), + minWidth: Match.Optional(Number), + minHeight: Match.Optional(Number), + sourceWidth: Match.Optional(Number), + sourceHeight: Match.Optional(Number), + top: Match.Optional(Number), + right: Match.Optional(Number), + bottom: Match.Optional(Number), + left: Match.Optional(Number), + contain: Match.Optional(Boolean), + cover: Match.Optional(Boolean), + aspectRatio: Match.Optional(Number), crop: Match.Optional(Boolean), - canvas: Match.Optional(Boolean) + orientation: Match.Optional(Boolean), + // canvas: Match.Optional(Boolean), + crossOrigin: Match.Optional(Boolean), + noRevoke: Match.Optional(Boolean) })); - if(options.canvas === undefined) - options.canvas = true; + // Canvas must be true + // if(options.canvas === undefined) + options.canvas = true; var fileData = { name: file.name, - size: file.size, type: file.type }; @@ -29,7 +43,7 @@ Resizer = { // Resize image with orientation metadata. loadImage(file, function(canvas) { - var resize_dataUrl = canvas.toDataURL(fileData.type); + var resize_dataUrl = canvas.toDataURL("image/jpeg",0.7); var binaryImg = atob(resize_dataUrl.slice(resize_dataUrl.indexOf('base64') + 7, resize_dataUrl.length)); var length = binaryImg.length; @@ -40,15 +54,17 @@ Resizer = { } fileData.data = new Blob([ua], {type: file.type, name: file.name}); - fileData.data.type = file.type; - fileData.size = ua.length; callback(null, _.extend(fileData.data, {name: fileData.name}, data.exif ? data.exif.getAll() : {})); }, options); - }, { maxMetaDataSize: 262144, disableImageHead: false }); + }, + { + maxMetaDataSize: 262144, + disableImageHead: false + }); } } From 0a130d40cbb0c1c10842da00400f9959e4b9b760 Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 15:47:13 -0600 Subject: [PATCH 4/6] Bump javascript-load-image version --- README.md | 47 +++++++++++++++++++++++++++++++++++++++-------- package.js | 2 +- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 186d7ab..56b66ab 100644 --- a/README.md +++ b/README.md @@ -14,14 +14,45 @@ This package solves that issue by allowing you to set a width, height and wether This package allows you to set the size you want to upload to the service, saving space and bandwidth from the client. The package gives you the following options: -``` -var options = { - width: 300, - height: 300, - cropSquare: true, - quality: 0.3 // optional -}; -``` +* **maxWidth**: Defines the maximum width of the img/canvas element. +* **maxHeight**: Defines the maximum height of the img/canvas element. +* **minWidth**: Defines the minimum width of the img/canvas element. +* **minHeight**: Defines the minimum height of the img/canvas element. +* **sourceWidth**: The width of the sub-rectangle of the source image to draw +into the destination canvas. +Defaults to the source image width and requires *canvas: true*. +* **sourceHeight**: The height of the sub-rectangle of the source image to draw +into the destination canvas. +Defaults to the source image height and requires *canvas: true*. +* **top**: The top margin of the sub-rectangle of the source image. +Defaults to *0* and requires *canvas: true*. +* **right**: The right margin of the sub-rectangle of the source image. +Defaults to *0* and requires *canvas: true*. +* **bottom**: The bottom margin of the sub-rectangle of the source image. +Defaults to *0* and requires *canvas: true*. +* **left**: The left margin of the sub-rectangle of the source image. +Defaults to *0* and requires *canvas: true*. +* **contain**: Scales the image up/down to contain it in the max dimensions if +set to *true*. +This emulates the CSS feature +[background-image: contain](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#contain). +* **cover**: Scales the image up/down to cover the max dimensions with the image dimensions if set to *true*. +This emulates the CSS feature +[background-image: cover](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Scaling_background_images#cover). +* **aspectRatio**: Crops the image to the given aspect ratio (e.g. `16/9`). +This feature assumes *crop: true*. +* **crop**: Crops the image to the maxWidth/maxHeight constraints if set to +*true*. +This feature assumes *canvas: true*. +* **orientation**: Allows to transform the canvas coordinates according to the +EXIF orientation specification. +This feature assumes *canvas: true*. +* **crossOrigin**: Sets the crossOrigin property on the img element for loading +[CORS enabled images](https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image). +* **noRevoke**: By default, the +[created object URL](https://developer.mozilla.org/en/DOM/window.URL.createObjectURL) +is revoked after the image has been loaded, except when this option is set to +*true*. The package returns a Blob file extended with the file name, size and type intact as you can see in a copy of a console log of a resized image from a file upload input: diff --git a/package.js b/package.js index 9ce42a0..a09ea16 100644 --- a/package.js +++ b/package.js @@ -14,7 +14,7 @@ Package.onUse(function(api) { api.versionsFrom('1.0.3.2'); api.use(["underscore", "jquery", 'check']); - api.use('marvin:javascript-load-image@2.1.0', 'client'); + api.use('marvin:javascript-load-image@2.1.1', 'client'); api.addFiles('image-resize-client.js', 'client'); if (api.export) { api.export('Resizer', 'client'); } From 5265649f5a06c9543cd5ab500423c02b03e62d27 Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 16:13:34 -0600 Subject: [PATCH 5/6] Revert changes in toDataURL --- image-resize-client.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image-resize-client.js b/image-resize-client.js index 7131876..15868a7 100644 --- a/image-resize-client.js +++ b/image-resize-client.js @@ -43,7 +43,7 @@ Resizer = { // Resize image with orientation metadata. loadImage(file, function(canvas) { - var resize_dataUrl = canvas.toDataURL("image/jpeg",0.7); + var resize_dataUrl = canvas.toDataURL(file.type); var binaryImg = atob(resize_dataUrl.slice(resize_dataUrl.indexOf('base64') + 7, resize_dataUrl.length)); var length = binaryImg.length; From 8ab717843ddb7270c99ddea9e565cc114ce89b2e Mon Sep 17 00:00:00 2001 From: Marvin Arnold Date: Thu, 31 Dec 2015 16:33:46 -0600 Subject: [PATCH 6/6] Use canvas.toBlob for more efficient conversion --- image-resize-client-tests.js | 12 +++++++----- image-resize-client.js | 25 ++++++------------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/image-resize-client-tests.js b/image-resize-client-tests.js index cca1b4a..24d7689 100644 --- a/image-resize-client-tests.js +++ b/image-resize-client-tests.js @@ -1,5 +1,5 @@ Tinytest.addAsync('Image - Resize', function(test, done) { - var originalImg, resizedImg, originalBlob + var originalImg, resizedImg, originalBlob, resizedBlob var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", 'https://scontent-dfw1-1.xx.fbcdn.net/hphotos-xpt1/v/t1.0-9/12346343_871489722946736_5400293110848611387_n.jpg?oh=420c4ce953952c93b6d282898dd1061d&oe=56DBA4F4', true); // true for asynchronous xmlHttp.responseType = 'blob'; @@ -12,7 +12,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { originalBlob.name = 'blob.jpg' originalImg = document.createElement('img'); - originalImg.style = 'display: none;' + // originalImg.style = 'display: none;' originalImg.src = window.URL.createObjectURL(originalBlob); originalImg.onload = onOriginalImgLoaded @@ -21,10 +21,11 @@ Tinytest.addAsync('Image - Resize', function(test, done) { } function onOriginalImgLoaded() { - Resizer.resize(originalBlob, {maxWidth: 300, maxHeight: 100, crop: true}, function(err, file) { + Resizer.resize(originalBlob, {maxWidth: 300, maxHeight: 100, crop: true, canvas: false}, function(err, file) { + resizedBlob = file resizedImg = document.createElement('img'); - resizedImg.style = 'display: none;' - resizedImg.src = window.URL.createObjectURL(file); + // resizedImg.style = 'display: none;' + resizedImg.src = window.URL.createObjectURL(resizedBlob); resizedImg.onload = onResizedImgLoaded document.body.appendChild(resizedImg) @@ -35,6 +36,7 @@ Tinytest.addAsync('Image - Resize', function(test, done) { test.isTrue(originalImg.height > resizedImg.height, "Original height not greater than resized") test.equal(resizedImg.height, 100, "Resized height incorrect") test.equal(resizedImg.width, 300, "Resized height incorrect") + test.isTrue(originalBlob.size > resizedBlob.size ) done() } diff --git a/image-resize-client.js b/image-resize-client.js index 15868a7..7a5e3c3 100644 --- a/image-resize-client.js +++ b/image-resize-client.js @@ -17,15 +17,11 @@ Resizer = { aspectRatio: Match.Optional(Number), crop: Match.Optional(Boolean), orientation: Match.Optional(Boolean), - // canvas: Match.Optional(Boolean), + canvas: Match.Optional(Boolean), crossOrigin: Match.Optional(Boolean), noRevoke: Match.Optional(Boolean) })); - // Canvas must be true - // if(options.canvas === undefined) - options.canvas = true; - var fileData = { name: file.name, type: file.type @@ -43,22 +39,13 @@ Resizer = { // Resize image with orientation metadata. loadImage(file, function(canvas) { - var resize_dataUrl = canvas.toDataURL(file.type); - - var binaryImg = atob(resize_dataUrl.slice(resize_dataUrl.indexOf('base64') + 7, resize_dataUrl.length)); - var length = binaryImg.length; - var ab = new ArrayBuffer(length); - var ua = new Uint8Array(ab); - for (var i = 0; i < length; i++) { - ua[i] = binaryImg.charCodeAt(i); - } - - fileData.data = new Blob([ua], {type: file.type, name: file.name}); - fileData.data.type = file.type; - fileData.size = ua.length; - callback(null, _.extend(fileData.data, {name: fileData.name}, data.exif ? data.exif.getAll() : {})); + canvas.toBlob(function(blob) { + fileData.data = blob + fileData.data.type = file.type; + callback(null, _.extend(fileData.data, {name: fileData.name}, data.exif ? data.exif.getAll() : {})); + }) }, options); },