Очень простая плавная анимация с помощью GTK+2 и Cairo

Я использую gtk+2.0 и cairo. Я написал простую программу, которая открывает окно и перемещает точку. Простой бильярд, только движение по горизонтали. Это просто тест.

Проблема в том, что вроде бы не все так гладко, и я бы спросил, нет ли тут какого-нибудь gtk или cairo эксперта, который мог бы проверить ошибки в коде.

Спасибо.

#include <gtk/gtk.h>
#include <math.h>

gboolean timeout(gpointer data)
{
    GtkWidget *widget = GTK_WIDGET(data);
    if (!widget->window) return TRUE;
    gtk_widget_queue_draw(widget);
}

gboolean configure(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
{ 
    return TRUE;
}

double px = 10;
double vx = **2**;

gboolean expose(GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
    cairo_t *cr = gdk_cairo_create(widget->window);
    cairo_rectangle(cr, event->area.x, event->area.y, event->area.width, event->area.height);
    cairo_clip(cr);

    cairo_set_source_rgb(cr,1,0,0);
    cairo_arc(cr, px, 100, 6, 0, 2*M_PI);
    cairo_fill(cr);
    cairo_set_source_rgb(cr,0,0,0);
    cairo_destroy(cr);

    if (px <= 3 || px >= 200-3) vx = -vx;
    px += vx;
    return FALSE;
}

int main(int argc, char *argv[])
{
    char *title = "Test";
    int sx = 200;
    int sy = 200;

    gtk_init(NULL,NULL);

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW(window),title);
    gtk_container_set_border_width(GTK_CONTAINER (window), 2);

    g_signal_connect(window, "destroy",G_CALLBACK(gtk_main_quit),&window);
    GtkWidget *drawing_area = gtk_drawing_area_new();
    //g_signal_connect(window,"key-press-event",G_CALLBACK(on_key_press),NULL);

    const GdkColor bianco = { 0, 0xffff, 0xffff, 0xffff };
    const GdkColor nero = { 0, 0x0, 0x0, 0x0 };
    gtk_widget_modify_bg(drawing_area, GTK_STATE_NORMAL, &bianco);

    gtk_widget_set_size_request(drawing_area, sx, sy);

    g_signal_connect(drawing_area,"configure_event",G_CALLBACK(configure), NULL);
    g_signal_connect(drawing_area,"expose_event",G_CALLBACK(expose),NULL);

    gtk_container_add(GTK_CONTAINER(window), drawing_area);
    gtk_widget_show(drawing_area);

    g_timeout_add(**10**, timeout, window);

    if (!GTK_WIDGET_VISIBLE (window))
        gtk_widget_show_all(window);
    else {
        gtk_widget_destroy (window);
        window = NULL;
    }

    gtk_main();
    return 0;
}

person Antonio Caruso    schedule 04.04.2014    source источник
comment
Нужно ли мне добавить контекст cairo и рендеринг в нем... а затем выполнить операцию копирования в конце события разоблачения в виджете рисования?   -  person Antonio Caruso    schedule 04.04.2014
comment
Возможно, вам придется позвонить cairo_flush(); а зачем создавать и уничтожать при каждом разоблачении? Кажется, лучше создать при запуске и уничтожить при выходе.   -  person luser droog    schedule 04.04.2014


Ответы (1)


Не так гладко? Ну, с периодом 100 мс вы рисуете в лучшем случае 10 кадров в секунду, неудивительно, что это не гладко... Вы должны стремиться к 60 кадрам в секунду. Кроме того, вы делаете недействительным весь виджет, вызывая gtk_widget_queue_draw, поэтому ваш вызов cairo_clip в основном бесполезен, так как область отсечения — это весь виджет. Вместо этого вы должны вызвать gtk_widget_queue_draw_area, чтобы ваша область отсечения была полезной, и определить область, сохраняя запись анимации в кадрах n и n-1, поэтому вы перерисовываете обе области. чтобы предыдущий кадр не был удален.

В блоге Оуэна Тейлора есть много интересного о восприятии анимации, начиная с этого поста и заканчивая более поздними:

http://blog.fishsoup.net/2009/05/28/frames-not-idles/

Дай посмотреть на все посты с цифрами, это золотая жила.

person liberforce    schedule 04.04.2014