问题
I need to draw 2 variations of the same image on top of each other, whose alpha values add up to 1.
For example :
- imageA
has an alpha of 0.4
, while imageB
has an alpha of 0.6
.
- The desired result is a completely opaque image (alpha of 1.0
for each pixel), which appears as a 60% blend of imageB
, and a 40% blend of imageA
.
However, I believe that the html5 canvas by default uses a blend mode other than add on alpha blended images, so two things drawn with alphas below 1 will not add up to 1.
I've tried all the different compositing modes, along with the Adobe blend modes, but none of them have the desired result.
A simple test case is to draw a magenta rect twice, both with alpha of 0.5
. My desire is for the resulting pixels to be rgb(255, 0, 255)
. However, the result is slightly transparent.
Does anyone know some way of achieving this result?
回答1:
• If you look at the canvas'context2D specification about the default blending mode ('source-over'):
http://dev.w3.org/fxtf/compositing-1/#simplealphacompositing
You'll see the formula used is ( i renamed the variables for clarity) :
colorOut = prevColor x prevAlpha + newColor x newAlpha x (1 - prevAlpha)
And for resulting alpha :
αlphaOut = prevAlpha + newAlpha x (1 - prevAlpha)
Notice that,
a) unexpectedly, the formula is not linear (due to : newAlpha x (1 - prevAlpha) factor ).
b) a point has both smaller r,g,b values and reduced alpha. So when re-using it, it
will contribute for square(0.6)==0.36 to the final image. (...completely counter-intuitive...)
• So what does happen for your 60% / 40% draws ?
1) image A is drawn with alpha = 60%. The formula above gives :
colorOut = color_A * 0.6 ;
alphaOut = 0.6 ;
2) image B is drawn with alpha = 40%
colorOut = color_A * 0.6 * 0.6 + color_B x 0.4 x (0.4);
alphaOut = 0.6 + 0.4 * 0.4;
So final formula is ==>
colorOut = 0.36 * color_A + 0.16 * color_B ;
alphaOut = 0.76 ;
You see that's not at all the 60/40 mix you expected.
• How to fix ?
1) You can do it by hand using getImageData / putImageData. Be aware of the Cross-Origin issue : if your images don't come from your domain, their imageData will be empty. For performances, bet on a 50+ slow-down factor compared to the canvas (= the GPU) doing the job. 'real time' (=60fps) might not be possible depending on image size/computing power/browser. But you have full control.
2) But if you now look at the 'ligther' composite mode, it seems we have our guy :
http://dev.w3.org/fxtf/compositing-1/#porterduffcompositingoperators_plus
Formula is now :
colorOut = prevColor x prevAlpha + newColor x newAlpha
And for resulting alpha :
αlphaOut = prevAlpha + newAlpha
You see that this is a simple 'plus' operator, perfect for your requirement.
So solution is :
- save ctx.
- set lighter composite mode.
- draw first image at 60% alpha.
- draw second image at 40% alpha.
- restore ctx.
Last words : if you want to check that the final alpha is right (==255), use that kind of function :
function logAlpha(ctx,x,y) {
var imDt = ctx.getImageData(x,y,1,1).data;
if (!(imDt[0] || imDt[1] || imDt[2])) console.log('null point. CORS issue ?');
console.log(' point at (' +x+ ',' +y+ ') has an alpha of ' + imDt[3] ) ;
}
来源:https://stackoverflow.com/questions/26200800/blending-two-images-with-html5-canvas