我有一个 JTable,需要非常频繁地重新绘制每个单元格,其中表格的单元格说明了灰度图像。通常图像需要缩放以适合绘制区域。我在 JComponent 的方法中有两个实现paintComponent(Graphics g)
:
- 创建缩放图像
Image.getScaledInstance()
,然后使用 Graphics 绘制它。 - 让 Graphics “即时”缩放图像;
第一种方法运行速度更快,并且需要更少的 CPU。但它会改变图像的颜色(灰度)。下图说明了这个问题。第一种方法(错误颜色)在左边,第二种方法(真颜色)在右边。
问题:为什么会发生这种情况以及如何解决?或者,任何其他解决方案将不胜感激。
注意:在此代码示例中,我实际上并未缩放图像,但调用Image.getScaledInstance()
仍会更改图像值。
编码:
class CellImage extends JComponent {
final boolean rescale_img;
final byte value;
CellImage(boolean flag, byte v) {
rescale_img = flag;
value = v;
}
@Override
public void paintComponent(Graphics g) {
Rectangle r = g.getClipBounds();
BufferedImage image = new BufferedImage(r.width, r.height, BufferedImage.TYPE_BYTE_GRAY);
byte[] bytes = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Arrays.fill(bytes, value);
if (rescale_img){
Image scaled = image.getScaledInstance(r.width, r.height, Image.SCALE_REPLICATE);
g.drawImage(scaled, 0, 0, null);
} else {
g.drawImage(image, 0, 0, null);
}
}
}
如果需要,请参阅下面的整个 SSCCE:
package book_test_paint;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.util.Arrays;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
class CellImage extends JComponent {
final boolean rescale_img;
final byte value;
CellImage(boolean flag, byte v) {
rescale_img = flag;
value = v;
}
@Override
public void paintComponent(Graphics g) {
Rectangle r = g.getClipBounds();
BufferedImage image = new BufferedImage(r.width, r.height, BufferedImage.TYPE_BYTE_GRAY);
byte[] bytes = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
Arrays.fill(bytes, value);
if (rescale_img){
Image scaled = image.getScaledInstance(r.width, r.height, Image.SCALE_REPLICATE);
g.drawImage(scaled, 0, 0, null);
} else {
g.drawImage(image, 0, 0, null);
}
}
}
class CellText extends JComponent {
final String str;
CellText(String s){
str = s;
}
@Override
public void paintComponent(Graphics g) {
Rectangle r = g.getClipBounds();
g.setColor(Color.BLACK);
g.fill3DRect(0, 0, r.width, r.height, true);
g.setColor(Color.WHITE);
((Graphics2D) g).drawString(str, 0.1f * r.width, 0.9f * r.height);
}
}
class MyTableModel extends DefaultTableModel {
final int nrows = 17;
@Override
public int getRowCount() {
return nrows;
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public Object getValueAt(int row, int column) {
Object obj = null;
// The value below represents the desired
// gray scale of the image in range [0, 255]
int val = (int)Math.min(255, row * 256.0 / (nrows - 1));
switch (column) {
case 0:
obj = new CellImage(true, (byte)val);
break;
case 1:
obj = new CellText("" + val);
break;
case 2:
obj = new CellImage(false, (byte)val);
break;
}
return obj;
}
}
public class Image_Scaling extends JTable {
public Image_Scaling(JPanel panel) {
setModel(new MyTableModel());
setRowHeight(25);
getColumnModel().getColumn(0).setPreferredWidth(200);
getColumnModel().getColumn(1).setPreferredWidth(40);
getColumnModel().getColumn(2).setPreferredWidth(200);
JTableHeader header = getTableHeader();
header.getColumnModel().getColumn(0).setHeaderValue("with getScaledInstance()");
header.getColumnModel().getColumn(1).setHeaderValue("gray");
header.getColumnModel().getColumn(2).setHeaderValue("without getScaledInstance()");
panel.setLayout(new BorderLayout());
panel.add(header, BorderLayout.NORTH);
panel.add(this, BorderLayout.CENTER);
}
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
return (Component) dataModel.getValueAt(row, column);
}
public static void main(String[] args) {
JPanel panel = new JPanel(new BorderLayout());
final Image_Scaling table = new Image_Scaling(panel);
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panel, BorderLayout.CENTER);
frame.pack();
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
}
}