Hello Bill and others,

Thank you very much for this detailed explanation.
It didn't clear all my doubts at the beginning but I've read some SDL source code and here is what I think now:

1. It is important for R, G & B masks in a newly created surface to match those from a surface we want to blit into
(i.e.  "screen"  in both tuxmath and tuxpaint), otherwise the colors will be swapped.

2. If we create a surface with an alpha channel (and set its mask) it is used while blitting and we enjoy transparency (provided that R,G & B mask match). (as written here: http://sdl.beuc.net/sdl.wiki/SDL_BlitSurface  and here:  http://sdl.beuc.net/sdl.wiki/SDL_SetAlpha ). Blitting doesn't care about amask in destination surface.

3. SDL_SetVideoMode (in our case) constructs a surface with default (= 0) values for RGB masks, which, when passed to SDL_AllocFormat results in RGBA masks = {0x00ff0000; 0x0000ff00; 0x000000ff; 0}   (RGB masks are shifted to the right as if it were 24 bpp)

Here is a piece of code from SDL_AllocFormat:

if ( Rmask || Bmask || Gmask ) { /* Packed pixels with custom mask */
} else if ( bpp > 8 ) {   /* Packed pixels with standard mask */
    /* R-G-B */
    if ( bpp > 24 )
      bpp = 24;
    format->Rloss = 8-(bpp/3);
    format->Gloss = 8-(bpp/3)-(bpp%3);
    format->Bloss = 8-(bpp/3);
    format->Rshift = ((bpp/3)+(bpp%3))+(bpp/3);
    format->Gshift = (bpp/3);
    format->Bshift = 0;
    format->Rmask = ((0xFF>>format->Rloss)<<format->Rshift);
    format->Gmask = ((0xFF>>format->Gloss)<<format->Gshift);
    format->Bmask = ((0xFF>>format->Bloss)<<format->Bshift);

which is a complicated way of writing:

    format->Rmask = 0x00FF0000;
    format->Gmask = 0x0000FF00;
    format->Bmask = 0x000000FF;
(Amask = 0 here)

So, TuxPaint's hard-coded masks are okay :)

If what I've written above is true, we can run into problems when SDL_SetVideoMode returns a surface with different masks (If some specific flag combination is used).
How about changing those four hard-coded values into:

  Rmask = screen->format->Rmask;
  Gmask = screen->format->Gmask;
  Bmask = screen->format->Bmask;
  if(screen->format->Amask == 0)
    /* find a free byte to use for Amask */
    Amask = ~(Rmask | Gmask | Bmask);
    Amask = screen->format->Amask;

It would probably be safer and clear for people reading code as well. (and it works in tuxmath)

What do you think? Does it make sense?


2009/6/10 Bill Kendrick <nbs@sonic.net>

On Mon, May 18, 2009 at 10:41:08PM +0200, Boles??aw Kulbabi??ski wrote:
>    Hello,
>    I'm implementing SVG support for TuxMath and TuxType as a GSOC project.
>    I would like to base my SVG loading code on what is already implemented in
>    TuxPaint and I have a question about the color masks that are being set in
>    load_svg() function in tuxpaint.c
>    Where do these values:
>    A  rmask = 0x00ff0000;
>    A  gmask = 0x0000ff00;
>    A  bmask = 0x000000ff;
>    A  amask = 0xff000000;
>    come from ? I tried to use SDL_CreateRGBSurface with different ones
>    depending on SDL_BYTEORDER but the svg wasn't displayed correctly. Are
>    values used in tuxpaint some cairo- or librsvg-related constants ? If os,
>    are they independent on endianness ?

Sorry for not responding sooner.  A few notes:

* our drawing surface is created as:
 canvas = SDL_CreateRGBSurface(screen->flags,
                               WINDOW_WIDTH - (96 * 2),
                               (48 * 7) + 40 + HEIGHTOFFSET,
                               screen->format->Bmask, 0);
 (where 'screen' is our display surface, i.e. the window or fullscreen
 surface returned to us when we called SDL_SetVideoMode())

* the temporary surface we create in 'load_svg()' is created using
 a set of R/G/B/A masks that match the image date Cairo gives us

 1. we call 'cairo_image_surface_create_for_data()' and send it the
    variable 'image', which is a big buffer of memory (unsigned char * that
    we then calloc())

 2. we tell rsvg and cairo to render the SVG; this happens into the
    buffer we gave it ('image')

 3. we set up the mask as follows; notice the comments:
   /* Adjust the SDL surface to match the cairo surface created
     (surface mask of ARGB)  NOTE: Is this endian-agnostic? -bjk 2006.10.25 */
   rmask = 0x00ff0000;
   gmask = 0x0000ff00;
   bmask = 0x000000ff;
   amask = 0xff000000;

 4. we create an SDL surface out of the thing rsvg/cairo rendered for us
    (i.e., the data buffer 'image')
   sdl_surface_tmp = SDL_CreateRGBSurfaceFrom((void *) image, width, height,
                                              bpp, stride,
                                              rmask, gmask, bmask, amask);

* finally, we convert it to the display format, so it will more quickly
 render into our 'canvas' surface (the user's painting, that is continuously
 draw onto the display, i.e., blitted into 'screen')

 /* Convert the SDL surface to the display format, for faster blitting: */
 sdl_surface = SDL_DisplayFormatAlpha(sdl_surface_tmp);

Note: This is based on code that's in the "new" 'load_svg()' function
(the one that libRSVG, rather than svg-cairo).  However, the mask stuff
appears to be the same in the older one.

As mentioned above, it seems cairo stores R/G/B/A values in the same
order, regardless of the endianness of the system...  Which is why
we SDL_CreateRGBSurfaceFrom() with the same R/G/B/A masks when we
create the surface using the 'image' buffer that cairo drew into.

WHEW!  Make sense!? :)


Crystal Reports - New Free Runtime and 30 Day Trial
Check out the new simplified licensing option that enables unlimited
royalty-free distribution of the report engine for externally facing
server and web deployment.
Tuxmath-devel mailing list