Furman
Furman

Reputation: 2395

Manipulating pixels in canvas

I'm trying to write simple 2D top-down game with grid based map and I have problem with manipulating pixels in certain canvas area. I have following function:

changeCellBackground(x, y, r, g, b){

    let cell = this.context.getImageData(x * this.TILE_SIZE, y * this.TILE_SIZE, this.TILE_SIZE, this.TILE_SIZE);

    for(let i=0; i<cell.data.length; i+=4){

        cell.data[i] += r;
        cell.data[i+1] += g;
        cell.data[i+2] += b;
    }

    this.context.putImageData(cell, x * this.TILE_SIZE, y * this.TILE_SIZE);
}

where context is canvas 2d context:

this.screen = $('#game');
this.context = this.screen[0].getContext('2d');

and Img tag which has src link to tileset:

let tileSet = $('<img></img>').attr('src', '../assets/tiles.png');

However when I try to use above function, I'm getting SecurityError: The operation is insecure.. As I understand it happens because of CORS limitations, so I try to add crossOrigin="anonymous" attribute to Img:

let tileSet = $('<img></img>').attr('crossOrigin', 'anonymous').attr('src', '../assets/tiles.png');

But now I'm getting NS_ERROR_NOT_AVAILABLE:. I guess it happens because image hasn't loaded yet when rest of script started to execute. How do I fix this? I tried this:

let tileSet = $('<img></img>');

    tileSet.onload = function(){

        tileSet.attr('crossOrigin', 'anonymous').attr('src', '../assets/tiles.png');

        gameScreen.drawAnimatedImage(0, 0, 'waterfall');
        gameScreen.drawAnimatedImage(2, 2, 'fountain');
        gameScreen.drawAnimatedImage(11, 5, 'street_lamp');
        gameScreen.drawAnimatedImage(10, 5, 'street_lamp');
        gameScreen.changeCellBackground(10, 15, -30, -30, -30);
    };

But it isn't working either - when I set console.log(tileSet) at the end of onload function nothing is logged on console. It seems that onload function didn't triggered. Why is that happening and how can I fix this?

Upvotes: 3

Views: 128

Answers (2)

AVAVT
AVAVT

Reputation: 7133

There's no link between changeCellBackground() and the tileset in your question, so I'm not sure if there's more problem ahead, but to wait for the image to load, you actually need to put the src part out of the function:

let tileSet = $('<img></img>');

tileSet.onload = function(){
    gameScreen.drawAnimatedImage(0, 0, 'waterfall');
    gameScreen.drawAnimatedImage(2, 2, 'fountain');
    gameScreen.drawAnimatedImage(11, 5, 'street_lamp');
    gameScreen.drawAnimatedImage(10, 5, 'street_lamp');
    gameScreen.changeCellBackground(10, 15, -30, -30, -30);
};

tileSet.attr('crossOrigin', 'anonymous').attr('src', '../assets/tiles.png');

As it stands currently the onload event is not triggered because there was no src to load from.

Upvotes: 0

Blindman67
Blindman67

Reputation: 54026

The is a much easier way to add a value to the pixels on the canvas.

Your function

changeCellBackground(x, y, r, g, b){

Adds r,g,b to each pixel on the tile at x,y

can be done with

function changeCellBackground(x, y, r, g, b){
     this.context.fillStyle = "rgb(" + Math.floor(r) + ","  + Math.floor(g) + "," + Math.floor(b) + ")";
     this.context.globalCompositeOperation = "lighter"; // adds the fill color to existing pixels
     this.context.fillRect(x * this.TILE_SIZE, y * this.TILE_SIZE, this.TILE_SIZE, this.TILE_SIZE);
     this.context.globalCompositeOperation = "source-over"; // restore default composite operation
 }

The above function is identical to your function but without the need to access the pixel data. This means you don't have to worry about the insecure image and can load it without the cross origin header.

As a guess, if you are working off your hard drive (ie ../assets/tiles.png is on your hard drive) you are not going to get a CORS image as that requires a server.

You can set up a server on your computer (there are many options) and then use the domain localhost in which case the image is not cross domain and does not need the headers. Or you can look up the browser security switches and turn off cross origin security which will give you access to the image data as well.

Upvotes: 1

Related Questions