I use canvas to draw an image only, but toDataURL() value is different with raw data even if canvas did not change.
the code as blow:
<html>
<head>
<title>For test</title>
<script type="text/javascript">
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
img.crossOrigin = 'Anonymous';
ctx.drawImage(img, 0, 0);
console.log(c.toDataURL().length);
console.log("data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAAzCAIAAAC43dc6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABPSURBVGhD7dOhAQAgDMAw/n96GCyqZiK5oKZndtPX6Gv0NfoafY2+Rl+jr9HXrO8D/t4nW+lr9DX6Gn2NvkZfo6/R1+hr9DX6Gn3N7r6ZCxmTdxPs4RLCAAAAAElFTkSuQmCC".length);
}
</script>
</head>
<body>
<p>Image to use:</p>
<img id="scream" src="data:img/jpg;base64,iVBORw0KGgoAAAANSUhEUgAAADUAAAAzCAIAAAC43dc6AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABPSURBVGhD7dOhAQAgDMAw/n96GCyqZiK5oKZndtPX6Gv0NfoafY2+Rl+jr9HXrO8D/t4nW+lr9DX6Gn2NvkZfo6/R1+hr9DX6Gn3N7r6ZCxmTdxPs4RLCAAAAAElFTkSuQmCC" /><p>
<p>Canvas:</p>
<canvas id="myCanvas" width="53" height="51"></canvas>
</body>
Simple: you are re-applying the jpeg compression, which is a lossy process. Unless your original image came from a from the same computer, it's a virtually impossibility that they would match exactly. Even if you used the exact same algo, you have to match the quality factor to get a re-encode match. I don't even see the quality being specified in the code (as an argument to toDataURL()).
Even if you used all the same settings, there's a slight bit of voodoo in these encoders, and a device resolution change, zooming, font updates, or a bogged down CPU can all (potentially) affect the output, due to the dynamic nature of canvas layout. Apps like photoshop are much more repeatable than browsers.
Related
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>Test</div>
<canvas id="canvas"></canvas>
<script>
const data = ""//long char,base64 in github
</script>
<script>
function watermarking(file, date, callback) {
const img = new Image();
img.src = file;
img.onload = function () {
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let width = img.width;
let height = img.height;
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0, width, height);
const newBase64 = canvas.toDataURL("image/jpeg");
callback(newBase64);
};
img.onerror = (e) => {
console.log(e);
};
}
watermarking(data, "2022-11-11", (base64) => {
console.log("base64 url:", base64)
})
</script>
</body>
</html>
When I use this image to get base64 url, canvas toDataURL() gets a blank image, other images get the desired effect, I don't know why.
Result from chrome log, there are many A!!!!.
From the results, it should be that the asynchronous somewhere does not take effect, resulting in toDataURL or drawImage() getting a blank image.
enter link description here
There is definitely something odd happening here.
First thing to note, the image that's contained in your data seems to be corrupted, at least Photoshop refuses to open it claiming that "an unexpected end-of-file was encountered.".
And indeed trying with any other image or even the same opened in macOS Preview app, and saved with the same settings does work fine.
So the best is to reencode your image. If it happens more often, you may want to open a new question, explaining how you are producing this image.
As for the odd part, it's that the browser is actually able to open and decode that image, but it seems that Chrome won't do this decode before being asked but still will fire the load event, and it won't wait for the image to be decoded before returning from drawImage. This is a bug on their part. drawImage is synchronous, and it's supposed to wait for the image is decoded before returning. My educated guess is that Chrome expects the decoding to already have happened when the load event fires and thus they don't check in the drawImage code farther than is_loaded.
Anyway, a quick workaround for that is to call the HTMLImageElement.decode() method, which will return a Promise resolving when the decoding is entirely done, even for this seemingly broken image.
But once again the best for you is to reencode that image.
I have multiple HTML5 canvases which are placed on top of one another. The order is important. Canvas 2 will be placed on Canvas 1 and so forth.
a) Is there a way I can create a single image (using toDataURL()) of all of these canvases keeping the same order?
b) And, then how can I copy this image to a clipboard so that it can be pasted in other applications?
Steps:
Create a new Canvas (maybe hidden)
Copy both canvas to the new canvas
Copy to image using canvas.toDataURL()
See this fiddle: http://jsfiddle.net/137f623c/
Let's say you have 3 canvas (2 source and 1 combined - hide this if you want) and an image:
<canvas id="myCanvas1" width="200" height="200"></canvas>
<canvas id="myCanvas2" width="400" height="200"></canvas>
<canvas id="combined" width="400" height="200"></canvas>
<img src="" id="image" />
And, the Script:
var canvas1 = document.getElementById("myCanvas1");
var ctx1 = canvas1.getContext("2d");
ctx1.fillStyle = "red";
ctx1.fillRect(10,10,100,100);
var canvas2 = document.getElementById("myCanvas2");
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "blue";
ctx2.fillRect(50,50,300,100);
var combined = document.getElementById("combined");
var ctx = combined.getContext("2d");
ctx.drawImage(canvas1, 0, 0); //Copying Canvas1
ctx.drawImage(canvas2, 0, 0); //Copying Canvas2
document.getElementById("image").src = combined.toDataURL()
Don't forget consider MarkE's Answer (for part b) and also see for compatibility among browsers. As far for copying I see that most modern browsers have Copy Image when right clicked. For old browsers, :\ uploading to server and downloading might be the solution.
As to part b) of your question...
You will have problems copying the merged canvas content to the clipboard because the required Clipboard API is not yet uniformly or well supported by modern browsers:
http://caniuse.com/#feat=clipboard
A workaround would be to:
Upload the canvas.toDataURL to your server and save it as an image file,
Download that image from the server and add it as an img element.
Then users can right-click-copy the img to their clipboard.
In a client-side standalone JS application, I'm trying to make it so I can call toDataURL() on a canvas on which I've drawn some images specified by a URL. Ie I can input into a textbox the url to any image (hosted on, say, imgur) that I want to draw on the canvas, click a "draw" button and it will draw on the canvas. The end user should be able to save their final render as a single image, for this I'm using toDataURL().
Anyway, until they actually fix that annoying "operation is insecure" error (gee, you're going to tell the end user what they can and can't do with their own data?) I followed a workaround that said to add the image to the DOM and set its crossOrigin property to "Anonmyous" and then draw it to the canvas.
Here's a full working simplified version of my code (but in reality there will be many more features):
<!DOCTYPE html5>
<html>
<head>
<style>
#canvas {border:10px solid green;background-color:black;}
#imgbox {border:2px solid black;}
</style>
</head>
<body>
<canvas id="canvas" width=336 height=336></canvas>
<br><br>
<input size=60 id="imgbox">
<input type="submit" value="Draw" onclick=draw()>
<script>
function draw() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var img = new Image();
img.src = document.getElementById("imgbox").value;
img.crossOrigin = "Anonymous";
context.drawImage(img, 40, 40);
}
</script>
</body>
</html>
Without the img.crossOrigin = "Anonymous"; line, I could input http://i.imgur.com/c2wRzfD.jpg into the textbox and click draw and it would work. However as soon as I added that line, the whole thing broke and it won't even be drawn to the canvas at all.
What do I need to change to fix this? I really need to be able to implement the functionality for the end user to save their final image and it's extremely annoying that the people who wrote the html5 spec purposely introduced this bug.
You must set the CORS request before the src - just swap the lines into:
img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;
You will also need to add an onload handler to the image as loading is asynchronous:
img.onload = function() {
context.drawImage(this, 40, 40);
// call next step in your code here, f.ex: nextStep();
};
img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;
When the server requires authorization to access the images the value should be:
img.crossOrigin = "Use-Credentials";
Otherwise the browser will give up after receiving HTTP 401.
If your image disappears after setting cross origin to anonymous it means your server doesn't allow cross origin. If you're using amazon s3 to serve your images, you need to enable public access to your bucket, and then add cross origin policy (from templates). After that adding cross origin "anonymous" should work.
I'm just trying to figure out how to get an image to draw on a canvas. I followed a tutorial on W3 schools, but when i tried it on my own it doesn't seem to be working. I copy and paste the code below into an HTML file, and the image never loads into the canvas. I downloaded the picture into the same directory. I've been asking around, and looked online, but no one seems to know what the problem is.
I'm using an updated version of chrome (Version 29.0.1547.76 m).
<!DOCTYPE html>
<html>
<body>
<p>Image to use:</p>
<img id="scream" src="img_the_scream.jpg" alt="The Scream" width="220" height="277">
<p>Canvas:</p>
<canvas id="myCanvas" width="250" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
<script>
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
this.ctx.drawImage(img,10,10);
</script>
</body>
</html>
Your image probably hasn't finished loading at the point you are using drawImage:
HTML
Add onload handler in img tag:
<img id="scream" onload="draw()" src="...
Then the function to handle it:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
var img=document.getElementById("scream");
function draw() {
ctx.drawImage(img,10,10);
}
Online demo here
Be aware of that where you place the scripts in your document matters as well.
I would recommend you setting the src attribute in JavaScript as well. That makes it more "safe" to handle the onload (or subscribed event with img.addEventListener('load', ...).
you should use the following approach that first let the image loaded then display:
image.onload = function() {
pic.getContext('2d').drawImage('your image to display', 0,0);
}
"If you try to call drawImage() before the image has finished loading, it won't do anything (or, in older browsers, may even throw an exception). So you need to be sure to use the load event so you don't try this before the image has loaded."
example:
var img = new Image(); // Create new img element
img.addEventListener('load', function() {
// execute drawImage statements here
}, false);
img.src = 'myImage.png'; // Set source path
Source
I'm nearly finished with a Javascript/HTML5-based game, and i've been testing it by using Chrome to open the HTML page on my local file system (i haven't uploaded anything anywhere). I'm using Chrome's file:// protocol to do this. But i'm running into a problem... At the beginning of the game, i display an image for a couple seconds before moving onto the menu screen. I pause the game by grabbing the canvas' pixel data, displaying that, then drawing a semi-transparent rectangle across the whole thing, with a crosshair as a custom pointer. However, Chrome is giving me trouble about a DOM Security Exception 18: "Unable to get image data from canvas because the canvas has been tainted by cross-origin data."
So i did some research on the Internet, and it turns out this is because Chrome sees that the image is grabbed from the local file system, and sees this as a security error. Using this question as a reference, i tried doing some research on Cross-Origin Resource Sharing, but quickly got lost. I figured it would be much easier to simply open the test HTML file using http:// and localhost like the question answerer suggested. But i have no idea how to do this, either.
I'd really like to use Chrome to continue testing my game (the developer tools accessed through Ctrl-Shift-I have proved to be invaluable), so i figured there were three solutions: Either figure out what CORS is and how to use it, learn how to open a local file using http://, or somehow hard-code my image data as a variable in my JavaScript script file (like a XPM file in C). I don't know how to do the first two, and i'm trying to avoid the third.
Yes, it’s probably time to download a local web server or sign up for a hosted server.
But if you want to continue testing without a server, you can sign up for a free dropbox.com account and host your images there.
Dropbox allows access to images using CORS friendly crossOrigin=”anonymous”.
Then CORS is no problem on Chrome & Mozilla. But, IE still fails to be CORS friendly—come on IE :(
Here’s how to load an image without CORS problems from dropbox (Chrome & Mozilla, not IE).
The “secret” is setting image.crossOrigin=”anonymous” before setting the image.src:
var externalImage2=document.createElement("img");
externalImage2.onload=function(){
canvas.width=externalImage2.width;
canvas.height=externalImage2.height;
ctx.drawImage(externalImage2,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage2,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxAnonymous.putImageData(imageData,0,0);
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/YdzHT/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasCORS=document.getElementById("canvasCORS");
var ctxCORS=canvasCORS.getContext("2d");
var canvasAnonymous=document.getElementById("canvasAnonymous");
var ctxAnonymous=canvasAnonymous.getContext("2d");
// Using image WITHOUT crossOrigin=anonymous
// Fails in all browsers
var externalImage1=new Image();
externalImage1.onload=function(){
canvas.width=externalImage1.width;
canvas.height=externalImage1.height;
ctx.drawImage(externalImage1,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage1,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxCORS.putImageData(imageData,0,0);
}
externalImage1.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
// Using image WITH crossOrigin=anonymous
// Succeeds in Chrome+Mozilla, Still fails in IE
var externalImage2=new Image();
externalImage2.onload=function(){
canvas.width=externalImage2.width;
canvas.height=externalImage2.height;
ctx.drawImage(externalImage2,0,0);
// use getImageData to replace blue with yellow
var imageData=recolorImage(externalImage2,0,0,255,255,255,0);
// put the altered data back on the canvas
// this will FAIL on a CORS violation
ctxAnonymous.putImageData(imageData,0,0);
}
externalImage2.crossOrigin = "Anonymous";
externalImage2.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
function recolorImage(img,oldRed,oldGreen,oldBlue,newRed,newGreen,newBlue){
var c = document.createElement('canvas');
var ctx=c.getContext("2d");
var w = img.width;
var h = img.height;
c.width = w;
c.height = h;
// draw the image on the temporary canvas
ctx.drawImage(img, 0, 0, w, h);
// pull the entire image into an array of pixel data
var imageData = ctx.getImageData(0, 0, w, h);
// examine every pixel,
// change any old rgb to the new-rgb
for (var i=0;i<imageData.data.length;i+=4)
{
// is this pixel the old rgb?
if(imageData.data[i]==oldRed &&
imageData.data[i+1]==oldGreen &&
imageData.data[i+2]==oldBlue
){
// change to your new rgb
imageData.data[i]=newRed;
imageData.data[i+1]=newGreen;
imageData.data[i+2]=newBlue;
}
}
return(imageData);
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Original external image</p>
<canvas id="canvas" width=140 height=140></canvas>
<p>.getImageData with .crossOrigin='anonymous'
<p>[Succeeds in Chrome+Mozilla, still fails in IE]</p>
<canvas id="canvasAnonymous" width=140 height=140></canvas>
<p>.getImageData without .crossOrigin='anonymous'
<p>[Fails on all browsers]</p>
<canvas id="canvasCORS" width=140 height=140></canvas>
</body>
</html>
Developing using the local file system is generally not a good idea for precisely the reason you have discovered. To use the localhost option you'll need a web server installed on your PC. Google for a WAMP package (Windows, Apache. MysQL, PHP) which should give you everything you need.
Unfortunately, CORS will only work for you if you have a web server!
[edit] You can get a WAMP server from wampserver.com, obviously!