本文へジャンプ

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

送信されたすべての情報は安全です。

  • 閉じる [x]

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む


送信されたすべての情報は安全です。

  • 閉じる [x]

Merlinの魔術:グラフィック表示を完全に制御する

Fullscreen Exclusive Mode APIを使用して表示モードを変更し、ビデオRAMに直接描画する

John Zukowski, President, JZ Ventures, Inc.
Author photo
John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。

概要: ゲーム開発者やコンピューターのスピードに熱を上げるユーザーは、Merlinの新しいFullscreenExclusive Mode (FEM) APIのとりこになるでしょう。これを使用すれば、グラフィック表示に対する完全な制御を維持しながら、ビデオ・メモリーに直接書き込むことができます。Merlinの魔術の今回の記事では、Javaプログラミングの専門家であるJohn Zukowski氏が、Merlinのこの最新機能のパワーについてご紹介します。ディスカッション・フォーラムで、著者や他の読者とこの記事に関する意見を交換してください(この記事の上部または下部にあるDiscussをクリックしても、ディスカッション・フォーラムにアクセスできます)。

日付:  2003年 4月 29日
レベル:  初級 この記事の原文:  英語
アクティビティー: 1519 ビュー
お気軽にご意見・ご感想をお寄せください: 


皆さんは、ご自分のプログラムに迷惑な動作をさせて、ユーザーを困らせるのがお好きですか? 「はい」と答えた読者には、必ず今月号をお楽しみいただけると思います。J2SE1.4を使用することにより、Javaプログラムでビデオ・モードを変更し、画面を完全に制御できるようになりました。皆さん自身が文字通り完全な制御を握るのですから、他の誰も楽しく使用できるようにする必要はありません。この全能とも思えるパワーは、新しいFullscreenExclusive Mode (FEM) APIによってもたらされます。

「いいえ」と答えた、他人を困らせて楽しむようなことをしない読者も、FEM APIに多くのメリットがあることをご理解いただけると思います。FEMAPIは、ビデオRAMに直接書き込みを行うことによって表示の完全な制御を可能にするため、ゲーム開発に最適ですが、それ以外にも多くの使用法があります。たとえば、プログラムの中には、特定の画面サイズにおいてその見た目や動作が良くなるものがあります。この記事を読み進めながら、皆さんの心の内にある制御への強い希望に気付いてください。

表示モードの変更


それでは、まず始めにFEM APIの java.awt.DislayMode クラスを考察しましょう。このクラスは、特定の表示モードの画面寸法とリフレッシュ・レートをラップします。サポートされるモードは、システムのハードウェア・サポートによって決まります。

特定のシステムでサポートされる表示モードを知るには、 GraphicsEnvironment を使用します。この環境からデフォルトの画面デバイスである GraphicDevice を取得し、この画面デバイスから表示モードを取得します。リスト1はこの手順を示しています。


リスト1. 表示モードの検索
                
    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] =
      graphicsDevice.getDisplayModes();
			

また、リスト2に示すように、 getDisplayMode() メソッドを使用しても現在の表示モードを知ることができます。


リスト2. 現在の表示モードの取得
                
    DisplayMode originalDisplayMode =
      graphicsDevice.getDisplayMode();
			

モードの変更は比較的簡単ですが、まず初めに GraphicsDeviceisDisplayChangeSupported() メソッドを使用して、関係するグラフィック・デバイスが変更をサポートしているかどうかを確認する必要があります。

それがわかったら、 setDisplayMode() メソッドを使用してモードを変更し、新しいモードに移行します。表示モードの変更は、通常 try / finally ブロックで発生し、 finally ブロックではコードをリセットして元のモードに戻すようになっています。この処理は絶対に必要というわけではありませんが、これにより、プログラムの終了時に確実に安全な表示モードに戻すことができます。リスト3は、表示モードを変更する一般的なパターンを示しています。


リスト3. モードの変更
                
    GraphicsDevice graphicsDevice = ...
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    DisplayMode newDisplayMode = ...
    try {
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(newDisplayMode);
      }
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
    }
			


全画面ウィンドウの使用

FEM APIを使用すると、リスト4のようにウィンドウを GraphicsDevicesetFullScreenWindow() メソッドに渡すだけで、簡単に全画面モードに切り替えることができます。非全画面モードに戻すときには、同じメソッドに null を渡します。もちろん、初めに isFullScreenSupported() メソッドを使って GraphicsDevice が全画面モードをサポートするかどうかを確認する必要があります。


リスト4. 全画面モードへの切り替え
                
    GraphicsDevice graphicsDevice = ...
    Window window = ...
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(window);
      }
    } finally {
      graphicsDevice.setFullScreenWindow(null);
    }
			

ここまでに学習したすべての内容を示すために、リスト5に完成された例を示します。リスト5のコードでは、表示モードを取得し、変更後の表示モードをランダムに選択してから、"Hello,World!" というテキストを表示した全画面ウィンドウを表示します。新しい表示モードの特性が表示され、ウィンドウの表示サイズ、リフレッシュ・レート、およびビット・デプスを知ることができます。


