imgflo 0.3: GEGL metaoperations++

Time for a new release of imgflo, the image processing server and dataflow runtime based on GEGL. This iteration has been mostly focused on ironing out various workflow issues, including documentation. Primarily so that the creatives in our team can be productive in developing new image filters/processing. Eventually this will also be an extension point for third parties on our platform.

By porting the png and jpeg loading operations in GEGL to GIO, we’ve added support for loading images into imgflo over HTTP or dataURLs. The latter enables opening local file through a file selector in Flowhub. Eventually we’d like to also support picking from web services.

Loading local file using html input type="file"

Loading local file using HTML5 input type=”file”

 

Another big feature is allowing to live-code new GEGL operations (in C) and load them. This works by sending the code over to the runtime, which then compiles it into a new .so file and loads it. Newly instatiated operations then uses that revision of code. We currently do not change the active operation of currently running instances, though we could.
Operations are never unloaded, due both to a glib limitation and the general trickyness of guaranteeing this to be safe for native code. This is not a big deal as this is a development-only feature, and the memory growth is slow.

Live-coding new image processing operations in C

Live-coding new image processing operations in C

 

imgflo now supports showing the data going through edges, which is very useful to understand how a particular graph works.

Selecting edges shows the buffer at that point in the graph

Selecting edges shows the buffer at that point in the graph

Using Heroku one can get started without installing anything locally. Eventually we might have installers for common OS’es as well.

Get started with imgflo using Heroku

 

Vilson Viera added a set of new image filters to the server, inspired by Instagram. Vilson is also working on our image analytics pipeline, the other piece required for intelligent automatic- and semi-automatic image processing.

Various insta filters

 

GEGL has for a long time supported meta-operations: operations which are built as a sub-graph of other operations. However, they had to be built programatically using the C API which limited tooling support and the platform-specific nature made them hard to distribute.
Now GEGL can load such operations from the JSON format also used by imgflo (and several other runtimes). This lets one use operations built with Flowhub+imgflo in GIMP:

imgflo-meta-ops-gimp

This makes Flowhub+imgflo a useful tool also outside the web-based processing workflow it is primarily built for. Feature is available in GEGL and GIMP master as of last week, and will be released in GIMP 2.10 / GEGL 0.3.

 

Next iteration will be primarily about scaling out. Both allowing multiple “apps” (including individual access to graphs and usage monitoring/quotas) served from a single service, and scaling performance horizontally. The latter will be critical when the ~20k+ users who have signed up start coming onboard.
If you have an interest in using our hosted imgflo service outside of The Grid, get in contact.

imgflo 0.2, The Grid launched

When I announced the first release of the imgflo project in April, it was perhaps difficult to see what exactly it was useful for and why we are developing it. This has changed now as 3 weeks ago we launched The Grid, our AI-based web publishing platform. We are on a bold mission to have “websites build themselves”; because until posting to personal websites becomes easier and more rewarding than posting to social media, content on the web will continue to pile up in closed silos.

thegrid-5k-join

To help solve this problem we built several open source technologies:

NoFlo: for creating highly testable, component-based, distributed software.
Flowhub: for visually and interactively building programs and extensions.
GSS: for building constraint-based, responsive layouts
And of course imgflo: for on-demand server-side image processing.

In total over 100k lines of code, and around 5000 commits over the last 12 months. Some of the stack is expained in more detail in a recent interview with Libre Graphics World.

imgflo on The Grid

thegrid.io launch site is of course built with The Grid. In the particular layout filter used, the look & feel is driven largely by the content. Colors for text captions are extracted from tweets and social media posts, and the featured images are largely unfiltered. Other Grid layout filters may style all provided content, including images, towards a uniform look specified by a color scheme. Or a layout filter may mix-and-match content- versus style-driven design.

The background texture on this section was created with imgflo, by passing the featured image through a blur graph:

thegridio-imgflo-bg-texture
https://imgflo.herokuapp.com/graph/vahj1ThiexotieMo/1ff47cef6f354fe0fbdefb…Fimages%2Fgrid-chrome.jpg&width=1300&height=768&std-dev-x=25&std-dev-y=25

It is important to note that no-one chose this exact image to be used in the particular layout section (and thus have the given image filter applied), which is why processing happens on-demand. The layout section with image inside a computer screen is available for content which has images of type “screenshot”. This property may be automatically detected by our image analytics pipeline, or manually annotated by user. The system allows describing many other such constraints, which are all taken into account when it works to create the appropriate layout for given content.

Even without considering styling, imgflo has a couple of important roles on a Grid site. Important is the ability create multiple scaled down versions to optimize download size. For this we also created a helper library called RIG, which is used to generate a set of CSS media-queries with imgflo request urls.


> rig = require 'rig-up'
> css = rig content, serverconfig, 'passthrough', parameters, ... 
 # passthrough is name of the graph to process through
 
@media (max-width: 503px) {
  .media, .background {
    background-image: url('https://imgflo.herokuapp.com/graph/apikey/6bb56129dc707894baa88d10a02a12b9/passthrough?input=https%3A%2F%2Fa.com%2Fb.png&width=400&height=225');
  }
}
@media (min-width: 504px) and (max-width: 1007px) {
  .media, .background {
    background-image: url('https://imgflo.herokuapp.com/graph/apikey/d099f7222293d335a6192d742f523bfa/passthrough?input=https%3A%2F%2Fa.com%2Fb.png&width=800&height=450');
  }
}

Processing images through imgflo also means that they are cached. So if the original image becomes unavailable, the website still has versions it can use. This can happen for instance on Twitter when people change their profile picture.
Note that while we optimize images when presented on site, we don’t touch the original image (non-destructive). This means image uploaded to The Grid has the full data & metadata preserved, unlike on some other social/web services. However, we are currently not preserving metadata in processed images.

 

imgflo 0.2

imgflo is now split into three repositories, the GEGL-based Flowhub runtime, the HTTP API server and the native dependencies. The runtime itself is plain C with glib, and could be used in non-web applications for desktop, mobile or embedded.

A major feature is that processing requests can now be authenticated, so that non-legitimate users cannot disrupt legitimate ones by overloading the server. We also use Amazon S3 for caching processed images, offloading a large portion of the work. Servicing 10k++ visits a day with a 2-dyno Heroko app has been no problem with this setup.

In imgflo-server we’ve also added support for using different processors than imgflo (which uses GEGL), in particular NoFlo with noflo-canvas. One can now build and deploy image processing pipelines using JavaScript, including all the libraries that work with the <canvas> element.

Building NoFlo image processing graph in Flowhub, then requesting from imgflo-server

Building NoFlo image processing graph in Flowhub, then requesting from imgflo-server

Full details about the changes can be found in the changelogs: server, runtime.

Scale

Flowhub provides imgflo a node-based visual & interactive IDE for developing new image filters for The Grid. It is similar to etablished tools like FilterForge, the Blender compositor,  vvvv and nuke – which many designers and visual artists are familiar with. However there are still many snags in the workflow for non-technical people. Smoothing out these is major part of the next imgflo milestone.
After that the focus will be on horizontal scalability, to handle the load as The Grid enters beta and opens to founding members in spring.

imgflo 0.1: An image processing server and Flowhub runtime

At TheGrid we are in need for a flexible service for doing server-side image processing. So after some discussion at LGM in Leipzig, I started writing one based on GEGL, the image processing library that will power the upcoming GIMP 2.10 release. The library provides a demand-driven graph-based API , with a ton of operations included and support for GPU processing using OpenCL.

Runtime

For creating image processing pipelines for the server, imgflo acts as a runtime for Flowhub, our open source visual programming IDE. It adds to the existing NoFlo browser, Node.js and MicroFlo microcontroller runtime targets, all possible thanks to the runtime-agnostic protocol.

The preview is live and changes whenever changes are made to the graph. This allows to quickly experiment and develop new graphs.

Server

As a server, imgflo provides a simple HTTP API where you specify the input image as an URL, the graph to process it through and any parameters exposed on that graph.

Requests being made to the imgflo server HTTP API on demo page

Processed images are cached, so that subsequent request on the same url just returns the image out of the cache.
The git repository includes configuration and build setup for Heroku, so deploying an instance is a 5 minute job.

Next

imgflo 0.1 is now minimally useful as image processing server, but there are many more enhancements on the todo list. Scalability and integration with NoFlo are two big topics, as as expanding the pool of available operations and graphs. Porting filters from GIMP to GEGL is a way of helping with the latter.

Additionally it would be interesting to provide a way of using imgflo with GIMP. First of all one could use graphs made with Flowhub via GEGL meta-operations. A more crazy idea is to integrate directly,  using Flowhub as a companion node-based editor.

 

 

Going to Libre Graphics Meeting 2014

LGM2014 will happen April 2-5th in Leipzig, Germany and this will be my fifth year attending. In fact LGM 2010 in Brussels was my first international conference ever, and convinced me that I wanted to make open source professionally.

I’m very excited about this years program, because once again we have managed to combine bleeding edge developments in open source software for graphics and visuals, with a wide range of connecting fields: open hardware, design, art activism, free cultural works, research and education.

Personally, I especially look forward to:

  • Richard Hughes: Building an OpenHardware Spectrograph for Color Profiling in Linux
  • Johannes Hanika: Wavelets for image processing
  • Manuel Quiñones: GEGL is not GIMP – creating graphic applications with GEGL (workshop)
  • Libre Graphics Magazine: Beating the drums, Why we made gender an issue

I am also hosting a BoF session on visual programming of libre graphics tools. Curious to see what comes out of that.

If you are interested in open source and graphics, don’t miss Libre Graphics Meeting.
Register now (it’s free and open for all)!

Can’t go to LGM, but would still like to contribute? Please consider donating to our travel fund.

I would like to thank the GIMP project for sponsoring my trip to LGM2014.

Libre Graphics Meeting 2013: We want you

Are you interested in the overlap between technology, art and design; and free, open, libre tools that join these domain? Do you use Libre Graphics software like GIMP, Blender, Krita, Inkscape, Scribus, MyPaint (and similar), and want to meet the people behind them?
Are you a developer of free and open source software in the areas of photography, graphics, page layout, design, publishing, typography, animation or video?

Come to the 8th annual Libre Graphics Meeting, from Wednesday 10th to Saturday 13th April in Madrid, Spain!

Registration is open (no attendance fee, sponsorship possible), and presentation & workshop proposals are accepted until 15th of February (2 weeks from today!).

Improved drawing performance in MyPaint brush engine

A first set of performance improvements for the brush engine has just landed in MyPaint master. The goals for this work for me were, in priority: a) Making sure that moving to a GEGL backend in MyPaint does not reduce performance, b) Improve performance when integrating the MyPaint brush engine in other applications, and lastly c) Improving the performance in MyPaint itself.

TL;DR: * Users of the soon-to-be-released MyPaint 1.1 should experience about 15% faster drawing of strokes for medium to big brushes. * Switching to the GEGL based backed for MyPaint 1.2 is now both feasible and highly desirable from a performance perspective.


Optimizations

The optimizations are implemented through three complimentary strategies:

1. Deferred data access to minimize fetching and updating of tiles

All dab drawing operations that happen as a result of a motion update event are queued up. When the brush engine has calculated where all dabs should go, tiles are fetched, all dabs drawn and the tiles updated. This in contrast to before where each dab drawing operation would fetch and update tiles.

2. Coarse grained parallelism using multi-threading via OpenMP directives

The tiles to be processed are divided evenly between processing threads (one per core). Each tile is processed completely independent of other tiles, so there is no locking or synchronization in the drawing code. The tile backing store must naturally be thread-safe and may ensure this using locks.

3. Fine grained parallelism using SSE via GCC auto-vectorization

Within each tile we attempt to make use of auto-vectorization to create the brush dab mask and do the composition of the dab onto the tile. Currently this is only implemented for a part of the mask calculation.

Results

Details of the results and how they can be reproduced is found in the original email thread.

Gains for MyPaint 1.1

Starting with the lowest priority goal, but the most relevant to users; performance impact on MyPaint right now.

Surface drawing results for existing Python-based backend

In terms of raw speed of drawing brushes to onto the underlying surface, speedups range from 20% to 50% for larger brushes (16 px+). This sets an upper boundary for the speedup perceived by the user.

Looking at the UI-enabled benchmarks of MyPaint, which is doing everything a normal application instance does, including layer compositing and rendering to screen, around 15% speedup was observed.  As the UI benchmark only tests a single brush at size=8.0px, it is possible that larger brushes will a higher speedup.

Users of the soon-to-be-released MyPaint 1.1 should experience about 15% faster drawing of strokes for medium to big brushes.

Note that the backend in use does not make use of the multi-threading introduced by (2) due to the tile store not being thread-safe and that it already had a cache to mitigate the problem fixed by (1).
Note

GEGL-based backend results, outlook for MyPaint 1.2

Surface drawing results, GEGL versus Python-based surface.
Test results by Till Hartmann on his Phenom II X6.

So in terms of raw surface rendering speed, the GEGL based backend is now significantly faster than the Python-based one. With 1 and 2 threads it is respectively up to 25% and 100% faster for big brush sizes. With 6 threads, it can be up to 4 times faster.

Switching to the GEGL based backed for MyPaint 1.2 is now both feasible and highly desirable from a performance perspective.

Note that to see UI performance increases approaching the raw surface drawing performance increase we may also need to do the layer compositing multi-threaded.

Gains in other applications

I’m trying to convince the Krita guys to update to the new version and to provide some feedback on the impact. Other consumers of the MyPaint brush engine do not tend to communicate much with us (some are proprietary).
I have strong hopes that (1) should increase their performance radically as their tile get/set cost is significantly higher than in the MyPaint case: They need to convert between the internal Krita and the MyPaint brush engine working colorspace each time. They may also be able to enable multi-threading and see speedups similar to the GEGL-based backend as a result.

Future Work

This is only lays the groundwork of better optimized MyPaint brush engine, many areas have room for improvement. For one only a small subset of the heavy code is vectorized. There may be inner loops that can be tweaked. It may be that, with a different tile access pattern compared to before, a different tile size would be more ideal. Perhaps doing the expensive calculation of the brush dab could be avoided some times by caching them… Thinking bigger, one could move all the drawing (and rendering) to the GPU.

More details on these ideas can be found here. If you are interested in working on any of it, get in touch and start hacking!

Piksels and Lines – Libre Graphics Research Unit seminar in Bergen

Last week I was so lucky as to attend the 3rd Libre Graphics Research Unit (LGRU) meeting in beautiful Bergen, Norway.

The meeting was titled Piksels and Lines and had “a particular focus on improvements, interoperability between and fringe use of F/LOSS graphic bitmap and vector software, as well as generative software used in performative contexts.”

The meeting was structured into three different areas: Seminar, Workshop and Performance.

Seminar

The attendants that were invited for the meeting each did a presentation of their choosing. They were recorded and are available in the online archive of the seminar. The video quality leaves something to be desired, but the audio is generally good.

The presentations I found particularly interesting were:

I gave a presentation titled MyPaint and cross-application workflows. It was an introduction to MyPaint as a creative tool, how it combines raster and vector (piksels and lines) concepts, and my perspective on interoperability between libre graphics applications.

 

 

Workshop

I had hoped to hack some code for one of my existing ideas during the workshops. That did not happen. Instead I ended up hacking specifications. Maybe that is just as good. Hacking one can always do later, hashing out and documenting ideas has to be done while it is fresh.

First the results of some discussions with Øyvind Kolås, the GEGL maintainer:

A journal for GEGL: transaction log over changes made to a GEGL graph. Specification. Discussion. This feature would allow for applications based on GEGL to:

  • Implement non-linear histories (undo/redo), and a timeline of the changes
  • Store the history in a document like OpenRaster
  • Share the history between different applications
  • Let multiple applications to work on the same document at the same time

A strategy for improved file format support in GEGL, and using this to improve  file support and interoperability in libre graphics applications. Proposed plan.

Executing this plan would move a lot of the existing file format support from GIMP (PSD, XCF, OpenRaster) down into GEGL so that it can be reused across applications. And would then let GEGL provide image support plugins for GdkPixbuf and QImage – so that at the very least – previews will work everywhere.

 

Chatting with Egil Möller, creator of Sketchspace, also resulted in:

A web based system supporting a continuous work-flow from free-hand
sketch to finished product. Concept and mockups

“Imagine starting from a freehand drawing or imported raster image and gradually refining this into a technical document with illustrations, UML-diagrams or even running code or a 3d model.”

Refining here means that the user guides the tool to transform freehand sketch into vector paths, then into vector shapes, then into something domain-specific and formal like UML – by adding additional data like annotations, strengthening of lines to “disambiguating” the transformation.

Needless to say, this is more a visionary thing. Realizing this would involve finding good solutions to a fair amount of computer vision problems.

Making GEGL available for use in web-based applications. Proposal.

More on the concrete side: allow GEGL to be used in interactive or batch-oriented web applications, or in native applications based on web technologies (Javascript, HTML5 user interfaces).

