Question

J'ai un JPanel . J'aimerais ajouter des images JPEG et PNG que je génère à la volée.

Tous les exemples que j'ai vus jusqu'à présent dans les didacticiels Swing . , spécialement dans les exemples Swing , utilisez ImageIcon s.

Je génère ces images sous forme de tableaux d'octets. Elles sont généralement plus grandes que l'icône qu'elles utilisent couramment dans les exemples (640 x 480).

  1. Existe-t-il un problème (de performance ou autre) lors de l'utilisation de la classe ImageIcon pour afficher une image de cette taille dans un fichier JPanel?
  2. Quelle est la manière habituelle de le faire?
  3. Comment ajouter une image à un JPanel sans utiliser la classe ImageIcon?

Modifier : un examen plus approfondi des didacticiels et de l'API montre qu'il est impossible d'ajouter un ImageIcon directement à un fichier JPanel. Au lieu de cela, ils obtiennent le même effet en définissant l'image comme une icône d'un JLabel. Cela ne me semble pas juste…

Était-ce utile?

La solution

Voici comment je le fais (avec un peu plus d'informations sur le chargement d'une image):

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage image;

    public ImagePanel() {
       try {                
          image = ImageIO.read(new File("image name and path"));
       } catch (IOException ex) {
            // handle exception...
       }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters            
    }

}

Autres conseils

Si vous utilisez JPanels, utilisez probablement Swing. Essayez ceci:

BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);

L'image est maintenant un composant pivotant. Il est soumis aux conditions de mise en page comme tout autre composant.

Le chemin de Fred Haslam fonctionne bien. J'ai eu des problèmes avec le chemin du fichier, car je veux référencer une image dans mon pot. Pour ce faire, j'ai utilisé:

BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));

Comme je n'ai qu'un nombre fini d'images (environ 10) à charger avec cette méthode, cela fonctionne assez bien. Il obtient un fichier sans avoir le chemin de fichier relatif correct.

Je pense qu'il n'est pas nécessaire de créer une sous-classe. Il suffit d'utiliser un Jlabel. Vous pouvez définir une image dans un Jlabel. Alors, redimensionnez le Jlabel puis remplissez-le avec une image. C'est bon. C'est comme ça que je fais.

Vous pouvez éviter de créer complètement votre propre sous-classe Component en utilisant la classe JXImagePanel des bibliothèques gratuites SwingX .

Télécharger

JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
  1. Il ne devrait y avoir aucun problème (autre que les problèmes généraux que vous pourriez rencontrer avec de très grandes images).
  2. Si vous parlez d’ajouter plusieurs images à un seul panneau, j’utiliserais les options ImageIcon . Pour une image unique, je penserais à créer une sous-classe personnalisée de JPanel et à remplacer sa méthode paintComponent pour dessiner l'image.
  3. (voir 2)

Vous pouvez sous-classer JPanel - voici un extrait de mon ImagePanel, qui place une image dans l’un des 5 emplacements suivants: haut / gauche, haut / droite, milieu / milieu, bas / gauche ou bas / droite:

protected void paintComponent(Graphics gc) {
    super.paintComponent(gc);

    Dimension                           cs=getSize();                           // component size

    gc=gc.create();
    gc.clipRect(insets.left,insets.top,(cs.width-insets.left-insets.right),(cs.height-insets.top-insets.bottom));
    if(mmImage!=null) { gc.drawImage(mmImage,(((cs.width-mmSize.width)/2)       +mmHrzShift),(((cs.height-mmSize.height)/2)        +mmVrtShift),null); }
    if(tlImage!=null) { gc.drawImage(tlImage,(insets.left                       +tlHrzShift),(insets.top                           +tlVrtShift),null); }
    if(trImage!=null) { gc.drawImage(trImage,(cs.width-insets.right-trSize.width+trHrzShift),(insets.top                           +trVrtShift),null); }
    if(blImage!=null) { gc.drawImage(blImage,(insets.left                       +blHrzShift),(cs.height-insets.bottom-blSize.height+blVrtShift),null); }
    if(brImage!=null) { gc.drawImage(brImage,(cs.width-insets.right-brSize.width+brHrzShift),(cs.height-insets.bottom-brSize.height+brVrtShift),null); }
    }

JPanel est presque toujours la mauvaise classe à sous-classe. Pourquoi ne pas sous-classer JComponent ?

Il y a un léger problème avec ImageIcon en ce que le constructeur bloque la lecture de l'image. Ce n’est pas vraiment un problème lors du chargement depuis le fichier jar de l’application, mais peut-être si vous lisez potentiellement via une connexion réseau. Il existe de nombreux exemples AWT d'utilisation de MediaTracker , ImageObserver et de leurs amis, même dans les démos JDK.

Je fais quelque chose de très similaire dans un projet privé sur lequel je travaille. Jusqu'ici, j'ai généré des images allant jusqu'à 1024x1024 sans aucun problème (sauf la mémoire) et je peux les afficher très rapidement et sans aucun problème de performances.

Remplacer la méthode de peinture de la sous-classe JPanel est excessif et nécessite plus de travail que nécessaire.

Voici comment je le fais:

Class MapIcon implements Icon {...}

OU

Class MapIcon extends ImageIcon {...}

