Systematically updating src of IMG. Memory leak.
I am currently updating an image every x sec. A few ways I thought of doing this were as follows:
Take One:
var url ="...";
$('#ImageID').attr('src', url);
Now this works perfectly changes the image but causes a memory leak.
Take Two:
So it is creating DOM elements, so I attempted the following.
<div id="ImageHolder">
</div>
var image - "..."; //Obv actual image in working.
$('#ImageHolder').empty();
$('#ImageHolder').html(image);
Now this works but causes a flicker when it changes which is unliked. Now with two images and swapping them at intervals works fine, but I want to stay as low on bandwidth as possible.
Edit 1:
My Code:
<form name="selected">
<input type="hidden" name="map" />
</form>
<img id="MyMaps" src="http://localhost/web/blank.png" alt="" />
<script type="text/javascript">
var locate = window.location;
var uri = document.selected.map.value;
var MyHost = window.location.hostname;
function delineate2(name) {
totheleft= uri.indexOf("maps/") + 5;
totheright= uri.lastIndexOf("&");
return (uri.substring(totheleft, totheright));
}
function loadImage() {
var CurrentMap = delineate2(name);
var url = 'http://' + MyHost+ '/map/' + CurrentMap+ '/image?' + new Date().getTime();
$('#MyMaps').attr('src', url);
setTimeout(loadImage, 10000);
}
</script>
Has anyone done something similar and found a working solution, or how can I go about preventing the memory leak / flickering when the image updates?
I believe that your "take one" should work. There should be no memory leak. You're overwriting the src tag every time around - if you hold no other references to old images, they should get garbage collected. I'm seeing this problem in FF and Chrome. Chrome tells me that JS memory usage is constant, the memory must be lost somehwere else.
I have opened a Chrome bug:
https://code.google.com/p/chromium/issues/detail?id=309543
In case you want to put in your weight as well and maybe star the bug :)
I have used different methods to solve this problem, none of them works. It seems that memory leaks when img.src = base64string and those memory can never get released. Here is my solution.
fs.writeFile('img0.jpg', img_data, function (err) {
// console.log("save img!" );
});
document.getElementById("my-img").src = 'img0.jpg?'+img_step;
img_step+=1;
My Electron app updating img every 50ms, and memory doesn't leak.
Forget about disk usage. Chrome's memory management piss me off.
I have never thought of doing this like your fist method.. interesting. I can imagine that it causes a memory leak because every single image is kept in memory because nothing is actually removed. Thats just a guess though.
I would recomend sticking to the second method but modifying it so solve the flicker, like fading between images. A good jQuery plugin to look at would be the jQuery Cycle Plugin
If that plugin doesn't do it for you or you want to keep the code small, jQuery also has some animation functions built in. fadeIn() and fadeOut() may be of interest.
Something like this might work better.
<div id="ImageHolder">
</div>
var image - "..."; //Obv actual image in working.
function loadImage() {
choose your image however you want to, preferably a preloaded image.
$('#ImageHolder').fadeOut('fast');
$('#ImageHolder').html(image);
$('#ImageHolder').fadeIn('fast');
setTimeout(loadImage, 10000);
}
I believe a shorter way to do this might be: (also delay() may be optional, I just put it there in case you need it.)
$('#ImageHolder').fadeOut('fast').html(image).delay('100').fadeIn('slow');
Additionally there may be a delay for the image to load if it hasn't been preloaded. I'm not 100% sure how to do that off the top of my head so a quick google seach came up with this:
http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript
5 years old question yet it still 'hot' for me, I want to share the very same problem I just faced.
The "take one" approach maybe is the very first approach every programmer used to change the image source, but till now ( 5 years after this question posted ) the problem is still occurred, change the <img> 'src' frequently and you can see on windows task manager that your browser became greedy.
The "take two" create flicker, and it is rarely acceptable.
Fortunately, html5 comes with <canvas>, so I try to use <canvas> to overcome this problem.
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");
img = new Image();
img.onload = function () {
ctx.drawImage(img, 0, 0);
img.src = "";
}
img.src = "data:image/png;base64," + stringSource;
The new problem found, <canvas> different from <img>, it will not automatically resize and 'fit'. We have to manually resize the image to fit the canvas and keep the ratio. Below is my code to resolve the problem
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");
img = new Image();
img.onload = function () {
var imageWidth = canvas.width;
var imageHeight = canvas.height;
var xPosition = 0;
var yPosition = 0;
var proportion = 1;
var padding = 2;
if ((img.width / canvas.width) > (img.height / canvas.height)) {
proportion = img.width / canvas.width;
}
else {
proportion = img.height / canvas.height;
}
imageWidth = img.width / proportion;
imageHeight = img.height / proportion;
xPosition = (canvas.width - imageWidth) / 2;
yPosition = (canvas.height - imageHeight) / 2;
ctx.drawImage(img, 0, 0, img.width, img.height, xPosition+padding, yPosition+padding, imageWidth-2*padding, imageHeight-2*padding);
img.src = "";
}
img.src = "data:image/png;base64," + stringSource;
Hope it will help any one who face the same problem.
Related
I have an <img> loading from a URL and I want to see what colors the pixels have.
var img = document.getElementById('image');
img.src = "https://someimage.url/image.jpg";
img.onload = function () {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
};
At this point, due to .getImageData, the console says operation is insecure.
I try to fix this by adding img.crossOrigin = "anonymous";.
This gets rid of the error message, but leaves me with an empty, unloaded image.
Other posts have suggested moving components out of img.onload, but it's the same result.
Some have suggested that this path is not possible due to security reasons. If so, what is another way of approaching this? How else does one determine the color of a certain pixel displayed on screen?
The documentation for getImageData seems to show exactly what I want to do. It looks like the only difference is that their image is on their own server instead of someone else's. Is this the only way around this problem? And if that's the case, would a more straightforward concept be:
download image to your own server
process it locally
delete image after processing
This might be more straightforward, but probably won't scale very well?
I am using domtoimage library to convert html (with lots of CSS) to image.
Actually I need high clarity image for printing purpose (600 DPI). So for this I scale (zoom) the html to 6.25(600/96) times, then I use domtoimage to capture it. I am successful as per my plans. My image is of high resolution and the printing clarity is also satisfactory.
But the issue is with optimization. It takes too long time for domtomimage. I know this issue is because of html scaling, but I need high resolution image.
Any idea for making this capture fast? My main focus is to reduce time taken by library to capture and create image.
Note:
I have also tried with phantom JS, but some css properties are not supported in it.
I have also tried html2canvas which has also limitation with some CSS property
Just for information I have added scale property to draw function
function draw(domNode, options) {
return toSvg(domNode, options)
.then(util.makeImage)
.then(util.delay(100))
.then(function (image) {
var canvas = newCanvas(domNode);
// canvas.getContext('2d').drawImage(image, 0, 0);
var ctx = canvas.getContext('2d');
if(options.scale){
ctx.scale(6.25,6.25);
}
ctx.drawImage(image, 0, 0);
return canvas;
});
function newCanvas(domNode) {
var canvas = document.createElement('canvas');
if(options.scale){
canvas.width = options.width || 6.25 * util.width(domNode);
canvas.height = options.height || 6.25 * util.height(domNode);
}
else{
canvas.width = options.width || util.width(domNode);
canvas.height = options.height || util.height(domNode);
}
if (options.bgcolor) {
var ctx = canvas.getContext('2d');
ctx.fillStyle = options.bgcolor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
return canvas;
}
}
I could speed up the image generation process by several times by skipping the font re-rendering process. I'm not sure what content you're trying to capture but if it is not font-specific, I forked and edited the library based on suggestions from several other users and it works for me. It also helped me get rid of many failed XHR requests due to CORS and is currently working on all browsers.
Check out: https://github.com/venkat4541/dom-to-image
This may or may not work for you based on your font usage. Hope it helps.
I have a problem when I load local (or online) images. So I use canvas HTML 5 then I put a new image on it but image never load at first time. I need to refresh page one time. The first time, images are loaded but not in good time so there are not showed. Then,, when I refresh, images are still in caches of my browser (firefox 50) so that's work.
I saw on forum that I need to wait with :
myimage.onload = function(){
context.drawImage(myimage,0,0,myimage_size,myimage_size);
}
myimage.src = mysrc;
But this doesn't work, images are not showed. So I try another way with that:
var elementHTML = document.createElement('img');
var canvas = document.createElement('canvas');
canvas.width = my_size;
canvas.height = my_size;
elementHTML.appendChild(canvas);
context = canvas.getContext('2d');
base_image = new Image();
base_image.src = base_image_src;
base_image.addEventListener('load', test(this.owner));
function test(owner){
//here there is the trick, with that, all work but I need program work without that !
while(window.alert("done")){};
context.drawImage(base_image,0,0,my_size,my_size)
if (owner == "1") {
//I modified some colors of my image
recolorRGBImage(context,lineTuleColorRGB,playerLineTuleColorRGB_1,lineTrigger);
recolorRGBImage(context,fillTuleColorRGB,playerFillTuleColorRGB_1,fillTrigger);
}
else {
recolorRGBImage(context,lineTuleColorRGB,playerLineTuleColorRGB_2,lineTrigger);
recolorRGBImage(context,fillTuleColorRGB,playerFillTuleColorRGB_2,fillTrigger);
}
}
That's work but obviously, alerts must never show. How can I do what I did without alert? (Alert used like onload because these methods doesn't work)
Thanks !
This question already has an answer here:
Memory leaks when manipulating images in Chrome
(1 answer)
Closed 6 years ago.
I think I have a problem related to:
Systematically updating src of IMG. Memory leak
I don't have enough rep to comment on answers but https://stackoverflow.com/a/34085389/3270244 is exactly my case.
var canvasElement = $('canvas', camContainer);
var ctx = canvasElement[0].getContext('2d');
var image = new Image();
image.onload = function() {
ctx.drawImage(this, 0, 0);
image.src = '';
};
//for every getCamImg I receive exactly 1 image
socket.on('getCamImg', function(err, data) {
if(data) {
var dataImg = data.substring(data.indexOf(';') + 1);
image.src = dataImg;
}
socket.emit('getCamImg');
});
socket.emit('getCamImg');
I change img.src every 1/10s (jpegs from a camera) and I can watch the browsers consume more and more memory. Firefox stops at 500MB, Edge stops at 100MB and for Chrome I stopped testing near 1G. If I remove the img.src change everything runs smooth (without an image of course).
I found a lot of (at leat I think so) related issues:
https://bugs.chromium.org/p/chromium/issues/detail?id=36142
https://bugs.chromium.org/p/chromium/issues/detail?id=337425
memory leak while drawing many new images to canvas
Memory leaks when manipulating images in Chrome
Somewhere someone mentioned (sorry for this :D) that maybe the cache is spammed because the old images are kept. I don't think it's a gc problem, because chrome has a tool to run him and nothing changes.
Can someone reproduce this or guide me the correct way?
Update:
socket.on('getCamImg', function(err, data) {
if(data) {
var image = document.createElement("img");
image.onload = function() {
ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
socket.emit('getCamImg');
image.src = '';
};
image.src = dataImg;
}
});
This works good in Firefox (the image.src='' is important). Chrome still leaks.
I'm doing nearly the same as you do in my current project. As this is too much for a comment I just share my observations in an answer. This is how I do it:
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
onNewImgData = function (data) {
//create a new image element for every base64 src
var img = document.createElement("img");
//bind the onload event handler to the image
img.onload = function () {
//draw the image on the canvas
ctx.drawImage(this, 0, 0);
//do some stuff
//...
//done, request next image
};
//update the image source with given base64 data
img.src = "data:image/bmp;base64," + data;
};
I don't clean up anything and I can't see a memory leak in my application (no matter which browser). But I had a memory leak before, as I logged all the base64 data into the browser console :) That caused the exact same issue you've described.
I have a web page which contains an iframe, the iframe contains a table which contains up to 1,000 canvas elements. The canvas elements are filled by a function defined in the iframe, which is called by the parent page. The function iterates and fills each canvas with a base64 data URI contained in a hidden span in the parent page. I have read a few other Stackoverflow posts regarding similar issues but I have already tried the most commonly used suggestion (using onload) and that does not seem to work. I am fairly fresh to html/javascript so I doubt I am doing things the best way. I was hoping someone could guide me on how to assure that all the canvas elements are successfully filled with the base64 data on the first load so I don't need to refresh multiple times to get me thumbnails? Here is some code, it is VERY trimmed up code from my html page. If I were to paste the entire page, it might be a bit too much too handle sensibly. Here is what I believe to be the essential actors.
My onload call to my function which fills the canvases, this is on the main page which contains the iframe -
<body onload="document.getElementById('framePage').contentWindow.fillCans();">
Here is the fillCans() function which is contained within the framePage iframe (I probably should move this to the main html page, but so far this is how the script has... evolved haha). pgCnt element contains the "counter" numbers if you will. It will defines how many elements to iterate through. When var c is assigned, the "can"+i elements are the canvases. When var imgSrc is assigned, the "span"+i elements are the hidden span elements on the parent page which contain the base 64 data URIs -
function fillCans() {
var cnt = document.getElementById("pgCnt").innerHTML;
var cnt = cnt.split("-");
var cnt1 = cnt[0];
var cnt2 = cnt[1];
for (var i=cnt1; i <= cnt2; i++) {
var targ = "span"+i
var c = document.getElementById("can"+i);
var ctx = c.getContext("2d");
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,128,128);
var image = new Image();
var imgSrc = parent.document.getElementById("span"+i).innerHTML;
imgSrc = imgSrc.split("*");
image.src = imgSrc[0];
ctx.drawImage(image,0,0);
}
Like I said, this will load fine, firebug reports no errors, however all the canvases are white with no thumbnails. When I hit refresh a few times they all finally load. I've read that the image data is asynchronously loaded but I am not certain to as what that means, I assume it means it doesn't play by the rules that onload plays by? Anyways as usual, thanks for all the help stackoverflow community. It will be great to know how to get these thumbnails to load successfully on the first page load.
After #Sebastian's great suggestions I was able to get my JavaScript functioning as it should. I am pasting in the new function. As it works now, in case anyone ever comes across a similar issue. I had to implement an onload call to a function which drew the image, and then utilize in IIFE for loop to insure that each thumbnail was drawn with correctly iterated data.
function fillCans() {
var cnt = document.getElementById("pgCnt").innerHTML;
var cnt = cnt.split("-");
var cnt1 = cnt[0];
var cnt2 = cnt[1];
for (var i=cnt1; i <= cnt2; i++) {
(function(iterable){
var c = document.getElementById("can"+iterable);
var ctx = c.getContext("2d");
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,128,128);
var image = new Image();
var imgSrc = parent.document.getElementById("span"+iterable).innerHTML;
imgSrc = imgSrc.split("*");
image.onload = function() {
ctx.drawImage(image, 0, 0);
};
image.src = imgSrc[0];
})(i);
}
}
You are right, the problem is that the image has to be loaded first, before you can draw it.
In your case image is not loaded at the time you call
ctx.drawImage(image,0,0);
However later on the browser has cached the image URL and in between the calls to the assignment of the source property and the drawImage call the browser has already retrieved the image from the cache.
To make it work in any case, you need to wait for the image to load:
image.onload = function() {
ctx.drawImage(image, 0, 0);
};
image.src = imgSrc[0];
Be sure to first register the callback and then set the src.
See HTML Canvas DrawImage Tutorial