# VG_DRAW_IMAGE_STENCIL confusion, urgent.

• 06-13-2006, 08:31 AM
Nils Pipenbrinck
VG_DRAW_IMAGE_STENCIL confusion, urgent.
Oh well. I tried to implement the stencil draw mode, but I have lots of troubles to get anything usefull output. What I get (my implementation), and what I get when I run test codes on the reference implementation differ a lot!

These are some tests, and I was able to break down the problem to a single pixel blend. Here are some colors:

[0.2, 0.4, 0.6, 1.0] -> Dest-Color (some nice blue)
[0.0, 1.0, 0.0, 1.0] -> Image-Color (Pure Green)
[0.0, 0.0, 0.0, 1.0] -> Paint Color (e.g. Black)

Note: All alpha values are 1, so it does not matter if we're talking about premultiplied colors or not (at least for now).

Now I take the equation from chapter 10.8 (the one that shows the stencil mode in combination with SRC_OVER). I leave out the division by AlphaTemp. it's 1 in any case, and the equations are already confusing enough.

For the case of a single color channel (in this case green):

Gdst = ((Aimg * Apaint * Gimg * Gpaint) + Adst * Gdst * (1- Aimg * Apaint * Gimg).

I plug in the constants (e.g. anything that does not change per channel, like dest-alpha and Paint Alpha ect:

Gdst = ((1 * 1 * Gimg * Gpaint) + 1 * Gdst * (1- 1*1* Gimg).

Simplify:

Gdst = ((Gimg * Gpaint) + Gdst * (1- Gimg).

So it will do simple linear interpolation between Paint-color and Dest-color based on the image color channel, at least for this simplified case, where all Alpha values are one.

If I apply this equations to the test colors I've listed above I ought to get:

[0, 0.4, 0, 1.0] -> Output-Color (some medium green)

However, the reference implementation gives me this color:

[0.2, 0.13, 0.6, 1.0]. That's a totally different result. Not even close..

Could anyone please help me and give a detailes answer what the stencil draw mode is supposed to do? For the reference, I also attached the test code I've used:

Code :

```void printcolor (char * name, unsigned char * buffer) { float r = buffer[3] / 255.0f; float g = buffer[2] / 255.0f; float b = buffer[1] / 255.0f; float a = buffer[0] / 255.0f; printf ("%s [%f, %f, %f, %f]\n", name, r,g,b,a); }     void stenciltest2 (void) { unsigned char buffer[4]; // to read back colors:   VGuint testdata[] = { 0x00ff00ff // Full alpha Green };   VGfloat clearColor[4] = {0.2,0.4,0.6,1};   // Create the Image: VGImage stencil = vgCreateImage (VG_sRGBA_8888, 1,1, VG_IMAGE_QUALITY_NONANTIALIASED); vgImageSubData (stencil, testdata, 1, VG_sRGBA_8888, 0,0,1,1);   // Create the Paint: VGImage paint = vgCreatePaint (); vgSetPaint (paint, VG_FILL_PATH); vgSetColor (paint, 0x000000ff); // Full alpha black:   // set a known destination color: vgSetfv (VG_CLEAR_COLOR, 4, clearColor); vgClear (0,0,10,10);   vgReadPixels (buffer, 4, VG_sRGBA_8888_PRE, 0, 0, 1, 1); printcolor ("Destination = ", buffer);   vgSeti (VG_IMAGE_MODE, VG_DRAW_IMAGE_STENCIL); vgSeti (VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE); vgLoadIdentity(); vgScale (10,10); vgSeti (VG_BLEND_MODE, VG_BLEND_SRC_OVER); vgDrawImage (stencil);   vgReadPixels (buffer, 4, VG_sRGBA_8888_PRE, 0, 0, 1, 1); printcolor ("Output = ", buffer);   vgDestroyImage (stencil); vgDestroyPaint (paint); }```
• 06-13-2006, 09:55 AM
Ivo Moravec
Re: VG_DRAW_IMAGE_STENCIL confusion, urgent.
Quote:

Originally Posted by Nils Pipenbrinck
Oh well. I tried to implement the stencil draw mode, but I have lots of troubles to get anything usefull output. What I get (my implementation), and what I get when I run test codes on the reference implementation differ a lot!

These are some tests, and I was able to break down the problem to a single pixel blend. Here are some colors:

[0.2, 0.4, 0.6, 1.0] -> Dest-Color (some nice blue)
[0.0, 1.0, 0.0, 1.0] -> Image-Color (Pure Green)
[0.0, 0.0, 0.0, 1.0] -> Paint Color (e.g. Black)

Note: All alpha values are 1, so it does not matter if we're talking about premultiplied colors or not (at least for now).

Now I take the equation from chapter 10.8 (the one that shows the stencil mode in combination with SRC_OVER). I leave out the division by AlphaTemp. it's 1 in any case, and the equations are already confusing enough.

For the case of a single color channel (in this case green):

Gdst = ((Aimg * Apaint * Gimg * Gpaint) + Adst * Gdst * (1- Aimg * Apaint * Gimg).

I plug in the constants (e.g. anything that does not change per channel, like dest-alpha and Paint Alpha ect:

Gdst = ((1 * 1 * Gimg * Gpaint) + 1 * Gdst * (1- 1*1* Gimg).

Simplify:

Gdst = ((Gimg * Gpaint) + Gdst * (1- Gimg).

So it will do simple linear interpolation between Paint-color and Dest-color based on the image color channel, at least for this simplified case, where all Alpha values are one.

If I apply this equations to the test colors I've listed above I ought to get:

[0, 0.4, 0, 1.0] -> Output-Color (some medium green)

However, the reference implementation gives me this color:

[0.2, 0.13, 0.6, 1.0]. That's a totally different result. Not even close..

Could anyone please help me and give a detailes answer what the stencil draw mode is supposed to do? For the reference, I also attached the test code I've used:

Judging from just the Red* value, the reference implementation and your equation simplification look correct.

[0.2, 0.4, 0.6, 1.0] -> Dest-Color (some nice blue)
[0.0, 1.0, 0.0, 1.0] -> Image-Color (Pure Green)
[0.0, 0.0, 0.0, 1.0] -> Paint Color (e.g. Black)

Gdst = ((Gimg * Gpaint) + Gdst * (1- Gimg).

Gives a red* of:

Gdst = ((0 * 0) + 0.2*(1 - 0) = 0.2

(* edit I originally said 'green' for some reason - I don't know what I was thinking...)
• 06-13-2006, 03:33 PM
Nils Pipenbrinck
Hi Ivo,

Yes, I messed up something. Guess I worked, thought and tried to long on the same problem. :P

I made a little test program that just blends the colors and prints them out. Straight forward, without any simplifications.

Code :

```float testblend (float Aimg, float Apaint, float Adst, float Cimg, float Cpaint, float Cdst) { float Atmp = (Aimg * Apaint + Adst*(1.0f-Aimg*Apaint)); float c = (Aimg * Apaint * Cimg * Cpaint) + Adst * Cdst * (1.0f - Aimg * Apaint * Cimg); return c /Atmp; }   void main (void) { float r = testblend (1, 1, 1, 0, 0, 0.2); float g = testblend (1, 1, 1, 1, 0, 0.4); float b = testblend (1, 1, 1, 0, 0, 0.6); printf ("r=%f g=%f b=%f\n", r,g,b); }```

That gives, with my test colors:

r = 0.2 g = 0 b = 0.6

(as expected.. the image color becomes source alpha per channel, and the blend equation just lerps between paint-color and dest-color)

The reference implementation still gives:

r = 0.2 g = 0.13 b = 0.6

That is is way off, and I have no idea how they derive their 0.13 value...

So, anyone has an idea if the reference implementation is simply broken, or if I still don't got the equations?

Nils
• 06-13-2006, 04:05 PM
Nils Pipenbrinck
I'm still thinking:

I took the original equations, assumed all variables are assumed to be in non premultiplied format and restated the equations with premultiplied input and output.

Equations from the spec:

tmp = (Aimg * Apaint + Adst*(1.0f-Aimg*Apaint))
Cout = ((Aimg * Apaint * Cimg * Cpaint) + Adst * Cdst * (1.0f - Aimg * Apaint * Cimg)) / tmp
Aout = tmp

-> with premultiplied output: division is obsolete now:

Cout = ((Aimg * Apaint * Cimg * Cpaint) + Adst * Cdst * (1.0f - Aimg * Apaint * Cimg))

-> with premultiplied paint: Cpaint = Cpaint * Apaint

Cout = ((Aimg * Cimg * Cpaint) + Adst * Cdst * (1.0f - Aimg * Apaint * Cimg))

-> with premultiplied Destination: Cdst = Cdst * Adst

Cout = ((Aimg * Cimg * Cpaint) + Cdst * (1.0f - Aimg * Apaint * Cimg))

-> with premultiplied Image Cimg = Cimg * Aimg

This gives the really nice equations (all inputs an output are now premultiplied):

Cout = Cimg * Cpaint + Cdst * (1.0f - Apaint * Cimg)
Aout = Aimg * Apaint + Adst * (1.0f - Apaint * Aimg)

The equations look almost like the SRC_OVER Porter-Duff blending. Just the AlphaSrc used in the Porter-Duff equations is multiplied with the corresponding image color channel. This even makes sense! The image becomes a per channel alpha source that is just multiplied into the equations.

Is it really *that* simple?
• 06-15-2006, 03:28 AM
Nils Pipenbrinck
Noone?
• 07-17-2006, 08:38 AM
muratmat
Some clarfication about stencil image mode.
Public specifications are not so clear, as in other cases...anyway i'll try to write down a simple example. Lets say to have:

- Drawing surface in premultiplied non-linear color space, cleared with rs = 0.2, gs = 0.2, bs = 0.2, as = 0.6
- A paint color in unpremultiplied non-linear color space of rp = 0.1, gp = 0.4, bp = 0.3, ap = 0.5
- An image pixel in unpremultiplied non-linear color space of ri = 0.4, gi = 0.3, bi = 0.1, ai = 0.5
- A source over blend mode, expressed in SourceOver(SrcColorPremultiplied, SrcAlpha, DstColorPremultiplied, DstAlpha)

Now, if you draw the image in stencil mode, the resulting color will be:

rs' = SourceOver(rp * ap * ri * ai, ap * ri * ai, rs, as)
gs' = SourceOver(gp * ap * gi * ai, ap * gi * ai, gs, as)
bs' = SourceOver(bp * ap * bi * ai, ap * bi * ai, bs, as)
as' = SourceOver(ai * ap, as)

rs' = SourceOver(0.1 * 0.5 * 0.4 * 0.5, 0.5 * 0.4 * 0.5, 0.2, 0.6)
gs' = SourceOver(0.4 * 0.5 * 0.3 * 0.5, 0.5 * 0.3 * 0.5, 0.2, 0.6)
bs' = SourceOver(0.3 * 0.5 * 0.1 * 0.5, 0.5 * 0.1 * 0.5, 0.2, 0.6)
as' = SourceOver(0.5 * 0.5, 0.6)

rs' = SourceOver(0.01, 0.1, 0.2, 0.6)
gs' = SourceOver(0.03, 0.075, 0.2, 0.6)
bs' = SourceOver(0.0075, 0.025, 0.2, 0.6)
as' = SourceOver(0.25, 0.6)

Applying source over equations:

rs' = 0.01 + 0.2 * (1 - 0.1) = 0.19
gs' = 0.03 + 0.2 * (1 - 0.075) = 0.215
bs' = 0.0075 + 0.2 * (1 - 0.025) = 0.2025
as' = 0.25 + 0.6 * (1 - 0.25) = 0.7

I wish this example could be of help.
Regards,
Matteo - AmanithVG Team
• 07-17-2006, 11:16 AM
Nils Pipenbrinck
*exactly* what I was looking for, thanks matteo.