JTable があり、各セルを頻繁に再描画する必要があります。テーブルのセルはグレースケールの画像を示しています。通常、画像は、ペイントされた領域に合わせてペイントするためにスケーリングする必要があります。JComponent のメソッド内に 2 つの実装がありますpaintComponent(Graphics g)
。
- スケーリングされたイメージ
Image.getScaledInstance()
を作成し、Graphics を使用して描画します。 - Graphics に「その場で」画像をスケーリングさせます。
最初の方法はより速く動作し、より少ない CPU を必要とします。ただし、画像の色(グレーレベル)が変わります。以下の画像は、この問題を示しています。左が最初の方法 (間違った色)、右が 2 番目の方法 (真の色)。
質問:これが発生する理由と解決方法を教えてください。あるいは、他の解決策があれば大歓迎です。
注: このコード例では、実際に画像をスケーリングしませんが、呼び出すと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);
}
});
}
}