Logo address

Filter

2012/11/11
2012/11/15

例1

あなたのブラウザは SVG をサポートしていません。最新のブラウザをお使いください。

参考のために Chrome での見え方を次に示す。

Chrome による見え方

これは次のように書かれている。

#encoding: utf-8
import svg
c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5))
d = c.define()

shadow = """
<!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.-->
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
<!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.-->
<feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
<!--offsetBlurの上に元のグラフィックを重ねる.→結果ドロップシャドウ効果となる.-->
<feMerge>
<feMergeNode in="offsetBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>"""
# http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_11.htm

d.filter(-1,-1,1,1,id="F1",filter=shadow)
d.end()

c.rect(-1,-1,1,1)
e = c.group(filter="url(#F1)",font="size:128")
e.text(0,0,text="SVG",anchor="C",fill="red")
c.close()

# Safari: NG
# Firefox: OK
# Opera: OK

このフィルターは

http://www.h2.dion.ne.jp/~defghi/svgMemo/svgMemo_11.htm
から、(コメント部を含めて)そのまま採ってきた。ここには、フィルターに関する非常に詳しい解説が載っている。

Shadow フィルターはよく使われそうなので辞書に登録しておいた。次のように使う。

#encoding: utf-8
import svg
c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5))
d = c.define()
p = d.filter(-1,-1,1,1,filter="shadow")
c.rect(-1,-1,1,1)
e = c.group(filter=p,font="size:128")
e.text(0,0,text="SVG",anchor="C",fill="red")
c.close()

"shadow" の中には "<" が含まれていないので、辞書を見に行く。

例2

例1のファイルターを、3ステップに分けて、各々の効果を示す。

あなたのブラウザは SVG をサポートしていません。最新のブラウザをお使いください。

この SVG ファイルを生成する Python のコードは

#encoding: utf-8
import svg

shadow1 = """
<!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.-->
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
"""

shadow2 = """
<!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.-->
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
<!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.-->
<feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
"""

shadow3 = """
<!--まず図形の影をSourceAlphaで取得し,ぼかす.結果をblurとして保持する.-->
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
<!--先ほどのぼかした結果をずらして,offsetBlurとして保持する.-->
<feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>
<!--offsetBlurの上に元のグラフィックを重ねる.→結果ドロップシャドウ効果となる.-->
<feMerge>
<feMergeNode in="offsetBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>"""

c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5))
d = c.define()
p1 = d.filter(-1.2,1.2,-0.2,0.2,filter=shadow1)
p2 = d.filter(1.2,1.2,0.2,0.2,filter=shadow2)
p3 = d.filter(-1.2,-1.2,-0.2,-0.2,filter=shadow3)
d.end()

e = c.group(filter=p1,font="size:128")
e.rect(-1.2,1.2,-0.2,0.2,stroke="white;width:4",fill="none")
e.end()
e = c.group(filter=p2,font="size:128")
e.rect(1.2,1.2,0.2,0.2,stroke="white;width:4",fill="none")
e.end()
e = c.group(filter=p3,font="size:128")
e.rect(-1.2,-1.2,-0.2,-0.2,stroke="white;width:4",fill="none")
e.end()

c.close()

フィルターは Firefox が一番しっかりしているようだ。次に Firefox での見え方を示す。

例3

次の図はフィルターをかけた場合の効果を示す例である。
現状では、ブラウザ依存性が強く、この図の場合には

  1. Firefox
  2. Opera
  3. Safari
  4. Chrome

の順で、効果が現れている。(IE9 は試していない)

Chrome は Firefox レベルの効果を出しているのだが、ひどいバグがあり、「SVG」の文字と中央の円が見たり見えなかったりする(安定しない)。これではフィルターに関しては使い物にならない!

あなたのブラウザは SVG をサポートしていません。最新のブラウザをお使いください。

参考のために Firefox での見え方を次に示す。

Firefox による見え方

この SVG ファイルを生成する Python のコードは

#encoding: utf-8
import svg
c = svg.Canvas(0,0,400,400,map=(-1.5,1.5,1.5,-1.5))
d = c.define()

