0

Long story short, I need to do alpha blending in javascript because I want aliased graphics ( which AA can't be turned off here ) and webGL is not currently an option. Don't judge me. :D

Interesting to note, my result is producing a similar phenomenon to one that I observe in the native canvas when alpha blending, so I don't feel too bad about it, but I'm just trying to get my head around what's happening here.

The example is on KA to make it easier to twiddle.

Here is my method:

// sanity check
// https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
// var sA = alpha ( sRGBA ) / 255;
// var dA = alpha ( dRGBA ) / 255;
// var rA = sA + dA * ( 1 - sA );
// var pRGB = [ red ( sRGBA ), green ( sRGBA ), blue ( sRGBA ) ].map ( function ( e ) {
//     return e * sA;
// } );
// var rRGBA = [ red ( dRGBA ), green ( dRGBA ), blue ( dRGBA ) ].map ( function ( e, i ) {
//     return Math.round ( ( pRGB [ i ] + e * dA * ( 1 - sA ) ) / rA );
// } );
// rRGBA.push ( Math.round ( rA * 255 ) );
// println ( rRGBA );

// horrible bit hack and lookup
var blendAlpha = ( function () {

    // lookup table for division
    var aDiv = ( function () {
        return this.Function ( "return new Uint32Array(256);" )();
    } ) ();

    // Aout = 0 => RGBout = 0
    for ( var i = 1; i < 256; i = i + 1 ) {
        aDiv [ i ] = 257 * 255 / i;
    }

    return function ( sRGBA, dRGBA ) {
        sRGBA = sRGBA >>> 0;
        dRGBA = dRGBA >>> 0;
        var s = ( 1 + ( sRGBA >>> 24 ) ) >>> 0, d = ( 1 + ( dRGBA >>> 24 ) ) >>> 0,
            a = ( s + d - ( s * d >>> 8 ) - 1 ) >>> 0,
            ia = ( 257 - s ) >>> 0,
            RB = 0xff00ff >>> 0,
            G = 0xff00 >>> 0,

            rb = (
                ( s * ( sRGBA & RB ) >>> 8 & RB >>> 0 ) +
                ( ( d * ( dRGBA & RB ) >>> 8 & RB >>> 0 ) * ia >>> 8 & RB >>> 0 )
            ) * aDiv [ a ] >>> 8 & RB >>> 0,

            g = (
                ( s * ( sRGBA & G ) >>> 8 & G >>> 0 ) +
                ( ( d * ( dRGBA & G ) >>> 8 & G >>> 0 ) * ia >>> 8 & G >>> 0 )
            ) * aDiv [ a ] >>> 8 & G >>> 0;

        return rb | g | a << 24 >>> 0;
    };
} ) ();

And here is what I see with a low alpha source color and a transparent destination:

source         destination    result         native
======================================================
0,127,0,0      0,0,0,0        0,0,0,1        0,0,0,0
0,127,0,1      0,0,0,0        0,0,0,2        0,0,0,1
0,127,0,2      0,0,0,0        0,85,0,3       0,128,0,2
0,127,0,3      0,0,0,0        0,63,0,4       0,85,0,3
0,127,0,4      0,0,0,0        0,102,0,5      0,128,0,4
0,127,0,5      0,0,0,0        0,85,0,6       0,102,0,5
0,127,0,6      0,0,0,0        0,109,0,7      0,128,0,6
0,127,0,7      0,0,0,0        0,95,0,8       0,109,0,7
0,127,0,8      0,0,0,0        0,113,0,9      0,128,0,8
0,127,0,9      0,0,0,0        0,102,0,10     0,113,0,9

Otherwise the resulting values are ballpark/acceptable, e.g. higher up in the alpha range on the source.

I believe the bifurcation is a result of the way I am doing the division step, but I'm not sure exactly.

Does anybody have a better idea of what's going on here or have dealt with this kind of issue before?

I don't care at the moment if my method is faulty/crappy. I'll deal with that later, just curious about the weird/unexpected bifurcation. I would have expected a stair step, or something like that, but not this.

Nolo
  • 846
  • 9
  • 19
  • In my opinion, this code is a mess and I think it produces some wrong values. You can easily make your own class and implement the *Alpha* blending algorithms which you can find here https://en.wikipedia.org/wiki/Alpha_compositing and here http://ssp.impulsetrain.com/porterduff.html. There is not only one compositing method, but there are 12 compositing methods and they are called *Porter/Duff* operators/methods. I think this code here used the *Source Over* method. – Christos Lytras Dec 11 '16 at 12:39
  • See the first paragraph of my answer [here](http://stackoverflow.com/questions/40796852/mix-two-non-opaque-colors-with-hue-blend-mode/40962043#40962043) on how to blend transparent colors with *Source Over Backdrop* method. – Christos Lytras Dec 11 '16 at 12:41
  • @ChristosLytras I see. It obviously produces some wrong values, but does the native canvas not technically produce wrong values as demonstrated in the output? Alpha blending is not the question. – Nolo Dec 11 '16 at 12:43
  • I wouldn't care much for a bifurcation result based on a wrong formula. I don't think these values are correct alpha blending values, but maybe there is something that I am missing here. How did you compare the values to those produced on a canvas when blending alpha colors? Did you find any online canvas example that confirms the RGBA result values? – Christos Lytras Dec 11 '16 at 13:01
  • @ChristosLytras I'm using Chrome. The column on the right side of the output is what Chrome says the RGBA values should be based on the input values in the first two columns. If you find that troubling then that would be a good starting point for trying to answer the question. :-) – Nolo Dec 11 '16 at 18:29
  • I am not sure how this algorithm treats alpha channel. Alpha channel values, suppose to be percentage fractions from `0` to `1` (`0%-100%`). May I ask why don't you use [Canvas Context2D globalCompositeOperation](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation) to blend alpha images inside a canvas element and you want to make your own blending functions? – Christos Lytras Dec 11 '16 at 19:41
  • @ChristosLytras When rendering shapes with the native context the edges of the shapes are anti-aliased, not what I want. This code is intended for the inner loop of a rendering algorithm for complex polygons. That's the purpose of the complicated code. It does approximately the same thing as the formula described on Wikipedia just with integer operations to make it more efficient. The percent values are values between 0 and 255 and the computation works around that fact to do the same thing as the formula. – Nolo Dec 11 '16 at 20:59
  • Yes I know that this method expects the alpha values to be byte wide, it's more than obvious if you look at the line where it converts those bytes to fractions `sA = alpha(sRGBA) / 255;`. With a simple search, I found a way around to get rid of antializing when drawing shapes on a canvas [here](http://stackoverflow.com/questions/195262/can-i-turn-off-antialiasing-on-an-html-canvas-element). It's not a real antialias for shapes, but if you need to make this to work only with shapes, then you need to implement a line drawing algorithm rather than just color alpha blending. – Christos Lytras Dec 12 '16 at 00:11
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/130367/discussion-between-nolo-and-christos-lytras). – Nolo Dec 12 '16 at 00:30

0 Answers0