質問
JPanel を持っていますその場で生成するJPEGおよびPNG画像を追加したい。
これまでに Swingチュートリアルで見たすべての例、特に Swingの例では、 ImageIcon
s。
これらの画像をバイト配列として生成していますが、通常、例で使用する一般的なアイコン(640x480)よりも大きくなっています。
- JPanelでImageIconクラスを使用してそのサイズの画像を表示する際に(パフォーマンスまたはその他の)問題はありますか?
- それを行うための通常の方法は何ですか?
- ImageIconクラスを使用せずにJPanelに画像を追加する方法
編集:チュートリアルとAPIをさらに注意深く調べると、ImageIconをJPanelに直接追加できないことがわかります。代わりに、画像をJLabelのアイコンとして設定することにより、同じ効果を実現します。これは正しくないようです...
解決
これを行う方法は次のとおりです(画像の読み込み方法についてもう少し詳しく説明します):
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
}
}
他のヒント
JPanelsを使用している場合は、おそらくSwingを使用しています。これを試してください:
BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);
画像は現在、swingコンポーネントです。他のコンポーネントと同様にレイアウト条件の対象となります。
Fred Haslamの方法はうまく機能します。ただし、jar内のイメージを参照するため、ファイルパスに問題がありました。これを行うには、次を使用しました。
BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));
このメソッドを使用してロードする必要がある有限数(約10)の画像しかないため、非常にうまく機能します。正しい相対ファイルパスを持たなくてもファイルを取得します。
サブクラス化する必要はないと思います。 Jlabelを使用するだけです。画像をJlabelに設定できます。したがって、Jlabelのサイズを変更してから、画像で塗りつぶします。大丈夫です。これが私のやり方です。
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
- 問題はないはずです(非常に大きな画像で発生する可能性がある一般的な問題以外)。
- 単一のパネルに複数の画像を追加する場合は、
ImageIcon
を使用します。単一の画像の場合、JPanel
のカスタムサブクラスを作成し、そのpaintComponent
メソッドをオーバーライドして画像を描画することを考えます。 - (2を参照)
JPanelをサブクラス化できます-これは、ImagePanelからの抜粋です。上/左、上/右、中/中、下/左、下/右の5つの場所のいずれかに画像を配置します。
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
はほとんどの場合、サブクラスにとって間違ったクラスです。なぜ JComponent
をサブクラス化しないのですか?
コンストラクターがイメージの読み取りをブロックするという点で、 ImageIcon
にはわずかな問題があります。アプリケーションjarからロードする場合、実際には問題ではありませんが、ネットワーク接続を介して読み取りを行う可能性がある場合は問題ありません。 JDKデモでも、 MediaTracker
、 ImageObserver
および友人を使用したAWT時代の例がたくさんあります。
私が取り組んでいるプライベートプロジェクトで非常によく似たようなことをしています。これまで、問題なし(メモリを除く)で最大1024x1024の画像を生成し、パフォーマンスの問題なく非常に高速に表示できました。
JPanelサブクラスのpaintメソッドのオーバーライドは過剰であり、必要以上の作業が必要です。
私がやる方法は:
Class MapIcon implements Icon {...}
または
Class MapIcon extends ImageIcon {...}
画像の生成に使用するコードはこのクラスにあります。 BufferedImageを使用して描画し、paintIcon()が呼び出されたときにg.drawImvge(bufferedImage)を使用します。これにより、画像の生成中に行われるフラッシュの量が減り、スレッド化できます。
次に、JLabelを拡張します:
Class MapLabel extends Scrollable, MouseMotionListener {...}
これは、スクロールペインに画像を配置したいためです。画像の一部を表示し、必要に応じてユーザーにスクロールさせます。
したがって、JScrollPaneを使用して、MapIconのみを含むMapLabelを保持します。
MapIcon map = new MapIcon ();
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport ().add (mapLabel);
しかし、あなたのシナリオでは(毎回画像全体を表示するだけです)。 MapLabelを最上位のJPanelに追加し、それらをすべて画像のフルサイズに合わせてサイズ変更する必要があります(GetPreferredSize()をオーバーライドします)。
プロジェクトディレクトリにソースフォルダーを作成します。この場合は、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();
この回答は、@ shawalliの回答を補完するものです...
jar内の画像も参照したかったのですが、BufferedImageを使用する代わりに、次のようにしました。
JPanel jPanel = new JPanel();
jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
独自の Component
とSwingXライブラリおよび ImageIO
クラスの使用を避けることができます:
File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
私は多くの答えを見ることができますが、実際にはOPの3つの質問に対処しているわけではありません。
1)パフォーマンスに関する言葉:バイト配列は、ディスプレイアダプターの現在の解像度と色深度に一致する正確なピクセルバイト順序を使用できない限り、非効率である可能性があります。
最高の描画パフォーマンスを実現するには、現在のグラフィックス構成に対応するタイプで生成されるBufferedImageに画像を変換するだけです。 https://docs.oracle.com/javase/tutorialのcreateCompatibleImageを参照してください。 /2d/images/drawonimage.html
これらの画像は、プログラミング作業なしで数回描画した後、ディスプレイカードメモリに自動的にキャッシュされます(これはJava 6以降のSwingの標準です)。したがって、実際の描画にかかる時間はごくわずかです- if 画像を変更していません。
画像の変更には、メインメモリとGPUメモリ間の追加のメモリ転送が伴います-これは遅いです。 「再描画」を避けるしたがって、画像をBufferedImageに格納する場合は、getPixelおよびsetPixelを絶対に行わないでください。
たとえば、すべてのゲームアクターをBufferedImageに、次にJPanelに描画する代わりにゲームを開発している場合、すべてのアクターを小さなBufferedImageとしてロードし、それらを1つずつ描画する方がはるかに高速です適切な位置にあるJPanelコード-この方法では、キャッシュ用の画像の初期転送を除き、メインメモリとGPUメモリ間の追加のデータ転送はありません。
ImageIconは内部でBufferedImageを使用しますが、基本的に proper グラフィックモードでBufferedImageを割り当てることが重要であり、これを正しく行う努力はありません。
2)これを行う通常の方法は、JPanelのオーバーライドされたpaintComponentメソッドでBufferedImageを描画することです。 Javaは、GPUメモリにキャッシュされたVolatileImagesを制御するバッファーチェーンなどの追加の優れた機能をサポートしていますが、GPUアクセラレーションのこれらすべての詳細を公開せずに適度に良好なジョブを実行するJava 6以降、これらを使用する必要はありません。
GPUアクセラレーションは、半透明の画像のストレッチなどの特定の操作では機能しない場合があります。
3)追加しないでください。上記のようにペイントしてください:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
"追加"画像がレイアウトの一部である場合に意味があります。これをJPanelを埋める背景または前景の画像として必要な場合は、paintComponentで描画してください。画像を表示できる一般的なSwingコンポーネントを作成したい場合、それは同じ話です(JComponentを使用してpaintComponentメソッドをオーバーライドできます)-そして、GUIコンポーネントのレイアウトに this を追加します。
4)配列をBufferedimageに変換する方法
バイト配列をPNGに変換してからロードすると、かなりのリソースが消費されます。より良い方法は、既存のバイト配列をBufferedImageに変換することです。
そのため:ループには使用しないとピクセルのコピー。それは非常に遅いです。代わりに:
- BufferedImageの優先バイト構造を学習します(現在はRGBまたはRGBA(ピクセルあたり4バイト)を想定しても安全です)
- 使用中のスキャンラインとスキャンサイズを学習します(たとえば、142ピクセル幅の画像がある場合がありますが、実際には256ピクセル幅のバイト配列として保存されます。 GPUハードウェア)
- これらの原則に従って配列を作成すると、BufferedImageのsetRGB配列メソッドは配列をBufferedImageにコピーできます。