Compressing the image on upload without affecting dimension of image - laravel

I need to compress the image on upload keeping the original dimensions of image. I have used Intervention package of Laravel and am successful in compressing the image size, but, the resize() function also changes dimensions. Is it possible to just reduce the size of image without changing the dimensions?

Referring to the documentation of Intervention, you can resize while maintaining the aspect ratio:
// resize the image to a width of 300 and constrain aspect ratio (auto height)
$img->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
});

Read the files using the HTML5 FileReader API with .readAsArrayBuffer
Create e Blob with the file data and get its url with
window.URL.createObjectURL(blob)
Create new Image element and set it's src to the file blob url
Send the image to the canvas. The canvas size is set to desired output size
Get the scaled-down data back from canvas via canvas.toDataURL("image/jpeg",0.7) (set your own output format and quality)
Attach new hidden inputs to the original form and transfer the dataURI images basically as normal text
On backend, read the dataURI, decode from Base64, and save it
var fileinput = document.getElementById('fileinput');
var max_width = fileinput.getAttribute('data-maxwidth');
var max_height = fileinput.getAttribute('data-maxheight');
var preview = document.getElementById('preview');
var form = document.getElementById('form');
function processfile(file) {
if( !( /image/i ).test( file.type ) )
{
alert( "File "+ file.name +" is not an image." );
return false;
}
// read the files
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (event) {
// blob stuff
var blob = new Blob([event.target.result]); // create blob...
window.URL = window.URL || window.webkitURL;
var blobURL = window.URL.createObjectURL(blob); // and get it's URL
// helper Image object
var image = new Image();
image.src = blobURL;
//preview.appendChild(image); // preview commented out, I am using the canvas instead
image.onload = function() {
// have to wait till it's loaded
var resized = resizeMe(image); // send it to canvas
var newinput = document.createElement("input");
newinput.type = 'hidden';
newinput.name = 'images[]';
newinput.value = resized; // put result from canvas into new hidden input
form.appendChild(newinput);
}
};
}
function readfiles(files) {
// remove the existing canvases and hidden inputs if user re-selects new pics
var existinginputs = document.getElementsByName('images[]');
var existingcanvases = document.getElementsByTagName('canvas');
while (existinginputs.length > 0) { // it's a live list so removing the first element each time
// DOMNode.prototype.remove = function() {this.parentNode.removeChild(this);}
form.removeChild(existinginputs[0]);
preview.removeChild(existingcanvases[0]);
}
for (var i = 0; i < files.length; i++) {
processfile(files[i]); // process each file at once
}
fileinput.value = ""; //remove the original files from fileinput
// TODO remove the previous hidden inputs if user selects other files
}
// this is where it starts. event triggered when user selects files
fileinput.onchange = function(){
if ( !( window.File && window.FileReader && window.FileList && window.Blob ) ) {
alert('The File APIs are not fully supported in this browser.');
return false;
}
readfiles(fileinput.files);
}
// === RESIZE ====
function resizeMe(img) {
var canvas = document.createElement('canvas');
var width = img.width;
var height = img.height;
// calculate the width and height, constraining the proportions
if (width > height) {
if (width > max_width) {
//height *= max_width / width;
height = Math.round(height *= max_width / width);
width = max_width;
}
} else {
if (height > max_height) {
//width *= max_height / height;
width = Math.round(width *= max_height / height);
height = max_height;
}
}
// resize the canvas and draw the image data into it
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
preview.appendChild(canvas); // do the actual resized preview
return canvas.toDataURL("image/jpeg",0.7); // get the data from canvas as 70% JPG (can be also PNG, etc.)
}

Related

How to write Parse Cloud Code to make Thumbnails

Assume I have a Table
---------Image-------------
imageFile (File) | thumbnail (File) | Post (a pointer to Post)
Any idea how to write a cloud code to take a smaller version of that image in another column?
For example, If a user uploaded a (2000x3500 px) image, Parse will save it in imageFile column, and save its thumbnail in the other column
thanks
Here's the current solution I'm using (Original script was published by Parse.com some time ago and deleted after the ownership transfer to the community):
Usage example in main.js:
var resizeImageKey = require('cloud/resize-image-key.js'),
THUMBNAIL_SIZE = 100,
Image = require("parse-image");
Parse.Cloud.afterSave("TableName", function(request) {
return resizeImageKey({
object: request.object, // ParseObject in which the thumbnail will be saved
url: objectImage.url(), // Full size image URL
toKey: "thumbnail", // thumbnail's attribute key
width: THUMBNAIL_SIZE, // width
crop: false // resize or crop ?
});
});
resize-image-key.js (async-await)
const Image = require("parse-image");
/*
Original: https://github.com/ParsePlatform/Anyimg/blob/master/parse/cloud/resize-image-key.js
Resizes an image from one Parse Object key containing
a Parse File to a file object at a new key with a target width.
If the image is smaller than the target width, then it is simply
copied unaltered.
object: Parse Object
url: URL of the Parse File
toKey: Key to contain the target Parse File
width: Target width
crop: Center crop the square
*/
module.exports = function(options) {
let format, originalHeight, originalWidth, newHeight, newWidth;
// First get the image data
const response = await Parse.Cloud.httpRequest({
//url: options.object.get(options.fromKey).url()
url: options.url
})
let image = new Image();
await image.setData(response.buffer);
// set some metadata that will be on the object
format = image.format();
originalHeight = image.height();
originalWidth = image.width();
if (image.width() <= options.width) {
// No need to resize
// Remove from code
} else {
var newWidth = options.width;
var newHeight = options.width * image.height() / image.width();
// If we're cropping to a square, then we need to adjust height and
// width so that the greater length of the two fits the square
if (options.crop && (newWidth > newHeight)) {
var newHeight = options.width;
var newWidth = newHeight * image.width() / image.height();
}
// resize down to normal width size
image = await image.scale({
width: newWidth,
height: newHeight
});
}
// crop ?
if (options.crop) {
let left = 0;
let top = 0;
// Center crop
if (image.width() > image.height()) {
left = (image.width() - image.height()) / 2;
} else {
top = (image.height() - image.width()) / 2;
}
image = await image.crop({
left: left,
top: top,
width: options.width,
height: options.width
});
}
newHeight = image.height();
newWidth = image.width();
// Get the image data in a Buffer.
const buffer = await image.data();
// Save the image into a new file.
const base64 = buffer.toString("base64");
//var scaled = new Parse.File("thumbnail_" + options.object.get("name") + "." + format, {
const scaled = new Parse.File("thumbnail." + format, {
base64: base64
});
const savedImage = await scaled.save();
// Set metadata on the image object
options.object.set(options.toKey, savedImage);
//return options.object.save();
options.object.set("thumbnail_width", newWidth);
options.object.set("thumbnail_height", newHeight);
};
resize-image-key.js (vanilla JS)
var Image = require("parse-image");
/*
Original: https://github.com/ParsePlatform/Anyimg/blob/master/parse/cloud/resize-image-key.js
Resizes an image from one Parse Object key containing
a Parse File to a file object at a new key with a target width.
If the image is smaller than the target width, then it is simply
copied unaltered.
object: Parse Object
url: URL of the Parse File
toKey: Key to contain the target Parse File
width: Target width
crop: Center crop the square
*/
module.exports = function(options) {
var format, originalHeight, originalWidth, newHeight, newWidth;
// First get the image data
return Parse.Cloud.httpRequest({
//url: options.object.get(options.fromKey).url()
url: options.url
}).then(function(response) {
var image = new Image();
return image.setData(response.buffer);
}).then(function(image) {
// set some metadata that will be on the object
format = image.format();
originalHeight = image.height();
originalWidth = image.width();
if (image.width() <= options.width) {
// No need to resize
return new Parse.Promise.as(image);
} else {
var newWidth = options.width;
var newHeight = options.width * image.height() / image.width();
// If we're cropping to a square, then we need to adjust height and
// width so that the greater length of the two fits the square
if (options.crop && (newWidth > newHeight)) {
var newHeight = options.width;
var newWidth = newHeight * image.width() / image.height();
}
// resize down to normal width size
return image.scale({
width: newWidth,
height: newHeight
});
}
}).then(function(image) {
if (options.crop) {
var left = 0;
var top = 0;
// Center crop
if (image.width() > image.height()) {
var left = (image.width() - image.height()) / 2;
} else {
var top = (image.height() - image.width()) / 2;
}
return image.crop({
left: left,
top: top,
width: options.width,
height: options.width
});
} else {
return Parse.Promise.as(image);
}
}).then(function(image) {
newHeight = image.height();
newWidth = image.width();
// Get the image data in a Buffer.
return image.data();
}).then(function(buffer) {
// Save the image into a new file.
var base64 = buffer.toString("base64");
//var scaled = new Parse.File("thumbnail_" + options.object.get("name") + "." + format, {
var scaled = new Parse.File("thumbnail." + format, {
base64: base64
});
return scaled.save();
}).then(function(image) {
// Set metadata on the image object
options.object.set(options.toKey, image);
//return options.object.save();
options.object.set("thumbnail_width", newWidth);
options.object.set("thumbnail_height", newHeight);
});
};
Update: Found a clone of the original script and archived it just in case: https://web.archive.org/web/20190107225015/https://github.com/eknight7/Anyimg/blob/master/parse/cloud/resize-image-key.js