Some of the discussions also resulted in me writing down the strategy for GEGL integration in MyPaint and the related ideas/plans for how to improve the performance of the MyPaint brush engine.

Now we just need to implement all the stuff… Contributions welcomed!

 

Performance

Since Piksel, with a long history in generative performance arts, was the hosting organization it was not surprising a project in that area materialized.

A workshop session hosted by media artist Brendan Howell called Demonstrating the Unexpected came up with the idea of the Piksels & Lines Orchestra (PLO): think of the collaborative use of our traditional libre graphics software as an orchestra. The applications, from MyPaint to Scribus, are instruments; the people using them players; a performance the use of these instruments. Can we create an experience for an audience based on this framework? How would it sound? How would it look?

Architecture diagram - no need for them to be dull looking.

Having plenty of code-crafting people available, the next afternoon it was decided to spend a couple of hours realizing a prototype. The LGRU blog has the details. We recorded video of our initial performances with this prototype as well, but that has sadly not made it online yet…

 

Thanks!

Thanks a lot to Piksel and LGRU for sponsoring my attendance, and the EU Culture Programme and Bergen municipality for funding activities that support libre graphics and free culture!

MyPaint and goats at LGM2012

Already covered in the news from LGM was the release of GIMP 2.8, and that GIMP 2.10 will be fully GEGLified. The goat-invasion branch which has most of that work, the result of 3 weeks of pippin and mitch on a couch hacking together, has already landed in master. This means that GIMP now has support for high bit-depth workflows for most operations. Finally.

Putting the goat in MyPaint

During LGM I started working on using GEGL in MyPaint. I have already mentioned this idea several times, so it was time to stop talking and get hacking.

As a first step in making use of GEGL I wanted to replace the current surface implementation with one based on GeglBuffer. Since GeglBuffer already provides tiling, and can store any buffer data supported by Babl this turned out to be easy. Øyvind (pippin) added the semi-quirky pixel format we currently use* in MyPaint to Babl, and I was able to get a rough working GEGL based Surface implementation the first evening.

The MyPaint brush engine working on top of GeglBuffer

* RGBA premultiplied alpha, in 16 bit unsigned integers with  2^15 being the maximum value.

The next couple of days went to moving to the GeglBufferIterator API instead of gegl_buffer_{get,set} to have zero-copy access to improve performance, and improving GEGL and GEGL-GTK so that some of the hacks in the initial implementation could be removed.

Most of the work is in the gegl branch of MyPaint. A simple test application, mypaint-gegl.py, is included, and you can read README.gegl for how to try it out. Warning: only intended for curious developers at this stage.

A lots of work remains to be done for MyPaint to be able to fully use GEGL. The progress is tracked in two bugs, one for MyPaint work and one for GEGL issues. Because one cannot combine PyGObject with PyGTK, it will likely not be possible to fully integrate GEGL in MyPaint before porting to PyGI and GTK+ 3.

Oh, in case the goat references are lost on you – check the GEGL page on wikipedia.

Combining PySide and PyGObject introspection bindings

Some while back I added basic GObject Introspection support to GEGL and GEGL-GTK master a while back. This will* allow application developers to write their Gegl + Gtk based applications in any language supported by GObject Introspection, like Python, Vala and Javascript. For GeglQt, the Qt integration library for using Gegl in Qt based applications, it was natural to use PySide to provide Python bindings for it. The initial setup was quick and easy, thanks to the binding tutorial, but there was one challenge.

The current widgets provided by GeglQt are for displaying the output of a node in the GEGL graph. Therefore they have methods with the following signature to hook up it up:

From gegl-qt/nodeviewwidget.h
GeglNode *inputNode() const;
void setInputNode(GeglNode *node);

GeglNode is a GObject (from the C based glib) subclass, and without help the bindings generator (Shiboken) does not know what to do with it so the method cannot be bound. PySide could have been used to also generate bindings for Gegl itself, but what we actually want to do is to make use of the existing PyGObject based bindings.

Marcelo Lira on #pyside let me know that this should be possible by adding some annotations to the typesystem.xml file, and implementing a Shiboken::Converter<T>. It is indeed possible, and for the above type looks something like this:

From typesystem_gegl-qt.xml
<primitive-type name="GeglNodePtr">
      <conversion-rule file="geglnode_conversions.h"/>
      <include file-name="pygobject.h" location="global"/>
</primitive-type>

From geglnode_conversions.h
namespace Shiboken {
template<>
struct Converter<GeglNodePtr>
{
    static inline bool checkType(PyObject* pyObj)
    {
        return GEGL_IS_NODE(((PyGObject *)pyObj)->obj);
    }

    static inline bool isConvertible(PyObject* pyObj)
    {
        return GEGL_IS_NODE(((PyGObject *)pyObj)->obj);
    }

    static inline PyObject* toPython(void* cppObj)
    {
        return pygobject_new(G_OBJECT((cppObj)));
    }

    static inline PyObject* toPython(const GeglNodePtr geglNode)
    {
        return pygobject_new(G_OBJECT(geglNode));
    }

    static inline GeglNodePtr toCpp(PyObject* pyObj)
    {
        return GEGL_NODE(((PyGObject *)pyObj)->obj);
    }
};
}

The PyGObject C API and the GObject type system is here being used to implement what Shiboken needs. The attentive reader will note that GeglNodePtr is used and not GeglNode*. This is a simple “typedef GeglNode * GeglNodePtr“, which looks to be neccesary with current PySide (1.0.6) to avoid it being confused by the pointer. Hopefully that is fixable and won’t be necessary in the future.

With this solved, I committed the initial Python support to GeglQt master yesterday. It contains a trivial Python example showing the usage. Some build cleanups, binding generator tweaks and testing remains to be done, but expect Python support to be a prominent feature for GeglQt 0.1.0

 

* There are still a lot of GObject Introspection annotations missing in Gegl. See the tracking bug. Help wanted!

Making GEGL easier to use in graphical applications

So, in the last couple of months I’ve been working a bit on GEGL. Some of the work has already been covered by LWN, so I guess it is time that I blog about it…

GEGL is a generic image processing library which is used by applications like GIMP, (and in the future maybe MyPaint and DarkTable). It provides applications with a graph based image processing backend that can do non-destructive processing of high-bitdepth images, among other things.

One of the problems that I think has been limiting adaptation of GEGL has been the entry barrier to starting to use it in a graphical application. While GEGL provides the image processing backend, it did not provide good and easy ways of displaying the output on screen. Now it does!

GTK+, Clutter and Qt integration libraries

Some code for integrating GEGL in GTK+ based applications has existed in the GEGL tree for a long time, but it was not well maintained and there was no public API. After brushing up the code to use Cairo for rendering and to support both GTK+ 2 and 3, it was split out to a separate library and repository: gegl-gtk. This library now provides a GtkWidget for displaying the output of a node in the GEGL graph, with basic support for scaling and translations. Any change in the GEGL graph will be reflected in the view widget. This makes it trivial for applications using a GTK+ based user interface to get started using GEGL, see for instance the provided examples in C or in Python.

The same functionality is provided for Clutter based user interfaces by gegl-clutter in form of a ClutterActor. This code was previously available as clutter-gegl, but has now been renamed and moved to be a part of the GEGL project, and is maintained by Øyvind Kolsås. Example code in C.

Last but not least, gegl-qt was created to serve the needs of applications using Qt based user interfaces. The different widget systems (QWidget-, QGraphicsWidget- and QML-based) are all supported. In addition to the features currently available in the GTK+ and Clutter versions, the Qt view widgets also support auto-scaling and auto-centering. Python bindings via PySide is planned, but blocking on a PySide issue at the moment.

A pretty boring screenshot showing two QWidget based examples (code: 1, 2) for transformations:

Artwork: “Wanted“, speedpainting by David Revoy

The first stable release of gegl-qt and gegl-gtk will hopefully be available soon. The list of tasks can be found in the README files.

Display operations

In GEGL, image processing is described as a graph of operations. “gegl:display” and “gegl:gtk-display” operations existed in the gegl tree, and by attaching one of these to a node in the graph one could display the output of the graph at the given node in a window . Such display operations are useful for applications that just want to show the output of a graph without having to use a GUI library directly.

The problem was that both of these operations were optional, so applications could not rely on this functionality to be present. This is solved by letting the “gegl:display” operation be a meta-operation, which uses other operations as a handler to actually display the output. Such display handler operations are now provided by gegl (optional, using SDL), gegl-gtk (using GTK+) and gegl-qt (using Qt). In addition a fallback operation that will export a PNG file and launch an external application to display it will be provided in GEGL.

More to GEGL stuff to come soon, hopefully.