load image inside JavaScript function - javascript

i have function to get image pixels color
function getImage(imgsrc){
var img = $( "<img>", {
src: imgsrc
});
var imageMap = new Object();
img.load(function() {
var canvas = $('<canvas/>')[0].getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
canvas.drawImage(this, 0, 0, this.width, this.height);
for(var i = 0;i < this.width;i++){
imageMap[i] = new Object();
for(var j = 0;j < this.width;j++){
var pixelData = canvas.getImageData(i, j, 1, 1).data;
imageMap[i][j] = rgbToHex(pixelData[0],pixelData[1],pixelData[2]);
}
}
console.log(imageMap[40][40]);
});
console.log(imageMap[40][40]);
return imageMap;
}
but it return undefined(it print 2nd console.log first)
what's wrong?
thx.

Your function is returning undefined because load is asynchronous. getImage is trying to return something before load finished loading.
You'll need to pass a callback to execute when the image has loaded, to getImage:
function getImage(imgsrc, callback){
var img = $( "<img>", {
src: imgsrc
});
var imageMap = new Object();
img.load(function() {
var canvas = $('<canvas/>')[0].getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
canvas.drawImage(this, 0, 0, this.width, this.height);
for(var i = 0;i < this.width;i++){
imageMap[i] = new Object();
for(var j = 0;j < this.width;j++){
var pixelData = canvas.getImageData(i, j, 1, 1).data;
imageMap[i][j] = rgbToHex(pixelData[0],pixelData[1],pixelData[2]);
}
}
console.log(imageMap[40][40]);
callback(imageMap)
});
}
Then you just call the function like this:
getImage("http://some.src.jpg", function(imageMap){
// Do stuff with imageMap here;
});
Of course, you can also define the callback elsewhere:
var myCallback = function(imageMap){
// Do stuff with imageMap here;
};
getImage("http://some.src.jpg", myCallback);

Now that promises starts to get [have] wide support you can do this instead:
// Define a common load function:
function loadImage(url) {
return new Promise(function(resolve, reject) {
var img = new Image;
img.onload = function() { resolve(this) };
img.onerror = img.onabort = function() { reject("Error loading image") };
img.src = url;
})
}
// Usage:
loadImage("https://i.stack.imgur.com/ynBVu.gif").then(function(image) {
// Use the `image` here
document.body.appendChild(image);
})
The promise will take the callback, states etc. internally. IE will get support in next version (there exists polyfill for it).

jQuery.load() is asynchronous, meaning the code will continue while it goes of working away.
If you want to process the imagemap, one option could be to pass a callback you execute ones the imagemap is populated, similar to:
function yourCallback(imageMap){
// ...process imageMap;
}
function getImage(imgsrc, yourCallback) {
var img = $("<img>", {
src: imgsrc
});
var imageMap = new Object();
img.load(function () {
var canvas = $('<canvas/>')[0].getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
canvas.drawImage(this, 0, 0, this.width, this.height);
for (var i = 0; i < this.width; i++) {
imageMap[i] = new Object();
for (var j = 0; j < this.width; j++) {
var pixelData = canvas.getImageData(i, j, 1, 1).data;
imageMap[i][j] = rgbToHex(pixelData[0], pixelData[1], pixelData[2]);
}
}
yourCallback(imageMap);
});
}
getImage(imgsrc,yourCallback);

Related

Can't convert base64Image to ImageData object

