Contextes graphiques
Mick
Membre
Bonjour à tous,
J'ai un petit soucis sur les graphicContexts. Voilà , j'ai compris qu'avant que la méthode draw d'une view soit appelée, le "current" graphic context est bidouillé pour que tout ce passe bien durant le tracé (pas de "fuite" à l'extérieur de la vue etc...)
Mon soucis : imaginons un controleur quelconque qui reçoit une info comme quoi une donnée a été changée. Ce controleur en informe une vue qui doit changer de place un truc graphique ou ajouter un truc. Comme je n'envoie pas le message setNeedsDisplay, le graphicContext n'est pas correct et c'est sur la fenêtre que ça dessine (je ne vois que ce qui "déborde")... Suis-je obligé de redessiner entièrement ma vue ? Y a-t-il moyen de ne dessiner que ce qui est nécessaire autrement que par un setNeedDisplayInRect: ? Comment paramétrer correctement le graphicContext pour cela ? (la doc de la classe ne laisse pas trop apparaà®tre de persepectives satisfaisantes...)
J'ai un petit soucis sur les graphicContexts. Voilà , j'ai compris qu'avant que la méthode draw d'une view soit appelée, le "current" graphic context est bidouillé pour que tout ce passe bien durant le tracé (pas de "fuite" à l'extérieur de la vue etc...)
Mon soucis : imaginons un controleur quelconque qui reçoit une info comme quoi une donnée a été changée. Ce controleur en informe une vue qui doit changer de place un truc graphique ou ajouter un truc. Comme je n'envoie pas le message setNeedsDisplay, le graphicContext n'est pas correct et c'est sur la fenêtre que ça dessine (je ne vois que ce qui "déborde")... Suis-je obligé de redessiner entièrement ma vue ? Y a-t-il moyen de ne dessiner que ce qui est nécessaire autrement que par un setNeedDisplayInRect: ? Comment paramétrer correctement le graphicContext pour cela ? (la doc de la classe ne laisse pas trop apparaà®tre de persepectives satisfaisantes...)
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Ceci dit rien n'empêche au controlleur de préparer/prémacher des structures de données afin que la vue puisse se réafficher efficacement.
En effet, imagine que tu dessines un rectangle rempli de vert, puis un cercle rempli d'un bleu semi-transparent à 40%, puis un autre rectangle par dessus le tout...
Et puis tout à coup ton controlleur ou autre qui gère les objets que tu présentes dans ta vue (j'imagine que tu as bien en tête le concept de MVC avec séparation entre les objets à manipuler (modèle) et leur représentation visuelle (vue) ?) décide que ton cercle voit son rayon changer ou sa couleur de remplissage modifiée...
Tu es bien obligé dans ce cas de redessiner tous les objets, au moins dans la zone correspondant au "rectangle englobant" contenant le cercle de plus grande taille (le nouveau cercle si son rayon a été agrandi, l'ancien cercle si la modification correspond à une diminution de rayon)...
Après tout le reste de la zone graphique qui n'aurait pas été modifiée tu peux la laissée inchangée, mais n'empêche qu'il ne suffit pas juste de redessiner le cercle (d'autant qu'il a une couleur de remplissage semi-transparente) puisque ce cercle et son remplissage impactent le rendu des autres objets dessinés en dessous ou au dessus.
Au final c'est donc bien "setNeedsDisplayInRect:" qu'il faut utiliser. Et utiliser le CGRect passé éventuellement pour cropper ton dessin ou ne dessiner que cette zone. Après à toi dans ton algo de dessin de déterminer pour chaque objet à dessiner si son "rectangle englobant" a une intersection / zone commune avec la zone à redessiner.
Pour cela, tu peux utiliser la fonction CGRectIntersect et autres fonctions C "CGRectXXX".
Par exemple si le CGRect passé à "drawInRect:" va de x=150 à x=200 et de y=180 à y=220, alors ne dessine que les objets dont au moins une partie est contenue dans cette zone : s'il y a un cercle de centre {220,200} mais de rayon 30, il s'étale donc de x=190 à x=250 et de y=170 à y=230, donc même si son centre n'est pas dans le rect dessiné, le "boundingRect" (rectangle englobant) de ce cercle a une intersection avec la zone dessinée
Effectivement, après réflexion, il paraà®t obligatoire d'envoyer le message setNeedsDisplay.
En fait j'avais en tête une autre structure : ma vue affiche des choses que j'aimerais considérer comme des "subViews". J'ai réussi pour des lignes verticales et horizontales (pour lecture graphique précise des coordonnées d'un point) => Je créais une NSBox utilisée en separateur. J'ajoutais à ma vue ces NSBox en tant que subView : lorsque la souris bouge, je supprime ces subviews et j'en recrée à la nouvelle position de la souris. Cela fonctionne bien. Comment puis-je faire de même avec des lignes obliques ? Dans l'état actuel des choses, je retrace entièrement la vue à chaque mouseMoved. N'y a-t-il pas mieux en terme de performance ? Avez-vous une idée pour avoir un affichage le plus fluide possible ?
La méthode -[setNeedsDisplayInRect:] permet de n'invalider qu'un partie de la vue. Les rectangles passés aux différents appels de cette méthode seront englobés par un rectangle passé au prochain appel de -[drawInRect:]. En pratique, cette technique est efficace si le rectangle englobant n'est pas trop grand => Bien adaptée pour des "marqueurs" horizontaux ou verticaux.
Tu traces une courbe, non ? Le tracé (en utilisant les NSBezierPath ou leurs équivalents Core Graphics) prend beaucoup de temps.
Tu devrais dessiner dans une bitmap en mémoire ("offscreen buffer") puis afficher cette bitmap à l'écran. Tu as plusieurs possibilités: NSBitmapImageRep (que je te déconseille, car c'est une classe assez imprévisible), ou utiliser directement Core Graphics (CGBitmapContext). Note que tu devras quand même prévoir le dessin directement dans la vue (sans passer par la bitmap) au cas où tu veux imprimer.
Ca a l'air difficile: ça l'est, mais pour obtenir de bonnes performances, il n'y a guère le choix.
Ok Céroce pour l'utilisation des subViews : c'est effectivement déconseillé.
Aurais-tu un exemple pour utiliser CGBitmapContext ?
En fait, j'ai vraiment besoin de performance lors d'un tracking de souris ou l'affichage doit se mettre à jour. Lors de ce tracking, je trace des lignes qui "se déplacent" selon la position de la souris. Dans l'état actuel des choses, je retrace l'ensemble de la vue à chaque fois (pas top).
Comment fonctionne ce "dessin" en mémoire ? as-tu un petit bout de code exemple ?
Je recherche parallèlement dans la doc...
Placer le dessin dans un "cache" (c'est bien à ça que sert la bitmap), n'apportera un gain que s'il est difficile de ne dessiner que la partie sale, et si les données d'entrée ne sont pas raffraichies trop souvent (puisqu'il faut redessiner dans la bitmap). Je ne pense pas que tu trouveras grand chose sur le net à ce propos, car cela devient très spécifique, et je n'ai pas non plus le temps de te pondre du code, mais l'idée en gros c'est:
- on crée une bitmap de la même taille (en pixels) que le graphique
- on dessine dans la bitmap lorsque les données d'entrée ont changé.
- quand il faut rafraà®chir la vue parce que la souris s'est déplacée, on dessine une portion de la bitmap dans le rectangle sale.
Si une valeur change, le déplacement du point peut être optimisé vu la petitesse du rectangle. Je vais essayer.
Le cas des lignes horizontales et verticales (si l'utilisateur est en mode "lecture de données") peut être optimisé aussi car les rect sales sont beaucoup plus petits.
Merci en tout cas de vos réponses, mais j'avais osé espérer que le dessin soit "facile" avec Cocoa. En fait, une simple vue qui affiche des lignes (droites et courbes) et des rectangles nécessite une usine à gaz si on veut optimiser. Je pensais vraiment (à tort) que l'utilisation des subviews était plus efficace, les développeurs d'Apple s'étant déjà chargé en écrivant le framework d'optimiser l'affichage de la vue en tenant compte de ses subviews et de la modification éventuelle de leur rect.
Apple nous laisse effectivement beaucoup de travail lorsqu'on fait quelque chose qui n'est pas commun à beaucoup d'applis; à vrai dire, c'est difficile d'imaginer des systèmes vraiment universels, mais je suis d'accord que le fait que la vue gère tout (dessin et événements) a tendance à rendre obèse le code des vues perso. Il faut bien travailler sur l'organisation des classes pour que la vue délègue son travail.
Ceci dit, nous disposons quand même du système graphique le plus puissant que j'aie pu voir. Compare avec ce que proposent les autres frameworks pour dessiner...