Java2D paintComponent optimization question

Hi All,

Any help with this would be greatly appreciated. I'm attempting to set up a Panel with a series of small images which is fairly dynamic. I'm using paintComponent for the panel to make it dynamic on resize and on the images so that I can modify the images and make some semi-transparent, etc... I'm finding issues when I make the window small and scroll around the content. There's a lot of artifacts hanging around and many of the images don't show up at all. It's essentially really flaky. Simplified sample code below, does anyone have advice on fixing?

Thanks!

Mike

CentralPane.java:

packagedefault;

import javax.swing.*;

import java.awt.*;

import java.util.*;

publicclass CentralPaneextends JPanel{

private Vector images;

private Toolkit kit;

public CentralPane()

{

kit = Toolkit.getDefaultToolkit();

images =new Vector();

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

images.add("c:\\sf.jpg");

}

privatevoid drawImages()

{

int startLeft = 15;

int imgSpacing = 33;

int rowSpacing = 41;

int heightSpacing = 139;

int imgWidth = 71;

int imgHeight = 101;

int imgOffset = 16;

int startTop = heightSpacing - (imgHeight+imgOffset);

int imgsPerRow = (int)(((double)(this.getWidth()-(startLeft*2)))/((double)(imgSpacing+imgWidth)));

if (imgsPerRow < 1)

imgsPerRow = 1;

int numRows = (int)((double)images.size()/(double)imgsPerRow);

int panelHeight = heightSpacing*numRows;

int imgsLastRow = images.size() % imgsPerRow;

this.setPreferredSize(new Dimension(this.getWidth(), panelHeight));

int panelWidth = this.getWidth();

if (imgsLastRow > 0 )

numRows++;

for (int i=0;i<numRows;i++)

{

int imgsInRow = (i==numRows-1)?imgsLastRow:imgsPerRow;

for (int j=0;j<imgsInRow;j++)

{

ImagePane myImage =new ImagePane((String)images.get((i*imgsPerRow)+j), i,j);

myImage.setBounds(startLeft+(j*(imgSpacing+imgWidth)), startTop+(i*heightSpacing), imgWidth, imgHeight);

this.add(myImage);

}

}

}

publicstaticvoid main (String arg[])

{

CentralPane pane =new CentralPane();

JScrollPane jScrollPane1 =new javax.swing.JScrollPane();

jScrollPane1.setViewportView(pane);

JFrame frame =new JFrame();

frame.setContentPane(jScrollPane1);

frame.pack();

frame.setBounds(10,10, 600,600);

frame.setVisible(true);

}

@Override

protectedvoid paintComponent(Graphics g){

System.out.println("Repainting Central Pane");

super.paintComponent(g);

this.regenerateImages();

}

privatevoid regenerateImages()

{

this.removeAll();

this.drawImages();

}

}

ImagePane.java :

packagedefault;

import java.awt.AlphaComposite;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Image;

import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;

import javax.swing.JPanel;

publicclass ImagePaneextends JPanel{

private Image myImage;

privateint row;

privateint col;

public ImagePane (String filename,int r,int c)

{

row = r;

col = c;

myImage =new ImageIcon(filename).getImage().getScaledInstance(71, 101, Image.SCALE_DEFAULT);

// Every second one should be semi-transparent

if (c % 2 == 0)

{

BufferedImage bi =new BufferedImage(71, 101,

BufferedImage.TYPE_INT_ARGB);// ARGB to support transparency if in original image

Graphics2D g2 = bi.createGraphics();

g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, 0.5f));

g2.drawImage(myImage, 0, 0,null);

myImage =new ImageIcon(bi).getImage();

}

}

@Override

protectedvoid paintComponent(Graphics g){

// TODO Auto-generated method stub

System.out.println("Repainting Image Pane - R:"+row+" C:"+col);

g.drawImage(myImage, 0, 0,this);

}

}

>

[8638 byte] By [mikekellanda] at [2007-11-26 15:03:24]
# 1

This concerns basic design. You can do this one of two general ways: draw the images in

paintComponent or use components. But not both.

You are removing, resizing and adding components in your paintComponent code. This won't

work well. Also, getScaledInstance is extremely slow compared to BufferedImage scaling.

The image returned from the method loads asynchronously, as does those returned from

getImage in the beginning.

To do this in your drawing code, get rid of the image component, load your images, and

draw them in paintComponent. You can create and use an array of Rectangles to locate the

images. Relocate and resize the rectangles for componentResized events, not for every trip

