DOM Security Exception 18: Tainted Canvas - javascript

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!

Related

Use emoji as favicon in websites

Emojis are awesome, so I was thinking how could add one as a favicon using link tag.
One possible solution:
I found one possible way in this blog post here. Based on that it's been achieved what I wanted so far with the following solution.
JavaScript code snippet:
const setFavicon = (emoji) => {
const canvas = document.createElement('canvas');
canvas.height = 32;
canvas.width = 32;
const ctx = canvas.getContext('2d');
ctx.font = '28px serif';
ctx.fillText(emoji, -2, 24);
const favicon = document.querySelector('link[rel=icon]');
if (favicon) { favicon.href = canvas.toDataURL(); }
}
setFavicon('🐢');
The link tag in HTML:
<link rel="icon" type="image/png" href="favicon.ico"/>
So my question:
Maybe creating favicon.ico file for this purpose would do the thing also. Is there any better way to achieve this without converting or having extra JavaScript snippets in your code? Thank you!
Now that all major browsers support SVG favicons, it's possible to refer to an SVG that contains the emoji inside:
<!-- favicon.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
<text y="32" font-size="32">🚀</text>
</svg>
Link it up like you'd normally do:
<link rel="icon" href="/favicon.svg" />
Another way to do it is:
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎯</text></svg>">
From: https://css-tricks.com/emojis-as-favicons/
Favicon are images, so just make a static image and use that.
You're not going to change it constantly, so there's no need to make people's computers spend the time running JS to generate an asset that is always the same, better to generate it once, save that as your favicon image file, and point to that. As a bonus, browsers will cache it, so instead of recomputing it every time, or even downloading it every time, as a favicon it'll only be downloaded once and then never again.
Also note that you don't need the .ico extension (it's the extension for an ancient image format), instead just use the extension the image is supposed to have, and set the type to the correct image mime type.
Making a screenshot is probably the better and easier solution (as Mike mentioned).
And there is one more advantage of this solution:
Emojis can look very different on different platforms.
An example:
You won't need to handle that problem, too..

How can I make a image appear in javascript (and scroll whilst zoomed in)

