[cairo] Merging a part of multiple pixmaps with alpha and a mask

Samuel Abels newsgroups at debain.org
Wed Jan 5 18:10:20 PST 2005


Hello,

I am writing a canvas using libcairo and now need to be able to draw all
items with z-order top-to-bottom - this can be done much faster then the
other way round for several reasons.

Drawing top-to-bottom introduces some difficulties though. When the top
item is drawn, other items must only draw around that item, thus there
needs to be a mask telling the non-top items which areas to touch and
which to leave unchanged. A function like that is probably non-standard
und thus (understandably) not part of the libcairo API.

I am now trying to add that functionality by writing my own function
doing just that. In addition, it does not only introduce the mask, but
also lets you specify a rectangle of the source pixmap along with the
destination position.

The problems are:

1. How can I obtain the height/width of a buffer from the cairo_t
structure? (I haven't found the definition in the header files in CVS,
so they are probably hidden somewhere I did not look.)

2. How can I obtain the buffer from the cairo_t structure?

3. Most difficult and most important: What is the right way to copy a
pixel from a source ARGB buffer into a destination ARGB buffer taking
care of a) the alpha value of the source buffer and b) the alpha value
of the mask as well?
This may be hard to understand without having seen the code that is
supposed to do that, so I have created a complete and
as-simple-as-possible test case for what I had in mind using some of the
demo code from cairo CVS. I have also added a lot of explanations and
marked all the positions where I do not know what to do with "XXX".
The test case is appended to this message, also including a makefile.

The problems are all in the draw() function at the bottom of alpha.c. I
have also copied that file into the body of this email for your
reference.

I would be most grateful if someone could point me to a solution.

-Samuel

------------------------------------------
int main(int argc, char **argv)
{
  cairo_t* cr[NUM_PIXMAPS];
  char     image[NUM_PIXMAPS][STRIDE * HEIGHT]; // Ignore too large alloc.
  char     mask[MASK_SIZE * MASK_SIZE];
  int      i = 0;
  
  // Init all images.
  for (i = 0; i < NUM_PIXMAPS; i++) {
    cr[i] = cairo_create();
    memset(image[i], 0, STRIDE * HEIGHT);  // Make 100% transparent.
  }
  
  // Draw background on one buffer.
  cairo_set_target_image(cr[0], image[0], CAIRO_FORMAT_ARGB32,
                         WIDTH, HEIGHT, STRIDE);
  cairo_rectangle(cr[0], 0, 0, WIDTH, HEIGHT);
  cairo_set_rgb_color(cr[0], 1, 1, 1);
  cairo_fill(cr[0]);
  
  // Draw two rectangles, each on a different buffer. Imagine these buffers
  // being images afterwise. I do use only two images here, but this could be
  // more in the actual application.
  // Note that both buffers use alpha.
  cairo_set_target_image(cr[1], image[1], CAIRO_FORMAT_ARGB32, 200, 200, 200*4);
  cairo_rectangle(cr[1], 50, 50, 150, 150);
  cairo_set_rgb_color(cr[1], 0.8, 0, 0);
  cairo_set_alpha(cr[1], 0x55);
  cairo_fill(cr[1]);
  
  cairo_set_target_image(cr[2], image[2], CAIRO_FORMAT_ARGB32, 200, 200, 200*4);
  cairo_rectangle(cr[2], 10, 10, 40, 100);
  cairo_set_rgb_color(cr[2], 0, 0.7, 0);
  cairo_set_alpha(cr[2], 0x66);
  cairo_fill(cr[2]);
  
  // Now these buffers are all to be merged into cr[0], with the smallest
  // rectangle (cr[2]) on top, the other below. What is important is this:
  // For performance reasons, I need to draw the top item first, then draw the
  // others around that. That means, there needs to be a mask to determine
  // which pixels must and which must not be drawn.
  memset(mask, 0, MASK_SIZE * MASK_SIZE);  // Make 100% transparent.
  
  // This is supposed to copy a subset of the top item (cr[2]) to position
  // (50, 50) on cr[0], preserving alpha. In the same run, it also marks
  // the pixels in the mask accordingly.
  draw(cr[2], cr[0], mask, 5, 5, MASK_SIZE, MASK_SIZE, 50, 50);
  
  write_png_argb32(image[0], "alpha.png", WIDTH, HEIGHT, STRIDE);
  
  for (i = 0; i < NUM_PIXMAPS; i++)
    cairo_destroy(cr[i]);
  
  return 0;
}


void draw(cairo_t* _src, cairo_t* _dest, char* _updatemask,
          double _x, double _y, double _w, double _h,
          double _dest_x, double _dest_y)
{
  // Copy the pixels from the source pixmap to the given surface using the
  // alpha from the cache combined with the alpha from the alpha mask.
  int row, col;
  int src_rowstride;
  int dest_rowstride;
  int mask_rowstride;
  src_rowstride  = _src->width  * 4;  // XXX: How can I obtain the width?
  dest_rowstride = _dest->width * 4;  // XXX: How can I obtain the width?
  mask_rowstride = _w;
  for (row = _y; row < _h; row++) {
    for (col = _x; col < _w; col++) {
      // The buffer source uses ARGB, so we point to the A.
      char* srcpixel  = _src->buffer    // XXX: How can I obtain the buffer?
                      + (int)(row * src_rowstride)
                      + (int)(col * 4);
      // The destination buffer uses ARGB either.
      char* destpixel = _dest->buffer   // XXX: How can I obtain the buffer?
                      + (int)((row + _dest_y) * dest_rowstride)
                      + (int)((col + _dest_x) * 4);
      // The alpha mask has only one byte per pixel.
      char* maskpixel = _updatemask
                      + (int)((row - _y) * mask_rowstride)
                      + (int)(col - _x);
      
      // FIXME: What is the correct way to merge that single pixel into the
      // destination here, also taking care of the alpha value in *maskpixel?
      //*destpixel = ??
      
      // Also, mark the pixel in the mask.
      *maskpixel += *srcpixel;
    }
  }
}
------------------------------------------
-- 
 ------------------------------------------------------
|      Samuel Abels       |   http://www.debain.org    |
| spam ad debain dod org  | knipknap ad jabber dod org |
 ------------------------------------------------------
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cairo-alpha.tgz
Type: application/x-compressed-tar
Size: 3674 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20050106/84e6cfa7/cairo-alpha.bin


More information about the cairo mailing list