Undefined behaviour with filters?

Hi,

I've previously made a nuisance of myself on this list on the subject of
premultiplied vs non-premultiplied Alpha channels in filters. But please
bear with me nevertheless.

I beleive that I have found an instance where the SVG specification
(WD-SVG-19991203) does not define how a conformant viewer should behave -
in a place where such guidance is necessary.

I will present two examples which will hopefully help clarify what I mean.

Contemplate first this example:


---------------------------------------
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG December 1999//EN"
	"http://www.w3.org/Graphics/SVG/SVG-19991203.dtd">
<svg>
  <defs>
    <filter id="MakeLessTransparent">
      <feColorMatrix in="SourceGraphic"
         type="matrix"
         matrix="1 0 0 0 0    0 1 0 0 0    0 0 1 0 0    0 0 0 0.5 0.5"
         result="lessTransparent" />
      <feMerge>
        <feMergeNode in="lessTransparent" />
      </feMerge>
    </filter>
  </defs>


  <desc>Example undefinedBehaviour01 - what happens to the previously
        transparent stuff which is now partially opaque?</desc>

  <text style="font-size:36px; fill:red; filter:url(#MakeLessTransparent)"
		x="10px" y="70px">Undefined Behaviour</text>

</svg>
--------------------------------------


The 'text' element draws the string 'Undefined behaviour' onto an empty,
clear background, with some level of anti-aliasing. Thus we end up with an
array of pixels containing Alpha values from 0 (untouuched) to 1 (where
the text is displayed), possibly with some pixels having values in
between (where anti-aliasing has taken place).

Now, the 'MakeLessTransparent' filter quite simply and legally applies a 
'feColorMatrix' transformation to the pixels, leacing their R G and B
values intact, but changin the Alpha values: A' = 0.5*A + 0.5; basically
making all pixels at least somewhat (50%) opaque.

So, what colour will that part of the image be which was left untouched by
the drawing of the string itself?



Now consider a second example. For this we will need an external image,
'hideAndSeek.png', a PNG image which has some pixels which are transparent
and some which are opaque. However, as PNG stores RGBA in a
non-premultiplied format, this permits that there be actual useful data
stored in the RGB parts even of pixels whose A=0.

Now sau that we want to use the 'MakeLessTransparent' filter from the
previous example on this PNG image, thus revealing some of what is
'hidden' in the image:


----------------------------------------
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG December 1999//EN"
	"http://www.w3.org/Graphics/SVG/SVG-19991203.dtd">
<svg>
  <defs>
    <filter id="MakeLessTransparent">
      <feColorMatrix in="SourceGraphic"
         type="matrix"
         matrix="1 0 0 0 0    0 1 0 0 0    0 0 1 0 0    0 0 0 0.5 0.5"
         result="lessTransparent" />
      <feMerge>
        <feMergeNode in="lessTransparent" />
      </feMerge>
    </filter>
  </defs>

  <desc>Example undefinedBehaviour02 - will those pixels which are at
alpha=0, but (r,g,b)!=(0,0,0) show their original colour when their alpha
is changed from 0 to non-zero?</desc>

  <image xlink:href="hideAndSeek.png" 
         style="filter:url(#MakeLessTransparent)">
  </image>

</svg>
----------------------------------------


Now, to quote from the spec:

-----
15.8 Filter Processing Nodes

[...] Unless otherwise stated, all image filters operate on linear
premultiplied RGBA samples. Filters which work more naturally on non
premultiplied data (feColorMatrix and feComponentTransfer) will
temporarily undo and redo premultiplication as specified. [...]
----


According to this, conceptually, the source image data will arrive at the
feColorMatrix node in premultiplied form, get unpremultiplied, then pass
through the node, and get repremultiplied.

But: In the process of premultiplying the RGBA data from the PNG image,
for all pixels with A=0, the resulting R G and B will also become = 0,
regardless of their original colour in the PNG file; so, the only thing
that will be 'revealed' by the feColorMatrix node (and this the whole
MakeLessTransparent filter) will be a homogenous black area wherever the
PNG image had an Alpha value of exactly 0.



Now, problem 2 above arises from the fact that, even if premultiplied
samples are stored with sufficient accuracy to un-premultiply successfully
for any non-zero Alpha value, multiplication of any R G or B value with an
Alpha of 0 will result in a new R G or B value of 0 as well - from which
no non-premultiplied color can be retrieved, no matter how hard one tries.

This can be 'solved' by acknowledging the data loss in this situation, but
I would consider that unsatisfactory; the only 'complete' solution to this
that I see is to store the non-premultiplied values alongside the
premultiplied ones as the image passes through the filter nodes, but
perhaps there is a better one?


The first problem of course can be solved by defining default R G and B
values for 'untouched' pixels - basically, explicitly stating the full
RGBA value which is assigned to the 'background' into which a
to-be-filtered element gets painted, rather than (as the case seems to be
now) stating only that its Alpha value is 0. 

Comments? Am I making any sense at all?

Best regards

// Christian Brunschen

Received on Wednesday, 2 February 2000 06:28:16 UTC