Change image color

Hello!

In phaser 3, how can I change a color image completely? I mean the following:

Example

Is this possible? Thanks!

For WebGL: image.setTint(0)
For Canvas you’ll have to look at Compositing or Pixel manipulation.

2 Likes

I’m using CAVAS in my projetc.

The second option seems to better fit what I’m looking for. I’ve investigating for a while, but I really don’t understand how to start using “Compositing” or “Pixel manipulation” on a image.

If you could give me a more precise example of how it works, I would appreciate it.

This is how Phaser2 used Pixi to do it:

http://pixijs.download/fix/color-utils-docs/docs/packages_canvas_canvas-renderer_src_CanvasTinter.js.html#line13

With a Phaser3 CanvasTexture you can use all the standard Canvas operations, and if needed, access every pixel individually.

1 Like

Thanks, Milton.

You were correct, and with this example I did exactly what I needed:

Phaser - Examples - Edit Texture Silhouette.

That is the Pixel manipulation way.
It is (very) slow, because of getImageData/putImageData. It does give you full control.
The much faster way (if you had to do it every frame), is using compositing.
Draw a filled rectangle in the color you want to tint with.
Then set context.globalCompositeOperation = 'multiply';
Now draw your image.
You now have a tinted image (rectangular).
To get rid of the rectangle, now set context.globalCompositeOperation = 'destination-atop';
And draw your image again. Only the overlap remains.

1 Like

That’s very interesting. I’m trying to test that way but I’m constantly getting a ‘getContext’ error in my code, I don’t know why. And to be sure, I copied the code for the example that appeared in the link you left, but I keep getting that error.

Error

This is the code:

> function draw() {
>   var ctx = document.getElementById('canvas').getContext('2d');
>   ctx.fillRect(0, 0, 150, 150);
>   ctx.translate(75, 75);
> 
>   // Create a circular clipping path
>   ctx.beginPath();
>   ctx.arc(0, 0, 60, 0, Math.PI * 2, true);
>   ctx.clip();
> 
>   // draw background
>   var lingrad = ctx.createLinearGradient(0, -75, 0, 75);
>   lingrad.addColorStop(0, '#232256');
>   lingrad.addColorStop(1, '#143778');
> 
>   ctx.fillStyle = lingrad;
>   ctx.fillRect(-75, -75, 150, 150);
> 
>   // draw stars
>   for (var j = 1; j < 50; j++) {
>     ctx.save();
>     ctx.fillStyle = '#fff';
>     ctx.translate(75 - Math.floor(Math.random() * 150),
>                   75 - Math.floor(Math.random() * 150));
>     drawStar(ctx, Math.floor(Math.random() * 4) + 2);
>     ctx.restore();
>   }
> 
> }
> 
> function drawStar(ctx, r) {
>   ctx.save();
>   ctx.beginPath();
>   ctx.moveTo(r, 0);
>   for (var i = 0; i < 9; i++) {
>     ctx.rotate(Math.PI / 5);
>     if (i % 2 === 0) {
>       ctx.lineTo((r / 0.525731) * 0.200811, 0);
>     } else {
>       ctx.lineTo(r, 0);
>     }
>   }
>   ctx.closePath();
>   ctx.fill();
>   ctx.restore();
> }
> 
> draw();

Pixi obviously created a ‘canvas’ element in the index.html.
I’m guessing you didn’t :slight_smile:

Add something like this to index.html:
<canvas id="canvas"></canvas>

1 Like

Yes, that was it. But now I’m a bit confused, in my game, I use a parent container:
<div id = 'container'> </div>
So now I have two canvas that are separate, and I don’t know how to do the compositing from this point.

C

Ah, you don’t want separate canvases.
So forget about adding a canvas in index.html.

Just use
var ctx = game.canvas.getContext('2d');
assuming const game = new Phaser.Game(config);

Or use a Phaser Graphics Object.

1 Like

Actually I tried that but I was getting the error I said before.
Error

I will take a look at the Phaser Graphics Object.

game.canvas gives null?

Did you change
var ctx = document.getElementById('canvas').getContext('2d');
to
var ctx = game.canvas.getContext('2d');

It seems impossible that would be undefined…

1 Like

Oh, that was because I had that code outside of my Function Create. :sweat:.

Now, when I try to draw something, nothing shows up on my canvas.

function draw() {
  var ctx = game.canvas.getContext('2d');
  for (var i = 0; i < 6; i++) {
    for (var j = 0; j < 6; j++) {
      ctx.strokeStyle = 'rgb(0, ' + Math.floor(255 - 42.5 * i) + ', ' +
                        Math.floor(255 - 42.5 * j) + ')';
      ctx.beginPath();
      ctx.arc(12.5 + j * 25, 12.5 + i * 25, 10, 0, Math.PI * 2, true);
      ctx.stroke();
    }
  }
}

draw();

Canvas

I guess Phaser doesn’t like people meddling with the canvas outside of its own displaylist, scenegraph etc.

So use one of Phaser’s Canvas options, either CanvasTexture which lets you use it as a standard canvas, or Graphics, which is slightly different, using Blendmodes.

1 Like

Well, does that mean I can’t use the Compositing? Because I’ve trying for a while and I can’t get it to work with the CanvasTexture.

Thank you for your patience.

function create() {
    originalTexture = this.textures.get('dude').getSourceImage();
    var newTexture = this.textures.createCanvas('dude_new', originalTexture.width, originalTexture.height);
    var ctx = newTexture.context;
    ctx.fillStyle = '#00ff00';
    ctx.fillRect(0, 0, originalTexture.width, originalTexture.height);
    ctx.globalCompositeOperation = 'multiply';
    ctx.drawImage(originalTexture, 0, 0);
    ctx.globalCompositeOperation = 'destination-atop';
    ctx.drawImage(originalTexture, 0, 0);

    dude = this.add.image(100, 100, 'dude');
    dude2 = this.add.image(200, 100, 'dude_new');
}
1 Like

Thanks a lot. You’ve been a great help

1 Like