FireFox : image base64 data using canvas object not working

This is the code i wrote to resize the image in aspect ratio, it works on chrome but not display on firefox, does anyone know what is wrong.
var image = new Image();
image.src = data;
//$(image).load(function () {
var aspectRatio = getAspectRatio(parseFloat($(image).prop('naturalWidth')),
parseFloat($(image).prop('naturalHeight')),
dstWidth,
dstHeight);
var canvas = document.createElement('canvas');
canvas.width = dstWidth;
canvas.height = dstHeight;
var x = (dstWidth - aspectRatio[0]) / 2;
var y = (dstHeight - aspectRatio[1]) / 2;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, x, y, aspectRatio[0], aspectRatio[1]);
return canvas.toDataURL("image/png");
This is work it generated by the canvas.toDataURL
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQEAAADACAYAAAAEL9ZYAAAA1klEQVR4nO3BAQ0AAADCoPdPbQ8HFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwYD7QAB/UrDfgAAAABJRU5ErkJggg==
To make it work you will need to handle the asynchronous nature of image loading. You will have to use a callback mechanism here. The reason it "works" in Chrome is accident; that is the image happen to be in the cache when you try and/or the browser is able to deliver the uncompressed/decoded image before you use the image in the drawImage call.
This will probably not work when it's online for most users so to properly handle loading you can do -
Example:
function getImageUri(url, callback) {
var image = new Image();
image.onload = function () { // handle onload
var image = this; // make sure we using valid image
var aspectRatio = getAspectRatio(parseFloat($(image).prop('naturalWidth')),
parseFloat($(image).prop('naturalHeight')),
dstWidth,
dstHeight);
var canvas = document.createElement('canvas');
canvas.width = dstWidth;
canvas.height = dstHeight;
var x = (dstWidth - aspectRatio[0]) / 2;
var y = (dstHeight - aspectRatio[1]) / 2;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, x, y, aspectRatio[0], aspectRatio[1]);
// use callback to provide the finished data-uri
callback(canvas.toDataURL());
}
image.src = url; // set src last
}
Then use it this way:
getImageUri(myURL, function (uri) {
console.log(uri); // contains the image as data-uri
});

WebGL single frame "screenshot" of webGL

