Search the Community
Showing results for tags 'alpha transparency'.
-
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.)
-
- alpha transparency
- setpixel
-
(and 1 more)
Tagged with: