Comment append une image à un JPanel?

J’ai un JPanel auquel j’aimerais append des images JPEG et PNG que je génère à la volée.

Tous les exemples que j’ai vus jusqu’ici dans les tutoriels Swing , spécialement dans les exemples Swing, utilisent ImageIcon s.

Je génère ces images en tant que tableaux d’octets, et ils sont généralement plus grands que l’icône commune qu’ils utilisent dans les exemples, à 640×480.

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

Edit : Un examen plus attentif des didacticiels et de l’API montre que vous ne pouvez pas append 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 me semble pas juste …

Voici comment je le fais (avec un peu plus d’informations sur comment 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 } } 

Si vous utilisez JPanels, alors vous travaillez probablement avec Swing. Essaye ça:

 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 sujet à des 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 des 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)); 

Étant donné que je n’ai qu’un nombre fini d’images (environ 10) que je dois charger en utilisant cette méthode, cela fonctionne assez bien. Il obtient le fichier sans avoir le chemin d’access relatif correct.

Je pense qu’il n’y a pas besoin de sous-classe de rien. Utilisez simplement un Jlabel. Vous pouvez définir une image dans un libellé. Donc, redimensionnez le Jlabel puis remplissez-le avec une image. C’est bon. C’est comme ça que je fais.

Vous pouvez éviter de déployer 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 tous les problèmes généraux que vous pourriez avoir avec de très grandes images).
  2. Si vous parlez d’append plusieurs images à un seul panneau, j’utiliserais ImageIcon s. Pour une seule image, je pense à créer une sous-classe personnalisée de JPanel et à paintComponent 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, 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 à la sous-classe. Pourquoi ne pas sous- 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 depuis l’application jar, mais peut-être si vous lisez potentiellement une connexion réseau. Il y a beaucoup d’exemples de l’utilisation de MediaTracker , ImageObserver et de ses 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’à présent, j’ai généré des images jusqu’à 1024×1024 sans aucun problème (sauf la mémoire) et je peux les afficher très rapidement et sans aucun problème de performance.

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

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

Ensuite je prolonge JLabel:

 Class MapLabel extends Scrollable, MouseMotionListener {...} 

C’est parce que je veux mettre mon image sur un panneau de défilement, c’est-à-dire afficher une partie de l’image et faire défiler l’utilisateur au besoin.

Donc, j’utilise un JScrollPane pour contenir le MapLabel, qui contient uniquement 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 append MapLabel au JPanel supérieur, et assurez-vous de tous les dimensionner à la taille maximale de l’image (en remplaçant GetPreferredSize ()).

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

Je voulais aussi faire référence à une image dans mon pot, mais au lieu d’avoir une image tamponnée, j’ai simplement fait ceci:

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

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(); 

Vous pouvez éviter d’utiliser votre propre bibliothèque de Component et de SwingX ainsi ImageIO classe ImageIO :

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

Je peux voir beaucoup de réponses, sans vraiment aborder les trois questions du PO.

1) Un mot sur les performances: les tableaux d’octets sont probablement inefficaces à moins que vous ne puissiez utiliser un ordre exact d’octets de pixels correspondant à la résolution actuelle et à la profondeur de couleur de vos cartes graphiques.

Pour obtenir les meilleures performances de dessin, convertissez simplement votre image en une 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 quelques tirages 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 s’accompagnera d’un transfert de mémoire supplémentaire entre la mémoire principale et la mémoire GPU, ce qui est lent. Évitez de “redessiner” l’image dans un BufferedImage, évitez donc de faire getPixel et setPixel.

Par exemple, si vous développez un jeu, au lieu de dessiner tous les acteurs du jeu dans un BufferedImage puis dans un JPanel, il est beaucoup plus rapide de charger tous les acteurs en BufferedImages plus petits et de les dessiner un par un dans votre code JPanel. leur position correcte – de cette façon, il n’y a pas de transfert de données supplémentaire entre la mémoire principale et la mémoire GPU à l’exception du transfert initial des images pour la mise en cache.

ImageIcon utilisera un BufferedImage sous le capot – mais la clé consiste à allouer une BufferedImage avec le mode graphique approprié , et il n’y a aucun effort à faire pour le faire correctement.

2) La méthode habituelle consiste à dessiner un BufferedImage dans une méthode paintComponent remplacée du JPanel. Bien que Java prenne en charge de nombreux autres avantages, tels que les chaînes tampons contrôlant VolatileImages mises en cache dans la mémoire GPU, il n’est pas nécessaire d’utiliser ces éléments depuis Java 6, qui fait un travail raisonnable sans exposer tous les 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 d’images translucides.

3) Ne pas append Il suffit de peindre comme mentionné ci-dessus:

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

“Ajouter” a du sens si l’image fait partie de la mise en page. Si vous avez besoin de cette image en arrière-plan ou de premier plan pour remplir le JPanel, dessinez simplement dans paintComponent. Si vous préférez créer un composant Swing générique capable d’afficher votre image, il s’agit de la même histoire (vous pouvez utiliser un composant JComponent et remplacer sa méthode paintComponent), puis l’append à votre présentation des composants de l’interface graphique.

4) Comment convertir le tableau en image Buffered

La conversion de vos tableaux d’octets en PNG, puis son chargement nécessite beaucoup de ressources. Une meilleure méthode consiste à convertir votre tableau d’octets existant en un BufferedImage.

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

  • apprendre la structure d’octet préféré de BufferedImage (de nos jours, il est possible de supposer RGB ou RGBA, qui est de 4 octets par pixel)
  • apprenez la scanline et scansize en cours d’utilisation (par exemple, vous pourriez avoir une image de 142 pixels de large – mais dans la vie réelle, elle sera stockée sous forme de tableau de 256 pixels) )
  • Une fois que vous avez construit un tableau conformément à ces principes, la méthode de groupe setRGB de BufferedImage peut copier votre tableau sur BufferedImage.