フィルター効果を用いることでそのままでは印象の薄い平面図形に様々な表情を持たせることができる.本項ではこのフィルター効果について説明する.SVGの持つ機能の中でも特に仕様が複雑なものの一つである.
フィルター効果を用いることでそのままでは印象の薄い平面図形に様々な表情を持たせることができる.本項ではこのフィルター効果について説明する.SVGの持つ機能の中でも特に仕様が複雑なものの一つである.
filter要素は図形に対しての視覚効果を表す.元の画像・図形を維持しつつ,見た目の変更・装飾を行えるため非常に魅力的な機能である.一般に画像はその構成する画素毎にrgb値と不透明度(alpha)値とを定めるが,filterはこの色に関わる4つの値を画素毎に編集するだけである.従ってサイズを変えたり回転させるといった画像の変形を伴う操作については原則transform属性を用いねばならない.なお,filterによる画像効果は暗黙的なラスタライズを引き起こし,本質的に重いため掛け過ぎてはならない.特にフィルターを掛ける領域が広くなるほど負荷が高くなる.
filter機能はsvgの仕様の中でもかなり巨大なものであり,ブラウザごとの実装状況に大きな隔たりが存在する.基本的な機能は概ね問題ないものの,いざ複雑な効果を得ようとした際に,環境によって見た目や動作が異なるばかりか一切動かないと言ったことも有りうる.従って,できる限り構造を簡潔にしたり十分な検証を行う必要がある.
feから始まる要素群を原始フィルターと呼ぶ.原始フィルターは基本的な画像操作で,これを手続き的(関数的)に組み合わせることで目的のフィルター操作を定義する.原始フィルターは上から順に実行され,in属性に設定された画像にフィルターを掛け,result属性に設定した名称で保持する.この画像を改めて他の原始フィルターのinに設定することで画像効果を重ねることが出来る.この処理を繰り返し,最後に実行された原始フィルターの結果がスクリーンに描画される.要はin(in2)を入力とし,resultを出力としたパイプ処理である.
例を示す.元画像に原始フィルターを適用して最終的にドロップシャドウ画像を生成している.
<svg viewBox="0 0 200 200">
<title>ドロップシャドウの基本形</title>
<defs>
<filter id="fil1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<!--まず図形の影を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>
</filter>
</defs>
<circle filter="url(#fil1)" cx="100" cy="100" r="80" fill="red" stroke="orange" stroke-width="10"/>
</svg>
もうひとつ例を示す.これはラテアートをイメージした効果を与えるもので,原始フィルターの組み合わせによっては非常に面白い効果が得られる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="caffeLatte">
<!--グレイスケール-->
<feColorMatrix type="saturate" values="0" result="gray"/>
<!--離散化-->
<feComponentTransfer>
<feFuncR type="discrete" tableValues="0,1"/>
<feFuncG type="discrete" tableValues="0,1"/>
<feFuncB type="discrete" tableValues="0,1"/>
</feComponentTransfer>
<!--ぼかし-->
<feGaussianBlur stdDeviation="1" result="blur"/>
<!--輪郭抽出-->
<feConvolveMatrix in="gray" order="3 3" targetX="1" targetY="1" edgeMode="none" kernelMatrix="1 1 1 1 -8 1 1 1 1" divisor="8" preserveAlpha="true" result="edge"/>
<!--輪郭を黒の不透明度に変換-->
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0" result="edge2"/>
<!--背景色-->
<feFlood flood-color="black" result="bg"/>
<!--背景色→ぼかし→輪郭の順に重ねる(この時点で黒)-->
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="blur"/>
<feMergeNode in="edge2"/>
</feMerge>
<!--色味をカフェオレ調に-->
<feComponentTransfer result="base">
<feFuncR type="linear" slope="0.6" intercept="0.4"/>
<feFuncG type="linear" slope="0.8" intercept="0.2"/>
<feFuncB type="linear" slope="1" intercept="0"/>
</feComponentTransfer>
<!--ノイズを生成-->
<feTurbulence type="turbulence" baseFrequency="0.005" numOctaves="5" seed="3" stitchTiles="noStitch"/>
<feColorMatrix type="saturate" values="0" result="noise"/>
<!--ノイズをブレンド-->
<feBlend in="base" in2="noise" mode="screen"/>
</filter>
</defs>
<image filter="url(#caffeLatte)" xlink:href="http://www.konami.jp/products/dl_xbox_dracula_hd/images/charlotte.jpg" width="200" height="200"/>
</svg>
filter要素のもつ属性は次の通り.
filter要素のx,y,width,height属性はフィルターを施す際のグラフィックの参照範囲及び出力領域を表す.この範囲外への描画は無視されてしまう.フィルターの処理結果が欠けてしまう等の問題が発生する場合,これらの値が適切かどうか確認しよう.
<svg viewBox="0 0 200 200">
<defs>
<filter id="frange" filterUnits="userSpaceOnUse" x="20" y="20" width="160" height="160">
<feOffset in="SourceAlpha" dx="50" dy="50"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect filter="url(#frange)" x="40" y="40" width="120" height="120" fill="red"/>
<rect x="20" y="20" width="160" height="160" fill="none" stroke="black" stroke-dasharray="2"/>
</svg>
なおこれらの値はfilterUnits属性の設定内容により意味合いが変化する.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fu_us" filterUnits="userSpaceOnUse" x="20" y="20" width="60" height="60">
<feOffset dx="0" dy="0"/>
</filter>
<filter id="fu_ob" filterUnits="objectBoundingBox" x="0.2" y="0.2" width="0.6" height="0.6">
<feOffset dx="0" dy="0"/>
</filter>
</defs>
<g fill="orange">
<path d="M0,80l20,20l80,-80l-20,-20z"/>
<path d="M100,180l20,20l80,-80l-20,-20z"/>
</g>
<!--フィルターで描画範囲を制限-->
<g fill="red">
<path d="M0,80l20,20l80,-80l-20,-20z" filter="url(#fu_us)"/>
<path d="M100,180l20,20l80,-80l-20,-20z" filter="url(#fu_ob)"/>
</g>
</svg>
filterRes属性はフィルターを施す際の解像度を表す.小さいと画質が悪く,大きいと処理が重くなる.フィルターが施された要素はこの解像度の影響を受けるため,画像拡大時にラスタ形式の画像と同様の品質の低下が発生する.なお意図的に値を小さく設定すると画像にモザイク的な効果を施すことも出来る(ブラウザによって結果が異なる点に注意).この値を指定するとベクタ形式が本来持っている解像度に依存しないと言ったメリットを打ち消すため,使い所をよく考える必要がある.
<svg viewBox="0 0 200 200">
<defs>
<filter id="res1" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100" filterres="100,100">
<feGaussianBlur stdDeviation="0"/>
</filter>
<filter id="res2" filterUnits="objectBoundingBox" x="0" y="0" width="1" height="1" filterres="5,5">
<feGaussianBlur stdDeviation="0"/>
</filter>
</defs>
<image filter="url(#res1)" xlink:href="img.png" x="0" y="0" width="100" height="100"/>
<image filter="url(#res2)" xlink:href="img.png" x="100" y="100" width="100" height="100"/>
</svg>
既に有るfilter要素の設定値をxlink:hrefを使うと流用することができる.流用可能な値は属性値及び原始フィルターの構成である.が,既存のフィルターにフィルターを追加すると言ったことは出来ない.
<svg viewBox="0 0 200 200">
<defs>
<!--基本となるfilter-->
<filter id="main1" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100" filterres="100,100">
<feGaussianBlur stdDeviation="5" result="a"/>
</filter>
<!--filterを参照して,設定値を上書きする-->
<filter id="main2" xlink:href="#main1" height="200"/>
<filter id="main3" xlink:href="#main1" x="100" height="200">
<feColorMatrix type="luminanceToAlpha"/>
</filter>
</defs>
<image filter="url(#main2)" xlink:href="img.png" x="0" y="0" width="200" height="200"/>
<image filter="url(#main3)" xlink:href="img.png" x="0" y="0" width="200" height="200"/>
</svg>
原始フィルターには様々な種類があるが,次の属性は共通して定義されている.
in属性に与える値には元画像を構成するグラフィックを表すキーワード他の原始フィルターの処理結果(result属性のキーワード)の何れかを指定する.in属性未設定の場合は直前の原始フィルターの処理結果が,先頭の原始フィルターの場合はSourceGraphicが与えられたものとして扱われる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fil" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feGaussianBlur stdDeviation="4"/>
</filter>
</defs>
<circle filter="url(#fil)" cx="100" cy="100" r="80" fill="red" stroke="orange" stroke-width="10"/>
</svg>
SourceGraphicが元の画像を表すのに対し,SourceAlphaは元画像の不透明度を黒色で取得する.要は影である.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fil_a" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feGaussianBlur in="SourceAlpha" stdDeviation="4"/>
</filter>
</defs>
<circle filter="url(#fil_a)" cx="100" cy="100" r="80" fill="red" stroke="orange" stroke-width="10"/>
</svg>
fill属性,stroke属性で利用しているグラフィックを取り出すこともできる.現状operaのみ対応.将来的にfirefoxにおいても利用可能.
<svg viewBox="0 0 200 200">
<defs>
<filter id="stroke_and_fill" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" x="0" y="0" width="100" height="100" result="a"/>
<feOffset in="SourceAlpha" x="100" y="0" width="100" height="100" result="b"/>
<feOffset in="StrokePaint" x="0" y="100" width="100" height="100" result="c"/>
<feOffset in="FillPaint" x="100" y="100" width="100" height="100" result="d"/>
<feMerge>
<feMergeNode in="a"/>
<feMergeNode in="b"/>
<feMergeNode in="c"/>
<feMergeNode in="d"/>
</feMerge>
</filter>
</defs>
<circle filter="url(#stroke_and_fill)" cx="100" cy="100" r="80" fill="red" stroke="orange" stroke-width="10"/>
</svg>
図形の描画範囲の背景画像を取得することもできる.presto operaのみ対応.なお,背景画像との合成を行う場合はSVG2で導入されたmix-blend-modeプロパティを使ったほうがよい.
<svg viewBox="0 0 200 200" enable-background="new">
<defs>
<filter id="fil2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feGaussianBlur in="BackgroundImage" stdDeviation="4" result="blur"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur2"/>
<feMerge>
<feMergeNode in="blur"/>
<feMergeNode in="blur2"/>
</feMerge>
</filter>
</defs>
<text x="0" y="20">operaだとぼかし</text>
<circle filter="url(#fil2)" cx="100" cy="100" r="40" fill="red"/>
</svg>
<svg viewBox="0 0 200 200" enable-background="new">
<defs>
<filter id="fil2-2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feBlend mode="screen" in="BackgroundImage" in2="SourceGraphic"/>
</filter>
</defs>
<rect width="100%" height="100%" fill="black"/>
<circle filter="url(#fil2-2)" cx="100" cy="70" r="50" fill="red"/>
<circle filter="url(#fil2-2)" cx="70" cy="125" r="50" fill="#0f0"/>
<circle filter="url(#fil2-2)" cx="130" cy="125" r="50" fill="blue"/>
</svg>
x,y,width,height属性は原始フィルターが処理対象とする処理範囲(原始フィルタ部分領域)を表す.参照した画像はこの範囲で切りだされ,この範囲に出力される.なお原始フィルターが参照する座標・長さは親となるfilter要素のprimitiveUnits属性により意味合いが変化する.
<svg viewBox="0 0 200 200">
<defs>
<filter id="pu_us" primitiveUnits="userSpaceOnUse" x="-0.1" y="-0.1" width="1.2" height="1.2">
<feOffset dx="30" dy="30" x="10" y="10" width="80" height="80"/>
</filter>
<filter id="pu_ob" primitiveUnits="objectBoundingBox" x="-0.1" y="-0.1" width="1.2" height="1.2">
<feOffset dx="0.3" dy="0.3" x="0.1" y="0.1" width="0.8" height="0.8"/>
</filter>
</defs>
<g fill="orange">
<circle cx="50" cy="50" r="50"/>
<circle cx="150" cy="150" r="50"/>
</g>
<g fill="red">
<circle filter="url(#pu_us)" cx="50" cy="50" r="50"/>
<circle filter="url(#pu_ob)" cx="150" cy="150" r="50"/>
</g>
</svg>
図形要素にfilter属性とtransform属性の両方が定義されていた場合,transform属性で変換された座標軸を基準にfilterが掛けられる(=filter処理の後で図形の変形が行われる).元の座標軸を基準にfilter処理を施したい場合は図形要素を一旦g要素で囲み,このg要素に対してfilter属性を設定すると良い.
<svg viewBox="0 0 200 200">
<defs>
<filter id="ft" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceAlpha" dx="10"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect filter="url(#ft)" x="25" y="25" width="50" height="50" fill="red" transform="rotate(45,50,50)"/>
<g filter="url(#ft)">
<rect x="125" y="125" width="50" height="50" fill="red" transform="rotate(45,150,150)"/>
</g>
</svg>
同様にg要素を用いてフィルターを重ねることも可能だ.これは原始フィルターを組み合わせたフィルターを更に組み合わせる場合に重宝する.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fil_1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feGaussianBlur stdDeviation="5"/>
</filter>
<filter id="fil_2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset dx="20" dy="20"/>
</filter>
</defs>
<g filter="url(#fil_2)">
<rect x="25" y="25" width="150" height="150" fill="red" filter="url(#fil_1)"/>
</g>
</svg>
以下原始フィルター要素の種類を示す.ブラウザにより対応状況や効果の程度等が異なるので,見た目の確認が必要である.なおここで示すフィルターは原始フィルターの中でもBasicFilterと呼ばれているもので,比較的単純に用いることができる.
画像の合成法を選択できる.概ねmultiplyでは暗く,screenでは明るくなる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feBlend1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feBlend in="img1" in2="img2" mode="normal"/>
</filter>
<filter id="feBlend2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feBlend in="img1" in2="img2" mode="multiply"/>
</filter>
<filter id="feBlend3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feBlend in="img1" in2="img2" mode="screen"/>
</filter>
<filter id="feBlend4" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feBlend in="img1" in2="img2" mode="darken"/>
</filter>
<filter id="feBlend5" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feBlend in="img1" in2="img2" mode="lighten"/>
</filter>
</defs>
<g fill="red" stroke="orange" stroke-width="8">
<circle filter="url(#feBlend1)" cx="25%" cy="33" r="20"><title>normal</title> </circle>
<circle filter="url(#feBlend2)" cx="75%" cy="33" r="20"><title>multiply</title> </circle>
<circle filter="url(#feBlend3)" cx="25%" cy="100" r="20"><title>screen</title> </circle>
<circle filter="url(#feBlend4)" cx="75%" cy="100" r="20"><title>darken</title> </circle>
<circle filter="url(#feBlend5)" cx="25%" cy="166" r="20"><title>lighten</title> </circle>
</g>
</svg>
SVG2では上記のmodeの他,Compositing and Blending Level 1によるブレンドモードがサポートされる.
<svg viewBox="0 0 300 600" style="width:200px;height:400px;">
<defs>
<filter id="fbase" filterUnits="userSpaceOnUse" x="0" y="0" width="100" height="100"/>
<filter id="feBlend11" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="normal"/>
</filter>
<filter id="feBlend12" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="multiply"/>
</filter>
<filter id="feBlend13" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="screen"/>
</filter>
<filter id="feBlend14" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="overlay"/>
</filter>
<filter id="feBlend15" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="darken"/>
</filter>
<filter id="feBlend16" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="lighten"/>
</filter>
<filter id="feBlend21" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="color-dodge"/>
</filter>
<filter id="feBlend22" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="color-burn"/>
</filter>
<filter id="feBlend23" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="hard-light"/>
</filter>
<filter id="feBlend24" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="soft-light"/>
</filter>
<filter id="feBlend25" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="difference"/>
</filter>
<filter id="feBlend26" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="exclusion"/>
</filter>
<filter id="feBlend31" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="hue"/>
</filter>
<filter id="feBlend32" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="saturation"/>
</filter>
<filter id="feBlend33" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="color"/>
</filter>
<filter id="feBlend34" xlink:href="#fbase">
<feImage xlink:href="img.png" x="40" y="40" width="60" height="60"/>
<feBlend in2="SourceGraphic" mode="luminosity"/>
</filter>
</defs>
<g fill="orange">
<circle cx="40" cy="40" r="40" filter="url(#feBlend11)" transform="translate(0,0)"><title>normal</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend12)" transform="translate(0,100)"><title>multiply</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend13)" transform="translate(0,200)"><title>screen</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend14)" transform="translate(0,300)"><title>overlay</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend15)" transform="translate(0,400)"><title>darken</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend16)" transform="translate(0,500)"><title>lighten</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend21)" transform="translate(100,0)"><title>color-dodge</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend22)" transform="translate(100,100)"><title>color-burn</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend23)" transform="translate(100,200)"><title>hard-light</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend24)" transform="translate(100,300)"><title>soft-light</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend25)" transform="translate(100,400)"><title>difference</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend26)" transform="translate(100,500)"><title>exclusion</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend31)" transform="translate(200,0)"><title>hue</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend32)" transform="translate(200,100)"><title>saturation</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend33)" transform="translate(200,200)"><title>color</title> </circle>
<circle cx="40" cy="40" r="40" filter="url(#feBlend34)" transform="translate(200,300)"><title>luminosity</title> </circle>
</g>
</svg>
三原色+透過度の4要素を5×4行列によって行う変換.色味を交換したり,色を透明度に変換したりする.type=matrixの場合の色演算を行列で表すと次の通り.(MathMLで記述しています.)
r'= a00*r + a01*g + a02*b + a03*a + a04
g'= a10*r + a11*g + a12*b + a13*a + a14
b'= a20*r + a21*g + a22*b + a23*a + a24
a'= a30*r + a31*g + a32*b + a33*a + a34
このままだと使いにくいが,よく使いそうなものについては予め定義されている.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feColorMatrix1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
<filter id="feColorMatrix2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="saturate" values="0.5"/>
</filter>
<filter id="feColorMatrix3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="hueRotate" values="135"/>
</filter>
<filter id="feColorMatrix4" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feColorMatrix type="luminanceToAlpha"/>
</filter>
</defs>
<circle filter="url(#feColorMatrix1)" cx="50" cy="50" r="30" fill="red" stroke="orange" stroke-width="12.5"/>
<circle filter="url(#feColorMatrix2)" cx="150" cy="50" r="30" fill="red" stroke="orange" stroke-width="12.5"/>
<circle filter="url(#feColorMatrix3)" cx="50" cy="150" r="30" fill="red" stroke="orange" stroke-width="12.5"/>
<circle filter="url(#feColorMatrix4)" cx="150" cy="150" r="30" fill="red" stroke="orange" stroke-width="12.5"/>
</svg>
R,G,B,不透明度毎に色味の変換関数を指定する.減色(discrete),ネガポジ変換(linear),色調補正(gamma),切り抜きなどに用いる.
<svg viewBox="0 0 200 200">
<title>ネガ</title>
<defs>
<filter id="feComponentTransfer" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feComponentTransfer>
<feFuncR type="linear" slope="-1" intercept="1"/>
<feFuncG type="linear" slope="-1" intercept="1"/>
<feFuncB type="linear" slope="-1" intercept="1"/>
</feComponentTransfer>
</filter>
</defs>
<image filter="url(#feComponentTransfer)" xlink:href="img.png" width="200" height="200"/>
</svg>
<svg viewBox="0 0 200 200">
<title>減色</title>
<defs>
<filter id="reduceColours">
<feComponentTransfer>
<feFuncR type="discrete" tableValues="0,0.5,1"/>
<feFuncG type="discrete" tableValues="0,0.5,1"/>
<feFuncB type="discrete" tableValues="0,0.5,1"/>
</feComponentTransfer>
</filter>
</defs>
<image filter="url(#reduceColours)" xlink:href="img.png" width="200" height="200"/>
</svg>
画像をくり貫きたい場合に便利.詳しくはこちらを参照.不透明度が1の部分をベースに考えると判りやすい.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feComposite1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="over"/>
</filter>
<filter id="feComposite2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="in"/>
</filter>
<filter id="feComposite3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="out"/>
</filter>
<filter id="feComposite4" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="atop"/>
</filter>
<filter id="feComposite5" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="xor"/>
</filter>
<filter id="feComposite6" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-10" dy="-10" result="img1"/>
<feOffset in="SourceGraphic" dx="10" dy="10" result="img2"/>
<feComposite in="img1" in2="img2" operator="arithmetic" k1="0.3" k2="0.5" k3="1" k4="0"/>
</filter>
</defs>
<g fill="red" stroke="orange" stroke-width="8">
<circle filter="url(#feComposite1)" cx="25%" cy="33" r="20"/>
<circle filter="url(#feComposite2)" cx="75%" cy="33" r="20"/>
<circle filter="url(#feComposite3)" cx="25%" cy="100" r="20"/>
<circle filter="url(#feComposite4)" cx="75%" cy="100" r="20"/>
<circle filter="url(#feComposite5)" cx="25%" cy="166" r="20"/>
<circle filter="url(#feComposite6)" cx="75%" cy="166" r="20"/>
</g>
</svg>
フィルタ範囲を指定の色で塗りつぶす.単体で用いることは無いかもしれない.x,y,width,height属性を指定し矩形範囲の塗りつぶすことができる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feFlood" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feFlood flood-color="blue" flood-opacity="0.5" x="10" y="10" width="180" height="180"/>
</filter>
</defs>
<rect filter="url(#feFlood)" width="200" height="200"/>
</svg>
画像をぼかす.コンボリューションフィルタによるぼかしよりも高速.SourceAlphaから図形の影を作るのは常套手段.x軸とy軸の別々にぼかす幅を指定することができる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feGaussianBlur" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feGaussianBlur in="SourceAlpha" stdDeviation="5 10"/>
</filter>
</defs>
<circle filter="url(#feGaussianBlur)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
外部の画像を読み込む.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feImage" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feImage xlink:href="img.png" x="10" y="10" width="180" height="180"/>
</filter>
</defs>
<circle filter="url(#feImage)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
feMergeNodeに設定した画像を順に重ねていく.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feMerge" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-25" dy="-25" result="img1"/>
<feOffset in="SourceGraphic" dx="25" dy="25" result="img2"/>
<feMerge>
<feMergeNode in="img1"/>
<feMergeNode in="img2"/>
</feMerge>
</filter>
</defs>
<circle filter="url(#feMerge)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
元の画像を指定した分ずらした画像を得る.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feOffset" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feOffset in="SourceGraphic" dx="-25" dy="-25" result="img1"/>
<feOffset in="SourceGraphic" dx="25" dy="25" result="img2"/>
<feBlend in="img1" in2="img2" mode="screen"/>
</filter>
</defs>
<circle filter="url(#feOffset)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
feImageで読み込んだ画像をタイル状に並べる等の用途に使う.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feTile" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feImage x="0" y="0" width="100" height="100" xlink:href="img.png" result="img"/>
<feTile x="0" y="0" width="200" height="200" in="img"/>
</filter>
</defs>
<rect filter="url(#feTile)" width="200" height="200"/>
</svg>
以下は先ほどの基本フィルターに含まれないものである.フィルターの中でも動作が複雑なものが多く,工夫次第で様々な効果を得ることができる.
日本語では行列による畳み込みと呼ばれる.画素を中心としたマトリクス領域を元に中心画素の色を算出する.エンボス,輪郭抽出,ぼかし,シャープネス等の処理が可能.具体的なアルゴリズムについてはこちらを参照のこと.概ね他のグラフィックツールの考えと同じなので,色々と調べてみるとよい.なお上手く行かないときはpreserveAlphaの値がtrueとなっているか確認しよう.
例)輪郭抽出order="3 3" kernelMatrix="1 1 1 1 -8 1 1 1 1" divisor="8" preserveAlpha="true"
<svg viewBox="0 0 200 200">
<defs>
<filter id="feConvolveMatrix" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feConvolveMatrix order="7 1" targetX="3" targetY="0" edgeMode="none" kernelMatrix="1 1 1 1 1 1 1"/>
</filter>
</defs>
<circle filter="url(#feConvolveMatrix)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
<svg viewBox="0 0 200 200">
<title>輪郭抽出</title>
<defs>
<filter id="feConvolveMatrix2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feConvolveMatrix order="3 3" targetX="1" targetY="1" kernelMatrix="1 1 1 1 -8 1 1 1 1" divisor="8" preserveAlpha="true"/>
</filter>
</defs>
<circle filter="url(#feConvolveMatrix2)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
元画像を変形し様々な効果を得る.transformのような均一な変形と異なり歪み等を表現できる.こちらも参照.
変形は次の計算式によって行われる.Pは元画像の座標を,P'は変換後の画像の座標を表す.XC,YCはin2の指定した座標の色を表す.(なお,色については不透明度(0〜1)を掛けた値となる)
P'(x,y) <- P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))
要はin2の暗い/明るいところは画像がズレると憶えておけば良い.この機能は高機能だが,使いこなすには難しいかもしれない.
feTurbulanceと合わせると自然な揺らぎなどが得られる.x方向,y方向の何れかの方向のみにずらしたい場合はfeComponentTransferと組み合わせると良い.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDisplacementMap1" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="1" seed="1" stitchTiles="noStitch" result="img"/>
<feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="20"/>
</filter>
</defs>
<g filter="url(#feDisplacementMap1)">
<circle cx="90" cy="90" r="60" fill="red" stroke="orange" stroke-width="25"/>
<circle cx="130" cy="130" r="50" fill="red" stroke="orange" stroke-width="25"/>
</g>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDisplacementMap2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feTurbulence type="fractalNoise" baseFrequency="0.995" numOctaves="1" seed="1" stitchTiles="noStitch"/>
<feComponentTransfer result="img">
<feFuncG type="linear" slope="0" intercept="0.5"/>
<feFuncA type="linear" slope="0" intercept="1"/>
</feComponentTransfer>
<feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="400">
<animate attributeName="scale" from="400" to="0" begin="0s" dur="10s" repeatCount="indefinite"/>
</feDisplacementMap>
</filter>
</defs>
<g filter="url(#feDisplacementMap2)">
<circle cx="90" cy="90" r="60" fill="red" stroke="orange" stroke-width="25"/>
<circle cx="130" cy="130" r="50" fill="red" stroke="orange" stroke-width="25"/>
</g>
</svg>
ブラウザ間のcolor-interpolation-filters属性の解釈の差異によって微妙に変形の度合いが異なるケースがあるので,見た目に大きな隔たりが出た場合はこの値を疑ってみよう.
<svg viewBox="0 0 200 200">
<defs>
<!--color-interpolation-filters属性によりズレる量が異なっている点に注意-->
<filter id="feDisplacementMap3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feFlood flood-color="#d00" x="50" y="50" width="100" height="100" result="img"/>
<feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="A" scale="100" color-interpolation-filters="sRGB" x="0" y="0" width="200" height="200"/>
</filter>
</defs>
<image filter="url(#feDisplacementMap3)" xlink:href="img.png" width="200" height="200"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<!--color-interpolation-filters属性によりズレる量が異なっている点に注意-->
<filter id="feDisplacementMap4" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feFlood flood-color="#d00" x="50" y="50" width="100" height="100" result="img"/>
<feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="A" scale="100" color-interpolation-filters="linearRGB" x="0" y="0" width="200" height="200"/>
</filter>
</defs>
<image filter="url(#feDisplacementMap4)" xlink:href="img.png" width="200" height="200"/>
</svg>
in2に指定する画像を用意する場合,スクリプトの利用を許すのであればcanvas要素でラスタ画像を動的に生成した後,得られたdataスキーム形式の文字列をfeImage要素に渡す方法もある.変形前と変形後とでピクセル毎にどのくらいずらすかをきちんと設計する必要があるため,それほど容易ではない.(素直にcanvas要素を使えばいい話だが,どうしてもfilterを使いたい場合に有効.)
<svg onload="
var canvas = document.createElement("canvas");
canvas.width = 200;
canvas.height = 200;
var ctx = canvas.getContext("2d");
var id = ctx.createImageData(200, 200);
var d = id.data;
for(var y = 0; y < 200; y++){
for(var x = 0; x < 200; x++){
var pos = (x + 200 * y) * 4;
//R
//水平方向に円弧状のラスタ変形を施す
d[pos + 0] = 128 + Math.round(128 * Math.sin(y / 200 * Math.PI));
//G
d[pos + 1] = 128;
//B
d[pos + 2] = 0;
//A
d[pos + 3] = 255;//不透明にしないと意図した変形が行われない.
}
}
ctx.putImageData(id, 0, 0);
var fe = this.getElementsByTagName("feImage")[0];
fe.setAttributeNS("http://www.w3.org/1999/xlink", "href", canvas.toDataURL());
">
<defs>
<filter id="feDisplacementMap5" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feImage x="0" y="0" width="200" height="200" result="img"/>
<feDisplacementMap in="SourceGraphic" in2="img" xChannelSelector="R" yChannelSelector="G" scale="-50" color-interpolation-filters="sRGB" x="0" y="0" width="200" height="200"/>
</filter>
</defs>
<image filter="url(#feDisplacementMap5)" xlink:href="img.png" width="200" height="200"/>
</svg>
周辺画素の明るさを元に中心の色を決定する.応用次第で筆で描いたような効果を得ることも可能だ.斜め方向の効果が大きくなりやすく,これが気になる場合はfilterRes属性を若干大きめに取ると改善される場合がある.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feMorphology" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feMorphology operator="dilate" radius="2"/>
</filter>
</defs>
<image filter="url(#feMorphology)" xlink:href="img.png" x="0" y="0" width="200" height="200"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feMorphology2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feMorphology operator="erode" radius="2"/>
</filter>
</defs>
<image filter="url(#feMorphology2)" xlink:href="img.png" x="0" y="0" width="200" height="200"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feMorphology2-2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feMorphology operator="dilate" radius="0 10"/>
</filter>
</defs>
<image filter="url(#feMorphology2-2)" xlink:href="img.png" x="0" y="0" width="200" height="200"/>
</svg>
<svg viewBox="0 0 200 200">
<title>縁どり</title>
<defs>
<filter id="feMorphology3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200" filterres="400,400">
<feMorphology in="SourceAlpha" operator="dilate" radius="4"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<g filter="url(#feMorphology3)">
<circle cx="90" cy="90" r="60" fill="red" stroke="orange" stroke-width="25"/>
<circle cx="130" cy="130" r="50" fill="red" stroke="orange" stroke-width="25"/>
</g>
</svg>
雲や大理石模様等の自然界にしばしば現れる紋様を擬似的に生成する.アルゴリズムが規定されているため,ブラウザごとの差異は見受けられない.この原始フィルター単体で利用されることはまず無く,他のフィルターと組み合わせることで真価を発揮する.firefox,choromeではこのフィルターがかけられた画像にさらなるフィルターをかけようとした際に不具合が発生するケースがあるので注意が必要.赤,緑,青,不透明度それぞれにノイズが設定されているため,適宜feColorMatrixを使ったりしてノイズを抽出する必要があるかもしれない.縦方向と横方向のbaseFrequencyを異なる値にすることで,印象がガラリと変わってくる.効果的に使うにはセンスが問われる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feTurbulance" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="10" seed="3" stitchTiles="noStitch"/>
</filter>
</defs>
<circle filter="url(#feTurbulance)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feTurbulance2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feTurbulence type="fractalNoise" baseFrequency="0.1" numOctaves="10" seed="3" stitchTiles="stitch"/>
</filter>
</defs>
<circle filter="url(#feTurbulance2)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feTurbulance3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feTurbulence type="turbulence" baseFrequency="0.01 0.05" numOctaves="1" seed="3" stitchTiles="noStitch"/>
</filter>
</defs>
<circle filter="url(#feTurbulance3)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
以下はSVG2から導入される原始フィルターである.
グラフィックに影を付けるといった操作は頻繁に行われるがSVG1.1ではこの機能を複数の原始フィルターを組み合わせることで実現していた.SVG2では新たに原始フィルターとして定義されたことにより簡単に影を付けることが可能となっている.もちろん,これまでどおり影を自作することも出来る.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDropShadow">
<feDropShadow/>
</filter>
<filter id="feDropShadow1">
<feDropShadow dx="3" dy="3" stdDeviation="0" flood-color="blue" flood-opacity="0.5"/>
</filter>
</defs>
<g fill="rgba(255,0,0,0.5)" stroke="orange" stroke-width="8">
<circle filter="url(#feDropShadow)" cx="50" cy="50" r="40"/>
<circle filter="url(#feDropShadow1)" cx="150" cy="150" r="40"/>
</g>
</svg>
原始フィルターによる画像効果は原則的に図形の変形を伴わないが,feCustomはこれらとは全く異なり,図形をメッシュ状に区切りそのメッシュを3次元的に変形することで立体感をもたせるものである.この変形には専用のシェーディング言語が用いられ,webglとの互換性が図られるとのことである.SVG2での導入は見送られました.
原始フィルターには光源を必要とするものがある.グラフィックを立体化し,影もしくは反射光を得るために用いられる.光源を表す要素として次の3つが定義されており,原始フィルターの子要素として配置する.またその際の光の色をlighting-color属性で指定することができる.
イメージとしては太陽光を考えると良い.elevationが90°の場合に明るさが最大となり,0°の時に0となる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDistantLight" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="red" surfaceScale="5">
<feDistantLight azimuth="45" elevation="45"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#feDistantLight)"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDistantLight2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="red" surfaceScale="5">
<feDistantLight azimuth="135" elevation="90"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#feDistantLight2)"/>
</svg>
光源の位置から放射状の照明効果を与える.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fePointLight" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="green" surfaceScale="5">
<fePointLight x="50" y="50" z="10"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#fePointLight)"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="fePointLight2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="green" surfaceScale="5">
<fePointLight x="50" y="50" z="100"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#fePointLight2)"/>
</svg>
光源を与えて任意方向に照明効果を与える.照明の当たる角度と光の広がりを設定する.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feSpotLight" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="blue" surfaceScale="5">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="1"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#feSpotLight)"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feSpotLight2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="blue" surfaceScale="5">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="30"/>
</feDiffuseLighting>
</filter>
</defs>
<circle cx="100" cy="100" r="100" fill="white" filter="url(#feSpotLight2)"/>
</svg>
下記に示すfeDiffuseLighting,feSpecularLighting原始フィルターは与えられたグラフィックの内容を立体とみなし,光源からその立体に光を当てた結果を求める.その際立体化の基準としてグラフィックの不透明度が用いられる.不透明度が1の部分をsurfaceScaleの分だけ盛り上がっているものと見做し,その他の部分はその不透明度の割合に応じた高さを持つものとして扱われる.また,diffuseConstantでは光を当てた際の拡散度合いを設定する.通常全く光を拡散しない(つまり光を吸収してしまう)物体は目に見えないため,この値を0に設定すると影しか得られない.
なおこれらの処理はベクタデータのままでは難しいため,何らかの基準点をとりその点に対して個別に影・反射の計算を行うこととなる.kernelUnitLengthではこの計算の緻密さを設定する.フィルター処理を行う際のfilterResを更に指定したサイズで分割し,この分割結果に対して影の計算を行う.従って小さい値を設定するとより良い結果が得られるように思えるが,計算量が増大する割に実際にはそれほど良い結果は得られない.
※試したわけではないが,1px≠1画素でない環境(retinaディスプレイ環境等)では思うような効果が得られない気がする.filterRes属性とkernelUnitLength属性とを適切に設定することで回避可能かもしれない.
また,得られる結果は元画像の色にはよらず,lighting-colorにのみ依存する.
不透明な箇所が盛り上がっているものとして扱い,影画像として取得する.feSpecularLightingの逆.
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDiffuseLighting" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="blue" surfaceScale="1" diffuseConstant="3">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="30"/>
</feDiffuseLighting>
</filter>
</defs>
<circle filter="url(#feDiffuseLighting)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feDiffuseLighting2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feDiffuseLighting lighting-color="blue" surfaceScale="1" diffuseConstant="1">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="30"/>
</feDiffuseLighting>
</filter>
</defs>
<circle filter="url(#feDiffuseLighting2)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
光が当たっている部分をlighting-colorの透明度で得る.feDiffuseLightingの逆
<svg viewBox="0 0 200 200">
<defs>
<filter id="feSpecularLighting" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feSpecularLighting lighting-color="blue" surfaceScale="5" specularExponent="1" specularConstant="3">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="30"/>
</feSpecularLighting>
</filter>
</defs>
<circle filter="url(#feSpecularLighting)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="feSpecularLighting2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feSpecularLighting lighting-color="blue" surfaceScale="5" specularExponent="5" specularConstant="3">
<feSpotLight x="0" y="0" z="50" pointsAtX="75" pointsAtY="75" pointsAtZ="0" limitingConeAngle="20" specularExponent="30"/>
</feSpecularLighting>
</filter>
</defs>
<circle filter="url(#feSpecularLighting2)" cx="100" cy="100" r="60" fill="red" stroke="orange" stroke-width="25"/>
</svg>
このフィルターはfeGaussianBlurと組み合わせ,図形に立体感を持たせる常套手段として用いられる.
<svg viewBox="0 0 200 200">
<defs>
<filter id="tube" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<!--元画像の外形をぼかす-->
<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur"/>
<!--光の反射画像を取得する-->
<feSpecularLighting in="blur" lighting-color="white" surfaceScale="5" result="light" specularConstant="1">
<feDistantLight azimuth="-135" elevation="45"/>
</feSpecularLighting>
<!--画像を重ねる-->
<feMerge result="merge">
<feMergeNode in="SourceGraphic"/>
<feMergeNode in="light"/>
</feMerge>
<!--画像を元の画像の形で繰り抜く-->
<feComposite in="merge" in2="SourceGraphic" operator="in"/>
</filter>
</defs>
<text filter="url(#tube)" x="100" y="125" text-anchor="middle" font-size="100" font-weight="bold">SVG</text>
</svg>
原始フィルターを使いこなすにはそれなりに熟練が必要だが,無理に自作しなくてもサンプルを元にパラメータを弄るだけでもそれなりの効果を得られる.またsvgドローイングツールのinkscapeでは様々な応用フィルターが定義されているため,それらをそのまま利用してしまうのも手だ.下の例はinkscapeの「つや消しゼリー」フィルタを使ったもので,このようなフィルターのサンプルが多数収録されており,気に入ったものを自作のsvgに組み込むだけでも見栄えがする.
<svg viewBox="0 0 200 200">
<defs>
<filter x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB" id="filter3961">
<feColorMatrix in="SourceGraphic" result="result0" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.85 0" id="feColorMatrix3963"/>
<feGaussianBlur stdDeviation="7" in="SourceAlpha" id="feGaussianBlur3965"/>
<feSpecularLighting result="result1" specularExponent="25" specularConstant="0.89999998" surfaceScale="5" id="feSpecularLighting3967">
<feDistantLight azimuth="225" elevation="60" id="feDistantLight3969"/>
</feSpecularLighting>
<feComposite in2="result0" operator="atop" id="feComposite3971"/>
</filter>
</defs>
<circle filter="url(#filter3961)" cx="100" cy="100" r="80" fill="red"/>
</svg>
このような汎用的なフィルターについては,外部svgファイルに定義しておくことで,他のsvg画像に効果を流用できるのでおすすめである.
それでも自作に拘るなら,Hands On: SVG Filter Effectsを活用すると良い.このページでは限定的ではあるが原始フィルターを動的にかけることが出来る.非常に分かりやすいので試してみよう.
2つの画像をfilterを使って合成する場合,次の3つの方法が考えられる.が,ブラウザ毎にサポート状況が異なるので憶えておくと良い.
何れにせよどこかしらに問題が発生するため,適宜switch要素などを用いてブラウザ毎に内容を切り替えると良いだろう.
<svg viewBox="0 0 200 200">
<defs>
<filter id="fi_1" filterUnits="userSpaceOnUse" x="0" y="0" width="400" height="200">
<feOffset x="0" y="0" width="200" height="200" dx="-200"/>
<feBlend in2="SourceGraphic" mode="screen"/>
</filter>
</defs>
<g filter="url(#fi_1)">
<circle cx="80" cy="80" r="60" fill="blue"/>
<circle cx="320" cy="120" r="60" fill="red"/>
</g>
</svg>
<svg viewBox="0 0 200 200">
<defs>
<filter id="fi_2" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feImage xlink:href="#fi_2_c"/>
<feBlend in2="SourceGraphic" mode="screen"/>
</filter>
<circle id="fi_2_c" cx="120" cy="120" r="60" fill="red"/>
</defs>
<circle cx="80" cy="80" r="60" fill="blue" filter="url(#fi_2)"/>
</svg>
<svg viewBox="0 0 200 200" enable-background="new">
<defs>
<filter id="fi_3" filterUnits="userSpaceOnUse" x="0" y="0" width="200" height="200">
<feBlend in="BackgroundImage" in2="SourceGraphic" mode="screen"/>
</filter>
</defs>
<circle cx="120" cy="120" r="60" fill="red"/>
<circle cx="80" cy="80" r="60" fill="blue" filter="url(#fi_3)"/>
</svg>
firefoxではsvg内で定義したフィルター効果はhtmlの要素にかけることも出来る.cssのfilter属性にsvgで定義したフィルターを設定する.下記のdiv要素にはフィルターをかけているので内部の文字がぼかされている.他のブラウザではforeignObject要素を使うと似たような処理ができるかもしれない.
なおfilter属性については現在htmlへの導入が検討されており,chrome等のwebkit系のブラウザではFilter Effects 1.0の一部を先行実装している.機能はsvgのものを追随しており,よく使うフィルターに関数名を付けてより使いやすくしたものとなっている.なおhtml要素専用であるため,svgの図形要素に適用することはできない.以下にfilterキーワードと対応するフィルターの定義の対比を示す.現状仕様と実装の両方がブレているようなので,あくまでfilter実装の参考程度に.
grayscale |
グレイスケール 定義: <feColorMatrix type="matrix" values="(0.2126 + 0.7874 * [1 - amount]) (0.7152 - 0.7152 * [1 - amount]) (0.0722 - 0.0722 * [1 - amount]) 0 0 (0.2126 - 0.2126 * [1 - amount]) (0.7152 + 0.2848 * [1 - amount]) (0.0722 - 0.0722 * [1 - amount]) 0 0 (0.2126 - 0.2126 * [1 - amount]) (0.7152 - 0.7152 * [1 - amount]) (0.0722 + 0.9278 * [1 - amount]) 0 0 0 0 0 1 0"/> |
<svg viewBox="0 0 200 200"> | |
sepia |
セピア 定義: <feColorMatrix type="matrix" values="(0.393 + 0.607 * [1 - amount]) (0.769 - 0.769 * [1 - amount]) (0.189 - 0.189 * [1 - amount]) 0 0 (0.349 - 0.349 * [1 - amount]) (0.686 + 0.314 * [1 - amount]) (0.168 - 0.168 * [1 - amount]) 0 0 (0.272 - 0.272 * [1 - amount]) (0.534 - 0.534 * [1 - amount]) (0.131 + 0.869 * [1 - amount]) 0 0 0 0 0 1 0"/> |
<svg viewBox="0 0 200 200"> | |
saturate |
彩度変換 定義: <feColorMatrix type="saturate" values="(1 - [amount])"/> |
<svg viewBox="0 0 200 200"> | |
hue-rotate |
色相回転 定義: <feColorMatrix type="hueRotate" values="[angle]"/> |
<svg viewBox="0 0 200 200"> | |
invert |
色の反転 定義: <feComponentTransfer> <feFuncR type="table" tableValues="[amount] (1 - [amount])"/> <feFuncG type="table" tableValues="[amount] (1 - [amount])"/> <feFuncB type="table" tableValues="[amount] (1 - [amount])"/> </feComponentTransfer> |
<svg viewBox="0 0 200 200"> | |
opacity |
不透明度 定義: <feComponentTransfer> <feFuncA type="table" tableValues="0 [amount]"/> </feComponentTransfer> opacity属性と何が異なるのか… |
<svg viewBox="0 0 200 200"> | |
brightness |
明度 定義: <feComponentTransfer> <feFuncR type="linear" slope="[amount]"/> <feFuncG type="linear" slope="[amount]"/> <feFuncB type="linear" slope="[amount]"/> </feComponentTransfer> |
<svg viewBox="0 0 200 200"> | |
contrast |
コントラスト 定義: <feComponentTransfer> <feFuncR type="linear" slope="[amount]" intercept="-(0.5 * [amount] + 0.5)"/> <feFuncG type="linear" slope="[amount]" intercept="-(0.5 * [amount] + 0.5)"/> <feFuncB type="linear" slope="[amount]" intercept="-(0.5 * [amount] + 0.5)"/> </feComponentTransfer> コレは間違いの気がする. <feComponentTransfer> <feFuncR type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5)"/> <feFuncG type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5)"/> <feFuncB type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5)"/> </feComponentTransfer> ではなかろうか? |
<svg viewBox="0 0 200 200"> | |
blur |
ぼかし 定義: <feGaussianBlur stdDeviation="[radius radius]"> |
<svg viewBox="0 0 200 200"> | |
drop-shadow |
影(color offset-x offset-y radius) ドロップシャドウは頻出のテクニックなので,半ば憶えてしまっても良い. (1)SourceAlphaをぼかす.(2)ぼかしたものをずらす.(3)影→元画像(SourceGraphic)の順に重ねる. 定義: <feGaussianBlur in="[alpha-channel-of-input]" stdDeviation="[radius]"/> <feOffset dx="[offset-x]" dy="[offset-y]" result="offsetblur"/> <feFlood flood-color="[color]"/> <feComposite in2="offsetblur" operator="in"/> <feMerge> <feMergeNode/> <feMergeNode in="input-image"/> </feMerge> htmlではわざわざこんなことをしないでもbox-shadowで十分な気もするが… |
<svg viewBox="0 0 200 200"> |