0

i'm setting up a custom cell renderer for a JTable, that has to contain an album cover image, that is from a byte[] taken with another library, the problem is that converting to a BufferedImage is so slow that in earlier version of the code the program wouldn't even start. Now the situation is better, but the lag is very noticeable nonetheless. Here is the code:

public class ImageCellRenderer extends DefaultTableCellRenderer {
   
   JLabel lbl = new JLabel();
   
   public Image[] getAlbumart() throws IOException {

      Image[] albumArt = new Image[allmymusic.size()];
          
      for (int i = 0; i < allmymusic.size(); i++) {
         byte[] data = allmymusic.get(i).albumImageData;
         ImageIO.setUseCache(false);
             
         if(allmymusic.get(i).albumImageData != null){

            BufferedImage bImage;                
            try(ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
               bImage = ImageIO.read(bis);
            }            
            ImageIcon icon = new ImageIcon(bImage);
            Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH);
            // ImageIcon iconscaled = new ImageIcon(image);
            albumArt[i] = image;

         } else {
            albumArt[i] = null;
         }
      }
      return albumArt ;
   }

   @Override
   public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
      ImageIcon icon = new 
      ImageIcon(getClass().getResource("/images/defaultimage.png"));
      Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH);
      Image[] albumArt;
        
       try {
          albumArt = getAlbumart(); 
          if(albumArt[row] == null){
             lbl.setIcon(new ImageIcon(image));
          } else {
             lbl.setIcon(new ImageIcon(albumArt[row]));
          }
       } catch (IOException ex) {      
          Logger.getLogger(ImageCellRenderer.class.getName())
                .log(Level.SEVERE, null, ex);
       }
       return lbl;
    }
}   

I accept alternative solutions.

Popovkov57
  • 179
  • 16
  • There is so much potential problems here that you need to hire someone who will refactor the app and solve the isseus. – Krzysztof Cichocki Jul 22 '22 at 20:48
  • very useful thank you (it is actually not) – Costantino Tessera Jul 22 '22 at 20:58
  • 2
    *i'm setting up a custom cell renderer for a JTable* - a custom renderer is NOT needed. The Icon should be stored in the model. Just override the `getColumnClass(...)` method and the Icon renderer will be used. Never do I/O in a rendering method. The rendering logic of components like JTable or JList is called repeatedly and the rendering logic so be as efficient as possible. – camickr Jul 22 '22 at 21:23
  • how? I tried to do that but it still only displays text – Costantino Tessera Jul 22 '22 at 21:26
  • See: https://stackoverflow.com/questions/5614875/how-to-set-icon-in-a-column-of-jtable/5615516#5615516. You can also check out: https://stackoverflow.com/questions/67341415/jlist-repaint-single-element/67348202#67348202 which demonstrates a ImageViewer. The code will read all the images in a directory and create a thumbnail version of each image to be displayed in a JLIst. – camickr Jul 22 '22 at 21:41
  • @Krzysztof Cichocki yeah now that i solved it i see how dumb my code was, just would've been nice to know exactly what i did wrong beforehand, and "hire someone to refactor"? A little dramatic. – Costantino Tessera Jul 22 '22 at 21:54

3 Answers3

1

On a first glimpse, I deem it unnecessary instantiating a ImageIcon object to get a scaled image of the BufferedImage, since that can be done directly from the BufferedImage itself:

Image image = bImage.getScaledInstance(50, 50, Image.SCALE_SMOOTH);

A second smell lies in this line:

ImageIcon icon = new ImageIcon(getClass().getResource("/images/defaultimage.png"));
Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH);

Since the image is being read from a constant static resource, you should avoid repeating performing the same operation again and again: Better read the resource and build the image just once, and store it in a static variable:

private static final Image MY_STATIC_ICON=readStaticIcon();

private static Image readStaticIcon()
throws IOException
{
    ImageIcon icon = new ImageIcon(MyClass.class.getResource("/images/defaultimage.png"));
    Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH);
    return image;
}

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    Image image = MY_STATIC_ICON;
    Image[] albumArt;
    try {
        albumArt = getAlbumart();
        ...
Little Santi
  • 8,563
  • 2
  • 18
  • 46
0
  1. why you always instantiate a new ImageIcon like this, can't you use one instance:
@Override
 public Component getTableCellRendererComponent(JTable table, Object value, 
 boolean isSelected,
 boolean hasFocus, int row, int column) {
 
 ImageIcon icon = new 
 ImageIcon(getClass().getResource("/images/defaultimage.png"));
 Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMO

2.This is very processor expensive , can you create thubnails and load ready images instead of generating it on the fly like this:

Image image = icon.getImage().getScaledInstance(50, 50, Image.SCALE_SMOOTH);
  1. Do you really need to load everything in single method "public Image[] getAlbumart()", can't you make it so it loads the images when they are needed to be displayed? Probable you could use a SwingWorker https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingWorker.html

  2. There is great book that will help you to desing and use Swing GUI in java: Filthy Rich Clients: Developing Animated and Graphical Effects for Desktop Java¿ Applications

Krzysztof Cichocki
  • 6,294
  • 1
  • 16
  • 32
-1
Your_JTable.getColumnModel().getColumn(0).setCellRenderer((JTable jtable, Object value, boolean bln, boolean bln1, int i1, int i2) -> {
                JLabel label = new JLabel();
                label.setIcon((ImageIcon) value);
                return label;
            });

With this the trick should be done. It just sets the selected column to Have a JLabel inside of it with an imageIcon. Thank you @camickr for suggesting the solution.

  • stick to java naming naming conventions when showing java code - even in snippets :) – kleopatra Jul 23 '22 at 13:12
  • repeating the comment by @camickr - there is __no__ need to re-invent the wheel by implementing a custom renderer - JTable has a default renderer for Icon, simply let it do its job ;) – kleopatra Jul 23 '22 at 13:21