tried searching for something like this, but I've had no luck. I'm trying to open a new tab with a screenshot of the current state of my webgl image. Basically, it's a 3d model, with the ability to change which objects are displayed, the color of those objects, and the background color. Currently, I am using the following:
var screenShot = window.open(renderer.domElement.toDataURL("image/png"), 'DNA_Screen');
This line succeeds in opening a new tab with a current image of my model, but does not display the current background color. It also does not properly display the tab name. Instead, the tab name is always "PNG 1024x768".
Is there a way to change my window.open such that the background color is shown? The proper tab name would be great as well, but the background color is my biggest concern.
If you open the window with no URL you can access it's entire DOM directly from the JavaScript that opened the window.
var w = window.open('', '');
You can then set or add anything you want
w.document.title = "DNA_screen";
w.document.body.style.backgroundColor = "red";
And add the screenshot
var img = new Image();
img.src = someCanvas.toDataURL();
w.document.body.appendChild(img);
Well it is much longer than your one liner but you can change the background color of the rectangle of the context.
printCanvas (renderer.domElement.toDataURL ("image/png"), width, height,
function (url) { window.open (url, '_blank'); });
// from THREEx.screenshot.js
function printCanvas (srcUrl, dstW, dstH, callback)
{
// to compute the width/height while keeping aspect
var cpuScaleAspect = function (maxW, maxH, curW, curH)
{
var ratio = curH / curW;
if (curW >= maxW && ratio <= 1)
{
curW = maxW;
curH = maxW * ratio;
}
else if (curH >= maxH)
{
curH = maxH;
curW = maxH / ratio;
}
return { width: curW, height: curH };
}
// callback once the image is loaded
var onLoad = function ()
{
// init the canvas
var canvas = document.createElement ('canvas');
canvas.width = dstW;
canvas.height = dstH;
var context = canvas.getContext ('2d');
context.fillStyle = "black";
context.fillRect (0, 0, canvas.width, canvas.height);
// scale the image while preserving the aspect
var scaled = cpuScaleAspect (canvas.width, canvas.height, image.width, image.height);
// actually draw the image on canvas
var offsetX = (canvas.width - scaled.width ) / 2;
var offsetY = (canvas.height - scaled.height) / 2;
context.drawImage (image, offsetX, offsetY, scaled.width, scaled.height);
// notify the url to the caller
callback && callback (canvas.toDataURL ("image/png")); // dump the canvas to an URL
}
// Create new Image object
var image = new Image();
image.onload = onLoad;
image.src = srcUrl;
}

HTML5 Pre-resize images before uploading

