SVG to canvas Image working half of the time - javascript

I want to compare two svg paths (user and model) at some point. The idea is to transform each of them onto ImageData to be able to make pixel comparisons. The problem I have is using the drawImage which leads me to an empty canvas half of the time.
let modelCanvas = document.createElement("canvas");
let modelContext = modelCanvas.getContext("2d");
modelCanvas.width = 898;
modelCanvas.height = 509;
document.body.appendChild(modelCanvas);
let modelImg = new Image(898, 509);
modelImg.src = 'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLW[....]';
modelContext.drawImage(modelImg, 0, 0, 898, 509);
The code is pretty straightforward and always run without producing error. Still drawImage seems to fail silently times to times.
Here is the JSFiddle (with the full data string) :
https://jsfiddle.net/Ldgpuo03/
Thank you very much for your help.

Image loading by web browser is an asynchronous operation.
You are trying to call modelContext.drawImage when the image is not guaranteed to be loaded.
You must place your drawing code inside the image.onload callback function
This function will be called once when the image loading is fully finished.
let modelCanvas = document.createElement("canvas");
let modelContext = modelCanvas.getContext("2d");
modelCanvas.width = 40;
modelCanvas.height = 40;
document.body.appendChild(modelCanvas);
let modelImg = new Image();
modelImg.src = 'https://i.stack.imgur.com/EK1my.png?s=48';
modelImg.onload = function(){
modelContext.drawImage(modelImg, 0, 0, 40, 40);
}

Related

How to see if PNG has a transparent background with Javascript [duplicate]

Is there a way to read transparent pixels from a picture using javascript?
I think, that it could be something similar to what PNG fixes does for IE (reading transparent pixels and applying some stuff, lol). But yes, for every browser..
Ah, would be awesome if it could be achieved without HTML5.
Well this question is actually answered by the dude from GoogleTechTalks in this video on javascript-based game engines.
http://www.youtube.com/watch?feature=player_detailpage&v=_RRnyChxijA#t=1610s
It should start at the point where it is explained.
Edit:
So I will summarize what is told in the video and provide a code-example.
It was a lot tougher than I had expected. The trick is to load your image onto a canvas and then check each pixel if it is transparent. The data is put into a two dimension array. Like alphaData[pixelRow][pixelCol]. A 0 is representing transparency while a 1 is not. When the alphaData array is completed it is put in global var a.
var a;
function alphaDataPNG(url, width, height) {
var start = false;
var context = null;
var c = document.createElement("canvas");
if(c.getContext) {
context = c.getContext("2d");
if(context.getImageData) {
start = true;
}
}
if(start) {
var alphaData = [];
var loadImage = new Image();
loadImage.style.position = "absolute";
loadImage.style.left = "-10000px";
document.body.appendChild(loadImage);
loadImage.onload = function() {
c.width = width;
c.height = height;
c.style.width = width + "px";
c.style.height = height + "px";
context.drawImage(this, 0, 0, width, height);
try {
try {
var imgDat = context.getImageData(0, 0, width, height);
} catch (e) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
var imgDat = context.getImageData(0, 0, width, height);
}
} catch (e) {
throw new Error("unable to access image data: " + e);
}
var imgData = imgDat.data;
for(var i = 0, n = imgData.length; i < n; i += 4) {
var row = Math.floor((i / 4) / width);
var col = (i/4) - (row * width);
if(!alphaData[row]) alphaData[row] = [];
alphaData[row][col] = imgData[i+3] == 0 ? 0 : 1;
}
a=alphaData;
};
loadImage.src = url;
} else {
return false;
}
}
I got errors when running local in Firefox and the try catch statement solved it. Oh I gotta eat...
Edit 2:
So I finished my dinner, I'd like to add some sources I used and wich can be helpful.
https://developer.mozilla.org/En/HTML/Canvas/Pixel_manipulation_with_canvas
Info about the imageData object.
http://blog.nihilogic.dk/2008/05/compression-using-canvas-and-png.html
Even more info about the imageData object and it's use.
http://www.nihilogic.dk/labs/canvascompress/pngdata.js
A really helpful example of the use of imageData, the code I provided resembles this one for a big part.
http://www.youtube.com/watch?v=_RRnyChxijA
Infos on scripting game-engines in javascript, really really interesting.
http://blog.project-sierra.de/archives/1577
Infos about the use of enablePrivilege in firefox.
This is a bit tricky problem, since the only way to access files directly from Javascript is by using FileReader, which is a relatively new feature and not yet supported in most browsers.
However, you could get the desired result by using a canvas. If you have a canvas, you could assign it some distinctive color (such as neon green used in green screens). Then you could insert the image onto canvas and use the method mentioned here to get each individual pixel. Then you could check each pixel's color and see whether that point corresponds to your background color (ergo it's transparent) or does it have some other color (not transparent).
Kind of hackish, but don't think there's anything else you can do with pure JS.
It appears that GameJS can do this and much, much more. I am referencing this SO question for any/all of my knowledge, as I don't claim to actually have any about this topic.
Of course, this is HTML5, and uses the canvas element.

