paperjs / paper.js

The Swiss Army Knife of Vector Graphics Scripting – Scriptographer ported to JavaScript and the browser, using HTML5 Canvas. Created by @lehni & @puckey
http://paperjs.org
Other
14.5k stars 1.23k forks source link

Some blendModes do not work, or reposition the object the center of the canvas, with an apparent position of [0, 0] #1479

Closed SkepticaLee closed 5 years ago

SkepticaLee commented 6 years ago

Using the example in the documentation for Rasters:

var url = 'http://assets.paperjs.org/images/marilyn.jpg'; raster = new Raster(url);

// If you create a Raster using a url, you can use the onLoad // handler to do something once it is loaded: raster.onLoad = function() { console.log('The image has loaded.'); raster.blendMode = "add"; };

This causes the image to disappear and a message in red appears on the console:

SecurityError: The operation is insecure.

Blend modes that appear to do this are e.g.: add, subtract, negation, average, pin-light

Blend modes that work as expected are e.g:

normal, screen, multiply, overlay, soft-light, hard-light, color-burn, color-dodge,

In my applications the image is translated into the center of the canvas, and its position is read out to be [0, 0].

Is this a bug?

SkepticaLee commented 6 years ago

The security error refers to line 13715 of paperjs version 0.11.5, which is the line:

var dstData = dstContext.getImageData(offset.x, offset.y, srcCanvas.width, srcCanvas.height)

This would normally be triggered by a cross origin, which is not the case here.

sasensi commented 6 years ago

Hi, as you have guessed, it is a CORS issue.

Problematic modes are those which are not natively supported by your browser and thus need access to the image data. And this image data access is by default prevented by CORS. Here is another issue related to this problem. To enable CORS access from Paper.js, you need to set the crossOrigin property on your Raster instance.

new Raster({
    crossOrigin: 'anonymous',
    source: 'http://assets.paperjs.org/images/marilyn.jpg'
});

Here is a Sketch showing all blend modes in action.

SkepticaLee commented 6 years ago

I thought it was a CORS issue as well, but then the images on http://paperjs.org/reference/raster/#raster shouldn't show at all to begin with. It is only when the blendMode is manipulated tha tth einmage disappears. Try this in the box on that page:

var raster = new Raster({
    crossOrigin: 'anonymous',
    source: 'http://assets.paperjs.org/images/marilyn.jpg',
    position: view.center
});

raster.scale(0.5);
raster.rotate(10);
raster.blendMode = 'negation';

BTW in your sketch you should have 5 rows, not 4, to show the "negation" effect.

sasensi commented 6 years ago

Sorry about the missing blend mode in the sketch :)

Your seing this issue on http://paperjs.org/reference/raster/#raster because when you load the page, the image is drawn in the canvas without CORS attribute and that result in a tainted canvas.

As soon as you draw into a canvas any data that was loaded from another origin without CORS approval, the canvas becomes tainted. A tainted canvas is one which is no longer considered secure, and any attempts to retrieve image data back from the canvas will cause an exception to be thrown.

So when you change the code snippet and click the run button, despite you use CORS attribute, the canvas is still tainted and this produces an error. This can be reproduced in Sketch by loading two images consecutively. First without, then with CORS attribute.

I'm not sure if we can / must do something about that. Is it user responsability to never load an image in the canvas without taking care of CORS if he wants to manipulate image data later ? @lehni & @sapics, what do you think ?

SkepticaLee commented 6 years ago

Well, you should do something about it, otherwise it looks like the page is broken and/or paperjs doesn't work...

SkepticaLee commented 6 years ago

Besides, if you leave out the crossOrigin property in your sketch, the modes "normal", "hard-light", "difference" and "color" still work, even though the latter three are not native blend modes. Something else is going on here.

SkepticaLee commented 6 years ago

Try this in the sketch to see what happens with the position, despite CORS being set:

function displayImage(point, blendMode) {
    new Raster({
        source: 'http://assets.paperjs.org/images/marilyn.jpg',
        crossOrigin: 'anonymous',
        position: point,
        scaling: 0.25,
        onLoad: function() {this.blendMode = blendMode;}
    });
    new PointText({
        point: point + [0,80],
        content: blendMode,
        justification: 'center',
        fillColor: 'black'
    });
}

new Path.Rectangle({
    rectangle: view.bounds,
    fillColor: 'orange'
});

var modes = [
    'normal', 'multiply', 'screen', 'overlay', 'soft-light',
    'hard-light', 'color-dodge', 'color-burn', 'darken', 'lighten',
    'difference', 'exclusion', 'hue', 'saturation', 'luminosity',
    'color', 'add', 'subtract', 'average', 'pin-light', 'negation'
];
for (var i = 0; i < 25; i++) displayImage(new Point(100 + (i % 6) * 150, 100 + Math.floor (i / 6) * (i % 6) * 135), modes[i]);
sasensi commented 6 years ago

About your last example, I think it is a problem with your position calculation. Is that what you were trying to do ?

sasensi commented 6 years ago

You are right, I haven't realized that the error stopped the script execution. So the least we can do is catch this error and provide an help message. Here is an updated Sketch precising which mode is natively supported.

lehni commented 6 years ago

@sasensi do these errors show up in the console? If so, what do they say? And do we really need to catch them and print a message?

sasensi commented 6 years ago

@lehni, not only error show up in the console, but it crashes script execution. Here is a Sketch demonstrating it.

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
lehni commented 6 years ago

@sasensi I think in that case we shouldn't add special code to emit warnings... We can just direct questions to this issue instead? Are you OK with closing this MR without merging?

sasensi commented 6 years ago

@lehni if you think that's the right way to go, it's fine for me. I closed the PR.

sasensi commented 5 years ago

I close this issue because the cause of the problem can't be fixed in the library. As a final note and as mentioned earlier in this discussion, any image loaded from an external source should be loaded with the crossOrigin attribute set, in order to avoid tainted canvas errors.

var raster = new Raster({
    crossOrigin: 'anonymous',
    source: 'http://assets.paperjs.org/images/marilyn.jpg'
});