blur = """
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>
<feOffset in="blur" dx="4" dy="4" result="offsetBlur"/>"""

lighting = """
<feSpecularLighting in="blur" surfaceScale="5" specularConstant=".75"
      specularExponent="20" lighting-color="#bbbbbb"
      result="specOut">
  <fePointLight x="-5000" y="-10000" z="20000"/>
</feSpecularLighting>"""

composite = """
<feComposite in="specOut" in2="SourceAlpha" operator="in" result="specOut"/>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic"
  k1="0" k2="1" k3="1" k4="0" result="litPaint"/>"""

merge = """
<feMerge>
  <feMergeNode in="offsetBlur"/>
  <feMergeNode in="litPaint"/>
</feMerge>"""

d.filter(-1,-1,1,1,id="F1",filter=blur+lighting+composite+merge)
d.end()

c.rect(-1,-1,1,1)
e = c.group(filter="url(#F1)",font="size:45")
e.text(0,0,text="SVG",anchor="C")
e.rect(-0.5,-1.2,0.5,1.2, stroke="red;width:10", fill="none")
e.circle(0,0,r=0.5,stroke="red;width:10", fill="none")

c.close()

このプログラムでは

d.filter(-1,-1,1,1,id="F1",filter=f1)
で、フィルターをかける領域(-1,-1,1,1)と、フィルター(f1)を指定している。
フィルターをかける領域は参考のために図では黒枠で示してある。結果を見る限り、この外では、フィルターがかからないのではなく、見えなくなる。だから、フィルターをかける領域と言うよりは、見せる領域と言った方が誤解が無い。

フィルター f1 は、SVG の形式そのものであり、ここに使われたものは

http://www.w3.org/TR/SVG/filters.html#FilterElement
から、そのまま、拝借した。(ただし、分かりやすくする4つに分解した)

この例のフィルターは4種から構成されている。それらを文字列 blur,lighting,composite,merge としている。
プログラムの内容を読むと、各々のフィルターへの input と output が、inresult で示されている。それらがお互いに絡み合っているので、独立したフィルターの定義(文字列)を単に足し合わせれば良いものではない。

このフィルターの効果を見るに、結構苦労して作られたのでないかと想像する。フィルルターはプログラム並みにソフトウェア資産と考えるべきであろう。

取りあえず、フィルターの効果が見えるようにしたが、フィルターも、ライブラリ化できるようにすべきではないかと思っている。その場合、多少の仕様の変更が発生するかも知れない。
だいたい僕は

filter="url(#F1)"
と書かせるような無粋な仕様は嫌いである。

雑感

今回、この問題を扱っていて2つの事に気づいた。

baseline

Python/Tk の anchor で "C" のように、簡潔に文字列を書く位置を決められるのは非常に楽である。他方では SVG の baseline は、ブラウザ依存性が強く、僕も使い方が分からない部分が多すぎる。こうした問題に関してネット上にも問題提起がある。

僕は、これまでは、text メソッドで直接フォントサイズを指定して、それを基に anchor="C" のように指定した時のテキストの位置を決められるようにしていたが、このやり方はグループに対してフォントサイズが指定される場合には使えない。
そこで今回は方針を転換して、Nelson さんのアドバイスに従う事とした。

スタイルか属性か?

このプログラムでは

e = c.group(filter="url(#F1)",font="size:45")
のように、グループに対してフォントサイズが指定されている。

僕は

<text font-size="45" font-family="Times">SVG</text>

<text style="font-size:45;font-family:Times">SVG</text>
は同じ事と思っていたが...

前者は実験した4つのブラウザでは機能していたが、後者は Firefox と Opera では font-size の指定が働いていない! この例では text タグを挙げたが、g タグの場合も同様である。

こんな基本的な所で、どうして?!

ところで、最終的な SVG を生成する時に、style の形式が良いのか、それとも attribute の形式が良いのか、迷う所であるが、このようなブラウザ依存の問題があれば style の形式を使う訳にはいかないかと思う。僕は、これまでは style に統一していたが、修正する事にした。