1

What's the best way to scale alpha values in a canvas?

The first problem I'm trying to solve is drawing a sprite that has intrinsic low alpha values. I want to draw it 3-4 times brighter than it really is. Currently I'm just drawing it 4 times in the same spot. (I cannot edit the image file and globalAlpha doesn't go above 1)

The second problem I'm trying to solve is drawing the boundary of multiple overlapping sprites. The sprites are circular but with squiggles. I figured I'd use this method combined with globalCompositeOperation = 'destination-out', but for that I need to maximize the alpha values for the second drawing.

Community
  • 1
  • 1
AnnanFay
  • 9,573
  • 15
  • 63
  • 86

2 Answers2

1

You can "brighten" an rgba color by flattening it to rgb and then increasing the rgb component values.

  • Convert the rgba value to rgb, also taking the background color into effect.
  • Increase the resulting red,green,blue values by a percentage to "brighten" the color.

Here's a function to do that (disclaimer: untested code here!):

function brighten(RGBA,bg,pct){

  // convert rgba to rgb

  alpha = 1 - RGBA.alpha/255;
  red = Math.round((RGBA.alpha*(RGBA.red/255)+(alpha*(bg.red/255)))*255);
  green = Math.round((RGBA.alpha*(RGBA.green/255)+(alpha*(bg.green/255)))*255);
  blue = Math.round((RGBA.alpha*(RGBA.blue/255)+(alpha*(bg.blue/255)))*255);

  // brighten the flattened rgb by a percentage (100 will leave the rgb unaltered)

  redBright=parseInt(  Math.min(255,red*pct/100) );
  greenBright=parseInt(  Math.min(255,green*pct/100) );
  blueBright=parseInt(  Math.min(255,blue*pct/100) );

  return({red:redBright,green:greenBright,blue:blueBright});

}    
markE
  • 102,905
  • 11
  • 164
  • 176
1

As an option to markE's answer - you can simply scale the alpha channel directly.

I would only recommend this approach as a part of a pre-processing stage and not for use every time you need to use a sprite as iterating the buffer this way is a relatively slow process.

LIVE DEMO HERE

snapshot from demo

Assuming you already have the sprite in a canvas and know its position:

/// get the image data and cache its pixel buffer and length
var imageData = context.getImageData(x, y, width, height);
var data = imageData.data;
var length = data.length;
var i = 0;

var scale = 4; /// scale values 4 times. This may be a fractional value

/// scale only alpha channel
for(; i < length; i += 4) {
    data[i + 3] *= scale;
}

context.putImageData(imageData, x, y);

The good thing with the Uint8ClampedArray which the canvas is using clamps and rounds the values for you so you do not need to check lower or upper bounds, nor convert the value to integer - the internal code do all this for you.

  • I'm marking this as the correct answer because it's what I ended up using. As you mention it has terrible efficiency. – AnnanFay Oct 20 '13 at 10:43