JAVA press Vol.3 Java2DとThe WALLシステム

                                                             村崎 達哉

                               JAVA press Vol.1(技術評論社)

RENGA

●はじめに

この記事が掲載されるころには、JDK1.2も正式にJavaSoftからリリースされているでしょうか。編集者の方から、Java2DやJava3D、AnimationやSpeech、TelephonyなどのいわゆるMedia APIの概説記事を書いてくれというメイルがあって、ちょっと忙しいので厳しいかなとお断りしようと考えたのですが、このごろ個人的にバイクがほしいなと思っていたので頭金稼ぎにお引き受けすることにしました。んー小市民。

しかし、いくらなんでもMedia API全体の記事を書くのはしんどいので、Java2Dだけに今回はしてもらおうと思いました。実際、読者にとっては、Multimedia APIの全体像を知りたいということで、概説的な記事を望んでおられる方も多いと思います。けれど私としても、仕事と絡めて記事を書くのが効率の面からいってベストでですので、最近かかわっているJava2Dをプログラミングレベルで具体的に説明することにしました。プログラムのない概説記事は、全体像は見渡せても、結局のところプログラマにとっては役に立ちませんからね。APIのサンプルがないために散々苦労して、ようやく動作するようになったとか、このAPIを使えばいいのだろうとは分かっていても、サンプルがないためにぜんぜん使い方が分からない、という思いをしたプログラマは多いでしょう。Windows API、MFC、そしてJDKなど、最近の開発では言語の仕様を知っているだけではプログラムはできません。

プログラムの例題としては2Dペイントプログラムを取り上げます。え?Javaでペイントソフト書けるのと思われる方も多いでしょう。それが、結構いけるのです。そして、Java2Dの登場によって、本格的なグラフィックスソフト、ペイントソフトやDTPソフトも非現実的ではありません。信じない人は我々The WALLプロジェクトが作成したプログラムを使ってみてください。

●連画とThe WALL

余談になりますが...いいえ、余談どころかこの記事の目的の一つにもなっているのですが、私が現在かかわっている開発について説明、宣伝しなければなりません。ウォールと聞くと、またどうせLinuxやFreeBSDのフリーUNIX系のファイヤーウォールかなにかのシステムの話ではないかと思われるかもしれません。大はずれです。一言でいうと

<The WALLはインターネット上の落書き専用の壁です。>

みんなが集まってお絵かきするためのネットワークペイントシステムです。これは、商業的プログラムではなく、<アート>です。このThe WALLシステムの考案者である安斎利洋さんと中村理恵子さんは、1992年より「連画」というメディアアートの実験を続けてきました。「連画」(レンガ)というのは「連歌」(レンガ)のアナロジーからきているのだと思います。連歌は短歌の上の句と下の句を別々の人が詠んで1つの歌を作る創作活動のことをいいます。同様に連画も複数の人が共同で、一連の組み作品を作成します。連画のホームページがありますのでそこから引用しますと、

<連画とは、CG作品を電子ネットワークを通して送りあい、相手の作品を引用したり直接手を加えることによって新しい作品を作り、組作品を成長させる創作システムである>

ということになります。「百聞は一見」ですので、そのサンプルをいくつか紹介しましょう。

RENGA

連画サイト:http://www.renga.com

いかがでしょうか。とてもユニークな試みですので、興味をもたれた方はぜひ連画のサイトにアクセスしてみてください。

連歌

図1

そして、その連画チームが通産省系の団体であるMMCA(マルチメディアコンテンツ協会)の公募に採用されて、その補助金で開発を進めているのが、「連画メソッドの応用実験」です。この実験には大別して2つのパートがあります。

です。これらのプロジェクトのスタッフを紹介します。敬称は略です

この他それぞれのセッションでは、触覚連画では造形作家の光島貴之さん、国際連画では海外のアーティストの方々、教育連画では横浜市の中川一史先生、佐藤幸江先生、佐藤教室の5年生の皆さんの参加協力があります。

●The WALLシステム

さて、ここまで読み進んでこられた方は、だからその連画がどうしてこのJAVA PRESS誌上で紹介されるのかということを訝る向きもあると思います。あまり突っ込まれると困るのですが、強いてあげれば、なんとThe WALLは100% Pure Javaで書かれています。しかも、現段階ではまだベータリリースのJKD1.2beta4で開発中です。特にペインター部分は、最新のJava2Dを使っています。なぜまだリリースされていないJava 2Dを使うかって、その答えは単純です。以前のjava.awt.Graphicsの描画機能ではとてもまともなペイントソフトは書けないし、Java2Dの機能と比べると月とスッポンほどの差があるからです。

