I am trying to convert canvas to dataURL by calling canvas.dataURL() method. It is working proper for some images with size 938x657 but when I try same function with large image size(for eg. 3750x2625, 7500x5250, etc) it returns "data:,"
Code:
var img = new Image();
img.src = app.getThemeLocalStorage("imgPath");
img.onload = function()
{
alert("width:"+img.width+" height:"+img.height);
$("#DummyCanvas").attr("width", img.width + "px");
$("#DummyCanvas").attr("height", img.height + "px");
var dummyCAN = document.getElementById("DummyCanvas");
var ctx = dummyCAN.getContext("2d");
ctx.drawImage(img, 0, 0);
var data = dummyCAN.toDataURL("image/png");
alert(data); //Alerts data url for image size 938x657 but blanks for 3750x2625
}
Any suggestion/solution?
Related
In my vb.net Project with Selenium, I have a set of JS statements to convert html to canvas to image and then download to file. The code so far is as follows:-
driver.ExecuteScript("
var url = '" + str + "';
var pages = [],
heights = [],
width = 0,
height = 0,
currentPage = 1;
var scale = 1.5;
PDFJS.disableWorker = true; // due to CORS
PDFJS.getDocument(url).then(function(pdf) {
pdf.getPage(currentPage).then(function(page) {
console.log('Printing ' + currentPage);
var viewport = page.getViewport({
scale: scale,
});
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render(renderContext).then(function() {
pages.push(ctx.getImageData(0, 0, canvas.width, canvas.height));
heights.push(height);
height += canvas.height;
if (width < canvas.width) width = canvas.width;
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
for (var i = 0; i < pages.length; i++)
ctx.putImageData(pages[i], 0, heights[i]);
document.body.appendChild(canvas);
var element = document.createElement('a');
element.setAttribute('href', document.getElementById('rakCanvas').toDataURL('image/png'));
element.setAttribute('download', '" + fileName + "');
console.log('Raky ========>' + url);
element.style.display = 'block';
document.body.appendChild(element);
element.click();
//document.body.removeChild(element);
return 'Done Rak';
});
});
});
Using jsfiddle, I found the JS code to be without any syntax errors. There are no errors during compiling and execution. And there is no file downloaded. To figure out the problem, I am looking for a way to monitor execution of each statement of above js code.
Alternatively, if there is a code chuck that could help in just converting the HTML canvas to Image to be downloaded as a file.
i am using cordova for my ios app which captures the image
the code looks
navigator.camera.getPicture(onSuccessCamera, onFailureCamera, {
quality: 25,
destinationType: navigator.camera.DestinationType.DATA_URL,
correctOrientation: true,
allowEdit:false
});
function onSuccessCamera(imageURI) {
var imgData = "data:image/jpeg;base64," + imageURI;
uploadFile(imgData);
}
function uploadFile(file){
var c=document.getElementById("picture");
c.width = window.innerWidth-50;//offset to prevent image flowing out of frame
// window.alert(window.innerWidth+":"+ window.innerHeight);414:736
c.height = "330";//window.innerHeight;//this.height;
var ctx=c.getContext("2d");
var showImg = new Image();
showImg.onload = function(){
var ratio = 1;
if (this.height > c.height) {
ratio = c.height/this.height;
}
else if (this.width>c.width) {
ratio = c.width/this.width;
}
ctx.drawImage(this,0,0, this.width*ratio, this.height*ratio);
// window.alert(c.width + ':' + c.height);
};
showImg.src = file;
}
i dont know why the image looks so horrible
It's because of the retina screen.
Make the height and width of the canvas to be the double, but then style it to be half pixels
c.width = (window.innerWidth-50)*2;//offset to prevent image flowing out of frame
c.height = 330*2;//window.innerHeight;//this.height;
c.style.width = (c.width/2)+"px";
c.style.height = (c.height/2)+"px";
Also, you can consider using a higher value on quality camera option.
I have added background image to canvas using css.
But when converting it into png using canvas.toDataURL() , its not showing background image.
It seems like its not parsing css background image.
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
d3.select('canvas')
.attr('style', 'background-image:url("chart.png");background-position:50%;background-repeat:no-repeat');
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
//var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var svg = new Blob([svgString], {type: "image/svg+xml"});
var url = DOMURL.createObjectURL(svg);
var png = '';
img.onload = function() {
ctx.drawImage(img, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="'+png+'"/>';
DOMURL.revokeObjectURL(png);
img.src = "";
};
img.crossOrigin = "anonymous";
img.src = url;
As said in comments, background-image is not part of the canvas context and thus, can't be exported by any canvas' export methods.
If you want to get this image drawn on the canvas, simply draw it before you draw the other image.* The main difficulty being to reproduce the different option CSS background has. But in your case, it's quite simple.
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var svg = new Blob([svgString], {
type: "image/svg+xml"
});
var url = DOMURL.createObjectURL(svg);
var png = '';
var toLoad = 2;
var loaded = 0;
var background = new Image();
var svgImg = new Image();
// attach the event to both images
background.onload = svgImg.onload = function(){
// only when both has loaded
if(++loaded === toLoad){
// set the canvas size to the svg image's one
canvas.width = svgImg.width;
canvas.height = svgImg.height;
// draw the background image first
ctx.drawImage(background, (canvas.width/2)-(background.width/2), (canvas.height/2)-(background.height/2));
// then the svg image
ctx.drawImage(svgImg, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="' + png + '"/>';
// you want to revoke the svgImg ObjectURL, not the canvas dataURI
DOMURL.revokeObjectURL(svgImg.src);
}
}
svgImg.src = url;
// set it only if you're doing a cross origin request
// Object URL are not
background.crossOrigin = 'anonymous';
background.src = 'https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png'
svg{border: 1px solid red;}
canvas{border: 1px solid blue;}
img{border: 1px solid green;}
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="55" height="55" fill-opacity=".5"/>
</svg>
<canvas id="canvas"></canvas>
<p id="chart"></p>
*As said by markE, if you can't draw the background before, you can also set the globalCompositeOperation of your context to destination-over in order to draw behind the actual content.
Now, this wasn't clearly part of your question, but if you tried to set the background-image to your svg node, you should have been able to draw it on your canvas image, as part of the svg.
But since HTMLImage (<img>) element can't load any external resources form its loaded media, you would have to first convert the image to a dataURI version, and append the styles either as the svg's style attribute, either in a <style> element appended inside the svg element.
Here is a ugly example that will convert an svg element with a CSS defined background-image, and draw it on the canvas.
Don't use it, this just here for the demo but is likely to break. Also, this has only been quickly tested against FF, Chrome and Safari.
function convertSVGWithBG(svg, callback) {
// this function is not be bullet proof, better if you know the urls in advance
// in case of multi-images
var imgsToLoad = getComputedStyle(svg).backgroundImage.split(',');
appendDocumentStylesTo(svg);
var toLoad = imgsToLoad.length,
loaded = 0,
// we need to keep it sorted
rules = [],
canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
imgsToLoad.forEach(function(s, i) {
var bgURL = parseURL(s),
bgImg = new Image();
bgImg.crossOrigin = 'anonymous';
var errored = false;
bgImg.onload = function() {
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0, 0);
rules[i] = canvas.toDataURL();
if (++loaded === toLoad) {
onend();
}
bgImg.onerror = function() {
if (!errored) {
errored = true;
this.crossOrigin = null;
this.removeAttribute('crossorigin');
this.src = this.src;
} else {
console.warn('failed to load img at src ', this.src);
if (--toLoad === loaded) {
onend();
}
}
};
};
bgImg.src = bgURL;
function onend() {
var toLoad = rules.filter(function(r) {
return r;
}).length,
loaded = 0;
// wait for the dataURI version has loaded too
rules.forEach(function(url) {
var img = new Image();
img.onload = function() {
if (++loaded === toLoad) {
callback(svg);
}
};
img.src = url
});
// it has to be inline style, or appended in a <style> element directly in our svg element
var fullRule = 'url(' + rules.join('), url(') + ')';
svg.style.backgroundImage = fullRule;
}
});
function parseURL(str) {
// this is ugly and there should be a better way to do so (maybe regex)
var url = str.split('"')[1];
if (!url) {
url = str.split("'")[1];
}
if (!url) {
url = str.split('(')[1].split(')')[0];
}
return url;
}
}
function svgToCanvas(svg) {
var svgString = new XMLSerializer().serializeToString(svg);
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var svg = new Blob([svgString], {
type: "image/svg+xml"
});
var url = DOMURL.createObjectURL(svg);
var svgImg = new Image();
svgImg.onload = function() {
canvas.width = svgImg.width;
canvas.height = svgImg.height;
ctx.drawImage(svgImg, 0, 0);
png = canvas.toDataURL("image/png");
document.querySelector('#chart').innerHTML = '<img src="' + png + '"/>';
DOMURL.revokeObjectURL(svgImg.src);
}
svgImg.src = url;
}
var svg = document.querySelector('svg');
convertSVGWithBG(svg, svgToCanvas);
// this is completely unstable and should never be used...
function appendDocumentStylesTo(element) {
var docStyleSheets = document.styleSheets;
var i = 0,
j, rules, r, key;
for (i; i < docStyleSheets.length; i++) {
rules = docStyleSheets[i].cssRules;
for (j = 0; j < rules.length; j++) {
r = rules[j];
if (element.matches(r.selectorText)) {
if (r.style) {
for (k = 0; k < r.style.length; k++) {
key = r.style[k];
// ugly hack for Safari
if (key.indexOf('repeat') > -1) {
key = 'background-repeat';
}
element.style[key] = r.style[key];
}
}
}
}
}
}
canvas {
border: 1px solid blue;
}
img {
border: 1px solid green;
}
svg {
background-image: url(https://dl.dropboxusercontent.com/s/rumlhyme6s5f8pt/ABC.png), url(https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png);
background-position: 50%;
background-repeat: no-repeat;
}
<svg width="120" height="120" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="55" height="55" fill-opacity=".5" />
</svg>
<canvas id="canvas"></canvas>
<p id="chart"></p>
This solution is working
var svgString = new XMLSerializer().serializeToString(document.querySelector('svg'));
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
//var svg = new Blob([svgString], {type: "image/svg+xml;charset=utf-8"});
var svg = new Blob([svgString], {type: "image/svg+xml"});
var url = DOMURL.createObjectURL(svg);
var png = '';
var emailConfirmationMessage = '';
var img1 = new Image();
img.onload = function() {
ctx.drawImage(img1, 136, 136);
ctx.drawImage(img, 0, 0);
sendEmail(canvas,DOMURL);
};
img1.onload = function(){
img.src = url;
};
img1.src = 'chart1.png';
I have a website which targets mobile phones, if a user clicks on a 16:9 scaled responsive image, I want clicked image to be displayed landscape, so that the user can rotate his phone to view the image in full size. I can't save the image rotated because it is an external resource. Is there any way I can achieve the desired effect?
I can't simply rotate 90° because that would rotate the scaled image.
If I rotate the unscaled image, it extends the portrait viewport to landscape width and displays the rotated image centered.
I already tried using a canvas, but it didn't nearly fit my needs, the image gets destroyed by bad downscaling on Firefox using JSFiddle while it didn't display at all on the Intel XDK emulator.
Since my scenario doesn't seem that uncommon to me, I'm wondering whether I missed an easy solution.
Found a jsfiddle doing that, and made it simpler for you.
Check it out: http://jsfiddle.net/7fw66/39/
html:
<div>
<canvas id="canvas"><img id="image" src="http://3.bp.blogspot.com/-JhISDA9aj1Q/UTECr1GzirI/AAAAAAAAC2o/5qmvWZiCMRQ/s1600/Twitter.png" /></canvas>
</div>
javascript:
var screenwidth = 400;
var imgurl = "http://3.bp.blogspot.com/-JhISDA9aj1Q/UTECr1GzirI/AAAAAAAAC2o/5qmvWZiCMRQ/s1600/Twitter.png";
$(document).ready(function () {
loadImage();
$('#restore').click(function () {
loadImage();
});
$('#rotate_ccw').click(function () {
rotate_ccw();
});
$('#rotate_cw').click(function () {
rotate_cw();
});
});
function loadImage() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.setTransform(1, 0, 0, 1, 0, 0);
var img = new Image();
if (imgurl == null || imgurl == "") {
imgurl = defaultimgurl;
}
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = w;
canvas.height = h;
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h);
};
ctx.save();
}
function rotate_cw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = h;
canvas.height = w;
ctx.translate(w - h, w);
ctx.rotate((-90 * Math.PI) / 180);
ctx.translate(0, -(w - h));
ctx.drawImage(img, 0, 0, w, h);
ctx.save();
}
function rotate_ccw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imgurl;
var maxsize = screenwidth;
var w = maxsize;
var ratio = (img.width / w);
var h = (img.height / ratio);
canvas.width = h;
canvas.height = w;
ctx.translate(h, 0);
ctx.rotate((90 * Math.PI) / 180);
ctx.drawImage(img, 0, 0, w, h);
ctx.save();
}
function urlProvided() {
var userinput = document.getElementById('imageurl');
imgurl = userinput.value;
loadImage();
}
I have some JS that makes some manipulations with images. I want to have pixelart-like graphics, so I had to enlarge original images in graphics editor.
But I think it'd be good idea to make all the manipulations with the small image and then enlarge it with html5 functionality. This will save bunch of processing time (because now my demo (warning: domain-name may cause some issues at work etc) loads extremely long in Firefox, for example).
But when I try to resize the image, it gets resampled bicubically. How to make it resize image without resampling? Is there any crossbrowser solution?
image-rendering: -webkit-optimize-contrast; /* webkit */
image-rendering: -moz-crisp-edges /* Firefox */
http://phrogz.net/tmp/canvas_image_zoom.html can provide a fallback case using canvas and getImageData. In short:
// Create an offscreen canvas, draw an image to it, and fetch the pixels
var offtx = document.createElement('canvas').getContext('2d');
offtx.drawImage(img1,0,0);
var imgData = offtx.getImageData(0,0,img1.width,img1.height).data;
// Draw the zoomed-up pixels to a different canvas context
for (var x=0;x<img1.width;++x){
for (var y=0;y<img1.height;++y){
// Find the starting index in the one-dimensional image data
var i = (y*img1.width + x)*4;
var r = imgData[i ];
var g = imgData[i+1];
var b = imgData[i+2];
var a = imgData[i+3];
ctx2.fillStyle = "rgba("+r+","+g+","+b+","+(a/255)+")";
ctx2.fillRect(x*zoom,y*zoom,zoom,zoom);
}
}
More: MDN docs on image-rendering
I wrote a NN resizing script a while ago using ImageData (around line 1794)
https://github.com/arahaya/ImageFilters.js/blob/master/imagefilters.js
You can see a demo here
http://www.arahaya.com/imagefilters/
unfortunately the builtin resizing should be slightly faster.
This CSS on the canvas element works:
image-rendering: pixelated;
This works in Chrome 93, as of September 2021.
You can simply set context.imageSmoothingEnabled to false. This will make everything drawn with context.drawImage() resize using nearest neighbor.
// the canvas to resize
const canvas = document.createElement("canvas");
// the canvas to output to
const canvas2 = document.createElement("canvas");
const context2 = canvas2.getContext("2d");
// disable image smoothing
context2.imageSmoothingEnabled = false;
// draw image from the canvas
context2.drawImage(canvas, 0, 0, canvas2.width, canvas2.height);
This has better support than using image-rendering: pixelated.
I'll echo what others have said and tell you it's not a built-in function. After running into the same issue, I've made one below.
It uses fillRect() instead of looping through each pixel and painting it. Everything is commented to help you better understand how it works.
//img is the original image, scale is a multiplier. It returns the resized image.
function Resize_Nearest_Neighbour( img, scale ){
//make shortcuts for image width and height
var w = img.width;
var h = img.height;
//---------------------------------------------------------------
//draw the original image to a new canvas
//---------------------------------------------------------------
//set up the canvas
var c = document.createElement("CANVAS");
var ctx = c.getContext("2d");
//disable antialiasing on the canvas
ctx.imageSmoothingEnabled = false;
//size the canvas to match the input image
c.width = w;
c.height = h;
//draw the input image
ctx.drawImage( img, 0, 0 );
//get the input image as image data
var inputImg = ctx.getImageData(0,0,w,h);
//get the data array from the canvas image data
var data = inputImg.data;
//---------------------------------------------------------------
//resize the canvas to our bigger output image
//---------------------------------------------------------------
c.width = w * scale;
c.height = h * scale;
//---------------------------------------------------------------
//loop through all the data, painting each pixel larger
//---------------------------------------------------------------
for ( var i = 0; i < data.length; i+=4 ){
//find the colour of this particular pixel
var colour = "#";
//---------------------------------------------------------------
//convert the RGB numbers into a hex string. i.e. [255, 10, 100]
//into "FF0A64"
//---------------------------------------------------------------
function _Dex_To_Hex( number ){
var out = number.toString(16);
if ( out.length < 2 ){
out = "0" + out;
}
return out;
}
for ( var colourIndex = 0; colourIndex < 3; colourIndex++ ){
colour += _Dex_To_Hex( data[ i+colourIndex ] );
}
//set the fill colour
ctx.fillStyle = colour;
//---------------------------------------------------------------
//convert the index in the data array to x and y coordinates
//---------------------------------------------------------------
var index = i/4;
var x = index % w;
//~~ is a faster way to do 'Math.floor'
var y = ~~( index / w );
//---------------------------------------------------------------
//draw an enlarged rectangle on the enlarged canvas
//---------------------------------------------------------------
ctx.fillRect( x*scale, y*scale, scale, scale );
}
//get the output image from the canvas
var output = c.toDataURL("image/png");
//returns image data that can be plugged into an img tag's src
return output;
}
Below is an example of it in use.
Your image would appear in the HTML like this:
<img id="pixel-image" src="" data-src="pixel-image.png"/>
The data-src tag contains the URL for the image you want to enlarge. This is a custom data tag. The code below will take the image URL from the data tag and put it through the resizing function, returning a larger image (30x the original size) which then gets injected into the src attribute of the img tag.
Remember to put the function Resize_Nearest_Neighbour (above) into the <script> tag before you include the following.
function Load_Image( element ){
var source = element.getAttribute("data-src");
var img = new Image();
img.addEventListener("load", function(){
var bigImage = Resize_Nearest_Neighbour( this, 30 );
element.src = bigImage;
});
img.src = source;
}
Load_Image( document.getElementById("pixel-image") );
There is no built-in way. You have to do it yourself with getImageData.
Based on Paul Irish's comment:
function resizeBase64(base64, zoom) {
return new Promise(function(resolve, reject) {
var img = document.createElement("img");
// once image loaded, resize it
img.onload = function() {
// get image size
var imageWidth = img.width;
var imageHeight = img.height;
// create and draw image to our first offscreen canvas
var canvas1 = document.createElement("canvas");
canvas1.width = imageWidth;
canvas1.height = imageHeight;
var ctx1 = canvas1.getContext("2d");
ctx1.drawImage(this, 0, 0, imageWidth, imageHeight);
// get pixel data from first canvas
var imgData = ctx1.getImageData(0, 0, imageWidth, imageHeight).data;
// create second offscreen canvas at the zoomed size
var canvas2 = document.createElement("canvas");
canvas2.width = imageWidth * zoom;
canvas2.height = imageHeight * zoom;
var ctx2 = canvas2.getContext("2d");
// draw the zoomed-up pixels to a the second canvas
for (var x = 0; x < imageWidth; ++x) {
for (var y = 0; y < imageHeight; ++y) {
// find the starting index in the one-dimensional image data
var i = (y * imageWidth + x) * 4;
var r = imgData[i];
var g = imgData[i + 1];
var b = imgData[i + 2];
var a = imgData[i + 3];
ctx2.fillStyle = "rgba(" + r + "," + g + "," + b + "," + a / 255 + ")";
ctx2.fillRect(x * zoom, y * zoom, zoom, zoom);
}
}
// resolve promise with the zoomed base64 image data
var dataURI = canvas2.toDataURL();
resolve(dataURI);
};
img.onerror = function(error) {
reject(error);
};
// set the img soruce
img.src = base64;
});
}
resizeBase64(src, 4).then(function(zoomedSrc) {
console.log(zoomedSrc);
});
https://jsfiddle.net/djhyquon/69/