リスト5. モード変更の例
                
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class DisplayModes {
  public static void main(String args[]) {
    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    JWindow window = new JWindow() {
      public void paint(Graphics g) {
        g.setColor(Color.blue);
        g.drawString("Hello, World!", 50, 50);
      }
    };
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(window);
      }
      Random random = new Random();
      int mode = random.nextInt(displayModes.length);
      DisplayMode displayMode = displayModes[mode];
      System.out.println(displayMode.getWidth() + "x" +
            displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
            " / " + displayMode.getBitDepth());
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(displayMode);
      }
      Thread.sleep(1000);
    } catch (InterruptedException e) {
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
      graphicsDevice.setFullScreenWindow(null);
    }
    System.exit(0);
  }
}
			


全画面レンダリング

リスト5で、全画面として使用されたウィンドウに、完全な機能を備えた paint() メソッドが含まれていることに注目してください。ただし、このウィンドウは全画面モードであるため、 paint() メソッドが必要とするオーバーヘッドや、メソッドがクリッピングを処理する方法、およびその他の表示処理用の動作を考慮する必要はありません。実際に、標準的なパッシブ・レンダリングはやり過ぎであり、全画面アプリケーションの速度を遅くすることがわかっています。ユーザーは、オーバーラップやウィンドウのサイズ変更などのタスクを処理する必要はありません。代わりに、もっとアクティブな方法を使用して、レンダリングそのものを処理するタイト・ループを作成してください。

ダブル・バッファリングに詳しい皆さんは、この方法でメモリー内の2つの Image オブジェクトが管理され、どちらが現在の表示情報を持っているかに基づいてこの2つがスワップされることをご存知だと思います。一方の Image を表示している間にもう一方に描画を行い、各シーン間で2つの Image オブジェクトをスワップします。

グラフィックス・カードにもこれとよく似た概念が採用されていますが、グラフィックス・カードでは実際のJava Image オブジェクトを操作するのではなく、メモリー・ページをスワップします。この方法では、メモリー・ページをスワップすると表示が変更されるため、プログラムのメモリーからビデオ・メモリーに Image オブジェクトをコピーする必要はなく、ビデオ・ポインターを変更するだけで表示が変更されます。ダブル・バッファリングの概念はまだ健在ですが、プログラム空間で Image に書き込みを行う代わりに、ビデオ・メモリー空間に直接描画するようになっています。

BufferStrategy クラスは、前述の2つのダブル・バッファリング手法のどちらが使用されるかについての情報をマスクし、システムが提供する任意のハードウェア・ベースのバッファリングを使用できるようにします。 BufferStrategy を作成するには、 createBufferStrategy() メソッドを使用して必要なバッファー数をシステムに知らせ、次に使用するバッファーを返す getDrawGraphics() メソッドを使用してバッファー間でスワップを行います。概念的にはこれが処理のすべてですが、リスト6に示すとおり、実際のコードにはもう少し工夫が必要です。


リスト6. BufferStrategyの使用
                
  JFrame frame = ...
  frame.createBufferStrategy(2); // Number of buffers to have
  BufferStrategy bufferStrategy = frame.getBufferStrategy();
  while (!done()) {  // Some condition to end
    Graphics g = null;
    try {
      g = bufferStrategy.getDrawGraphics();
      drawNextScene(g); // Method to draw to } finally {
      // Free resources
      if (g != null) {
        g.dispose();
      }
    }
    bufferStrategy.show();
  }
			

BufferStrategy を使用するときには、最後にバッファーに対して描画した項目が有効に残っているとは限りません。そのため、 contentsLost() メソッドを使用して、書き込んだ内容の有効性を確認しなければなりません。内容が失われていた場合は、バッファー全体を再描画する必要があります。バッファーの内容が残っていれば、最後に使用した時点からの変更分だけを描画すれば済みます。

BufferStrategy を使用したバッファー・サポートに加えて、 BufferCapabilities クラスの getCapabilities() メソッドを使用すると、全画面モードなど、その手法でサポートされる機能を知ることができます。


実施例

リスト7は、これまでに学んだすべての内容をまとめたサンプル・プログラムです。私にはあまり芸術的な才能がないので、洗練されたゲームを期待しないでいただけると助かります。しかし、これは、 BufferStrategy と全画面描画モードを使用した完成された実施例です。サンプル・プログラムでは、現在のバッファーに描画されなかったものを記憶することによってバッファー間の同期をとるということを、意図的にしていません。これは、複数のバッファーが操作されていることをより明確に示すためです。このプログラムは、各描画処理に10分の1秒の遅延を持たせて画面に100個の矩形をランダムに描画します。


