Very simple interpolated drawing code

I’ve started writing a very simple stylus ‘scribble’ program in C. It works. However, from on-line searches I learn that gtk + Cairo can’t produce continuous freehand lines without the program providing interpolation. This is a drag.

I have searched for “gtk interpolated freehand drawing” and have not found any examples. Before I hack my way into writing code for this rather obvious need, does anyone know of a demo example ?

I read: It’s not possible building choerent free hand drawing using GTK+ without interpolation!
Here: https://stackoverflow.com/questions/48557583/freehand-drawing-gtk-set-event-compressionfalse-does-not-work Is this really true ?

I can see that Cairo’s ‘primitives’ are rather fancy so I’m not surprised that it is too slow for interactive use. Is there a more primitive drawing option for gtk+ ? Drawing a colored dot of a specified radius on the screen (a filled disk) would be a simple and useful primitive. The Cairo facility for arcs is way, way too fancy.

It seems switching off event compression is nearly as good as performing interpolation.

The classical GTK drawing area example is still available at gitlab:

https://gitlab.gnome.org/GNOME/gtk/-/blob/main/examples/drawing.c

I think years ago it had its own HTML page with a picture, but I can not find it any longer.

You can save that C code as simple.c and compile it like

gcc -Wall simple.c -o simple pkg-config --cflags --libs gtk4

That program draws indeed only tiny unconnected rectangles at current mouse position. For me it is not really clear what you intend at all, and what your “interpolation” is. It is easy to modify that example to connect the mouse coordinates with cairo lines, that is the simplest form of interpolation.

Note that cairo is a vector library, so it may not be the best solution for freehand drawing as in gimp – you may prefer a raster bitmap based library? And note that user interaction has changed in GTK4. No gestures can be used. But the old more flexible legacy interface is still available.

You can create with gtk and cairo tools like https://github.com/StefanSalewski/SDT, but that is some work. Maybe in winter I will find some time to continue that app. The most difficult part for cairo drawing is to support scrolling and zooming. I once created this simple demo in Nim, which draws some basic shapes, and let the user zoom in and scroll, see https://github.com/StefanSalewski/gintro#a-more-advanced-example-for-cairo-drawing-with-zooming-panning-scrolling. That demo is still GTK3, but is was the basis for the SDT tool mentioned above. There may exists other ways in GTK for zooming ind scrolling, maybe with a GtkScrolledWindow.

Also note, that cairo is a bit outdated. A more modern, actively developed variant is blend2d. And GTK itself seems to use a lot of OpenGL for drawing its widgets in GTK4 now.

[EDIT]

It seems switching off event compression is nearly as good as performing interpolation.

This “event compression” seems to be more a myth, maybe from old GTK2 days. Ten years ago, I asked about it, and trying to deactivate compression made no difference. I got a few dozen mouse movement events per second max, which is generally OK. It may be different for XOrg and wayland. For me it was never a big problem getting too few events, but I can imagine that for smooth movement and scrolling on a 144 or 200 Hz display you may want to get up to 200 events per second. Maybe the experts can answer this.

Maybe we can play with Gdk.Event.get_history

Thank you for replying.
I have a quick reply here to show that event compression does do something.

Here is the call:
gdk_window_set_event_compression(((GdkEventButton *)event)->window, false);

Just for grins, my code prints the event x and y and the Euclidian distance between successive event locations. The three numbers are x , y and distance. You can see that with compression off the distance is often less than one. If compression is active then event points are at a much greater distance. This is only a rough experiment based on a simple hand-sweep of a stylus.

compression OFF
474.62 385.64 delta 0.84
475.21 386.22 delta 0.83
475.68 386.76 delta 0.71
476.15 387.07 delta 0.57
476.39 387.45 delta 0.44
476.51 387.71 delta 0.29
476.62 388.08 delta 0.39
476.51 388.24 delta 0.20
476.39 388.46 delta 0.24
476.15 388.67 delta 0.32
475.92 388.83 delta 0.29
475.32 388.88 delta 0.59
474.85 388.67 delta 0.52

compression ON
210.20 676.90 delta 17.72
225.56 676.21 delta 15.37
242.33 674.83 delta 16.83
259.46 673.39 delta 17.19
279.30 672.17 delta 19.88
291.35 671.64 delta 12.06
303.98 671.21 delta 12.64
310.36 671.11 delta 6.38
314.73 670.95 delta 4.37
318.16 670.84 delta 3.43
323.35 670.31 delta 5.22
327.96 669.35 delta 4.70
332.92 668.13 delta 5.11

GTK3 allows app developers to disable event compression as a backward compatibility compromise.

GTK4 does not allow you to do that; if you want more events, you will need to get the motion event history.

At the end of the day, though, the sampling rate for an input device is specific to the input device itself, and it may be too low depending on the size of the surface you’re drawing on; the windowing system—and the GUI toolkit that reports what the windowing system provides—cannot make event data up. Instead of drawing dots at the given event coordinates, you can use lines from the previous event coordinates to the current one; you can use the event history to increase the accuracy of the interpolation as well.

There’s literally nothing “fancy” about Cairo’s drawing API: it’s a very basic set of vector operations.

1 Like

I should add that my work in on Linux with Xorg, of course. For my non-GUI graphics work I use OpenGL and write simple shaders. The platform is an i7-1165G7 which has a decent (for my purposes) integrated GPU.

I only use GTK when I need a real user GUI. Even then, the simple GUIs I write are hand crafted without Glade.

