I can't display images dynamically loaded from web with p5.js - always a cross domain issue.

I'm testing in a host (meaning not local). What I can't understand is that html images do show fine though...

Any help on this? Thanks.

the test looks like this:

As you can see it shows the html image, the live canvas, but not the p5 Image. In console the cross-domain error

Cross-origin image load denied by Cross-Origin Resource Sharing policy.

the code goes like:

var canvas;

// window size
var w , h ;

// canvas width and height
var cw, ch;

//canvas positioning
var cx, cy;

// % of w to be canvas size
var w_prop = 1.;
var h_prop = 1.;

//img url
var imgURL = 'http:'+'//www.clker.com/cliparts/9/0/f/5/1194986802274589086football_ball_brice_boye_01.svg.med.png';

var img;
var htmlImg;


function setup() {

  // window size
  w = windowWidth, h = windowHeight;

 // calc everything
 calcSizes();

 // create canvas
 canvas =  createCanvas(cw, 100);//thats temp hard number

 // positining
 canvas.position(cx, cy);

img =  loadImage(imgURL);

 // this works
 htmlImg = createImg(imgURL);



}



function draw() {
    background(frameCount%255, 130,0);

    //this don't
    image(img,100,100);


}



// if window is resized, recalc stuff.
function windowResized(){
    calcSizes();
    resizeCanvas(cw, ch);
    canvas.position(cx,cy);
}


// that's it
var calcSizes = function(){
    w = windowWidth, h = windowHeight;

    // canvas width and height
    cw = w * w_prop;
    ch = h * h_prop;

    //canvas positioning
     cx = 0;//(w - cw) / 2.0;
     cy = 0;//100.0;
}

