Two beginner questions about programming in Gnome

Hello,

I’m new to programming in Gnome and I have two questions:

  1. There is a way to convert “cairo_surface_t” to “GdkPixbuf” with the call “gdk_pixbuf_get_from_surface”. But I can’t find any way to convert “GdkPixbuf” to “cairo_surface_t” anywhere. Is there a way?
  2. Is there a library for drawing and filling graphic primitives (lines, rectangles, circles, etc.)?

Thanks in Advance,
Michael

You need to create an Image surface with Cairo, and put the pixel data from the GdkPixbuf into it. Cairo stores the pixel data as an ARGB buffer, whereas GdkPixbuf uses RGBA—even when the data does not have an alpha channel. This means you’ll need to swizzle the channels when copying the data into Cairo, for instance:

cairo_surface_t *
pixbuf_to_surface (GdkPixbuf *pixbuf)
{
  // Query how big the pixbuf is, and how many channels it has
  int width = gdk_pixbuf_get_width (pixbuf);
  int height = gdk_pixbuf_get_height (pixbuf);
  int n_channels = gdk_pixbuf_get_n_channels (pixbuf);

  // Determine the content format for the Cairo surface
  cairo_format_t format = n_channels == 4
                        ? CAIRO_FORMAT_ARGB32
                        : CAIRO_FORMAT_RGB24;

  // Create an image surface with the same characteristics as the pixbuf
  cairo_surface_t *surface =
    cairo_image_surface_create (format, width, height);

  // Get the raw pixel buffer from the pixbuf
  int pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
  guchar *pixbuf_pixels = gdk_pixbuf_get_pixels (pixbuf);

  // Get the raw pixel buffer from the Cairo image surface
  int surface_rowstride = cairo_image_surface_get_stride (surface);
  guchar *surface_pixels = cairo_image_surface_get_data (surface);

  // Tell Cairo we're going to poke at the pixel buffer from the outside
  cairo_surface_flush (surface);

  for (int j = height; j != 0; j -= 1) {
    guchar *p = pixbuf_pixels;
    guchar *q = surface_pixels;

    // No alpha channel
    if (n_channels == 3) {
      // Find the end of the row
      guchar *end = p + 3 * width;

      while (p < end) {
        // Copy the channels in the right order,
        // depending on the endianness of the system
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
        q[0] = p[2];
        q[1] = p[1];
        q[2] = p[0];
#else
        q[1] = p[0];
        q[2] = p[1];
        q[3] = p[2];
#endif

        // Advance the buffer iterators to the next column
        p += 3;
        q += 4;
      }
    } else {
      // Find the end of the row
      guchar *end = p + 4 * width;

// Cairo uses pre-multiplied alpha channels
#define PREMULT(dest,channel,alpha) G_STMT_START { \
  guint __t = channel * alpha + 0x80; \
  dest = ((__t >> 8) + __t) >> 8; \
 } G_STMT_END

      while (p < end) {
        // Copy the channels in the right order,
        // depending on the endianness of the system
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
        PREMULT (q[0], p[2], p[3]);
        PREMULT (q[1], p[1], p[3]);
        PREMULT (q[2], p[0], p[3]);
        q[3] = p[3];
#else
        q[0] = p[3];
        PREMULT (q[1], p[0], p[3]);
        PREMULT (q[2], p[1], p[3]);
        PREMULT (q[3], p[2], p[3]);
#endif

#undef PREMULT

        // Advance the buffer iterators to the next column
        p += 4;
        q += 4;
      }
    }

    // Advance the buffer iterators to the next row
    pixbuf_pixels += pixbuf_rowstride;
    surface_pixels += surface_rowstride;
  }

  // Tell Cairo we're done poking at the surface data,
  // and it can now rebuild its internal caches
  cairo_surface_mark_dirty (surface);
}

Yes, that’ll be Cairo.

Hello Emmanuele,

  1. I solved my problem in another way, I load from file and display “GdkPixbuf” directly. This even gives me the advantage of scaling.
  2. I have to apologize, I didn’t see these commands under “Paths” in the documentation! :joy:

Many thanks for your answer.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.