Here's a noodle scratcher.
Bearing in mind we have HTML5 local storage and xhr v2 and what not. I was wondering if anyone could find a working example or even just give me a yes or no for this question:
Is it possible to Pre-size an image using the new local storage (or whatever), so that a user who does not have a clue about resizing an image can drag their 10mb image into my website, it resize it using the new localstorage and THEN upload it at the smaller size.
I know full well you can do it with Flash, Java applets, active X... The question is if you can do with Javascript + Html5.
Looking forward to the response on this one.
Ta for now.
Yes, use the File API, then you can process the images with the canvas element.
This Mozilla Hacks blog post walks you through most of the process. For reference here's the assembled source code from the blog post:
// from an input element
var filesToUpload = input.files;
var file = filesToUpload[0];
var img = document.createElement("img");
var reader = new FileReader();
reader.onload = function(e) {img.src = e.target.result}
reader.readAsDataURL(file);
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var MAX_WIDTH = 800;
var MAX_HEIGHT = 600;
var width = img.width;
var height = img.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
var dataurl = canvas.toDataURL("image/png");
//Post dataurl to the server with AJAX
I tackled this problem a few years ago and uploaded my solution to github as https://github.com/rossturner/HTML5-ImageUploader
robertc's answer uses the solution proposed in the Mozilla Hacks blog post, however I found this gave really poor image quality when resizing to a scale that was not 2:1 (or a multiple thereof). I started experimenting with different image resizing algorithms, although most ended up being quite slow or else were not great in quality either.
Finally I came up with a solution which I believe executes quickly and has pretty good performance too - as the Mozilla solution of copying from 1 canvas to another works quickly and without loss of image quality at a 2:1 ratio, given a target of x pixels wide and y pixels tall, I use this canvas resizing method until the image is between x and 2 x, and y and 2 y. At this point I then turn to algorithmic image resizing for the final "step" of resizing down to the target size. After trying several different algorithms I settled on bilinear interpolation taken from a blog which is not online anymore but accessible via the Internet Archive, which gives good results, here's the applicable code:
ImageUploader.prototype.scaleImage = function(img, completionCallback) {
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
while (canvas.width >= (2 * this.config.maxWidth)) {
canvas = this.getHalfScaleCanvas(canvas);
}
if (canvas.width > this.config.maxWidth) {
canvas = this.scaleCanvasWithAlgorithm(canvas);
}
var imageData = canvas.toDataURL('image/jpeg', this.config.quality);
this.performUpload(imageData, completionCallback);
};
ImageUploader.prototype.scaleCanvasWithAlgorithm = function(canvas) {
var scaledCanvas = document.createElement('canvas');
var scale = this.config.maxWidth / canvas.width;
scaledCanvas.width = canvas.width * scale;
scaledCanvas.height = canvas.height * scale;
var srcImgData = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
var destImgData = scaledCanvas.getContext('2d').createImageData(scaledCanvas.width, scaledCanvas.height);
this.applyBilinearInterpolation(srcImgData, destImgData, scale);
scaledCanvas.getContext('2d').putImageData(destImgData, 0, 0);
return scaledCanvas;
};
ImageUploader.prototype.getHalfScaleCanvas = function(canvas) {
var halfCanvas = document.createElement('canvas');
halfCanvas.width = canvas.width / 2;
halfCanvas.height = canvas.height / 2;
halfCanvas.getContext('2d').drawImage(canvas, 0, 0, halfCanvas.width, halfCanvas.height);
return halfCanvas;
};
ImageUploader.prototype.applyBilinearInterpolation = function(srcCanvasData, destCanvasData, scale) {
function inner(f00, f10, f01, f11, x, y) {
var un_x = 1.0 - x;
var un_y = 1.0 - y;
return (f00 * un_x * un_y + f10 * x * un_y + f01 * un_x * y + f11 * x * y);
}
var i, j;
var iyv, iy0, iy1, ixv, ix0, ix1;
var idxD, idxS00, idxS10, idxS01, idxS11;
var dx, dy;
var r, g, b, a;
for (i = 0; i < destCanvasData.height; ++i) {
iyv = i / scale;
iy0 = Math.floor(iyv);
// Math.ceil can go over bounds
iy1 = (Math.ceil(iyv) > (srcCanvasData.height - 1) ? (srcCanvasData.height - 1) : Math.ceil(iyv));
for (j = 0; j < destCanvasData.width; ++j) {
ixv = j / scale;
ix0 = Math.floor(ixv);
// Math.ceil can go over bounds
ix1 = (Math.ceil(ixv) > (srcCanvasData.width - 1) ? (srcCanvasData.width - 1) : Math.ceil(ixv));
idxD = (j + destCanvasData.width * i) * 4;
// matrix to vector indices
idxS00 = (ix0 + srcCanvasData.width * iy0) * 4;
idxS10 = (ix1 + srcCanvasData.width * iy0) * 4;
idxS01 = (ix0 + srcCanvasData.width * iy1) * 4;
idxS11 = (ix1 + srcCanvasData.width * iy1) * 4;
// overall coordinates to unit square
dx = ixv - ix0;
dy = iyv - iy0;
// I let the r, g, b, a on purpose for debugging
r = inner(srcCanvasData.data[idxS00], srcCanvasData.data[idxS10], srcCanvasData.data[idxS01], srcCanvasData.data[idxS11], dx, dy);
destCanvasData.data[idxD] = r;
g = inner(srcCanvasData.data[idxS00 + 1], srcCanvasData.data[idxS10 + 1], srcCanvasData.data[idxS01 + 1], srcCanvasData.data[idxS11 + 1], dx, dy);
destCanvasData.data[idxD + 1] = g;
b = inner(srcCanvasData.data[idxS00 + 2], srcCanvasData.data[idxS10 + 2], srcCanvasData.data[idxS01 + 2], srcCanvasData.data[idxS11 + 2], dx, dy);
destCanvasData.data[idxD + 2] = b;
a = inner(srcCanvasData.data[idxS00 + 3], srcCanvasData.data[idxS10 + 3], srcCanvasData.data[idxS01 + 3], srcCanvasData.data[idxS11 + 3], dx, dy);
destCanvasData.data[idxD + 3] = a;
}
}
};
This scales an image down to a width of config.maxWidth, maintaining the original aspect ratio. At the time of development this worked on iPad/iPhone Safari in addition to major desktop browsers (IE9+, Firefox, Chrome) so I expect it will still be compatible given the broader uptake of HTML5 today. Note that the canvas.toDataURL() call takes a mime type and image quality which will allow you to control the quality and output file format (potentially different to input if you wish).
The only point this doesn't cover is maintaining the orientation information, without knowledge of this metadata the image is resized and saved as-is, losing any metadata within the image for orientation meaning that images taken on a tablet device "upside down" were rendered as such, although they would have been flipped in the device's camera viewfinder. If this is a concern, this blog post has a good guide and code examples on how to accomplish this, which I'm sure could be integrated to the above code.
Correction to above:
<img src="" id="image">
<input id="input" type="file" onchange="handleFiles()">
<script>
function handleFiles()
{
var filesToUpload = document.getElementById('input').files;
var file = filesToUpload[0];
// Create an image
var img = document.createElement("img");
// Create a file reader
var reader = new FileReader();
// Set the image once loaded into file reader
reader.onload = function(e)
{
img.src = e.target.result;
var canvas = document.createElement("canvas");
//var canvas = $("<canvas>", {"id":"testing"})[0];
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var MAX_WIDTH = 400;
var MAX_HEIGHT = 300;
var width = img.width;
var height = img.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
var dataurl = canvas.toDataURL("image/png");
document.getElementById('image').src = dataurl;
}
// Load files into file reader
reader.readAsDataURL(file);
// Post the data
/*
var fd = new FormData();
fd.append("name", "some_filename.jpg");
fd.append("image", dataurl);
fd.append("info", "lah_de_dah");
*/
}</script>
Modification to the answer by Justin that works for me:
Added img.onload
Expand the POST request with a real example
function handleFiles()
{
var dataurl = null;
var filesToUpload = document.getElementById('photo').files;
var file = filesToUpload[0];
// Create an image
var img = document.createElement("img");
// Create a file reader
var reader = new FileReader();
// Set the image once loaded into file reader
reader.onload = function(e)
{
img.src = e.target.result;
img.onload = function () {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var MAX_WIDTH = 800;
var MAX_HEIGHT = 600;
var width = img.width;
var height = img.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
dataurl = canvas.toDataURL("image/jpeg");
// Post the data
var fd = new FormData();
fd.append("name", "some_filename.jpg");
fd.append("image", dataurl);
fd.append("info", "lah_de_dah");
$.ajax({
url: '/ajax_photo',
data: fd,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(data){
$('#form_photo')[0].reset();
location.reload();
}
});
} // img.onload
}
// Load files into file reader
reader.readAsDataURL(file);
}
If you don't want to reinvent the wheel you may try plupload.com
Typescript
async resizeImg(file: Blob): Promise<Blob> {
let img = document.createElement("img");
img.src = await new Promise<any>(resolve => {
let reader = new FileReader();
reader.onload = (e: any) => resolve(e.target.result);
reader.readAsDataURL(file);
});
await new Promise(resolve => img.onload = resolve)
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
let MAX_WIDTH = 1000;
let MAX_HEIGHT = 1000;
let width = img.naturalWidth;
let height = img.naturalHeight;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
let result = await new Promise<Blob>(resolve => { canvas.toBlob(resolve, 'image/jpeg', 0.95); });
return result;
}
The accepted answer works great, but the resize logic ignores the case in which the image is larger than the maximum in only one of the axes (for example, height > maxHeight but width <= maxWidth).
I think the following code takes care of all cases in a more straight-forward and functional way (ignore the typescript type annotations if using plain javascript):
private scaleDownSize(width: number, height: number, maxWidth: number, maxHeight: number): {width: number, height: number} {
if (width <= maxWidth && height <= maxHeight)
return { width, height };
else if (width / maxWidth > height / maxHeight)
return { width: maxWidth, height: height * maxWidth / width};
else
return { width: width * maxHeight / height, height: maxHeight };
}
fd.append("image", dataurl);
This will not work. On PHP side you can not save file with this.
Use this code instead:
var blobBin = atob(dataurl.split(',')[1]);
var array = [];
for(var i = 0; i < blobBin.length; i++) {
array.push(blobBin.charCodeAt(i));
}
var file = new Blob([new Uint8Array(array)], {type: 'image/png', name: "avatar.png"});
fd.append("image", file); // blob file
Resizing images in a canvas element is generally bad idea since it uses the cheapest box interpolation. The resulting image noticeable degrades in quality. I'd recommend using http://nodeca.github.io/pica/demo/ which can perform Lanczos transformation instead. The demo page above shows difference between canvas and Lanczos approaches.
It also uses web workers for resizing images in parallel. There is also WEBGL implementation.
There are some online image resizers that use pica for doing the job, like https://myimageresizer.com
You can use dropzone.js if you want to use simple and easy upload manager with resizing before upload functions.
It has builtin resize functions, but you can provide your own if you want.