How to make pre-initialized array contents work, vs. array.push()?

Why can't the images be defined in an array as shown here.
Why is it necessary push a new Image object in the array every time?
var canvas = null;
var ctx = null;
var assets = [
'/media/img/gamedev/robowalk/robowalk00.png',
'/media/img/gamedev/robowalk/robowalk01.png',
'/media/img/gamedev/robowalk/robowalk02.png',
'/media/img/gamedev/robowalk/robowalk03.png',
'/media/img/gamedev/robowalk/robowalk04.png',
'/media/img/gamedev/robowalk/robowalk05.png',
'/media/img/gamedev/robowalk/robowalk06.png',
'/media/img/gamedev/robowalk/robowalk07.png',
'/media/img/gamedev/robowalk/robowalk08.png',
'/media/img/gamedev/robowalk/robowalk09.png',
'/media/img/gamedev/robowalk/robowalk10.png',
'/media/img/gamedev/robowalk/robowalk11.png',
'/media/img/gamedev/robowalk/robowalk12.png',
'/media/img/gamedev/robowalk/robowalk13.png',
'/media/img/gamedev/robowalk/robowalk14.png',
'/media/img/gamedev/robowalk/robowalk15.png',
'/media/img/gamedev/robowalk/robowalk16.png',
'/media/img/gamedev/robowalk/robowalk17.png',
'/media/img/gamedev/robowalk/robowalk18.png'
];
var frames = [];
var onImageLoad = function() {
console.log("IMAGE!!!");
};
var setup = function() {
j=0;
body = document.getElementById('body');
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
canvas.width = 100;
canvas.height = 100;
body.appendChild(canvas);
for (i = 0; i <= assets.length - 1; ++i) {
frames[i].src = assets[i];
}
setInterval(animate,30);
}
var animate = function() {
ctx.clearRect(0,0,canvas.width,canvas.height);
if (j >= assets.length) {
j=0;
}
var image = new Image();
image.src = frames[j];
ctx.drawImage(image,0,0);
++j;
}
The first reason is to reduce latency. Putting only the URLs into an array means that images have not been pre-fetched before the animation starts. The first round of animation is going to be slow and jerky as each image is retrieved from the net. If the animation is repeated, the next round will be faster. This consideration mostly applies to animations which replaced image elements on the page (in the DOM) rather than by writing to a canvas.
The second reason is to remove overhead and improve efficiency in the animation loop. Using new Image() inside the loop means that drawing time for each frame includes the time taken to create a new Image object as well as draw it on the canvas. In addition the image content can only be written to the canvas after it has been fetched, making it necessary to write to the canvas from an onload handler attached to the image object. The posted code does not do this and could throw an error in some browsers trying to synchronously write an image with no data to the canvas. Even if otherwise successful, repeated animations would be creating a new Image object each time a frame is displayed and churning memory usage.
Note the original version probably used onImageLoad to check when the image has been fully loaded from the web before pushing the object into an array of preloaded image objects. This is the preferred method of prefetching animation images.
And don't forget to define j before use :-)

Fastest way to change image pixels before rendering on an HTML5 canvas