そして、更にもうひとつ理由があります。プログラムは何もゲームを作ったり、業務ソフトを作ったりするためだけにあるのではないことを皆さんに訴えるためにThe WALLを紹介しました。プログラムは、画家や彫刻家の絵筆やノミと同じようにアート作品を想像するためのすばらしい道具となります。その作品はダイナミックに動きます。

ちょっと先走りすぎました。The WALLシステムの説明をしなければなりません。

The WALLは連画的創作を支援するネットワークシステムです。このシステムは、円筒状の壁を隠喩(メタファー)とした、2Dコンピュータ・グラフィックスの共同創作支援システムでです(図2)。クライアント・サーバ型で、複数のユーザがネットワークを通して、The WALLのキャンバスを共有することができます。

システム構成

図2システム構成

ソフトウェアの構成は次のようになります。

ソフトウェア構成

(図3ソフトウェア構成)

それでは、各モジュールの機能をざっと見てみましょう。

◇The WALLサーバ

The WALLサーバは、「壁」をメタファーとする画像データを管理する機能をもち、連画の参加者が壁の上に落書きをする感覚を持ちながら、多人数での協調作業を自然に進行するための機能を提供する。 The WALLサーバ上の画像データは、壁の上に紙の小片を貼り重ねていくように蓄積する。The WALLサーバは、小片に相当する画像、画像間の関係、作成日時などをデータベースとして管理する。The WALLサーバは、アクセス者の要求に応じて画像を入力し、またアクセス者の要求に応じて蓄積された画像の一部と履歴情報を出力する。

◇The WALLページジェネレータ

The WALLページジェネレータは、WWWサーバから通知されたユーザからの要求によって、The WALLサーバのデータを入力とし、壁のルックフィールを持つWWW画面を自動生成し、出力する。

◇The WALLクライアント

The WALLクライアントは、The WALLサーバに接続し、画像を一覧し、ユーザによって選択された任意の一部をダウンロードし、ローカルファイルとして出力する機能を持つ。The WALLクライアントはまた、The WALLペインタなどによって加筆修正された画像ファイルを、The WALLクライアントにアップロードする機能を持つ。

◇The WALLペインタ

The WALLペインタは、The WALLクライアントによってローカルサイドにダウンロードされた画像ファイルを対象とし、画像に対する各種描画機能を提供する。

さて、The WALLシステムの概略を説明しました。これだけだったら、「ふーん多人数で一緒に絵を描くシステムね、そんなのがあってもまあいいかな」程度のインパクトしか与えないかもしれません。ところが今説明したのは、ほんの取っ掛かり、さわりに過ぎないことを申し添えておきましょう。実はこの基本スペックを完成させた後、よりゴージャスなThe WALLへ進化する予定です。今までそのスペックを安斎さんから聞いた者は皆絶句しました。特に開発者である私はこれから迎える茨の道を想像して思考停止状態に陥りかけました。それは大げさですが、詳細については、な・い・しょ としときます。

ヒントを与えるなら、フィルター、URL、ストリーム、Java3Dといったキーワードに象徴されます。

●WallPainterとSimplePainter

ようやくこの記事のの最大の目的であるJava2Dの具体的な説明に入れます。最初に話したように簡単なペインターをどのように作成したかという話を中心に、Java2Dの機能のほんの一部に触れてもらいます。一部といっても積極的にJava2Dを使わなければならない理由の説明としては十分な情報を提供できると思います。

SimplePainterはWallPainterの雛型です。WallPainterはより複雑なプログラムになっていて説明を始めたら、この記事の数倍のスペースを必要とすると思います。実際、現在の実装ではブラシなどのツール群はそれぞれオブジェクトになっており、オーバーローディングの技術を利用することで、ツールの追加を容易にしています。The WALLシステムは多分ソースフリーのシステムになるはずですから、興味を持った意欲的なプログラマーが自由にブラシ等を追加できるようなっています。

けれども、SimplePainterの方は、オーバーローディングを使ってオブジェクト指向というところまではい行っていません。ツールのタイプを保持するToolTypeにTOOL_LINEやTOOL_BRUSHを代入して、分岐処理しているオールドファッションプログラムです。