Le code que vous utilisez pour générer l'image sera dans cette classe. J'utilise un BufferedImage pour dessiner dessus lorsque l'appel de paintIcon () est appelé, utilisez g.drawImvge (bufferedImage); Cela réduit la quantité de clignotements effectués lorsque vous générez vos images et vous pouvez les enfiler.

Ensuite, j'étends JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

C’est parce que je veux mettre mon image dans un panneau de défilement, c.-à-d. affichez une partie de l'image et faites défiler l'utilisateur au besoin.

Alors, j’utilise un JScrollPane pour contenir le MapLabel, qui ne contient que le MapIcon.

MapIcon map = new MapIcon (); 
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();

scrollPane.getViewport ().add (mapLabel);

Mais pour votre scénario (montrez simplement l'image entière à chaque fois). Vous devez ajouter le MapLabel au fichier JPanel supérieur et vous assurer de les redimensionner à la taille de l'image (en remplaçant GetPreferredSize ()).

Créez un dossier source dans le répertoire de votre projet. Dans ce cas, je l’ai appelé Images.

JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();

Cette réponse complète la réponse de @ shawalli ...

Je voulais aussi référencer une image dans mon pot, mais au lieu d'avoir une BufferedImage, j'ai simplement fait ceci:

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));

Vous pouvez éviter d'utiliser vos propres Composant , la bibliothèque SwingX et la classe ImageIO :

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));

Je peux voir beaucoup de réponses, ne répondant pas vraiment aux trois questions du PO.

1) Un mot sur les performances: les tableaux d'octets sont probablement inefficaces, sauf si vous pouvez utiliser un ordre exact d'octets en pixels correspondant à la résolution et à la profondeur de couleur actuelles de vos cartes graphiques.

Pour obtenir les meilleures performances de dessin, convertissez simplement votre image en une image BufferedImage générée avec un type correspondant à votre configuration graphique actuelle. Voir createCompatibleImage à l'adresse https://docs.oracle.com/javase/tutorial. /2d/images/drawonimage.html

Ces images seront automatiquement mises en cache dans la mémoire de la carte graphique après avoir été dessinées plusieurs fois sans aucun effort de programmation (ceci est la norme dans Swing depuis Java 6). Par conséquent, le dessin proprement dit prendra un temps négligeable - si vous n’avez pas changé l’image.

La modification de l'image entraînera un transfert de mémoire supplémentaire entre la mémoire principale et la mémoire du processeur graphique, ce qui est lent. Evitez de "redessiner" Par conséquent, évitez d’utiliser getPixel et setPixel à tout prix.

Par exemple, si vous développez un jeu, au lieu d’attirer tous les acteurs du jeu vers une image BufferedImage puis vers un JPanel, il est beaucoup plus rapide de charger tous les acteurs sous la forme de BufferedImages plus petites et de les dessiner un par un dans votre environnement. Code JPanel au bon endroit - de cette manière, il n’ya pas de transfert de données supplémentaire entre la mémoire principale et la mémoire du processeur graphique, à l’exception du transfert initial des images pour la mise en cache.

ImageIcon utilisera une BufferedImage sous le capot - mais en principe, l’attribution d’une BufferedImage avec le mode graphique approprié est la clé, et aucun effort n’est fait pour le faire correctement.

2) La méthode habituelle consiste à dessiner une image BufferedImage dans une méthode paintComponent surchargée de JPanel. Bien que Java prenne en charge une bonne quantité de biens supplémentaires, tels que des chaînes de tampons contrôlant des VolatileImages mises en cache dans la mémoire du GPU, il n’est pas nécessaire de les utiliser, car Java 6 fait un travail raisonnablement bon sans exposer tous ces détails de l’accélération du GPU.

Notez que l'accélération du processeur graphique peut ne pas fonctionner avec certaines opérations, telles que l'étirement d'images translucides.

3) N'ajoutez rien. Il suffit de le peindre comme mentionné ci-dessus:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, this); 
}

" Ajout " logique si l’image fait partie de la mise en page. Si vous avez besoin de cela comme image d’arrière-plan ou d’avant-plan pour remplir JPanel, dessinez simplement paintComponent. Si vous préférez créer un composant Swing générique pouvant afficher votre image, il s'agit alors du même récit (vous pouvez utiliser un composant JComponent et en remplacer la méthode paintComponent), puis ajouter this à votre présentation des composants de l'interface graphique. .

4) Comment convertir le tableau en image tamponnée

Convertir vos tableaux d'octets au format PNG, puis le charger consomme beaucoup de ressources. Un meilleur moyen consiste à convertir votre tableau d'octets existant en image BufferedImage.

Pour cela: n'utilisez pas de boucles for et copiez des pixels. C'est très très lent. Au lieu de cela:

  • apprendre la structure en octets préférée de BufferedImage (de nos jours, il est prudent d'assumer RGB ou RGBA, ce qui correspond à 4 octets par pixel)
  • apprenez la ligne de numérisation et la taille de numérisation utilisée (par exemple, vous pourriez avoir une image large de 142 pixels - mais dans la vie réelle, elle sera stockée sous la forme d'un tableau d'octets d'une largeur de 256 pixels car il est plus rapide de traiter cela et de masquer les pixels inutilisés. le matériel GPU)
  • une fois que vous avez créé un tableau selon ces principes, la méthode setRGB de BufferedImage peut copier votre tableau dans BufferedImage.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top