So I've been working on a fun, online image effect program in javascript where the user enters a url to an image and hits 'enter', the image is drawn on the screen, and then the user can run some effects on it, such as g for greyscale, b for blur, etc.
My problem is that the console prints out either:
Redirect at origin [origin] has been blocked from loading by
Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin'
header is present on the requested resource. Origin 'null' is
therefore not allowed access.
or:
Uncaught SecurityError: Failed to execute 'getImageData' on
'CanvasRenderingContext2D': The canvas has been tainted by
cross-origin data.
I looked into many answers to this issue, and I added an extension to my chrome browser that enables cross-origin resource sharing, and my webpage runs fine (after a couple of reloads).
All the solutions I found require either enabling the cross-origin resource sharing option in chrome, or using some sort of php and ajax calls to enable this option. Since I'm writing the code on jsbin, I'm looking specifically for a solution that can be done in pure javascript, and I haven't been able to find any that work. If you have any ideas on something that could work, or the news that there is no possible solution, any response would be appreciated.
My code:
var background, context, image;
var docwidth, docheight;
image = new Image();
image.src = $('#image-src').val();
image.crossOrigin = "anonymous";
docwidth = $(document).outerWidth(true);
docheight = $(document).outerHeight(true);
background = document.getElementById("background");
context = background.getContext("2d");
image.onload = function() {
background.width = docwidth;
background.height = docheight;
context.drawImage(image,0,0,image.width,image.height, 0, 0, docwidth, docheight);
};
function change_image_src(src) {
image.src = $('#image-src').val();
}
// ... more image effect functions ...
function grayscale() {
var data = context.getImageData(0, 0, background.width, background.height);
var pixels = data.data;
for (var x = 0; x < data.width; x++)
for (var y = 0; y < data.height; y++) {
var i = (y * 4) * data.width + x * 4;
var avg = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
pixels[i] = avg;
pixels[i + 1] = avg;
pixels[i + 2] = avg;
}
context.putImageData(data, 0, 0, 0, 0, data.width, data.height);
}
$(document).keydown(function(e) {
switch (e.which) {
// ... other cases ...
case 71: // g
grayscale();
break;
}
});
Btw, I do have image.crossOrigin = "anonymous";
Thanks in advance!
In order to use images from another origin on a canvas without tainting it, the image must be served with CORS headers. This page on MDN explains it, but essentially, when the image is served, it has to be accompanied by an Access-Control-Allow-Origin header allowing the origin of your page (potentially via the * wildcard).
Unless the image is served that way, putting it in the canvas will taint it, and you won't be able to use getImageData.
So this isn't a JavaScript thing, really; it's how the image is served that determines how you can use it.
I was able to get over this problem using a CORS proxy server: http://crossorigin.me/
Related
I've written a JS script that consumes data from an http API (endpoint for the GET request: http://api.open-notify.org/iss-now.json). I intend to use it in a page hosted on github pages (which makes use of the https protocol).
Now that it is online I see in the console that this data can't be used in-browser because of the mixed active content error: Blocked loading mixed active content “http://api.open-notify.org/iss-now.json”.
I can't apply this solution because I'm not making use of a server.js file (my content is served by github). I wanted to try this other solution but it requires opening the adapter page in another tab, which just isn't viable.
I'm trying this workaround but https://cors-anywhere.herokuapp.com/http://api.open-notify.org/iss-now.json returns an error (Missing required request header. Must specify one of: origin,x-requested-with). If anyone know how to add headers to the loadJSON method please tell me, I can't find anything in its documentation. I'm not exactly at ease with the syntax of fetch, so when I try it:
var response = fetch("https://cors-anywhere.herokuapp.com/http://api.open-notify.org/iss-now.json", {
headers: {
Origin: window.location.protocol + '//' + window.location.host
}
});
if (response.ok) { // if HTTP-status is 200-299
// get the response body (the method explained below)
var json = response.json();
return(json);
} else {
alert("HTTP-Error: " + response.status);
}
I get to add the "origin" header, only to find myself with a
Cross-Origin Request Blocked: The Same Origin Policy disallows reading
the remote resource at
https://cors-anywhere.herokuapp.com/http://api.open-notify.org/iss-now.json.
(Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
Which as far as I understand can only be corrected server-side.
The github page of cors-anywhere encourages to implement their solution in your own script, by adding this snippet:
(function() {
var cors_api_host = 'cors-anywhere.herokuapp.com';
var cors_api_url = 'https://' + cors_api_host + '/';
var slice = [].slice;
var origin = window.location.protocol + '//' + window.location.host;
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
var args = slice.call(arguments);
var targetOrigin = /^https?:\/\/([^\/]+)/i.exec(args[1]);
if (targetOrigin && targetOrigin[0].toLowerCase() !== origin &&
targetOrigin[1] !== cors_api_host) {
args[1] = cors_api_url + args[1];
}
return open.apply(this, args);
};
})();
but I wouldn't know how to implement it, I haven't succeeded integrating it in my code for now.
My code is a bit of a mess right now but I can show you this much:
// global functions for the Tracker sample
function getData() {
// var promise = fetch("http://api.open-notify.org/iss-now.json");
loadJSON("http://api.open-notify.org/iss-now.json", gotData, 'jsonp');
}
function gotData(data) {
background(img)
displaySample()
// this will allow you to see the raw data live in your browser console
//console.log(data.iss_position.latitude);
//console.log(data.iss_position.longitude);
posX = (parseFloat(data.iss_position.latitude * latConst) + translateX)
posY = (parseFloat(data.iss_position.longitude * lonConst)* -1 + translateY)
console.log(posX);
console.log(posY);
fill(250, 50, 50, 90);
ellipse(posX, posY, 10, 10);
}
function draw() {
// case tracker
if (selectedSample === 1) {
translateX = boxSizeWidth / 2;
translateY = boxSizeHeight / 2;
latConst = boxSizeWidth / 360;
lonConst = boxSizeHeight / 180;
if (t === 0) {
getData()
}
}
I also tried finding an https API giving the same data (latitude and longitude of the ISS in real time) but I can't seem to find any for now, and finding a workaround would be interesting anyway.
You could use fetch like this:
fetch("https://cors-anywhere.herokuapp.com/http://api.open-notify.org/iss-now.json", {
headers: { Origin: window.location.host }
})
.then(res => res.json())
.then(res => {
console.log(res);
// gotData(res);
})
.catch(err => {
console.log(err);
});
I use easeJS in the implementation of the game robolucha, currently we display different colors of the characters by using shapes under transparent images.
We want to use Bitmaps and apply color filters to it.
Sadly the ColorFilter is not working.
The Fiddle is here for the code : https://jsfiddle.net/athanazio/7z6mqnrk/
And here is the code I´m using
var stage = new createjs.Stage("filter");
var head = new createjs.Container();
head.x = 300;
head.y = 300;
head.regX = 100;
head.regY = 100;
var path = "https://raw.githubusercontent.com/hamilton-lima/javascript-samples/master/easejs/colorfilter/";
var layer1 = new createjs.Bitmap(path + "layer1-green.png");
layer1.image.onload = function(){
layer1.filters = [ new createjs.ColorFilter(0, 0, 0, 1, 0, 0, 255, 1) ];
layer1.cache(0,0,200,200);
}
var layer2 = new createjs.Bitmap(path + "layer2.png");
head.addChild(layer1);
head.addChild(layer2);
stage.addChild(head);
createjs.Ticker.addEventListener("tick", headTick);
function headTick() {
head.rotation += 10;
}
createjs.Ticker.addEventListener("tick", handleTick);
function handleTick() {
stage.update();
}
The ColorFilter does not work in this example because the image is being loaded cross-domain. The browser will not be able to read the pixels to apply the filter. I am not exactly sure why there is no error in the console.
EaselJS has no mechanism to automatically handle cross-origin images when it creates images behind the scenes (which it does when you pass a string path). You will have to create the image yourself, set the "crossOrigin" attribute, and then set the path (in that order). Then you can pass the image into the Bitmap constructor.
var img = document.createElement("img");
img.crossOrigin = "Anonymous";
img.onload = function() {
// apply the filter and cache it
}
img.src = path + "layer1.png";
layer1 = new createjs.Bitmap(img);
You don't have to wait for the image to load to create the Bitmap and apply the filter, but you will have to wait to cache the image.
This fix also requires a server that sends a cross-origin header, which git does. Here is an updated fiddle with that change. Note that if your image is loaded on the same server, this is not necessary.
https://jsfiddle.net/7z6mqnrk/10/
Cheers.
I have functionality on a web page similar to posting a url on Facebook. The user inputs an url and an image and description is loaded. The user can then choose to post that image and description (all that works fine). When the user attempts to post the image, the behavior should be: Javascript will pick up the image src, load it to a canvas, resize it, add it to a form dynamically (along with the description), and post it. The result should be the image and description is sent to my server for processing and saving. All this works just fine if I have a local image source. It fails if I have a remote image source. My reasoning is I have a CORS issue, but I'm not sure how to solve it. Here is the function with the error in the comments.
function postNewsAuto() {
var MAX_WIDTH = 400;
var MAX_HEIGHT = 400;
var img = document.createElement("img");
/*
* the src on the next line contains an image from another site
*/
img.src = $('#auto-news-image').attr('src');
img.onload = function() {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var width = img.width;
var height = img.height;
if (width > height) {
if (width > MAX_WIDTH) {
height *= MAX_WIDTH / width;
width = MAX_WIDTH;
}
} else {
if (height > MAX_HEIGHT) {
width *= MAX_HEIGHT / height;
height = MAX_HEIGHT;
}
}
canvas.width = width;
canvas.height = height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
/*
* this next line fails with the error:
* Security Error: The operation is insecure
* presumably a CORS issue
*/
var dataurl = canvas.toDataURL("image/jpeg");
var blob = dataURItoBlob(dataurl);
var fd = new FormData(document.forms[0]);
fd.append("url", $('#auto-news-url').text());
fd.append("description", $('#auto-news-url').attrib('href'));
fd.append("image", blob);
/*
* at this point in the code I post
* the form via XMLHttpRequest()
* this code also works, I removed it for brevity
*/
};
}
I realize a simple way around this would be to store the image location rather than the image itself and then serve it up as needed. However I want to store it to avoid broken links since the remote image would be out of my control. The real issue is then, how do I get an image from a remote size and save it to my server? So putting aside all the resizing and posting (which I know how to do), at the most basic level, the question is simply, how do I make a copy of an image on my server from a remote site?
One other function is referenced here, dataURItoBlob(). This function also works fine, but I've posted it for reference.
function dataURItoBlob(dataURI) {
'use strict';
var byteString, mimestring;
if (dataURI.split(',')[0].indexOf('base64') !== -1) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = decodeURI(dataURI.split(',')[1]);
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0];
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i);
}
return new Blob([new Uint8Array(content)], {type: mimestring});
}
You have two options suitable for your case.
Bad solution
Setup CORS headers on remote website. However this solution is wrong for you, because you cannot control every website out there and ask them to setup CORS headers for you.
Good solution
You need to write server proxying middleware (Python, Node, etc.) which will send request any remote website and return content. This solution is better because your own server will not hit CORS issue.
Below I've created a simple test case that shows that when an img tag's src is set to different dataUrls, it leaks memory. It looks like the image data is never unloaded after the src is changed to something else.
<!DOCTYPE html>
<html>
<head>
<title>Leak Test</title>
<script type="text/javascript">
canvas = null;
context = null;
image = null;
onLoad = function(event)
{
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
image = document.getElementById('image');
setTimeout(processImage, 1000);
}
processImage = function(event)
{
var imageData = null;
for (var i = 0; i < 500; i ++)
{
context.fillStyle = "rgba(" + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.floor(Math.random() * 256) + "," + Math.random() +")";
context.fillRect(0, 0, canvas.width, canvas.height);
imageData = canvas.toDataURL("image/jpeg", .5);
image.src = imageData;
}
setTimeout(processImage, 1000);
}
</script>
</head>
<body onload="onLoad(event)">
<canvas id="canvas"></canvas>
<img id="image"></img>
</body>
</html>
If you load this html page, RAM usage builds over time and is never cleaned up. This issue looks very similar: Rapidly updating image with Data URI causes caching, memory leak . Is there anything I can do to prevent this memory leak?
I ended up doing a work around for the issue. The memory bloat only happens when the image.src is changed, so I just bypassed the Image object altogether. I did this by taking the dataUrl, converting it into binary (https://gist.github.com/borismus/1032746) then parsing it using jpg.js (https://github.com/notmasteryet/jpgjs). Using jpg.js I can then copy the image back to my canvas, so the Image element is completely bybassed thus negating the need to set its src attribute.
Panchosoft's answer solved this for me in Safari.
This workaround avoids the memory increase by bypassing the leaking Image object.
// Methods to address the memory leaks problems in Safari
var BASE64_MARKER = ';base64,';
var temporaryImage;
var objectURL = window.URL || window.webkitURL;
function convertDataURIToBlob(dataURI) {
// Validate input data
if(!dataURI) return;
// Convert image (in base64) to binary data
var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
var base64 = dataURI.substring(base64Index);
var raw = window.atob(base64);
var rawLength = raw.length;
var array = new Uint8Array(new ArrayBuffer(rawLength));
for(i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
// Create and return a new blob object using binary data
return new Blob([array], {type: "image/jpeg"});
}
then, in the processImage rendering loop:
// Destroy old image
if(temporaryImage) objectURL.revokeObjectURL(temporaryImage);
// Create a new image from binary data
var imageDataBlob = convertDataURIToBlob(imageData);
// Create a new object URL
temporaryImage = objectURL.createObjectURL(imageDataBlob);
// Set the new image
image.src = temporaryImage;
I'm also experiencing this issue and I do believe it's a browser bug. I see this happening in FF and Chrome as well. At least Chrome once had a similar bug that was fixed. I think it's not gone or not completely gone. I see a constant increase in memory when I set img.src repeatedly to unique images. I have filed a bug with Chromium, if you want to put some weight in :)
https://code.google.com/p/chromium/issues/detail?id=309543&thanks=309543&ts=1382344039
(The bug triggering example does not necessarily generate a new unique image every time around, but at at least it does with a high probability)
Some solutions not mentioned in the other answers:
For browser
jpeg-js
Similar to jpgjs mentioned by #PaulMilham, but with additional features and a nicer API (imo).
For NodeJS/Electron
sharp
General purpose image-processing library for NodeJS, with functionality to both read and write jpeg, png, etc. images (as files, or just in memory).
Since my program is in Electron, I ended up using sharp, as jpeg-js mentioned it as a more performant alternative (due to its core being written in native code).
Setting the source to a fixed minimal dataURI after handling the image seems to fix the issue for me:
const dummyPng = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAC0lEQVQYV2NgAAIAAAUAAarVyFEAAAAASUVORK5CYII=';
img.onload = () => {
// ... process the image
URL.revokeObjectURL(img.src);
img.onload = null;
img.src = dummyPng;
};
img.src = URL.createObjectURL(new window.Blob([new Uint8Array(data)], {type: 'image/png'}));
Is there any technique to convert images that have already been downloaded – inline JPEG/GIF/etc. images that occur in a webpage – into Base64 data using client-side JavaScript?
I am not talking about how to transform an image into Base64 using other means (server-side, online tools, etc.).
These are the constraints for my particular use case:
The image is on screen now, right in the page, in front of person. It has already been downloaded in a data sense.
The conversion from raw image data has to be done client-side.
The images in question are from arbitrary domains. That is, they may or may not, be of same origin domain.
The user, if needed (if helpful to solution), can give additional permissions (for example, a FF toolbar install to help skirt cross-domain and other issues). That is, code can be given special endorsement on the client side if that helps solve the issue.
The end goal is to transform all images on the page (in the DOM) into Base64 data inside of JavaScript. Put another way, every image the user can see on the page has been converted into a JavaScript variable of some sort that contains the Base64 data.
So far I see no posts that stay inside of all the above constraints.
I think this is close to what you are looking for but the only problem is that it only works for locally hosted images and HTML5 only.
function toURL(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
var test = document.getElementById("myImage");
console.log(toURL(test));
You can trick javascript into thinking an image is from your domain with the following code.
image.php
<?php
$image = getAnImagePathAndTypeFromTheDatabaseByID($_GET["id"]);
//returns something like
//array("path" => "http://www.anotherwebsite.com/image.png", "type" => "png")
header("Content-type: image/$image[type]");
echo file_get_contents($image["path"]);
?>
Then just navigate to image.php?id=1 for example.
For it to work in cross-domain client-side you need to call the image with the attribute crossorigin = "true", or, add a line in the Logan Murphy code:
function toURL(image) {
image.setAttribute('crossOrigin', 'anonymous');
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
var s = canvas.toDataURL();
return s.substring(s.indexOf(","));
}
I use this code:
// image-to-uri.js v1
// converts a URL of an image into a dataURI
function imageToURI(url, callback) {
// Create an empty canvas and image elements
let canvas = document.createElement('canvas');
let img = document.createElement('img');
img.onload = function () {
let ctx = canvas.getContext('2d');
// match size of image
canvas.width = img.naturalWidth || img.width;
canvas.height = img.naturalHeight || img.height;
// Copy the image contents to the canvas
ctx.drawImage(img, 0, 0);
// Get the data-URI formatted image
callback(null, canvas.toDataURL('image/png'));
};
img.ononerror = function () {
callback(new Error('FailedToLoadImage'));
};
// canvas is not supported
if (!canvas.getContext) {
setTimeout(callback, 0, new Error('CanvasIsNotSupported'));
} else {
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
};
};
which is based on this https://github.com/HenrikJoreteg/image-to-data-uri.js/blob/master/image-to-data-uri.js