Ruby/GD 入門

$Id: gd_intro-ja.html,v 1.6 2000-10-29 02:51:18+09 tam Exp tam $

Ruby/GD の主な使用方法について紹介します.Ruby/GD で提供されているメソッ ドの完全なリストについては同梱されているmanual.htmlを参照して下さい. また,このドキュメントの最新版は常にここ に置いてあります.


ライブラリの構造


       GD -+
           |
           +- GD::Image
           |
           +- GD::Polygon
           |
           +- GD::Font

GD

GD::Image,GD::Polygonオブジェクトから参照される定数をまとめたモジュー ル.

GD::Image

イメージの生成,図形やポリゴン,文字列などの描画や操作,出力を行うクラス.

GD::Polygon

ポリゴン(多角形)の頂点の生成,管理,操作を行うクラス.GD::Image#polygonによってポリゴンが実際 に描画される.

GD::Font

gdで提供されているフォントを指定したり,指定したフォントの情報を得るク ラス.GD::Fontオブジェクトは文字(列)描画メソッドの引数に用 いられる.

以下では次のような記法を採用します.


はじめに

Ruby/GD ライブラリを用いる場合,スクリプトの始めに
require 'GD'
の1行を書いて下さい.

目次

1. イメージを生成する
1.1 新規にイメージを生成する
1.2 画像ファイルから新規にイメージを生成する
2. イメージの操作
2.1 線分を描く
2.2 カスタマイズした線分を描く
2.3 図形を描く
2.4 多角形を描く
2.5 イメージのコピー
3. 文字(列)を描く
3.1 GD::Fontクラスで提供されるフォント を使う
3.2 TrueTypeフォントを使う
4. イメージの出力
5. 補足
5.1 オブジェクトの削除
5.2 インタレース
5.3 色の割り当てについてもっと

1. イメージを生成する

GD::Imageインタンス生成のための一連のクラスメソッドが用意 されています.

1.1 新規にイメージを生成する

イメージを新規に生成するために,次のクラスメソッドが提供されています.
GD::Image.new(w, h)

    - 幅w, 高さhのGD::Imageオブジェクトを生成する(w,hの単位はピクセル).
      戻り値は生成されたGD::Imageオブジェクト.

新規にイメージを生成した場合,描画などの操作を行う前に必ず背 景色を設定するようにしましょう.そのためにGD::Image#colorAllocateメソッ ドを使用します.

GD::Image#colorAllocate(r, g, b)
GD::Image#colorAllocate(str)

    - オブジェクトに対して r, g, bで指定したRGB要素,または文字列
      "#xxxxxx"(xは16進数)で色を設定する.
      r, g, bは0から255までの整数.戻り値は指定された色のテーブルのイ
      ンデックス.

      オブジェクトに対して最初にこのメソッドを適用した場合,
      そのときに指定した色が背景色となる.

1.2 画像ファイルから新規にイメージを生成する

以下のフォーマットの画像ファイルを読みこんで,GD::Imageオブジェクトを 生成することができます.

さらにあまり一般的ではありませんが,gd固有の画像フォーマットとしてGd, Gd2形式があり,これらで保存したイメージからもGD::Imageオブジェクトを作 成することができます.

典型的な例を挙げると,PNGイメージファイルからイメージを読み込んで, GD::Imageオブジェクトを生成するためのクラスメソッドは次の通りです.

GD::Image.newFromPng(file)

    - fileからイメージを読み込んでGD::Imageオブジェクトを生成する.
      file はFile.openによって読み取り用にオープンされたFileインス
      タンスか,標準入力.戻り値は生成されたGD::Imageオブジェクト.


2. イメージの操作

線分や図形を描画する際に点などの位置,または角度の情報を与えることが必 要になります.その際使用される座標軸はイメージの左上角を原点として下図 のように表されます.
(イメージの左上角)       X
       +-------------->
       |
       |   .
       |   (x,y)
       |
       |
     Y v

2.1 線分を描く

イメージに単色の線分を描くには,次のメソッドを用います.

GD::Image#line(x1, y1, x2, y2, color)

    - (x1, y1) から (x2, y2) まで color で指定された色で
      線分を描く.color は GD::Image#colorAllocateで指定
      された色.

im = GD::Image.new(100,100)
# 背景色は黒.im.colorAllocate("#000000") と書いてもいいです.以下同様.
black = im.colorAllocate(0, 0, 0) 
red = im.colorAllocate(255, 0, 0)

# (0,0) から (50, 50) まで赤で線分を描く
im.line(0,0,50,50,red) 
出力例:line.png

2.2 カスタマイズした線分を描く

(1) 描画色のパターンを指定する

上のようにcolorとしてGD::Image#colorAllocateで割り当てられた色を用いる と単色の線分が描かれますが,GD::Image#setStyleを用いると,この線分の色 をカスタマイズすることができます.これを利用して好みでカラフルな線分, 破線や点線などを生成することもできます.

GD::Image#setStyle(color1, color2...)

    - 描画色パターンを指定する.引数は1つ以上のGD::Image#colorAllocate 
      で割り当てられた色,もしくはGD::Transparentによって指定された透
      明色.

GD::Styled

    - 描画メソッドの色の引数にこの定数を与えると,上の
      GD::Image#setStyle で指定された色パターンを用いて描画が行われる.

例 ("カラフル"な線分を描く)

black  = im.colorAllocate(0,0,0) # 背景は黒.
red    = im.colorAllocate(255, 0, 0) # im.colorAllocate("#FF0000") でも
blue   = im.colorAllocate(0, 0, 255) # だいじょうぶです.以下同様.
green  = im.colorAllocate(0, 255, 0)

# 赤,緑,青の順に2ピクセルづつ色が異なる線分のスタイルを指定する.
im.setStyle(red, red, green, green, blue, blue) 

im.line(0,0,100,100, GD::Styled)
出力例:styled_line.png

破線や点線を描くときには,線分が切れて描かれないところは背景の画像の色 がそのままであった方がよいでしょう.この目的のためにスタイルの指定時に 定数GD::Transparentを使うことができます.

例 (赤地に白色の破線を描く)

red    = im.colorAllocate(255, 0, 0)
white  = im.colorAllocate(255, 255, 255)

im.setStyle(white, white, white, 
            GD::Transparent, GD::Transparent, GD::Transparent)

im.line(0,0,100,100, GD::Styled)
出力例:styled_line_transparent.png

もちろん,線を描画するイメージが複雑でもちゃんと透過してくれます.

出力例:demoout.png

(2) ブラシを指定する

幅があり何らかの形状を持つイメージを繰り返しを用いて線や図形を描くこと があります.そのようなイメージは特にブラシ(brush) といいます.Ruby/GD では任意のGD::Imageオブジェクトをブラシとすることができま す.このブラシを用いて線分を描くことができます.そのためには,ブラシイ メージを指定しなければなりません.

GD::Image#setBrush(image)

    - ブラシとしてimageを指定する.

GD::Image::Brushed

    - 描画メソッドの色の引数にこの定数を与えると,
      上のGD::Image#setBrushで指定したブラシで描画される.

例 (ブラシを使って線分を描く:その1)

例えば,
xpy.png("xpy.png")
をブラシとして指定したいとします(こ のイメージに描かれている絵を以降「プヨの絵」と称することにします).

im = GD::Image.new(100,100)
white = im.colorAllocate(255,255,255) # 背景色は白
black = im.colorAllocate(0,0,0)

br_file = open("xpy.png", "r")
brush = GD::Image.newFromPng(br_file)
im.setBrush (brush)

# (0,0) から (100,100) までsetBrushで指定したブラシイメージで線分を描く
im.line(0,0,100,100,GD::Brushed)    
出力例:brush1.png

これはまさに期待される出力結果なのですが,指定したブラシイメージが悪かっ たんでしょうね.もう少し見映えよく線分が描画されたいものです.

まず,元のブラシ画像でプヨの絵の境界から外は背景の色を透過させたい.そ してプヨの絵がもうすこし分かるように描画されたい.前者の目的のために次 のメソッドが提供されています.

GD::Image#transparent(color)

    - 透過色をcolorで指定された色にする.colorは
      GD::Image#colorAllocateなどによってわりあてられた色.
次の定数を利用すると,後者の目的を果たすことができます.
GD::StyledBrushed

    - 描画メソッドの色の引数にこの定数を与えると,GD::Image#setStyleで
      指定されたパターンで,GD::Image#setBrushで指定したブラシによって
      描画される.

例 (ブラシを使って線分を描く:その2)

im = GD::Image.new(100,100)
white = im.colorAllocate(255,255,255) # 背景色は白
dummy_color = im.colorAllocate(0,0,0)

file = open("xpy.png", "r")
brush = GD::Image.newFromPng file
brush.transparent white     # 透過色を白(背景色)に設定する.

# 10個のGD::Transparentからなる配列を生成.
tr_ary = Array.new(10, GD::Transparent)  

im.setBrush(brush)
# 描画パターンの指定.*を付けて配列を展開していることに注意.
im.setStyle(black, *tr_ary)  

im.line(0,0,100,100,GD::StyledBrushed)

出力例:brush2.png ほとんど「線分の描画」というテーマから離れてきているな...

上のスクリプト例では,描画パターンの指定は「黒1ピクセルに10ピクセル分 の背景イメージ色を変化させない」ですが,GD::StyledBrushed を描画メソッドの色の引数に与えると,描画メソッドはこのsetStyleメソッド で指定されたパターンを見て,「GD::Transparent0と指定されていないところ」(上の例では "black")にブラシイメージを配置します.ですから "black" でなくても任意の色であればよいのです.

まとめると,上の例では,「10ピクセルの間隔でプヨの絵を並べる」というパ ターンで線分を描画していることになります.プヨの絵は縦横ともに32ピクセ ルなので一部重なっているのです.

2.3 図形を描く

以下に示すメソッドで描画色を指定する際,上でとりあげた GD::StyledGD::BrushedGD::StyledBrushedを指定することができます.

長方形を描く

長方形を描くためには次のメソッドを用います.

GD::Image#rectangle(x1, y1, x2, y2, color)

    - (x1,y1)から(x2,y2)までの線分を対角線とする長方形を color 色で描く.

例 (長方形および破線の長方形)

im = GD::Image.new(100, 100)
white = im.colorAllocate(255, 255, 255) # 背景は白
black = im.colorAllocate(0, 0, 0)

im.setStyle(black, black, GD::Transparent, GD::Transparent)

im.rectangle(10, 10, 50, 50, black)
im.rectangle(50, 50, 90, 90, GD::Styled)
出力例:rectangle.png

弧を描く

次のメソッドを用います.

GD::Image#arc(cx, cy, w, h, s, e, color)

    - (cx,cy) を中心にもつ幅w高さhの楕円のうち,角度s から角度eまでの範囲を
      色colorにて描画する.

例 (楕円,およびブラシで描かれた円弧

im = GD::Image.new(100, 100)
brush = GD::Image.new(5,5)
green = brush.colorAllocate(0,255,0) # ブラシの背景は緑

white = im.colorAllocate(255, 255, 255) # 背景は白
black = im.colorAllocate(0, 0, 0)

im.setBrush br # 5x5ピクセルの緑色の正方形をブラシに設定

im.arc(25, 50, 40, 90, 0, 360, black)
im.arc(75, 50, 40, 90, 0, 160, GD::Brushed)
出力例:arc.png

2.4 多角形 (ポリゴン) を描く

ポリゴンを描画するためには,次のような手順にしたがいます.

  1. クラスメソッドGD::Polygon.newによって新しいポリゴンオブジェクトを生成する
  2. ポリゴンオブジェクトに提供されているメソッドを用いて,頂点の追加等の操作を行なう(下記参照).
  3. GD::Image#polygonによってポリゴンをイメージ上に描く.

頂点の追加

ポリゴンを生成するには,ポリゴンを形成する(頂)点を指定する必要がありま す.そのためのメソッドは次のようなものです.

GD::Polygon#addPt(x, y)

    - ポリゴンオブジェクトに,(x, y)に位置する点を追加する.
頂点は追加された順にインデックスが振られ,n角形ならばそれぞれ追加した 順に0番目,1番目,... ,(n-1)番目の頂点になります.

例 (三角形を描画する)

im = GD::Image.new(100,100)
black = im.colorAllocate(0,255,0) # 背景色は緑
red = im.colorAllocate(255, 0, 0) # ポリゴンの色は赤にしよう.

poly = GD::Polygon.new # この時点では多角形の頂点は存在していない.
poly.addPt(25, 25)     # 以下,頂点の追加
poly.addPt(75, 25)
poly.addPt(25, 75)

im.polygon(poly, red)  # イメージ im 上にこのポリゴンを描画

出力例:poly.png

色の指定に,2.1で扱ったGD::Styled,GD::Brushed, GD::StyledBrushedを用いることもできます.

頂点の追加を次のように書いても,上のスクリプトと同様の出力結果を得ること ができます.

poly = GD::Polygon.new # この時点では多角形の頂点は存在していない.
poly.toPt(25, 25)     # 以下,頂点の追加
poly.toPt(50, 0)
poly.toPt(-50, 50)
GD::Polygon#toPt(dx, dy)

    - ポリゴンオブジェクトの頂点から(dx, dy)だけ増減させた座標に
      新しい点を追加する.頂点が存在しないとき,(dx, dy)が最初の
      頂点となる.

GD::Polygon.newによって新規にオブジェクトを生成した時点ではまだ頂点が 存在しないので,上のpoly.toPt(25,25)によって最初の新しい 頂点(25,25)が生成されます.その次のpoly.toPt(50,0)によっ て,新規に頂点(25+50, 25+0)が追加されます.以下同様です.

頂点の変更

ある頂点の座標の値を変更したいときもあります.そのためのメソッドが GD::Image#setPtです.

GD::Polygon#setPt(idx, new_x, new_y)

    - idx番目の頂点の座標を(new_x, new_y)に設定する.

例 (最初の頂点を変更する)

im = GD::Image.new(100,100)
black = im.colorAllocate(0,255,0) # 背景色は緑.
red = im.colorAllocate(255, 0, 0) # ポリゴンの色は赤.

poly = GD::Polygon.new 
poly.addPt(25, 25)     
poly.addPt(75, 25)
poly.addPt(25, 75)

poly.setPt(0, 100, 100)  # 最初の(0番目の)頂点座標を(100,100)に変更した.
im.polygon(poly, red)  # イメージ im 上にこのポリゴンを赤で描画.
出力例:poly_setPt.png

頂点の削除

ある頂点を削除したい場合には次のメソッドを用います.
GD::Polygon#deletePt(idx)

    - idx番目の頂点を削除する.

ポリゴンの情報を得る (頂点座標の把握)

ポリゴンオブジェクトが現在どのような頂点を持っているか知りたいことがあ ります.そのために,次のようなメソッドが用意されています.
GD::Polygon#vertices

    - ポリゴンの頂点座標をArrayに格納して返す.

GD::Polygon#length

    - ポリゴンの頂点の個数を返す.

...
poly = GD::Polygon.new 
poly.addPt(25, 25)     
poly.addPt(75, 25)
poly.addPt(25, 75)

p poly.vertices          # => [[25, 25], [75, 25], [25, 75]]
p poly.length            # => 3

poly.setPt(0, 100, 100)  

p poly.vertices          # => [[100, 100], [75, 25], [25, 75]]
...

2.5 イメージのコピー

あるイメージを別のイメージ上にコピーしたいときのために,コピー方法に応 じて次の4つのメソッドが提供されています.

そのうち2つは,イメージの一部をコピーして別のイメージの指定した箇所に 貼りつけるものです.

GD::Image#copy(Image, to_x, to_y, from_x, from_y, w, h)

    - コピー先のImageの(to_x, to_y)に,コピー元(self)の(from_x, from_y)
      を左上角とした幅w高さhの領域をコピーする.

GD::Image#copyResized(Image, to_x, to_y, from_x, from_y, to_w, to_h, from_w, from_h)

    - コピー先のImageの(to_x, to_y)を左角上とする幅to_w高さto_hの領域
      に,コピー元(self)の(from_x, from_y) を左上角とした幅from_w
      高さfrom_hの領域をコピーする.領域の大きさが異なる場合拡大または
      縮小される.

例 (画像ファイルからイメージを読んで,新規イメージへコピー)

高さが同一の2つのPNGイメージ(0.png0.pngと1.png1.png)からイメージオブジェクトをそれぞれ生成し,新規イメー ジにコピーします.新規イメージの幅は2つのコピー元のイメージがちょうど おさまるようにします.
f0 = open("0.png", "r")
f1 = open("1.png", "r")
im0 = GD::Image.newFromPng(f0)
im1 = GD::Image.newFromPng(f1)
f0.close
f1.close

# GD::Image#width,GD::Image#height はそれぞれイメージの
# 幅と高さを返すメソッド.
im = GD::Image.new(im0.width+im1.width, im0.height)
# 何はともかく背景色は必ず設定する.重要!
im.colorAllocate(0,0,0)

im0.copy(im, 0, 0, 0, 0, im0.width, im0.height)
im1.copy(im, im0.width+1, 0, 0, 0, im1.width, im1.height)

出力例:copy.png

[注意]
悲しいことに,PNG形式の利点の1つであるアルファ チャネルは現段階のgdではサポートされていないようです.よって,上の 0.png,1.pngをそのままGD経由で読み込むとアルファチャネルが無視され,出 力されたPNG画像は上のように見映えが悪くなってしまいます.
あとの2つは,イメージの一部をコピー先のイメージの指定した箇所に"重ねあ わせるように"(コピーした部分の元画像が見えるように)コピーするものです. この機能を用いると,単一色画像を用いて,コピー先の指定した部分をハイラ イト(強調)するような効果をもたせることができます.
GD::Image#copyMerge(Image, to_x, to_y, from_x, from_y, w, h, percent)

    - コピー先のImageの(to_x, to_y)に,コピー元(self)の(from_x,
      from_y) を左上角とした幅w高さhの領域を重ねあわせる.混ざり具合は
      percentで指定する.
GD::Image#copyMergeGray(Image, to_x, to_y, from_x, from_y, w, h, percent)

    - コピー先のImageの(to_x, to_y)に,コピー元(self)の(from_x,
      from_y) を左上角とした幅w高さhの領域を,グレースケールで重ねあわ
      せる.混ざり具合はpercentで指定する.

例 (イメージの一部の領域を"強調"する)

f0 = open("demoin.png", "r")
im = GD::Image.newFromPng(f0)
f0.close

# 緑の背景色をもつイメージの生成
hilightt_im = GD::Image.new(50, 50)
green = hilight_im.colorAllocate(0, 255, 0)

# im の(25,25)のところからhilight_imを透過度50%で重ね合わせる.
hilit_im.copyMerge(im, 25, 25, 0, 0, 50, 50, 50)

出力例:copy_merge.png

GD::Image#copyMergeGrayはどうも上手く機能し ないようです..gd に添付されているサンプル画像(copy_merge.png)でも, グレースケールにはならず,copyMerge された画像と同一になっています.


3. 文字(列)を描く

文字(列)の描画には,gd であらかじめ容易されたフォントをGD::Fontクラス を用いて利用する方法と,TrueTypeフォントを用いる方法の2種類があります. 後者の利用には,gd-1.6.1 以上が必要です.

3.1 GD::Fontクラスで提供されるフォント を使う

gdでは"Giant","Large","Medium","Small","Tiny"という5種類のサイズの 異なるフォントが用意されています.Ruby/GD でそれらを利用するために GD::Fontクラスが提供されており,このクラスのオブジェクト を用いて文字列の描画を行なうことができます.

GD::Font.new(Font_size)

    - GD::Fontオブジェクトの生成を行うクラスメソッド.上記の5つの
      フォントのどれを使うかは,Font_sizeに文字列
      "Giant","Large","Medium","Small","Tiny" 
      を指定することによって決定する.
GD::Fontオブジェクトには,指定したフォントのポイントサイズなどを調べる メソッドが提供されています.そのようなメソッドが必要ないとき,フォント の指定にはGD::Fontクラスで定義されている定数GD::Font::GiantFontGD::Font::LargeFontGD::Font::MediumFontGD::Font::SmallFontGD::Font::TinyFontを使用することもできます.

実際に文字列を描画するメソッドは次の通りです.

GD::Image#sting(Font, x, y, str, color)

    - 位置(x, y)に,フォント Font を用いて文字列 str をcolor色で描画する.
      Font には上で生成されたGD::Fontオブジェクトか,またはGD::Fontクラスで
      定義された定数を用いる.

例 (文字列の描画: フォントをGD::Fontインタンスで指定する)

# GD::Fontオブジェクトの生成
giant = GD::Font.new("Giant")
large = GD::Font.new("Large")
medium = GD::Font.new("Medium")
small = GD::Font.new("Small")
tiny = GD::Font.new("Tiny")

string = "Hello, Ruby/GD World!!"
str_size = string.size

# GD::Font#width はフォントに含まれる文字の幅を返すメソッド
im_width = giant.width*str_size
# GD::Font#height はフォントに含まれる文字の高さを返すメソッド
im_height = giant.height + large.height + medium.height + small.height + tiny.height

# 描画するべきイメージの生成
im = GD::Image.new(im_width, im_height)
white = im.colorAllocate(255, 255, 255) # 背景は白色
black = im.colorAllocate(0, 0, 0)

im.string(giant, 0, 0, string, black)
im.string(large, 0, giant.height, string, black)
im.string(medium, 0, giant.height + large.height, string, black)
im.string(small, 0, giant.height + large.height + medium.height, string, black)
im.string(tiny, 0, giant.height + large.height + medium.height + small.height, string, black)

出力例:string.png

例 (文字列の描画: フォントを定数で指定する)

im = GD::Image.new(200, 30)
white = im.colorAllocate(0, 0, 0) # 背景は黒色
green = im.colorAllocate(0, 255, 0) 

im.string(GD::Font::GiantFont, 0, 0, "This is the Giant font", green)
出力例:string2.png

3.2 TrueType フォントを使う

gd-1.6.1 からはTrueTypeフォントで描画することが可能になりました. TrueTypeフォントを用いて文字列を描画するメソッドは次の通りです.
GD::Image#stingTTF(color, font_path, pt, angle, x, y, str)

    - 位置(x, y)に,font_pathで指定されたTrueTypeフォントを用いて,
      ポイントサイズpt, 回転角angle(ラジアン),色colorで文字列strを描画する.
      戻り値は次のような配列である.
        ["エラーメッセージ", [文字列のBounding boxの8つの頂点からなる配列]]
      文字列が正しく描画された場合,エラーメッセージはnilとなる.

例 (TrueTypeフォントを用いた描画)

im = GD::Image.new(100, 100)
white = im.colorAllocate(255, 255, 255)
blue = im.colorAllocate(0, 0, 255)

# フォントパスは文字列にて指定する
ttfont = "/usr/share/fonts/TrueType/wadalab-gothic.ttf"

str = "日本語文字列もオッケー!"
angle = 45*(Math::PI/180.0) # degree -> radian

im.stringTTF(blue, ttfont, 14, angle, 0, 50, str)
出力例:stringttf1.png

上の例では,14ポイントフォントで文字列をイメージ左上角(0, 0)のところか ら-45度傾けて描画しているのですが,イメージサイズは100x100ピクセルで決 め打ちしているので,出力例のように文字列がはみ出て一部表示できていませ ん.そこで,メソッドの戻り値を調べてみると,

[nil, [1, 4, 147, 151, 160, 138, 13, -7]]

となっています.2番目の要素の配列の意味は下の通りです.

 (13, -7)               (160, 138)
    +------------------+
    |日本語文字列もオッケー!|         # 実際には-45度傾いてます.
    +------------------+
  (1,4)                 (147, 151)

よって,指定された文字列をちゃんと表示するには100x100のイメージ では不十分で,もう少し大きなイメージが必要です.その情報は上の配列から 得ることができますが,これを得るためには実際に1回文字列を描画するとい う,不要な(しかもコストが高い)操作を伴ないます.

要は,実際に描画するに先立って上の頂点の配列の情報が分かればそれなりに 対処もできるということです.そこでこの目的のために,次のクラスメソッド が提供されています.

GD::Image.stingTTF(color, font_path, pt, angle, x, y, str)

    - GD::Image#stringTTFと同じであるが,実際に描画は行わない.描画し
      た場合に得られるエラーメッセージと,Bouding Boxの配列からなる配
      列を返す:
      ["エラーメッセージ", [文字列のBounding boxの8つの頂点からなる配列]]
      正しく描画できる場合,エラーメッセージはnilである.

例 (TrueType フォントを用いた描画:改訂版)

ttfont = "/usr/share/fonts/TrueType/wadalab-gothic.ttf"
str = "日本語文字列もオッケー!"
angle = -45*(Math::PI/180.0)

# Bounding box の頂点を先ずは把握する.
msg, brect = GD::Image.stringTTF(0, ttfont, 14, angle, 0, 0, str)
unless msg == nil
  puts msg
  exit 1
end

# イメージサイズは,文字列がちょうど描ける大きさに...
im_height = [brect[3], brect[5]].max - [brect[1], brect[7]].min
im_width = [brect[2], brect[4]].max - [brect[0], brect[6]].min

im = GD::Image.new(im_width, im_height)
white = im.colorAllocate(255, 255, 255)
blue = im.colorAllocate(0, 0, 255)

msg, brect = im.stringTTF(blue, ttfont, 14, angle, 0, 7, str)

出力例:stringttf2.png

上のスクリプトは行き当たりばったりなので(^^;;,より一般的な例については, 配布物に同梱されているgdtestttf.rbを参照して下さい.


4. イメージの出力

Ruby/GD が対応している出力イメージフォーマットのうち,よく使う2つのメ ソッドだけ示します.
GD::Image#png(file)

    - 完成したイメージをPNG形式のイメージとしてfileに出力する.file は
      File.openで指定された書きこみ可能なファイルか,$stdout(標準出力)
      などである.

GD::Image#jpeg(file, quality)

    - 完成したイメージをJPEG形式のイメージとしてfileに出力する.file 
      はFile.openで指定された書きこみ可能なファイルか,$stdout(標準出
      力)などである.また,quality は0-100までの数値を指定した場合には
      対応する品質で出力される.-1を指定した場合にはgdで提供されたデフォ
      ルトの品質で出力される.

5. 補足

今までのところで言及できなかった幾つかの重要と思われる事項を最後にまと めておきます.

5.1 オブジェクトの削除

イメージを出力した後や,設定したブラシで描画が終わった後など,不要となっ たイメージオブジェクトはgdの流儀にしたがうならば,GD::Image#deleteにて即座に削除するのがよい のかも知れません.

5.2 インタレース

イメージ全体を全部読みこんでから一度に表示させるのではなく,読み込んだ 順から徐々に表示させていくプログレッシブ効果を実現するためにはインタレー ス機能を用います.

Ruby/GD ではGD::Image#interlace= というメソッドが用意されていて,これにtrueを設定すると, イメージがインタレースされます.

またイメージがインタレースされているかどうか知りたいときのために GD::Image#interlaceというメソッ ドが用意されており,インタレースされているならばtrue を返 します.

インタレース機能の使用例については,'sample/webpng.rb' を御覧ください.

5.3 色の割り当てについてもっと

GD::Imageオブジェクトは内部に(r,g,b)の組を256個保持し(こ れをカラーテーブルと呼びます),それぞれに0番目から255番目までインデッ クスが振られています.すなわち,GD::Image#colorAllocateによって,ひとつのイ メージに高々256種類の色しか割り当てることができません.しかも,このメ ソッドは機械的に引数(r,g,b)(または"#xxxxxx")を登録して新しいインデック スを作成してしまうため,既製のイメージにさらに色を割り当てる際,既に登 録されている(r,g,b)の組の存在を知らずに重複して割り当ててしまうことが あります.比較的多数の色が使われているイメージを扱う場合,このような無 駄は最大256 色という制限には好ましくないです.

そこで,いま割り当てようとする色が既にイメージの中で使われているのなら ば,それのインデックスを使えるようにすれば効率よいというものです.その ために次のメソッドが提供されています.

GD::Image#colorExact(r, g, b)
GD::Image#colorExact(str)

    - 色(r, g, b)(または"#xxxxxx")の組が既に用いられているのならば,そ
      のカラーテーブルのインデックスを返す.用いられていない場合には,-
      1を返す.

次に,色がこれ以上追加できないような状況を考えてみましょう.この場合, GD::Image#colorAllocateを使うこ とは勿論できません.そこで,上のGD::Image#colorExact(r, g, b)の利用が考えられます. ところが,これの戻り値が-1であり,割り当てようとした色は未だ登録されて いないとします.このような場合,既存のイメージに使われている色のうち, 割り当てようとしている色に最も「近い」色のインデックスを返してくれる便 利なメソッドが提供されているので,それを使用することができます.

GD::Image#colorClosest(r, g, b)
GD::Image#colorClosest(str)

    - 色(r, g, b)(または"#xxxxxx")の組に最も「近い」カラーテーブルのイ
      ンデックスを返す.
すると,色の割り当ての方法の中で一番効率的な方法というのは次のようにな ります(よほど多数の色を使っている状況ではない限りここまですることは必 要ないのかも知れませんが).

  1. GD::Image#colorExactを用い てイメージオブジェクト内のカラーテーブルを調べ,既にその色が使われてい ることが判明すれば,そのインデックスを用いる.
  2. この色が未だ使われていない場合には,GD::Image#colorAllocateによって新規にカラー テーブルに色を割り当てる.
  3. 既に256色使ってしまっている場合,これ以上色は割り当てられないので, やむを得ずGD::Image#colorClosest を用いてイメージオブ ジェクトのカラーテーブルの中で一番近い色を「代用色」として使用する.

これをRubyのコードで書くと次のようになります.

# RGB要素が(r0, g0, b0)である色color0を割り当てようとする.
if (color0 = im.colorExact(r0, g0, b0)) == -1
  if (color0 = im.colorAllocate(r0, g0, b0)) == -1
    color0 = im.colorClosest(r0, g0, b0)
  end
end  
色を割り当てる際,毎回このようなコードを書くのは面倒です.Ruby/GD では 上のコードと等価なことを次のメソッドで実現することができます.
GD::Image#colorResolve(r, g, b)
GD::Image#colorResolve(str)

    - 割り当てようとする色(r, g, b)(または"#xxxxxx")が既にカラーテーブ
      ルに登録されていたらそれに対応するインデックスを返す.そうでない
      場合は新たに登録し,対応するインデックスを返す.さらに登録できな
      い場合には,イメージのカラーテーブルの中から,(r, g, b)(または
      "#xxxxxx")に最も近い色のインデックスを返す.
GD::Image#colorAllocateに対するこの メソッドの利点は,イメージで既に何色使われていようが,どんな色を指定し ようが,必ず対応するインデックスが返ってくるということです.

例 (GD::Image#colorResolveで,多いときも安心)

では,このメソッドの使用例を下の人為的なイメージを用いて例示してみましょ う./usr/X11R6/lib/X11/rgb.txtの最初の256行に記載されている色を,GD::Image#colorAllocateを用いて全て使用したイメー ジです.

256.png:既に256色使ってしまったイメージ

実は,rgb.txtの最初の256行には重複しているエントリが幾つか存在し,上の イメージも厳密に256色使っているわけではありません.ところが,GD::Image#colorAllocateは重複のチェックはせずに単 にカラーテーブルに指定した色のRGB要素を追加していくだけなので,イメー ジオブジェクトの内部ではカラーテーブルの全てが使われて,これ以上色の追 加は不可能な状態になっています. このイメージの上に,

  • blue(このイメージのカラーテーブルに既に登録されている色)
  • purple1(このイメージのカラーテーブルに既に登録されていない色)

で描画します.色blueを使って線分を描き,色purple1を使って塗り潰し四角 形を描いてみます.

blue = im.colorResolve(0, 0, 255)
# (155,48,255) はrgb.txtで定義されているpurple1のRGB要素.
purple1 = im.colorResolve(155, 48, 255)
# これの戻り値は220,すなわちこの色に最も近い色がイメージの
# インデックス220に発見された.

im.line(0,0,im.width, im.height, blue)
im.filledRectangle(0,im.height-30, 30, im.height, purple1)

出力例:resolve.png

blue はイメージオブジェクトのカラーテーブル内に対応するインデックスが あるので,それを用いて線分が描かれています.また,左下隅に,イメージに 既に登録されている色のなかで purple1 に最も近い色(RGB要素は (160,32,240))で四角形が描画されています.

このように,それ以上色が割り当てられない状況で何らかの色で描画しなけれ ばならないとき,GD::Image#colorResolve を用 いれば最悪でも指定した色にできるだけ近い色で描画することが可能となるの です.



御意見,御要望はこちらまで :田村 龍一(tam@kais.kyoto-u.ac.jp)