I have a (largish) HTML5 canvas. Its rendering a pictures from a file, using context.drawImage() and this is quite fast. (Note that there are more than one picture on the same canvas).
Now I need to perform some manipulations to the pixels on the canvas, basically I need to perform Alpha Blending which darkens certain areas of the picture. So instead I used this approach.
//create an invisible canvas so that we don't do the actual rendering of the image
var invisibleCanvas = document.createElement('canvas');
invisibleCanvas.width = myWidth;
invisibleCanvas.height = myHeight;
var invContext = invisibleCanvas.getContext('2d');
invContext.drawImage(imageObj, 0, 0, invisibleCanvas.width, invisibleCanvas.height);
var imageData = invContext.getImageData(0, 0, invisibleCanvas.width, invisibleCanvas.height)
var pixelComponents = imageData.data;
var dkBlendingAmount = 0.5;
for (var i = 0; i < pixelComponents.length; i += 4)
{
//there are a few extra checks here to see if we should do the blending or not
pixelComponents[i] = pixelComponents[i] * dkBlendingAmount;
pixelComponents[i+1] = pixelComponents[i+1] * dkBlendingAmount;
pixelComponents[i+2] = pixelComponents[i+2] * dkBlendingAmount;
}
//this is the real place where I want it
context.putImageData(imageData, xOffset, yOffset);
Is there a way to make this faster? Is there a way to get the image data directly from my imageObj rather than having to put it on a canvas, get the data, convert it and put it on another canvas?

HTML5 canvas redraw on parameter change

I have this code, running every time when one of parameners (c) is changed. (It's some kind of seamless texture generator, for better understanding what I want to get):
class drawPreview
constructor: (c) ->
pic = new Image()
pic.src = $("#sample_file").attr('src')
canvas = document.getElementById("preview_canvas")
context = canvas.getContext("2d")
img_w = $("#sample_file").attr('width')
img_h = $("#sample_file").attr('height')
tiles = Math.floor(img_w / c.w) + 1
for i in [0..tiles]
console.log('t')
context.width = context.width
context.drawImage(pic, c.x, c.y, c.w, c.h, c.w*i, 0, c.w, c.h)
This works good in Firefox (with some lagging). But in Chrome it freezes for some time and unfreezes same way. Is there any way to do it more sensible to user's actions?
if only parameter changing is 'C' then move all the code outside of the this block.
move the below code out it should be called once.
pic = new Image()
pic.src = $("#sample_file").attr('src')
canvas = document.getElementById("preview_canvas")
context = canvas.getContext("2d")
img_w = $("#sample_file").attr('width')
img_h = $("#sample_file").attr('height')
The above operations may be costly.

Use Javascript and Canvas to move a pixel from an image

I'm interested in moving a pixel (eventually all of them) from an image drawn on canvas. This is the sample code i'm working off and i believe it does some pretty standard stuff in terms of drawing the image:
var images = [ // predefined array of used images
'http://distilleryimage6.instagram.com/928c49ec07d411e19896123138142014_7.jpg'
];
var iActiveImage = 0;
$(function(){
// drawing active image
var image = new Image();
image.onload = function () {
ctx.drawImage(image, 0, 0, image.width, image.height); // draw the image on the canvas
}
image.src = images[iActiveImage];
// creating canvas object
canvas = document.getElementById('panel');
ctx = canvas.getContext('2d');
console.log(ctx);
var imageData = ctx.getImageData(200, 150, 1, 1);
var pixel = imageData.data;
});
So i have this but could someone point me in the right direction of doing something like picking a pixel at random from the image and physically moving it somewhere else on the page? Is this possible?
Thanks
I'm not sure what you mean by "physically moving it", but you can use ctx.putImageData() to apply the pixel elsewhere inside the canvas.
var imageData = ctx.getImageData(200, 150, 1, 1);
var pixel = imageData.data;
// You can even get or set the color or alpha of the pixel. (values between 0-255)
var r = pixel[0];
var g = pixel[1];
var b = pixel[2];
var a = pixel[3];
ctx.putImageData(imageData, x, y); // Where x y are the new coordinates.
Also, you should put all this imageData manipulation inside the onload function, because in your example, the image is still not loaded when you call ctx.getImageData(), so you're manipulating blank pixels.
Note also that for security reason, you cannot use getImageData() on an image loaded from a different domain. So I think your example will throw an exception of type Uncaught Error: SECURITY_ERR: DOM Exception 18, because the image is loaded from Instagram.
I think this tutorial might help you:
https://developer.mozilla.org/en/Canvas_tutorial/Basic_animations

Categories