3

I am trying to draw a png image, the contents of which I have in memory in an ARGB32 format, using C++ Cairo and Gtk on Ubuntu.

First, I create a GtkDrawingArea, then on its expose-event I draw a solid blue background with a red line from top left to bottom right, then I create a surface and try to draw it onto the drawing area. Here's my expose-event callback:

unsigned char *pCairoBuf = ...

....

gboolean OnDrawingAreaExposeEvent(GtkWidget *pWidget, GdkEventExpose *pEvent, gpointer data)
{
    cairo_t *cr = gdk_cairo_create(pWidget->window);

    cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
    cairo_rectangle(cr, 0.0, 0.0, pEvent->area.width, pEvent->area.height);
    cairo_fill(cr);

    cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
    cairo_set_line_width(cr, 10.0);
    cairo_move_to(cr, 0.0, 0.0);
    cairo_line_to(cr, pEvent->area.width, pEvent->area.height);
    cairo_stroke(cr);

    // Use the existing buffer pCairoBuf that has (iRenderWidth * iRenderHeight * 4) bytes, 4 bytes per pixel for ARGB32 and zero row padding   
    cairo_surface_t *pSurface = cairo_image_surface_create_for_data(pCairoBuf, CAIRO_FORMAT_ARGB32, iRenderWidth, iRenderHeight, (iRenderWidth * 4));

    cairo_set_source_surface(cr, pSurface, 0.0, 0.0);
    cairo_paint(cr);

    cairo_destroy(cr);

    return TRUE;
}

The drawing area and the image are both 244x278 pixels. The image is an image of Smokey the Bear's head and is transparent around his head:

enter image description here

And I expect the final result to look like this:

enter image description here

But it ends up looking like this:

enter image description here

I did not add the code that shows how I got the data buffer pCairoBuf because I figured it would only cloud the issue, but perhaps I'm wrong? I figured that there's something else I'm doing wrong having to do with cairo surfaces, etc. that would explain the difference between what I'm expecting and what I'm getting.

Thanks in advance for any help!

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
user2062604
  • 247
  • 3
  • 16
  • Is the data in `pCairoBuf` [alpha-premultiplied](https://developer.gnome.org/cairo/unstable/cairo-Image-Surfaces.html#CAIRO-FORMAT-ARGB32:CAPS)? – andlabs Jul 16 '15 at 17:06
  • Yes, thank you @andlabs for asking. Just to clarify, my interpretation of "alpha-premultiplied" is that if the original pixel is, say, a nice shade of blue (0, 148, 255) and the alpha is 27.45%, then the full ARGB would be 0x46002946, am I understanding that right? – user2062604 Jul 16 '15 at 17:23

2 Answers2

1

As a guess, not having used any of those libraries, I'd say your alpha channel's uniform across your image and the rendering's applying this also to the parts of the image you'd like non-transparent. Try only applying the alpha channel to those pixels which you'd like to be transparent.

mrtimdog
  • 487
  • 4
  • 13
  • Thanks for asking @mrtimdog but I double checked and the data in `pCairoBuf` does have all correct ARGB values, with varying alpha levels depending on the pixel. I'm not sure what you mean by "Try only applying the alpha channel to those pixels which you'd like to be transparent". Isn't the nature of ARGB and `CAIRO_FORMAT_ARGB32` that every pixel has 32 bits, 8 for each of the four channels? Perhaps I'm misunderstanding the nature of alpha-premultiplication? – user2062604 Jul 16 '15 at 20:29
  • @user2062604, every pixel has an 'alpha channel' value, of those pixels for which you'd like to be non-transparent, do they all have a 100% opaque alpha channel value as opposed to all pixels having the same, semi-transparent, alpha channel value? Think of it as a transparency mask. – mrtimdog Jul 16 '15 at 20:45
  • Thanks for that. Yes, the pixels which should not be transparent all have 100% opaque alpha channel values, the pixels which should be fully transparent all have 0% opaque alpha channel values, and some pixels have alpha channel values in the middle. In short, the alpha bits in each pixel in `pCairoBuf` seem to be correct. – user2062604 Jul 16 '15 at 21:25
  • Hmm, the only issue I can deduce from that is either; there's a default alpha modifier for the whole image which is 'transparent', or there's a bug in the renderer! This is all still guesses though? Sorry if I've not helped! – mrtimdog Jul 16 '15 at 21:31
  • Thank you very much @mrtimdog for your input. Unfortunately I think that the issue is something specific to the Cairo image library, i.e. I'm using Cairo incorrectly or making bad assumptions, something like that. – user2062604 Jul 16 '15 at 22:02
0

I figured it out. When I was filling in the ARGB32 data in the buffer that I pass to cairo_image_surface_create_for_data(), I was filling in the bytes in that order, A, R, G, B. As an experiment I reversed the order and filled in the bytes B, G, R, A, and it worked perfectly.

user2062604
  • 247
  • 3
  • 16
  • It's documented that image surfaces use the endianness of the host. So the proper way to fill bytes AFAICT is to declare the source colour as a `uint32_t` and then `memcpy()` that to the data buffer. That way, the host handles the endianness for you, in a portable way unlike hard-coding a specific byte order. – underscore_d Dec 12 '18 at 19:09