<PainterCanvas.javaのマウスダウンのイベントハンドラ>
  // マウスダウンのオールドファッションなプログラム
  void this_mousePressed(MouseEvent e) {
    if (toolType == TOOL_LINE) {          // TOOL_LINEのとき
      if (barkImage == null)
        return;
      x1 = e.getX() - barkImage.getX();
      y1 = e.getY() - barkImage.getY();
    }
    else if (toolType == TOOL_BRUSH ||    // TOOL_BRUSH,TOOL_ERASERのとき
             toolType == TOOL_ERASER) {
      if (barkImage == null)
        return;
      x1 = e.getX() - barkImage.getX();
      y1 = e.getY() - barkImage.getY();
      Graphics2D g2 = (Graphics2D)barkImage.getImage().getGraphics();
      if (toolType == TOOL_BRUSH)
        g2.setColor(penColor);
      else
        g2.setColor(eraserColor);
      int r = penThickness / 2;
      g2.fillOval(x1 - r, y1 - r, penThickness, penThickness);
      g2.dispose();
      repaint(barkImage.getX(), barkImage.getY(),
            barkImage.getWidth(), barkImage.getHeight());
    }

ここではJava 2Dを説明するのが主目的ですので我慢していただきます。図4にSimplePainterのGUIを示します。

wallpainter

図4 wallpainter

SimplePainterのファイル構成を説明します。

ファイルメ名内容
WallPainter.java ― main()のあるアプリケーションのソース
MainWindow.java ― メインウインドウのソース
ToolPanel.java ― 左側のツールパネルのソース
PainterCanvas.java― 描画キャンバスのソース

以上が主なファイルです。

●JDK1.2におけるJava2Dの位置付け

さて、Java 2DというのはJDKの中でどういった位置付けがなされているのでしょうか。JavaSoftのサイトを見ると次のように分類されていることが分かります。

Java Media APIs 
	Java 2D API (core API) 
	Java 3D API 
	Java Advanced Imaging API 
	Java Media Framework API 
	Java Shared Data Toolkit 
	Java Sound API 
	Java Speech API 
	Java Telephony API

編集者の方がいっていたMedia APIというのはこんなにあります。そして、これらのAPIが出揃ったときにJavaは史上最強の開発環境になることは間違いありません。ただ一点の不安点を解決できるならです。もちろんそれは、C/C++に比べれば処理速度が遅いということです。また、実行速度だけでなく現在Javaの世界で問題になっているのは、プログラマに天国の世界を約束してくれるはずだったメモリ管理機構(不要なメモリをフリーする)すなわちガベージコレクションの問題です。ガベージコレクションが起動してしまうとどうしても処理速度が落ちてしまいます。しかし私は処理速度の問題はこの1、2年のうちに解決できると信じています。

いずれにしても、これほどのAPIはWindowsをも凌駕していますし、何よりも構成が非常に美しい。MSのように1社で他の企業の声に耳を貸さず作られたライブラリ、コンパチビリティが足かせになって辻褄合わせに終始しているライブラリとは本質的にレベルが異なります。たとえば、Java 2Dの開発にはグラフィックスソフトの勇、アドビシステムが協力しているということです。Media APIの詳細やサンプルを入手したい人は当然http://www.javasoft.comをブラウズしてみてください。Javaをやろうと思ったらこのサイトとお友達にならざるを得ません。

●Java 2Dの特徴とAWTとの相違点

さて、概説的なことは書きたくないといいましたが、Java 2Dの内容ぐらいは説明しなければならないでしょう。前出のMedia APIのリストにあったようにJava 2Dはcore APIということになっています。したがって今後開発されるJavaのグラフィクス系のソフトはJDK 1.1までのAWTではなくて、Java 2Dで開発されることになります。Java 2D APIは次のパッケージから構成されています。

Java 2DはこれまでのAWTの拡張として実装されています。(NEW)マークのついたものは新規に追加されたもので、他の2つのパッケージも大幅にクラスが追加されています。

それではその特徴をまとめて見ましょう。JavaSoftのドキュメント「Java 2D API JDK1.2Beta4 DRAFT」から受け売りをすると次のようにまとめることができるでしょう。

◇グラフィックコンテキストクラスがGraphics -> Graphics2Dに拡張された。

◇座標系がUser SpaceとDevice Spaceの2つになった。

◇アフェイン変換クラスAffineTransformクラスが提供された。

◇フォントやテキストレイアウトが木目細かく設定できるようになった。

◇Imageクラスから派生したBufferedImageクラスが導入され、画像に対して様々なオペレーションが可能となった。BufferedImageはアルファチャンネルを持ち、ラスターオペレーション、高度なフィルタリング、カラーモデルの変更などに対応している。

◇Geometry Shapeクラスが提供された。たとえば、Rectangle2DやEllisp2D、CurversなどがShapeクラスから派生させられた。また、これらのオブジェクトのコンポジット、和や差をとることができるようになった。

◇FillやPenスタイルが豊富になった。幅のあるラインや丸みを帯びたエッジ、アンチエイリアシングの利いたラインを描くことができ、パターンでの塗りつぶしができるようになった。

◇The Java Printing APIで本格的な印刷に対応した。

◇JPEGファイルへの出力をサポートした。(Java 2D本体ではない)

●Swingと一緒に使う

ところでSimplePainterはどのような開発環境で開発されたのでしょうか。JDKで開発するというのもひとつの方法です。UNIXプログラマは迷わずそうするでしょう。UNIXプログラマというのはGUIなんてどうでもよいと思っている人も多いので、そのような人にはこれ以上何も言うことはありません。しかし、今の若いプログラマはビジュアル世代(Visual BasicやVisual C++やDelphiを使ってきた人)です。GUIのリソースをテキストでコーディングするというのは絶対いやだと思います。GUIのビルダーなら、いろいろ見た目を試行錯誤してみたい気になりますが、ハードコーディングだと動けばいいやと言う気持ちになりがちです。また、ビジュアル世代のプログラマにとってはソースデバッグは必須でしょう。

また、軽量コンポーネントのSwingも手放せません。Swingによって初めて何とか商用レベルのアプリケーションを作れるようになったと感じました。今更AWTだけで作るというのはとても禁欲的です。

Swingの方は1.03などはJDK1.1.6と一緒に使えるので、JBuilder 2.0やVisualCafe 2.5、VisualAge 2.0 for Javaなどで開発できます。ところが困ったことにJava 2DはJDK1.2でないとサポートされないのです。ところがJDK1.2に完全対応したビジュアルな開発環境は見当たりません。本家本元のSunのWorkShopぐらいは対応しているかなと調べましたが、これもできません。ただし、インプライズのJBuilder2.0だけは、JDKを切り替える機能があり、JDK1.2のベータもにも対応しているとマニュアルに書いてありました。

●JBuilder 2.0でJDK1.2beta4を使う

ところが、これが曲者でした。JBuilder 2.0でJDK1.2beta4を使うときの設定の仕方を説明します。

(1) Windows版JDK1.2beta4をJavaSoftよりダウンロードしてきて、PCにインストールする。

(2) JBuilder 2.0の「ファイル/プロジェクトプロパティ」でダイアログを表示し、「パス」のタブを選択します。この「ターゲットJDKバージョン」を変更します。

(3) 「定義」ボタンを押して「使用可能なJDKバージョン」ダイアログを表示し、「新規」ボタンを押して「JAVA.EXEパス」に\JDK1.2beta4\bin\java.exeを選択します。すると左側のリストにJDK1.2ベータが表示されるはずですが、
"Warning:JIT compiler "JAVACOMP" not found. Will use interpreter."
などというメッセージが代わりに表示されます。とりあえずこれでOKです。

(4) 「クラスパス」には\jdk1.2beta4\jre\lib\rt.jarを設定してください。

これで、とりあえずJDK1.2beta4でコンパイル/実行することはできます。ところがこれは米国のJBuilderのメイリングリストでも反感を買っていたところですが、「実行/実行」は動作しても、「実行/デバッグ」は実行できません。このバージョンのJDKではデバッグはサポートされていません、というエラーが表示されます。ですから、JBuilder 2.0の「JDK1.2のベータのサポート」は不完全なものです。

ソースデバッグはできないけれども、GUIの編集はビジュアルにできるので、これでよしとすることにしました。デバッグは実行ログにSystem.out.println()でメッセージすることで我慢することにしました。

●Graphics2Dの取得

先に述べましたようにJava 2Dではjava.awt.Graphicsが拡張されjava.awt.Graphics2Dに変更されました。


java.lang.Object
  |  +--java.awt.Graphics
          |        +--java.awt.Graphics2D

public abstract class Graphics2D extends Graphics {

  //コンストラクタ
  Graphics2D();
  
  //メソッド  
  abstract  void  clip(Shape s);
  abstract  void  draw(Shape s);
  abstract  void  drawGlyphVector(GlyphVector g, float x, float y); 
  abstract  void  drawImage(BufferedImage img, BufferedImageOp op, int x, int y); 
  abstract  boolean  drawImage(Image img, AffineTransform xform, ImageObserver obs); 
  abstract  void  drawRenderableImage(RenderableImage img, AffineTransform xform); 
  abstract  void  drawRenderedImage(RenderedImage img, AffineTransform xform); 
  abstract  void  drawString(AttributedCharacterIterator iterator, float x, float y);
    abstract  void  drawString(AttributedCharacterIterator iterator, int x, int y); 
  abstract  void  drawString(String s, float x, float y); 
  abstract  void  drawString(String str, int x, int y); 
  abstract  void  fill(Shape s); 
  abstract  Color  getBackground(); 
  abstract  Composite  getComposite(); 
  abstract  GraphicsConfiguration  getDeviceConfiguration(); 
  abstract  FontRenderContext  getFontRenderContext(); 
  abstract  Paint  getPaint(); 
  abstract  Object  getRenderingHint(String hintKey); 
  abstract  RenderingHints  getRenderingHints(); 
  abstract  Stroke  getStroke(); 
  abstract  AffineTransform  getTransform(); 
  abstract  boolean  hit(Rectangle rect, Shape s, boolean onStroke); 
  abstract  void  rotate(double theta, double x, double y); 
  abstract  void  rotate(double theta); 
  abstract  void  scale(double sx, double sy); 
  abstract  void  setBackground(Color color); 
  abstract  void  setComposite(Composite comp); 
  abstract  void  setPaint(Paint paint); 
  abstract  void  setRenderingHint(String hintKey, Object hintValue); 
  abstract  void  setRenderingHints(RenderingHints hints); 
  abstract  void  setStroke(Stroke s); 
  abstract  void  setTransform(AffineTransform Tx);
  abstract  void  shear(double shx, double shy); 
  abstract  void  transform(AffineTransform Tx); 
  abstract  void  translate(double tx, double ty); 
  abstract  void  translate(int x, int y); 
}  

Graphics2Dの取得の方法は次の2タイプあります。SimplePainterのソースを使って説明します。なお、この記事が出るころには、プログラムのソースは連画サイトにアップされていることでしょう。ソースの入手はそこからダウンロードしてください。所在がわからないときは私にメイルください。

@Graphicsをキャストする方法

<PainterCanvas.javaのpaintメソッド>
  public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2 = (Graphics2D)g;   ←-----------------------------------Graphics2Dの取得
    if (wallImage != null)
      g2.drawImage(wallImage.getImage(), 0, 0, this);
    if (barkImage != null) {
      offset = barkImage.getOffset();
      g2.drawImage(barkImage.getImage(), offset.x, offset.y, this);
    }
    if (ilockRect != null) {
      g2.setColor(Color.yellow);
      g2.draw(ilockRect);
    }
  }

