Search code examples
c++gtkcairogtkmm

Get a mock Cairo::Context to test conditions on the path


this is a follow up on this post, in which I asked about checking some condition on the border of a shape drawn using Cairomm in a Gtk::DrawingArea derived widget. In my case, I have a void drawBorder(const Cairo::RefPtr<Cairo::Context>& p_context) method which is virtual and is overriden to specify the shape's border. For example, if I wanted a circle, I could provide the following implementation:

void drawBorder(const Cairo::RefPtr<Cairo::Context>& p_context)
{
    const Gtk::Allocation allocation{get_allocation()};

    const int width{allocation.get_width()};
    const int height{allocation.get_height()};
    const int smallestDimension{std::min(width, height)};

    const int xCenter{width / 2};
    const int yCenter{height / 2};

    p_context->arc(xCenter,
                   yCenter,
                   smallestDimension / 2.5,
                   0.0,
                   2.0 * M_PI);
}

I would like to use this method to check my condition on the border curve, as suggested in the answer:

So, you would somehow get a cairo context (cairo_t in C), create your shape there (with line_to, curve_to, arc etc). Then you do not call fill or stroke, but instead cairo_copy_path_flat.

So far, I am unable to get a usable Cairo::Context mock to perform the check. I don't need to draw anything to perform my check, I only need to get the underlying path and work on it.

So far, I have tried:

  1. passing nullptr as the Cairo::Surface (which of course failed);
  2. get an equivalent surface to my widget.

But it failed. This: gdk_window_create_similar_surface looked promising, but I have not found an equivalent for widgets.

How could one go about getting a minimal mock context to perform such checks? This would also help me very much in my unit testing, later on.


So far I got this code:

bool isTheBorderASimpleAndClosedCurve()
{
    const Gtk::Allocation allocation{get_allocation()};

    Glib::RefPtr<Gdk::Window> widgetWindow{get_window()};

    Cairo::RefPtr<Cairo::Surface> widgetSurface{widgetWindow->create_similar_surface(Cairo::Content::CONTENT_COLOR_ALPHA,
                                                                                     allocation.get_width(),                                                                            allocation.get_height()) };

    Cairo::Context nakedContext{cairo_create(widgetSurface->cobj())};
    const Cairo::RefPtr<Cairo::Context> context{&nakedContext};

    drawBorder(context);

    // Would like to get the path and test my condition here...!
}

It compiles and links, but at runtime I get a segfault with this message and a bunch of garbage:

double free or corruption (out): 0x00007ffc0401c740

Solution

  • Just create a cairo image surface with size 0x0 and create a context for that.

    Cairo::RefPtr<Cairo::Surface> surface = Cairo::ImageSurface::create(
        Cairo::Format::FORMAT_ARGB32, 0, 0);
    Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create(surface);
    

    Since the surface is not used for anything, it does not matter which size it has.

    (Side note: According to the API docs that Google gave me, the constructor of Context wants a cairo_t* as argument, not a Cairo::Context*; this might explain the crash that you are seeing)