I'm trying to use alpha blending with the HTML5 canvas. While the colors seem roughly OK to the eye, examining the actual pixel values with context.getImageData()
shows discrepancies from the standard Porter-Duff model that seem far too large to be explained by the limitations of 8-bit arithmetic.
Here's the summary:
Read a blank canvas =>
R=G=B=A=0
. This is a bit strange. Since my unpainted canvas shows up as pure white, I would have expectedR=G=B=255
. Of course, withA=0
, the R, G, and B are irrelevant anyway. Nonetheless, I'm not sure howR=G=B=A=0
displays as pure white.Paint over it with
R=200,G=B=0,A=0.75
. Read the result asR=201,G=B=0,A=0.75
. TheA=0.75
seems correct; Porter-Duff wantA_final=.75+(.25*0)=.75
. TheG=B=0
is fine too. But I believe the R should beR=(.75*200)+(0*(.75-1)*0)=150
. Clearly150 != 201
. Instead, they seem to have used the equationFinal_color=new_color
for R, G and B.Paint over it again with
R=100,G=200,B=0,A=0.2
. Read the result asR=175,G=50,B=0,A=0.8
. Again, the A computations seem correct;.2+(1-.2)*.75=.8
. However, Porter-Duff predictsR=(.2*100)+(.75*(1-.2)*201)=(.2*100)+(.6*201)=141
. Again, clearly141 != 175
. Green is similarly inconsistent. However, both R and G are explained by the formulaFinal_color=.25*new_color+.75*old_color
-- which unfortunately is not what Porter-Duff blending predicts.
A few other notes:
- I've presented all alpha values above as being in the range [0,1] for simplicity. Of course, getImageData actually returns them in [0,255].
I've checked the spec, and it seems to agree with me as far as what the canvas should do.
I've tried both Chrome V37 and IE V11 with essentially the same results.
Painting with
A=0
works fine, as does painting withA=1
.I found a 2011 post that complains of similar issues, but believes it is a browser bug. I have a hard time believing such a blatant bug would still remain three years later -- to appear identically in two different browsers.
Here are the code details for how I did the reading:
pixels=(context.getImageData (10,10,1,1)).data; console.log(pixels);
And the writing:
context.beginPath();
context.arc (10,10, 10, 0, 2*Math.PI);
context.fillStyle = "rgba(200,0,0,.75)";
context.fill ();