Jump to content

Applying transparency when setting pixels in the 2D canvas


grelf
 Share

Recommended Posts

When using the 2D context of a canvas each pixel has 4 components: red, green, blue and alpha (transparency). Each has a range from 0 to 255 (unsigned byte). Access to the data is via an object of type ImageData which has 3 properties: width, height and data. Data has the pixel values sequentially scanning horizontally and then downwards from top left. The data array therefore has a length equal to 4 times the number of pixels.

It is possible to add getPixel() and setPixel() methods to the ImageData prototype but I will not call them exactly that in case the standard someday adds these anyway. So I could simply write

    /** Returns an array with 4 elements: RGBA. */
    ImageData.prototype.grGetPixel = function (x, y)
    { var i = (this.width * Math.round (y) + Math.round (x)) * 4;
      // NB: JavaScript will try to index arrays with non-integers!
      return [this.data [ i ] , this.data [i + 1], this.data [i + 2], this.data [i + 3]];
    };

    /** rgba is an array with 4 elements: RGBA.
      * Ignores transparent pixels (A = 0). */
    ImageData.prototype.grSetPixel = function (x, y, rgba)
    { var i = (this.wd * Math.round (y) + Math.round (x)) * 4;
      if (0 === rgba [3]) return;
      this.data [ i ] = rgba [0];
      this.data [i + 1] = rgba [1];
      this.data [i + 2] = rgba [2];
      this.data [i + 3] = rgba [3];
    };

BUT what about using the alpha transparency value? How is it supposed to be applied?

Trying to find an algorithm in the working HTML5 standard, or in the W3 CSS standards or on the excellent MDN site all eventually lead to a short paragraph in the W3C standard for SVG: https://www.w3.org/TR/SVG11/masking.html#SimpleAlphaBlending .
That says that it assumes the alpha to be premultiplied. To find out what that means see https://en.wikipedia.org/wiki/Alpha_compositing .
Thanks to a section on another Wikipedia page (https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending) I was able eventually to work out what I wanted to do.

The next difficulty was that the alpha part of a canvas pixel has integer values in the range 0..255 but the compositing formulae need decimal multipliers in the range 0..1. Did I want to do a division, a / 255, every time I want to set a pixel? Certainly not: division is slow. Fortunately there are only 256 cases to consider so the solution is to set up a look-up table (LUT) when my program starts:

    var alphaLUT = new Array (256); // Converts index 0..255 to 0..1
    for (var i = 0; i < 256; i++) alphaLUT [ i ] = i / 255;

So my grSetPixel finally became

    /** rgba is an array with 4 elements: RGBA.
      * Ignores completely transparent pixels (A = 0). */
    ImageData.prototype.grSetPixel = function (x, y, rgba)
    { var i = (this.width * Math.round (y) + Math.round (x)) * 4;
      if (0 === rgba [3]) return;
      var r = this.data [ i ] ; // Existing value, to be composited
      var g = this.data [i + 1];
      var b = this.data [i + 2];
      var a01 = alphaLUT [rgba [3]];
      var a10 = 1 - a01;
      this.data [ i ] = (a10 * r + a01 * rgba [0]) & 0xff;
      this.data [i + 1] = (a10 * g + a01 * rgba [1]) & 0xff;
      this.data [i + 2] = (a10 * b + a01 * rgba [2]) & 0xff;
      this.data [i + 3] = 255;
    };

and it works a treat. I wanted to put glass signs in my forest.

(Yes, I am oldfashioned - I see nothing wrong with var.)

 

GlassSign_400x189.png

Edited by grelf
My code sections keep being altered when I submit
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...