canvas toDataURL not returning a complete image - javascript

I'm building a jQuery plugin which watermarks images (and yes, i'm well aware of the multitudinal drawbacks of a javascript/html5 watermarking system but just ignore that for now.) The basic method for each image is:
paste the image to the background of a canvas
add the data for a watermark image over that,
replace the src of the original image with that of the canvas (which now contains the watermark.)
Now it appears to work fine if I replace the image element with the canvas itself.. all of the elements appear on the canvas. But when I get the dataURL of the canvas, everything but the last image drawn onto it appears. I wouldn't even mind except this plugin also needs to replace the links to images as well, and so replace the hrefs with data urls (with the watermark.)
This is the current code:
(function($){
$.fn.extend({
cmark: function(options) {
var defaults = {
type: 'image',
content: 'watermark.png',
filter: 'darker',
scale:300,
box: {
top : 0.5,
left : 0.5,
width : 0.75,
height : 0.75,
bgcolor : '#000000',
bgopacity : 0.5,
fgopacity : 1
},
callback_unsupported: function(obj){
return obj;
}
}
var getScale = function(w, h, scale){
ratio = Math.min(scale/w, scale/h);
scalew = Math.round(ratio*w);
scaleh = Math.round(ratio*h);
return [scalew,scaleh];
}
var options = $.extend(defaults, options);
return this.each(function() {
obj = $(this);
canvas = document.createElement('canvas');
if(!window.HTMLCanvasElement){
return options.callback_unsupported(obj);
}
/* if selecting for images, reset the images. Otherwise,
we're replacing link hrefs with data urls. */
if(obj.attr('src')){
target_img = obj.attr('src');
}
else if (obj.attr('href')){
target_img = obj.attr('href');
}
// get the filetype, make sure it's an image. If it is, get a mimetype. If not, return.
ftype = target_img.substring(target_img.lastIndexOf(".")+1).toLowerCase();
canvasbg = new Image();
canvasbg.onload = function(){
iw = canvasbg.width;
ih = canvasbg.height;
scale = getScale(iw, ih, options.scale);
iw = scale[0];
ih = scale[1];
canvas.setAttribute('width', iw);
canvas.setAttribute('height', ih);
ctx = canvas.getContext('2d');
/* define the box as a set of dimensions relative to the size of the image (percentages) */
bw = Math.round(iw * options.box.width);
bh = Math.round(ih * options.box.height);
// for now the box will only ever be centered.
bx = Math.round((iw * options.box.top) - (bw/2));
by = Math.round(ih * options.box.left - (bh/2));
/* draw the box unless the opacity is 0 */
if(options.box.bgopacity > 0){
ctx.fillStyle = options.box.bgcolor;
ctx.globalAlpha = options.box.bgopacity;
ctx.fillRect(bx, by, bw, bh);
}
wm = new Image();
wm.onload = function(){
ww = wm.width;
wh = wm.height;
scalar = Math.max(bw, bh); // scale to within the box dimensions
scale = getScale(ww, wh, scalar);
ww = scale[0];
wh = scale[1];
ctx.globalCompositeOperation = options.filter;
ctx.drawImage(wm, bx, by, ww, wh);
}
wm.src = options.content;
ctx.drawImage(canvasbg, 0, 0, iw, ih);
obj.replaceWith(canvas);
$('body').append('<img src="'+canvas.toDataURL()+'">');
//obj.attr('src', canvas.toDataURL());
}
canvasbg.src = target_img;
});
}
})
})(jQuery);
I added a line which dumps an image with the data url directly onto the page for testing and this is what I see... on the left is the canvas element, on the right is the image with the data url:
So yeah, this has had me stumped for a couple of days now. I'm probably missing something horribly obvious but I can't see it.
... edited because the example is no longer online. sorry.

First of all, don't build a string buffer that big for a tag.
var img = new Image();
img.src = canvas.toDataURL();
$('body').append(img);
Or if you prefer:
$('body').append($('<img>').attr('src', canvas.toDataURL()))
Second, you are getting there dataURL of the canvas before you draw the watermark. The drawing happens in the wm.onload callback function, which happens when the watermark loads. That may not fire until way after canvasbg.onload fires off, which is where you get the dataURL.
So move the image append into code at the end of the wm.onload callback and you should be good.

Related

Explorer - canvas image editing issue

I'm currently building a site that comes with a set of icons. They are white .png images with a transparent area. Each image is placed over a div whose background colour changes on mouseover/mouseout events.
I wrote a small JavaScript function that I can use to set the white portion of each .png to whatever the header background colour is so that the images blend in with the header and all you see is the transparent cut-away area change on mouse events - it's quite a nice effect.
So the white portion (not the transparent part) of the image should be changed to whatever the header background colour is, and the function works really great on Firefox, but for some reason doesn't work so well on Explorer (11).
Is there something I'm not seeing here? Here's the code that does the image editing - all I need to do is pass the id of the image and the hexadecimal colour string:
function setImageBg(
imageID,
imageColor
) {
var img = document.getElementById(imageID);
var canvas = document.createElement('canvas');
// Get the red, green and blue values from the hex color
// string.
var redMix = parseInt(imageColor.substr(1, 2), 16);
var greenMix = parseInt(imageColor.substr(2, 2), 16);
var blueMix = parseInt(imageColor.substr(5, 2), 16);
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
// Get the image data...
var pixelData = canvas.getContext('2d').getImageData(0, 0, img.width, img.height);
var imgData = pixelData.data;
var ctx = canvas.getContext('2d');
var imgDataURL;
// Edit the image data.
for (imagePos = 0; imagePos < ((img.width * img.height) * 4); imagePos += 4) {
if (pixelData.data[(imagePos + 3)] <= 99)
continue;
imgData[imagePos] = redMix;
imgData[(imagePos + 1)] = greenMix;
imgData[(imagePos + 2)] = blueMix;
}
ctx.putImageData(pixelData, 0, 0);
imgDataURL = canvas.toDataURL();
document.getElementById(imageID).src = imgDataURL;
}
Like I say, it's great on Firefox - on Explorer all I see is the div, which still dutifully changes colour on mouse events. It's as though the entire image has been made transparent.
Any help would be greatly appreciate, much obliged.
UPDATE:
Ok, so I thought I'd cracked this...here's what happened.
The problem was that the image was loading and being edited in both IE and FF, when I noticed the image had loaded and rendered in IE I thought I'd found and fixed the bug...but after a refresh the image again disappeared.
I scratched my head over it for a bit then decided I'd wasted enough time on it and brushed it aside. Now everything else is in place and working, so I'm back to this and it's a proper head scratcher.
So - IE DOES actually load and edit/render the image, when you initially open the page that is. If you refresh the page, the image disappears...or does it?
I made some adjustments to the code, using alert()'s to try and diagnose the problem, I decided what I do was add some code to check the source file - instead of diving in and changing the pixel colours - I decided to check the image before it is being edited.
Seems that, on the second run of my function - the image loaded is completely black and completely transparent. All pixels have a value of 0!
What does this mean? There's definitely something there, but not what should be.
Is htis a cacheing issue? I messed about with it for a bit, it's odd that IE will load and display the image initially but will neglect to do so after a refresh, but Ff is fine. I tried a few tricks to disable cacheing but nothing works...so now I'm thinking, perhaps it's something else.
Any ideas would be greatly appreciated, I mean it's not a deal-breaker. Everything is fine without this single feature but it's a shame if I can't get it to work.
For anyone interested, here's the updated function after various changes/edits:
function setImageBg(
imageSource,
imageSrc,
imageDst,
imageColor
) {
var img = document.getElementById(imageSrc);
var canvas = document.createElement('canvas');
var redMix = parseInt(imageColor.substr(1, 2), 16);
var greenMix = parseInt(imageColor.substr(3, 2), 16);
var blueMix = parseInt(imageColor.substr(5, 2), 16);
canvas.width = img.width;
canvas.height = img.height;
canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
var pixelData = canvas.getContext('2d').getImageData(0, 0, img.width, img.height);
var imgData = pixelData.data;
var ctx = canvas.getContext('2d');
var imgDataURL;
// Here's a change I made - on the first call of this function
// when the page loads, all is good - on the second call after a
// refresh this always returns on IE - it's as though the loaded
// image (from file) is completely black and transparent
// (everything is 0)...problem is the image file is never actually
// altered - the loaded image is altered but the actual file never
// changed - very odd...
//
if (pixelData.data[0] == redMix) {
if (pixelData.data[1] == greenMix) {
if (pixelData.data[2] == blueMix) {
document.getElementById(imageDst).src = document.getElementById(imageSrc).src;
return;
}
}
}
for (imagePos = 0; imagePos < ((img.width * img.height) * 4); imagePos += 4) {
if (pixelData.data[(imagePos + 3)] <= 99)
continue;
pixelData.data[imagePos] = redMix;
pixelData.data[(imagePos + 1)] = greenMix;
pixelData.data[(imagePos + 2)] = blueMix;
}
ctx.putImageData(pixelData, 0, 0);
document.getElementById(imageDst).src = canvas.toDataURL();
return true;
}

How to get Coordinate of Cropped Image by CropperJS?

