You are here

Planet GNOME

Subscribe to Feed Planet GNOME
Planet GNOME -
Përditësimi: 2 ditë 20 orë më parë

Debarshi Ray: Image wrangling with GEGL: an introduction

Hën, 20/11/2017 - 12:38md

One of the core dependencies of GNOME Photos, other than GTK+ and Tracker, is a library called GEGL. It is a GObject-based image processing library primarily developed for GIMP. GEGL is used by Photos to load pixels from files, create thumbnails, edit, share and export images.

Unfortunately, even though GEGL is a powerful and generic image processing framework, it can be hard to find documentation and code samples to refer to, and the pool of people who understand it well enough is relatively small. I am going to do a series of blog posts to address this by feeding the search engines. Hopefully this will be useful for new contributors to GIMP and GNOME Photos, and some of it can be folded back into the reference GEGL documentation; or maybe it will encourage adoption in new and interesting places.

Nodes and operations

Processing images with GEGL requires the creation of a graph, represented by a GeglNode. A GeglNode can either have a number of child nodes connected to each other forming a directed acyclic graph, or it can have a GeglOperation. An operation is where the actual image processing takes place. Multiple operations are chained together in a graph to obtain the desired outcome.

This is enough to get started with some basic effects and enhancements. Here is a snippet that takes a path to an input image, enhances the blacks and saves it as a PNG.

#include <gegl.h> … g_autoptr (GeglNode) graph = NULL; GeglNode *exposure; GeglNode *load; GeglNode *sink; graph = gegl_node_new (); load = gegl_node_new_child (graph, "operation", "gegl:load", "path", /* input path as C string */, NULL); exposure = gegl_node_new_child (graph, "operation", "gegl:exposure", "black-level", 0.03, NULL); sink = gegl_node_new_child (graph, "operation", "gegl:png-save", "bitdepth", 8, "path", /* output path as C string */, NULL); gegl_node_link_many (load, exposure, sink, NULL); gegl_node_process (sink);

Notice the many similarities with GStreamer.

There is a whole list of such filters to choose from. Such as gegl:cartoon to simulate a cartoon drawn with a black felt pen, gegl:mosaic to transform an image into a mosaic, gegl:saturation to change the colourfulness of the image, or gegl:posterize, which is used by the similarly named tool in GIMP.


Image pixels are held in a GeglBuffer. Most applications would directly interact with a GeglBuffer at one point or the other. For example, to decode an image file and carry the pixels around instead of repeatedly decoding them off the storage. In the above code sample, the buffers were implicitly created by GEGL unbeknownst to us, but we can use a similar graph to load pixels off a file into a GeglBuffer.

#include <gegl.h> … g_autoptr (GeglBuffer) buffer = NULL; g_autoptr (GeglNode) graph = NULL; GeglNode *load; GeglNode *sink; graph = gegl_node_new (); load = gegl_node_new_child (graph, "operation", "gegl:load", "path", /* input path as C string */, NULL); sink = gegl_node_new_child (graph, "operation", "gegl:buffer-sink", "buffer", &buffer, NULL); gegl_node_link_many (load, sink, NULL); gegl_node_process (sink);

A loaded buffer can be then fed into a graph using a gegl:buffer-source.

As the custodian of pixels, GeglBuffer is similar to the role played by GdkPixbuf, but it has some extra features that are handy for image processing.

Most notably, a GeglBuffer is designed to handle massive images that are larger than the amount of physical RAM available on the system. Instead of holding all the pixels in a linear sequence of bytes, it splits them up into small tiles that can be paged out into a file when not in use. However, if necessary, it is possible to optionally dumb down a GeglBuffer by setting it up to use a single array of bytes, or forcing all tiles to be held in RAM.

A GeglBuffer is not restricted to a single pixel format such as RGB with 8 bits per channel. It can transparently handle a horde of formats — monochrome, Lab, HSL, etc. with different degrees of precision per channel. Finally, it is mipmap-capable.

All these features make GeglBuffer a very sophisticated data structure for storing image pixels. However, they aren’t that important for an introduction to GEGL, so we will save them for a future article.

Happy hacking

This is enough to start playing with GEGL. Here is the code used to create the above image, and is proof that knowing just this much is enough to do practically useful things.