ABufferedImageに直接描くとき

<Bark.javaのwriteJPEGメソッド>
    try {
      // Create the temporaly image doesn't have alpha data and copy
      // the original image to this
      BufferedImage tmpImg = new BufferedImage(
                                (int)size.getWidth(),
                                (int)size.getHeight(),
                                BufferedImage.TYPE_INT_RGB);
      Graphics2D g2 = tmpImg.createGraphics();  ←-------------------------Graphics2Dの取得

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

●Graphics2Dのメンバメソッドの使い方

それでは、どうやって描画するのでしょうか。上記のGraphics2Dのメソッドをみて目を引くのは


  abstract  void  draw(Shape s);

  abstract  void  rotate(double theta, double x, double y); 
  
  abstract  void  transform(AffineTransform Tx); 
  

などのメソッドでしょう。下の2つは座標変換用のメソッドだというのは分かります。回転とアフェイン変換です。最初のdraw(Shape s)というのがJava 2Dに特徴的な重要なメソッドです。

<:PainterCanvas.javaのマウスアップのイベントハンドラ>
  void this_mouseReleased(MouseEvent e) {
    if (toolType == TOOL_LINE) {
      if (barkImage == null)
        return;
      x2 = e.getX() - barkImage.getX();
      y2 = e.getY() - barkImage.getY();
      Graphics2D g2 = (Graphics2D)barkImage.getImage().getGraphics();
      g2.setStroke(new BasicStroke(penThickness));     ←------------------- ・
      g2.setColor(penColor);
      g2.drawLine(x1, y1, x2, y2);    ←------------------------------------ ・             
      g2.dispose();
      repaint(barkImage.getX(), barkImage.getY(),    ←--------------------- ・
            barkImage.getWidth(), barkImage.getHeight());
    }
    else if (toolType == SET_INTERLOCK) {
      x2 = e.getX();
      y2 = e.getY();
      ilockRect = new Rectangle2D.Float((float)x1, (float)y1,
                (float)(x2 - x1), (float)(y2 - y1));     ←----------------- ・

      Graphics2D g2 = (Graphics2D)this.getGraphics();
      g2.setColor(Color.yellow);
      g2.draw(ilockRect);      ←------------------------------------------- ・
      g2.dispose();
      repaint();
      if (ilockRect != null)
        mainWindow.SetInterlock(ilockRect);
    }
  }

このルーチンはマウスアップのときのイベントハンドラです。この中にはいくつもの教訓的なプログラムをしていますので説明しましょう。

@g2.setStroke(new BasicStroke(penThickness));

これは、penThicknessの幅を持ったストロークを生成して、ストロークを設定しています。ありていに言えば太い線を設定しているのです。こんな単純なことがAWTではできなかったのです。

ストローク

図5 ストローク

Ag2.drawLine(x1, y1, x2, y2);

これはオールドファッションなラインの描画です。もちろんGraphics2DはGraphicsのサブクラスなので昔のメソッドも使えます。

・repaint(barkImage.getX(), barkImage.getY(), 
        barkImage.getWidth(), barkImage.getHeight());

これはキャンバス上のある範囲だけをリペイントしています。覚えておかなければ行けないのは、

repaint() ―> update() ―> clear(),paint()

という順で呼び出されるということです。

・ilockRect = new Rectangle2D.Float((float)x1, (float)y1,
                 (float)(x2 - x1), (float)(y2 - y1));

これはIt's newです。java.awt.geomの下に定義されているシェイプ、プリミテイブ、幾何学図形であるRectangle2Dを生成しています。

java.lang.Object
  |
  +--java.awt.geom.RectangularShape
        |
        +--java.awt.geom.Rectangle2D

この他にもArc2D、CubicCurve2D、Line2Dなど10以上の図形が定義されています。

Dg2.draw(ilockRect);

そして、その生成した図形をdrawするというのが新しいGraphics2Dのやり方です。オブジェクト指向です。

●BufferedImageの作成とdrawImage()

次に昔のImageクラスに豊富な機能を追加したBufferedImageの生成と、そのイメージへの描画を見てみましょう。

<MainWindow.javaのSetInterlock()の一部>
 BufferedImage bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
  Graphics2D g2 = bi.createGraphics();
  g2.setColor(new Color(0x00000000, true)); // Alpha = 0x00;
  g2.fill(iRect);

一行目でBufferedImageを生成していますが、TYPE_INT_ARGBというタイプで生成しています。これは1ピクセルが1INT(32ビット)の画像を生成しています。画像サイズは(w,h)です。ビットの内訳はARGBと書いてあるように
|アルファ8ビット|赤8ビット|緑8ビット|青8ビット|
です。これによってアルファチャンネルを操作することができるようになります。
下の2行はBufferedImageのアルファチャンネルをクリアしいます。どういうことかというとこの上に描かれた絵は、描いたところは色が付いて見えますが、他の部分は透明で背景画像が見える状態を作ります。

アルファ

図6 アルファ

●アルファプレーンの使い方

アルファの設定で半透明なエアブラシなどを作れるわけですが、その設定の仕方は至極簡単です。

g2.setColor(new Color(0x00000000, true)); // Alpha = 0x00;

上のこの行では色の設定をINT型で設定しています。普通の赤なら0xFFFF0000、白なら0xFFFFFFFFです。50%透明な赤なら0x7FFF0000でしょう。
その他次のColorコンストラクタを使う方法もあります。

penColor = new Color(penColor.getRed(), penColor.getGreen(),
					ZpenColor.getBlue(), penAlpha);

●ダブルバッファリング

ダブルバッファリングはちらつきのない描画を実現する方法で、ペイントソフトやアニメーションでは必ず実装しなければなりません。理屈からいうと、画面に直接描画してしまうと描画している様が見えるので、メモリ上にあるoffScreenImageにあらかじめ描いておき、最後に高速描画ルーチンdrawImage(offScreenImage, 0, 0, this)でキャンバスに描きなおします。

<PainterCanvas.javaのpaint(),update()>
  offScreenImage = (BufferedImage)createImage(   ★★★
                  wallImage.getWidth(), wallImage.getHeight());
  offScreenGraphics = (Graphics2D)offScreenImage.createGraphics();

  public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2 = (Graphics2D)g;
    if (wallImage != null)
      g2.drawImage(wallImage.getImage(), 0, 0, this);
    if (barkImage != null) {
      offset = barkImage.getOffset();
      g2.drawImage(barkImage.getImage(), offset.x, offset.y, this);
    }
    if (ilockRect != null) {
      g2.setColor(Color.yellow);
      g2.draw(ilockRect);
    }
  }

  public void update(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    paint(offScreenGraphics);     ★★★
    g2.drawImage(offScreenImage, 0, 0, this);
  }
キャンバスではrepaint() ―> update() ―> clear(),paint()という順序で呼ばれます。update()の中の★の行を見るとpaint()メソッドを使ってoffScrrenGraphicsに描画しています。そしてそのイメージをキャンバスに描きなおしています。

●ラスターの使用、JPEGファイルへの出力例

次のメソッドは非常に興味深いメソッドです。Javaでは今までJPEGやGIFファイルの読み込み表示はURLクラスを使って簡単に可能でした。しかし、書き出しとなると自分でエンコーダを書く必要がありました。ところがJava 2D全く簡単にJPEGファイルへの出力が可能です。

<Bark.javaのwriteJPEG>
import com.sun.image.codec.jpeg.*;   ←---------------------------------- ・

  .....
  // Wrte a BufferedImage as a JPEG file
  private void writeJPEG(BufferedImage img, String fname)
    throws Exception {

    try {
      // Create the temporaly image doesn't have alpha data and copy
      // the original image to this
      BufferedImage tmpImg = new BufferedImage(
                                (int)size.getWidth(),
                                (int)size.getHeight(),
                                BufferedImage.TYPE_INT_RGB);
      Graphics2D g2 = tmpImg.createGraphics();
      g2.drawImage(img, 0, 0, null);

      // Write the RGB image to the JPEG file   ←--------------------------・
      File file = new File(fname + ".jpg");
      BufferedOutputStream out =
          new BufferedOutputStream(new FileOutputStream(file));
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(tmpImg);
      param.setQuality(1.0f, false);
      encoder.setJPEGEncodeParam(param);
      encoder.encode(tmpImg);
      out.close();

      // Write the Alpha image to the JPEG file
      WritableRaster raster = img.getAlphaRaster();  ←---------------------・

      file = new File(fname + "_a.jpg");
      out = new BufferedOutputStream(new FileOutputStream(file));
      encoder = JPEGCodec.createJPEGEncoder(out);
      param = encoder.getDefaultJPEGEncodeParam(raster,
                          JPEGEncodeParam.COLOR_ID_GRAY);
      param.setQuality(1.0f, false);
      encoder.setJPEGEncodeParam(param);
      encoder.encode(raster);
      out.close();

    } catch (Exception e) {
      throw e;
    }
  }