I am using cropper JS to crop my image. I am able to get width and height of my canvas, however, need to know co-ordinates (X and Y) of cropped image.
Here is my code-
(function () {
//getting ratio
function getImageRatio(sourceCanvas) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var width = sourceCanvas.width;
var height = sourceCanvas.height;
canvas.width = width;
canvas.height = height;
context.beginPath();
context.arc(width / 2, height / 2, Math.min(width, height) / 2, 0, 2 * Math.PI);
context.strokeStyle = 'rgba(0,0,0,0)';
context.stroke();
context.clip();
context.drawImage(sourceCanvas, 0, 0, width, height);
return canvas;
}
window.addEventListener('DOMContentLoaded', function () {
var image = document.getElementById('image');
var button = document.getElementById('button');
var result = document.getElementById('result');
var croppable = false;
var cropper = new Cropper(image, {
aspectRatio: 1,
viewMode: 1,
built: function () {
croppable = true;
}
});
button.onclick = function () {
var croppedCanvas;
var roundedCanvas;
var roundedImage;
if (!croppable) {
return;
}
// Crop
croppedCanvas = cropper.getCroppedCanvas();
console.log(getImageRatio(croppedCanvas));
};
});
})();
Any idea, how to get coordinate of cropped image, so that I can crop this image by PHP.
So, if i understand properly, you are looking for the method getData.
It will return these properties as the documentation says:
x: the offset left of the cropped area
y: the offset top of the cropped area
width: the width of the cropped area
height: the height of the cropped area
rotate: the rotated degrees of the image
scaleX: the scaling factor to apply on the abscissa of the image
scaleY: the scaling factor to apply on the ordinate of the image
Although, i suggest taking a look at this section of the doc:
https://github.com/fengyuanchen/cropper#getcroppedcanvasoptions
Specifically at this part:
After then, you can display the canvas as an image directly, or use HTMLCanvasElement.toDataURL to get a Data URL, or use HTMLCanvasElement.toBlob to get a blob and upload it to server with FormData if the browser supports these APIs.
If you cannot use the toBlob method because some browser limitation (you can always add a polyfill), you can get the image using toDataUrl over the canvas (IE9+). And send it to the backend service already cropped. For this you are going to need to do something like this:
var formData = new FormData();
formData.append('image', canvas.toDataURL());
You can check the formData browser support in caniuse
Hope you find it helpful!

how to create a canvas dynamically in javascript

I have a canvas that you can draw things with mouse.. When I click the button It has to capture the drawing and add it right under the canvas, and clear the previous one to draw something new..So first canvas has to be static and the other ones has to be created dynamically with the drawing that I draw .. What should I do can anybody help
here is jsfiddle
http://jsfiddle.net/dQppK/378/
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
painting = false,
lastX = 0,
lastY = 0;
You can create a new canvas the same way you’d create any element:
var newCanvas = document.createElement('canvas');
Then you can copy over your old canvas:
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
oldCanvas.parentNode.replaceChild(newCanvas, oldCanvas);
ctx = newCanvas.getContext('2d');
But if you’re just looking to clear your drawing surface, what’s wrong with clearRect?
ctx.clearRect(0, 0, canvas.width, canvas.height);
Or, in your case, another fillRect. Updated demo
here's the function i use for this, it is part of a library i made and use to ease a few things about canvas.
I just put it on github in case other function might be be of use, i'll have to make a readme later...
https://github.com/gamealchemist/CanvasLib
with namespaceing removed, the code is as follow to insert a canvas :
// insert a canvas on top of the current document.
// If width, height are not provided, use all document width / height
// width / height unit is Css pixel.
// returns the canvas.
insertMainCanvas = function insertMainCanvas (_w,_h) {
if (_w==undefined) { _w = document.documentElement.clientWidth & (~3) ; }
if (_h==undefined) { _h = document.documentElement.clientHeight & (~3) ; }
var mainCanvas = ga.CanvasLib.createCanvas(_w,_h);
if ( !document.body ) {
var aNewBodyElement = document.createElement("body");
document.body = aNewBodyElement;
};
document.body.appendChild(mainCanvas);
return mainCanvas;
}

Cannot crop the image with correct location or original x,y.

