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

Lazarus Pascal Discussion :

Besoin d'éclaircissements sur le TImage (suite lecture Wiki)


Sujet :

Lazarus Pascal

  1. #1
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut Besoin d'éclaircissements sur le TImage (suite lecture Wiki)
    Bonsoir,

    La lecture du wiki m'ayant plus embrouillé qu'autre chose, je viens vers vous pour essayer de dépatouiller tout ça.

    Premier point : sur cette page on peut lire :
    En dehors de ["l'événement" je suppose] Image1.OnPaint, Image1.Canvas pointe vers Image1.Picture.Bitmap.Canvas
    , qui me fait penser qu'on pourrait utiliser indifféremment l'une ou l'autre approche un peu partout, genre dans le clic d'un bouton ou autre.
    Le souci c'est qu'avec une procédure MakeGradient (trouvée dans le sous-forum voisin et qui me donne toute satisfaction) ou avec GraphUtil.DrawVerticalGradient (qui ressemble beaucoup [en termes de résultat] à la précédente), l'utilisation de Image1.Canvas fonctionne, mais pas celle de Image1.Picture.Bitmap.Canvas, qui n'affiche rien...

    Que veut donc dire la phrase que j'ai quotée ?

    Second point : plus bas sur la page on lit :
    Peindre sur l'aire visuelle volatile de TImage

    Vous pouvez seulement peindre sur ce secteur pendant OnPaint. OnPaint est éventuellement appellé automatiquement par la LCL quand le secteur a été invalidé. Vous pouvez invalidé manuellement le secteur avec Image1.Invalidate. Ceci n'appellera pas immédiatement OnPaint et vous pouvez appeler Invalidate autant de fois que vous le voulez.
    et après un exemple :
    Dessiner sur l'événement OnPaint

    Dans ce cas tout le dessin doit être fait sur l'événement OnPaint de la fiche. Il ne reste pas sur le tampon, comme sur la TImage.
    Admettons, mais alors, à quoi sert concrètement dans la vraie vie cet événement OnPaint ?

    Parce que mon drame est là : il y a plein de propriétés, plein d'événements, mais rien pour nous dire quoi utiliser quand et comment.

    Par exemple le OnPaint c'est typique : il va falloir appeler (depuis où ?) Image1.Invalidate pour redessiner, ok, mais quand ? Il me faut détecter que ma fiche a été recouverte puis découverte pour invalider l'image ?

    Si ça vous paraît idiot comme questions, je précise que tout ça découle d'un fait très simple : je me suis amusé à faire changer les couleurs composant un dégradé affiché par/sur/dans un TImage, ça fonctionne très bien sous Linux et pas du tout sous Windows, désolé Jon, et donc, le truc sur lequel je n'aurais pas dû passer plus d'une trentaine de minutes, ben ça fait des heures que j'y suis et y a plus rien qui avance...

    Et à partir de là, c'est comm' d'hab', embrouilles, sacs de nœuds et recherches à l'infini sur le web. Et les exemples du wiki, ben, c'est plus ce que c'était :

    Nom : tests_wiki.jpg
Affichages : 596
Taille : 69,3 Ko
    Ce que vous voyez sur cette image, c'est à gauche l'absence de toute coloration dans Image1 (positionnée en haut à gauche sur la fiche), pas de couleur verte mais un joli pavé noir dans Image2, pas de ligne bleue sur la fiche tant qu'elle n'a pas été recouverte puis découverte, bref le seul truc valable c'est le FormPaint et sa ligne pourpre.
    Ces 4 codes viennent du wiki, tels quels à part les couleurs pour différencier les résultats.
    Je vous laisse apprécier la catastrophe...

    Merci de vos éclairages perspicaces !
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  2. #2
    Expert confirmé
    Avatar de BeanzMaster
    Homme Profil pro
    Amateur Passionné
    Inscrit en
    Septembre 2015
    Messages
    1 899
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Amateur Passionné
    Secteur : Tourisme - Loisirs

    Informations forums :
    Inscription : Septembre 2015
    Messages : 1 899
    Points : 4 346
    Points
    4 346
    Billets dans le blog
    2
    Par défaut
    Bonsoir,

    Je viens de parcourir le wiki, il est vrai que la traduction est coucicouça. Bref

    Note : À l'intérieur de Image1.OnPaint Image1.Canvas pointe vers l'aire visible volatile. En dehors de Image1.OnPaint l'Image1.Canvas pointe vers Image1.Picture.Bitmap.Canvas.
    Ce que je comprend ici est que que le TImage a son propre canvas, et que lorsqu'on utilise l'evenement OnPaint et si on utilise Image1.Canvas Image1.Canvas pointe vers son propre canvas. En dehors de Image1.OnPaint Image1.Canvas pointe vers
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Image1.Picture.Bitmap.Canvas
    Dessiner sur l'évênement OnPaint
    Dans ce cas tout le dessin doit être fait sur l'évênement OnPaint de la fiche. Il ne reste pas sur le tampon, comme sur la TImage.
    En gros pour rafraichir l'affichage du Bitmap que contient le TImage il faut le faire via l'evenement Onpaint de la fiche
    Image1.Invalidate sert juste a repreindre la "surface" du TImage.

    cf l'exemple du contrôle à soi qui se dessine lui-même

    En gros si je j'ai bien compris on peut très bien avoir un TImage de taille XY avec son Picture.Bitmap de taille X1Y1 et que si on veux avoir acces au Picture.Bitmap il faudrait l'appeler directement avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Image1.Picture.Bitmap.Canvas
    et non avec mais ce n'est pas possible
    Note: Ne pas employer ceci pendant OnPaint.
    Il semblerai que dans l'évênement Image1.OnPaint "Picture" ou "Bitmap" est protégé.

    C'est très tordu comme fonctionnement, mais logique afin que l'on ne puisse pas y avoir accès. Mais du coup lorsque l'on dessine sur le Image1.Canvas dans Image1.OnPaint est-ce que les modifications sont également présente dans le Image1.Picture.Bitmap ?

    Dans tous les cas les Bitmaps, Canvas sous lazarus c'est la misère, il existe des bugs, farfelus sur des choses qui normalement devraient fonctionner. J'avais eu pas mal de petits problèmes, quand j'ai commencé mes petits composants. (Et pour info sous Delphi ça fonctionne )
    • "L'Homme devrait mettre autant d'ardeur à simplifier sa vie qu'il met à la compliquer" - Henri Bergson
    • "Bien des livres auraient été plus clairs s'ils n'avaient pas voulu être si clairs" - Emmanuel Kant
    • "La simplicité est la sophistication suprême" - Léonard De Vinci
    • "Ce qui est facile à comprendre ou à faire pour toi, ne l'est pas forcément pour l'autre." - Mon pèrei

    Mes projets sur Github - Blog - Site DVP

  3. #3
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Bonsoir,

    et merci d'avoir amorcé le débat.

    Citation Envoyé par BeanzMaster Voir le message
    Je viens de parcourir le wiki, il est vrai que la traduction est coucicouça. Bref
    Ah, tu as remarqué, toi aussi
    Je n'ai pas voulu relever ce point, mais il est vrai que le texte est parfois dur à comprendre.

    Sinon, pour en revenir au cœur du problème,
    Citation Envoyé par BeanzMaster Voir le message
    [...] lorsqu'on utilise l'evenement OnPaint [...]
    je ne sais toujours pas pourquoi on utilise cet événement...
    Quand je dois attaquer la bagnole et qu'un boulon de 14 se présente, je prends une clé de 14 et pas un tournevis plat ou un chalumeau ; hé bien là c'est pareil mais à l'envers : on me dit d'utiliser la clé de 14 mais je ne vois pas le boulon ! Quelqu'un pourrait me dire où il se cache ?
    On me dit d'utiliser l'événement OnPaint, mais pourquoi ? Pour afficher quelque chose dans un TImage ? D'habitude je remplis d'abord un TBitmap puis je fais MonImage.Picture.Assign(LeBitmapRempli); et hop !, ça le fait. Alors le OnPaint, je ne vois pas à quoi il sert...

    Regardez :
    J'ai rajouté un TImage en bas à gauche de la fiche, j'ai rajouté quelques lignes (bon, ok, j'ai oublié le tmpbmp.Free; mais c'est juste un brouillon et je ne refais pas la copie d'écran pour ça ) et voilà un magnifique carré fuchsia. Alors le OnPaint...
    Nom : méthode_qui_fonctionne.jpg
Affichages : 535
Taille : 56,7 Ko

    Citation Envoyé par BeanzMaster Voir le message
    En gros pour rafraichir l'affichage du Bitmap que contient le TImage il faut le faire via l'evenement Onpaint de la fiche
    Image1.Invalidate sert juste a repeindre la "surface" du TImage.
    Voilà ! Je n'ai rien compris parce que vue la réponse au point précédent, qui fonctionne très bien ainsi, je repose la question : OnPaint c'est pour faire quoi ? Doublon (mal foutu -- vous avez vu mon illustration dans le post d'origine ? Sans commentaires...) du .Picture.Assign ?
    J'ai presque l'impression que ce OnPaint serait une survivance des débuts de la POO, une espèce de fonction bas-niveau qui subsiste là par compatibilité avec les vieux bouts de code, mais depuis on a inventé le .Picture.Assign et ma foi, ça le fait mieux que l'exemple du wiki...

    Citation Envoyé par BeanzMaster Voir le message
    cf l'exemple du contrôle à soi qui se dessine lui-même
    Oui, je l'ai vu mais je n'ai pas compris à quoi il sert d'autant plus qu'il n'y a rien dans le texte qui l'explique, et rien non plus qui montre comment l'utiliser (dans quel contexte, pourquoi, etc.).

    Allez, Thierry, jipépaté et les autres, au plaisir de vous lire...
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  4. #4
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Bonjour,

    Une bonne nouvelle dans ce monde de brutes, et la preuve que la nuit porte conseil : comme dit hier, l'affichage doux et velouté de mes dégradés était parfait sous Nux et tout pourri plein de flickering sous XP
    La solution fut de passer la propriété DoubleBuffered du parent direct du TImage (un TabSheet du PageControl) à True et voilà !

    Le TImage n'a pas cette propriété (), et il me semblait que passer celle de la MainForm était suffisant (souvenir de Delphi ? Je la positionne systématiquement à partir du moment où j'utilise des images et des choses comme ça) mais non...
    Ouf, je vais pouvoir déployer mon truc,

    Quant à l'explication de l'utilisation du OnPaint, ne m'oubliez pas !

    Bonne journée, bonne semaine,
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  5. #5
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    Salut JPT !
    Voici ce que je comprends :
    Un TImage est fondamentalement destiné à afficher des images, chargées dans un Bitmap qui dispose d'un canevas (pour faire du dessin, utiliser un TPaintBox).
    Le TImage dispose de son propre canevas, qui sert à l'affichage.
    Quand c'est nécessaire, le TImage va rafraîchir son image à partir du Bitmap et "oublier" ce qu'on aurait pu ajouter par code comme dessins superposés à ce Bitmap.
    L'événement OnPaint est utilisable pour nous permettre de redessiner ce qu'il faut ajouter et qui aurait été "oublié" lors d'un rafraîchissement image. C'est donc là qu'il faut loger notre code de dessin sur le canevas du TImage.

    Il y a eu récemment une discussion au sujet de la différence entre ces deux canevas, que j'ai la flemme de chercher !
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  6. #6
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Coucou, Yves !

    Bienvenue au club,
    Citation Envoyé par tourlourou Voir le message
    Il y a eu récemment une discussion au sujet de la différence entre ces deux canevas, que j'ai la flemme de chercher !
    Oui, je l'ai retrouvée. Bien sûr j'en étais l'initiateur (qu'est-ce que j'ai pu poser comme questions concernant les images, scanline, tout ce genre de choses, c'est fou !), et Gilles est venu apporter son grain de sel, que je saupoudre ici histoire de ne pas le perdre (les mises en gras sont de son fait) :
    Citation Envoyé par gvasseur58 Voir le message
    [...] résultat de différentes manipulations et déductions que j'ai pu faire et qui précisent peu à peu la donne :
    • on dessine en utilisant TImage.Canvas et non directement sur TImage.Picture.Bitmap.Canvas => en traçant un appel à Image1.Canvas.Rectangle(10, 10, 20, 20) par exemple, on voit que non seulement le TCanvas est traité, mais que l'image est redessinée par un appel à Invalidate ;
    • le seul cas où TImage.Canvas ne pointe pas sur le TCanvas persistant, mais sur le TCanvas volatile est à l'intérieur du gestionnaire d'événement OnPaint. Il est fortement déconseillé d'accéder à ce TCanvas volatile, car il est indépendant de l'image vraiment conservée (en fait, il n'en montre qu'une partie) ;
    • il est indispensable que le TBitmap de TImage.Picture ait des dimensions différentes de 0, sinon rien ne peut être affiché et/ou mémorisé => il est créé si nécessaire, mais, sans autre précision, les valeurs de Height et Width seront de 0.
    En fait, je crois bien que je me suis embrouillé (j'ai l'habitude... ) avec cet affichage tout foireux sous XP et ai cherché à tort une solution dans les entrailles du fonctionnement du TImage. Pour prendre une image (si je puis dire ), c'est comme si lors de l'arrêt inopiné d'un véhicule je commençais par déculasser pour aller trafiquer les pistons alors qu'en fait c'était une bête panne de carburant.

    Citation Envoyé par tourlourou Voir le message
    Un TImage est fondamentalement destiné à afficher des images, chargées dans un Bitmap qui dispose d'un canevas (pour faire du dessin, utiliser un TPaintBox).
    Ce qui revient à utiliser image1.Picture.Assign(bitmap_temporaire_de_préparation); ou image1.Canvas.Draw(0, 0, bitmap_temporaire_de_préparation); ? Pourquoi ai-je deux possibilités pour arriver à un résultat identique (avec plus de contrôle sur la seconde [la position c'est , voir mon image plus bas]) ?

    Citation Envoyé par tourlourou Voir le message
    Le TImage dispose de son propre canevas, qui sert à l'affichage.
    Si j'osais, je dirais que je ne veux pas en entendre parler pour la bonne et simple raison que c'est un "machin" internal au compo et que si je choisis le compo TImage c'est pour... afficher !, pas pour faire des frites ou accéder à une BdD.

    Citation Envoyé par tourlourou Voir le message
    Quand c'est nécessaire, le TImage va rafraîchir son image à partir du Bitmap et "oublier" ce qu'on aurait pu ajouter par code comme dessins superposés à ce Bitmap.
    Et c'est là que tu me fais peur : quand c'est nécessaire le TImage va rafraîchir... , nous voilà face à un composant qui est doté d'une vie autonome et décide d'actions à mettre en œuvre ! On dirait Carl dans "2001 Odyssée de l'espace"
    C'est là que j'ai besoin de précisions, car si ça interfère avec ce que j'ai (mal) codé (par ignorance), normal que j'aille au pâté !

    Tiens, trouvé dans une autre discussion en rapport avec le TImage :
    Citation Envoyé par Jipété Voir le message
    [...] (quand je me promène sur le web je prends des notes, des fois) :
    http://forum.lazarus.freepascal.org/...?topic=12770.0
    If image is modified through its graphic canvas, then Invalidate is called automagically. If image is modified by byte manipulation, then no call is made -> user must do it ! [...]
    automagically, c'est moi qui mets en gras.
    Et donc, pour éviter les embrouilles et les oublis, le plus simple pour moi, c'est de modifier le TBitmap de travail que je contrôle de a à z, et de le réassigner au TImage ensuite, et attention aux mots : le re-.Canvas.Draw-er devrais-je dire, si je peux me permettre ce barbarisme .

    Citation Envoyé par tourlourou Voir le message
    L'événement OnPaint est utilisable pour nous permettre de redessiner ce qu'il faut ajouter et qui aurait été "oublié" lors d'un rafraîchissement image. C'est donc là qu'il faut loger notre code de dessin sur le canevas du TImage.
    ...pour nous permettre... Et là je reprends la main ?
    Je ne vois pas comment je pourrais "oublier" quelque chose avec mon .Assign(bitmap_temporaire_de_préparation); : c'est là aussi que j'ai encore besoin de précisions, ou alors je zappe tout ça et je me concentre exclusivement sur le image1.Canvas.Draw(posX, posY, bitmap_préparé_en_amont);, à charge pour moi de vérifier qu'il y a du carburant si le moteur s'arrête.

    Enfin, il me semble. Parce qu'au final, je ne m'en suis pas trop mal sorti avec ces .Draw, regardez, il y a 2 TImage's en service, et celui du dessus affiche 2 TBitmap's construits à partir d'un TBitmap "master" qui représente à plat et de 0 à 360 la roue chromatique qu'on voit à droite (image du web) :
    Nom : démo_colorjob.png
Affichages : 452
Taille : 9,8 Ko
    Le petit rectangle "0/360" indique le point de raccordement dans le TImage supérieur des 2 TBitmap's de travail (l'un va de "début" à 360 et l'autre de 0 à "fin", étant entendu que 360 et 0 c'est le même endroit "géographique" sur le cercle, à "3 heures" en mode avion ["bandit à 3 heures", ]) ; vous pouvez aller "espionner" avec un ColorPicker, il n'y a pas d'accroc ni de trou dans la map .
    Cette construction représente le dégradé entre deux couleurs, en haut le parcours direct et dessous le "complément" ou comment partir de la fin et atteindre le début.
    Attention, la lecture de ces images doit se faire de droite à gauche (ben oui, comme le cercle chromatique qui, dans sa partie supérieure est parcouru de 0 à 180, de 3 heures à 9 heures, sens anti-horaire de droite à gauche).


    Un dernier mot, pris dans la même discussion que celle citée au début et qu'on doit à l'ami Thierry :
    Citation Envoyé par ThWilliam Voir le message
    [...] J'ai parfois (souvent ?) trouvé qu'en informatique : pourquoi faire simple quand on peut faire compliqué ??? La preuve : ces bitmap, canvas, rawimage... [...]
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  7. #7
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    La faculté du OnPaint n'est pas là pour nous obliger à redéfinir l'image à chaque fois : si elle a été logée dans le Bitmap par un Assign, et qu'on n'a rien à y ajouter, ne plus s'occuper de rien.
    Mais si on veut changer d'image au OnPaint (pourquoi pas ?) ou agrémenter le Bitmap de dessins par code, là, il faut le faire dans le OnPaint.
    L'OS se charge d'envoyer des messages à destination du TImage pour qu'il sache qu'il doit se repeindre (fenêtre du dessus qui s'est fermée, etc.).
    A nous d'appeler Refresh ou Invalidate ou Repaint quand on besoin de le déclencher, dans notre code, suite à un changement qu'on doit faire prendre en compte illico.
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  8. #8
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Citation Envoyé par tourlourou Voir le message
    Mais si on veut changer d'image au OnPaint (pourquoi pas ?) ou agrémenter le Bitmap de dessins par code, là, il faut le faire dans le OnPaint.
    Je pose la question inverse : si on veut changer d'image au OnPaint (pourquoi ?)
    Pourquoi utiliser le OnPaint ?
    Personne ne répond à cette question...

    Voilà ce qui me posait un affreux problème sous XP (solutionné avec le DoubleBuffered du parent du TImage à True, si vous suivez) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
        for i := 255 downto 0 do begin
          Application.ProcessMessages;
          //if (i mod 4 = 0) then  // décommenter pour accélérer (en sautant des pas)
          begin
            GraphUtil.DrawVerticalGradient(tmpbmp.Canvas, img.ClientRect, RGBtoColor(0,i,0), RGBtoColor(0,0,0));
            img.Picture.Assign(tmpbmp);
            Sleep(3);  // pour ralentir + ou -
          end;
        end;
    Je change 256 fois une couleur (du vert au noir), la procédure DrawVerticalGradient bricole avec le tmpbmp créé au préalable, tout ce que j'ai à faire c'est de l'assigner au TImage à chaque tout de boucle pour avoir un dégradé changeant et voilà !

    Alors, utiliser OnPaint, ok, mais pourquoi ? Qu'est-ce que j'y gagne ?
    Et comment l'utiliser ? Je n'utilise plus mon tmpbmp ? Comment vous goupilleriez tout ça ?
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

  9. #9
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Salut JP.

    Regarder le code de TImage est beaucoup plus instructif qu'une documentation (surtout si elle est mal traduite).
    TImage est un descendant de TCustomImage (unité ExtCtrls) lui-même dérivé de TGraphicControl (qui est le composant de base pour les composants visuels non fenêtrés, pcq il dispose d'un Canvas)
    L'implémentation de TCustomImage se trouve dans le fichier CustomImage.Inc
    TImage peut utiliser soit le Canvas de TGraphicControl, soit son "propre Canvas" qui est en fait le Canvas de sa propriété Picture.Graphic
    Tout dépend de la valeur interne de FUseAncestorCanvas : true, on utilise le inherited Canvas (celui de TGraphicControl), false, le Canvas du Bitmap. FUseAncestorCanvas est initialisé à false dans le constructeur.

    Si j'écris :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      with image1 do
      begin
        Canvas.Brush.Color:= clBlue;
        Canvas.FillRect(ClientRect);
      end;
    end;
    ... le Canvas m'est fourni par la fonction GetCanvas :

    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
    function TCustomImage.GetCanvas: TCanvas;
    var
      TempBitmap: TBitmap;
    begin
      if not FUseAncestorCanvas and (FPicture.Graphic = nil) then // FPicture est "vide"
      begin
        // make a new bitmap to draw on
        TempBitmap := TBitmap.Create;
        try
          TempBitmap.Width := Width;
          TempBitmap.Height := Height;
          FPicture.Graphic := TempBitmap;
        finally
          TempBitmap.Free;
        end;
      end;
        // try draw on the bitmap, not on the form's canvas
      if not FUseAncestorCanvas and (FPicture.Graphic is TBitmap) then
        Result := TBitmap(FPicture.Graphic).Canvas
      else
        Result := inherited Canvas;
    end;
    Donc avec le code Button1Click, j'ai dessiné directement sur le Canvas du Bitmap. Ce n'est pas un dessin volatile. Si j'enregistre (image1.picture.SaveToFile), le fichier bmp obtenu est tout bleu.

    Si, par contre, j'utilise l'événement OnPaint du TImage :

    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
    procedure TForm1.Image1Paint(Sender: TObject);
    begin
       if checkbox1.checked then
       begin
       with image1 do
       begin
         Canvas.Pen.Color:= clred;
         Canvas.MoveTo(0,0);
         Canvas.LineTo(Width, Height);
       end;
       end
       else
       with image1 do
       begin
         Canvas.Pen.Color:= cllime;
         Canvas.MoveTo(Width,0);
         Canvas.LineTo(0, Height);
       end;
    end;
     
    procedure TForm1.CheckBox1Change(Sender: TObject);
    begin
       Image1.Invalidate;
    end;
    ... dans l'événement OnPaint, c'est le Canvas de TGraphicControl qui est appelé. Celui-ci est volatile, le dessin ne se fait pas sur le bitmap du TImage. Il se superpose au dessin de ce bitmap. Si j'enregistre, sur le fichier bmp obtenu, je ne verrai pas le dessin de la ligne. C'est clair en regardant le code Paint:

    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
    procedure TCustomImage.Paint;
    var
      R: TRect;
      C: TCanvas;
    begin
      //...
      if Picture.Graphic = nil 
      then Exit;
      // dessin de Picture.Graphic
      C := inherited Canvas;
      R := DestRect;
      C.AntialiasingMode := FAntialiasingMode;
      FPainting:=true;
      try
        C.StretchDraw(R, Picture.Graphic);
      finally
        FPainting:=false;
      end;
     
      FUseAncestorCanvas := True; 
      try
        inherited Paint; // Canvas volatile de TGraphicControl
      finally
        FUseAncestorCanvas := False;
      end;
    end;
    A remarquer que si le TImage ne contient pas de Picture.Graphic, on sort de la procédure, donc le inherited Paint n'est pas exécuté.
    Si, dans mon exemple, je change la valeur du CheckBox avant de dessiner mon TImage en bleu, la ligne ne sera pas affichée !

    J'en reviens toujours à me dire : TImage est pratique pour afficher sans code une image provenant d'un fichier (bmp, png, jpg...). Mais pour un dessin à construire : NON.
    Un simple PaintBox qui, dans son événement OnPaint, va afficher un bitmap.
    Je dessine sur le Canvas de ce bitmap, puis Invalidate du PaintBox.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.PaintBox1Paint(Sender: TObject);
    begin
        if Assigned(MyBmp) then
         PaintBox1.Canvas.Draw(0,0, MyBmp);
    end;
    De plus, je n'ai pas besoin de doublebuffered, pcq tout est préalablement dessiné sur un bitmap qui s'affiche "en une fois". Beaucoup plus rapide et pas de scintillements. C'est d'ailleurs le principe du doublebuffered.

    Amicalement
    Thierry

  10. #10
    Expert éminent sénior
    Avatar de Jipété
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    10 730
    Détails du profil
    Informations personnelles :
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 10 730
    Points : 15 132
    Points
    15 132
    Par défaut
    Salut Thierry,

    OK, avec tes explications détaillées je crois que je commence à comprendre.

    Résultat, j'ai monté un tout petit programme de test avec à gauche toutes les actions (du Create du bitmap temporaire à sa destruction) concernant l'affichage dans un TImage dans la procédure d'un bouton, et à droite la même chose pour un TPaintBox sauf qu'il faut trois procédures au lieu d'une seule.

    Et je vous jure que ce n'est pas un traficotage à la 'Toshop, voilà les résultats du GetTickCount :
    Nom : compar_timage_tpaintbox.jpg
Affichages : 491
Taille : 24,8 Ko

    Comme je n'en croyais pas mes yeux, j'ai relancé le prog et là, le TPaintBox était plus lent de 29 microsecondes (oui, c'est peanut's, mais c'est manière de dire).

    Donc pour moi c'est match nul, avec des risques de problèmes en plus pour le TPaintBox, genre pas question de libérer le Bitmap temporaire sinon AV (vécu !) selon le contexte...

    Tu avais raison, pourquoi faire simple (avec un TImage) quand on peut faire compliqué et risqué (avec un TPaintBox) pour un résultat identique !

    D'autres avis ?

    PS : le code utilisé : une fiche avec un TPanel en haut contenant deux TButton's et deux TEdit's, dessous à gauche un TImage 256x256 et à droite un TPaintBox aux mêmes dimensions.
    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
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
      private
        { private declarations }
        tmpBmp: TBitmap;
      public
        { public declarations }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    uses
      LCLIntf, //GetTickCount
      GraphUtil;
     
    {$R *.lfm}
     
    { TForm1 }
     
    const
      ONE_OVER_MILLION = 1E-6; // micro
      ONE_OVER_MILLIER = 1E-3; // milli
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      StartTime, StopTime, Delta    :  QWord;
      i: integer;
    begin
      DoubleBuffered := True;
      StartTime := GetTickCount;
     
      tmpBmp:= TBitmap.Create;
      with tmpBmp do begin
        PixelFormat := pf24bit;
        SetSize(image1.Width, image1.Height);
      end;
      for i := 0 to 255 do begin
        Application.ProcessMessages;
        begin
          GraphUtil.DrawVerticalGradient(tmpBmp.Canvas, image1.ClientRect, RGBtoColor(0,0,0), RGBtoColor(0,i,0));
          image1.Canvas.Draw(0,0,tmpBmp);
          Sleep(3);
        end;
      end;
     
      StopTime := GetTickCount;
      Delta := StopTime - StartTime;
      Edit1.Text := FloatToStr(Delta * ONE_OVER_MILLIER) + ' microsecondes';
     
      tmpBmp.Free;
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      StartTime, StopTime, Delta    :  QWord;
      i: integer;
    begin
    //  DoubleBuffered := True;
      StartTime := GetTickCount;
     
      tmpBmp:= TBitmap.Create;
      with tmpBmp do begin
        PixelFormat := pf24bit;
        SetSize(PaintBox1.Width, PaintBox1.Height);
      end;
      for i := 0 to 255 do begin
        Application.ProcessMessages;
        begin                                                       / image1 gardé pour dimensions, paintbox1 ne connaît pas ClientRect
          GraphUtil.DrawVerticalGradient(tmpBmp.Canvas, image1.ClientRect, RGBtoColor(0,0,0), RGBtoColor(0,i,0));
          PaintBox1.Invalidate;
          Sleep(3);
        end;
      end;
     
      StopTime := GetTickCount;
      Delta := StopTime - StartTime;
      Edit2.Text := FloatToStr(Delta * ONE_OVER_MILLIER) + ' microsecondes';
     
    //  tmpBmp.Free; // AV garanti si décommenté malgré "if Assigned" plus loin...
    end;
     
    procedure TForm1.PaintBox1Paint(Sender: TObject);
    begin
      try // protection inutile, AV quand même selon contexte...
    // j'ai l'impression que ce if Assigned n'est pas fiable à 100 % 
    // déjà vécu des trucs tordus avec lui...
        if Assigned(tmpBmp) then
         PaintBox1.Canvas.Draw(0,0, tmpBmp);
      finally
        //
      end;
    end;
     
    procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
    begin
      if Assigned(tmpBmp) then
        tmpBmp.Free;
    end;
    Il a à vivre sa vie comme ça et il est mûr sur ce mur se creusant la tête : peutêtre qu'il peut être sûr, etc.
    Oui, je milite pour l'orthographe et le respect du trait d'union à l'impératif.
    Après avoir posté, relisez-vous ! Et en cas d'erreur ou d'oubli, il existe un bouton « Modifier », à utiliser sans modération
    On a des lois pour protéger les remboursements aux faiseurs d’argent. On n’en a pas pour empêcher un être humain de mourir de misère.
    Mes 2 cts,
    --
    jp

Discussions similaires

  1. [WD16] Besoin d'éclaircissement sur Réplication HFCS
    Par mogwai162 dans le forum WinDev
    Réponses: 1
    Dernier message: 08/11/2011, 08h02
  2. Besoin d'éclaircissement sur les évenements
    Par franquis dans le forum jQuery
    Réponses: 2
    Dernier message: 24/12/2010, 09h55
  3. Besoin de conseil sur les TImage
    Par LescureImage dans le forum C++Builder
    Réponses: 1
    Dernier message: 04/07/2008, 18h11
  4. Besoin d'éclaircissement sur un bug
    Par banjo12 dans le forum Windows Forms
    Réponses: 4
    Dernier message: 25/06/2008, 15h51
  5. Besoin d'éclaircissement sur les sockets
    Par Delphy113 dans le forum Langage
    Réponses: 3
    Dernier message: 11/06/2008, 18h10

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