0

Here's what I tried:

#include <gtk/gtk.h>

static GtkWidget *window, *drawing_area;

static gboolean drawCallback(GtkWidget *widget, cairo_t *cr, gpointer arg) {

    cairo_set_source_rgb(cr, 1.0, 0, 0);
    cairo_arc(cr, 200.0, 150.0, 50.0, 0, 2*G_PI);
    cairo_fill(cr);
}

static gboolean savePNG(gpointer arg) {

    cairo_surface_t *surface = cairo_image_surface_create(
        CAIRO_FORMAT_RGB24, 400, 300
    );
    cairo_t *cr = cairo_create(surface);

    gdk_cairo_set_source_window(
        cr,
        gtk_widget_get_window(GTK_WIDGET(window)),
        0, 0
    );

    printf("cairo_status(cr): %s\ncairo_surface_status(surface): %s\n",
        cairo_status_to_string(cairo_status(cr)),
        cairo_status_to_string(cairo_surface_status(surface)));

    cairo_surface_write_to_png(surface, "test.png");
}

static void app_activate(GtkApplication *app, gpointer user_data) {

    window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Sandbox");
    gtk_widget_set_size_request(GTK_WIDGET(window), 400, 300);
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);

    drawing_area = gtk_drawing_area_new();
    gtk_container_add(GTK_CONTAINER(window), drawing_area);
    g_signal_connect(G_OBJECT(drawing_area), "draw", G_CALLBACK(drawCallback), NULL);

    gtk_widget_show_all(window);
    gtk_window_move(GTK_WINDOW(window), 10, 10);
}

int main(int argc, char **argv) {

    GtkApplication *app;
    int status;

    app = gtk_application_new("the.application.id", G_APPLICATION_FLAGS_NONE);
    g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);

    g_timeout_add(1000, savePNG, NULL);

    status = g_application_run(G_APPLICATION(app), argc, argv);

    g_object_unref(app);
    return status;
}

The window shows a red circle, but the test.png file that is created contains an all black image.

Here's the output:

cairo_status(cr): no error has occurred
cairo_surface_status(surface): no error has occurred

This answer suggested the pattern that I used.

Jason
  • 2,725
  • 2
  • 14
  • 22

1 Answers1

1

You forgot to paint anything into your surface before saving.

static gboolean savePNG(gpointer arg)
{
  cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 300, 200);  // <<= Reduced size to get only a part of the window.
  cairo_t *cr = cairo_create(surface);

  cairo_translate(cr, -50.0, -50.0);  // <<= Move the part we want to copy from source into position....

  gdk_cairo_set_source_window(
      cr,
      gtk_widget_get_window(GTK_WIDGET(window)),
      0, 0
  );
  cairo_paint(cr);  // <<= DO IT! ;)
  cairo_surface_write_to_png(surface, "test.png");
}

EDIT:

I updated the code to only copy a part of the window to the file. You need to move the source to the new starting point of your desired part of the window. Of course then your surface also needs to be smaller.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
  • As an alternative, you can use `gtk_widget_draw()` to avoid the need to fiddle with GDK surfaces. Be sure to read the documentation *carefully* if you do so. – andlabs Jun 08 '17 at 15:18
  • @Gerhardh Thanks. Now, how do I get only part of the window? I tried calling [`gdk_cairo_get_clip_rectangle`](https://developer.gnome.org/gdk3/stable/gdk3-Cairo-Interaction.html#gdk-cairo-get-clip-rectangle) on the `cairo_t`, but it had no effect. – Jason Jun 08 '17 at 18:03
  • Hint: you want to *set* the clip, not *get* it. Look up `cairo_clip()`. – andlabs Jun 08 '17 at 18:56
  • 1
    @Jason, You need to "move" (`cairo_translate`) the source into the correct position before drawing into the new surface. I have updated the code. – Gerhardh Jun 09 '17 at 06:01
  • @andlabs Wouldn't `cairo_clip` keep the surrounding parts of the surface empty? My understanding is that the parts that are not copied should be cropped from the file. – Gerhardh Jun 09 '17 at 06:05
  • @Gerhardh Fantastic! You saved me a lot of frustration of digging through the labyrinth of documentation. There's not a lot of examples out there for GTK3 yet. – Jason Jun 09 '17 at 06:40
  • No, `cairo_clip()` would keep surrounding parts of the *context* empty, so the parts are indeed missing from the output file. But I suppose it only applies if you're going to use `gtk_widget_draw()` instead. Also note: with `gtk_widget_draw()`, the `cairo_translate()` would be a `cairo_move_to()`, as GTK+ will place the upper-left corner at the current point. – andlabs Jun 09 '17 at 12:55
  • 1
    If I replace `cairo_translate` by `cairo_rectangle(cr, 50,50,300,200); cairo_clip(cr);` the upper left part of the PNG file is black and the copied image is cropped. As the auther did not use `gtk_widget_draw`, I didn't try any combination with that command. Maybe you want to present another answer containing a working alternative using your suggested commands. – Gerhardh Jun 09 '17 at 13:06
  • @Jason, I know what you mean. Tutorials and examples are not really easy to find for GTK3. And it is not really straight forward, especially when things start to get mixed up with `gtk_*`, `gdk_*` and `cairo_*` functions. – Gerhardh Jun 10 '17 at 21:48
  • Actually, there are lots of tutorials in the GTK+ documentation itself *and* an entire program full of examples called `gtk3-demo` that each show you their source in-program, as well as a whole bunch more in the GTK+ source code. And certain language bindings, like the Python ones, are extremely well-documented. Just remember that cairo is an entirely separate library with its own set of tutorials. – andlabs Jun 10 '17 at 21:56