I’m new to programming in Gnome and I have two questions:
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?
Is there a library for drawing and filling graphic primitives (lines, rectangles, circles, etc.)?
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);
}