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

2D Java Discussion :

Line.2D hyper lent, comment dessiner plus vite ?


Sujet :

2D Java

  1. #1
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut Line.2D hyper lent, comment dessiner plus vite ?
    Bonjour,

    Je suis débutant en graphisme java et j'ai besoin de dessiner des dizaines de milliers de petites lignes qui se déplacent sur l'écran pendant que je bouge mon stylet wacom, sans alourdir le processeur.

    J'ai fait des essais avec Line2D.Float() dans Graphics2D ( en suivant les conseils de http://java.developpez.com/faq/gui/?...DESSIN_vitesse ), voir le programme ci-dessous, mais voila: Mon intel core duo 2 à 2,2Ghz mets 1 secondes pour exécuter le programme suivant, qui dessine 450 000 petites lignes de 16 pixels. Je me pose la question suivante : comment se fait t'il qu'il faille 305 cycles machines pour dessiner juste un seul pixel dans un buffer ??? (2,2Ghz -> 2 200 000 000 cycles par secondes / 450 000 lignes / 16 pixels = 305 cycles )

    N'y a t'il pas d'autres méthodes plus rapides et directes pour dessiner des lignes en java ?
    J'ai essayé BufferedImage.setRGB() mais c'est kif-kif par pixel. (ok, c'est plus rapide de dessiner chaque pixel avec BufferedImage.setRGB(), plutôt que d'utiliser Line2D.Float(), s'il y a moins de 10 pixels par ligne, et c'est plus lent s'il y en a plus.)

    Merci d'avance pour quelque réponse.
    Alexandre

    P.S : Je précise que je suis un peu débutant (j'utilise surtout java pour faire de l'algorithmique, s'exécutant dans un environnement qui s'appelle Max/msp), et que la lecture des librairies java, c'est souvent du chinois pour moi. Le programme suivant est un programme d'exemple que j'ai modifié pour arriver à mes fins :
    (Le programme s'exécute comme objet max-msp, mais pour l'exécuter ailleurs, il suffit de remplacer "..extends MaxObject.." et les lignes "JitterMatrix ... outlet(0, ... " par ce qui convient...)

    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
    import com.cycling74.max.*;
    import com.cycling74.jitter.*;
    import java.awt.*;
    import java.awt.geom.*;
    import java.awt.image.BufferedImage;
     
    /**
    * Creates a graph in a BufferedImage and then pops it into a
    * JittterMatrix for output in Max-Msp.
    * (By sending "outputDrawing" message to the max-msp "bufferedimage_test" object) 
    */
    public class bufferedimage_test extends MaxObject {
     
    	public bufferedimage_test(Atom[] args) {
    		declareInlets(new int[]{DataTypes.ALL});
    		declareOutlets(new int[]{DataTypes.ALL});
    	}
     
    	public void outputDrawing() {
    		// the drawing code is probably very low impact, but it's still a
    		// good idea to wrap it in a Thread to free up processor time.
    		( new Thread() {
    			public void run() {
    				BufferedImage bim = bakePieGraph();
     
    				JitterMatrix jim = new JitterMatrix(bim);
    				outlet(0, "jit_matrix", jim.getName());
    			}
    		}).start();
    	}
     
    	public BufferedImage bakePieGraph() {
    		Frame theframe = new Frame ();
    		theframe.addNotify ();
    		Image myImage = theframe.createImage(320,200);
     
    		Graphics2D gc = (Graphics2D)myImage.getGraphics ();
    		gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
    		gc.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
    		gc.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
    		gc.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
    		gc.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
     
                    // c'est cette boucle-ci qui met une seconde a s'exécuter :
     
    		for ( int i = 0; i<450000 ;i++) { 
    			Shape ligne = new Line2D.Float( 10, 10,20,25  );
    			gc.draw(ligne);
    		}
    		return makeBufferedImage(myImage);
    	}
     
    	public static BufferedImage makeBufferedImage(Image image) {
    		BufferedImage bufferedImage = new BufferedImage(
    				image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
    		Graphics2D g2 = bufferedImage.createGraphics();
    		g2.drawImage(image, null, null);
    		return bufferedImage;
    	}
    }

  2. #2
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Alors :

    1) il faut savoir que la creation/destruction d'objet sont des operations couteuses en temps puisqu'il fait allouer et desallouer de la memoire. Ainsi quand il faut optimiser ce genre de traitements, il faut mieux promouvoir la reutilisation.

    Globalement tu n'as besoin que d'UN SEUL objet Line2D que tu modifieras pas la suite avec la methode setLine().

    2) Note : ca ne s'applique pas dans ton code mais je l'indique ici pour des optimisations futures :

    Ensuite Gfx nous indique dans son livre que lorsqu'on se contente de tracer des lignes a l'horizontale ou a la verticale sans modifiier le Stroke de base il est bien plus rapide de passer par les primitives drawLine() de la classe Graphics.

    En effet le passage par une Shape c'est bien au niveau objet mais au final comme pour toutes les autres formes, il va y avoir creation d'un coutour avec le Stroke, meme si la forme est une simple ligne d'epaisseur 1 et donc ca peut egalement etre une operation couteuse en temps.

    3) Quoi d'autre ?

    Ah oui essaie de creer une image compatible (faire recherche sur le forum "compatible BufferedImage") avec ton affichage. Je ne sais pas si ca jouera ici mais ca evitera de perdre du temps en conversion d'un espace de couleur a un autre en tout cas ca devrait peut-etre etre plus rapide que d'utiliser la methode createImage() de la classe Frame...
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  3. #3
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut
    Merci beaucoup pour ta reponse mais :

    - Je dois être une brute en java mais je comprend pas comment ecrire la methode setLine() : j'ai essayé "ligne.setLine( 10, 10,20,25 );" mais ça marche pas... ??

    Merci pour le tuyau des lignes horizontales et verticales.

    Pour l'instant je dois surtout dessiner des lignes diagonales.

    Peut on en dessiner sans passer par une shape ? (j'avoue que je devine le concept de shape, mais je ne l'ai peut-être pas bien compris)

  4. #4
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    car setLine() est une methode de la classe Line2D, pas de la classe Shape.

    Apres quelques tests, il semble que ce soit plus rapide d'utiliser les primitives de rendu:

    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
     
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.GraphicsConfiguration;
    import java.awt.GraphicsEnvironment;
    import java.awt.RenderingHints;
    import java.awt.Shape;
    import java.awt.geom.*;
    import java.awt.image.BufferedImage;
     
    /**
     */
    public class Test2D {
     
      enum Test {
     
        SHARED, ALLOCATE, PRIMITIVE;
      }
     
      public Test2D() {
      }
     
      public static void main(String... args) {
        new Test2D().outputDrawing();
      }
     
      public void outputDrawing() {
        // the drawing code is probably very low impact, but it's still a
        // good idea to wrap it in a Thread to free up processor time.
        (new Thread() {
     
          public void run() {
            for (Test test : Test.values()) {
              BufferedImage bim = null;
              int times = 100;
              float meanTime = 0;
              for (int i = 0; i < times; i++) {
                long time1 = System.currentTimeMillis();
                bim = bakePieGraph(bim, test);
                long time2 = System.currentTimeMillis();
                meanTime += time2 - time1;
                if (i % (times / 10) == 0) {
                  System.out.println(i);
                }
              }
              System.out.println();
              System.out.println("Test: " + test + "\tRuns: " + times + "\tMean Time: " + (meanTime / times) + "ms");
            }
          }
        }).start();
      }
     
      public BufferedImage bakePieGraph(BufferedImage myImage, Test test) {
        if (myImage == null) {
          GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
          GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
          myImage = gc.createCompatibleImage(320, 200);
        }
        Graphics2D g = myImage.createGraphics();
        try {
          g.setColor(Color.WHITE);
          g.fillRect(0, 0, 320, 200);
          g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
          g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
          g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
          g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
          g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
          g.setColor(Color.BLACK);
          Line2D.Float line = (test == Test.SHARED) ? new Line2D.Float() : null;
          for (int i = 0; i < 450000; i++) {
            switch (test) {
              case SHARED:
                line.setLine(10, 10, 20, 25);
                g.draw(line);
                break;
              case ALLOCATE:
                line = new Line2D.Float(10, 10, 20, 25);
                g.draw(line);
                break;
              case PRIMITIVE:
                g.drawLine(10, 10, 20, 25);
            }
          }
        }
        finally {
          g.dispose();
        }
        return myImage;
      }
    }
    Resultats :
    0
    10
    20
    30
    40
    50
    60
    70
    80
    90

    Test: SHARED Runs: 100 Mean Time: 1296.58ms
    0
    10
    20
    30
    40
    50
    60
    70
    80
    90

    Test: ALLOCATE Runs: 100 Mean Time: 1310.77ms
    0
    10
    20
    30
    40
    50
    60
    70
    80
    90

    Test: PRIMITIVE Runs: 100 Mean Time: 467.26ms
    Ce qui semble prendre plus de temps c'est donc bien la rasterisation de la forme et le calcul de son contour. Le temps pour l'allocation d'un nouvel objet Line2D semble etre negligeable.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  5. #5
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut methode setLine() pas plus rapide
    merci de ton aide !

    chez moi c'est 2 fois plus rapide aussi avec g.drawLine*...
    (Reste plus à se demander se qu'il fout pendant 150 cycles machines pour dessiner un pixel dans un buffer... )

    Ah mais peut-être que le "new thread" utilise pas toutes les ressources de la machine comme c'est un thread, par definition, en parallele... alors j'ai crée 6, puis 12 objets "bufferedimage_test" dans mon environnement max-msp, pour me rendre compte, qu'effectivement, si j'en "lance" 6 ou 12 d'un seul coup dans max-msp, il m'affiche le resultat dans 6 ou 12 petites images differentes... non pas aprés 6 ou 12 secondes... mais après 3,5 ou 6 secondes. étrange. un programme java n' utiliserait que la moitié des ressources de la machine ?
    et plusieurs programme en même temps les utiliseraient... (J'ai essayé de virer le bazar du "new thread", et dans ce cas, un programme met toujours une seconde, et 12 programmes "lancés en même temps" par max-msp, ça met alors 12 secondes - et avec la roue multicolore qui tourne, en prime ! -... car max-msp est alors obligé d'attendre que le premier soit terminé pour lancer le suivant, etc.)

    ...Reste plus à se demander se qu'il fout pendant 75 cycles machines pour mettre un pixel dans un buffer...?
    (mettre simplement une valeur dans un array int semble 30 à 50 fois plus rapide, j'ai testé... Sinon, a propos des calculs j'aimerai bien savoir s'il existe quelque part des methodes plus rapides que les java.lang.Math, en float par exemple ? )

    > (faire recherche sur le forum "compatible BufferedImage")

    J'ai fait une recherche et j'ai trouvé "Une VolatileImage est une image qui est chargee dans la memoire de la carte video pour une acceleration maximum." Donc j'ai essayé aussi (en suivant http://gpwiki.org/index.php/Java:Tut...:VolatileImage ) mais il semble que ça met toujours le même temps...


    Donc alors, Graphics2D.drawLine, est-ce vraiment le mieux du mieux qu'on peut faire en java, à part programmer en C(mais non merci) ?!


    > Ah oui essaie de creer une image compatible (faire recherche sur le
    > forum "compatible BufferedImage") avec ton affichage. Je ne sais pas
    > si ca jouera ici mais ca evitera de perdre du temps en conversion d'un
    > espace de couleur a un autre ...

    Je ne pige pas tout mais j'imagine que tu parlais d'une conversion avant ou aprés la boucle.
    Effectivement, ce n'est pas là le probleme (Si je change 450000 itération de ma boucle par 45 itérations, tout s'execute "instantanement", donc c'est bien ce qui se passe dans la boucle qui est important)


    * J'avais même pas vu cette méthode dans l'api car j'avais zappé le paragraphe "Methods inherited from class java.awt.Graphics"

  6. #6
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Bon alors je n'ai pas tout pige ce que tu me racontes compte tenu du fait que je ne connais pas ton envirronement de devel, d'execution, ton systeme d'exploitation, ni meme la JVM utilisee, sa version ou son fournisseur. Donc difficile de dire ce que Java peut bien faire durant son temps perdu. Par contre une image n'est pas qu'un simple tableau d'entiers, en temoignent la demi-douzaine de classes qui gravitent autour de BufferedImage.

    A noter qu'il t'es possible de speficier la priorite d'execution de ton Thread entre Thread.MIN_PRIORITY et Thread.MAX_PRIORITY . Mais si ta nouvelle Thread est creee dans l'EDT (la Thread executant l'affichage de Swing/Awt et la propagation des evenements), elle devrait deja avoir une priorite assez haute (heritee de sa Thread parente donc).

    Une VolatimeImage c'est autre chose, c'est une image qui reside integralement dans la memoire video (et donc s'affiche beaucoup plus vite) et dont le contenu est volatile (d'ou son nom) il faut donc verifier regulierement que le contenu est toujours valide et si besoin de redessiner. C'est assez penible a utiliser et manipuler et je doute que tu en ai jamais besoin, sauf cas bien specifique (garde ca sous la manche en dernier ressort au cas ou tu butes vraiment sans jamais arriver a mieux optimiser les choses).

    Une image compatible est une BufferedImage creee comme dans le code que j'ai poste. C'est une BufferedImage dont le format interne est le meme que celui de la carte video, donc quand cette image sera dessinee/composee (a l'ecran) il n'y aura pas besoin de la convertir dans un format supporte par la carte video. D'ou un affichage plus rapide.

    Maintenant la question est : le contenu de l'image change-t-il suffisament souvent pour que tu ai besoin de rendre l'image a chaque reaffichage. Une autre technique d'optimisation est de reutiliser tout le temps le meme image pre-dessinee, et de la composer sur l'ecran. En effet composer une bitmap est bien plus rapide que de dessiner "en live". Quand l'affichage est modifie, , alors on remodifie le buffer offscreen puis on le recompose a nouveau sur l'ecran.

    Enfin dans ce test les lignes sont toutes identiques et toutes placees au meme endroit, mais dans le cas d'un affichage reel que ce soit avec un rendu des lignes pour un affichage direct sur l'ecran ou pour un rendu offscreen, il faudra utiliser la zone de clip fournie au Graphics pour restreindre la quantite de choses qu'il y a a redessiner. Pour que cela soit reellement efficace, il faut de meme, lorsqu'on demande un reaffichage de l'interface, faut se cantonner a demander un repaint() d'une zone particuliere plutot qu'un repaint() complet de l'interface graphique.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  7. #7
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut
    Je suis sur mac os x-leopard/ MacBookPro intel core duo 2. (la version de java je sais plus ou c'est marqué)
    En fait la tablette wacom echantillonne à chaque 10 millisecondes, et je veux afficher des milliers de lignes qui bougent (de maniere imprevisible) à chaque fois.

    Citation Envoyé par bouye Voir le message
    Une VolatimeImage c'est autre chose, c'est une image qui reside integralement dans la memoire video (et donc s'affiche beaucoup plus vite) et dont le contenu est volatile (d'ou son nom) il faut donc verifier regulierement que le contenu est toujours valide et si besoin de redessiner.
    Oui, ça j'avais bien noté, de toutes façons ça ne semble pas plus performant.

    Citation Envoyé par bouye Voir le message
    Maintenant la question est : le contenu de l'image change-t-il suffisament souvent pour que tu ai besoin de rendre l'image a chaque reaffichage.
    eh oui..

    Citation Envoyé par bouye Voir le message
    Enfin dans ce test les lignes sont toutes identiques et toutes placees au meme endroit,
    oui, seulement dans ce test. normalement mes lignes seront sur tout l'écran.


    Sinon, dans l'idée de faire les lignes pixel par pixel (qui serait certainement plus efficient dans le cas du remplissage d'un array), j'ai trouvé ça :
    http://forum.java.sun.com/thread.jsp...sageID=9455417
    ("Faster pixel method than BufferedImage's setRGB")
    dans leur discussion, ils disent que c'est 10 fois plus rapide, ce qui semble concorder avec mes tests de remplissage d'array. si j'ai surtout des petites lignes, ça peut le faire..

    Je verrai ça plus tard, là je vais me coucher..

    Merci encore,
    Alexandre

  8. #8
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Toujours pas tres clair tout ca.

    Si les lignes restent les meme a une transformation affine pres (translation par exemple) on peut considerer qu'elles sont identiques et qu'une transformation de l'image offscreen est suffisante. Combinee a une gestion de la zone de clip c'est suffisant pour avoir de bonnes performances.

    Si chacune des lignes changent (la pente ou la longueur de chacun des segments est modifiee), la oui c'est toute l'image qui doit etre regeneree a chaque fois et le probleme de performances sera bien present.

    Sinon oui il me semble tout a fait normal qu'une seul appel setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize) soit plus rapide qu'une foultitude d'appels a setRGB(int x, int y, int rgb).
    Chacune de ces methodes en appele d'autres sur le ColorModel et sur le Raster (colorModel.getDataElements() puis raster.setDataElements()) or dans le cas du tableau un objet pixel qui stocke la valeur de la couleur est cree une seule fois puis reutilise a chaque appel sur chaquecellule du tableau. Dans la seconde methode, cet objet est recree a chaque fois que l'on appelle setRGB(int x, int y, int rgb).
    Je ne sais pas ce qu'il en est sur le Mac et le JDK d'Apple mais le JDK de Sun contient les sources de l'API dans le fichier src.zip.

    Note : pour avoir la version de Java il suffit de faire java -version a l'interpreteur de commande.

    Les methodes indiquee dans le post sont interressantes et sont probablement a explorer pour ton cas puisque tu as besoin d'une extreme optimisation mais attention l'acces direct au Raster d'une image fait perdre tout acceleration materielle potentielle de l'image (mais bon, si ca va plus vite a la fin...).

    Hum, il est dommage que Gfx ne soit pas par la ces temps-ci, il aurait ete fort utile pour ce genre de probleme je crois.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  9. #9
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    Dès quil faut de la performance en termes d'affichage, je passe en mode tout manuel. D'une part parce que je sais faire et d'autre part parce que je n'ai pas encore trouvé plus rapide.
    Dans l'exemple suivant, sans faire du plein-écran, dans une fenêtre de 800x600, afficher 45000 lignes aléatoires de longueurs maximum 50 pixels me prend moins de 230 ms sur un valeureux Pentium 4 mono-coeur 2GHz. Je trace tous les traits manuellement, c'est à dire directement dans un tableaux de pixels avec la méthode classique de Bresenham. Voici un exemple de code simple que j'ai refais et testé pour toi, si ça peut t'intéresser :
    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
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.image.MemoryImageSource;
     
    import javax.swing.JFrame;
    import javax.swing.JPanel;
     
    public class Main extends JPanel implements Runnable
    {
    	public static void main( String[] arg )
    	{
    		new Main();
    	}
     
    	private JFrame				frame;
    	private MemoryImageSource	img;
    	private Image				image;
    	private int[]				pixels;
    	private int					width, height;
    	private Thread				thread;
     
    	public Main()
    	{
    		frame = new JFrame();
    		frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    		width = 800;
    		height = 600;
    		pixels = new int[ width * height ];
    		img = new MemoryImageSource( width, height, pixels, 0, width );
    		img.setAnimated( true );
    		image = createImage( img );
    		setDoubleBuffered( false );
    		setPreferredSize( new Dimension( width, height ) );
    		frame.getContentPane().add( this );
    		thread = new Thread( this );
    		thread.start();
    		frame.pack();
    		frame.setVisible( true );
    	}
     
    	@Override
    	public void update( Graphics g )
    	{
    		paint( g );
    	}
     
    	@Override
    	public void paint( Graphics g )
    	{
    		g.drawImage( image, 0, 0, this );
    	}
     
    	@Override
    	public void run()
    	{
    		int red = ( 255 << 24 ) | ( 255 << 16 );
    		int green = ( 255 << 24 ) | ( 255 << 8 );
    		int iPixels = 0, iLigne = 0;
    		int taille = width * height;
    		int couleur = red;
    		int fond = ( 255 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | 255;
    		int x1 = 0, y1 = 0;
    		long temps = System.currentTimeMillis();
     
    		while( true )
    		{
    			temps = System.currentTimeMillis();
    			for( iPixels = 0; iPixels < taille; ++iPixels )
    			{
    				pixels[ iPixels ] = fond;
    			}
     
    			for( iLigne = 0 ; iLigne < 45000 ; ++iLigne )
    			{
    				x1 = (int)(Math.random() * 700 ) + 50;
    				y1 = (int)( Math.random() * 500 ) + 50;
    				drawLine( x1, y1,
    						x1 + (int) ( Math.random() * 100 ) - 50,
    						y1 + (int) ( Math.random() * 100 ) - 50, couleur );
    			}
     
    			img.newPixels();
    			temps = System.currentTimeMillis() - temps;
     
    			if( couleur == red ) { couleur = green; }
    			else { couleur = red; }
     
    			System.out.println( temps );
     
    			try
    			{
    				Thread.sleep( 500 );
    			}
    			catch( Exception exc )
    			{
    				exc.printStackTrace();
    			}
    		}
    	}
     
    	int e = 0, inc1 = 0, inc2 = 0, dx = 0, dy = 0, incx = 0, incy = 0, x = 0, y = 0, iPoint = 0;
     
    	public void drawLine( int x1, int y1, int x2, int y2, int couleur )
    	{
    		dx = x2 - x1;
    		dy = y2 - y1;
     
    		if( dx < 0 ) dx = -dx;
    		if( dy < 0 ) dy = -dy;
    		incx = 1;
    		if( x2 < x1 ) incx = -1;
    		incy = 1;
    		if( y2 < y1 ) incy = -1;
    		x = x1;
    		y = y1;
     
    		if( dx > dy )
    		{
    			pixels[ x + y * width ] = couleur;
    			e = ( dy << 1 ) - dx;
    			inc1 = ( dy - dx ) << 1;
    			inc2 = dy << 1;
    			for( iPoint = 0; iPoint < dx; iPoint++ )
    			{
    				if( e >= 0 )
    				{
    					y += incy;
    					e += inc1;
    				}
    				else { e += inc2; }
    				x += incx;
    				pixels[ x + y * width ] = couleur;
    			}
    		}
    		else
    		{
    			pixels[ x + y * width ] = couleur;
    			e = ( dx << 1 ) - dy;
    			inc1 = ( dx - dy ) << 1;
    			inc2 = dx << 1;
    			for( iPoint = 0; iPoint < dy; iPoint++ )
    			{
    				if( e >= 0 )
    				{
    					x += incx;
    					e += inc1;
    				}
    				else e += inc2;
    				y += incy;
    				pixels[ x + y * width ] = couleur;
    			}
    		}
    	}
    }
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  10. #10
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut
    Merci encore pour vos idées!
    Dinobogan, merci pour ton code, ton algorythme de ligne à l'air efficient, j'essairai ta maniere un peu plus tard, mais là il faut que j'avance mon programme, pour maintenant, je vais me contenter des "75 cycles par pixels" avec Graphics2D.drawLine(), car j'ai plus le temps. J'essairais d'autres methodes plus tard si j'ai besoin de meilleures performances...

    ( "Si chacune des lignes changent (la pente ou la longueur de chacun des segments est modifiee)" -> eh oui c'est le cas )

    Sinon, l'affichage risque de ne pas etre le seul soucis de mon programme, j'ai aussi beaucoup de calculs :

    -- Vous ne savez où je peux trouver des "comparatifs" de d'efficience entre : les differentes petites operations simples -,+,*,/, les assignations de variable (int,float,double) array ou non, les conditions (if), et les methodes java.lang.Math ??
    Je sais pas si je suis trés clair mais par exemple:
    Dinobogan, dans ton programme il y a : " if( dx < 0 ) dx = -dx; "
    Pourquoi ça, plutot que " dx = Math.abs(dx) ; " ??
    Es-ce significativement plus rapide de faire une condition puis d'inverser le signe, plutot que simplement virer le signe en utilisant Math.abs() ? est-ce parce-que l'appel d'une methode Math serait toujours plus long ?

  11. #11
    Modérateur
    Avatar de dinobogan
    Homme Profil pro
    ingénieur
    Inscrit en
    Juin 2007
    Messages
    4 073
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : ingénieur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 4 073
    Points : 7 163
    Points
    7 163
    Par défaut
    En effet, l'appel à la méthode "Math.abs" sera toujours plus long que le if car il faudra cumuler l'appel à la fonction et le "if" contenu dans la fonction.
    Tu peux te faire tes propres tests pour tous ces petits trucs.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java
    Que la force de la puissance soit avec le courage de ta sagesse.

  12. #12
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut
    Fantastique !!!!!!!

    Merci dinobogan !!

    Ta methode drawLine(), que j'applique sur directement sur un array que je converti ensuite en image-de-type-max/msp (mon logiciel duquel j'appelle la class java), executé sur mon intel core duo 2 à 2,2Ghz, affiche 6 millions de petites lignes par secondes !
    C'est 12 fois plus rapide que mon programme à l'origine de ce topic, et 7 fois plus rapide que le plus rapide qu'on peut faire avec le graphisme java, Graphics2D.drawLine. Ca ne fait plus que 10 cycles machine pour afficher un pixel, ouf... (en fait je mesure 20 cycles, mais, comme je l'ai expliqué plus haut, je peux lancer l'équivalent de deux threads en même temps a cette vitesse, donc je peux potentielement afficher 12 millions de petites lignes par secondes, je crois que ça m'ira. )

    Bravo pour ton algorithme drawLine() vraiment bien foutu !

    Tu as donc raison, quand on veux de l'efficience, il faut faire les choses soit même, pixel par pixel, dans un array...

    Délire..

  13. #13
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    Pas tant que çà délire. Les classes de bases java sont prévues pour toutes sortes d'usage (gestion du gamma, transparence, images supportant des formats divers, épaisseur de ligne, lien éventuel avec l'interfaces graphique, masque, clibbox, opérations xor, or, etc), toutes ces choses dont tu n'as pas toujours besoin, et dont tu peux donc te débarasser dans ton cas. Tu peux aussi jeter un oeil sur java / sdl (librairie sdl pour java), censé permettre des opérations graphiques rapide, ainsi que sur la combinaison java /opengl (déléguer le boulot au gpu)

  14. #14
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 34
    Points : 55
    Points
    55
    Par défaut
    Dans le cas de lignes de forme identiques, il est possible d'utiliser un GeneralPath au lieu de Line2D. Un seul appel JNI est alors effectué par le sous-système graphique lors de l'affichage de toutes les lignes, ce qui peut énormement améliorer les perfs en supprimant les appels JNI qui "bouffent" le temps processeur pour un Line2D.

    A noter que dans les toutes dernières versions de la JDK qui utilisent D3D ou OpenGL, l'affichage des primitives batché dans un thread d'affichage rend probablement caduc cette optimisation.

  15. #15
    Membre du Club
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    34
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 34
    Points : 55
    Points
    55
    Par défaut
    Comme j'avais un peu de temps, je viens d'ajouter le cas du GeneralPath au code de test indiqué dans un post précédent. Voici les résultats sur mon portable core 2 duo en JDK 6 :

    Test: SHARED Runs: 100 Mean Time: 614.06 ms
    Test: ALLOCATE Runs: 100 Mean Time: 605.48 ms
    Test: PRIMITIVE Runs: 100 Mean Time: 232.18 ms
    Test: GPATH Runs: 100 Mean Time: 120.0 ms

    *************************************************
    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
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
     
    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.GraphicsConfiguration;
    import java.awt.GraphicsEnvironment;
    import java.awt.RenderingHints;
    import java.awt.geom.*;
    import java.awt.image.BufferedImage;
     
    /**
     */
    public class TestLine {
     
        private int COUNT = 450000 ;
     
        enum Test {
            SHARED, ALLOCATE, PRIMITIVE, GPATH;
        }
     
        public TestLine() {
        }
     
        public static void main(String[] args) {
            new TestLine().outputDrawing();
        }
     
        public void outputDrawing() {
            // the drawing code is probably very low impact, but it's still a
            // good idea to wrap it in a Thread to free up processor time.
            (new Thread() {
     
                public void run() {
                    for (Test test : Test.values()) {
                        BufferedImage bim = null;
                        int times = 100;
                        float meanTime = 0;
                        for (int i = 0; i < times; i++) {
                            long time1 = System.currentTimeMillis();
                            bim = bakePieGraph(bim, test);
                            long time2 = System.currentTimeMillis();
                            meanTime += time2 - time1;
                            if (i % (times / 10) == 0) {
                                System.out.println(i);
                            }
                        }
                        System.out.println();
                        System.out.println("Test: " + test + "\tRuns: " + times + "\tMean Time: " + (meanTime / times) + " ms");
                    }
                }
            }).start();
        }
     
        public BufferedImage bakePieGraph(BufferedImage myImage, Test test) {
     
            if (myImage == null) {
                GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
                GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
                myImage = gc.createCompatibleImage(320, 200);
            }
     
            Graphics2D g = myImage.createGraphics();
            try {
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, 320, 200);
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
                g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
                g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
                g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
                g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
                g.setColor(Color.BLACK);
     
                switch (test) {
                    case GPATH:
                        drawGPathLine(g);
                        break;
                    case SHARED:
                        drawSharedLine(g);
                        break;
                    case ALLOCATE:
                        drawNewLine(g);
                        break;
                    case PRIMITIVE:
                        drawPrimitiveLine(g);
                        break;
                }
            }
            finally {
                g.dispose();
            }
            return myImage;
        }
     
        private void drawSharedLine(Graphics2D g) {
            Line2D.Float line = new Line2D.Float();
            for (int i = 0; i < COUNT; i++) {
                line.setLine(10, 10, 20, 25);
                g.draw(line);
            }
        }
     
        private void drawNewLine(Graphics2D g) {
            for (int i = 0; i < COUNT; i++) {
                g.draw(new Line2D.Float(10, 10, 20, 25));
            }
        }
     
        private void drawPrimitiveLine(Graphics2D g) {
            for (int i = 0; i < COUNT; i++) {
                g.drawLine(10, 10, 20, 25);
            }
        }
     
        private void drawGPathLine(Graphics2D g) {
            GeneralPath path = new GeneralPath(GeneralPath.WIND_NON_ZERO, COUNT*2);
            for (int i = 0; i < COUNT; i++) {
                path.moveTo(10f, 10f);
                path.lineTo(20f, 25f);
            }
            g.draw(path);
        }
     
    }

  16. #16
    Expert éminent sénior
    Avatar de tchize_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Avril 2007
    Messages
    25 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Belgique

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2007
    Messages : 25 481
    Points : 48 806
    Points
    48 806
    Par défaut
    java version "1.6.0"
    OpenJDK Runtime Environment (build 1.6.0-b09)
    OpenJDK 64-Bit Server VM (build 1.6.0-b09, mixed mode)
    Sur fedora core,
    Core 2 duo T7300 2Ghz

    Test: SHARED Runs: 100 Mean Time: 513.46 ms
    Test: ALLOCATE Runs: 100 Mean Time: 526.94 ms
    Test: PRIMITIVE Runs: 100 Mean Time: 184.49 ms
    Test: GPATH Runs: 100 Mean Time: 127.25 ms

  17. #17
    Membre à l'essai
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    Points : 20
    Points
    20
    Par défaut
    Tu peux aussi jeter un oeil sur java / sdl (librairie sdl pour java), censé permettre des opérations graphiques rapide, ainsi que sur la combinaison java /opengl (déléguer le boulot au gpu)
    Merci pour vos suggestions ! , j'irai voir opengl quand j'aurai le temps...

    Sinon, sincerement, vous devriez essayer la method drawline() de Dinobogan (post numéro 9 de ce topic), dans vos benchs...

    Chez moi (intel core duo2), c'est 7 fois plus rapide que ce que vous appelez "PRIMITIVE Runs", et 15 fois plus rapide que "ALLOCATE Runs" et "SHARED Runs".

    En toute logique, vous devriez obtenir entre 30 et 40 ms dans vos tests... soit au moins trois fois plus rapide que "GPATH Runs"....

  18. #18
    Rédacteur/Modérateur

    Avatar de bouye
    Homme Profil pro
    Information Technologies Specialist (Scientific Computing)
    Inscrit en
    Août 2005
    Messages
    6 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Nouvelle-Calédonie

    Informations professionnelles :
    Activité : Information Technologies Specialist (Scientific Computing)
    Secteur : Agroalimentaire - Agriculture

    Informations forums :
    Inscription : Août 2005
    Messages : 6 840
    Points : 22 854
    Points
    22 854
    Billets dans le blog
    51
    Par défaut
    Merci j'avais effectivement oublie de creer une variante avec GeneralPath ou Path2D. Je ne m'attendais pas a ce que cette solution soit aussi rapide. Attention toutefois il faut egalement voir s'il n'y a pas d'optimisation en supprimant des lignes supperflues ou des trucs du genre (puisque toutes dessinees au meme endroit et sans antialiasing).

    Oui mais tout le monde n'a pas forcement de BufferedImage sous la main et une maniere generaliste de dessiner c'est de ne manipuler qu'un Graphics/Graphics2D sans se soucier de la sortie effective (image, imprimante, ecran, fichier vectoriel*). De plus tous ceux qui ont a traiter des dessins vectoriels ont d'autres elements a prendre en compte qui ne peuvent pas etre facilement regles par la manipulation d'un simple tableau d'entier tels que l'antialias, les transformations affines appliquee a la zone de rendu, voir pire le Stroke (dessiner une ligne avec des pointilles ou autres formes plus complexe ???) ou meme le Paint (va dessiner des textures ou des gradients circulaires avec ca...).

    *si je dessine dans un tableau de pixel... c'est pas top si ma sortie est un fichier SVG.

    C'est dans le genre de cas, bien specifiques, que tu as a traiter (reactivite super-importante quand besoin d'afficher un nombre enorme de lignes) que les besoin d'une telle optimisation se font sentir et sont alors vraiment necessaires et fortement utiles.

    Une idee pour info, justement, si jamais le besoin d'antialias, stroke ou paint, ... se fait sentir et que tu ne veux pas l'implementer toi-meme en modifiant le rendu manuel des lignes : si jamais l'image est au repos et ne se deplace pas, tu pourrais utiliser Java2D pour rendre LENTEMENT une image correcte et la cacher pour reutilisation MAIS, des que l'image est en deplacement, tu pourrais utiliser un codepath different qui utiliserait la version super-optimisee (ca bouge donc c'est moins visible a l'oeuil quand la qualite est moindre).
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

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

Discussions similaires

  1. Comment dessiner sur un Bitmap sans scintillement ?
    Par TOTO32 dans le forum Composants VCL
    Réponses: 3
    Dernier message: 10/01/2005, 10h11
  2. DBGrid, comment dessiner dans les titres des colonnes
    Par dleu dans le forum Bases de données
    Réponses: 9
    Dernier message: 04/11/2004, 17h49
  3. [Transparent] - Comment dessiner sur 2 TImage ?
    Par TOTO32 dans le forum Langage
    Réponses: 15
    Dernier message: 08/08/2004, 01h06

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