JPEGのエンコード、デコードのAPIはJava 2Dのパッケージには含まれません@をインポートする必要があります。
AのブロックでアルファありのBufferedImageのRGBだけをJPEGファイルにセーブしています。気をつけてほしいのはJFIF形式のJPEGファイルはアルファをサポートしていないので、BufferedImageの4プレーンを一気にJPEGファイルにエンコードすることはできません。

BではBufferedImageのアルファプレーンだけをRasterという形式で取り出しています。そしてそれを256グレイスケールのJPEGファイルに保存しています。こんな複雑な処理をたったこれだけのステップでこなしているのは驚異的です。出力された画像を図7にしめします。

sample_org

図7 リスト9の出力画像
sample1 sample2

●Java2Dの可能性

私が紹介したのはJava 2Dの機能のほんの一部に過ぎません。アンチエイリアシングやストローク、座標変換、フォント設定機能の充実などはDTPやCADのシステムを意識した作りになっていることが見て取れます。画像のフィルタリング機能の実装の容易さは、フォトレタッチソフトの開発をよく考慮されていると思います。JavaSoftはJavaでグラフィックスの本格的アプリケーションを開発者が開発することを本気で考えているようです。

そして、私の興味はJava 3Dに続いていきます。

●月刊化おめでとう

とうとう本誌も月刊化することになるそうです。編集者の皆さんおめでとうございました。ひとつ意見させてもらうなら、雑誌の装丁とかページのデザインとかでIDGの「Java WORLD」に完全に負けていると思います。ただあの雑誌は月刊に名を借りた単行本といえるくらい高価なのでいただけません。紙質は米国の雑誌のようにペラペラでもいいので、何かもっとクールな紙面にできないでしょうか。

