javascript fixing canvas tainted in MDN example - javascript

I am working my way through MDN Canvas tutorial.
Working well until I get to Pixel manipulation, a color picker example.
getImageData gives:
MDNTutorialBaseppp.html:21 Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
at HTMLCanvasElement.pick
Here's the code I'm running:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<canvas id="canvas" width="600" height="300"></canvas>
<script>
var img = new Image();
//img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.src = 'photo2.jpg';
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
img.onload = function() {
ctx.drawImage(img, 0, 0);
img.style.display = 'none';
};
var color = document.getElementById('color');
function pick(event) {
var x = event.layerX;
var y = event.layerY;
var pixel = ctx.getImageData(x, y, 1, 1);
var data = pixel.data;
var rgba = 'rgba(' + data[0] + ', ' + data[1] +
', ' + data[2] + ', ' + (data[3] / 255) + ')';
color.style.background = rgba;
color.textContent = rgba;
}
canvas.addEventListener('mousemove', pick);
</script>
</body>
</html>
Been beating on for several hours over several.
Can anyone spot the error of my ways.

The comments on this question provide an approach to working through this issue for the particular case presented. You need to be running a local server which is easy to do. The underlying issue is a complicated topic that I don't pretend to fully understand.

Related

How can I get RGB array from img?

I am trying to get the RGB array of img using canvas like this:
Teplate
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="display"></div>
<img id="face" src="./220px-Leonhard_Euler.jpg" />
<canvas
id="face2"
width="112"
height="112"
style="border: 1px solid #d3d3d3;"
></canvas>
<script src="src/index.js"></script>
</body>
</html>
Script
const img = document.getElementById("face");
img.crossOrigin = "anonymous";
const canvas = document.getElementById("face2");
const context = canvas.getContext("2d");
context.drawImage(img, 0, 0);
let pix = context.getImageData(0, 0, 112, 112).data;
pix = Array.from(pix);
const pix2 = [];
for (let i = 0; i < pix.length; i += 4) {
pix2.push(pix[i + 0]);
pix2.push(pix[i + 1]);
pix2.push(pix[i + 2]);
}
const imgData = context.createImageData(112, 112);
for (let i = 0; i < imgData.data.length; i += 4) {
imgData.data[i + 0] = pix2[i + 0];
imgData.data[i + 1] = pix2[i + 1];
imgData.data[i + 2] = pix2[i + 2];
imgData.data[i + 3] = 255;
}
context.putImageData(imgData, 10, 10);
As you can see in the first part of the code I try to extract the 4d array of the image RGBA, and try to remove the alpha channel.
After that, I tried to set again the image with a default alpha channel like 255 only to check that I got the RGB data only.
But I got the following image:
The input image is 112x112 and the canvas is 112x112, any idea about why I could get only the RGB of the img?
EDIT2
The #tracktor response worked but I still get error with another image like this:
Same code the only difference is that I load this image using http://localhost:8080/face.jpg instead of crossorigin.
Thanks
HTMLImageElement crossorigin attribute
Put a crossorigin attribute on the img tag in HTML to ensure the browser requests the image with headers for a CORS operation, such as
Origin: http://localhost
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
In return the site responding with the image data must include an "Access-Control-Allow-Origin" header set from the "origin" request header, such as
Access-Control-Allow-Origin: http://localhost
or the wildcard version:
Access-Control-Allow-Origin: *
to enable javascript access to the data returned.
Note https://i.stack.imgur.com does not return access control headers, so images returned from that domain will taint canvas objects. I highly recommend reading http://expressjs.com/resources/middleware/cors.html if you are are writing or modifying a node/express server to support CORS.
window load event
Wait until after the window load event fires to process image data. If processed earlier image loading may not be available.
Corrupt image results
The incorrect image data shown in the updated post is caused by not alligning the 3-tuples of rgb data in pix2 with the 4-tuples of rgba data required in imageData.data. To transpose the pixel data successfully try something equivalent to:
for (let i = 0, j = 0; i < imgData.data.length; i += 4) {
imgData.data[i + 0] = pix2[j++];
imgData.data[i + 1] = pix2[j++];
imgData.data[i + 2] = pix2[j++];
imgData.data[i + 3] = 128; // test value
}
The following snippet creates two canvas elements: one to show the image loaded and another to show the result of canvas data manipulation. The code uses localhost servers on different ports and won't run successfully without them:
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>cross origin</title></head>
<body>
<h2>Image</h2>
<img id="face" crossorigin src="http://localhost:8080/share/imperial.jpg" />
<h2>Canvas</h2>
<canvas
id="face2"
width="112"
height="112"
style="border: 1px solid blue"
></canvas>
<canvas
id="modified"
width="112"
height="112"
style="border: 1px solid red"
></canvas>
<script type="text/javascript">"use strict";
window.addEventListener("load", ()=> {
const img = document.getElementById("face");
const canvas = document.getElementById("face2");
const context = canvas.getContext("2d");
context.drawImage(img, 0, 0);
let imgData1 = context.getImageData(0, 0, 112, 112)
let pix;
try {
pix = imgData1.data;
}
catch( err) {
console.warn( err.message);
}
pix = Array.from(pix);
const pix2 = [];
for (let i = 0; i < pix.length; i += 4) {
pix2.push(pix[i + 0]);
pix2.push(pix[i + 1]);
pix2.push(pix[i + 2]);
}
const imgData = context.createImageData(112, 112);
for (let i = 0, j = 0; i < imgData.data.length; i += 4) {
imgData.data[i + 0] = pix2[j++];
imgData.data[i + 1] = pix2[j++];
imgData.data[i + 2] = pix2[j++];
imgData.data[i + 3] = 128; // test value
}
const canvas2 = document.getElementById("modified");
const context2 = canvas2.getContext("2d");
context2.putImageData(imgData, 10, 10);
});
</script></body>
</html>

Get all 0's in return after use getImageData()

I'm drawing an image on my canvas and trying to use getImageData() from canvas to get the image data but I got all 0's in return. From my understanding, the image hasn't finished a load so I get the 0's. From this issue: getImageData always returning 0 seems like I need to wait by using onload. Is there another way to get the data by not use onload function? I want to declare an image tag in my html. Here is my code:
<html>
<head>
</head>
<body>
<script>
var bgMap = {
"bg1": "image1.JPG",
"bg2": "image2.JPG"
}
</script>
<center>
<select id="selectBg" onchange="document.getElementById('bgImage').src = bgMap[this.value]">
<option value="bg1">Bg1</option>
<option value="bg2">Bg2</option>
</select>
</center>
<img id='bgImage' src='image1.JPG' width=500 height=500 />
<canvas id='bgCanvas'></canvas>
<script>
var canvas = document.getElementById('bgCanvas')
var bgImage = document.getElementById('bgImage')
canvas.width = 500
canvas.height = 500
var ctx = canvas.getContext("2d")
ctx.drawImage(bgImage, 0, 0, 500, 500)
var imageData = ctx.getImageData(0, 0, 500, 500)
var rgba = imageData.data;
for (var px = 0, ct = 500 * 500 * 4; px < ct; px += 4) {
var r = rgba[px];
var g = rgba[px + 1];
var b = rgba[px + 2];
var a = rgba[px + 3];
console.log(r,g,b,a)
}
console.log(rgba)
</script>
</body>
</html>

Reading pixel data by JavaScript thought Canvas