I'm currently trying to convert a base64Encoded string (representation of an image) into an ImageData object in Javascript. However, this comes back with an error:
Uncaught InvalidStateError: Failed to construct 'ImageData': The input data length is not a multiple of 4.
The encoded image is 940 x 740
What am I missing? Any help appreciated
JSFiddle link with full code
function _base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}
const base64String = ""; //truncated because stackoverflow question too long.
const arrBuffer = _base64ToArrayBuffer (base64String );
var array = new Uint8ClampedArray(arrBuffer);
console.log(array);
var image = new ImageData(array, 900, 740);
console.log(image);
You can get the ImageData from a Canvas
function _base64ToImageData(buffer, width, height) {
return new Promise(resolve => {
var image = new Image();
image.addEventListener('load', function (e) {
var canvasElement = document.createElement('canvas');
canvasElement.width = width;
canvasElement.height = height;
var context = canvasElement.getContext('2d');
context.drawImage(e.target, 0, 0, width, height);
resolve(context.getImageData(0, 0, width, height));
});
image.src = 'data:image/png;base64,' + buffer;
document.body.appendChild(image);
setTimeout(() => {
document.body.removeChild(image);
}, 1);
});
}
window.addEventListener('load', () => {
_base64ToImageData(base64String, 2056, 1236).then(data => {
console.log(data);
});
});

how to draw multiple images in canvas

I need to draw images in their own canvas. What am I doing wrong?
If I delete the "for loop" the code works just fine (e.g: set var i=0, then img[0] is drawn in canv[0]).
window.onload = function() {
var canv = [];
var ctx = [];
var img = [];
var info = [{"url":"1.jpg"},{"url":"2.jpg"},{"url":"3.jpg"}];
for(i=0; i< 3; i++){
canv[i] = document.createElement('canvas');
canv[i].width = 460;
canv[i].height = 620;
canv[i].style.border = "1px solid";
document.body.appendChild(canv[i]);
img[i] = new Image();
img[i].src = info[i].url;
ctx[i] = canv[i].getContext("2d");
img[i].onload = function(){
ctx[i].drawImage(img[i], 10, 70, 440,440);
}
}
}
Found the solution, just in case somebody has the same problem:
(function (_i) {
img[_i] = new Image();
img[_i].src = info[_i].url;
img[_i].onload = function () {
ctx[_i].drawImage(img[_i], 10, 70, 440,440);
};
})(i);

convert image from cordova imagepicker to base64