The WALLについてですが、もし編集者の方がが許してくれるなら、今後の展開についても逐次報告していこうと思っています。乞うご期待。

さて、最後にちょっとスケールの大きい話をさせてもらいます。ルネッサンスの時代までは、科学と芸術は不可分のものでした。ところが、それを理性と感情の対立に置き換えることで、全く相容れない2つの別世界として捕らえるようになったのは、この近代の数百年のことです。たとえばレオナルド・ダ・ビンチは言うまでもなく科学者であり、アーティストでもあったわけです。おそらく彼の中では、科学することと創作活動は自然を捉えるという意味で、全く連続した一連の行為だったに違いありません。科学とアートが分離したことによって科学の暴走から様々な地球上の問題、産業廃棄物や大気汚染、温暖化、核の拡散、原子力などといった問題を生み出すことになってしまったのではないでしょうか。

それゆえ、21世紀を迎えようとする私たちは、再び、技術的な目とアーティスティックな目、更に付け加えると哲学的な目を持って物事を捉えなければならないと思うのです。離婚した二人(技術とアート)のよりを戻すべく、「The WALLチーム」は日夜精進しています。くれぐれも「ポストペット」と同列にしないでください。

現代ではアートは様々な様相を呈しています。レンブラントやゴッホの絵画や青磁の壷や掛軸などのお金と引き換えられるものだけがアートだと思っている人は、まさかいないと思います。けれども、クラシックな書画、骨董にしか価値を見出せない人は多いと思います。たしかに、時代にろ過され評価の定まった芸術作品には作者の技量と時代を超えた「真実」が存在します。それは、そこに描かれ造形されているものが人間に普遍な死や性、あるいは愛といった主題を持っているからかもしれません。