I have a problem t read the pixel's RGBA data from a image.But I am facing that all RGBA data( all 4 bytes for all pixel ) zero value.
i use this code for JavaScript :
By the way I use this code for html.
and tehn I run html by Chrome or Firefox but When I see the console log the all value of pixel Data is Zero.Why?
var canvas= document.getElementById('mycanvas');
var c=canvas.getContext("2d");
// c.beginPath();
// c.moveTo(0,0);
// c.lineTo(500,200);
// c.stroke();
var img = new Image();
img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';
img.crossOrigin = "Anonymous";
// var img= document.getElementById('image')
img.onload=function () {
c.drawImage(img,0,0);
}
var myImageData = c.getImageData(0, 0, 500, 500);
//var myImageData = c.createImageData(600, 600);
var numBytes = myImageData.data.length;
var pixelData=myImageData.data;
console.log (numBytes);
console.log (pixelData);
// var x= function () {
// for(var i=0;i<pixelData.length;i+=40)
// {
// pixelData[i] = 255 - pixelData[i]; // red
// pixelData[i + 1] = 255 - pixelData[i + 1]; // green
// pixelData[i + 2] = 255 - pixelData[i + 2]; // blue
// }
// c.putImageData(myImageData, 0, 0);
// };
// //if (pixelData[i]&&pixelData[i+1]&&pixelData[i+2]===255) {
// //console.log (numBytes);
// //}
// //else {}
// //};
// //
// x();
//pixel = imageData.data[((row * (imageData.width * 4)) + (colume * 4)) + colorindex];
//var img = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); // here is the most important part because if you dont replace you will get a DOM 18 exception.
//window.location.href=image;
<!DOCTYPE html>
<html>
<head>
<title>Image processing</title>
<style type="text/css">
</style>
</head>
<body>
<canvas id="mycanvas" width="300" height="227">
</canvas>
<img src="https://mdn.mozillademos.org/files/5397/rhino.jpg" id="image" style="display:none;">
</style>
</style>="">
<script src="img.js">
</script>
</body>
</html>
This won't work as the image comes from another domain. The canvas accepts only images from the same origin. You cannot and should not be using getImageData() from external sources that don't support CORS.
But you can convert the image into dataURL and then paint the canvas.
Edit: Sorry for not expressing it properly. The only way would be to locally download the image to your server/domain and then draw it on the canvas.
I solved my problme.
firstly as Mohanesh Said we need use Localhost or http domain.So I installed the Python and used this command to creat localhost erver.
Python -m http.server
Second I used the creatpattern command instead drawimage.this code works for me:
var canvas = document.getElementById('mycanvas')
var c = canvas.getContext('2d');
// canvas.height = window.innerHeight;
// canvas.width = window.innerWidth;
var img = document.getElementById("image")
img.crossOrigin = "Anonymous";
var ptrn = c.createPattern(img, 'no-repeat');
c.fillStyle = ptrn;
c.fillRect(0, 0, canvas.width, canvas.height);
var myImageData = c.getImageData(0, 0, canvas.width, canvas.height);
var numBytes = myImageData.data.length;
var pixelData = myImageData.data;
console.log(numBytes);
console.log(pixelData);
<!DOCTYPE html>
<html>
<head>
<title>Image processing</title>
<style type="text/css">
</style>
</head>
<body>
<canvas id="mycanvas" width="100" height="100">
</canvas>
<img src="test1.png" alt="" id="image" style="display: none;" />
<script src="img22.js">
</script>
</body>
</html>

Sanitize HTML as XHTML to draw DOM object to SVG to Canvas

I am following this guide - https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas - to draw element to canvas.
I however am taking the innerHTML of a div on my page. When I do this I have things like <br> and <img> and . And this is causing it to error. If I replace br and img with regex to self closing tags and remove all the it works, however it doesnt look like it is supposed to. The user expected it the spaces to be there.
Is there any sanitizer/parser to properly parse my string as XHTML?
I researched here - DOMParser - https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
However that is not working.
In the below snippet, I have given this. If you load it then click "Make img" you will get prompted with a data url, paste that to a new tab and you can see it works fine. However in the content editable div, hit enter a few times, and then try "Make Image" and it will fail.
function makeimg() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var txt = document.getElementById('rawr').innerHTML;
var w = 200;
var h = 200;
canvas.width = w;
canvas.height = h;
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="' + w + '" height="' + h + '"><foreignObject width="100%" height="100%">' + txt + '</foreignObject></svg>';
var img = new Image();
var svg = new Blob([data], {
type: 'image/svg+xml;charset=utf-8'
});
var url = URL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
prompt('', canvas.toDataURL('image/png', ''));
}
img.src = url;
}
<div id="rawr" contenteditable="true">
rawr rawr !!! hi! yep y
</div>
<button onClick="makeimg()">Make img</button>

