ImageMagick 使用例 -- 描画
IM における描画とは、既存の画像に新しい要素を追加する手段です。テキスト描画の多くは複合フォント効果の例ページや画像への注記で扱っていますが、このページでは「[-draw](https://imagemagick.org/command-line-options/#draw)」演算子のそれ以外のより一般的な側面を扱います。draw コマンドは単純な画像を作成する手段として始まりました。しかし時とともに拡張され、ベクターグラフィックからラスター画像への変換のためのインターフェースとなりました。
ImageMagick の描画コマンド コンピュータにおける画像は一般に2つの異なる方法で保存されます。1つ目の最も一般的な方法は、これらの例ページを通じて見てきたもので、ラスターグラフィックとして知られています。この方式では、画像はピクセルの矩形配列として保存されます。もう1つの方法はあまり一般的ではなく、加工しにくいですが、別の意味でより汎用性のあるオブジェクトベクターグラフィックです。この形式では、画像は線・弧・色の塗りつぶし、ときには深さによって記述されます。これが有用なのは、これらの画像をほぼ任意の大きさに拡大縮小しても完璧に表示できるためです。また、ラスター形式の同等物と比べて、非常に大きく複雑な画像をごく少ない容量で記述できます。ベクターグラフィック画像の例としては postscript や、新しい SVG -- Scalable Vector Graphics があります。TrueType フォントもベクターグラフィックの一例で、これにより個々の文字の記述を任意の縮尺で使えます。「[-draw](https://imagemagick.org/command-line-options/#draw)」画像演算子は、ImageMagick のベクター描画機能への窓口であり、IM の通常のコマンドライン画像演算子とはかなり別個のコマンド群を形成しています。 | _一般に使われているベクターグラフィックのファイル形式はごくわずかで、そうした形式はどれも互いに大きく異なるのが普通です。その結果、コードの共有はほとんどできません。
このため、ImageMagick はベクターグラフィックを SVG 形式の画像描画に用いることにより重きを置いています。Postscript や TrueType フォントのグラフィックは、それらの種類のベクターグラフィック形式の描画にずっと適した、他の外部の「デリゲート」ライブラリやアプリケーションに渡されます。
とはいえ、SVG 用のデリゲートが無いわけではありません。一例として、コンパイル時に利用可能な RSVG ライブラリや GTK SVG ライブラリがあります。IM はこれらのライブラリにリンクし、自前で行おうとするのではなく SVG を変換します。_
---|---
基本の描画コマンド
まずは「[-draw](https://imagemagick.org/command-line-options/#draw)」画像演算子の MVG コマンドのうち、最も古く、最も単純で、最も一般的な描画プリミティブから始めましょう。なお、すべての引数は浮動小数点として扱われ、これらの例で私が通常使っているような整数である必要はありません。 |
# 単一ピクセルの描画(2つの方法 -- これらは拡大してあります)
# Point は色付きのピクセルを「塗る」
magick -size 10x6 xc:skyblue -fill black \
-draw 'point 3,2' -scale 100x60 draw_point.gif
# Color Point は色付きのピクセルを「置き換える」
magick -size 10x6 xc:skyblue -fill black \
-draw 'color 6,3 point' -scale 100x60 draw_color_point.gif
![[IM Output]](../static/img/draw/draw_point.gif)
![[IM Output]](../static/img/draw/draw_color_point.gif)
これら2つの point の方法は、コメントにある通り、半透明の色が関わると異なる結果を生みます。詳細は下記の色の塗りつぶしプリミティブを参照してください。 |
# 矩形 / 角丸矩形 / 矩形内の弧
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "rectangle 20,10 80,50" draw_rect.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "roundrectangle 20,10 80,50 20,15" draw_rrect.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 0,360" draw_arc.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "arc 20,10 80,50 45,270" draw_arc_partial.gif
![[IM Output]](../static/img/draw/draw_rect.gif)
![[IM Output]](../static/img/draw/draw_rrect.gif)
![[IM Output]](../static/img/draw/draw_arc.gif)
![[IM Output]](../static/img/draw/draw_arc_partial.gif)
「arc」描画プリミティブが矩形とともに挙げられているのは、それが実は2つの座標で定義される「rectangle」の内側に収まる「ellipse(楕円)」にすぎないからです。部分的な弧はめったに使われません。角度を90度の倍数に限らない限り、端点を決めるのが難しいことがあるためです。
「circle」と「ellipse」プリミティブは、「中心」座標に、それぞれ「縁」の座標、あるいは「サイズ」と「角度」の値を伴います。 |
# 円 / 楕円 (点を中心とする)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "circle 50,30 40,10" draw_circle.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 0,360" draw_ellipse.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 40,20 45,270" draw_ellipse_partial.gif
![[IM Output]](../static/img/draw/draw_circle.gif)
![[IM Output]](../static/img/draw/draw_ellipse.gif)
![[IM Output]](../static/img/draw/draw_ellipse_partial.gif)
回転させた楕円を作る方法の例については、Push/Pop コンテキストも参照するとよいでしょう。 |
# 線 / 折れ線 / 多角形 / ベジェ
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "line 20,50 90,10" draw_line.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polyline 40,10 20,50 90,10 70,40" draw_polyline.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "polygon 40,10 20,50 90,10 70,40" draw_polygon.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "bezier 40,10 20,50 90,10 70,40" draw_bezier.gif
![[IM Output]](../static/img/draw/draw_line.gif)
![[IM Output]](../static/img/draw/draw_polyline.gif)
![[IM Output]](../static/img/draw/draw_polygon.gif)
![[IM Output]](../static/img/draw/draw_bezier.gif)
線や曲線を描くより良い方法はSVG パス描画を使うことです。これははるかに汎用性が高く、「相対的な線の描画」さえも可能にします。 |
# テキスト描画 / 画像
magick -size 100x60 xc:skyblue -fill white -stroke black \
-font Candice -pointsize 40 -gravity center \
-draw "text 0,0 'Hello'" draw_text.gif
magick -size 100x60 xc:skyblue -gravity center \
-draw "image over 0,0 0,0 'terminal.gif'" draw_image.gif
![[IM Output]](../static/img/draw/draw_text.gif)
![[IM Output]](../static/img/draw/draw_image.gif)
これら最後の2つの fill 系の操作は、現在のところ「[-gravity](https://imagemagick.org/command-line-options/#gravity)」の影響を受ける唯一の draw 操作です。これらの操作のその他の修飾子には、「[-fill](https://imagemagick.org/command-line-options/#fill)」「[-tile](https://imagemagick.org/command-line-options/#tile)」「[-origin](https://imagemagick.org/command-line-options/#origin)」「[-stroke](https://imagemagick.org/command-line-options/#stroke)」「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」「[-font](https://imagemagick.org/command-line-options/#font)」「[-pointsize](https://imagemagick.org/command-line-options/#pointsize)」「[-box](https://imagemagick.org/command-line-options/#box)」があります。他にも修飾子はありますが、それらはより高度な Magick Vector Graphics 言語に関連するものです。
ベジェプリミティブ
「bezier」プリミティブは曲線を描くために使います。各コマンドは曲線セグメントを1つだけ描きます。通常は4つの点(8個の数値)が与えられます。開始点の「ノット(節点)」、2つの制御点、そして終了点の「ノット」です。2つの制御点は、曲線がそれに付随する端点「ノット」からどの方向へ、どれだけ速く逸れるかを定義します。2つの曲線を滑らかに接続するには、終端の制御点を「ノット」を通して鏡映し、次のベジェ曲線の制御点を形成する必要があります。例として、ここでは滑らかに接続する2つのベジェ曲線を描いてみます。(あわせて描いた)制御線と制御点が、接続座標を通って、角度と長さの両方でまっすぐに鏡映している様子に注目してください。これは曲線を滑らかにするために重要です。 |
points="10,10 30,90 25,10 50,50 50,50 75,90 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 75,90 70,10 90,40 " \
draw_bezier_joined.gif
![[IM Output]](../static/img/draw/draw_bezier_joined.gif)
制御点の1つを動かして、同じ「ノット」のもう一方の制御点から見て、付随する「ノット」を通って「鏡映」されない位置にすると、曲線は不連続になります。 |
points="10,10 30,90 25,10 50,50 50,50 80,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 80,50 70,10 90,40 " \
draw_bezier_disjoint.gif
![[IM Output]](../static/img/draw/draw_bezier_disjoint.gif)
制御点をさらに動かして、関連する「ノット」点と一致させると、線はその点から、まったく「曲がり」なしに直接出てきます。 |
points="10,10 30,90 25,10 50,50 50,50 50,50 70,10 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 30,90 25,10 50,50 " \
-draw "stroke red bezier 50,50 50,50 70,10 90,40 " \
draw_bezier_no_curve.gif
![[IM Output]](../static/img/draw/draw_bezier_no_curve.gif)
両方の制御点がそれぞれの「ノット」に設定されると、直線が生成されます。 |
points="10,10 10,10 50,50 50,50 50,50 50,50 90,40 90,40"
clines=`echo "$points" | sed 's/ /\n/g' |\
while read line; do echo "line $line"; done`
symbols=`echo path "'"; for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done; echo "'"`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray $clines stroke blue $symbols " \
-draw "stroke red bezier 10,10 10,10 50,50 50,50 " \
-draw "stroke red bezier 50,50 50,50 90,40 90,40 " \
draw_bezier_lines.gif
![[IM Output]](../static/img/draw/draw_bezier_lines.gif)
「bezier」プリミティブは、4つの点をすべて指定しないと実際にはあまり役に立ちません。最初と最後の点だけが「ノット」に分類され、曲線はそこを通過(あるいは終了)します。その間にある他のすべての点は純粋に制御点とみなされ、与えられた順序で曲線に影響を与えます。制御点が遠いほど、その曲線セグメントへの影響は大きくなります。 |
points="10,10 30,90 25,10 75,90 70,10 90,40"
symbols=`for point in $points; do
echo "M $point l -2,-2 +4,+4 -2,-2 l -2,+2 +4,-4 -2,+2"
done`
magick -size 100x100 xc:skyblue -fill none \
-draw "stroke gray polyline $points " \
-draw "stroke red bezier $points " \
-draw "stroke blue path '$symbols' " \
draw_bezier_multi.gif
![[IM Output]](../static/img/draw/draw_bezier_multi.gif)
物事を単純に保つため、「bezier」曲線セグメントごとに4つより多いか少ない点を使うことは推奨しません。実のところ、「bezier」プリミティブはまったく使わず、代わりに曲線生成にはSVG パス三次ベジェを使うことをお勧めします。これには特別な「S」曲線継続関数があり、滑らかに接続する曲線セグメントを生成するために適切な制御点の「鏡映」を自動的に行ってくれるので、使う必要のある制御点の数が減ります。また、パス内で直前の終点からの相対的な点を定義することもできます。
色の塗りつぶしプリミティブ
上記の「単純な」プリミティブに加えて、「[-draw](https://imagemagick.org/command-line-options/#draw)」は色の塗りつぶしや変更のプリミティブ群も提供します。これらは、指定された点を起点として、選んだ方法に従って画像の色を変更します。これらの fill 系の方法は実際には真の「draw」コマンドではなく、色の置換関数です。プログラムのごく初期のバージョンで、ImageMagick にこれらの操作を組み込む最も簡単な場所が draw だったため、draw に追加されました。上記と同様に、使う色は「[-fill](https://imagemagick.org/command-line-options/#fill)」の色設定で設定しますが、「[-tile](https://imagemagick.org/command-line-options/#tile)」画像が設定されている場合はそちらが代わりに使われます。上記の他の設定オプションは使われず、これらの操作には効果がありません。これらのプリミティブにはさらに2つの設定が適用されます。「[-bordercolor](https://imagemagick.org/command-line-options/#bordercolor)」と「[-fuzz](https://imagemagick.org/command-line-options/#fuzz)」係数の設定です。ただし、これらの設定は「MVG」言語内では定義できないので、「[-draw](https://imagemagick.org/command-line-options/#draw)」演算子を使う前にのみ設定できます。これらのうち最初の「color point」は、上記の例ですでに「point」描画プリミティブの代替として見てきました。よく見ると、テスト画像に設定した単一の白いピクセルが見えます。
|
magick color_test.png -fill white \
-draw 'color 30,20 point' color_point.png
![[IM Output]](../static/img/draw/color_point.png)
しかし、透明や半透明の色を描画する場合、これらの関数は同じではありません。ここに3ピクセルの赤い画像(拡大)があり、その2番目すなわち中央のピクセルには「point」関数を使って、赤いピクセルの上に半透明の青色を塗り、紫の結果を得ています。一方、「color point」関数を使うと(最後すなわち右のピクセル)、赤色は半透明の青ピクセルで完全に置き換えられます。重ねられるのではありません。 |
magick -size 3x1 xc:red -alpha on -fill '#00F8' \
-draw 'point 1,0' \
-draw 'color 2,0 point' -scale 33x33 draw_points.png
![[IM Output]](../static/img/draw/draw_points.png)
すべての「color」関数は完全な色の置換を行うのに対し、他のすべての色プリミティブは画像の上に色を「塗り」ます。そのため、「color」を使って透明な色を描くことができます。「color replace」描画関数は、指定した位置にある、与えられた色とまったく同じ色のすべての箇所を置き換えます。ご覧の通り、その領域は連続している必要はありません。 |
magick color_test.png -fill white \
-draw 'color 30,20 replace' color_replace.png
magick color_test.png -fill white -fuzz 13%\
-draw 'color 30,20 replace' color_replace_fuzz.png
![[IM Output]](../static/img/draw/color_replace_fuzz.png)
しかし最初の結果でわかるように、縁に沿った一部のピクセルは置き換えられませんでした。これらのピクセルは選択したピクセルと_厳密には_同じ色ではないため、無視されました。小さなファズ係数を加えると、元の色に似た色も含まれます。上記の2番目の例で示した通りです。もちろん「ファズ係数」は大した解決策ではありません。そうした縁のピクセルをすべて捕捉できるわけではないからです。これはこれらすべての「色の塗りつぶし」方法に共通する問題で、一般的な解決策がないものです。画像自体から色を選ぶのではなく、特定の既知の色を置き換えたい場合は、代わりに「[-opaque](https://imagemagick.org/command-line-options/#opaque)」画像演算子を使えます。この関数も「[-fuzz](https://imagemagick.org/command-line-options/#fuzz)」係数の設定を使って、与えた色に一致する色の範囲を広げます。「floodfill」方法もかなり単純で、選択した点の周囲の領域全体を塗りつぶすだけで、何らかの形でつながっていない、似た色の他の領域は選択しません。「[-fuzz](https://imagemagick.org/command-line-options/#fuzz)」を使って似た色を含めることで、塗りつぶす領域を広げることもできます。この例では、交差する境界も含むくらい高い値を選び、塗りつぶしが画像の反対側に「漏れる」ようにしています。 |
magick color_test.png -fill white \
-draw 'color 30,20 floodfill' color_floodfill.png
magick color_test.png -fill white -fuzz 15% \
-draw 'color 30,20 floodfill' color_floodfill_fuzz.png
![[IM Output]](../static/img/draw/color_floodfill_fuzz.png)
領域を色で塗りつぶすことには問題がないわけではありません。色は細い境界を越えて、望まない領域へ漏れることがあります(その実演として背景パターン上の GIFを参照)。あるいは、選択した領域を縁まで塗りつぶさないこともあります(アンチエイリアスと塗りつぶしの問題を参照)。しかし機能はします。「filltoborder」は「floodfill」に似ていますが、塗りつぶし処理で置き換えられる色ではなく、塗りつぶす領域を縁取る色を指定します。 もちろん、その境界色の選択に「似た色」を含めて塗りつぶしをさらに限定するため、ファズ係数もあわせて推奨されます。 |
magick color_test.png -fill white -bordercolor royalblue \
-draw 'color 30,20 filltoborder' color_filltoborder.png
magick color_test.png -fill white -bordercolor blue \
-draw 'color 30,20 filltoborder' color_filltoborder2.png
magick color_test.png -fill white -bordercolor blue -fuzz 30% \
-draw 'color 30,20 filltoborder' color_filltoborder_fuzz.png
![[IM Output]](../static/img/draw/color_filltoborder.png)
![[IM Output]](../static/img/draw/color_filltoborder2.png)
![[IM Output]](../static/img/draw/color_filltoborder_fuzz.png)
最後の draw の色の方法は「reset」で、これは画像全体を fill 色に置き換える、すなわちリセットするだけです。この場合、実際に選択したピクセルは結果にまったく影響しません。 |
magick color_test.png -fill white \
-draw 'color 30,20 reset' color_reset.png
![[IM Output]](../static/img/draw/color_reset.png)
これは実際には非常に有用で、既存の画像から単色(あるいはタイル画像)のキャンバスを生成する簡単な1つの方法を提供します。(この方法および同じことを行う他の方法については既存画像に合わせたサイズのキャンバスを参照)。 将来: 「[-tile](https://imagemagick.org/command-line-options/#tile)」パターンを使って領域を塗りつぶす。
アルファ塗りつぶしプリミティブ
「alpha」描画プリミティブは、上記の「color」プリミティブとまったく同じように機能しますが、選択した領域の色を置き換えるのではなく、選択した領域の「アルファ」チャンネルのみを置き換えます。(すなわち、これらの fill 関数によって「アルファ」チャンネルのみが調整されます)。「color」fill 関数と同様に、「アルファ」値は fill 色を使います(「[-tile](https://imagemagick.org/command-line-options/#tile)」を「アルファ値」の供給源として使う場合を除く)。ここでは上記の「color floodfill」と同じ例を使いますが、ここではアルファチャンネルだけを調整して、塗りつぶした部分を完全に透明にします。つまり、元の色はそのまま残っていて、ただ透明になっているのです。 |
magick color_test.png -fill none \
-draw 'alpha 30,20 floodfill' matte_floodfill.png
magick color_test.png -fill none -fuzz 15% \
-draw 'alpha 30,20 floodfill' matte_floodfill_fuzz.png
![[IM Output]](../static/img/draw/matte_floodfill_fuzz.png)
「alpha reset」関数を使って、画像全体を半透明にすることもできます。もちろんこの場合、画像内で半透明の色を受け入れられる PNG に出力しなければなりません。 |
magick color_test.png -fill '#00000080' \
-draw 'alpha 30,20 reset' matte_reset.png
![[IM Output]](../static/img/draw/matte_reset.png)
操作では「black」の色成分は使われず、色のアルファ成分だけが使われたことに注目してください。画像の元の色はそのまま残されます。 将来: 「[-tile](https://imagemagick.org/command-line-options/#tile)」パターンを使った面白いアルファ効果。 「color」と「alpha」はどちらも色の完全置換の関数であり、常にブール的な(オール・オア・ナッシングの)種類の色置換を生み出します。そのため、こうした領域の縁には常にエイリアシング効果が現れます。このため、これらは一般に、GIF 画像の透明領域(これもブール的です)の設定を除けば、一般的な画像開発には良い画像演算子ではありません。とはいえ、すべてが台無しというわけではなく、背景の除去の例で見られます。
描画コマンドの詳細
ピクセル座標
「[-draw](https://imagemagick.org/command-line-options/#draw)」コマンド(および IM の他の多くのコマンド)は「ピクセル座標」と呼ばれるものを使います。つまり、座標「10,10」は、左上隅から10ピクセル下と右に移動したピクセルの中心です。この座標系では、0,0 が左上ピクセルの中心、w-1,h-1 が右下隅の中心です。実際の縁は -0.5,-0.5 と w-0.5,h-0.5 にあり、中心ピクセル(画像が奇数サイズの場合)は「(w-1)/2,(h-1)/2」にあります。しかし、画像を数学的に処理する場合(distort を使う場合など)、実際のピクセルには本当の意味がないため、「画像座標」を使います。この系では、画像の実際の縁が「0,0」と「w,h」にあります。そして画像の中心(ピクセルの中心であってもなくてもよい)は「w/2,h/2」にあります。「ピクセル座標」を画像座標に変換するには ½ を足します。そのため、左上ピクセルの中心は「0.5,0.5」、右下ピクセルは「w-0.5,h-0.5」です。例: 小さな画像での円の中心
ガンマと色空間の補正をともなう描画
ほぼすべての ImageMagick の操作と同様に、「[-draw](https://imagemagick.org/command-line-options/#draw)」は線形演算子であり、そのため線形 RGB 色空間で動作します。これは、滑らかな縁を得るには、保存する前に画像に何らかのガンマ補正を行い、非線形(ガンマ補正済み)の sRGB 色空間で保存されるようにする必要があるかもしれない、ということを意味します。 例えば、大きな円を描いてそのまま保存すると… |
magick -size 81x81 xc:black -fill white -draw 'circle 40,40 40,3' \
circle_raw.png
![[IM Output]](../static/img/draw/circle_raw.png)
円の縁を見てください。実際にはあまり滑らかには見えません。顕著な階段状の効果が見えます。これは、円を線形 RGB 色空間で描いたためです。しかしその後、画像をあたかも本当に sRGB 色空間であるかのように保存してしまったのです。 これを修正するには、保存する前に画像にガンマ補正を加える必要があります。 |
magick -size 81x81 xc:black -fill white -draw 'circle 40,40 40,3' \
-gamma 2.2 circle_gamma.png
![[IM Output]](../static/img/draw/circle_gamma.png)
これで円の縁が、あるべき通りに滑らかで丸く見えるようになりました。これを正しく行うには、本来は色空間を使って補正すべきです。しかし IM は保存時のデフォルト色空間を RGB と仮定するため、正しく処理させるには少しトリッキーな扱いが必要です。 |
magick -size 81x81 xc:black -set colorspace RGB \
-fill white -draw 'circle 40,40 40,3' \
-colorspace sRGB circle_sRGB.png
![[IM Output]](../static/img/draw/circle_sRGB.png)
| sRGB 色空間(画像を保存する正しい方法)は、単に 2.2 のガンマ補正を適用するのとまったく同じではないことに注意してください。しかし、両者の結果の違いは小さく、ごくごく暗い画像でしか見えません。
---|---
| IM v6.7.5-1 より前では、色空間名「sRGB」と「RGB」(線形 RGB)は実際には逆になっていました。そのため、古いバージョンの IM では、上記の2つのラベルを入れ替える必要があります。
---|---
実際の画像を使って正しく描画(あるいは何らかの「線形」画像処理を)するには(IMv7 では)、まず既存のガンマを取り除き、画像を処理してから、そのガンマ補正を復元する必要があります。詳細は色空間補正を使ったリサイズを参照してください。これは実際の画像に描画する例です…まず何の色補正もなし(raw)で、次にガンマ補正と色空間補正ありで。 |
magick rose: -fill none -stroke white -draw 'line 5,40 65,5' rose_raw.png
magick rose: -gamma .454545 \
-fill none -stroke white -draw 'line 5,40 65,5' \
-gamma 2.2 rose_gamma.png
magick rose: -colorspace RGB \
-fill none -stroke white -draw 'line 5,40 65,5' \
-colorspace sRGB rose_sRGB.png
![[IM Output]](../static/img/draw/rose_sRGB.png)
ご覧の通り、ガンマ補正や色空間補正を使うと、直接描画したときに見られるギザギザの「階段状」のエイリアシング効果がなく、線が非常に滑らかに見えるようになります。(見るには非常に良いモニターが必要です) | 上記の線は「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色を使って描かれました。「[-fill](https://imagemagick.org/command-line-options/#fill)」を使って線を描いても同じ結果が得られますが、その場合は「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」による線の太さの制御ができません。詳細は下記のストローク色の設定を参照してください。
---|---
| _色名は実際には「sRGB」色空間の値を使って定義されていますが、draw によって、あたかも画像が線形 RGB 色空間であるかのように適用されます。そのため、上記のガンマ補正を名前付きの色(「white」や「black」以外)と一緒に使うと、それらの色が歪んでしまいます。そうした場合は、ガンマ補正や色空間補正を使わない方が、名前付きの色が正しくマッピングされてよいかもしれません。
名前付き「sRGB」色を、描画先の画像の色空間へ正しくマッピングすることは、IMv7 開発の一環として修正される予定です。
ストローク、ストローク幅、塗りつぶしの相互作用
「[-stroke](https://imagemagick.org/command-line-options/#stroke)」と「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」オプションは、フォントの縁に輪郭を描くときに使われます。これらのオプションは、わずかな手間でテキストをより面白くするために「[-fill](https://imagemagick.org/command-line-options/#fill)」とよく一緒に使われます。
magick -size 380x70 xc:lightblue -pointsize 50 -font Chisel \
-fill green -stroke black -draw 'text 10,55 "Black Border"' \
stroke_font.jpg
デフォルトの設定は「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth) 1」と「[-stroke](https://imagemagick.org/command-line-options/#stroke) None」です。しかしこれだと輪郭のストロークが見えなくなり、「[-fill](https://imagemagick.org/command-line-options/#fill)」色だけが残るので、ストロークは見えません。「[-stroke](https://imagemagick.org/command-line-options/#stroke)」が「不可視」のとき「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」が持つ唯一の効果はフォントのサイズ属性に対するもので、つまりフォントの位置やラベルとキャプション画像生成のサイズに影響を与えることはできます。それ以外では、ストロークを可視にするまで、幅に目に見える効果はありません。「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」が(可視にしたとき)実際にフォントの見た目にどう影響するかを見るために、ここではさまざまな幅で、「オフ」から徐々に大きくしながらテキストを描いてみました。
magick -size 320x420 xc:lightblue -pointsize 70 -font Vademecum \
-fill red -stroke none -draw 'text 30,80 "Stroke -"' \
-fill red -stroke black -strokewidth 0 -draw 'text 30,160 "Stroke 0"' \
-fill red -stroke black -strokewidth 1 -draw 'text 30,240 "Stroke 1"' \
-fill red -stroke black -strokewidth 2 -draw 'text 30,320 "Stroke 2"' \
-fill red -stroke black -strokewidth 3 -draw 'text 30,400 "Stroke 3"' \
stroke_table.jpg
上記の例から、「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」を「0」に設定することは、「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色を「none」(デフォルト)に設定することと同じではないことに注意してください。前者は非常にごく細いストロークの輪郭を作り、後者は事実上それをオフにします。どちらの場合もストロークは依然として描かれます。しかし、「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」が「0」であっても、画像の輪郭は単なる「塗りつぶし」のみの画像(「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色「none」を使う)よりもごくごくわずかに広がることにも注意すべきです。基本的に、「1.0」より小さいどんな幅も正しくは機能しません。これが問題になりうる場合には注意が必要です。ただし、「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」も浮動小数点の設定であることを覚えておいてください。つまり、ストローク幅「0.5」も有効です。とはいえ、これが重要になるのは通常、アンチエイリアスをオフにして細いビットマップ円の描画を試みる場合だけです。
これは極端に大きなストローク幅を使う例です。
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
stroke_thick.jpg
「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」は線を内側と外側の両方に広げることに注意してください。これは同じ例ですが、非常に太いストロークの内側部分を取り除くために、ストローク輪郭なしでフォントを再描画したものです。
magick -size 320x100 xc:lightblue -font Candice -pointsize 72 -fill white \
-stroke black -strokewidth 15 -draw "text 25,65 'Anthony'" \
-stroke none -draw "text 25,65 'Anthony'" \
stroke_outline.jpg
ストロークを使うさらなる例については複合フォント効果を参照してください。特に「風船効果」を見てください。
線(ストローク)の描画
IM のデフォルトの線描画にはいくつか奇妙な挙動があり、知っておく価値があります。これはデフォルトの線描画です… |
magick -size 100x40 xc:lightblue \
-draw "line 5,35 95,5" \
line_default.jpg
![[IM Output]](../static/img/draw/line_default.jpg)
「[-fill](https://imagemagick.org/command-line-options/#fill)」オプションで線の色を設定できます。 |
magick -size 100x40 xc:lightblue \
-fill white -draw "line 5,35 95,5" \
line.jpg
![[IM Output]](../static/img/draw/line.jpg)
また、「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色を設定することで、線を少し太くすることもできます。 |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -draw "line 5,35 95,5" \
line_stroke.jpg
![[IM Output]](../static/img/draw/line_stroke.jpg)
しかし、「[-fill](https://imagemagick.org/command-line-options/#fill)」オプションで指定した白色はどうなったのでしょうか。これが ImageMagick で線を描くときのトリッキーな側面です。プログラムが実際に行うのは、線を幅約1ピクセルの塗りつぶされたオブジェクトとして考えることです。これは自然なことで、一般に複数の線は、塗りつぶされる領域を掃き出すために使われるからです。そのため、前のセクションでフォントにストロークを使ったときと同様に、IM は線(あるいはオブジェクト)を fill 色で描いてから、その周りを stroke 色で描きます。その結果、上記の stroke 色の線は少し太くなり、その下に fill 色が完全に隠れます。stroke 色を半透明にすると、その fill 色を再び見えるようにできます。まとめると、線は「[-fill](https://imagemagick.org/command-line-options/#fill)」色で描かれるように見えますが、そのオプションは、「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色がデフォルトの「none」や「transparent」以外のものとして定義された時点で意味を持たなくなります。 | オプション「-linewidth」は実は「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」のエイリアスにすぎず、使うべきではありません。
---|---
例えば、このコマンドは非常に太い線を生み出すと思うかもしれません。確かにそうなのですが、「[-stroke](https://imagemagick.org/command-line-options/#stroke)」色が不可視なので見えません。線の1ピクセル幅の領域の内側の「塗りつぶし」だけが見えます。 |
magick -size 100x40 xc:lightblue \
-fill white -strokewidth 3 -draw "line 5,35 95,5" \
line_fill_3.jpg
![[IM Output]](../static/img/draw/line_fill_3.jpg)
| 上記の結果を私は実際にはバグだと考えています。塗りつぶされる「領域」がなく、線の「ストローク色」も設定されていないので、何も描かれるべきではありません。IM が現在これを行う理由は、新しいユーザーの混乱を避けるためですが、実際には上級ユーザーに問題を引き起こすだけです。詳細はDraw の塗りつぶし境界を参照してください。
---|---
しかし stroke 色も定義すると、要求した通りの太い線が得られます… |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 3 -draw "line 5,35 95,5" \
line_stroke_3.jpg
![[IM Output]](../static/img/draw/line_stroke_3.jpg)
「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」設定を1にすると、上記の線は完全に覆われます。 |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 1 -draw "line 5,35 95,5" \
line_stroke_1.jpg
![[IM Output]](../static/img/draw/line_stroke_1.jpg)
もちろん、この知識を身につければ、フォント描画と同じように、それを使って創造的になれます。 |
magick -size 100x40 xc:lightblue \
-stroke black -strokewidth 5 -draw "line 5,35 95,5" \
-stroke white -strokewidth 2 -draw "line 5,35 95,5" \
line_multi.jpg
![[IM Output]](../static/img/draw/line_multi.jpg)
ここでは、上記のフォントで行ったのと同じように、最も細い「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth)」設定「0」を使いました。 |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,35 95,5" \
line_stroke_0.jpg
![[IM Output]](../static/img/draw/line_stroke_0.jpg)
これは、黒い点と灰色のセグメントからなる点線という、非常に奇妙な結果を生みます。これは stroke 色、fill 色、背景色の間の奇妙な「色のうなり周波数」の結果です。これは線を拡大した表示です… |
magick -size 25x10 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_white.jpg
![[IM Output]](../static/img/draw/line_stroke_0_white.jpg)
| 「色のうなり周波数」効果は、ごくわずかに調律のずれた2本のギターがあるときに得られる「音のうなり」に似ていなくもありません。この場合、stroke 色が下にある fill 色を完全に上書きする箇所で黒い点が得られ、stroke 色が fill 色と背景色の両方と混ざる箇所で灰色の点が得られます。 この色の混合は、IM が線やその他の draw オブジェクトの見た目を改善しようとして使うアンチエイリアス処理の自然な帰結です。詳細は私のIM におけるアンチエイリアスの議論と例のページを参照してください。
---|---
この効果は斜めの線にのみ現れ、純粋な水平または垂直の線には現れないことに注意してください。そこではエイリアシングが効かず、したがって「色のうなり周波数」効果もないのです。 |
magick -size 100x40 xc:lightblue \
-fill white -stroke black -strokewidth 0 -draw "line 5,20 95,20" \
line_stroke_horz.jpg
![[IM Output]](../static/img/draw/line_stroke_horz.jpg)
ここでは拡大表示で異なる下地の fill 色を使い、色が結果のうなりをどう変えるか見られるようにしました。 |
magick -size 25x10 xc:lightblue \
-fill none -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_none.jpg
magick -size 25x10 xc:lightblue \
-fill red -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_red.jpg
magick -size 25x10 xc:lightblue \
-fill black -stroke black -strokewidth 0 -draw "line 2,8 22,1" \
-scale 400% line_stroke_0_black.jpg
magick -size 25x10 xc:lightblue \
-fill black -stroke none -draw "line 2,8 22,1" \
-scale 400% line_stroke_-_black.jpg
![[IM Output]](../static/img/draw/line_stroke_-_black.jpg)
ご覧の通り、非常に細い線を描くときは、fill 色と stroke 色を同じにするか、あるいはどちらかの色を none に設定してオフにすることで、その「うなり」を減らせます。後者が最良の考えですが、前者の方が特定のプログラミングのニーズには実用的かもしれません。fill の線の太さは「0」であることに注意してください。しかし stroke の線はより大きな太さを持てます。これも浮動小数点の値です。幅2.5ピクセルの線も完全に有効です。 | これらの結果は、バグのあるストローク幅0が色のうなりを引き起こすことだけでなく、塗りつぶされる実際の領域がないのに「fill 色」が余分に直径1.0の太さで描かれることによっても引き起こされます。これも私はバグだと考えています。Draw の塗りつぶし境界を参照してください。
---|---
Draw の塗りつぶし境界
さまざまな描画プリミティブについて、他にも注意すべき点がいくつかあります。ストローク幅は1.0より大きい浮動小数点の値ではうまく機能しますが、1.0未満の値では破綻するようです。これは実装に使われているアルゴリズムによるもので、単に間違っているわけではありません。より太い線では問題なく機能するからです。基本的に、ストローク幅にゼロを使うと、stroke 色が追加されないと期待するかもしれません。代わりに、線が実際にピクセルの「中心」を通るときに stroke 色がフルの強さになる、一種のうなりパターンが得られます。 本来起こるべきは、ピクセルに追加される色の量が、描かれる線の面積を反映することであって、そのピクセルの線からの距離ではないということです。そのため、幅ゼロの線は画像に色をまったく追加すべきではなく、太さ1.0未満の線はより少ない量の色だけを追加すべきです。上記のストローク幅とストロークをともなう線の描画の例を参照してください。 もう1つの問題は、fill 色が描かれる図形(多角形)の縁まで適用されるのではなく、½ピクセル外側まで適用されることです。これには「ストローク」が適用されておらず縁が正確であるべき状況も含まれます。また、実際には「ゼロ」の fill の太さしか持たない「線」を描く状況も含まれます。基本的に、ストロークを有効にせずに線を描くと、技術的には「fill」の太さがないので線は見えないはずです。代わりに、線は最低でも1ピクセル幅の「fill」色を含んで描かれます。これは歴史的な理由によるもので、一般に IM の新しいユーザーの混乱を避けます。残念ながら、上級ユーザーにとっては正しくありません。 これが意味するのは、fill 色だけを使って辺を共有する2つの多角形を描くと、各多角形がすべての縁に沿って½ピクセル大きいため、その辺が1ピクセル重なる、ということです。言い換えれば、多角形やその他の図形は互いにぴったり合わず、重なるのです。例として、ここでは draw を使って画像を2つの半分に分割しようとしてみます(白の上に黒を描く)。これを行うために、辺を正確に重なりなく共有する2つの多角形を描きます。結果の「小さな」画像は、表示のために拡大してあります。
magick -size 10x10 xc: -draw 'polygon 2,-1 7,10 10,10 10,-1' bound_left.gif
magick -size 10x10 xc: -draw 'polygon 2,-1 7,10 -1,10 -1,-1' bound_right.gif
magick bound_left.gif bound_right.gif -compose Plus -composite bound_add.gif
2つの黒い部分(実際に描かれたもの)は、実は互いに重なっています。言い換えれば、描いた多角形を使って2つの領域を別々に描こうとしたのに、塗りつぶされた領域は要求したものよりわずかに大きいのです。さらに、描かれた黒い領域の重なりを実際に見られるよう、2つの画像を(Plus 合成で)足し合わせました。もし2つの多角形が完璧に合致していれば、「足し合わせた」描画は単色の白になるはずです。実際の重なりの量は、デフォルトの「[-strokewidth](https://imagemagick.org/command-line-options/#strokewidth) 1.0」設定に相当します。そのため、通常この余分な領域は通常のストローク幅で覆われると期待されます。しかし、これは実際に問題を引き起こすことがあります。 余談: 接合の完全なテストには、黒い背景に50%灰色の領域を生成して足し合わせます。そうすれば、領域が(上で示したように)「重なる」だけでなく、領域を足し合わせたときに(塗りつぶされた領域の間に隙間を残して)「不足する」かどうかもテストできます。結果の画像は、接合に沿って色のばらつきのない、完璧に滑らかな50%灰色になるはずです。透明度のチェックには、完全に透明な背景上で50%透明・50%灰色の色を使うとよいでしょう。単一のマスク画像に基づく完璧な切り抜きと再合成の例を見るには、合成方法の例Compose DstOutを参照してください。 将来のバグ修正: 塗りつぶされる領域は正確であるべきですが、図形を描くときにこれを補正するために、デフォルトの「stroke 色」は(それ自体が明示的に設定されていない限り)fill 色に設定されるべきです。
MVG - Magick Vector Graphics
上で示したプリミティブは、提供されているすべての「[-draw](https://imagemagick.org/command-line-options/#draw)」操作の基礎を成します。これらを合わせたものが、ImageMagick における特別な内部言語、Magick Vector Graphics 言語の出発点です。この言語の詳細については、IM ウェブサイトの MVG のプリミティブと構文のまとめ を参照してください。この「MVG」言語は、さらに複雑な SVG(Scalable Vector Graphics)言語を ImageMagick で扱えるようにするという目標のもとに設計されました。SVG 形式で与えられた画像を、より単純な内部の MVG 形式へ変換しようとすることでこれを行います。詳細は下記の SVG の扱い を参照してください。結果として、上で見たものは「[-draw](https://imagemagick.org/command-line-options/#draw)」演算子の能力のごく一部にすぎません。もっとも、複雑なオブジェクトを描きたいのであれば、「[Sodipodi](http://the-labs.com/Sodipodi/)」のような SVG エディタを使って、そのオブジェクトの SVG 形式の画像を別途作成することをお勧めします。(下記の IM 以外のベクターグラフィックプログラム を参照)。 SVG と異なり、MVG には「コンテナ」や画像コマンドの集合という形式のものはありません。これらは MVG 描画コマンドの単純化された並びを生成する変換過程ですべて取り除かれます。代わりに、さまざまな描画設定を保存・復元するために グラフィックコンテキスト という概念を使います。次はこれを見ていきましょう。
コマンドライン設定と MVG 設定
まず、描画プリミティブが使うコマンドラインオプションで設定するほぼすべての設定には、MVG 描画コマンドに直接対応するものがあります。コマンドラインオプション(「-strokewidth」など)で設定するのと、MVG 描画文字列内で設定(例えば「stroke-width」)するのとの主な違いは、MVG 設定はその MVG コマンド文字列の間だけ持続するという点です。
**一般的な描画設定のまとめ**
__cmd_option__ __draw_MVG__ __引数__
-fill fill 図形内部の色/タイル
-tile fill 画像タイル、fill 色を置き換える
-stroke stroke 図形周りの線の色/タイル
-strokewidth stroke-width ピクセル幅
+antialias stroke-antialias 0/1 線の縁のエイリアシング
-font font font_name / font_file
-family font-family ?
-weight ? ?
-stretch ? ?
-pointsize font-size ポイント単位の高さ
-kerning - 文字間の追加スペース
+antialias text-antialias 0/1 テキスト描画のエイリアシング
-box text-undercolor フォントの境界ボックスの fill 色
- decorate (None, Underline, LineThrough, Overline)
-gravity gravity (None, North, South-East,...)
-fuzz - 色の差分 / パーセンテージ
-bordercolor - 色
注:
- 該当するオプションなし ? 不明
| これらの設定は、上で定期的に使い、実演してきたので、たいていよく理解されています。 | font, stretch, style, weight は、ImageMagick のフォントリストからフォントを識別するために使われます。しかしほとんどの人は、代わりに特定のフォントと pointsize を選んで使うだけです。そのため、IM ではめったに使われません。 |
|---|---|
ご覧の通り、「色の塗りつぶし」プリミティブのための特別な設定には、MVG に直接対応するものがありません。すなわち「-bordercolor」と「-fuzz」係数の設定です。これらは「[-draw](https://imagemagick.org/command-line-options/#draw)」演算子を使う前に、コマンドラインから指定しなければなりません。一部の MVG 設定は、グローバルなコマンドライン設定としての方が有用でしょう。フォント描画用の「decorate」設定などです。警告: 「-gravity」は SVG 仕様の一部ではありません。MVG 内では、テキストと画像の配置、および位置揃えにのみ使われます。現在のところ、デフォルトの「重力」効果とは別個の位置揃え設定はありません。しかし、位置揃えは SVG テキスト処理の一部なので、これはいずれ変わる可能性が高いです。さて、(MVG 描画文字列の外側の)グローバルなコマンドライン設定は、適用する各「-draw」操作の設定を初期化するために使われます。だからこそ「-fill」の色を設定し、その色の円を描けるのです。 |
|
| --- | |
magick -size 100x60 xc:skyblue -fill red \
-draw "circle 50,30 40,10" draw_circle_global.gif
![[IM Output]](../static/img/draw/draw_circle_global.gif)
そのグローバル設定は、「-draw」MVG 引数内でローカルに上書きできます…
|
magick -size 100x60 xc:skyblue -fill red \
-draw "fill green circle 50,30 40,10" draw_circle_override.gif
![[IM Output]](../static/img/draw/draw_circle_override.gif)
ただし、単一の「-draw」MVG 引数内で設定した設定は、その「-draw」操作の間だけ存在します。つまり、「-draw」内の設定はその draw にローカルなものであって、後の別個の「-draw」引数には引き継がれません。
|
magick -size 100x60 xc:skyblue -fill red -draw 'fill green' \
-draw "circle 50,30 40,10" draw_circle_local.gif
![[IM Output]](../static/img/draw/draw_circle_local.gif)
多くの操作を行う予定がある場合は、複数の「-draw」操作よりも、それらすべてを単一の MVG 文字列で行う方がよいかもしれません。
|
magick -size 100x60 xc:skyblue \
-draw "fill green circle 41,39 44,57
fill blue circle 59,39 56,57
fill red circle 50,21 50,3 " draw_circle_multi.gif
![[IM Output]](../static/img/draw/draw_circle_multi.gif)
MVG 固有の設定 線やオブジェクトの描かれ方を制御するその他の MVG 設定も、プリミティブ操作を使うときでも知っておくと有用です。これらには以下が含まれます…
__draw_MVG__ __説明/引数__
fill-opacity 塗りつぶしの透明度、0.0 から 1.0
clip-rule 交差する線の塗りつぶしスタイル (evenodd, nonzero)
stroke-opacity 線の透明度、0.0 から 1.0 の数値
stroke-dasharray 線の「on」と「off」の長さのリスト
stroke-dash
stroke-linecap 線端の見た目: butt round square
stroke-linejoin 線の接合: butt miter round square
stroke-miterlimit 「miter」接合が「bevel」(または「butt」)になる角度
すべての MVG 設定と描画演算子の完全なリストは、IM ウェブサイトの MVG のプリミティブと構文のまとめ で見られることを覚えておいてください。より単純な設定のいくつかの効果を見てみましょう… |
# ストロークの不透明度
magick -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-opacity 0.8 path 'M 10,20 L 90,20'" \
-draw "stroke-opacity 0.6 path 'M 10,30 L 90,30'" \
-draw "stroke-opacity 0.4 path 'M 10,40 L 90,40'" \
-draw "stroke-opacity 0.2 path 'M 10,50 L 90,50'" \
set_stroke_opacity.gif
# 塗りつぶしの不透明度
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw " rectangle 5,10 15,50 " \
-draw "fill-opacity 0.8 rectangle 20,10 30,50 " \
-draw "fill-opacity 0.6 rectangle 35,10 45,50 " \
-draw "fill-opacity 0.4 rectangle 50,10 60,50 " \
-draw "fill-opacity 0.2 rectangle 65,10 75,50 " \
-draw "fill-opacity 0 rectangle 80,10 90,50 " \
set_fill_opacity.gif
# 通常の線と破線
magick -size 100x60 xc:skyblue -fill none -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw " path 'M 10,10 L 90,10'" \
-draw "stroke-dasharray 5 3 path 'M 10,20 L 90,20'" \
-draw "stroke-dasharray 5 5 path 'M 10,30 L 90,30'" \
-draw "stroke-dasharray 10 3 3 3 path 'M 10,40 L 90,40'" \
-draw "stroke-dasharray 1 6 path 'M 10,50 L 90,50'" \
set_lines_fill.gif
# 注: 技術的には、2番目の画像は1番目と同じになるべきです。
# 「塗りつぶされた」線には面積が含まれないからです。これを私はバグと考えています。
# 線端と接合
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 8 \
-draw " path 'M 20,20 L 20,70'" \
-draw "stroke-linecap butt path 'M 40,20 L 40,70'" \
-draw "stroke-linecap round path 'M 60,20 L 60,70'" \
-draw "stroke-linecap square path 'M 80,20 L 80,70'" \
set_endcaps.gif
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-linejoin miter path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-linejoin bevel path 'M 55,70 L 70,20 85,70'" \
-draw "stroke-linejoin round path 'M 75,70 L 90,20 105,70'" \
set_linejoin.gif
magick -size 100x60 xc:skyblue -fill white -stroke black -strokewidth 5 \
-draw " path 'M 5,70 L 20,20 35,70'" \
-draw "stroke-miterlimit 7 path 'M 35,70 L 50,20 65,70'" \
-draw "stroke-miterlimit 6 path 'M 65,70 L 80,20 95,70'" \
set_miterlimit.gif
![[IM Output]](../static/img/draw/set_endcaps.gif)
![[IM Output]](../static/img/draw/set_linejoin.gif)
![[IM Output]](../static/img/draw/set_miterlimit.gif)
「stroke-miterlimit」設定はかなり実演しづらいものです。このプロパティは、「miter」接合が「bevel」接合に変わる角度を定義します。基本的に、非常に鋭い角度では、miter は2本の線の実際の接合からかなり遠くまで伸びることがあります。これは、その鋭さに最大限度を設定し、伸びすぎたときに角の頂点を鈍らせます。ただし、これは何らかの角度の三角関数値を表すものであり、長さや距離ではないことに注意してください。値は1.0より大きくなければなりません。上記は、私が表示している接合角度に対して、miter が6から7のどこかの値で突然 bevel に変換される様子を示しています。例えば、「stroke-miterlimit」が1.414だと、90度未満の任意の角度で「miter」が「bevel」に変換されます。値4.0(デフォルト)では、およそ29度未満の角度で接合が変換されます。一方、値10.0では、およそ11.5度未満の角度で変換されます。 SVG パス描画 SVG パスは SVG の基本的な描画プリミティブです。線、図形、円、曲線、弧などを描くために使われます。SVG パスの完全な仕様は SVG パス仕様 ドキュメントにあります。しかしこれは、ユーザーではなくプログラマー向けなので、読みやすいドキュメントではありません。そこで、パス仕様を簡略化してまとめます…
- 文字はコマンドであり、(浮動小数点の)すべての数値は引数です。
- カンマやスペースを引数の区切りとして使えますが、それ以外は完全に無視されます。
- 各パス要素の最後の2つの引数 (x,y) が、そのパス要素の終点(または「ノット」)になります。
- 大文字は最終的な点を絶対座標で指定します。
小文字は直前の要素の終点に対する相対です。
例: 「M 1,2 l 3,4 l 2,-4」は「M 1,2 L 4,6 L 6,2」と同じです。
つまり、3,4 が 1,2 に足されて、4,6 への線が描かれます。
次に、2,-4 が足されて、最終座標 6,2 への線が描かれます。 - 各要素の引数は、同じパス文字を再発行しなくても、数値引数のグループをさらに追加することで繰り返せます。ただし曲線については、読みやすさのため、それでも関数文字を追加することをお勧めします。
- 「
M」や「m」の繰り返し引数は、それぞれ「L」や「l」として扱われます。
例: 「M 1,2 3,4 5,6」は「M 1,2 L 3,4 L 5,6」と同じです。
そして: 「m 1,2 3,4 2,-4」は「m 1,2 l 3,4 l 2,-4」と同じです。 - 三次ベジェの場合、すべての点(制御点と終点のノット点)は、直前のパス要素の終点に対する相対で与えられます。
絶対座標または相対座標のどちらかで物事を指定できる点に注目してください。そのため、オブジェクトを相対座標で定義し、パス全体を配置するための最初の絶対「移動」座標を1つ与えるだけで済みます。一方、他の「グラフィックコンテンツ」コマンドを使って、「ビューボックス」や「平行移動」内で描画全体を動かすこともできます(下記参照)。ですから、SVG パスで絶対座標を使うか相対座標を使うかは、本当はどちらでも構いません。 移動、線、パスの閉じ が、SVG オブジェクトパスを学ぶための最初の出発点です。 |
# 開いたパス、完結したパス、閉じたパス(同じ点)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40'" path_open.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 L 20,50 90,10 70,40 40,10'" path_complete.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,50 90,10 70,40 Z'" path_closed.gif
![[IM Output]](../static/img/draw/path_complete.gif)
![[IM Output]](../static/img/draw/path_closed.gif)
ただし、「Z」はループを閉じるだけだということに注意してください。別個のオブジェクトを作るわけではありません。そのため、2つの「閉じた」パスは、重なっていようと完全に分離していようと、依然として単一の描画オブジェクトに分類されます。 ここでは、同じ方向に描かれた、閉じているが重なっている2つのループを示します。単一のパスしか使われていないため、オブジェクトは単一のオブジェクトであり、「fill-rule」設定が重なる領域をどう塗りつぶすかを制御します。 |
# 重なるパスと塗りつぶし規則
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_evenodd.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 70,40 90,10 Z' " path_nonzero.gif
![[IM Output]](../static/img/draw/path_evenodd.gif)
![[IM Output]](../static/img/draw/path_nonzero.gif)
オブジェクトは中心の周りを同じ角度方向に描かれたため、2つの閉じたループは巡回値2を持つ領域を囲みます。そのため、「evenodd」規則はその領域を塗りつぶさず、非ゼロの「nonzero」規則はそれを塗りつぶしました。ただし、すべてのパスは見えていることに注意してください。実際には同じオブジェクトだからです。パスが描かれる方向は非常に重要で、一般にすべてのパスは、オブジェクトの「内側」に対してまったく同じ方向に描かれるべきです。例として、ここでは2番目のオブジェクトを1番目とは逆方向に描きます。そのため、2つのオブジェクトが重なるとき、その領域は「ゼロ」回囲まれます。つまり、どの「fill-rule」を使っても塗りつぶされず、「穴」が作られます。 |
# 重なる閉じたオブジェクト、2番目のオブジェクトは逆方向に描かれる
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule evenodd \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_evenodd.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "fill-rule nonzero \
path 'M 40,10 20,20 70,50 Z
M 20,40 90,10 70,40 Z' " path_rvs_nonzero.gif
![[IM Output]](../static/img/draw/path_rvs_nonzero.gif)
![[IM Output]](../static/img/draw/path_rvs_evenodd.gif)
これは、進行方向に対してオブジェクトの「内側」を同じ側に保つように方向を逆にすることで、オブジェクトに「穴」を作れることを意味します。 |
# 逆方向に描かれた穴を持つオブジェクト!
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,10 20,55 70,50 80,5 Z
M 50,20 60,40 40,30 Z' " path_with_hole.gif
![[IM Output]](../static/img/draw/path_with_hole.gif)
穴は「even」かつ「zero」なので塗りつぶされず、結果は「fill-rule」設定にかかわらず同じです。もちろん、完全に別個の「path」要素を使えば、完全に別個のオブジェクトが生成されます。その場合、「fill-rule」は適用されず、オブジェクトは与えられた順序で互いの上に描かれるだけです。 |
# 別個のパスは別個のオブジェクト
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 40,10 20,20 70,50 Z'
path 'M 20,40 70,40 90,10 Z' " path_separate.gif
将来: 座標軸に揃ったパス「H」と「V」
楕円弧 は SVG パスの円描画機能です…
「large」と「sweep」のパラメータは特に重要です。これらは、そのパス要素について、開始点から終了点へ「弧を描く」4通りの方法のうちどれを使うかを決めるために使われるからです。 2つのフラグ「large」と「sweep」が、その半径の4つの弧のうちどれが2点をつなぐかを定義します。 |
# 楕円弧 : A radius_x,y angle large,sweep x,y
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,0 70,20'" path_arc.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 0,1 70,20'" path_arc2.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,0 70,20'" path_arc3.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,15 0 1,1 70,20'" path_arc4.gif
![[IM Output]](../static/img/draw/path_arc.gif)
![[IM Output]](../static/img/draw/path_arc2.gif)
![[IM Output]](../static/img/draw/path_arc3.gif)
![[IM Output]](../static/img/draw/path_arc4.gif)
2番目のフラグ「sweep」は、弧を直線パスの方向のどちら側に描くべきかを単に決めます。「large」フラグは、楕円の中心の周りを回る長い方のパスを選ぶために使われます。つまり、設定された弧の角度が180度より大きくなります。オフにすると、楕円の中心を含まない小さい方の「弧」が得られ、180度未満の角度にわたって弧を描きます。弧を「Z」で閉じると、最後の直線セグメントを描くだけです。完全な楕円や円を作るには、最初の点から2番目の点へ、そして2番目の点から最初の点へ戻る、少なくとも2つの「弧」セグメントが必要です。両方の弧は同じ「sweep」設定を持つべきで、そうすれば弧は進行方向が異なることで別々の側に来ます。一方の弧は「large」設定をオンにすべきです。 |
# 閉じた、角度のついた楕円弧(2つの端点で定義)
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 Z '" path_arc5.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 1,1 70,20 Z '" path_arc6.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 30,20 20 0,0 70,20 \
A 30,20 20 1,0 30,40 Z '" path_arc7.gif
![[IM Output]](../static/img/draw/path_arc5.gif)
![[IM Output]](../static/img/draw/path_arc6.gif)
![[IM Output]](../static/img/draw/path_arc7.gif)
線が、与えられた角度で与えられた楕円サイズに収まらないほど長い場合、楕円のサイズは、線を中心に楕円が来るように線に合わせて拡大されることに注意してください。これは、軸半径に小さな数値を使うことで、軸の長さの比だけを指定して、直線パスが楕円の中心点を通ることを保証できる、ということを意味します。つまり、パスは楕円の一方の側から他方の側への楕円直径を形成します。これは必ずしも楕円の長軸や短軸ではなく、ただの楕円直径です。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 3,2 45 0,0 70,20'" path_arc_x.gif
![[IM Output]](../static/img/draw/path_arc_x.gif)
もちろん、長さ「1,1」を使うと、一方の点から次の点へ向かう完璧な半円になります。この場合、楕円の角度は何の違いも生みません。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 1,1 0 0,0 70,20'" path_hcircle.gif
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 1,1 0 0,0 70,20
A 1,1 0 1,0 30,40 Z'" path_circle.gif
![[IM Output]](../static/img/draw/path_circle.gif)
SVG の「弧」の定義では、2つの半径のいずれかがゼロの場合、直線を描くべきだとも宣言されています。そのため、半径が「0,0」のどんな弧も、単純な直線の弧になります… |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 0,0 0 0,0 70,20'" path_arc_line.gif
![[IM Output]](../static/img/draw/path_arc_line.gif)
弧に非常に大きな半径を指定し、戻りパスに「large sweep」を指定しないと、2点の間にその半径のレンズ形を作れます。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 30,40 A 50,50 0 0,0 70,20
A 50,50 0 0,0 30,40 Z'" path_lens.gif
![[IM Output]](../static/img/draw/path_lens.gif)
この種類の弧は重要な機能です。本来は直線である線に、小さいながらもはっきりした曲がりを、非常に簡単に与えることができます。例えば、このような単純な三角形の代わりに… |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 L 25,10 L 70,5 L 20,55 Z' " triangle.gif
![[IM Output]](../static/img/draw/triangle.gif)
各線を大きな半径の弧で置き換えて、わずかな曲がりだけを与えることができます。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 A 100,100 0 0,0 25,10
A 100,100 0 0,0 70,5
A 100,100 0 0,0 20,55 Z' " triangle_curved.gif
![[IM Output]](../static/img/draw/triangle_curved.gif)
線の端点には変更がなく、起こったことは各「L」が弧セグメントに置き換えられただけです。ただし、弧のサイズは線の長さに比例すべきです。私はこれをしなかったので、長い対角線は他の2つよりはるかに強く深い曲がりを持ちます。描くオブジェクトをリサイズまたは拡大縮小するときは、半径もその線の長さと同じ量だけ拡大縮小し、曲がりが相応にリサイズされて弧も正しく拡大縮小されるようにすることを覚えておいてください。「sweep」フラグは、各パスセグメントが描かれる方向に従って、曲がりが外側に膨らむか内側に膨らむかを制御することに注意してください(上記参照)。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 A 100,100 0 0,0 25,10
A 100,100 0 0,1 70,5
A 100,100 0 0,1 20,55 Z' " triangle_bulge.gif
![[IM Output]](../static/img/draw/triangle_bulge.gif)
直線の縁からなる「静的」に見える三角形が、今や風をはらむ帆のように少し見えます。本当に線を、真の線セグメントに戻すことなく完全にまっすぐにしたい場合は、弧の半径ゼロを使って曲がりをオフにできます。このように弧は、楕円や円を生成するのに良いだけでなく、まっすぐな線セグメントやわずかに曲がった線セグメントを描くのにも有用です。非常に汎用性の高い、一般的な点から点へのドローパスです。分離した曲がった線セグメントを生成するために楕円弧を使う代わりの単純な方法の1つは、代わりに二次ベジェセグメントを使うことです。主な違いは、弧を定義するのに円の半径ではなく実際の位置を使う点です。これにより弧を線セグメントの一端に偏らせることもできますが、対称な弧を生成しにくくなるという代償があります。もちろん両方を使って「混ぜ合わせる」こともできます。 円グラフの例 弧を使うことの締めくくりとして、円形のくさび形を生成するためにそれらを使う例を挙げましょう。もちろん、必要な終点のパス座標を決めるには、外部の三角法の数学を使う必要があるかもしれません(高校の数学はどのくらい得意でしたか?)。 |
magick -size 140x130 xc:white -stroke black \
-fill red -draw "path 'M 60,70 L 60,20 A 50,50 0 0,1 68.7,20.8 Z'" \
-fill green -draw "path 'M 60,70 L 68.7,20.8 A 50,50 0 0,1 77.1,23.0 Z'" \
-fill blue -draw "path 'M 68,65 L 85.1,18.0 A 50,50 0 0,1 118,65 Z'" \
-fill gold -draw "path 'M 60,70 L 110,70 A 50,50 0 1,1 60,20 Z'" \
-fill black -stroke none -pointsize 10 \
-draw "text 57,19 '10' text 70,20 '10' text 90,19 '70' text 113,78 '270'" \
piechart.jpg
![[IM Output]](../static/img/draw/piechart.jpg)
すべての弧が「線パス」の左側に描かれ、それに応じて(「sweep」フラグを使って)フラグ付けされていることに注意してください。しかし、弧が180度より大きい角度をカバーする場合は、「large」フラグを設定する必要があります。上記の例の最後の「gold」要素を参照してください。また、各セクションを完全に描くべきであることにも注意してください。これは、境界の線を2回描く必要があるかもしれないことを意味します。そうしないと、そのセクションを色で完全に塗りつぶせないか、あるいは fill 色が前に描いたセクションの輪郭を覆ってしまう可能性が高いです。複数の線が重複するのを避ける唯一の方法は、まず塗りつぶされた領域をすべて描いてから、繰り返して輪郭を描くことです。つまり、すべてを2回描いて、物事がきちんと合うようにする必要があります。そのため、おそらく輪郭が二重になるのが最も単純な解決策でしょう。 三次ベジェ 曲線は、2つの制御点と最終的な終点を定義する「C」関数を使って定義できます。直前の制御点の鏡像を使う三次ベジェ曲線を継続する(連続した曲線のため)には、「S」関数を使えます。これがその例です。この関数は複雑なので、制御点の位置と、直前の制御点の「仮定された鏡像」を示すキャンバスをあらかじめ用意しておきました。 |
# 三次ベジェ: C control_1_x,y control_2_x,y x,y
# 滑らかな継続 " : S control_2_x,y x,y
magick path_cubic_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 C 10,4 50,4 50,30 S 90,55 90,30' " \
path_cubic.gif
![[IM Output]](../static/img/draw/path_cubic.gif)
そのパスセグメントのパス上の制御点と最終点をつなぐ線(制御線)は、基本的にパス上のその点を通る曲線の方向を定義します。長い制御線はその点でより滑らかな曲線を生み、短い線はその点でより鋭い曲線を生みます。制御点が曲線の点と一致する(制御線の長さがゼロの)場合、その点で曲線は鋭い不連続を持ち、まるで直線セグメントだけが使われたかのようになります。より実践的な例として、以下のコード片は、IM Examples ロゴの波打つしぶき領域を作る IM Examples ロゴ生成スクリプト から抜き出したものです。この例の巧妙な部分は、私が使う三次ベジェのパス文字列を、ベジェ曲線を生成するために使われる制御線を示す別のパスに変換することです。これにより曲線の制御線の角度と長さが見えるようになり、結果を調整するのがずっと簡単になります。曲線と制御の両方を表示するために調整が必要な点の組は1つだけなので、ミスを最小限に抑えられます。 |
curve="M 12,27 C 7,37 18,50 18,60 S 0,80 10,94
S 40,74 50,78 S 60,99 76,95 S 72,70 75,65
S 95,55 95,42 S 69,37 66,32 S 67,2 53,7
S 43,17 35,22 S 17,17 12,27 Z"
c_ctrls=`echo $curve | \
sed '1s/\([0-9]\) *\([0-9]\)/\1 M \2/;
s/S/M/g; s/C/ /;' -`
magick -size 100x100 xc:white \
-draw "stroke None fill Green path '$curve'" \
-draw "stroke Red fill None path '$c_ctrls'" \
curvy_splash.gif
![[IM Output]](../static/img/draw/curvy_splash.gif)
画像をよく見ると、曲線の開始と終了に、互いに反対方向を向いた2本の制御線があるのがわかります。閉じた連続パスでは、開始と終了の両方の制御線は同じ角度(ただし鏡映の方向)で、もちろん同じ長さであるべきです。これは間違えやすいので、覚えておくことが重要です。曲線に沿った他のすべての点には、曲線が描かれる方向と_反対方向_を指す制御点/線が1つだけあります。その線セグメントが長いほど、その制御点での曲線の「鋭さ」は減り、長さがゼロだと「尖り」を生みます。「S」関数は、内部的に直前のセグメントのデータから次のセグメントの鏡像制御点/線を生成し、曲線の滑らかな継続を生み出します。このパス関数のさらなる例については、SVG: 三次ベジェ曲線コマンドを参照してください。ベジェ曲線を手動で生成する ことは、凝った GUI ツールを必要とせずに比較的簡単に行えます。
- まず、曲線を通らせたいすべての座標点を定義し、リストの最後に開始座標を繰り返します。
- 次に、このリストを拡張して、すべての x,y 座標点を組に倍にし、各組の前に「
S」(滑らかな三次)関数を追加します。各組の最初の数値は、曲線上の点を表す2番目の数値につながる制御点です。ただし最初の点の組はこれが逆で、最初の点が曲線の開始で、2番目が最初で唯一の反転した制御点を表します。 - 最初の座標の組の関数文字を「
S」から「M」に変更し、次にこの座標の組の間に「C」を追加します。最後に、2番目の座標の組から「S」を削除して、最初の三次(「C」)関数を完成させます。 - 最後に「
Z」を追加して曲線を閉じ、パスを完成させます。
これがどう見えるべきかは上記の例の並びを参照してください。 - この時点でパスを試し描きできます。すべての制御線の長さがゼロなので、パスは直線セグメントだけで構成されます。
- あとは、望む最終的な曲線を得るために、制御線セグメント(各「
S」の組の最初の座標)の位置をゆっくり慎重に調整するだけです。制御線を長くしすぎたり、間違った方向にしたりしないでください。さもないと非常におかしな曲線になります。 - 変更を見てミスを見つけやすくするには、上記の変換「
sed」コマンドを使って、パス制御点と曲線制御点の間の制御線を描きます。ただし長さゼロの制御線は見えませんが、線が鋭い尖りを生むので位置は明らかなはずです。 - 最後に、「
C」の後の最初の制御点/線が、同じ位置で、終端の制御/線とちょうど反対になっていることを確認します。
対話的な曲線生成 は、いくつかのベクターグラフィックエディタを使って行うこともできます。例えば、Luis Guerra が報告するところによると、「Inkscape」で生成したベジェ曲線は、「Edit -> XML Editor」機能を使い、制御点が欲しいパスや図形を選択することでアクセスできるようになります。
GUI ツールを使ってベジェ曲線(曲線上の点ごとに2つまたは1つの制御点を与える)を抽出する他の方法をご存じですか。あるいは、そうした曲線を生成する何か他の技法は? メールをください! ぜひ聞きたいです。他の人がそうしてきたように、その技法はあなたの功績として記載されます。
二次ベジェ は、2つの制御点が単一の制御点に統合された場合の、三次ベジェ関数の簡略化です。これも「Q」関数で曲線を開始し、次に「T」関数を使って、直前の制御点を鏡映して曲線を継続できます。 |
# 二次ベジェ: Q control_x,y x,y
# 滑らかな継続 " : T x,y
magick path_quad_canvas.gif -fill white -stroke black \
-draw "path 'M 10,30 Q 20,4 50,30 T 90,30' " \
path_quad.gif
![[IM Output]](../static/img/draw/path_quad.gif)
ただし、警告しておきますと、「T」継続関数は、等間隔の点をつなぐパスでしか実際にはうまく機能しません。その使用は推奨しません。二次曲線の利点は、楕円弧の代替としてのもので、弧に半径ではなく実際の位置を使う点です。また、弧を一方の端に偏らせることもでき、これは楕円弧を使うときには実用的でありません。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "path 'M 20,55 Q 30,32 25,10
Q 50,1 70,5
Q 50,45 20,55 Z' " triangle_bulge_2.gif
![[IM Output]](../static/img/draw/triangle_bulge_2.gif)
この場合、弧はそれほど一様でなく、帆というよりは逆さまのサメのひれのようなものが得られます。二次弧は放物線であるのに対し、楕円弧は基本的に円のセグメントを生成することを覚えておいてください。これが、どちらの種類の弧状の線セグメントを使うべきかを決める鍵になるかもしれません。このパス関数のさらなる例については、SVG: 二次ベジェ曲線コマンドを参照してください。
描画面のゆがめ
これらの能力に加えて、オブジェクトが描かれる描画面をさまざまな方法でゆがめて、驚くようなことができるようにできます。まず、「translate」「rotate」「scale」「skewX」「skewY」「affine」といった、一般的な描画面の変更を適用できます。例えば、線の「パス」が与えられたとき、描画面の原点すなわち 0,0 点を別の位置へ「平行移動」できます。 |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30
image over 3,3 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_translate.gif
![[IM Output]](../static/img/draw/transform_translate.gif)
描画領域の「0,0」すなわち原点が、画像の中心に来たことに注目してください。ただし Y 軸は画像の上部で負、下部で正のままです。「rotate」操作は描画面を回転させるので、その後にその面に描かれるものはすべて回転して描かれます。もちろん、平行移動された原点の周りで回転するので、両方の変換演算子を一緒に使うのが良い考えです。 |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 rotate -30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_rotate.gif
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1.5,1.5
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_scale.gif
![[IM Output]](../static/img/draw/transform_scale.gif)
「scale」の一般的な用途の1つは、正の Y 値が上向きになるように Y 軸を反転させることです。もちろん、物事を整然と保つために、原点も中心または左下隅へ移動すべきです。 |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 scale 1,-1
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_flip.gif
![[IM Output]](../static/img/draw/transform_flip.gif)
そして最後に、「skewX」と「skewY」は X 方向と Y 方向に画像をせん断します。例えば、ここでは「skewX」を使って画像の垂直な Y 軸に傾きを与えます。 |
magick -size 100x60 xc:skyblue \
-draw "translate 50,30 skewX 20
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " transform_skewY.gif
![[IM Output]](../static/img/draw/transform_skewY.gif)
これらの演算子には、一般的な用途のために MVG「[-draw](https://imagemagick.org/command-line-options/#draw)」文字列の外側にも同等のものがあります。ただし、これらのコマンドライン版は演算子であり、まだベクターオブジェクトが描かれていない描画面だけにではなく、すでにメモリ内に存在する画像に対して即座に適用されます。詳細は画像のゆがめを参照してください。
描画面のアフィンゆがめ
上記の5つのキャンバス変換はすべて、一般的なアフィン行列演算子に組み合わせられます。MVG プリミティブ「affine」を使うか、「[-draw](https://imagemagick.org/command-line-options/#draw)」を呼ぶ前に「[-affine](https://imagemagick.org/command-line-options/#affine)」でアフィン変換を設定することによってです。アフィン変換は「行列係数」の集合を使い、与えた座標が実際の描画座標へどう変更されるべきかを定義します。これらの「係数」が実際にどう機能するかの詳細については、アフィン行列変換を参照してください。例えば… オブジェクトが描かれる位置に対して、中心の原点を設定するだけなら… |
magick -size 100x60 xc:skyblue \
-draw "affine 1,0,0,1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_null.gif
magick -size 100x60 xc:skyblue \
-draw "affine 1,0,0,-1,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_flip.gif
magick -size 100x60 xc:skyblue \
-draw "affine .866,-.5,.5,.866,50,30
image over 4,4 0,0 'terminal.gif'
fill white stroke black
path 'M 0,20 -45,20 20,-25 -25,-25'
fill none stroke red
path 'M 0,10 0,-10 M 10,0 -10,0' " affine_rot.gif
![[IM Output]](../static/img/draw/affine_rot.gif)
より複雑なアフィン変換には、その目的のために作られたアフィン補助スクリプトを利用できます。これらのスクリプトは、回転角度や中心点といったものを、「[-draw](https://imagemagick.org/command-line-options/#draw) affine」や「[-affine](https://imagemagick.org/command-line-options/#affine)」設定で直接使えるアフィン座標へ変換します。
Push/Pop コンテキスト
一部の MVG プリミティブは、適切に使うために実際これらの変換の利用に依存しています。例えば、楕円プリミティブは、軸が直交して揃った状態でしか直接指定できません。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "ellipse 50,30 30,15 0,360" ellipse_orthogonal.gif
![[IM Output]](../static/img/draw/ellipse_orthogonal.gif)
しかし、描画変換を使えば、楕円に「回転角度」を簡単に追加できます。 |
magick -size 100x60 xc:skyblue -fill white -stroke black \
-draw "push graphic-context
translate 50,30 rotate 30
fill white stroke black
ellipse 0,0 30,15 0,360
pop graphic-context" ellipse_rotated.gif
![[IM Output]](../static/img/draw/ellipse_rotated.gif)
楕円の「中心」(回転の点)が、回転が適用される前にまず平行移動されたことに注意してください。その後、「ellipse」がその平行移動された位置「0,0」に描かれました。上記はまた、2つの新しい MVG 描画プリミティブも示しています。「push graphic-context」と「pop graphic-context」です。これらは上記の例では厳密には必要ありませんが、大きな描画変換を行うときには推奨されます。「push」と「pop」プリミティブが行うのは、現在の描画状態すなわち「グラフィックコンテキスト」を保存し、その後再びそれを復元することです。2つのプリミティブの間で変更された描画設定はすべて忘れ去られます。これには「translate」や「rotate」のような面のゆがめ、「fill」や「stroke」の色設定、その他描画「状態」を変更したものが含まれます。これらのプリミティブにより、多くの変換をともなう非常に複雑なオブジェクトを描き、その後より「通常」の状況へ物事を戻して後の描画操作を行うのが簡単になります。これのより実践的な実演は、下記の矢印の描画で見られます。
Push/Pop 特殊オブジェクト
作成中
MVG が SVG 形式を扱うために特化して使う、さらなる設定。
font-family font-stretch font-style font-weight
encoding 'UTF-8'
push defs
push gradient 'def_name' linear X1,Y1 X2,Y2
stop-color 'color' where
stop-color 'color' where
# where は与えた2つのピクセルの間の点 (0 = X1,Y1 1= X2,Y2)
gradient-units 'objectBoundingBox|userSpaceOnUse'
affine ....
pop gradient
push gradient 'def_name' radial CX,CY FX,FY R
# ここで CX,CY は半径 R の放射状グラデーションの中心
# FX,FY は焦点で、通常は CX,CY と同じ
# グラデーションを特定の方向にゆがめようとする場合を除く
stop-color 'color' where
...
pop gradient
pop defs
push graphic-context
fill 'url(#def_name)'
... ここに物を描く ...
pop graphic-context
例については _Florent Monnier_ の開発サイトを参照…
<http://www.linux-nantes.fr.eu.org/~fmonnier/OCaml/MVG/>
MVG ファイルの読み込み 上記の例で見たように、MVG「-draw」引数は非常に長くなることがあります。実際、SVG から MVG への変換は、極端に長い MVG 描画引数を生み出すことがあります(下記参照)。しかし、IM の一般的なコマンドラインインターフェースでは、「@filename」引数を代わりに使うことで、任意の文字列引数をファイルから読み込めます。これは便利で、非常に長く複雑な MVG 描画コマンドを別ファイルから読み込めることを意味します。例えば、MVG 操作を「draw_circles.mvg」というファイルに入れておけば、次のように描けます… |
magick -size 100x60 xc:skyblue -draw @mvg_circles.mvg mvg_draw.gif
| ![[IM Output]](../static/img/draw/mvg_draw.gif)
それだけでなく、ImageMagick は「MVG:」画像ファイル形式を直接読み込むことも理解しているので、そうしたコマンドをより直接的に描けます。ただし、MVG ファイルがキャンバスを定義していない限り、描画先の初期キャンバス(「-size」と「-background」)を指定する必要があるかもしれません。 |
magick -size 100x60 -background limegreen mvg_circles.mvg mvg_file.gif
| ![[IM Output]](../static/img/draw/mvg_file.gif)
適切な背景色の塗りつぶし描画とともに「viewbox」を MVG ファイルに追加することで、初期キャンバス設定を MVG 画像内に移動できます。これで MVG 画像ファイルが完全な画像定義として完成します。 |
magick mvg_circles2.mvg mvg_image.gif
| ![[IM Output]](../static/img/draw/mvg_image.gif)
| _現在、MVG 引数文字列の内側から外部の MVG ファイルを読み込む方法は1つだけで、それは「image」描画プリミティブを使うことです。残念ながらこれは、MVG のインクルードを、描画面にオーバーレイする_前_にラスター画像へ変換します。
言い換えれば、現在 MVG の「インクルード」関数はありません。_ :-(
---|---
作成中
「[+render](https://imagemagick.org/command-line-options/#render)」を使って、IM の低レベルな draw 操作を記録できます。
その後「[-render](https://imagemagick.org/command-line-options/#render)」設定/演算子を与えると、IM はそれらの保存された
操作を即座に描きます。
奇妙なことに、単に「MVG」ファイルに出力するだけでもこれを行うようです…
magick ... -draw '....' draw_commands.mvg
注: MVG 形式ファイルに出力しながら曲線を描くと、ファイルは曲線を
元の曲線ではなく一連の短い線セグメントとして列挙します。
もちろん、もっと汎用的な SVG 形式を使うところまで進めます。
下記の「[SVG 形式の扱い](#svg)」を参照してください。
MVG アルファ合成
作成中
オブジェクトの描画に、アルファ合成(基本的に「over」アルファ合成である
「画家の」アルゴリズムを除く)が使われているのを見たことがありません。
しかし、できないというわけではありません。
矩形、楕円、円、その他何でも、異なるアルファ合成(「DstOver」のような
Under 風の合成など)で合成したい場合は、元の画像と同じサイズの空の透明
キャンバスに図形を描き、それを画像に合成してください。
ただし、SVG ではアルファ合成を使ってテキストやその他の項目を画像に描けるので、
将来の追加機能になるだろうと想像します。
お楽しみに!
シンボルの描画
画像上の一連の点に、十字や円などの参照シンボルを描きたいことがあります。残念ながら現在のところ、IM にはそうしたシンボルを簡単に描くコマンドはありませんが、少し余分な作業をすればそうしたシンボルを描けます。
シンボルの描画技法
与えられた位置のリストに複数のシンボルを描くコツは、シェルスクリプトや使っている API を使って MVG 描画コマンドを生成し、与えられた点の集合を適切な描画コマンドの集合へ変換することです。例えば、ここでは点の並びを、それらの各点に「プラス」を描くものに変換します… |
# X 座標と Y 座標の文字列を定義
# 値の間はカンマ、座標の間はスペース。
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# 各点を十字の draw コマンドに変換(「awk」を使う)
# 「tr」はスペースを「改行」に変換(1行に1点)。
crosses=`echo $points | tr -s ' ' '\012' |\
awk -F, '{ print "line " $1-3 "," $2 " " $1+3 "," $2 ;
print "line " $1 "," $2-3 " " $1 "," $2+3 ; }' -`
# 点の間に赤い線を、点の上に青い十字を描く。
magick -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $crosses " \
points_plus.gif
![[IM Output]](../static/img/draw/points_plus.gif)
上記は「tr」を使って各点(2つの数値)を1行に1点ずつ分け、次に「awk」を使って、与えられた点の上に「プラス」を描くために必要なすべての数学計算を行います。これには好きなものを何でも使えます。私は単に、入力された点のリストに一種のテキストマクロ展開を適用しているだけだからです。ほぼどんなプログラミング言語でもこれができます。上記のシェルスクリプトの場合、「awk」が最も単純で最速の手段だとわかったので使いました。実は、「magick」のフォーマットオプションを使って、ImageMagick 自体にその「マクロ」展開をさせることもできます… 例えば、ここではこの「点シンボル」のために、円の円周上の点を計算するのにそれを使います。 |
# X 座標と Y 座標の文字列を定義
# 値の間はカンマ、座標の間はスペース。
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# 各点の周りに描く円の半径(またはシンボルサイズ)。
radius=3.5
# 各点を十字の draw コマンドに変換
# この場合、点はシェルによってスペース区切りになっている
circles=$(for point in $points; do
x=$(echo "$point" | cut -d, -f1)
y=$(echo "$point" | cut -d, -f2)
# IM に浮動小数点の数学をさせる。例: y2=$y+$radius
y2=$(magick xc: -format '%[fx:'"$y"'+'"$radius"']' info:)
echo "circle $x,$y $x,$y2"
done)
# 点の間に赤い線を、点の上に青い円を描く。
magick -size 100x100 xc:white \
-draw "fill none stroke red polyline $points " \
-draw "fill none stroke blue $circles " \
points_circle.gif
![[IM Output]](../static/img/draw/points_circle.gif)
さて、生成する draw 文字列はかなり長くなることがあり、最終的なコマンドの長さに問題を引き起こし始める可能性があります。そこで、点を長い文字列に変換してそれをコマンドラインで IM に渡す代わりに、draw コマンドをファイルとして IM にパイプで渡せます。今回はまた、描画プリミティブの描画方法ではなくSVG パスの描画方法を使います。また、生成するシンボルは各点の周りの三角形です。 |
# X 座標と Y 座標の文字列を定義
# 値の間はカンマ、座標の間はスペース。
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# 各点を三角形を描く draw コマンドに変換
for point in $points; do
echo "path 'M $point m 0,-5 -4,+8 +8,0 -4,-8'"
done |\
magick -size 100x100 xc:white \
-fill none -stroke red -draw "path 'M $points' " \
-fill none -stroke blue -draw '@-' \
points_tri.gif
![[IM Output]](../static/img/draw/points_tri.gif)
SVG パスは、相対的なピクセル移動を可能にすることで、これを実際に簡単にします。シンボルを描くための「移動」と「線」の並びを与える前に、最初の絶対移動「M」が1つだけ必要になるようにシンボルを設計できます。このため、浮動小数点の計算はまったく必要ありません。IM draw が必要な位置決めの数学を行ってくれるからです。 | 相対移動のSVG パス項目「m」は IM v6.4.3-5 より前では壊れています。あなたの IM がこれより古い場合、上記(および次)の例は何も描かないかもしれません。古いバージョンでは、上記の相対移動「m」を適切な相対線「l」の並びに置き換えることで修正できます。
---|---
さらにもう一歩進めて、描画キャンバス指定を含む完全な形の MVG ファイルを、描画コマンドのパイプラインとして IM に直接送り込めます。今回は、上記の最初の「プラス」の例(多くの計算が必要だった)に似た「十字」をやってみましょう。 |
# X 座標と Y 座標の文字列を定義
# 値の間はカンマ、座標の間はスペース。
points="6.6,7.7 25.0,75.0 42.2,85.4 75.8,94.7 51.5,39.3 92.5,66.6"
# IM がすべての要素を描くための MVG ファイルを生成
( echo "viewbox 0 0 100 100 fill white rectangle 0,0 100 100"
echo "fill none stroke red path 'M $points'"
echo "fill none stroke blue path '"
for point in $points; do
echo " M $point m -2,-2 +4,+4 m -4,0 +4,-4"
done
echo "'"
) | magick mvg:- points_cross.gif
![[IM Output]](../static/img/draw/points_cross.gif)
これは特別なシェルプログラミング技法を使っており、シェルの括弧内で「echo」されたものはすべて、最終的な「magick」コマンドに MVG ファイルとして送り込まれます。最初の「echo」が画像の描画キャンバスを定義して塗りつぶし、「while」ループが与えられた各「点」を、与えられた半径の円に変換します。この方法の利点は、他の2つの方法で生じうる文字列の制限を受けない点です。生成できる他のシンボルには、箱、ひし形、エラーバーなどがあります。また、計算不要の相対「path」円描画を含む他の円の方法については、下記の円の描画も参照してください。
シンボル描画の代替手段
シンボルを直接描く以外にも、画像にシンボルを追加する方法があります。
シンボルフォント
シンボルフォントからシンボルを抽出し、小さなビットマップとして保存できます。小さな、しかしカラフルな、あらかじめ定義された画像をこれに使うこともできます。ただし、これは特定のピクセルに対してフォントを正確に位置決めするのが難しいかもしれません。つまり、あまり精密な技法ではありません。しかし、任意の画像を任意のピクセル位置に合成できます。例えば、これらのシンボルは、これらの例ページで特定の用途のためにいくつかのフォントから抽出されました。
画像をより大きな背景に合成する例は、画像のレイヤー化のセクションにあります。ただし、レイヤー化画像のプログラム的な位置決めで示すような、ループを使う方法の方が有用かもしれません。将来: 座標を使って画像をレイヤー化する例
モルフォロジー
もう1つの代替手段は、モルフォロジーを使い、「[Disk](morphology.html#disk)」「[Ring](morphology.html#ring)」「[Plus](morphology.html#plus)」などの特別な「形状」カーネルや、自前のユーザー定義カーネルを使って、単一のピクセルを「膨張」させることです。例えば… |
magick -size 80x80 xc:black -fill white \
-draw 'point 20,15 point 55,30 point 40,60' points_pixels.gif
magick points_pixels.gif -morphology Dilate Ring points_rings.gif
magick points_pixels.gif -morphology Dilate Plus:4 points_pluses.gif
magick points_pixels.gif -morphology Dilate Cross:3 points_crosses.gif
結果は次に、Alpha Shape 演算子を使って直接、色付きのオーバーレイに変換できます。これの大きな利点は、各シンボルの個々の位置を実際に知る必要がない点です。あるいはシンボルがいくつあるかも。しかし、それが欠点にもなりえます。大きな欠点は、位置が整数の位置にしかないことです。浮動小数点の「サブピクセル」位置決めを使って「描く」ことはできません。
畳み込み
ほぼ同一の技法は、特別に設計されたカーネルで「Convolve」を使うことです。これにより、上記のような単純なオン/オフの結果ではなく、さまざまな灰色の濃淡を設定できます。画像の各チャンネル(赤、緑、青、アルファ)に異なるユーザー定義カーネルを使うことで、各ピクセル座標から多色のシンボルを作ることさえ可能です。
これには、色付き画像(右参照)を各チャンネルの個別の浮動小数点畳み込みカーネルに変換するために私が書いた特別なスクリプト「[image2kernel](../static/img/scripts/image2kernel)」を使います。
image2kernel -q marker.png marker.dat
これは「[marker_R.dat](../static/img/draw/marker_R.dat)」のような4つのファイルを生成します。非常に小さな入力画像の各チャンネルに1つで、それぞれが画像のユーザー定義表現です(原点は画像の中心)。さて、それらのカーネルデータファイルを使って、それらの単一の点を、透明な背景の上にカラフルなマーカー画像へ畳み込みできます。
magick points_pixels.gif -alpha off \
\( -clone 0 -morphology Convolve @marker_R.dat \) \
\( -clone 0 -morphology Convolve @marker_G.dat \) \
\( -clone 0 -morphology Convolve @marker_B.dat \) \
\( -clone 0 -morphology Convolve @marker_A.dat \) \
-delete 0 -channel RGBA -combine point_markers.png
| IM v6.7.6-9 より前では、Combine 演算子は画像の透明度チャンネルがアルファ値ではなく「不透明度」値として与えられることを必要とするため、作られた結果のアルファチャンネルを反転する必要があります。例: |
... "`cat marker_A.dat`" -negate \) \
小さな画像のみを使い、ピクセル点はシンボルが重ならないよう十分に離して配置すべきです。これは、Convolveが重なる領域を足し合わせて、期待より明るくしてしまうためです。上記は、より使いやすくするために UNIX シェルスクリプト「[convolve_image](../static/img/scripts/convolve_image)」に変換してあります。
convolve_image points_pixels.gif marker.png point_markers.png
この技法は、IM フォーラムの議論 A Fun Experience with IM から生まれました。そのユーザーは、フットボール場の背景画像に小さな人々を配置し、その位置が画像内で人の名前を綴るようにしたいと考えていました。
レイヤー
ソース画像から抽出したピクセルのリストを使って位置決めする画像のレイヤーのような別の技法の方が、より良いアプローチかもしれません。前景画像の前に、より遠いシンボル画像を先に重ねることができ、どのシンボルがどの点を置き換えるかをプログラム的に選んだり、ランダム化したりできます。この例については地図上のピンを参照してください。
円の描画
draw オプションは、とても基本的なこと…円を描くことを行う方法をいくつも提供します。例えば、円周上の任意の点を通る円を描けます。そのため、中心点と、その最初の点から半径分(例えば25ピクセル)離れた2番目の点を計算する必要があります。 |
magick -size 100x60 xc: -stroke Firebrick -fill tomato -strokewidth 2 \
-draw 'circle 50,30 50,55' circle_circle.gif
![[IM Output]](../static/img/draw/circle_circle.gif)
Fred Weinhaus は、平行移動を使うことで、円の縁の座標を計算する必要がなくなり、半径を直接与えるだけでよくなる、と指摘しました。 |
magick -size 100x60 xc: -stroke SeaGreen -fill PaleGreen -strokewidth 2 \
-draw 'translate 50,30 circle 0,0 25,0' circle_circle_trans.gif
![[IM Output]](../static/img/draw/circle_circle_trans.gif)
ただし、複数の円を描く場合、上記は各円に別個の「[-draw](https://imagemagick.org/command-line-options/#draw)」操作が必要になるか、コンテキストのプッシュを使う必要があります。楕円を使えば、半径を軸の長さとして直接指定できます。 |
magick -size 100x60 xc: -stroke Sienna -fill Wheat -strokewidth 2 \
-draw 'ellipse 50,30 25,25 0,360' circle_ellipse.gif
![[IM Output]](../static/img/draw/circle_ellipse.gif)
「stroke-linecap round」を使って、非常に非常に短い線を描くことでも円を生成できます。ストローク幅が円の直径を設定します。線には(どんなに小さくても)何らかの長さがなければならないことに注意してください。さもないと draw は何も描きません。 |
magick -size 100x60 xc: -stroke Blue -strokewidth 50 \
-draw 'stroke-linecap round line 50,30 50,30.0001' \
circle_line.gif
![[IM Output]](../static/img/draw/circle_line.gif)
この技法は、残念ながら生成した円を輪郭で縁取れませんが、大きな領域を覆うには、大きなストローク幅が有用です。下記のいくつかの単純な例を参照してください。この方法はSVG パスの描画方法を利用しており、余分な座標を計算する必要なく円を描けます。 |
magick -size 100x60 xc: -stroke Blue -fill DodgerBlue -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 a 1,1 0 0,0 0,-50 a 1,1 0 1,0 0,50'" \
circle_path.gif
![[IM Output]](../static/img/draw/circle_path.gif)
中心を定義するために最初の絶対移動「M」だけが必要で、それに続く残りのパス要素の「25」と「50」が、この中心に対する円の半径と直径を定義します。 | 相対移動のSVG パス項目「m」は IM v6.4.3-5 より前では壊れています。あなたの IM がこれより古い場合、円は単一のピクセルとしてしか現れないかもしれません。古いバージョンでは、上記の「m」を「l」に置き換えることで修正できます。
---|---
Fred Weinhaus は次のベジェ円の方法を追加しました。これは(厳密ではありませんが)本物の円に非常に近く、浮動小数点の計算が必要です。 |
r=25; cx=50; cy=30;
x1=25; x2=75; # = cx ± radius
y1=-3.25; y2=63.25; # = cy ± radius*1.275
magick -size 100x60 xc: -stroke Purple -fill Violet -strokewidth 2 \
-draw "bezier $x1,$cy $x1,$y1 $x2,$y1 $x2,$cy" \
-draw "bezier $x1,$cy $x1,$y2 $x2,$y2 $x2,$cy" \
circle_bezier.gif
![[IM Output]](../static/img/draw/circle_bezier.gif)
正確な円を描くことが重要でない場合は、円の X と Y の境界だけを計算に使う、この4つのベジェセグメントの SVG パスを使えます。 |
r=25; cx=50; cy=30;
x1=25; x2=75; # X 境界 = cx ± radius
y1=5; y2=55; # Y 境界 = cy ± radius
magick -size 100x60 xc: -stroke Tomato -fill Gold -strokewidth 2 \
-draw "path 'M $cx,$y1 Q $x1,$y1 $x1,$cy T $cx,$y2 $x2,$cy $cx,$y1 z'" \
circle_bezier_path.gif
![[IM Output]](../static/img/draw/circle_bezier_path.gif)
中心の開始点から完全に相対的に描かれるものが好みなら、この技法を使えます。半径の値だけが使われるので、API の文字列関数だけを使って生成するのが簡単です。 |
magick -size 100x60 xc: -stroke Orange -fill LemonChiffon -strokewidth 2 \
-draw "path 'M 50,30 m 0,25 q 25,0 25,-25 t -25,-25 -25,25 25,25 z'"\
circle_bezier_path_rel.gif
矢印の描画 -- シンボルの位置決め、回転、拡大縮小
上記の技法を使えば、矢じりのような特別なシンボルを作り、その先端が線の末端に来るように位置決めして、その上に描けます。線の後に矢印を描けば(典型的な状況)、矢印は線の上に描かれます。ただし、定義できる矢印には3つの種類があり、各種類は用途に応じて異なる方法で定義されます。
- 測定 用。何らかの工学図面で測定の限界を示すために、単に線の両端を矢じりで印付けたいだけの場合。非常に単純です。
- ベクター。何らかの値の方向と強度を示します。
例えば、気象の風のチャートで。尾が必要で、0,0 点は尾の末端です。しばしばそうしたベクターの大きな格子が作られます。 - 指示子。何らかの詳細を指し示します。
このためには、0,0 点はおそらく先端、または矢印自体の少し前の何らかの距離であるべきです。
測定矢印
線の末端に矢じりを追加するだけなら比較的簡単に行えます。基本的に「矢じり」シンボルを作り、それを正しい位置に描きます。例えば… |
arrow_head="l -15,-5 +5,+5 -5,+5 +15,-5 z"
magick -size 100x60 xc: -draw 'line 10,30 80,30' \
-draw "stroke blue fill skyblue
path 'M 80,30 $arrow_head' " \
arrow_horizontal.gif
![[IM Output]](../static/img/draw/arrow_horizontal.gif)
シンボルを、その開始点が線のまさに末端になるように描いたことに注意してください。こうすれば、前に描いた線の上に後ろ向きに描くことができ、とてもきれいで整ったシンボルになります。ただし矢印には向きが関連付けられています。多くの異なる角度の矢印定義を膨大に作ることもでき、多くのプログラムがそうしています。しかし矢印はベクターなので、矢印をベクターとして回転させてはどうでしょうか。IM draw コマンドには描画の回転(キャンバスのゆがめ)が組み込まれているので、それを使いましょう。これにはまた、位置を矢じりの「path」定義から外に出せるという利点もあり、パス全体を「定数」として指定できます… |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45
$arrow_head
" \
arrow_rotate.gif
![[IM Output]](../static/img/draw/arrow_rotate.gif)
矢印のサイズを変えたい場合は、rotate の後に「scale」draw オプションを追加します。 |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45 scale 2,2
$arrow_head
" \
arrow_scale.gif
![[IM Output]](../static/img/draw/arrow_scale.gif)
指定した位置に矢印の「先端」を残したまま拡大した様子に注目してください。これは矢印を扱う上で非常に重要な側面で、矢印を追加する線の末端の点と角度だけが問題になるからです。「変換」の順序は重要で、実際に実行される順序とは実は逆です。つまり、scale がまず座標に適用され、次に rotate、そして translate です。座標変換がこの順序で行われないと、矢印の最終的な配置も拡大縮小してしまい、期待する場所に来なくなります。また、scale には2つの数値があり、元の矢じりシンボルは水平(角度ゼロ)に設計されているので、矢印の幅を高さに対して別々に拡大縮小できます。ストローク幅も矢じりのサイズに合わせて拡大縮小され、物事が一貫している様子にも注目してください。 |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: -draw 'line 25,55 70,10' \
-draw "stroke blue fill skyblue
translate 70,10 rotate -45 scale 2,1
$arrow_head
" \
arrow_scale_aspect.gif
![[IM Output]](../static/img/draw/arrow_scale_aspect.gif)
さて、個々の矢印を描くためにキャンバスをゆがめており、おそらく他の多くの描画操作もあるので、それらすべてを1つの「[-draw](https://imagemagick.org/command-line-options/#draw)」操作で行いたいかもしれません。例えば線を描き、その後に両端へ矢印を追加するには、異なる色、位置、回転の組、場合によっては異なる拡大率まで必要です。つまり、各個々の矢じりの描画にキャンバスのゆがめの範囲を限定する必要があります。範囲を限定しないと、後の他の描画操作に影響を与え始め、何を生成しているか確信が持てなくなります。ゆがめ(および他のすべての描画属性)の範囲を限定するには、関係する部分を「graphic-context」で包みます… |
arrow_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
magick -size 100x60 xc: \
-draw "stroke black fill none
path 'M 10,40 A 50,50 0 0,1 90,40'
push graphic-context
stroke blue fill skyblue
translate 10,40 rotate 135
$arrow_head
pop graphic-context
push graphic-context
stroke firebrick fill tomato
translate 90,40 rotate 45
$arrow_head
pop graphic-context
" \
arrow_context.gif
![[IM Output]](../static/img/draw/arrow_context.gif)
「push」は本質的に現在のすべての描画属性を将来の使用のために保存し、「pop」はそれらの属性を復元して、その時点の設定(色、ゆがめ、位置など)を以前に保存した設定で置き換えます。これは、「pop」した後は「キャンバスのゆがめ」が取り消され、draw が変更前の状態に戻ることを意味します。上記の技法は矢印を生成する1つの方法にすぎず、技術図面のように距離を測る一環として矢印を描くときには良い方法です。
ベクター矢印
ベクターは、述べた通り、何らかの値の方向と強度の両方を示します。つまり、矢印の長さは可変で、矢じりはベクターの開始点からほぼ任意の離れた位置に来ることになります。さて、ベクターの長さと角度から矢じりが置かれるべき位置を計算するために重い数学をすることもできますが、ImageMagick にその計算をさせる、ずっと良い方法があります。解決策は、ゆがめたキャンバス空間で、ベクターの長さを正しい長さの水平線として描くことです。その線が描かれたら、キャンバスがまだ「ゆがめられた」ままで、描画空間を線の末端へ再び平行移動するだけです。これで正しく位置決めされ、正しい回転で、ベクターの「矢じり」を通常通り描けます。例えば、ここでは -35 度の角度で70ピクセルの長さのベクターを生成します。 |
vector_head="path 'M 0,0 l -15,-5 +5,+5 -5,+5 +15,-5 z'"
indicator="path 'M 10,0 l +15,+5 -5,-5 +5,-5 -15,+5 m +10,0 +20,0 '"
magick -size 100x100 xc: \
-draw "stroke black fill none circle 20,50 23,50
push graphic-context
stroke blue fill skyblue
translate 20,50 rotate -35
line 0,0 70,0
translate 70,0
$vector_head
pop graphic-context
push graphic-context
stroke firebrick fill tomato
translate 20,50 rotate 40
$indicator
translate 40,0 rotate -40
stroke none fill firebrick
text 3,6 'Center'
pop graphic-context
" \
arrow_with_tails.gif
指示子矢印
上記では、前のベクター矢印の開始点を指す指示子矢印も実演しました。ただし、前にやったように矢印を描く代わりに、原点(または開始点)から10ピクセル離れた位置から始まる、反転した矢印シンボルとして作りました。つまり、シンボルは指し示したい位置にあるので、実際にはその位置の真上に矢印が来るのではなく、そこから少し離れた位置に来てほしいのです。さて、指示子はベクターよりも扱いが単純で、通常は可変長を必要としませんが、指し示しているものを指定するために、通常は指示子の遠い端にテキストを追加したいものです。前と同様に、その位置を計算するのは難しいことがあるので、なぜわざわざするのでしょう。テキストの位置決めの解決策もベクターと同じです。指示子矢印を描くのに使った元のゆがめた空間を保ち、原点をその矢印の尾の端(ゆがめた空間で水平に40ピクセル)へ平行移動します。物事を再配置したので、その新しい位置の周りでゆがめを「逆回転」でき、テキストを通常通り(わずかにオフセットして)描けます。残念ながら、上記ではデフォルトのテキスト位置揃え「left」が機能しますが、現在のところ MVG では、gravity とは別個の設定としてテキストの位置揃えを指定できません。これが問題なら、IM バグフォーラムに要望を出してください。そうすれば、(gravity による位置決めとは別個の)テキストの位置揃えが、特にそれが実際 SVG 仕様の一部であることもあり、実現する見込みがあります。
オブジェクトの描画
色の幅広いストローク
さまざまな図形を作るのに、塗りつぶし領域をパスや輪郭で完全に囲む必要はありません。非常に大きく幅広いストロークを使えば、キャンバス上に大きな色の領域や色見本を生成できます。 例えば、幅広いストロークの楕円弧で、ポスター作成に実際に使われているのを見たことがある、素敵な色の領域を生成できます。 |
magick -size 100x100 xc: -fill none -stroke powderblue \
-draw 'stroke-width 70 ellipse -30,0 90,90 10,50' \
-rotate 180 arc_background.gif
magick -size 100x100 xc: \
-draw 'fill none stroke-linecap round
stroke-width 40 stroke tomato ellipse 50,0 70,70 65,115
stroke-width 2 stroke black ellipse 50,0 70,70 60,120
stroke-width 40 stroke palegreen line 50,40 50,40.01' clown.gif
円柱
IM フォーラムの議論で、ImageMagick の draw コマンドを使った円柱、特に陰影のある円柱の描画について熱い議論がありました。円柱描画のコツは、両端が楕円を形成するように「roundrectangle」プリミティブを描くことです。つまり、円柱の幅が例えば50ピクセルなら、矩形の角をそれぞれ25ピクセルと12ピクセルで丸めます。つまり、矩形幅の半分、そしてさらにその半分です。こうして円柱は、互いの上に描かれた2つの角丸矩形になります。2番目のより明るい色で塗りつぶされた「端の楕円」は、両方の角の寸法のちょうど2倍になるようサイズ調整されます。例えば… |
magick -size 60x100 xc:white -stroke snow4 \
-fill chartreuse3 -draw 'roundrectangle 5,5 55,95 25,12' \
-fill chartreuse2 -draw 'roundrectangle 5,5 55,29 25,12' \
cylinder.gif
![[IM Output]](../static/img/draw/cylinder.gif)
最初の fill 色をグラデーションの陰影(メモリ内タイリング技法を使う)で置き換えると、円柱をもう少し3D らしく見せられます… |
magick -size 60x100 xc:white -stroke snow4 \
\( -size 1x60 gradient:chartreuse1-chartreuse4 -rotate -90 \
-write mpr:shading +delete \) \
-tile mpr:shading -draw 'roundrectangle 5,5 55,95 25,12' +tile \
-fill chartreuse2 -draw 'roundrectangle 5,5 55,29 25,12' \
cylinder_shade.gif
![[IM Output]](../static/img/draw/cylinder_shade.gif)
(IM フォーラムで議論されたように)円柱の描画を少しずつ洗練させることで、非常に複雑で視覚的に魅力的な円柱の生成へとずいぶん遠くまで進めます。これには、囲む半透明のガラス円柱、影効果、ラベル付けの追加が含まれました。その議論の最終的な成果が、円柱のパーセンテージバーを生成するスクリプト「[cylinder_bar](../static/img/scripts/cylinder_bar)」です…
cylinder_bar 95 cylinder_95.png
このスクリプトは任意のサイズの画像を生成でき、そのサイズおよびスクリプトの先頭で定義された他の設定に基づいて、すべてのパラメータを適切に調整します。また、囲む半透明のガラス円柱と内側の色付き円柱の間に隙間を作るための「ガラスの厚さ」という概念も含んでいます。特に緑の円柱の端がガラス円柱の端と重なるときの、円柱の非常に微妙な陰影に注目してください! ちょっとした事前の考慮で、これほどのことができるのは驚きです。
テキスト文字列内の特殊文字の描画
クォートかバックスラッシュか?
人々が -draw で抱える最大の問題の1つは、UNIX シェルや DOS コマンドライン、あるいは C、Perl、PHP、R、Visual Basic のような他の言語でも特別な意味を持つ文字を描くことです。この点で最大の元凶は、2種類のクォート文字、それにドル「$」のような変数置換文字や、シェルおよび ImageMagick のエスケープ文字であるバックスラッシュ「\」です。基本的に、「[-draw](https://imagemagick.org/command-line-options/#draw)」への MVG 引数はクォートする必要があり、_さらに_その中の「text」文字列引数にも余分なクォートが必要になることがあります。これを解決するため、ユーザーは通常2つの異なるクォート文字を使います。1つはシェル用、もう1つは MVG テキスト文字列用です。
`**-draw '... text 0,0 "string" ...'**`
これは Windows ユーザーにとって唯一の現実的な選択肢であることに注意してください。Windows には独自のクォートの問題と方法があります。あるいは、クォートを入れ替えて、次のように使います…
**-draw "... text 0,0 'string' ..."**
これにより、(エスケープせずに「$」を使った)シェル変数の置換を含められます。正しい形を選べば、ほとんどの問題は解決しますが、それでも一部の文字には困難が残り、各解決策は使うクォートの組に正確に依存します。クォートの組が特殊文字をどうエスケープすべきかも定義するからです。これがクォートと特殊文字の扱いの4つのケースです…
- シェル引数にシングルクォートを使い、
MVG テキスト文字列をダブルクォートで囲む。draw のテキスト文字列を扱う最も単純な技法は、囲むシェル引数にシングルクォートを使うことです。ただしこれは、描く文字列にアポストロフィを含めるには、シェルの「シングルクォートモード」を抜けて、そのアポストロフィをシェルのシングルクォートの外で与える必要がある、ということを意味します。例えば、私が話した4つの特殊文字を扱う方法は次の通りです。 |magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \ -draw 'text 0,0 " '\'' \" $ \\ " ' \ -trim +repage text_special_sd.gif
![[IM Output]](../static/img/draw/text_special_sd.gif)
ドル記号はエスケープを必要としないので、シェル変数の内容を置換するのにも使えないことに注意してください。バックスラッシュが、IM の draw 文字列が扱う_唯一の_特殊文字であることを覚えておくことが重要です。また、その存在理由は純粋に、上で私たちがダブルクォートに対して使ったような「IM draw 文字列のクォート」をエスケープできるようにするためです。これ以外の、他のすべての奇妙さは、IM ではなく UNIX コマンドラインシェルによって引き起こされます。PC-DOS には独自の奇妙さがあり、その環境から IM を使うときの特殊文字のエスケープについて、ご意見をいただければありがたいです。
* シェル引数にダブルクォートを使い、
MVG テキスト文字列をシングルクォートで囲む。描く文字列に「シェル変数」を挿入したい場合は、外側のシェル引数にダブルクォートを使う必要があります。これによって事態はずっと複雑になります。シェルの保護を失い、ドル「$」記号だけでなく、バックスラッシュ「\」もエスケープしなければならなくなるからです。一方で、シェルはシングルクォート文字を引数の終わりの区切り文字として使う必要がなくなるので、その面は単純化されます。私たちの短い特殊文字リストについて結果をまとめましょう。 |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 ' \\' \" \$ \\\\ ' " \
-trim +repage text_special_ds.gif
![[IM Output]](../static/img/draw/text_special_ds.gif)
バックスラッシュ自体を描きたい場合、MVG テキスト文字列ではバックスラッシュを二重にする必要があります(前の例のように)が、シェル自体もそれらのバックスラッシュをそれぞれ二重にする必要があり、そうした文字を1つ生み出すだけで合計4つのバックスラッシュになることに注意してください。この二重化はすぐに圧倒的で混乱を招くものになり、望むことを達成するのに多くのバックスラッシュが必要になります。ゆっくり気楽に進めれば、あなたの状況に合わせて理解できるでしょう。
* シェル引数にシングルクォートを使い、
MVG テキスト文字列をシングルクォートで囲む。最後の2つのクォートの組み合わせのまとめでこれを締めくくりましょう。これらがシェルと MVG によってどうデコードされるかは、あなたに考えてもらいましょう。 |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 '\'' \'\'' " $ \\ '\'' ' \
-trim +repage text_special_ss.gif
![[IM Output]](../static/img/draw/text_special_ss.gif)
* シェル引数にダブルクォートを使い、
MVG テキスト文字列をダブルクォートで囲む。 |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw "text 0,0 \" ' \\\" \$ \\\\ \"" \
-trim +repage text_special_dd.gif
ご覧の通り、コマンドラインからの「[-draw](https://imagemagick.org/command-line-options/#draw)」引数は、コマンドラインシェルと、MVG テキスト文字列内のバックスラッシュおよびクォートのエスケープの両方に対処しなければなりません。結果は混乱しやすくトリッキーになりえます。シェルが2種類のクォートを異なる方法で扱うのに対し、MVG テキスト文字列はそうしないことを覚えておいてください。もちろん複雑なスクリプトでは、より良い方法はシェルとスクリプトの問題を完全に避けることかもしれません。これは、MVG draw ファイルから「[-draw](https://imagemagick.org/command-line-options/#draw)」引数を読み込むことで行えます。
`**-draw @drawfile.mvg**`
もちろん、使っているクォート文字、それにテキスト内のバックスラッシュもバックスラッシュでエスケープする必要があります。しかしこれは、シェル自身のクォートとエスケープの仕組みを IM のものと同時に扱おうとするよりはずっと単純です。 |
magick -size 500x50 xc:lightblue -font Candice -pointsize 36 \
-gravity center -draw @text_quotes.mvg text_quotes.gif
![[IM Output]](../static/img/draw/text_quotes.mvg.gif)
![[IM Output]](../static/img/draw/text_quotes.gif)
最初の画像は、私が使った「MVG」テキストファイルの1つからのものです。これにはシェルのエスケープやクォートは含まれていません。そのため、MVG のクォートとエスケープだけが存在します。上記で、もし MVG テキスト文字列にシングルクォートを使っていたら、唯一の変更は、文字列内でダブルクォート文字ではなくシングルクォート文字をバックスラッシュでエスケープする必要があることだけだったでしょう。パーセント文字について 「-draw text」演算子の特殊な「エスケープ」文字について、最後の点を1つだけ。パーセント文字「%」は「そのまま」描かれるはずです。それらを描くのに特別なことをする必要はないはずです。「そのまま」描かれない場合、あなたは古いバージョンの IM を使っているので、できるだけ早くアップグレードすべきです。 | IM バージョン 6.2.4 までは、「%」文字は描くテキスト文字列に余分な画像情報を含めるためのエスケープ文字として使われていました。そうしたエスケープは、SVG 画像もパーセント文字を描こうとするときに混乱を招き不正確だったため、これはもう当てはまりません。
---|---
このパーセント「エスケープ」(および「\n」改行エスケープ)の使用は、「[-draw](https://imagemagick.org/command-line-options/#draw)」演算子、および MVG 形式の SVG 画像形式を扱うという意図された用途と互換性がないと判断されました。そのため、IM バージョン 6.2.4 以降、% エスケープは機能せず、バックスラッシュは自身と周囲のクォートのみをエスケープします。 |
magick -size 250x50 xc:none -box white -pointsize 20 -gravity center \
-draw 'text 0,0 "%w\n%h"' -trim +repage text_escapes.gif
![[IM Output]](../static/img/draw/text_escapes.gif)
「パーセントバグ」の詳細と、古い ImageMagick で「[-draw](https://imagemagick.org/command-line-options/#draw)」を使うときにそれを避ける方法については、パーセントバグの描画のページを参照してください。Draw の代わりに Annotate こうした種類の問題を避けるより良い方法は、テキスト描画に draw ではなく「-annotate」を使うことです。この演算子は draw 演算子のラッパーで、draw のすべての能力を使えますが、より単純な形でです。基本的に、この演算子は(シェル用の)1組のクォートしか必要としません。これにより特殊文字の扱いがずっとずっと単純になります。残念ながら、IM のためにクォートをエスケープする必要はもうなくなりますが、今度は「@」ファイル読み込み、「\n」改行、その他のパーセントエスケープ展開といったパーセントエスケープがあります。例えば、シングルクォートを使うと… |
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '\@ '\'' " $ \\ %% ' \
-trim +repage annotate_s.gif
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 "\@ ' \" \$ \\\\ %% " \
-trim +repage annotate_d.gif
![[IM Output]](../static/img/draw/annotate_d.gif)
ただし、「@」エスケープを使って文字列をファイルから読み込むと、注記のすべてのクォートとエスケープは完全に無視されます。例えば、ここでは画像の幅と高さの情報を含めます! |
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '%w\n%h' -trim +repage annotate_percents.gif
![[IM Output]](../static/img/draw/annotate_percents.gif)
ただし、注記文字列をファイルから読み込むと、すべてのエスケープは完全に無視されます。 |
echo -n '@ %w\n%h' |\
magick -size 200x50 xc:none -box white -pointsize 20 -gravity center \
-annotate 0 '@-' -trim +repage annotate_file.gif
![[IM Output]](../static/img/draw/annotate_file.gif)
詳細はAnnotate テキスト描画演算子、特にAnnotate のエスケープ文字を参照してください。
IM と SVG の扱い
SVG 入力ドライバ: RSVG 対 MSVG
実際の SVG 画像形式を扱うのは非常に複雑な仕事です。エンジンは、SVG -- Scalable Vector Graphics ドキュメントで定義されている、その全側面を扱う必要があります。これには多くのプログラミングの労力と時間が必要です。そのため、ImageMagick は SVG 形式画像の扱いに2つの方法を提供します。1つ目は、オープンソースの RSVG ライブラリを使って SVG 形式を、IM が問題なく扱えるラスター画像へ変換することです。このエンジンは SVG の扱いのほぼあらゆる側面で完全です。2つ目の方法は、MSVG と呼ばれる組み込みの方法を使って、IM が SVG を MVG へ変換しようとすることです。MSVG は SVG 画像を IM の「-draw」演算子の「MVG」描画言語へ変換しようとします。draw MVG の機能の多くは、特にこの目的のために作られました。残念ながら、基本的な線描画と色付けは存在しますが、完全な SVG 変換器からは程遠いものです。特別な入力形式「MSVG:」(IM v6.3.4 で追加)を使って SVG 画像を読み込むことで、内部の MSVG 変換器の使用を強制できます。ただし、RSVG ライブラリが存在する場合、ほとんどの ImageMagick は代わりにそれを使って SVG 画像をレンダリングします。あなたの IM が何をするかを調べるには、次を使います…
magick -list format | grep SVG
括弧内の「RSVG」でわかるように、私自身の IM は、私のコンピュータに存在する RSVG ライブラリを、与えられたバージョンで使います。 ここでは、白い背景に単純な対角グラデーションの矩形を作る、小さな手作りの SVG 画像「diagonal.svg」(フォーラムユーザー penciledin の寄稿)を「描き」ます。 |
magick diagonal.svg diagonal_rsvg.gif
![[IM Output]](../static/img/draw/diagonal_rsvg.gif)
完璧です。適切な対角グラデーションが生成されます。 しかし、これを内部の MSVG(RSVG ライブラリが存在しない場合のデフォルト)を使ってレンダリングすると… |
magick msvg:diagonal.svg diagonal_msvg.gif
![[IM Output]](../static/img/draw/diagonal_msvg.gif)
ご覧の通り、内部の MSVG 変換は失敗し、対角ではなく垂直のグラデーションを返します。SVG を直接 MVG ファイルへ変換することで、IM が生成する実際の MVG コマンドを見ることもできます。
magick msvg:diagonal.svg mvg:diagonal.mvg
MSVG 変換器が SVG を MVG 描画コマンドへ変換しようとした様子が、おそらくわかるでしょう。現在の内部 MSVG が失敗することがわかっているものには、以下が含まれます…
- 非垂直のグラデーション(新しい MVG グラデーション処理への変換なし)
- 曲がったパスに沿ったテキスト
- テキストの位置揃え(gravity とは別個のもの)
しかし、ほとんどの基本的な描画動作は扱われます。MVG 言語は実は SVG ができないことも扱えることも覚えておいてください。これには、画像とテキストの位置決めに gravity を使うことが含まれます。Gravity は SVG 仕様の一部ではありませんが、IM のテキストとフォントの扱いに不可欠な部分です。また、MVG には SVG が持つのと同じコンテナの仕組みがないことも覚えておいてください。内部の MSVG 変換器は、XML コンテナをグラフィックコンテキストのプッシュとポップで置き換えます(上記の MVG 出力参照)が、これは同じ効果を持ちます。
SVG 設定
SVG 画像形式はベクター形式であり(ベクター画像形式についての一言を参照)、そのため画像は通常デフォルトの「サイズ」を持ちません。代わりに、postscript と同様に、特定の「[-density](https://imagemagick.org/command-line-options/#density)」で「描画」または「レンダリング」されます(デフォルトの密度は 72 dpi)。また、SVG が背景を「塗らない」場合は、「[-background](https://imagemagick.org/command-line-options/#background)」設定を使って、使う背景色を指定できます。例えば、これはもう1つの小さな SVG 画像「[home.svg](../static/img/images/home.svg)」で、透明な背景を含む3つの異なる背景で、3つの異なる密度を使って「レンダリング」されています。
magick -density 36 home.svg home_1.gif
magick -background skyblue home.svg home_2.gif
magick -density 144 -background none home.svg home_3.png
上記の例の、より大きな透明背景版には PNG 形式の画像を使ったことに注意してください。これは、半透明の縁ピクセルのおかげで、GIF 画像形式が生成するよりもきれいな画像を生み出します。最終画像に透明度が関わる場合は、PNG が常に推奨されます。 | _一部の SVG 画像は拡大縮小しないことがわかっています。つまり、それらは「ポイント」「インチ」「ミリメートル」のような実世界の長さではなく、「ピクセル」で定義されています。その結果、「[-density](https://imagemagick.org/command-line-options/#density)」設定が全体の画像サイズ(実世界の単位での)を変えても、「ピクセル」のサイズは変わらず、画像自体のサイズは変わりません。ただし、そうした SVG 画像はかなりまれです。
さらに悪いことに、いくつかの SVG 画像は「ピクセル」と「ポイント」の測定を混在させており、作者が意図的にそうしたのでない限り、作者が意図したのとは異なる密度で使おうとすると、本当にめちゃくちゃになる可能性があります。これらは幸いさらにまれです。
単純な修正は通常、SVG 内のすべての「ピクセル」単位を「ポイント」に変えることですが、「ピクセル」の使用が意図的だった場合に備えて、やみくもに行うべきではありません。_
---|---
SVG 出力の扱い
IM v6.4.2 以降、IM は任意のビットマップ画像を SVG ベクターグラフィックへ変換できます! 変換は常に成功するとは限りませんが、より大きく、かつ/または単純な画像(ビットマップマスクなど)は非常にうまく変換されます。例えば、ここではひどいビットマップ形状を SVG 画像へ変換し、それをまた戻すことで、ビットマップを適切なアンチエイリアスのかかった形状へ滑らかにします。
magick -pointsize 72 -font Candice label:A -threshold 50% \
-trim +repage -bordercolor white -border 5x5 A.gif
magick A.gif A.svg
magick A.svg A.png
| | | ![[IM Text]](../static/img/draw/A.svg.gif)
ただし、これが機能するには、「開発版」の「[AutoTrace](http://autotrace.sourceforge.net/)」ライブラリがインストールされ、IM が「--with-autotrace」スイッチで構成されている必要があります。「[AutoTrace](http://autotrace.sourceforge.net/)」ライブラリがインストールされ IM にコンパイルされていない場合、生成される SVG 出力は、滑らかな SVG 輪郭画像ではなく、膨大な数の単一ピクセルの円となり、二値的な結果を生みます。そうした画像は比較すると巨大で、しばしば SVG レンダラーによるレンダリングに非常に長い時間がかかります。より良いデフォルトのラスターからベクターへの技法が、実は必要です。おそらくモルフォロジーの骨格化や MAT 技法を使ったものです。 上記のすべての手順を「autotrace」コマンドを直接使って一度に行う、「入力ビットマップ画像を滑らかにする」ための「autotrace:」入力デリゲートがありました。しかし、最後に見たときには、このデリゲートは消えていました。これはその使い方でした… |
magick autotrace:A.gif A_traced.png
![[IM Output]](../static/img/draw/A_traced.png)
もちろん、これは「autotrace」コマンドから SVG 出力を得るのではなく、入力画像を SVG を通してフィルタリングして滑らかにするだけです。代替として、ラスターからベクターへの縁取りや Autotrace を使った骨格化の例で示すように、実際に「autotrace」コマンドを直接使えます。写真の変換の解決策を検討した cancerberosgx の、SVG 画像の生成での結果も見るとよいでしょう。
IM 以外のベクターグラフィックエディタ
| ImageMagick はピクセル配列のプロセッサであり、一般にベクター画像を保存せず(「MVG」だけが唯一の例外です)、読み込んでピクセル配列へ変換するだけです。Gimp、Photoshop などの他のピクセル画像エディタも同様です。ベクターベースの画像の編集と扱いには、次のようなプログラムを使ってください。 Sodipodi | SVG ベースのベクターグラフィックエディタ |
|---|---|
| Xfig | 単純だが非常に良い、ベクターオブジェクトエディタ |
| (標識、地図、ページ上の写真の配置に最適) | |
| Dia | |
| AutoTrace | ビットマップ配列内の形状をベクター輪郭へ変換 |
| Sketch | 曲がったテキストを持つ Python ベースのベクターエディタ。 |
| これはもちろん完全なリストではありません。OpenOffice、Word、TeX のような多くのワープロでさえ、一般にさまざまな単純な、しかししばしば使いにくいオブジェクトエディタを備えています。ただし、ベクターグラフィック形式を別のベクター形式へ一般的に変換するには、ImageMagick を使わないでください。ImageMagick は、本質的にラスター画像すなわちビットマップグラフィックの変換器・操作器であり、これからもずっとそうです。詳細はベクター画像形式についての一言を参照してください。 |
![[IM Output]](../static/img/draw/color_replace.png)
![[IM Output]](../static/img/draw/color_floodfill.png)
![[IM Output]](../static/img/draw/matte_floodfill.png)
![[IM Output]](../static/img/draw/rose_raw.png)
![[IM Output]](../static/img/draw/rose_gamma.png)
![[IM Output]](../static/img/draw/stroke_font.jpg)
![[IM Output]](../static/img/draw/stroke_table.jpg)
![[IM Output]](../static/img/draw/stroke_thick.jpg)
![[IM Output]](../static/img/draw/stroke_outline.jpg)
![[IM Output]](../static/img/draw/line_stroke_0_none.jpg)
![[IM Output]](../static/img/draw/line_stroke_0_red.jpg)
![[IM Output]](../static/img/draw/line_stroke_0_black.jpg)
![[IM Output]](../static/img/draw/bound_left_mag.gif)
![[IM Output]](../static/img/draw/bound_right_mag.gif)
![[IM Output]](../static/img/draw/bound_add_mag.gif)
![[IM Output]](../static/img/draw/set_stroke_opacity.gif)
![[IM Output]](../static/img/draw/set_fill_opacity.gif)
![[IM Output]](../static/img/draw/set_lines.gif)
![[IM Output]](../static/img/draw/set_lines_fill.gif)
![[IM Output]](../static/img/draw/path_separate.gif)
![[IM Output]](../static/img/draw/path_hcircle.gif)
![[IM Output]](../static/img/draw/transform_rotate.gif)
![[IM Output]](../static/img/draw/affine_null.gif)
![[IM Output]](../static/img/draw/affine_flip.gif)
![[IM Output]](../static/img/draw/points_rings.gif)
![[IM Output]](../static/img/draw/points_pluses.gif)
![[IM Output]](../static/img/draw/points_crosses.gif)
![[IM Output]](../static/img/draw/point_markers.png)
![[IM Output]](../static/img/draw/circle_bezier_path_rel.gif)
![[IM Output]](../static/img/draw/arrow_with_tails.gif)
![[IM Output]](../static/img/draw/arc_background.gif)
![[IM Output]](../static/img/draw/clown.gif)
![[IM Output]](../static/img/draw/cylinder_95.png)
![[IM Output]](../static/img/draw/text_special_dd.gif)
![[IM Output]](../static/img/draw/annotate_s.gif)
![[IM Text]](../static/img/draw/svg_handling.txt.gif)
![[IM Text]](../static/img/draw/diagonal.svg.gif)
![[IM Text]](../static/img/draw/diagonal.mvg.gif)
![[IM Output]](../static/img/draw/home_1.gif)
![[IM Output]](../static/img/draw/home_2.gif)
![[IM Output]](../static/img/draw/home_3.png)
![[IM Output]](../static/img/draw/A.png)