しかし、普遍的なものだけが「真実」とは限らないのです。私たちの生活はこの200年で大きく変わりました。いやがおうでも科学技術の恩恵を受けて生活しています。私たちの生活とテクノロジー、とりわけこの10年間に猛烈な勢いで発達してきたネットワークやマルチメディアなどのテクノロジーが人間の生き方そのものに与える影響は無視できないものがあります。

21世紀の私たちの大半は、更にいっそうテクノロジーの中で人生のかなりの時間を生活するようになることは間違いありません。そのとき、テクノロジーと私たちの関係を技術的な側面からだけ捕らえるのではなく、比喩的にあるいは皮肉たっぷりと、また自戒を込めて2者の関係を考察する人々の存在が必要だと思います。そのような人が新しいアーティスト呼べるのだと思います。そして彼らはまた、現代に警鐘を鳴らし、未来を予見し、新しい価値を、その作品によって示していかなければならないのです。

そういう意味で技術とアートの両方に造詣のある新しいアーティストの出現が待たれているのです。前出の安斎さんは「今一番アーティストとして有望なのはプログラマではないか」といっていました。確かにそうかもしれません。絵筆に代わって、今を、このテクノロジーのダイナミズムを一番よく表現できる道具はプログラムかもしれません。ただプログラムが組めるだけでは駄目なことはいうまでもありません。アーティストとしての素養を持っているプログラマこそが必要とされているのです。そして、その素養とは一体どんなものなのでしょうか。「自分はゲームプログラマで、グラフィックスに自身があるし、絵心もある。もしかしたら俺のような人間かな」と一人で悦にいる君、多分違うと思うよ。人と同じようなことをやっている君はまだまだ修行が足りませんね。

ではどういう人間か?それは私も分かりません。


前にもどる
[おわり]