Javascript Pixel Manipulation: These aren't my colors

I know questions like this have been asked several time, but I have yet to find just what I'm looking for. I am reading an image into a canvas object (in javascript) and trying to manipulate some specific pixels. For example, I am looking for the color RGB: 224 64 102, and trying to change this to a different color.
I can apply greyscale to the image, so I know the manipulation works, but the code is not finding any pixels with this color (that Adobe Illustrator said was the RGB color). I'm hoping I'm just missing a small detail. The code is below, hopefully someone will see it.
Thanks!
var canvas = document.getElementById("testcanvas");
var canvasContext = canvas.getContext('2d');
imgObj = new Image();
imgObj.src = "ss.jpg";
//imgObj.width = 200;
//imgObj.height = 200;
var imgW = imgObj.width;
var imgH = imgObj.height;
canvas.width = imgW;
canvas.height = imgH;
canvasContext.drawImage(imgObj, 0, 0);
var imgPixels = canvasContext.getImageData(0, 0, imgW, imgH);
//hash_table = {};
for (var x = 0; x < imgPixels.width; x++) {
for (var y = 0; y < imgPixels.height; y++)
{
var i = (y * imgPixels.width + x) * 4;
//Want to go from:
//E04066
//224 64 102 -> to
//134 135 185
if(imgPixels.data[i] == 224 && imgPixels.data[i+1] == 64 && imgPixels.data[i+2] == 102) {
imgPixels.data[i] = 134;
imgPixels.data[i+1] = 135;
imgPixels.data[i+2] = 185;
}
//To greyscale:
/*
var avg = (imgPixels.data[i] + imgPixels.data[i + 1] + imgPixels.data[i + 2]) / 3;
imgPixels.data[i] = avg;
imgPixels.data[i + 1] = avg;
imgPixels.data[i + 2] = avg;
imgPixels.data[i + 3] = 255;
*/
}
}
canvasContext.putImageData(imgPixels, 0, 0, 0, 0, imgPixels.width, imgPixels.height);
//color_count = 0;
//for(key in hash_table) {
// color_count++;
//}
//console.log(color_count);
//console.log(hash_table);
return canvas.toDataURL();
});
});
</script>
</head>
<body>
<canvas id="testcanvas"></canvas>
<img src="ss.jpg" id="testimage"/>
You are probably unable to get image data from canvas because the canvas has been tainted by cross-origin data.
If that file, ss.jpg is local then it won't work. I imagine that's the case.
Search for canvas cross-origin on SO or Google for more information on that. There's a lot out there. Here's a bit of an explanation:
http://simonsarris.com/blog/480-understanding-the-html5-canvas-image-security-rules
Here's a site about enabling it on your server:
http://enable-cors.org/
Otherwise, your code works. Here is the same code converting a tiny red dot into a tiny green dot:
http://jsfiddle.net/RBaxt/
Canvas really don't work with .JPG format. You have to convert your image into .PNG using any picture editing tool like Photoshop. Your code works well.
I think you are loading an Image that is not ready to be painted. Below I have updated your code above, though I have not test it but I feel it could lead you somewhere
var canvas = document.getElementById("testcanvas");
var canvasContext = canvas.getContext('2d');
imgObj = new Image();
imgObj.src = "ss.jpg";
//imgObj.width = 200;
//imgObj.height = 200;
var imgW = imgObj.width;
var imgH = imgObj.height;
imgObj.onload = function(){
//Put the pixel manipulation code here;
// This ensures the image has been loaded before it is accessed.
}

Categories