I am very new to JavaScript. I'm trying to create a basic infinite runner game, but I'm stuck on one little issue. I need a image to print out on my HTML canvas, but when I try to nothing happens. I am creating this game with basic JavaScript. No AJAX/jQuery. Here's my code, and what I have tried.
//this is my code as of right now for printing images.
function make_base () {
base_image = new Image();
base_image.src = 'picture.png'
}
//draw the image to the canvas 2d context
cc.drawImage(base_image, 0, 0);
What this is doing, is nothing. It is not showing any sign of a image. Here's some other code I've gotten, as from w3schools (they're awesome :) )
//image source by html hidden element
var img = document.getElementById("picture");
cc.drawImage(img, 10, 10);
</script>
<image src="picture.png" id="picture" hidden></image>
This is doing the same thing that the new code is doing. Showing absolutely nothing. I'm not sure why, I've tried with Google Chrome, Firefox, and Internet Explorer. All of them with the file uploaded to my website, and with file:///c:/users/name/desktop/js/one.html. Nothing is showing up on the canvas for some strange reason. I've also told Chrome to always use JavaScript. Not only this, but how can I make it zoom in, and scroll from left to right (I can add the blocks/variables that "kill"/make the player restart)?
You probably need to wait for the image to load before trying to draw it.
const c = document.querySelector('canvas')
const cc = c.getContext('2d');
drawImage();
function drawImage() {
const image = new Image();
image.onload = function() {
cc.drawImage(image, 0, 0)
}
image.src = "//placecage.com/200/300"
}
<canvas width="500" height="500"></canvas>
Also, it's <img> not <image>. I haven't checked this, but some browsers don't allow loading things if you're on file://, you'll have to spin up a small server and serve from localhost.

Drawing images to canvas with img.crossOrigin = "Anonymous" doesn't work

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.

canvas has been tainted by cross-origin data work around

im writing a script (or editing and hacking things together) to edit the look of images on a page. I know the basics of javascript but this is my first time looking at canvas. so bear with me
I'm getting this error:
Unable to get image data from canvas because the canvas has been tainted by cross-origin data.
so heres my code snippet throwing the error:
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
height = img.naturalHeight || img.offsetHeight || img.height,
width = img.naturalWidth || img.offsetWidth || img.width,
imgData;
canvas.height = height;
canvas.width = width;
context.drawImage(img, 0, 0);
console.log(context);
try {
imgData = context.getImageData(0, 0, width, height);
} catch(e) {}
now i found this post :
http://bolsterweb.com/2012/06/grabbing-image-data-external-source-canvas-element/
but i have no idea how to make it fit my needs..
I know its all due to security and all that - but is there a work around to make it all happen?
Thanks
EDIT
Oh wait.. the error is because you can't getImageData.. so is there away to make it 'local'
To satisfy CORS, you can host your images on a CORS friendly site like dropbox.com
Then the security error will not be triggered if you speify image.crossOrigin="anonymous":
var image=new Image();
image.onload=function(){
}
image.crossOrigin="anonymous";
image.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/4djSr/
<!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 image=new Image();
image.onload=function(){
ctx.drawImage(image,0,0);
// desaturation colors
var imgData=ctx.getImageData(0,0,canvas.width,canvas.height);
var data=imgData.data;
for(var i = 0; i < data.length; i += 4) {
var grayscale= 0.33*data[i]+0.5*data[i+1]+0.15*data[i+2];
data[i]=grayscale;
data[i+1]=grayscale;
data[i+2]=grayscale;
}
// write the modified image data
ctx.putImageData(imgData,0,0);
}
image.crossOrigin="anonymous";
image.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/colorhouse.png";
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=140 height=140></canvas>
</body>
</html>
I don't have enough reputation to actually add a comment (but I do have enough to respond to the question, huh??), I just wanted to add to markE's answer that I had to capitalize Anonymous.
image.crossOrigin = "Anonymous"
I found it works with chrome if you create a windows shortcut like this:
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files
Shut down chrome and start it via your shortcut.
Source:
https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally
Related post:
Cross-origin image load denied on a local image with THREE.js on Chrome
I assume you are working with a local file without any web server.
Try adding crossorigin="anonymous" to the image element. For example
<img src="http://example.com/foo.jpg" crossorigin="anonymous"/>
Enabling CORS on the server side is a way out, But that's if you have access to the server side. That way the server serves CORS enabled images.

How to resize an image with JavaScript / jQuery without pixelation like the browser does?

I'm making a javascript / jquery program for a webpage to let a user open an image file locally from their computer, and then the program resizes it to a 16 x 16 icon size and saves it as a data url.
Everything works fine, except the resized image is very pixelated. I currently am taking the local image file, and making a data url from it. Then creating a 16 x 16 canvas element, and then drawing the image onto the canvas, and making a new data url from that.
I would like some different way to do it, as the newly resized image is very pixelated and not smooth, and does not seem anti-aliased. When the browser displays the original image with the width and height attributes set to 16, it looks very nice and smooth. This is what I would like. I don't understand how to get the same result as what is displayed when the browser does it. I am using Google Chrome.
I made a small example of it here: http://jsfiddle.net/5Pj8m/
In the example I used the jsFiddle logo, although you could test it with any local file, and see the results. Maybe this code can help someone else learn how to do it, but still, I think the resulting resize image could or should look much better!
I hope I have explained what I am trying to do, and that it is somehow possible. Can anyone help me or figure this out?
Here is the code.
HTML:
<input type='file' id='inputFile'>
<table border=1><tr><td>
<span id='spanDisplayOrigFull'>OrigFull:
<img src=http://doc.jsfiddle.net/_images/jsfiddle-logo-thumb.png>
</span>
</td><td>
<span id='spanDisplayOrigIcon'>OrigIcon:
<img src=http://doc.jsfiddle.net/_images/jsfiddle-logo-thumb.png width='16' height='16'>
</span>
</td></tr><tr><td>
<span id='spanDisplayNewFull'>NewFull: </span>
</td><td>
<span id='spanDisplayNewIcon'>NewIcon: </span>
</td></tr></table>
JAVASCRIPT:
$('#inputFile').bind('change', function()
{
var file = inputFile.files[0];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function()
{
var imageUrlFull = reader.result;
var imageLocalFull = new Image();
imageLocalFull.src = imageUrlFull;
imageLocalFull.id = 'imageLocalFull';
imageLocalFull.onload = function()
{
var canvas = document.createElement('canvas');
canvas.width = 16; canvas.height = 16;
var context = canvas.getContext('2d');
context.drawImage(imageLocalFull, 0, 0, 16, 16);
var imageUrlIcon = canvas.toDataURL(file.type);
var imageLocalIcon = new Image();
imageLocalIcon.src = imageUrlIcon;
imageLocalIcon.id = 'imageLocalIcon';
imageLocalIcon.onload = function()
{
spanDisplayNewFull.appendChild(imageLocalFull);
spanDisplayNewIcon.appendChild(imageLocalIcon);
};
};
};
});
You could use a php base like timthumb script to scale.
So the basic idea is using js detect when the browser or div or whatever change or resize. Then trigger the php script to resize the image.
script.setAttribute( 'src', 'remote.php?value=my message' );
remote.php
you have your php code.
Well, from your code it looks like the image scaling job is actually left to your browser's implementation, so I think you're going to have varying results with different browsers. Some might smooth it, some might not.
I see two ways to go about trying to tackle this problem. One way is to use active content, such as a Java applet/Flash or to use an actual back-end to do the scaling. I.e. you receive the file, send it to a server and the server returns the scaled-down image, as most image scaling sites do.
The second way is to try HTML5 and implement a down-scaling algorithm yourself. The best I can do there for guidance is point you to this and this as a starting point.
Sorry if that doesn't help.

Categories