I am using the following snippet for my conversion operation (images from cordova image picker to base64 and store them in an array) but due to the async behavior, it is assigning same string as of the first image to all images. I tried while loop but then, the app crashed. Any suggestion how can I solve this problem.
Edit: results[ 0 ] is defined but all other results[ i ] are 'undefined', hence image source remains same for all iteration
window.imagePicker.getPictures(
function(results) {
for (var i = 0; i < results.length; i++) {
var img = new Image();
img.crossOrigin = 'Anonymous';
img.src = results[i];
img.onload = function(){
var canvas = <HTMLCanvasElement>document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage( img, 0, 0);
var dataURL = canvas.toDataURL('image/jpeg').slice(23);
Attachments.push(dataURL); // array for storing base64 equivalent of all images
canvas = null;
};
}
img.src = results[ i ] starts reading the file at results[ i ] async, so when loop continues for i=1, results[ 1 ] is undefined because the file system is still reading for results[0]. Hence all iteration returns dataURL of the first image.
To avoid it use callbacks which solve this problem with the concept of closures.
window.imagePicker.getPictures(
function(results) {
console.log(results);
for (var i = 0; i < results.length; i++) {
parent.tobase64(results[i],function(dataURL){
parent.email_data.Attachments.push(dataURL);
});
}
}, function (error) {
console.log('Error: ' + error);
}
}
tobase64(file,callback){
var parent=this;
var img = new Image();
img.crossOrigin = 'Anonymous';
img.src = file;
img.onload = function(){
var canvas = <HTMLCanvasElement>document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage( img, 0, 0);
var dataURL = canvas.toDataURL('image/jpeg').slice(23);
canvas = null;
callback.call(this,dataURL);
}
}

Images not loaded on first call in html canvas

I want to load the sorted array images in canvas in a same sequence, for that i created a load function and I am calling it from there.
Every thing works fine, but the problem is that it does not draw the images on canvas at first call.
Here is a fiddle
With my current code, it does not show you an image , but when you run it again, it will create and show the canvas image.
I am facing the same issue in my local host where i load the images with ajax.
var ImagesArray = new Array("http://s4.postimg.org/dw8aujkkd/slim_Fit.png",
"http://s27.postimg.org/l6mq3hboz/outer_collar1.png",
"http://s22.postimg.org/e7alkm44x/inner_collar11.png",
"http://s12.postimg.org/h4kgdjn4t/maincolar.png",
"http://s13.postimg.org/edabwknfr/outer_fastening1.png");
//alert(ImagesArray.length);
MainMethods(ImagesArray);
function MainMethods(responseImages) {
//alert(responseImages.length);
var canvas = document.getElementById("product-image");
canvas.height = ($(window).height()) - 120;
canvas.width = canvas.height * 0.75;
var heightscreen = ($(window).height()) - 120;
var canvasheight = heightscreen;
var canvaswidth = canvas.height * 0.75;
canvaswidthdiv4 = 0;
var widthNeeded = canvasheight * 0.75;
var context = canvas.getContext('2d');
var returnedImages = loadImages(responseImages);
for (i = 0; i < returnedImages.length; i++) {
console.log(returnedImages[i]);
context.drawImage(returnedImages[i], canvaswidthdiv4, 55, widthNeeded, canvasheight);
}
// Image loading global variables
var loadcount = 0;
var loadtotal = 0;
var preloaded = false;
// Load images
function loadImages(imagefiles) {
// Initialize variables
loadcount = 0;
loadtotal = imagefiles.length;
preloaded = false;
// Load the images
var loadedimages = [];
for (var i = 0; i < imagefiles.length; i++) {
// Create the image object
var image = new Image();
// Add onload event handler
image.onload = function() {
loadcount++;
if (loadcount == loadtotal) {
// Done loading
preloaded = true;
}
};
// Set the source url of the image
image.src = imagefiles[i];
// Save to the image array
loadedimages[i] = image;
}
// Return an array of images
return loadedimages;
}
}
<canvas id="product-image" width="700" height="823"></canvas>
The problem is that your images are not loaded yet when you try to draw it.
Wrap the drawImage part in a function and call it when preloaded becomes true, here your boolean does nothing.
I refactored your code a little bit, you also had an issue with loadtotal being reset to 0 after your call to loadImage() function. So loadcountwon't ever be === loadtotal and your useless preloaded never switched to true.
var ImagesArray = new Array ("http://s4.postimg.org/dw8aujkkd/slim_Fit.png",
"http://s27.postimg.org/l6mq3hboz/outer_collar1.png",
"http://s22.postimg.org/e7alkm44x/inner_collar11.png",
"http://s12.postimg.org/h4kgdjn4t/maincolar.png",
"http://s13.postimg.org/edabwknfr/outer_fastening1.png");
function MainMethods(responseImages) {
var canvas = document.getElementById("product-image");
canvas.height = ($(window).height()) - 120;
canvas.width = canvas.height * 0.75;
var canvasheight = ($(window).height()) - 120;
var canvaswidth = canvas.height * 0.75;
canvaswidthdiv4 = 0;
var widthNeeded = canvasheight * 0.75;
var context = canvas.getContext('2d');
// an other error, if you set loadtotal to 0 after calling loadImages(), it will be set to 0 when images are loaded.
// and loadcount won't ever be === loadtotal.
var loadcount;
var loadtotal;
function loadImages(imagefiles) {
loadcount = 0;
loadtotal = imagefiles.length;
var loadedimages = [];
for (var i=0; i<imagefiles.length; i++) {
var image = new Image();
image.onload = function () {
loadcount++;
console.log(loadcount+'/'+ loadtotal);
if (loadcount == loadtotal) {
// done loading all the images
draw(loadedimages);
}
};
image.src = imagefiles[i];
loadedimages[i] = image;
}
}
// Wrap your drawing in a function that will be called asynchronously
function draw(loadedimages){
for (var i = 0; i < loadedimages.length; i++) {
console.log(loadedimages[i]);
context.drawImage(loadedimages[i], canvaswidthdiv4, 55, widthNeeded, canvasheight);
}
}
// Once all your variables are set, you can load the images
loadImages(responseImages);
}
MainMethods(ImagesArray);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="product-image"></canvas>
Since it appears to be a browser specific issue, here's a hack to get it to work:
Replace : MainMethods(ImagesArray);
with : setTimeout(function () { MainMethods(ImagesArray); }, 0);
With timeout set to 0 ms, it would virtually execute instantaneously.
JsFiddle : http://jsfiddle.net/wdfrrayh/3/
Tested on IE, FF, Chrome
image src needs to be called after drawImage which i was doing before I have updated my function which is working fine,
below is working code for this
function loadImages(sources, callback, jsondata) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var canvas = document.getElementById('product-image');
canvas.height = (jQuery(window).height()) -120;
canvas.width = canvas.height * 0.75;
var heightscreen = (jQuery(window).height()) -120;
var canvasheight = heightscreen;
var canvaswidth = canvas.height * 0.75;
canvaswidthdiv4 = 0;
var widthNeeded = canvasheight * 0.75;
var context = canvas.getContext('2d');
var sources = new Array ("http://s4.postimg.org/dw8aujkkd/slim_Fit.png",
"http://s27.postimg.org/l6mq3hboz/outer_collar1.png",
"http://s22.postimg.org/e7alkm44x/inner_collar11.png",
"http://s12.postimg.org/h4kgdjn4t/maincolar.png",
"http://s13.postimg.org/edabwknfr/outer_fastening1.png");
loadImages(sources, function(images) {
for(var i=0; i < sources.length; i++){
context.drawImage(images[i], canvaswidthdiv4, 55,widthNeeded,canvasheight);
}
});

Converting images to base64 within a loop before adding to jspdf - javascript

I have researched issues with the base64 conversion and jspdf function quite a bit. ( PS this is my first question on stackoverflow, please bare with any rookie mistakes ).
All seems to work fine with the below code except that the pdf is generated and saved before the loop where the images are converted to base64 and placed to the document is finished. I added a couple alerts to check timing. Would the solution be to check when the loop is finished, the images placed before continuing with the pdf function? if so, how? please help.
$(document).ready(function(){
$("a#getdoc").click(function(){
var doc = new jsPDF('landscape, in, legal');
var myimages = 'img1.jpg|img2.jpg|img3.png';
var myimgarray = myimages.split('|');
function convertImgToBase64(url, callback, outputFormat){
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
return canvas.toDataURL("image/jpeg");
var dataURL = canvas.toDataURL("image/jpeg");
callback(dataURL);
canvas = null;
}
for(var i = 0; i < myimgarray.length; i++)
{
icount.count = i;
var img = new Image();
alert(checkoutimgarray);
img.src = '/Portals/0/repair-images/' + myimgarray[i];
img.onload = function(){
newData = convertImgToBase64(img);
alert(newData);
doc.addImage(newData, 'JPEG', (icount * 100), 10, 70, 15); // would doc be undefined here? out of scope?
};
}
doc.setFontSize(20);
doc.text(100, 20, "This is a test to see if images will show");
doc.save('My_file.pdf');
});
});
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
function convertImgToBase64(img, outputFormat){
// clear canvas
canvas.width = img.width;
canvas.height = img.height;
// draw image
ctx.drawImage(img, 0, 0);
// get data url of output format or defaults to jpeg if not set
return canvas.toDataURL("image/" + (outputFormat || "jpeg"));
}
var images = [];
for(var i = 0; i < myimgarray.length; i++) {
var img = new Image();
img.onload = function() {
images.push({
base64: convertImgToBase64(this),
width: this.width,
height: this.height
});
// all images loaded
if(images.length === myimgarray.length) {
for(var j = 0; j < images.length; j++) {
doc.addImage(images[j].base64, 'JPEG', (j * 100), 10, 70, 15);
}
doc.setFontSize(20);
doc.text(100, 20, "This is a test to see if images will show");
doc.save('My_file.pdf');
}
};
img.src = '/Portals/0/repair-images/' + myimgarray[i];
}

Categories