リスト7. 実施例
                
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Random;
public class MultipleBuffers {
  public static void main(String args[]) {
    GraphicsEnvironment graphicsEnvironment =
      GraphicsEnvironment.getLocalGraphicsEnvironment();
    GraphicsDevice graphicsDevice =
      graphicsEnvironment.getDefaultScreenDevice();
    DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
    DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
    JFrame frame = new JFrame();
    frame.setUndecorated(true);
    frame.setIgnoreRepaint(true);
    try {
      if (graphicsDevice.isFullScreenSupported()) {
        graphicsDevice.setFullScreenWindow(frame);
      }
      Random random = new Random();
      int mode = random.nextInt(displayModes.length);
      DisplayMode displayMode = displayModes[mode];
      System.out.println(displayMode.getWidth() + "x" + displayMode.getHeight() +
            " \t" + displayMode.getRefreshRate() +
            " / " + displayMode.getBitDepth());
      if (graphicsDevice.isDisplayChangeSupported()) {
        graphicsDevice.setDisplayMode(displayMode);
      }
      frame.createBufferStrategy(2);
      BufferStrategy bufferStrategy = frame.getBufferStrategy();
      int width = frame.getWidth();
      int height = frame.getHeight();
      for (int i=0; i<100; i++) {
        Graphics g = bufferStrategy.getDrawGraphics();
        g.setColor(new Color(random.nextInt()));
        g.fillRect(random.nextInt(width), random.nextInt(height), 100, 100);
        bufferStrategy.show();
        g.dispose();
        Thread.sleep(100);
      }
    } catch (InterruptedException e) {
    } finally {
      graphicsDevice.setDisplayMode(originalDisplayMode);
      graphicsDevice.setFullScreenWindow(null);
    }
    System.exit(0);
  }
}
			

バッファー間の同期をとったり、バッファーの内容が失われた場合にバッファー全体を再描画する必要があるかどうかを確認するなどの機能強化を加えれば、このサンプル・プログラムはさらに良くなります。同期をとるには、最後に描画された矩形(とその色) を記憶しておく必要があるだけですが、バッファーの再描画の必要性を確認するには、描画された矩形をすべて記憶しておく必要があります。


まずは画面を制することから始めよう

新しいFullscreen Exclusive Mode APIの登場より、Javaによる開発はゲーム開発の主流になります。このAPIにより、DirectXやOpenGLなどのプラットフォーム固有のAPIを操作する必要がなくなり、すべてのJava対応プラットフォームで標準のAPIを使用できるようになります。これは、野心的なプログラミングの独裁者にも想像がつかないほどのすばらしい進歩です。


参考文献

  • この記事に関するディスカッション・フォーラムにご参加ください (この記事の上部または下部にある Discuss をクリックしても、ディスカッション・フォーラムにアクセスできます)。

  • John Zukowski氏のMerlinの魔術のコラムのアーカイブでは、J2SE 1.4についてのさまざまなヒントが得られます。

  • Retained Graphic Object API入門 」(developerWorks、2001年11月) では、Retained Graphic Objects (RGO) APIについてBarry Feigenbaum氏が詳しく説明しています。このAPIは、Javaプラットフォーム用の再利用可能なグラフィック・オブジェクトの作成を可能にするものです。

  • AlphaCompositeクラスは、デジタル・イメージ合成に関する12のPorter-Duffのルールのうち、これまでは8つしかサポートしていませんでしたが、12のルールすべてをサポートするようになりました。Merlinの魔術の初期の記事「 Porter-Duffのルール! 」(developerWorks、2001年9月) では、John Zukowski氏が この12のルールをすべて説明し、インタラクティブなプログラムを使用してそのルールがどのように働いているかを示しています。

  • FEM APIの背景情報をさらに詳しくお知りになりたい方は、Sun Microsystemsの New Fullscreen Exclusive Mode API の資料をお読みください。

  • Bugtraq Report # 4189326 もご覧下さい (無料のログインが必要)。

  • DisplayMode クラスについてさらに学んでください。

  • GraphicsDevice クラスについても、さらに詳細な情報があります。

  • Javaチュートリアルでは、 FEM APIの詳細情報 が提供されています。

  • Java technology zone では、developerWorks が提供するJavaプログラミングに関する多数の記事を参照することができます。

著者について

Author photo

John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。

不正使用の報告のヘルプ

不正使用の報告

ありがとうございます。 このエントリーは、モデレーターの注目フラグが設定されました。


不正使用の報告のヘルプ

不正使用の報告

不正使用の報告の送信に失敗しました。


developerWorks: サイン・イン


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


お客様が developerWorks に初めてサインインすると、プロフィールが作成されます。 プロフィールで選択した情報は公開されますが、いつでもその情報を編集できます。 お客様の姓名(非表示設定にしていない限り)とディスプレイ・ネームは、投稿するコンテンツと一緒に表示されます。

表示名をお選びください

developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

(半角英数字で3文字以上31文字以下にする必要があります)


「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 利用条件

 


この記事を評価する

コメント

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology
ArticleID=218824
ArticleTitle=Merlinの魔術:グラフィック表示を完全に制御する
publish-date=04292003
author1-email=jaz@zukowski.net
author1-email-cc=jaloi@us.ibm.com

タグ

Help
このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。

スライダーバーを使用することで、より多く(少なく)タグを表示します。

人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。

マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。

このタグで、My developerWorks のすべてのタイプのコンテンツを見つけるために検索フィールドを使用します。人気のタグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するトップのタグを表示します。マイ・タグは、この特定のコンテンツ・ゾーン(例えば、Java テクノロジー、Linux や WebSphere など)に対するお客様ご自身のタグを表示します。