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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    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 901
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

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

    Informations forums :
    Inscription : Août 2005
    Messages : 6 901
    Billets dans le blog
    54
    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 averti
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    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 901
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

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

    Informations forums :
    Inscription : Août 2005
    Messages : 6 901
    Billets dans le blog
    54
    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 averti
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    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 901
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Nouvelle-Calédonie

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

    Informations forums :
    Inscription : Août 2005
    Messages : 6 901
    Billets dans le blog
    54
    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 averti
    Inscrit en
    Août 2005
    Messages
    29
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 29
    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

+ 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