IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

GTK+ avec C & C++ Discussion :

Comment fonctionne gtk_widget_queue_draw_area()


Sujet :

GTK+ avec C & C++

  1. #1
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut Comment fonctionne gtk_widget_queue_draw_area()
    Bonjour à tous,

    Ma question aujourd'hui est juste alimentée par ma curiosité personnelle. J'utilise (abondement) gtk_widget_queue_draw_area() pour écrire des codes au cours desquels je fait bouger des choses (en cinématique) sur une drawing area. Ca marche très bien, mais je ne comprends pas (et suis épaté par) comment cette fonction gtk_widget_queue_draw_area() fonctionne algorithmiquement (je veux dire, comment a t-elle été codée par les développeurs de GTK).

    Prenons un exemple, et imaginons que j'ai une drawing area carré de 500 sur 500 pixels. Sur cette drawing area j'ai plusieurs centaines de lignes de code qui conduisent à y dessiner progressivement des choses (une série de pixels, de différentes couleurs, etc.). A un certain moment, dans une zone identifiée de - disons 10 sur 10 pixels - quelque chose change de place, et j’appelle donc gtk_widget_queue_draw_area() sur cette zone particulière pour la réactualiser. Comment cette fonction fait-elle pour redessiner correctement cette zone ? Comment "sait-elle" ce qui a changé ou pas ? Il n'est pas possible que l'ensemble de la chaine algorithmique qui ait abouti à ce qui est présent ou pas dans cette zone soit recalculée.

    Il y a quelque chose qu'il m'échappe ici (peut-être quelque chose de simple et évident?), et je suis preneur d'une explication.

    Encore une fois, juste pour assouvir ma curiosité.

    Cordialement, Eric.

  2. #2
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 303
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 303
    Points : 4 967
    Points
    4 967
    Billets dans le blog
    5
    Par défaut
    Bonjour.

    Voila le code source de la dite fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    void
    gtk_widget_queue_draw_area (GtkWidget *widget,
                                gint       x,
                                gint       y,
                                gint       width,
                                gint       height)
    {
      GdkRectangle rect;
      cairo_region_t *region;
      g_return_if_fail (GTK_IS_WIDGET (widget));
      g_return_if_fail (width >= 0);
      g_return_if_fail (height >= 0);
      if (width == 0 || height == 0)
        return;
      rect.x = x;
      rect.y = y;
      rect.width = width;
      rect.height = height;
      region = cairo_region_create_rectangle (&rect);
      gtk_widget_queue_draw_region (widget, region);
      cairo_region_destroy (region);
    }
    Comme tu peux le voir elle crée une région puis appelle gtk_widget_queue_draw_region ();. Cela à pour effet d'invalider cette dite région. Lors de l'appel à "expose-event" dans la boucle principale gtk_main(); toutes les régions déclarées invalides sont redessinées.

    Dans les faits ta fonction callback attachée au signal "draw" est appelée à ce moment là. Elle va naturellement calculer toute l'image mais seule la région invalide sera actualisée à l'écran.

  3. #3
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par gerald3d Voir le message
    Comme tu peux le voir elle crée une région puis appelle gtk_widget_queue_draw_region ();. Cela à pour effet d'invalider cette dite région. Lors de l'appel à "expose-event" dans la boucle principale gtk_main(); toutes les régions déclarées invalides sont redessinées.

    Dans les faits ta fonction callback attachée au signal "draw" est appelée à ce moment là. Elle va naturellement calculer toute l'image mais seule la région invalide sera actualisée à l'écran.
    C'est ce que je pensais, mais j'ai du mal à imaginer que toute l'image soit recalculée. La drawing area peut être très grande, et la chaine de calcul très longue. Ca prendrait à chaque fois de longues minutes de calcul pour savoir ce qui a changé ou non. Par ailleurs, si tout était recalculé, le temps d’exécution de gtk_widget_queue_draw_area() serait grossièrement le même si je demande de réactualiser une petite zone ou l'ensemble de la drawing area (sauf le temps de traçage qui est infime par rapport au temps de calcul qui y aboutie). Il y a quelque chose de pas clair (pour moi en tout cas).

    Eric.

  4. #4
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 303
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 303
    Points : 4 967
    Points
    4 967
    Billets dans le blog
    5
    Par défaut
    Voila un petit exemple qui te montre bien que le callback associé au signal "draw" est appelé à chaque fois.

    C'est d'ailleurs pour cette raison qu'il est déconseillé de dessiner directement dans ce callback. Il vaut mieux dessiner dans une surface en dehors du callback. Le callback ne sert alors que pour afficher cette surface.

    Code exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <stdlib.h>
    #include <gtk/gtk.h>
     
     
    gboolean drawing_draw_CB (GtkWidget *drawing, cairo_t *cr, gpointer userdata)
    {
      g_print ("appel du callback drawing_draw_CB ();\n");
      return FALSE;
    }
     
    void button_clicked_CB (GtkButton *button, GtkWidget *drawing)
    {
      g_print ("appel du callback button_clicked_CB ();\n");
      gtk_widget_queue_draw_area (drawing, 10, 10, 50, 50);
    }
     
    gint
    main (gint argc, gchar *argv[])
    {
      GtkWidget *window = NULL;
      GtkWidget *box = NULL;
      GtkWidget *drawing = NULL;
      GtkWidget *button = NULL;
     
      /* init gtk */
      gtk_init(&argc, &argv);
     
      /* Création de la fenêtre principale */
      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
      box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
      gtk_container_add (GTK_CONTAINER (window), box);
     
      /* Création du drawingarea */
      drawing = gtk_drawing_area_new ();
      gtk_widget_set_size_request (drawing, 200, 200);
      g_signal_connect (G_OBJECT (drawing), "draw", G_CALLBACK (drawing_draw_CB), NULL);
      gtk_box_pack_start (GTK_BOX (box), drawing, FALSE, FALSE, 4);
     
      /* Création d'un bouton. À chaque clique on lance gtk_widget_queue_draw_area ();
       * sur le drawing */
      button = gtk_button_new_with_label ("Active queue_draw");
      g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (button_clicked_CB), drawing);
      gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 4);
     
       /* Signaux */
      g_signal_connect (G_OBJECT(window) , "destroy", G_CALLBACK (gtk_main_quit) , NULL);
     
      /* Affichage de la fenêtre */
      gtk_widget_show_all (window);
     
      gtk_main ();
     
      return EXIT_SUCCESS;
    }
    P.S. : gtk_widget_queue_draw(); appelle gtk_widget_queue_draw_area(); pour invalider toute l'image.

  5. #5
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut
    Oui, merci. J'ai bien compris (je crois). Mais je trouve étonnant que - donc - l'ensemble de la chaine de calcul (i.e., donc nécessairement sur l'ensemble de la drawing area) soit recalculée à chaque fois, y compris pour réactualiser une petite zone seulement. En plus du fait que je ne comprenne pas comment GTK fait ceci (en stockant toute la chaine de calcul??), je continue à penser que ça devrait prendre un temps énorme si cette chaine de calcul est longue et compliquée (comme c'est le cas dans mes codes), ce qui n'est pas le cas.

    On peut arréter cette discussion ici, si nécessaire, mais ça continue à ne pas être clair pour moi.

    Eric.

  6. #6
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    De ce que j'en sais:

    - tu invalides une zone. Si plusieurs événements d'invalidation d'une zone de l'écran sont dans la boucle d'événements, GTK+ peut les fusionner en un seul d'une zone plus grande si cela a du sens.
    - GTK appelle la callback associée au signal "draw". La zone que tu as invalidée est indiquée comme zone de clipping dans le contexte cairo.
    - tu dessines ce que tu veux où tu veux. C'est à toi de regarder quelles zones sont invalidées (et ont donc besoin d'être redessinées) si tu veux optimiser ton temps de rendu. Ainsi, peut être que tu sais que certains des objects que tu dessines se trouvent hors de la zone de clipping, et tu peux donc par un simple test ignorer tout le code qui permet de dessiner ces objets. le fait qu'il n'y a que la zone de clipping qui sera mise à jour permet à cairo de faire des optimisations : pas besoin de tracer et redessiner ce qui n'est pas visible.
    - une fois tes opérations de dessin effectuées, GTK+ demande à cairo d'appliquer la nouvelle image sur l'ancienne, par blitting. Ce blitting se fera uniquement sur la zone de clipping, il n'y a donc qu'elle qui sera modifiée.
    - une couche de double buffering ? (c'est le cas pour à peu près touts les widgets, mais je ne sais plus si GtkDrawingArea est une exception)
    - Il faut ensuite envoyer la scène à la carte graphique (et le hardware ça prend aussi du temps)

  7. #7
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut
    Ok, donc tout est recalculé effectivement, en tout cas c'est ce que je comprends. Et du coup -si le code n'est pas optimisé pour ne recalculer que ce qui est nécessaire - alors invalider une petit zone ou une grande devrait prendre sensiblement le même temps, ce qui n'est pas le cas dans les essais que j'ai fait, loin s'en faut. Ceci reste une énigme pour moi.

    Par ailleurs, il y a un autre point qui m'échappe. Dans mon cas, la chaine de calcul qui conduit à construire ce qui apparait dans la drawing area est issu de tirages aléatoires en abondance. Je ne vois donc pas comment tout peut être recalculé à chaque fois, ou bien GTK stocke l'ensemble - pixel par pixel et au fur et à mesure - de ce qui apparait, ce qui est également dur à croire.

    Dans tous les cas, cette discussion n'est que pour ma curiosité personnelle. Mes codes tournent très bien de toute façon.

    Merci pour vos réponses.

    Eric.

  8. #8
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Ce ne sont peut être pas tes tirage aléatoires qui prennent du temps, mais plutôt la mise à jour des buffers, qui dépendent de la taille des zones modifiées.

    Mettre à jour une petite zone ou une grande a tout de même une importance, car Cairo a normalement des optimisations pour ignorer tout ce que tu tentes de modifier en dehors de la zone de clipping.
    Donc même si tu appelles tes fonctions de dessins, cairo ne va rien faire si tu tentes de dessiner en dehors de cette zone, économisant du temps processeur.

    Je te conseille de lire cet article du blog GTK+ pour plus de détails:
    https://blog.gtk.org/2016/06/15/drawing-in-gtk/

  9. #9
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par liberforce Voir le message
    Ce ne sont peut être pas tes tirage aléatoires qui prennent du temps, mais plutôt la mise à jour des buffers, qui dépendent de la taille des zones modifiées.

    Mettre à jour une petite zone ou une grande a tout de même une importance, car Cairo a normalement des optimisations pour ignorer tout ce que tu tentes de modifier en dehors de la zone de clipping.
    Donc même si tu appelles tes fonctions de dessins, cairo ne va rien faire si tu tentes de dessiner en dehors de cette zone, économisant du temps processeur.

    Je te conseille de lire cet article du blog GTK+ pour plus de détails:
    https://blog.gtk.org/2016/06/15/drawing-in-gtk/
    Ok, merci. Je vais regarder ceci. Je reste épaté par le fait que toute la chaine de calcul est stockée quelque part, et par optimisation les chose coulent correctement.

    Merci encore, Eric.

  10. #10
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    La chaîne de calcul n'est pas "stockée". C'est le résultat qui l'est. Tu as beau redessiner ce que tu veux, il n'y a que ce qu'il y a dans la zone de clipping qui sera mis à jour.

    Fais des recherches sur le double buffering:
    https://en.wikipedia.org/wiki/Multip...puter_graphics

  11. #11
    Membre régulier
    Homme Profil pro
    chercheur
    Inscrit en
    Décembre 2012
    Messages
    195
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : chercheur

    Informations forums :
    Inscription : Décembre 2012
    Messages : 195
    Points : 84
    Points
    84
    Par défaut
    Citation Envoyé par liberforce Voir le message
    La chaîne de calcul n'est pas "stockée". C'est le résult qui l'est. Tu as beau redessiner ce que tu veux, il n'y a que ce qui a dans la zone de clipping qui sera mis à jour.

    Fais des recherches sur le double buffering:
    https://en.wikipedia.org/wiki/Multip...puter_graphics
    Très clair ! Merci ! Eric.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [JSF] comment fonctionne <h:message> ?
    Par anitshka dans le forum JSF
    Réponses: 5
    Dernier message: 29/06/2005, 18h36
  2. Comment fonctionne TXmlDocumment ????
    Par almisuifre dans le forum C++Builder
    Réponses: 8
    Dernier message: 18/02/2005, 13h54
  3. comment fonctionne une interface graphique???
    Par elekis dans le forum Langages de programmation
    Réponses: 2
    Dernier message: 28/10/2004, 00h10
  4. Comment fonctionne le ClassExplorer ?
    Par borisd dans le forum C++Builder
    Réponses: 7
    Dernier message: 30/09/2004, 18h44
  5. Comment fonctionne le CVS ?
    Par mathieu dans le forum CVS
    Réponses: 6
    Dernier message: 23/03/2004, 12h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo