SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

サーバサイドではじめるJavaグラフィック講座

JSP/サーブレットでイメージを生成する

サーバサイドではじめるJavaグラフィック講座 1


GraphicsとGraphics2D

 BufferedImageを作成したら、ここからGraphicsインスタンスを取得します。これは次のようにして行います。

Graphics g = im.createGraphics();

 BufferedImageからのGraphicsの取得には2通りの方法があります。1つは「getGraphics」メソッドを呼び出す方法。もう1つが「createGraphics」を呼び出す方法です。

 両者の違いは、作成するGraphicsインスタンスです。getGraphicsでは、一般的なGraphicsインスタンスが作成され返されますが、createGraphicsの場合、返されるのはGraphicsではなく、「Graphics2D」クラスのインスタンスです。要するに、より分かりやすく書くなら、この文は実はこういうことをやっていたわけです。

Graphics2D g2 = im.createGraphics();
Graphics g = (Graphics)g2;

 Graphics2dは、Graphicsをさらに拡張したもので、より複雑なグラフィック処理を行うための機能が用意されています。もちろん、Graphicsの機能もすべて持っていますから、特に理由がなければこちらを使った方が良いでしょう。ただし、今回はGraphicsとしての機能しか使っていませんから、Graphicsとして取得し利用しています。

 また、ここでcreateGraphicsを使った理由はもう1つあります。それは「AWTやSwingの描画関係メソッドでも、渡されるのは実はGraphicsではなくGraphics2Dである」という点です。先ほど、アプレットのpaintメソッドを使ったサンプルをあげましたが、ここでpaintメソッドにGraphicsが渡されていました。これは、本当はGraphicsではなく「Graphics2D」なのです。すなわち、Graphics2DGraphicsにキャストして渡していたのです。ですから、まぁここでもそれに習って書いておいた、というわけです。

 なぜそんなことをするのかと言うと、これは「それ以前のバージョンとの互換性」の問題からでしょう。AWTのコンポーネントにあるpaintメソッドは、Graphicsを引数として渡す形になっていました。Graphicsを拡張したGraphics2Dが登場したからといって、paintメソッドの引数をいきなりGraphics2Dに変更したりしたら、プログラムの大幅な修正を余儀なくされます。そこで、それまでと同じようにGraphicsとしてインスタンスを渡し、「必要ならば、各自でGraphics2Dに戻して利用してください」という形にしてあるわけです。

Graphicsによる描画の基本

 Graphicsが取得できたら、後はこのGraphicsに用意されているメソッドを呼び出して描画を行います。Graphicsの基本的な使い方については既にご存知の人も多いでしょうが、簡単にまとめておきましょう。

for(int i = 0;i < 16;i++)
    for(int j = 0;j < 16;j++){
        Color c = new Color(i * 16,0,255 - j * 16);
        g.setColor(c);
        g.fillRect(i * 16, j * 16, 16, 16);
    }

 ここではsetColorメソッドで描画色を設定し、fillRectで四角形を描画しています。Graphicsを使った描画は、基本的に「描画のための設定を行なっておき、それから描画を行う」という形になります。

描画の色を設定する
setColor(Color c)
描画を行うメソッド
drawLine(int x1, int y1, int x2, int y2)
drawOval(int x, int y, int width, int height)
drawRect(int x, int y, int width, int height)
drawRoundRect(int x, int y, int width, int height,
              int arcWidth, int arcHeight)
draw3DRect(int x, int y, int width, int height, boolean raised)
drawArc(int x, int y, int width, int height,
        int startAngle, int arcAngle)
drawPolygon(int[] xPoints, int[] yPoints, int nPoints)
drawPolyline(int[] xPoints, int[] yPoints, int nPoints)

fillOval(int x, int y, int width, int height)
fillRect(int x, int y, int width, int height)
fillRoundRect(int x, int y, int width, int height,
              int arcWidth, int arcHeight)
fill3DRect(int x, int y, int width, int height, boolean raised)
fillArc(int x, int y, int width, int height,
        int startAngle, int arcAngle)
fillPolygon(int[] xPoints, int[] yPoints, int nPoints)

……他、多数

 まず、setColorで描画に使う色を設定します。Graphicsでは描画色に関する情報を保持するようになっており、描画のメソッドが呼ばれた際には、あらかじめ設定されていた描画色を使って図形を描きます。ですから、最初にsetColorで色を指定しておく必要があります。

 色の値は、java.awt.Colorクラスのインスタンスを使います。主な色については、Colorのクラスフィールドとして用意されているので、それをそのまま利用できますし、それ以外の色についてはnew Colorでインスタンスを作成して利用します。主なクラスフィールドとコンストラクタは次のようになります。

Colorクラスフィールド
BLACK,BLUE,CYAN,DARK_GRAY,GRAY,GREEN,LIGHT_GRAY,MAGENTA,
ORANGE,PINK,RED,WHITE,YELLOW
(以上、同名で小文字表記のフィールドもある)
Colorコンストラクタ
new Color(int r, int g, int b)
new Color(int r, int g, int b, int a)
new Color(float r, float g, float b)
new Color(float r, float g, float b, float a)

 描画用のメソッドは、大きく分けて2通りあります。「draw~」で始まるのが図形の形状(輪郭)を描画するもので、「fill~」で始まるのが図形の形状を塗りつぶして描画するものです。Graphicsにはさまざまな形の図形を描画するためのメソッドが多数揃えられているので、一通り調べてみるとよいでしょう。

 最後に、描画が終わったらGraphicsインスタンスを破棄します。

g.dispose();

 これは、AWTのpaintメソッドなどでグラフィックを利用する場合には不要の処理です。AWTなどでは、Graphicsインスタンスの処理はシステム側で行なってくれるので作り手は意識する必要がないのです。が、createGraphicsなどで自分でGraphicsインスタンスを生成した場合には、使用後に破棄するように心がけておくのが良いでしょう。

 通常、Javaでは使用済みのオブジェクトの破棄を明示的に行う必要はありません。にも関わらず、なぜGraphics(またはGraphics2D)に関しては使用後に破棄を行うのか。それは、Graphicsが「ネイティブ環境のリソースを消費するから」です。

 Graphicsは、実際の描画に関する部分をネイティブ環境に委譲しており、インスタンスを作成すると同時にネイティブ側のリソースを消費するようになっています。このため、オブジェクトを作ったままにしておくと、システムのリソースを無駄に消費したままの状態となってしまうのです。

 無論、いずれはガベージコレクタにより処理はされるはずなのですが、やはり使い終わったらすぐさまリソースを解放するように書くのが一種のマナーだ、と思えばよいでしょう。

 ……以上、Graphicsを使った描画の基本について整理しました。が、注意して欲しいのは、「描画に関する説明の大半は、Graphicsを使った方法である」という点です。実は、Graphics2Dでは、もう少し違った方法で描画を行うことができます。それらについてはいずれ改めて触れるとして、「(Graphics2Dではなく、その前の)Graphicsクラスでは、これが基本的な流れなのだ」と考えてください。

イメージの送信

 描画については一通り理解できました。続いて、イメージの送信について説明しておきましょう。これは、doActionの後半部分の処理となります。

 イメージをクライアント側に送信する基本的な流れを整理すると、次のようになります。

  1. responseのsetContentTypeをイメージデータのものに設定する。
  2. これは、次のように行います。
    response.setContentType("image/jpeg");
    
    ここでは、JPEGイメージとして画像を送信しますので、"image/jpeg"にコンテントタイプを変更します。
  1. ServletOutputStreamを用意する。
  2. これは、response.getOutputStreamを呼び出して取得すればよいでしょう。
  1. OutputStreamを元にImageOutputStreamを作成する。
  2. これは次のように行います。
    ImageOutputStream ios = ImageIO.createImageOutputStream(sos);
    
    ImageOutputStreamは、文字通りImageインスタンスを出力するためのアウトプットストリームです。これは、ImageIO.createImageOutputStreamメソッドとして用意されています。引数にOutputStreamを指定すれば、そのストリームにイメージを送信するImageOutputStreamが生成されます。
  1. 出力するフォーマットのためのImageWriterインスタンスを取得する。
  2. ImageWriterは、イメージのデータを特定のフォーマットに符号化し送信するためのものです。これは、ImageIO.getImageWritersByFormatNameメソッドを使って取得します。ここで引数に"jpeg"と指定すれば、JPEGフォーマットへのImageWriterが得られるわけです。
    ただし、これで得られるのは、ImageWriterインスタンスをひとまとめにしたIteratorインスタンスである、という点に注意する必要があります。ImageWriterが1つだけポンと返されるわけではないのです。そこで、nextでそこから1つを取り出して利用することになります。
    ImageWriter iw = (ImageWriter)ImageIO.
                ugetImageWritersByFormatName("jpeg").next();
    
    このようにgetImageWritersByFormatNameの後に「next」がついていたのは、そういうわけです。これは、慣れないとよく引っかかるところですので注意しましょう。
  1. ImageWriterに出力先のImageOutputStreamを設定する。
  2. これは、ImageWritersetOutputメソッドで行います。これで引数にImageOutputStreamを指定してやれば、それに出力先が設定されます。
  1. ImageWriterにイメージを出力する。
  2. ImageWriterwriteメソッドを実行すると、引数に指定されたBufferedImage(正確にはRenderedImageインスタンス)を符号化し出力します。
  1. ImageOutputStreamを閉じる。
  2. 送信が終わったら、最後にcloseImageOutputStreamを閉じて作業終了です。

 描画と違い、イメージの出力は、一度やり方が分かれば、後はどんなプログラムであれやることは同じです。「このとおりに書けば動く」と考えてしまえば、それほど面倒なものではありませんね。

まとめ

 グラフィックの利用の基本は「java.awt.Graphicsの描画用メソッドの使い方を覚える」ということだけです。が、サーバサイドからそれを利用するためには、これに加えて「BufferedImageインスタンスの作成」と「イメージの送信方法」について理解しておく必要があります。この3つを順序良く呼び出すのがサーバサイドにおけるグラフィック利用の基本だ、といってよいでしょう。

 これでようやく、イメージを作成し、それをクライアント側に送信する――という、サーバサイドにおけるグラフィック利用の基本がほぼ分かりました。基本的な流れが分かったところで、次回はグラフィックイメージの使い方についてもう少し掘り下げていくことにしましょう。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
サーバサイドではじめるJavaグラフィック講座連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/978 2007/12/25 10:15

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー