Question with regards to componentAlpha

Thomas Roell <[email protected]> Fri, 13 Sep 2002 15:51:29 -0600 (MDT)
Newsgroups gmane.comp.xfree86.render
Message-ID <[email protected]>
While implementing the render-extension for our X-Server
(Accelerated-X), we ran across a very convoluted problem
with the specifiction:

Consider the PictOpOver operation, here with some excepts from the
spec:

(I)		dest = (source IN mask) OP dest

(II)		C = Ca * Fa + Cb * Fb

(III)		PictOp          Fa                      Fb
		Over            1                       1-Aa


If one expands this now to all color channels it reads like this
(componentAlpha not set):

(IV)	dest.b = (source.b IN mask.a) OVER dest.b
	dest.g = (source.g IN mask.a) OVER dest.g
	dest.r = (source.r IN mask.a) OVER dest.r
	dest.a = (source.a IN mask.a) OVER dest.a
	
	Or, using arithmetical terms:

	dest.b = (source.b * mask.a) * 1 + dest.b * (1 - source.a * mask.a)
	dest.g = (source.g * mask.a) * 1 + dest.g * (1 - source.a * mask.a)
	dest.r = (source.r * mask.a) * 1 + dest.r * (1 - source.a * mask.a)
	dest.a = (source.a * mask.a) * 1 + dest.a * (1 - source.a * mask.a)


Now if componentAlpha is turned on, the spec says this:

	Whether the mask has a single alpha value for all four channels or
	whether each mask channel should affect the associated source/dest
	channels. 


This could be read in two ways. First off, in the original formula
above that states how dest, source & mask are to be used, there are
brackets. Hence if mask has now components it should do this:

(V)	dest.b = (source.b IN mask.b) OVER dest.b
	dest.g = (source.g IN mask.g) OVER dest.g
	dest.r = (source.r IN mask.r) OVER dest.r
	dest.a = (source.a IN mask.a) OVER dest.a

	Or, using arithmetical terms:

	dest.b = (source.b * mask.b) * 1 + dest.b * (1 - source.a * mask.a)
	dest.g = (source.g * mask.g) * 1 + dest.g * (1 - source.a * mask.a)
	dest.r = (source.r * mask.r) * 1 + dest.r * (1 - source.a * mask.a)
	dest.a = (source.a * mask.a) * 1 + dest.a * (1 - source.a * mask.a)

The term (1 - source.a * mask.a) is used, as the specification
(II),(III) clearly says that one is supposed to use the alpha channel
or (source IN mask). On the other hand, the definition for
componentAlpha says:

	"whether each mask channel should affect the associated
	 source/dest channels"

Now this could mean that instead of the (1 - Aa) value we should use
(1 - Ca):


(VI)	dest.b = (source.b * mask.b) * 1 + dest.b * (1 - source.b * mask.b)
	dest.g = (source.g * mask.g) * 1 + dest.g * (1 - source.g * mask.g)
	dest.r = (source.r * mask.r) * 1 + dest.r * (1 - source.r * mask.r)
	dest.a = (source.a * mask.a) * 1 + dest.a * (1 - source.a * mask.a)


As things get very tricky already, the XFree4.2 code in fbpict.c offer
us yet another variant:

(VII)	dest.b = (source.b * mask.b) * 1 + dest.b * (1 - source.a * mask.b)
	dest.g = (source.g * mask.g) * 1 + dest.g * (1 - source.a * mask.g)
	dest.r = (source.r * mask.r) * 1 + dest.r * (1 - source.a * mask.r)
	dest.a = (source.a * mask.a) * 1 + dest.a * (1 - source.a * mask.a)	

This however contradicts the specification in applying (I). Even
worse, unless "source" or "mask" is a constant it is not implementable
in hardware reasonable (unless 2 passes are used).

The question is now which of the three formulas (V), (VI) or (VII) is
according to the spec and what the rationale is for which variant. 

- Thomas
-- 
	     Thomas Roell   /\	       Das Reh sprint hoch,
	     Xi Graphics   /  \/\ _     das Reh springt weit,
	 [email protected]	  /   /	 \ \	 was soll es tun,
			 / Oelch! \ \     es hat ja Zeit.