Results 1 to 7 of 7

Thread: VG_DRAW_IMAGE_STENCIL confusion, urgent.

  1. #1

    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);
    }

  2. #2
    Senior Member
    Join Date
    Feb 2006
    Posts
    115

    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

    Check your arithmetic

    (* edit I originally said 'green' for some reason - I don't know what I was thinking...)

  3. #3
    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

  4. #4
    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?

  5. #5

  6. #6

    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
    AmanithVG Lead Programmer

  7. #7
    *exactly* what I was looking for, thanks matteo.

Similar Threads

  1. Replies: 5
    Last Post: 03-11-2011, 06:12 AM
  2. [urgent] How to solve warning LNK4204??
    By lavendersg in forum OpenGL ES general technical discussions
    Replies: 4
    Last Post: 05-11-2005, 04:37 AM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •