Discussion:
Canvas - globalCompositeOperation
Philip Taylor
2007-03-27 23:30:41 UTC
Permalink
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
Loading Image...
Loading Image...
Loading Image...

"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-***@public.gmane.org
ddailey
2007-03-28 00:11:05 UTC
Permalink
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-***@public.gmane.org>
To: <whatwg-***@public.gmane.org>
Sent: Tuesday, March 27, 2007 7:30 PM
Subject: [whatwg] Canvas - globalCompositeOperation
Post by Philip Taylor
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
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
| 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.
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
/* 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
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
Philip Taylor
2007-03-29 12:14:31 UTC
Permalink
I've added some tests at
http://canvex.lazyilluminati.com/tests/tests/index.2d.composite.transparent.html
based on the definitions I suggested. Firefox and Safari match the
expected results, Opera misses on atop/xor/lighter. (I skipped darker,
because it doesn't make sense and I'm hoping it'll just go away...)
(The automatic testing may give the wrong answers if your browser has
a getImageData that isn't the same as Firefox's (which returns
premultiplied alpha) - I believe that's a problem in Firefox and/or
the spec, but I'm not looking into it in any detail now.)


When I originally said
| The calculation of aO must be clamped to the range [0, 1].
that actually should be
| The calculation of aO and cO must be clamped to the range [0, 1].
because of e.g. the "white lighten white" case, where cO can exceed 1.
Post by ddailey
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
Thanks, I hadn't thought of looking there. That SVG 1.1 section
(feComposite) seems to skip over the issue of actually defining what
the operators mean, and refers to the Porter-Duff paper. The SVG 1.2
draft at http://www.w3.org/TR/2004/WD-SVG12-20041027/rendering.html#comp-op-prop
does give the equations, and also adds a dozen extra operators, but
removes the ability to define your own arithmetic ones. SVG Tiny 1.2
at http://www.w3.org/TR/SVGMobile12/painting.html#CompositingSimpleAlpha
appears to get rid of all the compositing except for plain source-over
blending, so it's the same as SVG 1.0 at
http://www.w3.org/TR/SVG10/masking.html#SimpleAlphaBlending . (I don't
know what to conclude from this, except that there presumably isn't a
universal consistent convention for what compositing operators should
be provided.)
Post by ddailey
http://srufaculty.sru.edu/david.dailey/svg/newstuff/filterComposite2.svg
It seems to at least do
http://www.w3.org/TR/SVG11/images/filters/feComposite.svg quite close
to the example rendering (whereas Firefox does nothing except
source-over) - the colours are a bit darker, but I don't know if
that's an issue with Opera or with the example.
Post by ddailey
David Dailey
--
Philip Taylor
excors-***@public.gmane.org
Vladimir Vukicevic
2007-04-04 07:07:11 UTC
Permalink
Post by Philip Taylor
[...]
Cool stuff! I'll look through your tests and fix up the mozilla
implementation as much as possible.
Post by Philip Taylor
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.
I'd be happy with getting rid of it.
Post by Philip Taylor
"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"?
Well, if we have lighter, we should keep darker; I think that for
mozilla at least, we can implement this using some slow-boat fallback
mechanism -- basically, render the path/image to a separate surface,
then manually do the requested math if things don't map directly to one
of our operators; this is what our SVG impl does now for many of the SVG
filters.

- Vlad
Ian Hickson
2007-05-16 23:25:31 UTC
Permalink
Post by Philip Taylor
http://keithp.com/~keithp/porterduff/p253-porter.pdf
I've written tests for the 11 operators defined in the paper, plus a
http://dbaron.org/tests/canvas/composite-operations4
It seems like 'darker' is currently not interoperable, and might be
unlikely to become interoperable, due to different availability of
compositing operators across platforms. (Quartz has PlusDarker. Mozilla
uses the saturate operator that is provided by Cairo/libpixman (and also
by XRender), which is described in [2] and [3], but seems to me to be
quite different.)
One solution to that problem would be to remove 'darker' from the spec
rather than defining it one way or the other. Thoughts?
I've referenced the paper and dropped 'darker'.
Post by Philip Taylor
[snip a very long and detailed discussion of the operators, with much
research]
Wow. Thanks! Based on David's input above and on your own advice, and on
Vlad's later comments, I've simply removed 'darker' and referenced
Porter-Duff for the others.
Post by Philip Taylor
Well, if we have lighter, we should keep darker; I think that for
mozilla at least, we can implement this using some slow-boat fallback
mechanism -- basically, render the path/image to a separate surface,
then manually do the requested math if things don't map directly to one
of our operators; this is what our SVG impl does now for many of the SVG
filters.
According to David, 'lighter' is just Porter-Duff's 'plus'. Is that not
right? For now I've left it, with that definition.
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Mathieu HENRI
2007-05-20 14:40:34 UTC
Permalink
Post by Ian Hickson
Post by Philip Taylor
http://keithp.com/~keithp/porterduff/p253-porter.pdf
I've written tests for the 11 operators defined in the paper, plus a
http://dbaron.org/tests/canvas/composite-operations4
It seems like 'darker' is currently not interoperable, and might be
unlikely to become interoperable, due to different availability of
compositing operators across platforms. (Quartz has PlusDarker. Mozilla
uses the saturate operator that is provided by Cairo/libpixman (and also
by XRender), which is described in [2] and [3], but seems to me to be
quite different.)
One solution to that problem would be to remove 'darker' from the spec
rather than defining it one way or the other. Thoughts?
I've referenced the paper and dropped 'darker'.
Please, please, pretty please, bring 'darker' back.
Rename it 'multiply' if you want, or if like me you think this name
better reflects the operation previously known as 'darker'.


Here is a list of use cases for 'darker', I already applied in realtime
animations:

1. filtering and/or separating the channels of a canvas *very* quickly.

2. multiplying images. This can be really useful to compose/combine images.

3. "burning" images. To do so, copy the canvas A onto a smaller canvas
B, fill B with a white rectangle of medium/high opacity if you want,
then draw B on top of A in 'daker'. Tada, you just enhanced the dark
areas of A and even introduced some nice color bleeding.


Of couse all of this, actually ANY composite operation, can be achieved
using ImageData objects ... but *MUCH* slower.
Post by Ian Hickson
Post by Philip Taylor
[snip a very long and detailed discussion of the operators, with much
research]
Wow. Thanks! Based on David's input above and on your own advice, and on
Vlad's later comments, I've simply removed 'darker' and referenced
Porter-Duff for the others.
--
Mathieu 'p01' HENRI
JavaScript developer, Opera Software ASA
Philip Taylor
2007-05-20 16:53:13 UTC
Permalink
Post by Mathieu HENRI
Post by Ian Hickson
I've referenced the paper and dropped 'darker'.
Please, please, pretty please, bring 'darker' back.
Rename it 'multiply' if you want, or if like me you think this name
better reflects the operation previously known as 'darker'.
I think this is more like an entirely new operation, rather than being
related to 'darker', given that 'darker' has three totally different
implementations and none of them multiply. In the cases where all the
alpha values are 1, and when CO is the non-premultiplied output colour
component and CA and CB are the input (source and destination)
colours, I believe you get:

Mozilla: CO = CB
Opera: CO = min(CA, CB)
Safari: CO = CA + CB - 1

If you care about alpha, and take cX as premultiplied colour
components (and CX as non-premul, and aX as alpha), then you get:
Mozilla:
cO = cA * min(1, (1-aB)/aA) + cB
aO = aA * min(1, (1-aB)/aA) + aB
Opera:
CO = min( (1-aA)*CB + CA, (1-aB)*CA + CB )
aO = 1 - (1-aA)*(1-aB)
Safari:
CO = 1 - (1-CA)*aA - (1-CB)*aB except not quite
aO = aA + aB

(Opera is kind of similar to SVG 1.1's 'darker', except not the same
since it's doing it on non-premultiplied colours instead.)
Post by Mathieu HENRI
Here is a list of use cases for 'darker', I already applied in realtime
1. filtering and/or separating the channels of a canvas *very* quickly.
2. multiplying images. This can be really useful to compose/combine images.
3. "burning" images. To do so, copy the canvas A onto a smaller canvas
B, fill B with a white rectangle of medium/high opacity if you want,
then draw B on top of A in 'daker'. Tada, you just enhanced the dark
areas of A and even introduced some nice color bleeding.
If there was a 'multiply', it should probably be defined as something like:
CO = CA * CB
aO = aA * aB
(taking all values to be in the range [0,1]) because that actually
makes some sense and it seems to be useful.

For case 1, you could multiply by (1, 0, 0, 1) to extract the R
channel. (You wouldn't be able to combine the channels to make
greyscale, but you can't do that now anyway - that'd need something
like OpenGL's dot operator, which does RO=GO=BO = RA*RB + GA*GB +
BA*BB, which would probably be a nice one to add if we wanted more
operations.)

For case 2, I'm not sure why you can't do that already. You can reduce
the brightness by drawing a (0, 0, 0, 0.5) rectangle on top of the
image. If it's e.g. a spotlight where you want to multiply by an image
rather than a solid colour, you can make that image be black and store
its shape in its alpha channel, then draw that on top with source-over
to mask out certain areas. You can make an image brighter by drawing
on top of itself with 'lighter' to get up to 2x brightness (and repeat
for exponential brightening). Are there specific situations which
can't already be handled without 'darker'?

For case 3, I think 'multiply' above would possibly handle that,
depending on exactly what the desired effect is.


I assume 'multiply' would be a bit more complicated to implement and
less efficient than the other operations, since the graphics libraries
usually don't provide that function; but maybe that's acceptable. At
least the definition above is identical whether you store
premultiplied or non-premultiplied components, so it'll only ever be a
multiplication and (I think) a couple of shifts and an addition per
component, which seems fast enough.

Maybe the new 'multiply' operation should just be named 'darker' so
it's compatible with old browsers (though very buggily implemented in
all of them), but defined like above.


(Just for completeness with respect to my original email about
composite operations, Opera's implementation of 'lighter' appears to
be:
if aA == 0:
CO = CB
aO = aB
else if aB == 0:
CO = CA
aO = aA
else:
CO = max(CA, CB)
aO = 1 - (1-aA)*(1-aB)
which has very odd behaviour, since the output changes drastically if
you change alpha from 0.0 to 0.01. In all the implementations,
'lighter' is confusingly unlike 'darker' - names like 'plus' and
'multiply' make much more sense.)
--
Philip Taylor
excors-***@public.gmane.org
L. David Baron
2007-05-20 22:30:15 UTC
Permalink
Post by Philip Taylor
If you care about alpha, and take cX as premultiplied colour
cO = cA * min(1, (1-aB)/aA) + cB
aO = aA * min(1, (1-aB)/aA) + aB
Correct. This is the cairo "saturate" operator, which I think is
the same as Xrender's saturate operator.
Post by Philip Taylor
CO = 1 - (1-CA)*aA - (1-CB)*aB except not quite
aO = aA + aB
I thought what Safari does is basically "lighter", but with the
color components inverted (where inverting is CX = 1-CX, or cX = aX
- cX), so that it comes out to:

aO = min(1, aA + aB)
cO = aO - min(1, aA - cA + aB - cB)
Post by Philip Taylor
you change alpha from 0.0 to 0.01. In all the implementations,
'lighter' is confusingly unlike 'darker' - names like 'plus' and
'multiply' make much more sense.)
Hrm. I thought lighter was confusingly *like* darker, in that they
act exactly the same when the alphas add to less than or equal to 1.
(I think that's true for plus (a.k.a. lighter), Cocoa's darker, and
for saturate.)

-David
--
L. David Baron <URL: http://dbaron.org/ >
Technical Lead, Layout & CSS, Mozilla Corporation
ddailey
2007-05-21 00:17:58 UTC
Permalink
On Sunday, May 20, 2007 6:30 PML. David Baron wrote:
"...This is the cairo 'saturate' operator..."

The above reminded me: using SVG suggested to me a couple of questions*
about SVG that would seem to be just as relevant to the <canvas> tag:

1. Is there a way, using filters, to take an image A and produce its
photographic negative A', such that 255-C(A)=C(A') for each channel C in
{R,G,B)? I've fooled a bit with the filter "feComponentTransfer" with some
hints of success, but it seems like so natural a thing that there must be a
straightforward way that I'm just missing. Here is an approach that comes
close using <feComponentTransfer> together with <feComposite
operator="arithmetic">.
http://srufaculty.sru.edu/david.dailey/svg/ComponentTransferComposite.svg.
It produces an image which looks like a spectrum-equalized version of the
negative.

2. On a related theme, <feBlend/> has modes including "screen", "multiply"
etc. Is there a way to create the "difference" between two images (as with
the Photoshop difference filter) in SVG? Here's the best I have figured out
so far: http://srufaculty.sru.edu/david.dailey/svg/ImageDiffer.svg . The
ability to calculate differences between images comes in quite handy, for
example, in explaining image compression to students, in scientific image
processing, and I believe in forensics as well.

Both examples above will require either Opera's native support for SVG+SMIL
or something with the Adobe plugin, since the effect is differentially
phased in with SMIL.

Another question arises in my mind in this context: is there any reason that
any of the treatments of these effects (like the Porter-Duff operators,
darken, saturate etc.) should be any different than they are in the SVG
spec? So many things that I see in the treatment of canvas remind me of
something so similar to what is in SVG that it makes me wonder why not just
reference say http://www.w3.org/TR/SVG/filters.html ?

If indeed, my suspicion that 1 and 2, above, are not possible in current
approaches within SVG, then that would certainly justify a reopening and
augmentation of that treatment, but having come late to the discussions
about <canvas> and having not been involved in the discussions about the SVG
filters, I am a bit baffled by some of this. (Not that my state of
bafflement is anyone's problem but mine, mind you: I am well prepared to
deal with what I fear is a rather persistent condition.)

David


*taken from http://srufaculty.sru.edu/david.dailey/svg/svg_questions.htm
Bjoern Hoehrmann
2007-05-21 00:40:32 UTC
Permalink
Post by ddailey
http://srufaculty.sru.edu/david.dailey/svg/ComponentTransferComposite.svg.
It produces an image which looks like a spectrum-equalized version of the
negative.
Use color-interpolation-filters='sRGB'.
--
Björn Höhrmann · mailto:bjoern-d+***@public.gmane.org · http://bjoern.hoehrmann.de
Weinh. Str. 22 · Telefon: +49(0)621/4309674 · http://www.bjoernsworld.de
68309 Mannheim · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/
Philip Taylor
2007-05-21 01:10:01 UTC
Permalink
Post by ddailey
"...This is the cairo 'saturate' operator..."
The above reminded me: using SVG suggested to me a couple of questions*
1. Is there a way, using filters, to take an image A and produce its
photographic negative A', such that 255-C(A)=C(A') for each channel C in
{R,G,B)?
I would have thought that
<feComposite ... operator="arithmetic" k1="0" k2="0" k3="-1" k4="1"/>
would have done that, except it doesn't seem to work in Opera. But
<feColorMatrix type="matrix" in="SourceGraphic"
values="-1 0 0 0 1
0 -1 0 0 1
0 0 -1 0 1
0 0 0 1 0"/>
does seem to work and gives the negative effect, and is probably
easier to understand than the feComponentTransfer thing.

(As far as I can tell, there's no way at all to do this with <canvas>.
(...except for via getImageData --
http://canvex.lazyilluminati.com/misc/filter.html isn't too dreadfully
slow (if you ignore the fillRects bit), and would be faster if you're
only doing a 1x1 filter.))
Post by ddailey
2. On a related theme, <feBlend/> has modes including "screen", "multiply"
etc. Is there a way to create the "difference" between two images (as with
the Photoshop difference filter) in SVG?
I'd have thought something like <feComposite in="SourceGraphic"
in2="BackgroundImage" operator="arithmetic" k1="0" k2="-0.5" k3="0.5"
k4="0.5" result="comp"/> except negative values still don't seem to
work and I'm not sure why...

(Also not possible with <canvas>.)
Post by ddailey
Another question arises in my mind in this context: is there any reason that
any of the treatments of these effects (like the Porter-Duff operators,
darken, saturate etc.) should be any different than they are in the SVG
spec? So many things that I see in the treatment of canvas remind me of
something so similar to what is in SVG that it makes me wonder why not just
reference say http://www.w3.org/TR/SVG/filters.html ?
The P-D operators should be the same in <canvas> and SVG and the rest
of the world now, all just referring to the original paper. I don't
see anything specific in SVG (not counting the arithmetic/matrix
modes) that matches the <canvas> 'lighter' (P-D 'plus') or any
implementation's 'darker', though I've not looked hard.

The arithmetic/matrix SVG operators couldn't be done in <canvas>
without making a much more complex API, and its blending modes
(normal/multiply/screen/darken/lighten) seem like a fairly arbitrary
selection and wouldn't be particularly easy to implement -- the choice
of the current <canvas> operators matches what most standard graphic
libraries (Cairo, Quartz, Java 2D, etc) already provide, and it would
take more work to extend beyond their boundaries to match SVG, and
there doesn't seem to be much justification for doing that work, hence
the disparity with SVG.
Post by ddailey
David
--
Philip Taylor
excors-***@public.gmane.org
Ian Hickson
2008-01-16 02:48:39 UTC
Permalink
Post by ddailey
1. Is there a way, using filters, to take an image A and produce its
photographic negative A', such that 255-C(A)=C(A') for each channel C in
{R,G,B)? I've fooled a bit with the filter "feComponentTransfer" with
some hints of success, but it seems like so natural a thing that there
must be a straightforward way that I'm just missing. Here is an approach
that comes close using <feComponentTransfer> together with <feComposite
operator="arithmetic">.
http://srufaculty.sru.edu/david.dailey/svg/ComponentTransferComposite.svg.
It produces an image which looks like a spectrum-equalized version of
the negative.
There is no way to do this using <canvas> globalCompositeOperation (1-A is
not a PorterDuff operator), but you could do it manually using the
getImageData()/putImageData() methods.
Post by ddailey
2. On a related theme, <feBlend/> has modes including "screen",
"multiply" etc. Is there a way to create the "difference" between two
images (as with the Photoshop difference filter) in SVG? Here's the best
http://srufaculty.sru.edu/david.dailey/svg/ImageDiffer.svg . The ability
to calculate differences between images comes in quite handy, for
example, in explaining image compression to students, in scientific
image processing, and I believe in forensics as well.
You could use the <canvas> globalCompositeOperation "xor", does that do
what you want?
Post by ddailey
Another question arises in my mind in this context: is there any reason
that any of the treatments of these effects (like the Porter-Duff
operators, darken, saturate etc.) should be any different than they are
in the SVG spec? So many things that I see in the treatment of canvas
remind me of something so similar to what is in SVG that it makes me
wonder why not just reference say http://www.w3.org/TR/SVG/filters.html
?
The <canvas> globalCompositeOperation is just defined in terms of
PorterDuff, which far predates SVG.
Post by ddailey
If indeed, my suspicion that 1 and 2, above, are not possible in current
approaches within SVG, then that would certainly justify a reopening and
augmentation of that treatment, but having come late to the discussions
about <canvas> and having not been involved in the discussions about the
SVG filters, I am a bit baffled by some of this. (Not that my state of
bafflement is anyone's problem but mine, mind you: I am well prepared to
deal with what I fear is a rather persistent condition.)
A future version may introduce new operators, but at this point it's
probably best if we stabalise on the well-known PorterDuff operators.

Cheers,
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Krzysztof Żelechowski
2008-01-16 15:35:47 UTC
Permalink
Post by Ian Hickson
Post by ddailey
Another question arises in my mind in this context: is there any reason
that any of the treatments of these effects (like the Porter-Duff
operators, darken, saturate etc.) should be any different than they are
in the SVG spec? So many things that I see in the treatment of canvas
remind me of something so similar to what is in SVG that it makes me
wonder why not just reference say http://www.w3.org/TR/SVG/filters.html
?
The <canvas> globalCompositeOperation is just defined in terms of
PorterDuff, which far predates SVG.
And why does the canvas specification have to pretend that it predates
SVG too?

Chris
Ian Hickson
2008-01-16 21:41:01 UTC
Permalink
Post by Krzysztof Żelechowski
Post by Ian Hickson
Post by ddailey
Another question arises in my mind in this context: is there any reason
that any of the treatments of these effects (like the Porter-Duff
operators, darken, saturate etc.) should be any different than they are
in the SVG spec? So many things that I see in the treatment of canvas
remind me of something so similar to what is in SVG that it makes me
wonder why not just reference say http://www.w3.org/TR/SVG/filters.html
?
The <canvas> globalCompositeOperation is just defined in terms of
PorterDuff, which far predates SVG.
And why does the canvas specification have to pretend that it predates
SVG too?
It doesn't, particularly, but it seems simpler to reference the original
paper than to reference a document that itself references that paper.
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Philip Taylor
2007-05-21 01:22:03 UTC
Permalink
Post by L. David Baron
I thought what Safari does is basically "lighter", but with the
color components inverted (where inverting is CX = 1-CX, or cX = aX
aO = min(1, aA + aB)
cO = aO - min(1, aA - cA + aB - cB)
Aha, that does indeed match what Safari does exactly - I thought I'd
tried that before, but must have done something wrong.
Post by L. David Baron
Hrm. I thought lighter was confusingly *like* darker, in that they
act exactly the same when the alphas add to less than or equal to 1.
(I think that's true for plus (a.k.a. lighter), Cocoa's darker, and
for saturate.)
I thought it may be more common to have alpha=1 on source and
destination - then you get
red lighter green = yellow
red saturate green = green
red darker green = black
so they don't seem that much alike, except when looking in the range
where they do seem alike. But I have no idea how people tend to really
use these things in practice, and whether they give similar results in
those cases...
Post by L. David Baron
-David
--
Philip Taylor
excors-***@public.gmane.org
Ian Hickson
2007-05-20 19:35:51 UTC
Permalink
Post by Mathieu HENRI
Post by Ian Hickson
I've referenced the paper and dropped 'darker'.
Please, please, pretty please, bring 'darker' back.
Rename it 'multiply' if you want, or if like me you think this name better
reflects the operation previously known as 'darker'.
Here is a list of use cases for 'darker', I already applied in realtime
animations: [...]
The reason I dropped 'darker' wasn't lack of use cases, it was lack of a
formal definition and lack of hardware support. Can you point to a
definition of 'darker' that is defined to the level of the Porter-Duff
operators and has native support in the primary graphics libraries?
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Loading...