possible to use html images like canvas with getImageData / putImageData?

I'd like to know if there is a way to dynamically modify/access the data contained in html images just as if they were an html5 canvas element. With canvas, you can in javascript access the raw pixel data with getImageData() and putImageData(), but I have thus far been not able to figure out how to do this with images.
// 1) Create a canvas, either on the page or simply in code
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// 2) Copy your image data into the canvas
var myImgElement = document.getElementById('foo');
ctx.drawImage( myImgElement, 0, 0 );
// 3) Read your image data
var w = myImgElement.width, h=myImgElement.height;
var imgdata = ctx.getImageData(0,0,w,h);
var rgba = imgdata.data;
// 4) Read or manipulate the rgba as you wish
for (var px=0,ct=w*h*4;px<ct;px+=4){
var r = rgba[px ];
var g = rgba[px+1];
var b = rgba[px+2];
var a = rgba[px+3];
}
// 5) Update the context with newly-modified data
ctx.putImageData(imgdata,0,0);
// 6) Draw the image data elsewhere, if you wish
someOtherContext.drawImage( ctx.canvas, 0, 0 );
Note that step 2 can also be brought in from an image loaded directly into script, not on the page:
// 2b) Load an image from which to get data
var img = new Image;
img.onload = function(){
ctx.drawImage( img, 0, 0 );
// ...and then steps 3 and on
};
img.src = "/images/foo.png"; // set this *after* onload
You could draw the image to a canvas element with drawImage(), and then get the pixel data from the canvas.
After having some issues with this code, I want to add one or two things to Phrogz's answer :
// 1) Create a canvas, either on the page or simply in code
var myImgElement = document.getElementById('foo');
var w = myImgElement.width, h=myImgElement.height; // New : you need to set the canvas size if you don't want bug with images that makes more than 300*150
var canvas = document.createElement('canvas');
canvas.height = h;
canvas.width = w;
var ctx = canvas.getContext('2d');
// 2) Copy your image data into the canvas
ctx.drawImage( myImgElement, 0, 0, w, h ); // Just in case...
// 3) Read your image data
var imgdata = ctx.getImageData(0,0,w,h);
var rgba = imgdata.data;
// And then continue as in the other code !
that worked for me (IE10x64,Chromex64 on win7, chromium arm linux, ...seems to bug with firefox 20 arm linux but not sure ... to re-test)
--html--
<canvas id="myCanvas" width="600" height="300"></canvas>
<canvas id="myCanvasOffscreen" width="1" height="1"></canvas>
-- js --
// width & height can be used to scale image !!!
function getImageAsImageData(url, width, height, callack) {
var canvas = document.getElementById('myCanvasOffscreen');
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(imageObj, 0, 0, width, height);
imgData = context.getImageData(0,0,width, height);
canvas.width = 1;
canvas.height = 1;
callack( imgData );
};
imageObj.src = url;
}
-- then --
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imgData;
getImageAsImageData('central_closed.png', IMG_WIDTH, IMG_HEIGHT,
function(imgData) {
// do what you want with imgData.data (rgba array)
// ex. colorize( imgData, 25, 70, 0);
ctx.putImageData(imgData,0,0);
}
);
you first want to draw a pic on the canvas and then get the imageData from the canvas ,it is a wrong way,because the js think it is a "Cross-domain access",but the getIamgeData method don't allow the "Cross-domain access" to an image.you can hava a try by put the in the root place and access it by "localhost" .
Im not sure if it is possible, but you can try requesting pixel information from PHP, if GD library it will be an easy task, but surely will be slower. Since you didnt specified application so I will suggest checking SVG for this task if they can be vector images than you will be able to query or modify the image.
Directly work on IMG element is also valid:
var image = document.createElement('img'),w,h ;
image.src = "img/test.jpg" ;
$(image).one('load',function(){
w = image.naturalWidth ;
h = image.naturalHeight ;
var cnv = document.createElement('canvas') ;
$(cnv).attr("width",w) ;
$(cnv).attr("height",h) ;
var ctx = cnv.getContext('2d') ;
ctx.drawImage(image,0,0) ;
var imgdata = ctx.getImageData(0,0,w,h) ;
var rgba = imgdata.data ;
for (var px=0,ct=w*h*4;px<ct;px+=4){
var r = rgba[px+0] ;
var g = rgba[px+1] ;
var b = rgba[px+2] ;
var a = rgba[px+3] ;
// Do something
rgba[px+0] = r ;
rgba[px+1] = g ;
rgba[px+2] = b ;
rgba[px+3] = a ;
}
ctx.putImageData(imgdata,0,0) ;
$("body").append(cnv) ;
}) ;

Resources