It seems very odd that Cairo is no longer the preferred primitive renderer. Even more odd if GTK is now using OpenGL and not Vulkan. Not that I am a fan of Vulkan.

Pray tell what harm does disabling event compression do ? If it is benign why disable it ? Too late to ask that question, I guess.

If one wants a simple disk drawn one has to use the Cairo primitive cairo_arc. Including starting and ending angles and then has to fill the dot as a separate call. There has to be a lot of stuff going on in cairo_arc to do that. This is what I mean by fancy – I’m used to whamo, blamo pixel pushing.

The “tl;dr” answer is: the more events GTK needs to dispatch to client code, the less time is available in the frame budget.

Dispatching signals can be slow, and since everything in GTK happens inside the same thread and main loop, it means that too many signals being dispatched will reduce the amount of time available for client code—i.e. your application’s code—to perform its own operations. Since the average display runs at 60 Hz, this means that each frame has 16.67 milliseconds at its disposal to do:

  • event dispatching
  • layout
  • drawing

If any of these operations take too long, frames will be skipped, and that may be displeasing. Additionally, input will be skipped as well, since the toolkit’s main loop won’t be able to “catch up” to the windowing system.

This is why compression on motion events is typically paired with storing the history of the coordinates of each motion event that happens in sequence during the same frame.

That’s not how GTK works, sorry.

You may want to use something simpler, for instance an immediate mode toolkit like imgui if you want to merely draw pixels and some UI controls around them. SDL2 is another option, though it’ll require you write your own UI elements.

GTK lets you draw things that UI elements require: boxes, shadows, gradients, text, texture data. For primitive drawing we have vector-based API in the form of Cairo. That means you’ll have to keep some form of state in your own widget, that you can then draw.

I have a quick reply here to show that event compression does do something.

Well, I can remember a discussion maybe 8 years ago, in early GTK3 days, when disabling compression made no difference for some people, including me. If you should depend on disabling compression, you may ensure that it works really reliable for all of your users, including Mac and Windows. I will do a test myself later. But I think for new software we should use GTK4 instead of GTK3 now.

And I actually wonder how the event rate is on 144 or 200 Hz displays, I really hope it is not hard coded to 60 Hz max?

4 posts were split to a new topic: Rendering backend for GTK

Users ? I write for myself ! :wink: What I’m currently writing has one user and is on one platform.

I started using GTK a while back when I wanted to make it possible for an artist collaborator to be able to use some software I had written that had NO user interface – not even a command line. To change the operation of the code I would change the source, recompile and run ! The source code was the UI – it’s the best UI there is :wink:

Most of my work is with OpenGL and GLSL. Vulkan isn’t for me. I’ve written just a very few GTK applications.

My original question was to find a small GTK application example that uses: gdk_event_get_history and integrates it, with interpolation, for freehand drawing.

It still seems odd that GTK would drop disabling compression when it is already off by default. Those that might find it useful can then choose to disable compression. My single case shows it is useful. My 4K 30Hz screen with a Wacom tablet draws much better without the event compression.

Still, gdk_event_get_history should do things in the best way and thus I have been looking for a demo example.

That’s the opposite of what is happening: GTK3 enables event compression by default, but allows you to disable it for legacy reasons. GTK4 always performs compression for motion and scroll events, and does not have any way of disabling it.

Yes, I understand that. It just doesn’t make sense to take a facility away that is off by default and isn’t getting in anyone’s way. Consequently weirdos, like me, that find it useful, now cannot choose to override it. More choices are usually better, if they don’t get in anybody’s way and don’t slow things down.

The event compression is obviously some piece of buried code that chooses what to throw away, meanwhile stashing all the events in a history table.

Oh, and when I go look for the history stuff I find:
https://gitlab.gnome.org/GNOME/gtk/-/issues/4809
not very encouraging.

1 Like

It makes sense to the people that maintain the toolkit to remove code that is legacy. That’s why it happened in GTK4.

I wouldn’t call people writing drawing applications “weirdos”: after all, that’s what GIMP does.

Event compression was introduced in GTK3 because every application that is not drawing directly from input events is not going to benefit from more events than necessary; since existing applications being ported from GTK2 to GTK3 did not want to refactor their code to the extent of making use of the event history, a toggle was introduced to ease their porting process. That toggle has now been removed in GTK4, as applications have had 9 years to port their internals to the new API.

If you expect GTK to not have bugs, you’ll be sorely disappointed. :wink:

That particular issue has been fixed 3 months ago.

6 posts were split to a new topic: Deprecations in GTK

A post was split to a new topic: Creating and destroying Cairo contexts

Now that I understand that Cairo is somewhat orphaned, what is to replace it ?

gtk_window_create_similar_surface() returns a Cairo surface is there some other kind of drawing surface available ?

I can only vaguely imagine that there might be a gtk_window_create_similar_OpenGL_window() but that seems close to impossible.

I’m guessing I need to use X11 via gdk_x11_window_foreign_new_for_diaplay() to get to more direct drawing ?

If you need high quality rendering, you are still going to use Cairo. If you want to render things like widgets—i.e. things that get styled with CSS—then you want the GtkSnapshot API.

No, if you want to draw with OpenGL, then you need GtkGLArea.

Nope, you never, ever draw on windowing system surfaces directly.

I’ll investigate GtkGLArea. If I can get to an OpenGL session then all kinds of things open up such as GLSL shaders . Is this true ?

Yes, you can also use GLSL shaders.

There are quite a few demos in gtk4-demo for GTK4.

For GTK3, there’s a demo in gtk3-demo, and an explanation in this glarea example.