I can get the location of top,left,width,height, but I can't crop as the alert show.
<script>
$(document).ready(function(e) {
$("#crop").click(function(){
var canvas=document.getElementById("Mystore1");
var context=canvas.getContext("2d");
var top=$('#face').offset().top;
var left=$('#face').offset().left;
var width=$('#face').width();
var height=$('#face').height();
alert(top);
alert(left);
alert(width);
alert(height);
var imageSrc ='../../../Public/Pictures/Sample Pictures/Desert.jpg';
var imageObj=new Image();
imageObj.onload=function(){
context.drawImage(imageObj, top, left, width, height, top, left, width, height);
};
imageObj.src=imageSrc;
});
});
</script>
here is an working demo. i found some problem in your code. on ready function param you should
send $ and on click an event variable named e (whatever you want).
croparea is the face area selected by your crop tool. main image is the image portion.
your js should be like so
$(document).ready(function($) {
$("#crop").click(function(e){
var Imgwidth = $('#face').width(),
Imgheight = $('#face').height(),
faceOffset = $('#face').offset(),
imgOffset = $('#imgHolder').find('img').offset(),
imgX1 = faceOffset.left-imgOffset.left,
imgY1 = faceOffset.top-imgOffset.top,
imgX2 =imgX1+Imgwidth,
imgY2 = imgY1+Imgheight;
var imageSrc ='http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg';
var imageObj=new Image();
imageObj.src=imageSrc;
selx1 = imgX1;
sely1 = imgY1;
selx2 = imgX2;
sely2 = imgY2;
selw = Imgwidth;
selh = Imgheight;
console.log(imgX1);
console.log(imgY1);
/*console.log(imgX2);
console.log(imgY2);*/
var canvas=document.getElementById("Mystore1");
var context=canvas.getContext("2d");
context.canvas.height = Imgheight;
context.drawImage(imageObj, imgX1, imgY1, selw, selh, 3, 3, Imgwidth, canvas.height-5);
});
});
when you select an area ( here #face div) from an image to crop then i calculate the top-left(X,Y) co-ordinate of the selected area in this line
imgX1 = faceOffset.left-imgOffset.left,
imgY1 = faceOffset.top-imgOffset.top,
and the right-bottom co-ordinate in these line
imgX2 =imgX1+Imgwidth,
imgY2 = imgY1+Imgheight;
and thus we get a rectangular co-ordinate system to draw the portion of the image that we selected to crop. for drawImage documentation please go to the link that i post in comment. i hope now its clear how i get the exact position to crop.
here is a working demo click here

What is leaking memory with this use of getImageData, javascript, HTML5 canvas

I am working with the 'canvas' element, and trying to do some pixel based manipulations of images with Javascript in FIrefox 4.
The following code leaks memory, and i wondered if anyone could help identify what is leaking.
The images used are preloaded, and this code fragment is called once they are loaded (into the pImages array).
var canvas = document.getElementById('displaycanvas');
if (canvas.getContext){
var canvasContext = canvas.getContext("2d");
var canvasWidth = parseInt(canvas.getAttribute("width"));
var canvasHeight = parseInt(canvas.getAttribute("height"));
// fill the canvas context with white (only at start)
canvasContext.fillStyle = "rgb(255,255,255)";
canvasContext.fillRect(0, 0, canvasWidth, canvasHeight);
// for image choice
var photoIndex;
// all images are the same width and height
var imgWidth = pImages[0].width;
var imgHeight = pImages[0].height;
// destination coords
var destX, destY;
// prep some canvases and contexts
var imageMatrixCanvas = document.createElement("canvas");
var imageMatrixCanvasContext = imageMatrixCanvas.getContext("2d");
// Set the temp canvases to same size - apparently this needs to happen according
// to one comment in an example - possibly to initialise the canvas?
imageMatrixCanvas.width = imgWidth;
imageMatrixCanvas.height = imgHeight;
setInterval(function() {
// pick an image
photoIndex = Math.floor(Math.random() * 5);
// fill contexts with random image
imageMatrixCanvasContext.drawImage(pImages[photoIndex],0,0);
imageMatrixData = imageMatrixCanvasContext.getImageData(0,0, imgWidth, imgHeight);
// do some pixel manipulation
// ...
// ...
// choose random destination coords (inside canvas)
destX = Math.floor(Math.random() * (canvasWidth - imgWidth));
destY = Math.floor(Math.random() * (canvasHeight - imgHeight));
// show the work on the image at the random coords
canvasContext.putImageData(imageMatrixData, destX, destY);
}, 500);
}
Oh.. mistake. The memory lookes OK after few test.
But there is another problem.
The size of used memory by tab process is growing when changing the src property of img elements...
Src property = canvas.getContext('2d').toDataURL('image/png') (changing each time);
I've tried to "delete img.src", remove node...
Changing imageMatrixData = ... to var imageMatrixData = ... might help a bit, but I doubt that is the full story. But as far as i can tell imageMatrixData is a global scope variable that you assign on every interval iteration, and that cannot be healthy especially with a big chunk of data :)
I know that getImageData used to memoryleak in Chrome but that was pre version 7, not sure how it is now, and seeing as you are talking about ff4 then that is probably very irrelevant.

Categories