<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=Content-Type content="text/html; charset=us-ascii">
<META content="MSHTML 6.00.2900.3698" name=GENERATOR></HEAD>
<BODY>
<DIV dir=ltr align=left><SPAN class=848101022-04012011><FONT face=Arial
color=#0000ff size=2>If Microsoft, Opera, and Mozilla all subscribed to the
current version of the spec, it looks like WebKit is the only hold out.
</FONT></SPAN></DIV>
<DIV dir=ltr align=left><SPAN class=848101022-04012011><FONT face=Arial
color=#0000ff size=2>In the discussions that I followed, I noticed no strong
arguments, other then my own (concerning resource consumption, which Robert
O'Callahan aparently rejected as invalid) in favor of changing the specification
such that only covered pixels are affected by compositing. </FONT></SPAN></DIV>
<DIV dir=ltr align=left><SPAN class=848101022-04012011><FONT face=Arial>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><FONT color=#0000ff><FONT size=2><SPAN class=118300323-03012011><SPAN
class=848101022-04012011>Given the statements above I no longer think that
changing the spec in this regard is a good thing, but I still believe that the
disappearance of shadows for the source-in and copy modes and the strange result
when shadows are drawn and the composite operation is source-out should be
corrected. To do this </SPAN></SPAN><SPAN class=118300323-03012011>I
suggest<SPAN class=848101022-04012011>ed the following in my previous e-mail,
but I got no comments about my suggestion so I repeat it here (please excuse my
insistence):</SPAN></SPAN></FONT></FONT></DIV>
<DIV><FONT color=#0000ff><FONT size=2><SPAN class=118300323-03012011><SPAN
class=848101022-04012011></SPAN></SPAN></FONT></FONT> </DIV>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff><FONT size=2><SPAN
class=848101022-04012011>Replace </SPAN>steps 3 to 6 of the drawing
model, <SPAN
class=848101022-04012011>with</SPAN>:</FONT></FONT></SPAN></DIV>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><FONT color=#0000ff><FONT size=2><SPAN class=118300323-03012011>3.
</SPAN><SPAN class=118300323-03012011><A
title=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#when-shadows-are-drawn
href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#when-shadows-are-drawn">When
shadows are drawn</A>, composite <VAR title=""><SPAN
class=848101022-04012011>B (source)</SPAN> <SPAN
class=848101022-04012011>with A (destination) using </SPAN><SPAN
class=848101022-04012011>destination-over operation</SPAN></VAR>.
</SPAN></FONT></FONT></DIV>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff size=2>4. Multiply the
alpha component of every pixel in <VAR title="">A</VAR> by <CODE
title=dom-context-2d-globalAlpha><A
title=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalalpha
href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-globalalpha">globalAlpha</A></CODE>.</FONT></SPAN></DIV>
<DIV><SPAN class=118300323-03012011><FONT face=Arial color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><SPAN class=118300323-03012011><FONT color=#0000ff size=2>5. Composite <VAR
title="">A</VAR> within the </FONT><A
title=http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#clipping-region
href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#clipping-region"><FONT
size=2>clipping region</FONT></A><FONT color=#0000ff size=2> over the current
canvas bitmap using the current composition operator.
</FONT></DIV></SPAN></FONT></SPAN></DIV>
<DIV><FONT face=Arial color=#0000ff size=2></FONT> </DIV>
<DIV><FONT face=Arial color=#0000ff size=2><SPAN class=848101022-04012011>Please
consider the proposal above.</SPAN></FONT></DIV>
<DIV><FONT face=Arial color=#0000ff size=2><SPAN
class=848101022-04012011></SPAN></FONT> </DIV>
<DIV><SPAN class=848101022-04012011><FONT face=Arial color=#0000ff
size=2>Thanks to everyone for considering my
thoughts,</FONT></SPAN></DIV>
<DIV><SPAN class=848101022-04012011><FONT face=Arial color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><SPAN class=848101022-04012011><FONT face=Arial color=#0000ff size=2>Carol
Szabo</FONT></SPAN></DIV>
<DIV><BR></DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: #0000ff 2px solid; MARGIN-RIGHT: 0px">
<DIV class=OutlookMessageHeader lang=en-us dir=ltr align=left>
<HR tabIndex=-1>
<FONT face=Tahoma size=2><B>From:</B> rocallahan@gmail.com
[mailto:rocallahan@gmail.com] <B>On Behalf Of </B>ext Robert
O'Callahan<BR><B>Sent:</B> Tuesday, January 04, 2011 4:40 PM<BR><B>To:</B>
Szabo Carol (Nokia-MS/Boston)<BR><B>Cc:</B> chuck@jumis.com;
whatwg@whatwg.org<BR><B>Subject:</B> Re: [whatwg] Fwd: RE: Inconsistent
behaviour of globalCompositeOperation property - Drawing model
discussion<BR></FONT><BR></DIV>
<DIV></DIV>On Wed, Jan 5, 2011 at 8:12 AM, <SPAN dir=ltr><<A
href="mailto:carol.szabo@nokia.com">carol.szabo@nokia.com</A>></SPAN>
wrote:<BR>
<DIV class=gmail_quote>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid">
<DIV>
<DIV dir=ltr align=left><SPAN><FONT face=Arial color=#0000ff size=2>Please
see my in-line comments below:</FONT></SPAN></DIV><BR>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: #0000ff 2px solid; MARGIN-RIGHT: 0px">
<DIV lang=en-us dir=ltr align=left>
<HR>
Version 1:</DIV>
<DIV class=gmail_quote>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV text="#000000" bgcolor="#ffffff">
<DIV dir=ltr align=left><SPAN><SPAN lang=EN>
<P>4.8.11.1.13 Drawing model</P>
<P><FONT face=Arial color=#0000ff size=2></FONT> </P>
<P>When a shape or image is painted, user agents must follow these
steps, in the order given (or act as if they do):</P>
<P>1. Render the shape or image onto an infinite transparent black
bitmap, creating image M1, as described in the previous sections except
that for the purpose of this step every pixel of the image will be
considered to be fully opaque white and the current fillStyle will be
considered to be solid fully opaque white and the strokeStyle will be
considered fullyOpaque white as well
<BR></P></SPAN></SPAN></DIV></DIV></BLOCKQUOTE>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV text="#000000" bgcolor="#ffffff">
<DIV dir=ltr align=left><SPAN><SPAN lang=EN>
<P>2. When shadows are drawn, render the shadow from image M1, using a
fully opaque white shadow color as described in the shadows section,
creating image M2. </P></SPAN></SPAN></DIV></DIV></BLOCKQUOTE>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV text="#000000" bgcolor="#ffffff">
<DIV dir=ltr align=left><SPAN><SPAN lang=EN>
<P>3. Let C1 be a region obtained by intersecting the canvas's cliping
region with the set of pixels in the canvas that correspond to pixels in
M1 (by having the same coordinates) that are not fully transparent.</P>
<P>4. Let C2 be a region obtained by intersecting the canvas's cliping
region with the set of pixels in the canvas that correspond to pixels in
M2 (by having the same coordinates) that are not fully
transparent.</P></SPAN></SPAN></DIV></DIV></BLOCKQUOTE>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV text="#000000" bgcolor="#ffffff">
<DIV dir=ltr align=left><SPAN><SPAN lang=EN>
<P>5. Render the shape or image onto an infinite transparent black
bitmap, creating image A, as described in the previous sections. For
shapes, the current fill, stroke, and line styles must be honored, and
the stroke must itself also be subjected to the current transformation
matrix.</P>
<P>6. When shadows are drawn, render the shadow from image A, using the
current shadow styles, creating image B.</P>
<P>7. When shadows are drawn, multiply the alpha component of every
pixel in B by globalAlpha.</P>
<P>8. When shadows are drawn, composite B with the current canvas
bitmap, cliping results to region C2 defined above, using the current
composition operator.</P>
<P>9. Multiply the alpha component of every pixel in A by
globalAlpha.</P>
<P>10. Composite A with the current canvas bitmap, cliping results to
region C1 defined above, using the current composition operator.
</P></SPAN></SPAN></DIV></DIV></BLOCKQUOTE><FONT face=Arial color=#0000ff
size=2></FONT>
<DIV><FONT face=Arial color=#0000ff size=2></FONT><BR>Making a binary
fully-transparent/not-fully-transparent per-pixel decision to create
regions C1 and C2 seems like it can't be right in the presence of
antialiasing.<BR><BR>Suppose we have a path filled with black and operator
"copy". Any pixel on the edge of that path that gets any nonzero coverage
value from antialiasing will end up solid black with this proposal. That's
going to look very ugly. We'll want a solution where any canvas pixel
which has a very small amount of coverage by the path will be mostly
unchanged in the final result.<BR><SPAN><FONT face=Arial color=#0000ff
size=2> </FONT></SPAN></DIV>
<DIV><SPAN>
<DIV dir=ltr align=left><SPAN><FONT face=Arial color=#0000ff size=2>I do
not understand why pixels touched by antialiasing are going to be solid
black.</FONT></SPAN></DIV></SPAN></DIV></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE>
<DIV>Yes, I made a mistake. The actual result will be mostly-transparent
black, but that is equally unacceptable.<BR><BR>In step 1, every pixel which
is very slightly covered by the path will be filled with mostly-transparent
white.<BR>In step 3, all such pixels will be added to C1.<BR>In step 5, those
pixels will be set to mostly-transparent black in image A.<BR>In step 10, for
those pixels we'll composite mostly-transparent black onto the canvas with
operator "copy", setting the canvas pixels to mostly-transparent
black.<BR><BR>The core problem is steps 3 and 4. Making a binary decision for
each pixel whether it's "in" or "out" of the shape simply can't work
well when coverage-based antialiasing is being used.<BR><BR>If you
generalize C1 and C2 to be alpha masks, or rephrase the approach so that it
permits such generalization, then this could work. But you have to be careful
about how you use partial alpha values in step 10.<BR><BR></DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: rgb(0,0,255) 2px solid; MARGIN-RIGHT: 0px">
<DIV class=gmail_quote>
<DIV><SPAN>
<DIV dir=ltr align=left><SPAN><FONT face=Arial color=#0000ff size=2>The
way I understand antialiasing (and maybe I am wrong), pixels that are
partly touched retained partly their old color and transparency and get
parly the new color and transparency. More precisely the
resulting transparency and color components an average of the color
component being painted and the previous color component weighted by the
coverage fraction of the pixel. Hence partially covered pixels are
partially transparent, thus the background behind the canvas should shine
through and the partially covered pixels won't be entirely black unless
that background is black as
well.</FONT></SPAN></DIV></SPAN></DIV></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE>
<DIV><BR>That's right, but your proposal interacts with this
process.<BR><BR></DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: rgb(0,0,255) 2px solid; MARGIN-RIGHT: 0px">
<DIV class=gmail_quote>
<DIV><SPAN>
<DIV dir=ltr align=left><SPAN><FONT face=Arial color=#0000ff size=2>I
agree with you though that there are cases when inappropriately using
globalCompositeOperation can yield ugly and perhaps surprising results,
such as in the case you described if the canvas is completely red
before the operation and it is put on a page that has green background,
thus the shape will acquire an unexpected slightly green rim between
black and
red.</FONT></SPAN></DIV></SPAN></DIV></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE>
<DIV><BR>Yes, it's easy to produce ugliness. With great power comes great
responsibility :-).<BR></DIV>
<DIV><BR></DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: rgb(0,0,255) 2px solid; MARGIN-RIGHT: 0px">
<DIV class=gmail_quote>
<DIV><SPAN></SPAN><BR> </DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV text="#000000" bgcolor="#ffffff">
<DIV dir=ltr align=left>Version 2:<SPAN><SPAN lang=EN>
<P><FONT face=Arial color=#0000ff size=2></FONT> </P>
<P>4.8.11.1.13 Drawing model</P>
<P><FONT face=Arial color=#0000ff size=2></FONT> </P>
<P>When a shape or image is painted, user agents must follow these
steps, in the order given (or act as if they do):</P>
<P>1. Render the shape or image onto an infinite transparent black
bitmap, creating image A, as described in the previous sections. For
shapes, the current fill, stroke, and line styles must be honored, and
the stroke must itself also be subjected to the current transformation
matrix.</P>
<P>2. When shadows are drawn, render the shadow from image A, using the
current shadow styles, creating image B.</P>
<P>3. When shadows are drawn, multiply the alpha component of every
pixel in B by globalAlpha.</P>
<P>4. When shadows are drawn, composite, using the current composition
operator, B with the current canvas bitmap, cliping results to the
cliping region of the canvas and to the pixels that would have taken the
shadow's color if the composition operator would have been source-over
and the shadow would have been fully opaque and the globalAlpha would
have been 1.</P>
<P>5. Multiply the alpha component of every pixel in A by
globalAlpha.</P>
<P>6. Composite, using the current composition operator, A with the
current canvas bitmap, cliping results to the cliping region of the
canvas and to the pixels that would have taken the shape's or image's
pixel color if the composition operator would have been source-over and
the image would have been fully opaque, the fillStyle and strokeStyle
would have been a solid fully opaque color, and the globalAlpha would
have been 1</P></SPAN></SPAN></DIV></DIV></BLOCKQUOTE><FONT face=Arial
color=#0000ff size=2></FONT>
<DIV><FONT face=Arial color=#0000ff size=2></FONT><BR>Again, this needs to
be modified to take into account the possibility that some pixels are
partially covered.<SPAN><FONT face=Arial color=#0000ff
size=2> </FONT></SPAN></DIV>
<DIV><SPAN><FONT face=Arial color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><SPAN><FONT face=Arial color=#0000ff size=2>Again, as above,
Porter-Duff does not allow for partially applying the composition
operator. For every pair of source and target pixels, the operator is
applied.</FONT></SPAN></DIV></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE>
<DIV><BR>Right. In practice implementations go beyond Porter-Duff to take some
kind of "mask alpha" into account to support coverage-based
antialiasing.<BR> </DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: rgb(0,0,255) 2px solid; MARGIN-RIGHT: 0px">
<DIV class=gmail_quote><SPAN><FONT face=Arial color=#0000ff size=2>I
myself have no particular quarel with the current spec language other than
the fact that it looses shadows and that it is very expensive to implement
with graphics primitives that I know of, due to the need to alocate large
intermediary images to be composited later, rather then drawing directly
on the target bitmap using an appropriate pen and
compositor.</FONT></SPAN></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE>
<DIV><BR>This depends on what your underlying implementation can do and what
the workload is. In practice usage of operators other than "over" is quite
rare. Copying one canvas to another is probably the main use. You can optimize
the case where the source fills the destination clip region --- in those
cases, which I think are most cases in practice, of course it doesn't matter
whether the operation is bounded by the source shape or not.<BR><BR></DIV>
<BLOCKQUOTE class=gmail_quote
style="PADDING-LEFT: 1ex; MARGIN: 0pt 0pt 0pt 0.8ex; BORDER-LEFT: rgb(204,204,204) 1px solid">
<DIV>
<BLOCKQUOTE
style="PADDING-LEFT: 5px; MARGIN-LEFT: 5px; BORDER-LEFT: rgb(0,0,255) 2px solid; MARGIN-RIGHT: 0px">Given
that Microsoft have indicated they're happy with the current spec, and are
presumably implementing it, I think we should get their explicit approval
before we change the spec here. I'm still happy with either the current
spec or your proposed change (after the issues have been addressed).<BR>
<DIV><SPAN><FONT face=Arial color=#0000ff
size=2></FONT></SPAN> </DIV>
<DIV><SPAN><FONT face=Arial color=#0000ff size=2>My understanding from
some older thread was that Microsoft favored the approach where only
pixels covered by the shape are composited, but that information may be
outdated.</FONT></SPAN></DIV></BLOCKQUOTE></DIV></BLOCKQUOTE><BR></DIV>Yes,
they seem to have changed their minds.<BR clear=all><BR>Rob<BR>-- <BR>"Now the
Bereans were of more noble character than the Thessalonians, for they received
the message with great eagerness and examined the Scriptures every day to see
if what Paul said was true." [Acts 17:11]<BR></BLOCKQUOTE></BODY></HTML>