- From: ddailey <ddailey@zoominternet.net>
- Date: Tue, 27 Mar 2007 20:11:05 -0400
I suspect you already are aware of this but in addition to the references you provide the SVG 1.1 reco gives examples of the Porter-Duff composites http://www.w3.org/TR/SVG11/filters.html It appears that Opera is not handling them properly in SVG either: http://srufaculty.sru.edu/david.dailey/svg/newstuff/filterComposite2.svg fwiw David Dailey ----- Original Message ----- From: "Philip Taylor" <excors+whatwg@gmail.com> To: <whatwg at lists.whatwg.org> Sent: Tuesday, March 27, 2007 7:30 PM Subject: [whatwg] Canvas - globalCompositeOperation > It has been mentioned before [1] that globalCompositeOperation is > poorly defined - the spec has a note saying "The source-* descriptions > below don't define what should happen with semi-transparent regions" > and is vague about non-transparent colours too - and it is not > implemented interoperably. I haven't seen any descriptions of what it > ought to do, so this is an attempt to explain and describe what I > believe should be specified. > > ~~~~ > > Most of the operations are defined in the Porter-Duff paper [2], which > does say how semi-transparent regions should be handled. My summary of > it: > > A single pixel with 25% alpha is considered to be a large number of > subpixels of which a uniformly-random 25% are totally solid and 75% > are totally transparent - the subpixels only have 1-bit alpha. When > you combine that 25% alpha pixel 'A' with a 50% alpha pixel 'B', you > expect 25%*50% of the subpixels to overlap, while (1-25%)*(1-50%) of > subpixels are covered by neither pixel, and similar for the subpixels > that are covered by only 'A' or only 'B'. > > The composite operators define how you choose which of the inputs (0, > A, B) is used as the output of the subpixel, for each of the four > possible coverage cases (!A & !B, !A & B, A & !B, A & B). Then you > just (conceptually) average all the subpixels, to get the actual pixel > output. > > > The P-D paper assumes colours are represented with pre-multiplied > alpha (where nonpremul[r, g, b, a] == premul[r*a, g*a, b*a, a]), e.g. > 50%-transparent bright red is premul[0.5, 0, 0, 0.5]. The canvas > (inheriting from CSS) and seemingly much of the rest of the world > (e.g. Cairo, and most humans) use non-pre-multiplied alpha in their > APIs, e.g. 50% transparent red is "rgba(255, 0, 0, 0.5)". But the > compositing equations won't work nicely with non-pre-multiplied alpha, > and implementations appear to use pre-multiplied alpha internally, so > the operation should be specified in the pre-multiplied form. (I'll > use lowercase c for pre-multiplied colour components, uppercase C for > non-pre-multiplied.) > > > Taking that into account gives the following algorithm for most of the > composite operators: > > | Operator | FA | FB > | -----------------+------+------ > | source-over | 1 | 1-aA > | destination-over | 1-aB | 1 > | source-in | aB | 0 > | destination-in | 0 | aA > | source-out | 1-aB | 0 > | destination-out | 0 | 1-aA > | source-atop | aB | 1-aA > | destination-atop | 1-aB | aA > | xor | 1-aB | 1-aA > | copy | 1 | 0 > | lighter | 1 | 1 > | > | cO = cA*FA + cB*FB > | aO = aA*FA + aB*FB > | > | where cX is the pre-multiplied colour component of pixel X, in the > range > | [0, 1]; aX is the alpha component of pixel X in the range [0, 1]; A and > B > | are the source and destination pixels respectively; O is the output > pixel. > | > | The calculation of aO must be clamped to the range [0, 1]. > > ("lighter" can result in aO > 1, hence the need to clamp it.) > > Only "darker" cannot fit in this table (given that FA is a function of > aB, and FB is a function of aA). > > ~~~~ > > To compare the main implementations (Firefox trunk 20070326, Opera > 9.20, Safari 2.0.4), there is a demonstration at > http://canvex.lazyilluminati.com/tests/composite/composite.html > and example outputs at > http://canvex.lazyilluminati.com/tests/composite/firefox3_20070326.png > http://canvex.lazyilluminati.com/tests/composite/opera920_8762.png > http://canvex.lazyilluminati.com/tests/composite/safari204.png > > "over", "in", "out" and "copy" are all correct (in that they match the > above algorithm). > > "xor" is correct in Firefox and Safari, but incorrect in Opera; Opera > appears to be using the pre-multiplied equations on the > non-pre-multiplied colours (i.e. doing CO = CA*FA + CB*FB, where CX is > the non-pre-multiplied colour component). > > "atop" and "lighter" are correct in Firefox and Safari, but incorrect > in Opera; I don't know what Opera is doing. > > "darker" is messy: > > Firefox's "darker" is implemented as: > > Operator | FA | FB > ------------------+-------------------+------ > darker [saturate] | min(1, (1-aB)/aA) | 1 > > It seems this can't easily be hardware-accelerated - the Cairo GL > backend [3] doesn't support CAIRO_OPERATOR_SATURATE, and says > > case CAIRO_OPERATOR_SATURATE: > /* XXX: This line should never be reached. Glitz backend should > bail out earlier if saturate operator is used. OpenGL can't do > saturate with pre-multiplied colors. Solid colors can still be done as > we can just un-pre-multiply them. However, support for that will have > to be added to glitz. */ > > Safari gives completely different output, and is very close to > implementing it with non-pre-multiplied colours as: > > CO = 1 - (1-CA)*aA - (1-CB)*aB > aO = aA + aB > > except not quite like that (see the bottom-right box in the example > page), and I don't know what it's really doing. Opera is also quite > like that, except more different. > > KHTML [4] doesn't implement either "lighter" or "darker" - it treats > them as "source-over". Rhino Canvas [5] does the same. Both are > relying on existing graphics libraries for the actual drawing, which > don't provide those operations - see QPainter::CompositionMode [6] and > java.awt.AlphaComposite [7]. > > ~~~~ > > Conclusion: The above definition is sensible (in my opinion) and works > (in practice). > > Opera needs to fix "xor" and "atop". Firefox and Safari are fine. > [...at least when ignoring other compositing bugs, unrelated to this > colour calculation.] > > I would be happy if "darker" was removed from the spec - there isn't > an obvious definition for it, and it's not interoperably implemented > at all and it sounds like it never will be. Existing implementations > can add "apple-plusdarker", "moz-saturate", etc, if they still want to > provide the old functionality. > > "lighter" seems much easier to define, and more useful, so I think > it's perhaps worth keeping - but it looks like a pain for those using > Qt/Java/etc libraries which don't support anything other than the > standard Porter-Duff operators, and I don't know if it's a difficulty > for Opera to fix their implementation of it. Does anyone have views on > this or on "darker"? > > ~~~~ > > [1] > http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2006-July/006963.html > [2] http://keithp.com/~keithp/porterduff/p253-porter.pdf > [3] > http://gitweb.freedesktop.org/?p=cairo.git;a=blob;hb=HEAD;f=src/cairo-glitz-surface.c > [4] > http://websvn.kde.org/trunk/KDE/kdelibs/khtml/ecma/kjs_context2d.cpp?revision=605784&view=markup > [5] > http://rhino-canvas.cvs.sourceforge.net/rhino-canvas/rhino-canvas/src/net/sf/rhinocanvas/js/CanvasRenderingContext2D.java?view=markup > [6] http://doc.trolltech.com/4.1/qpainter.html#CompositionMode-enum > [7] http://java.sun.com/j2se/1.5.0/docs/api/java/awt/AlphaComposite.html > > -- > Philip Taylor > excors at gmail.com > >
Received on Tuesday, 27 March 2007 17:11:05 UTC