through paintComponent.

For the component approach do not override either paintComponent or paint in the container

that will host the image components. You can resize and relocate the image components for

componentResized events.

Using newer BufferedImage methods will speed things up a lot.

Images with non-opaque color run slower than opaque images.

Here's an example of using drawing code for this:

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.AffineTransform;

import java.awt.image.BufferedImage;

import java.io.*;

import javax.imageio.ImageIO;

import javax.swing.*;

public class FasterImages extends JPanel {

BufferedImage[] images;

BufferedImage[] scaled;

Rectangle[] rects;

AlphaComposite ac;

public FasterImages(BufferedImage[] images) {

this.images = images;

rects = new Rectangle[images.length];

for(int j = 0; j < rects.length; j++)

rects[j] = new Rectangle();

int rule = AlphaComposite.SRC_OVER;

float alpha = 0.5f;

ac = AlphaComposite.getInstance(rule, alpha);

}

protected void paintComponent(Graphics g) {

super.paintComponent(g);

Graphics2D g2 = (Graphics2D)g;

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

if(scaled == null)

initScaledImages();

Composite plain = g2.getComposite();

for(int j = 0; j < scaled.length; j++) {

if(j % 2 == 0)

g2.setComposite(plain);

else

g2.setComposite(ac);

g2.drawImage(scaled[j], rects[j].x, rects[j].y, this);

}

}

private void initScaledImages() {

scaled = new BufferedImage[images.length];

int w = ((JViewport)getParent()).getExtentSize().width;

int startLeft = 15;

int imgSpacing = 33;

int rowSpacing = 41;

int heightSpacing = 139;

int imgWidth = 71;

int imgHeight = 101;

int imgOffset = 16;

int startTop = heightSpacing - (imgHeight+imgOffset);

int imgsPerRow = (w-(startLeft*2))/(imgSpacing+imgWidth);

if (imgsPerRow < 1)

imgsPerRow = 1;

int numRows = images.length / imgsPerRow;

int imgsLastRow = images.length % imgsPerRow;

if (imgsLastRow > 0 )

numRows++;

int panelHeight = heightSpacing * numRows;

setPreferredSize(new Dimension(w, panelHeight));

//int panelWidth = this.getWidth();

for (int j = 0, count = 0; j < numRows; j++) {

int imgsInRow = (j == numRows-1) ? imgsLastRow : imgsPerRow;

for (int k = 0; k < imgsInRow; k++) {

scaled[count] = scale(images[count], imgWidth, imgHeight);

rects[count++].setFrame(startLeft+(k*(imgSpacing+imgWidth)),

startTop+(j*heightSpacing),

imgWidth, imgHeight);

}

}

revalidate();

}

private BufferedImage scale(BufferedImage src, int w, int h) {

BufferedImage scaled = new BufferedImage(w, h, src.getType());

Graphics2D g2 = scaled.createGraphics();

g2.setBackground(getBackground());

g2.clearRect(0,0,w,h);

double xScale = (double)w / src.getWidth();

double yScale = (double)h / src.getHeight();

double scale = Math.min(xScale, yScale);

double x = (w - scale*src.getWidth())/2;

double y = (h - scale*src.getHeight())/2;

AffineTransform at = AffineTransform.getTranslateInstance(x, y);

at.scale(scale, scale);

g2.drawRenderedImage(src, at);

g2.dispose();

return scaled;

}

public static void main(String[] args) throws IOException {

String[] ids = {

"--", "-c", "-cg--", "-c-h-", "-c--t", "--g--", "h-", "-t"

};

BufferedImage[] images = new BufferedImage[ids.length];

for(int j = 0; j < images.length; j++)

images[j] = ImageIO.read(new File("images/geek" + ids[j] + ".gif"));

final FasterImages test = new FasterImages(images);

JFrame f = new JFrame();

f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

f.getContentPane().add(new JScrollPane(test));

f.setSize(400,400);

f.setLocation(200,200);

f.setVisible(true);

EventQueue.invokeLater(new Runnable() {

public void run() {

test.addComponentListener(test.resizer);

}

});

}

private ComponentListener resizer = new ComponentAdapter() {

public void componentResized(ComponentEvent e) {

scaled = null;

repaint();

}

};

}

crwooda at 2007-7-8 8:52:54 > top of Java-index,Security,Cryptography...
# 2
Fantastic, thanks so much for your excellent help with this! Mike
mikekellanda at 2007-7-8 8:52:54 > top of Java-index,Security,Cryptography...