I have been trying to make a script that compares two images in HTML5 and Javascript. But for some odd reason, it always returns that the images are completely the same.
And when looking at what the problem could be, I found out that every data value of every pixel returned, for some odd reason, "0".
So, any idea of what I have done wrong? :)
For some reason I feel like it's something very simple, but I just learned about the canvas element, so yeah.
This is my code:
function compareImg() {
var c1 = document.getElementById("c");
var ctx1 = c1.getContext("2d");
var c2 = document.getElementById("c2");
var ctx2 = c2.getContext("2d");
var match = 0;
var img1 = new Image();
img1.src = "cat.jpg";
img1.onload = function() {
ctx1.drawImage(img1, 0, 0);
}
var img2 = new Image();
img2.src = "bird.jpg";
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
}
for(var x = 0; x<c1.width; x++) { // For each x value
for(var y = 0; y<c1.height; y++) { // For each y value
var data1 = ctx1.getImageData(x, y, 1, 1);
var data2 = ctx2.getImageData(x, y, 1, 1);
if (data1.data[0] == data2.data[0] && data1.data[1] == data2.data[1] && data1.data[2] == data2.data[2]) {
match++;
}
}
}
var pixels = c1.width*c1.height;
match = match/pixels*100;
document.getElementById("match").innerHTML = match + "%";
}
You are not waiting until your images have loaded and drawn before performing your comparison. Try this:
var img = new Image;
img.onload = function(){
ctx1.drawImage(img,0,0);
var img = new Image;
img.onload = function(){
ctx2.drawImage(img,0,0);
// diff them here
};
img.src = 'cat.jpg';
};
img.src = 'cat.jpg';
As shown above, you should always set your src after your onload.
I suspect that the problem is that your image data is probably not ready at the point you try to use it for the canvas. If you defer that code to the onload handlers, that will (probably) help:
var img1 = new Image(), count = 2;
img1.src = "cat.jpg";
img1.onload = function() {
ctx1.drawImage(img1, 0, 0);
checkReadiness();
}
var img2 = new Image();
img2.src = "bird.jpg";
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
checkReadiness();
}
function checkReadiness() {
if (--count !== 0) return;
for(var x = 0; x<c1.width; x++) { // For each x value
for(var y = 0; y<c1.height; y++) { // For each y value
var data1 = ctx1.getImageData(x, y, 1, 1);
var data2 = ctx2.getImageData(x, y, 1, 1);
if (data1.data[0] == data2.data[0] && data1.data[1] == data2.data[1] && data1.data[2] == data2.data[2]) {
match++;
}
}
}
var pixels = c1.width*c1.height;
match = match/pixels*100;
document.getElementById("match").innerHTML = match + "%";
}
All I did was add a function wrapper around your code. That function checks the image count variable I added, and only when it's zero (i.e., only after both images have loaded) will it do the work.
(This may be superstition, but I always assign the "onload" handler before I set the "src" attribute. I have this idea that, perhaps only in the past, browsers might fail to run the handler if the image is already in the cache.)
Now another thing: you probably should just get the image data once, and then iterate over the returned data. Calling "getImageData()" for every single pixel is going to be a lot of work for the browser to do.
Related
I was trying to create an array of image objects and load the images after the windows has loaded in canvas.
Here is my code:
var canvasObj = document.getElementById('myCanvas');
var ctx = canvasObj.getContext('2d');
var imgsrcs = ["1.png", "2.png", "3.png"];
var imgs = [];
for(var i=0; i<imgsrcs.length; i++){
imgs[i] = new Image();
imgs[i].onload = function () {
ctx.drawImage(imgs[i], xb,yb);
}
imgs[i].src = imgsrcs[i];
}
however, I am getting this error in console:
TypeError: Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap.
ctx.drawImage(imgs[i], xb,yb);
What am I doing wrong?
Thanks in advance
By the time onload event is invoked, for-loop is iterated hence value of i is length+1, as there is no element at index length+1, undefined is passed as first argument for ctx.drawImage
Use this context in the drawImage method where this === Image-Object
var canvasObj = document.getElementById('myCanvas');
var ctx = canvasObj.getContext('2d');
var imgsrcs = ["http://i.imgur.com/gwlPu.jpg", "http://i.imgur.com/PWSOy.jpg", "http://i.imgur.com/6l6v2.png"];
var xb = 0,
yb = 0;
var imgs = [];
for (var i = 0; i < imgsrcs.length; i++) {
imgs[i] = new Image();
imgs[i].onload = function() {
console.log(imgs[i]); //Check this value
ctx.drawImage(this, xb, yb);
xb += 50;
yb += 50;
}
imgs[i].src = imgsrcs[i];
}
<canvas id="myCanvas"></canvas>
Before your read this, my first account was blocked because i asked bad questions.. So please dont vote negative but say what i am doing wrong
Sooo I have this script:
var img1 = new Image()
img1.src="image1.jpg"
var img2 = new Image()
img2.src="image2.jpg"
var img3 = new Image()
img3.src="image3.jpg"
function Canvas() {
var ctx = document.getElementById('slider').getContext('2d');
var pic=1
function slider() {
this.draw = function() {
if(pic<4){pic++}
else{
pic=1
}
var img = "img"+pic
ctx.drawImage(img,0,0,ctx.canvas.width,ctx.canvas.height)
}
}
var slider = new slider();
function draw() {
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.save();
//draw
slider.draw();
//draw
ctx.restore();
}
var animateInterval = setInterval(draw,100)
}
window.addEventListener('load', function(event) {
Canvas();
});
I am trying to draw the image 1 or 2 or 3 on my canvas. But I also have the var pic wich has the number. So i tried ctx.drawimage(img+pic,0,0) or var img = "img"+pic and draw it then. But it doesnt work for me.
I hope you accept my question and can answer it, THNX
Use an array instead
var img = [];
/* you should use a for loop here */
for (var i = 0; i < 3; i++) {
img[i] = new Image();
img[i].src = "image" + (i+1) ".jpg";
}
and later you refer the right image with img[pic]. Be only sure to use an index between 0 and img.length - 1
Don't use separate variables, use an array.
var images = [ new Image(), new Image(), new Image() ];
for (var i = 0; i < images.length; i++) {
images[i].src = "image" + (i+1) + ".jpg";
}
Then refer to the array in the Slider() function:
var pic = 0;
function slider() {
this.draw = function() {
pic = (pic + 1) % images.length;
var img = images[pic];
ctx.drawImage(img,0,0,ctx.canvas.width,ctx.canvas.height)
}
}
The error seems to be that you refer to the string "img1" and not the object img1. Try use an array instead.
Set the following:
...
var pic=0;
var img=[img1,img2,img3];
function slider() {
this.draw = function() {
if(pic<3){pic++}
else{
pic=0;
}
ctx.drawImage(img[pic],0,0,ctx.canvas.width,ctx.canvas.height)
}
}
...
Currently having some issues with drawImage();. Namely it wont actually draw. I tried it out with fillRect(); and it worked aswell as putting the drawImage(); inside the the onload function aswell (which worked).
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 400;
document.body.appendChild(canvas);
var tileArray = [
[0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,1,1,1,1,0,0,1,0,0,0],
[0,0,0,1,1,1,1,0,1,1,1,0,0],
[0,0,0,1,1,1,0,0,0,0,1,1,0],
[0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,1,1,1,0,0,0,0,0,0,0],
[0,0,0,0,1,1,1,0,0,0,0,0,0],
[0,0,0,0,1,1,1,0,0,0,0,0,0],
[0,0,0,0,0,1,1,1,0,0,0,0,0],
[0,0,0,0,0,0,1,1,0,0,0,0,0]
];
var grassReady = false;
var grass = new Image();
grass.onload = function() {
grassReady = true;
};
grass.src = "images/grass.png";
var sandReady = false;
var sand = new Image();
sand.onload = function() {
sandReady = true;
};
sand.src = "images/sand.png";
var posX = 0;
var posY = 0;
if(grassReady) {
ctx.drawImage(grass, posX, posY);
}
Any pointers as to why this is would be greatly appreciated and I appologize in advance if messed up the code section in anyway. I went through other similar posts and coulden't find a solution that seemed to work.
As #Suman Bogati correctly says, you must wait for your images to load before using them in drawImage.
A Demo: http://jsfiddle.net/m1erickson/jGPGj/
Here's an image loader that preloads all images and then calls the start() function where you can use drawImage because all the images are fully loaded.
var imageURLs=[]; // put the paths to your images here
var imagesOK=0;
var imgs=[];
imageURLs.push("images/grass.png");
imageURLs.push("images/sand.png");
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
// grass.png is in imgs[0]
// sand.png is in imgs[1]
}
This statement ctx.drawImage(); should be inside the grass.onload = function() {} function, something like
grass.onload = function() {
ctx.drawImage(grass, posX, posY);
}
If you define drawImage() outside the grass.onload() function, then that statment would executed first, so at that point grassReady is false, So the condition is not satisfied.
Bascially it's related to asynchronous concept.
Your code is running into order
1) First
var grassReady = false;
if(grassReady) {
//grassReady is false, this condition is not satisfied
ctx.drawImage(grass, posX, posY);
}
2) Second
grass.onload = function() {
grassReady = true;
};
The problem I have is that when the page is loaded sometimes it displays all the images, sometimes just 2 images and sometimes all. I don´t know why this is happening.
Any ideas?
$('#banners .box img').each(function(index){
var randval = (index+1)*100;
var _this = $(this)
setTimeout(function(){
_this.attr('id' , 'banner' + index);
to_canvas('banner' + index, 300, 223);
}, randval)
});
to_canvas function:
function to_canvas(im,w,h){
var canvas;
var imageBottom;
var im_w = w;
var im_h = h;
var imgData;
var pix;
var pixcount = 0;
var paintrow = 0;
var multiplyColor = [70, 116, 145];
var x_offset = Math.floor(($('#'+im).attr('width') - im_w)/2);
var y_offset = Math.floor(($('#'+im).attr('height') - im_h)/2);
imageBottom = document.getElementById(im);
canvas = document.createElement('canvas');
canvas.width = im_w;
canvas.height = im_h;
imageBottom.parentNode.insertBefore(canvas, imageBottom);
ctx = canvas.getContext('2d');
ctx.drawImage(imageBottom, -x_offset , -y_offset);
imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
pix = imgData.data;
for (var i = 0 ; i < pix.length; i += 4) {
if(pixcount > im_w - (im_h - paintrow) ){
pix[i ] = multiply(multiplyColor[0], pix[i ]);
pix[i+1] = multiply(multiplyColor[1], pix[i+1]);
pix[i+2] = multiply(multiplyColor[2], pix[i+2]);
}
if(pixcount < im_w-1){
pixcount++;
}else{
paintrow++;
pixcount = 0;
}
}
ctx.putImageData(imgData, 0, 0);
$('#'+im).remove();
}
function multiply(topValue, bottomValue){
return topValue * bottomValue / 255;
}
I'm using the canvas function to add a triangle with multiply effect (like Photoshop).
Make sure the images are loaded :
$('#banners .box img').each(function(index, elem){
var randval = (index+1)*100,
self = this,
img = new Image(); // create image object
img.onload = function() { // wait until it's loaded
setTimeout(function(){
self.id = 'banner' + index;
to_canvas('banner' + index, 300, 223);
}, randval)
}
img.src = elem.src; // set source to same as elem
});
Wrap it all in this code to make sure the images are loaded before you execute your script. When you initially load your page, it caches the images(stores them in temp memory), but not before all your elements are rendered. When you reload, it reads the images from the cache–which is much faster than refetching the images again from the server–and therefore the images load about the same time everything else does. This results in visible images.
Like I said, to get your page to work, make sure everything is loaded, then run your script.
$(window).load(function(){
...your scripts(you can exclude functions definitions from this scope)
}
I am playing with the canvas, and it seems to be working great in FF6, but in Chrome 13, the sprite that I am drawing does not appear reliably. I have done some research and found that the problem stems from the function firing before the asset has loaded completely.
Fiddle here:
http://jsfiddle.net/LqHY9/
Relevant Javascript:
function sprite(ipath, sh, sw, ih, iw){
/* BASIC INFO FOR SPRITE */
this.frameWidth = sw;
this.frameHeight= sh;
frame_rows = ih/sh;
frame_columns = iw/sw;
num_frames = frame_columns*frame_rows ;
this.frame = new Array();
frameNumber = 0;
for(row = 0; row<frame_rows; row++){
for(i=0;i<frame_columns;i++){
this.frame[frameNumber] = {};
this.frame[frameNumber].offsetX = this.frameWidth*i;
this.frame[frameNumber].offsetY = this.frameHeight*row;
frameNumber++
}
}
this.sheight = sh;
this.swidth = sw;
this.raw = new Image();
this.raw.src = ipath;
}
animation=new sprite("http://www.melonjs.org/tutorial/tutorial_final/data/sprite/gripe_run_right.png",64,64,64,512);
context.drawImage(animation.raw, animation.frame[0].offsetX, animation.frame[0].offsetY, animation.frameWidth, animation.frameHeight, 0, 0, animation.frameWidth,animation.frameHeight)
(Don't worry, my context variable is defined, I just cut that bit out, you can see the whole thing in the JSFiddle.)
The Image object has an onload event which you should hook into.
Assuming you have more than one image, you could implement a sort of a "loader". This would basically just take an array of image URLs, load each of them, and listen to their onload events. Once each image has loaded, it would in turn call some other function, which would signal that every resource has finished loading.
the Image() object has an onload(and onerror) event. If you need to execute something after it loads you can attach a function.
e.g.
var img = new Image();
img.onload = function() {
//do something
};
Just make sure you attach the onload before setting the src.
You need to use the onload handler for the image. You must set the handler before you set the .src for the object because in some browsers, the load event may fire immediately upon setting .src if the image is in the browser cache. Here's a piece of pseudo code:
var img = new Image();
img.onload = function () {
// image is now loaded and ready for handling
// you can safely start your sprite animation
}
img.src = "xxx";
You can see related sample code from another answer I wrote here: jQuery: How to check when all images in an array are loaded?.
function Sprite(urls, speed, box)
{
var that = this, running = false, interval = 0, loaded = false;
that.urls = urls;
that.speed = speed;
that.images = new Array();
that.box = box || { x: 0.0, y: 0.0, w: 64, h: 64 };
for(var i = 0; i < that.urls.length; ++i)
{
that.images[i] = new Image();
that.images[i].src = that.urls[i];
that.images[i].id = i;
var len = that.urls.length;
that.images[i].onload = function(){ if(parseInt(this.id) === len) { loaded = true; } };
}
that.current = 0;
var Draw = function(ctx)
{
if(loaded)
{
var curr = that.images[that.current];
ctx.drawImage(curr, 0.0, 0.0, curr.width, curr.height, that.box.x, that.box.y, that.box.w, that.box.h);
}
};
that.Run = function(ctx)
{
if(!running)
{
running = true;
interval = setInterval(function(){
Draw(ctx);
if(that.current < that.urls.length)
{
that.current++;
}
else
{
that.current = 0;
}
}, that.speed);
}
};
that.Clear = function()
{
if(running)
{
running = false;
clearInterval(interval);
}
};
}
// Exemple
var test = new Sprite(["image1.png", "image2.png", "image3.png"], 250);
test.Run(myContext);