Answers

  • edited October 2015

    1. https://GitHub.com/processing/p5.js/blob/master/src/image/loading_displaying.js#L61
    2. https://GitHub.com/processing/p5.js/blob/master/lib/addons/p5.dom.js#L211
  • Thanks @GoToLoop going to check that. I'll be back.

  • edited June 2016 Answer ✓
    • Well, I've been doing further research by myself too. This is what I've dredged by now:
    • When we try to load some remote URL, createImg() always gets it as a tainted image.
    • While loadImage() always tries to get it as non-tainted.
    • In order to do so, it sets the HTMLImageElement's crossOrigin attribute to 'Anonymous':
      https://GitHub.com/processing/p5.js/blob/master/src/image/loading_displaying.js#L96
    • However, if the remote URL image got no CORS permission, that approach utterly fails!
    • Trick lies at resetting crossOrigin to null. It then triggers it to reload it as tainted! :ar!
    • My solution is based on manipulate the "failure" callback's parameter & access its target property.
    • Which is actually the HTMLImageElement that has just failed! \m/
    • Check out the p5.js sketch below and its loadImageErrorOverride() callback replacement: :bz

    /**
     * LoadImageErrorOverride (v1.12)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11608/
     * i-can-t-display-images-dynamically-loaded-from-web-
     * with-p5-js-always-a-cross-domain-issue#Item_3
     */
    
    const URL = 'http:/' + '/www.Clker.com/cliparts/9/0/f/5/'
              + '1194986802274589086football_ball_brice_boye_01.svg.med.png';
    
    var img;
    
    function loadImageErrorOverride(errEvt) {
      const pic = errEvt.target;
    
      if (!pic.crossOrigin)  return print('Failed to reload ' + pic.src + '!');
    
      print('Attempting to reload it as a tainted image now...');
      pic.crossOrigin = null, pic.src = pic.src;
    }
    
    function setup() {
      createCanvas(500, 400);
      noLoop();
    
      loadImage(URL,
                function (pic) { print(img = pic), redraw(); },
                loadImageErrorOverride);
    }
    
    function draw() {
      background(img || 0350);
    }
    

    P.S.: That errEvt.target.src = errEvt.target.src; is weird...
    However, my Chrome-based browser needed that in order to trigger the reload! 8-}
    Firefox works just fine w/o that btW! 8-|

    Another P.S.: This approach to load tainted images can't be used within preload() unfortunately! :(

  • _vk_vk
    edited July 2015

    @GoToLoop I haven't have time to go trough this yet. I will as soon as I can. In the meantime could you provide me with a clarification? I've searched, but could not find the meaning of tainted image in this context. What is it? Thanks!

    Never mind I found it. :\">

  • @GoToLoop Finally i got time to try it out. Amazing!! It Just works! Thanks very much! You can see and test it here.

    How would I catch 404 errors. I can't figure?

    Is this a security flaw? Should I bother?

    Another way to do this would be via php?

    If I try to loadImage() (without you hack) via node.js would I be facing the same issue?

    Thanks.

  • edited July 2015

    Glad it worked for ya! Let's hope the official loadImage() gets better in the future! O:-)
    Although I'm intermediary in JS, I'm still pretty much ignorant about DOM, HTML & CSS! X_X
    W/ that in mind, lemme try to answer those: :(|)

    How would I catch 404 errors. I can't figure?

    I doubt that the Event triggered by HTMLImageElement would have such property!
    I'm afraid that you're gonna need an XMLHttpRequest instead:
    https://developer.Mozilla.org/en-US/docs/Web/API/XMLHttpRequest

    It got lotsa informative properties like status & responseType for example:
    https://developer.Mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status
    https://developer.Mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType

    Within my loadImageErrorOverride() "band-aid", it's assumed its 1st trigger is due to CORS.
    By assigning null to crossOrigin, next time it triggers, it knows it's the 2nd time by checking crossOrigin out for null.
    Therefore for the 2nd trigger, the cause isn't CORS anymore and much probably it's due the URL doesn't exist after all.

    Is this a security flaw? Should I bother?

    Nope & not much. Although tainted images do contaminate any canvas they touch.
    A tainted canvas can't have its content read anymore. Beware of it though! #-o

    Another way to do this would be via PHP?

    Dunno PHP. Sorry!

    If I try to loadImage() (without you hack) via Node.js would I be facing the same issue?

    I know lil' 'bout Node.js and IO.js. But since they're based on the same Chrome/Chromium JS VM runtime, it's expected to have similar issues.
    Although I believe since they're server-side JS solutions, they're expected to ignore CORS issues! [-O<

  • Has anyone tried this workaround in instance mode? I simply can't get it to work in instance mode. Weirdly (at least in chrome) I'm seeing this example code launch the print interface when I convert it to instance mode. WTFBBQ?

  • edited June 2016

    Hi @gregab. Instance mode demands that all p5.js' API should be called via the reference passed as the parameter of the wrapper function. In turn, that function is passed to p5's constructor.

    Take a look at these 2 links below to have some better idea how it works: :-bd

    1. https://github.com/processing/p5.js/wiki/p5.js-overview#instantiation--namespace
    2. https://github.com/processing/p5.js/wiki/Instantiation-Cases

    Anyways, I've just converted my old workaround "fix" to use instantiation mode now too. :bz
    You can also check it online here: http://CodePen.io/anon/pen/zqbZYV?editors=1010

    P.S.: The choice to name the sketch's passed parameter as p is merely a Processing's convention. We can call it anything else of course. :P

    /**
     * LoadImageErrorOverride (v2.0.2)
     * by GoToLoop (2015/Jul/09)
     *
     * forum.Processing.org/two/discussion/11608/
     * i-can-t-display-images-dynamically-loaded-from-web-
     * with-p5-js-always-a-cross-domain-issue#Item_8
     *
     * Instance Mode (2016-May-10)
     * CodePen.io/anon/pen/zqbZYV?editors=1010
     */
    
    new p5(p => {
      "strict mode";
    
      const URL = 'http:/' + '/www.Clker.com/cliparts/9/0/f/5/'
                + '1194986802274589086football_ball_brice_boye_01.svg.med.png';
    
      let img;
    
      function loadImageErrorOverride(errEvt) {
        const pic = errEvt.target;
    
        if (!pic.crossOrigin)  return p.print(`Failed to reload ${pic.src}!`);
    
        p.print(`Attempting to reload ${pic.src} as a tainted image now...`);
        pic.crossOrigin = null, pic.src = pic.src;
      }
    
      p.setup = () => {
        p.createCanvas(500, 400);
        p.noLoop();
    
        p.loadImage(URL, 
                    pic => { p.print(img = pic), p.redraw(); },
                    loadImageErrorOverride);
      };
    
      p.draw = () => {
        p.background(img || 0o350);
      };
    });
    
  • edited June 2016

    Recalled there were some fixes in preload() that allows for the workaround to work there as well now.
    Here's version 2.1+ altered to loadImage() inside preload() under more recent p5.js versions: \m/

    http://CodePen.io/anon/pen/zqbZYV?editors=0010

    <script src=http://p5js.org/js/p5.min.js></script>
    
    <script>
    
    /**
     * LoadImageErrorOverride (v2.2.3)
     * by GoToLoop (2015/Jul/09)
     *
     * https://forum.Processing.org/two/discussion/11608/
     * i-can-t-display-images-dynamically-loaded-from-web-
     * with-p5-js-always-a-cross-domain-issue#Item_9
     *
     * Instance Mode (2016-May-10)
     * http://CodePen.io/anon/pen/zqbZYV?editors=1010
     */
    
    new p5(p => {
      "strict mode";
    
      const URL = 'http:/' + '/www.Clker.com/cliparts/9/0/f/5/'
                + '1194986802274589086football_ball_brice_boye_01.svg.med.png';
    
      let img;
    
      p.preload = () => img = p.loadImage(URL, pic => p.print(pic), loadImgErrFix);
      p.setup = () => ( p.createCanvas(500, 400), p.noLoop() );
      p.draw = () => p.background(img);
    
      function loadImgErrFix(errEvt) {
        const pic = errEvt.target;
    
        if (!pic.crossOrigin)  return p.print(`Failed to reload ${pic.src}!`);
    
        p.print(`Attempting to reload ${pic.src} as a tainted image now...`);
        pic.crossOrigin = null, pic.src = pic.src;
      }
    });
    
    </script>
    
  • @GoToLoop What are tainted images?

    In your last post, you draw your image in line 27. Your function loadImgErrFix assigns pic.src = pic.src; How is this related to the img object so for the whole thing to work at the end?

    Last question. CORS issue is resolved by setting up a local server? Then one should not care about implementing this fix, correct?

    Kf

  • edited April 2017

    @kfrajer, we should avoid unnecessarily dredging 1+ year forum threads. But whatever! :P

    A tainted image is an HTMLImageElement object which was acquired from a server which hasn't explicitly setup CORS permission for that image file. =;

    Though we can freely display tainted images in our webpages, we can't read nor modify their pixels! :-O

    And once a tainted HTMLImageElement is copied to an HTMLCanvasElement, the later becomes tainted as well! :-&

    Obviously, all relative paths within the confines of the root folder don't need any CORS. :-bd

  • edited April 2017

    CORS issue is resolved by setting up a local server?

    Not exactly. Only relative path accesses within the confines of some ".html" page are free from CORS demand. :-B

    Anything else gotta have explicit CORS permission or else the grabbed image becomes tainted! 3:-O

  • edited April 2017

    Your function loadImgErrFix() assigns pic.src = pic.src;.
    How is this related to the img object...

    As I had mentioned in my solution post, pic.src = pic.src; was only necessary for Chrome-based browsers for re-triggering the download. L-)

    In my experiment back then, pic.crossOrigin = null was already enough to trigger the re-loading attempt for Firefox-based browsers. \m/

    But you might ask: How can assignment operations trigger anything? :-/
    Guess what, both crossOrigin & src properties are actually getter & setter functions: @-)

    1. https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get

    2. https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set

    That is, every reading & writing access to those 2 properties are replaced by an action of invoking an appropriate function instead! ;)

  • edited April 2017

    The problem was that after turning off CORS request by assigning null to getter/setter property crossOrigin, I also had to assign something to getter/setter property src in order to trigger its reloading action in Chromium.

    Well, how about assigning its own value back to itself just for re-triggering's sake. :ar!

  • @GoToLoop

    Thank you for your detail explanation. I believe the tainted explanation was missing in this post.

    Anything else gotta have explicit CORS permission or else the grabbed image becomes tainted!

    The CORS permissions is dependent on each browser? Or is defined by the server? You might have already answered this question here:

    A tainted image is an HTMLImageElement object which was acquired from a server which hasn't explicitly setup CORS permission for that image file

    But then, I also remember prev posts were you said that FireFox do not have CORS restriction (?) and Chrome is another option only if you tag your Chrome's run command with some additional parameters.

    Last thing,

    The problem was that after turning off CORS request by assigning null to getter/setter property crossOrigin, I also had to assign something to getter/setter property src in order to trigger its reloading action in Chromium.

    So how come you are able to disable cors this way? The re-Triggering downloading this way is guaranteed to always succeed? Now, would you qualify this as a hack? Or is there a proper way to retrieve these elements?

    Kf

  • edited April 2017

    But then, I also remember prev. posts where you said that FireFox do not have CORS restriction (?)

    • CORS isn't restricted to images only. Any file resource requires CORS.
    • Firefox since its inception has always allowed files grabbed fromfile:// scheme.
    • And for Chromium, there's a hidden command line argument for the same thing.
    • Otherwise, Chromium triggers CORS violation when using file:// scheme. X(
  • edited April 2017

    So how come you are able to disable CORS this way?

    I've already said that we need to assign null to getter/setter property crossOrigin from an HTMLImageElement object. :-@

    1. https://developer.Mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes
    2. http://StackOverflow.com/questions/18336789/purpose-of-the-crossorigin-attribute/40694741
    3. http://www.HTMLquick.com/reference/tags/img.html#specific-attributes

    This way we can grab the image from its server, even though it arrives tainted! :-&

Sign In or Register to comment.