Comment ajouter une image à un Composite?


J'ai un JPanel auquel 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 Tutoriels Swing , en particulier dans les Exemples Swing utilisent ImageIcon s.

Je génère ces images sous forme de tableaux d'octets, et elles sont généralement plus grandes que l'icône commune qu'elles utilisent dans les exemples, à 640x480.

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

Edit : Un examen plus attentif des tutoriels et de l'API montre que vous ne pouvez pas ajouter un ImageIcon directement à un 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 fonctionne tout simplement pas...

Author: nachokk, 2008-11-18

14 answers

Voici comment je le fais (avec un peu plus d'informations sur la façon de charger 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            
    }

}
 255
Author: Brendan Cashman, 2016-10-18 23:45:57

Si vous utilisez JPanels, vous travaillez probablement avec 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 swing. Il devient soumis à des conditions de mise en page comme tout autre composant.

 532
Author: Fred Haslam, 2013-06-11 06:38:34

La façon de Fred Haslam fonctionne bien. J'ai eu des problèmes avec le chemin de fichiers, 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));

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

 39
Author: shawalli, 2013-10-24 12:57:49

Je pense qu'il n'est pas nécessaire de sous-classe de quoi que ce soit. Utilisez simplement un Jlabel. Vous pouvez définir une image dans un Jlabel. Alors, redimensionnez le Jlabel puis remplissez - le avec une image. Son OK. C'est la façon dont je le fais.

 34
Author: CoderTim, 2009-10-03 19:07:59

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

Télécharger

 20
Author: Dan Vinton, 2012-08-06 10:37:09
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
 12
Author: , 2013-05-04 21:40:06

Vous pouvez sous-classer JPanel - voici un extrait de mon ImagePanel, qui place une image dans l'un des 5 emplacements, en haut/à gauche, en haut/à droite, au milieu/au milieu, en bas/à gauche ou en 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); }
    }
 8
Author: Lawrence Dol, 2008-11-18 17:48:00
  1. Il ne devrait y avoir aucun problème (autre que tous les problèmes généraux que vous pourriez avoir avec de très grandes images).
  2. Si vous parlez d'ajouter plusieurs images à un seul panneau, j'utiliserais ImageIcon s. Pour une seule image, je penserais à créer une sous-classe personnalisée de JPanel et à remplacer sa méthode paintComponent pour dessiner l'image.
  3. (voir 2)
 8
Author: Michael Myers, 2008-11-18 17:49:03

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

Il y a un léger problème avec ImageIcon en ce que le constructeur bloque la lecture de l'image. Pas vraiment un problème lors du chargement à partir du pot d'application, mais peut-être si vous lisez potentiellement sur une connexion réseau. Il y a beaucoup d'exemples d'utilisation de l'ère AWT MediaTracker, ImageObserver et des amis, même dans les démos JDK.

 6
Author: Tom Hawtin - tackline, 2008-11-18 18:21:05

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

Remplacer la méthode paint de la sous-classe JPanel est exagéré et nécessite plus de travail que vous n'en avez besoin.

La façon dont je le fais est:

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 ensuite lorsque paintIcon () est appelé, utilisez g. drawImvge (bufferedImage); Cela réduit la quantité de clignotement effectué pendant que vous générez vos images, et vous pouvez l'enfiler.

Ensuite, j'étends JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

C'est parce que je veux mettre mon image sur un volet de défilement, c'est-à-dire afficher une partie de l'image et faire 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 (il suffit de montrer l'image entière à chaque fois). Vous devez ajouter le MapLabel au JPanel supérieur et assurez-vous de les dimensionner tous à la taille complète de l'image (en remplaçant GetPreferredSize()).

 6
Author: Thomas Jones-Low, 2008-11-18 18:28:11

Créez un dossier source dans votre répertoire de 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();
 5
Author: , 2016-04-18 05:02:53

Vous pouvez éviter d'utiliser propre Components et bibliothèque SwingX et ImageIO classe:

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
 5
Author: Muskovets, 2018-02-23 07:32:44

Cette réponse est un complément à la réponse de @shawalli...

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

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
 4
Author: Filipe Brito, 2015-09-30 17:10:45

Je vois beaucoup de réponses, ne répondant pas vraiment aux trois questions de l'OP.

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

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

Ces images seront automatiquement mises en cache sur la mémoire de la carte d'affichage après avoir dessiné plusieurs fois sans aucun effort de programmation (ceci est standard dans Swing depuis Java 6), et donc le dessin réel prendra un temps négligeable - si vous n'avez pas changé l'image.

La modification de l'image viendra avec un transfert de mémoire supplémentaire entre la mémoire principale et la mémoire GPU-qui est lent. Évitez de "redessiner" l'image dans un BufferedImage par conséquent, évitez de faire getPixel et setPixel à tous les moyens.

Par exemple, si vous développez un jeu, au lieu de dessiner tous les acteurs du jeu sur un BufferedImage puis sur un JPanel, il est beaucoup plus rapide de charger tous les acteurs sous forme de BufferedImages plus petits, et de les dessiner un par un dans votre code JPanel à leur position appropriée - de cette façon, il n'y a transfert des images pour la mise en cache.

ImageIcon utilisera un BufferedImage sous le capot - mais l'allocation d'un BufferedImage avec le mode graphique approprié est la clé, et il n'y a aucun effort pour le faire correctement.

2) La façon habituelle de le faire est de dessiner un BufferedImage dans une méthode paintComponent surchargée du JPanel. Bien que Java prenne en charge une bonne quantité de goodies supplémentaires tels que les chaînes de tampon contrôlant VolatileImages mis en cache dans le GPU mémoire, il n'est pas nécessaire d'utiliser l'un d'eux depuis Java 6 qui fait un travail raisonnablement bon sans exposer tous ces détails de l'accélération GPU.

Notez que l'accélération GPU peut ne pas fonctionner pour certaines opérations, telles que l'étirement translucide images.

3) Ne pas ajouter de. Il suffit de le peindre comme mentionné ci-dessus:

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

"Ajouter" est logique si l'image fait partie de la mise en page. Si vous en avez besoin comme image d'arrière plan ou de premier plan remplissez le JPanel, il suffit de dessiner dans paintComponent. Si vous préférez brasser un composant Swing générique qui peut montrer votre image, alors c'est la même histoire (vous pouvez utiliser un JComponent et remplacer sa méthode paintComponent) - puis ajouter this à votre disposition des composants GUI.

4) Comment convertir le tableau à une Bufferedimage

Convertir vos tableaux d'octets en PNG, puis les charger demande beaucoup de ressources. Une meilleure façon est de convertir votre tableau d'octets BufferedImage.

Pour cela: ne pas utiliser pour les boucles et copier les pixels. C'est très très lent. Au lieu de cela:

  • apprendre la structure d'octets préférée de la BufferedImage (de nos jours, il est sûr de supposer RVB ou RGBA, qui est de 4 octets par pixel)
  • apprenez la scanline et la scansize en cours d'utilisation (par exemple, vous pourriez avoir une image de 142 pixels de large-mais dans la vraie vie, elle sera stockée sous la forme d'un tableau d'octets de 256 pixels de large car il est plus rapide de traiter cela et de masquer les pixels inutilisés par le matériel GPU)
  • ensuite, une fois que vous avez construit un tableau selon ces principes, la méthode setRGB array de BufferedImage peut copier votre tableau dans BufferedImage.
 1
Author: Gee Bee, 2018-03-10 00:36:41