WIP: new Cairo support features

28 views
Skip to first unread message

Albrecht Schlosser

unread,
Mar 21, 2023, 3:20:45 PM3/21/23
to fltk.coredev
Hi all,

I'm currently working on new "Cairo support" features. The main goal is to support users who want to use Cairo in FLTK windows and widgets w/o the need to configure FLTK for Cairo with configure --enable-cairo, --enable-cairoext, or CMake options OPTION_CAIRO and OPTION_CAIROEXT.

The current status is that I have completed support to draw in windows and widgets, roughly equivalent to our current Cairo support. Users can use Cairo drawing in their own subclasses of windows and widgets using stock FLTK libraries. I don't want to bother you with too many details, but if you're interested, this is all in my branch Fl_Cairo2a on my fork: https://github.com/Albrecht-S/fltk/tree/Fl_Cairo2a. Note that building the full library currently fails, but this is a known issue and has nothing to do with the new functionality (only demo programs should fail).

I could now finish this, but I would like to add a feature to draw in other surfaces than the active window, like Fl_Image_Surface, Fl_Copy_Surface and siblings.

My current implementation works for Windows but I need help for other platforms. If anybody can help I would appreciate this very much.

Details can be found here: https://github.com/Albrecht-S/fltk/blob/676743ea6c6819f67da08bcc89158aed6220de4e/test/cairo2_image.cxx#L45

The function below: cairo_t *cairo_context(Fl_Image_Surface *surf) works so far for Windows but for no other platform. I'd like to have this completed for macOS, Linux (X11), and Wayland. Once I have the basics, I intend to put this code into appropriate FLTK methods.

The output on Windows looks like this:


The inner (smallest) square is drawn using Cairo and is missing on all platforms other than Windows. Window resizing and zooming (ctrl/+/-/0) works well.

I could probably figure this out myself for X11 and even on macOS, but I don't think that I can do this easily for Wayland. Maybe we need accessor methods for the Cairo surface or Cairo context on Wayland.

Manolo and others, can you help to complete the method mentioned above?

Thanks you all for helping in advance.

A note to the implementation: since the FLTK library can be a stock FLTK build w/o direct Cairo support the user is responsible for b

The relevant code follows, just in case ...

// Create a Cairo context that can be used on an FLTK image surface.
// In this demo we assume that the surface is the "current surface" after
// 'Fl_Surface_Device::push_current(surf)' but it would be great if it
// worked without this assumption.
cairo_t *cairo_context(Fl_Image_Surface *surf) {
// Windows: proof of concept, tested, works well:
#if defined(_WIN32)
cairo_surface_t *cairo_surface = cairo_win32_surface_create((HDC)fl_gc);
cairo_t *cairo_context = cairo_create(cairo_surface);
return cairo_context;
#endif
// other platforms still need implementation ...
return NULL; // not yet implemented
} // cairo_context(Fl_Image_Surface *surf)

    As you can see the code for Windows is implemented, all other
    platforms are missing.

Thanks for reading so far and for any help, even if it's only for one missing platform.

Albrecht Schlosser

unread,
Mar 21, 2023, 3:33:23 PM3/21/23
to fltkc...@googlegroups.com
Sorry, I missed something and pushed "Send" too early.

On 3/21/23 20:20 Albrecht Schlosser wrote:
> The current status is that I have completed support to draw in windows
> and widgets, roughly equivalent to our current Cairo support. Users
> can use Cairo drawing in their own subclasses of windows and widgets
> using stock FLTK libraries.

I also implemented a new Fl_Cairo2_Window class which works similar to
Fl_Cairo_Window.

The entire implementation is in the header files, hence it works like an
additional "single header library" and doesn't make FLTK itself depend
on Cairo.

> I don't want to bother you with too many details, ...
>
> A note to the implementation: since the FLTK library can be a stock
> FLTK build w/o direct Cairo support [...]

... users are responsible for building their programs with Cairo, i.e.
users need to find Cairo headers and libs themselves. An exception is
Linux, particularly the Wayland build, where Cairo is included anyway.

I started working on a demo (CMake) project to show users how to build
their programs using the new Cairo features with a stock FLTK library -
it should even work with FLTK 1.3 if they only add the new Fl_Cairo2*
header files. However, this project is work in progress and not yet
included in my branch.

Therefore, building the demo programs with Cairo may be a small
challenge on some systems, particularly on macOS where several different
Cairo libs can be installed.

Manolo

unread,
Mar 22, 2023, 3:33:42 AM3/22/23
to fltk.coredev
Under Wayland, the adequate API already exists :
#include <FL/platform.H>
#ifdef FLTK_USE_WAYLAND
  if (fl_wl_display()) { // to avoid when the hybrid library is in x11 mode
      cairo_t *cr =  fl_wl_cairo();
      cairo_rectangle(cr, x, y, w, h); // for example
  }
#endif
returns the cairo_t* quantity that is used in all cairo fiunction calls.
This function can be called as soon as the image surface has been made current.

I also believe that in
#if defined(_WIN32)
cairo_surface_t *cairo_surface = cairo_win32_surface_create((HDC)fl_gc);
cairo_t *cairo_context = cairo_create(cairo_surface);
return cairo_context;
#endif

there's a missing statement. This one should go after the cairo_create() call :
  cairo_surface_destroy(cairo_surface);
because cairo objects are reference counted, and in your code the cairo_surface
finishes with a count set at 2, so when you'll call cairo_destroy() at the end of
the cairo_t object, the count will go to 1 and the cairo_surface object will
survive without access to it.

Manolo

unread,
Mar 22, 2023, 3:38:00 AM3/22/23
to fltk.coredev
Le mercredi 22 mars 2023 à 08:33:42 UTC+1, Manolo a écrit :
Under Wayland, the adequate API already exists :
#include <FL/platform.H>
#ifdef FLTK_USE_WAYLAND
  if (fl_wl_display()) { // to avoid when the hybrid library is in x11 mode
      cairo_t *cr =  fl_wl_cairo();
      cairo_rectangle(cr, x, y, w, h); // for example
  }
#endif
returns the cairo_t* quantity that is used in all cairo fiunction calls.
This function can be called as soon as the image surface has been made current.

I should have written
"This function can be called as soon as the target window or image surface has been made current."


Manolo

unread,
Mar 22, 2023, 4:01:47 AM3/22/23
to fltk.coredev
Under macOS, you should be good with this:

#include <FL/platform.H>
#if defined(__APPLE__) && !defined(FLTK_USE_X11)
#  include <ApplicationServices/ApplicationServices.h>
#  include <cairo-quartz.h>
      CGContextRef gc =  fl_mac_gc();
      int W = CGBitmapContextGetWidth(gc);
      int H = CGBitmapContextGetHeight(gc);
      cairo_surface_t *cairo_surface = cairo_quartz_surface_create_for_cg_context(gc, W, H);
      cairo_t *cairo_context = cairo_create(cairo_surface);
      cairo_surface_destroy(cairo_surface);
      return cairo_context;
#endif

Albrecht Schlosser

unread,
Mar 22, 2023, 9:07:05 AM3/22/23
to fltkc...@googlegroups.com
On 3/22/23 08:33 Manolo wrote:
Under Wayland, the adequate API already exists :
#include <FL/platform.H>
#ifdef FLTK_USE_WAYLAND
  if (fl_wl_display()) { // to avoid when the hybrid library is in x11 mode
      cairo_t *cr =  fl_wl_cairo();
      cairo_rectangle(cr, x, y, w, h); // for example
  }
#endif
returns the cairo_t* quantity that is used in all cairo fiunction calls.
This function can be called as soon as the [target window or] image surface has been made current.

Thanks. I knew that this worked with the current window but didn't know I could apply it to any surface.

I also believe that in
#if defined(_WIN32)
cairo_surface_t *cairo_surface = cairo_win32_surface_create((HDC)fl_gc);
cairo_t *cairo_context = cairo_create(cairo_surface);
return cairo_context;
#endif

there's a missing statement. This one should go after the cairo_create() call :
  cairo_surface_destroy(cairo_surface);
because cairo objects are reference counted, ...

Thanks for this hint. In my code of class Fl_Cairo2 I'm currently keeping a pointer to the surface and destroying the surface in the destructor. I'm not sure which way I'll choose in the final code.

Regarding your later reply: Thanks for the macOS code as well.

I'll try your code and update my branch accordingly.

Albrecht Schlosser

unread,
Mar 22, 2023, 1:33:54 PM3/22/23
to fltkc...@googlegroups.com
On 3/22/23 14:07 Albrecht Schlosser wrote:
On 3/22/23 08:33 Manolo wrote:
Under Wayland, the adequate API already exists :

[...]


I'll try your code and update my branch accordingly.

Done. The current demo program can draw with Cairo:

- on all supported platforms (in windows and/or widgets)
- on Fl_Image_Surface and Fl_Copy_Surface
- very likely on other FLTK surfaces as well (untested)

See the complete code: https://github.com/Albrecht-S/fltk/blob/Fl_Cairo2a/test/cairo2_image.cxx
(note that this branch will be deleted at some time and thus this link will probably become stale [1]).

@Manolo: Thank you very much for the code on several platforms.

BTW: I added cairo_surface_destroy() where applicable in this demo program.

I can now finish my work on the new Cairo support (Fl_Cairo2*).


[1] Relevant code as of now for reference (slightly edited):

// Create a Cairo context that can be used on an FLTK drawing surface.
// In this demo we assume that the surface is the "current surface" after
// 'Fl_Surface_Device::push_current(surf)'.
cairo_t *cairo_context(Fl_Widget_Surface *surf, int W, int H) {
cairo_surface_t *cairo_surface = NULL;
cairo_t *cairo_context = NULL;
// Windows
#if defined(_WIN32)
cairo_surface = cairo_win32_surface_create(fl_win32_gc());
cairo_context = cairo_create(cairo_surface);
cairo_surface_destroy(cairo_surface);
return cairo_context;
#endif
// Wayland
#ifdef FLTK_USE_WAYLAND
if (fl_wl_display()) { // to avoid when the hybrid library is in x11 mode
cairo_context = fl_wl_cairo();
return cairo_context;
}
#endif
// X11
#if defined(FLTK_USE_X11)
cairo_surface = cairo_xlib_surface_create(fl_display, fl_window, fl_visual->visual, W, H);
cairo_context = cairo_create(cairo_surface);
cairo_surface_destroy(cairo_surface);
return cairo_context;
#endif
// macOS
#if defined(__APPLE__) && !defined(FLTK_USE_X11)
#include <ApplicationServices/ApplicationServices.h>
#include <cairo-quartz.h>
CGContextRef gc = fl_mac_gc();
cairo_surface = cairo_quartz_surface_create_for_cg_context(gc, W, H);
cairo_context = cairo_create(cairo_surface);
cairo_surface_destroy(cairo_surface);
return cairo_context;
#endif
// remaining platforms (shouldn't be any)
return NULL;
} // cairo_context(Fl_Widget_Surface *surf)

  

Bill Spitzak

unread,
Mar 22, 2023, 4:07:00 PM3/22/23
to fltkc...@googlegroups.com
I think you can use fl_wl_cairo in the wayland case even if x11 is being used, it uses cairo to draw in all cases.

There is also the ability to compile X11 but use Cairo, not sure if that is still available outside of the wayland version.

--
You received this message because you are subscribed to the Google Groups "fltk.coredev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fltkcoredev...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fltkcoredev/37898953-e20b-a5de-e301-7626723e35fd%40online.de.

Albrecht Schlosser

unread,
Mar 22, 2023, 6:35:04 PM3/22/23
to fltkc...@googlegroups.com
On 3/22/23 21:06 Bill Spitzak wrote:
> I think you can use fl_wl_cairo in the wayland case even if x11 is
> being used, it uses cairo to draw in all cases.

Interesting, thanks. That might simplify things a bit. Need to check this...

> There is also the ability to compile X11 but use Cairo, not sure if
> that is still available outside of the wayland version.

Yes, it is, but that's not my point. My intention is to replace the
existing - IMHO ugly (for several reasons) - way to support Cairo in
user programs which needs to configure FLTK in a special way. The new
version shall work with any FLTK library, no matter how it was
configured. It targets users wishing to use Cairo for their own drawings
and not changing the FLTK library to use Cairo. The latter is also
currently not an option for Windows and macOS builds which should be
supported as well.

Reply all
Reply to author
Forward
0 new messages