SVGのfeMorphology:パスを増やさず輪郭を付ける
feMorphologyとSourceAlphaでSVGに輪郭を付けます。元のパス形状を維持したまま、膨張、収縮、穴、縦横で異なる半径を比較します。
目次
多色SVG全体のシルエットに輪郭を付けるには、SourceAlpha を膨張させ、その大きな結果から元のアルファを除き、残った輪に色を付け、元の図形を上に重ねます。フィルターは描画結果の見た目を変えますが、元のパスデータはそのままです。
アルファのシルエットを小さくしたい場合は、operator="erode" を使います。どちらの操作も、編集可能なオフセットパスを作るものではありません。モルフォロジー処理は円形のストロークオフセットではなく長方形の近傍を使うため、穴、狭い隙間、鋭い角は目視で確認してください。
色チャンネルではなくシルエットから始める
W3CのFilter Effects仕様では、膨張を長方形の近傍内における成分ごとの最大値、収縮を最小値として定義しています。SourceAlpha を使うと、入力は図形のアルファチャンネルになります。青緑色と珊瑚色のチャンネルを別々に広げずに、シルエットを取り出せます。
この例では、2つのパスからなる図形全体にフィルターを適用します。透明な正方形の穴もアルファのシルエットの一部です。珊瑚色の部分はすでに不透明な青緑色の領域内にあるため、独立した外側の境界を作りません。
私たちはSVGを手書きし、独立した5種類のファイルをブラウザーで開きました。どのファイルにも同じ2つの元のパス定義を残しています。以下はそれらの実際の描画結果で、トレースした図形や生成された効果のプレビューではありません。

半径 10 では、輪郭が見えるシルエットの外側と穴の内側に広がります。22 では、この例の正方形の開口部を金色が埋めます。18 4 の場合は縦方向より横方向に大きく広がります。収縮は不透明な外側の領域を小さくし、透明な開口部を広げます。下の尖った部分も変わります。
これらはこの図形と設定での観察結果です。他のロゴやアイコンにも適用できる一般的な安全半径の規則ではありません。
外側のアルファの輪を作る
次の完全な例を outline.svg として保存し、ブラウザーで開いてください。画像のキャプチャに使った半径10のSVGそのものです。
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="220" viewBox="0 0 300 220">
<defs>
<g id="artwork">
<path fill="#173b40" fill-rule="evenodd"
d="M50 60H250V170H170L150 195L130 170H50Z M105 90H145V130H105Z"/>
<path fill="#f28567" d="M175 90H220V140H175Z"/>
</g>
<filter id="outline" filterUnits="userSpaceOnUse"
primitiveUnits="userSpaceOnUse" x="0" y="0" width="300" height="220">
<feMorphology in="SourceAlpha" operator="dilate" radius="10" result="grown"/>
<feComposite in="grown" in2="SourceAlpha" operator="out" result="ring"/>
<feFlood flood-color="#e4b84c" result="paint"/>
<feComposite in="paint" in2="ring" operator="in" result="coloredRing"/>
<feMerge>
<feMergeNode in="coloredRing"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<use href="#artwork" filter="url(#outline)"/>
</svg>フィルターは4つの工程で動作します。
feMorphologyがSourceAlphaを膨張させてgrownを作ります。feCompositeが元のアルファの外側にあるgrownを残し、ringを作ります。feFloodが金色を供給し、もう1つの合成でその色を輪の部分に限定します。feMergeが元のSourceGraphicを色付きの輪の上に置きます。
差し引きでは入力の順序が重要です。仕様の合成に関する節では、in をソース、in2 を宛先として扱います。ここでは in="grown"、in2="SourceAlpha"、operator="out" により、膨張したアルファから元の不透明な領域を除きます。入力を逆にしても、この外側の輪は得られません。
ここでいう「外側」は、透明な穴の内部も含めた、不透明なアルファ領域の外側です。輪郭を内側の開口部に付けたくない場合、このフィルターが自動で区別してくれるわけではありません。別のシルエットか、意図する外側を定義する別の構成が必要です。
変更していない SourceGraphic を最後に合成するので、元の色が見えたままになります。これは通常のパスのストロークとは別の処理です。中央配置、内側へのクリップ、外側へのマスクを使うストロークはSVGのストローク配置を参照してください。描画順序では、より単純なストロークと塗りの重なりを説明しています。
アルファを収縮させ、残った色を維持する
輪郭のフィルターを次の定義に置き換え、最後のuseを filter="url(#erode)" に変えます。
<filter id="erode" filterUnits="userSpaceOnUse"
primitiveUnits="userSpaceOnUse" x="0" y="0" width="300" height="220">
<feMorphology in="SourceAlpha" operator="erode" radius="10" result="shrunken"/>
<feComposite in="SourceGraphic" in2="shrunken" operator="in"/>
</filter>この合成は、収縮したアルファが残る場所だけに元の図形を保持します。元の図形全体を上に戻して重ねると、意図した細まりが隠れるため、そうはしません。この例では外側の青緑色の領域が小さくなり、その中の珊瑚色の領域は残ります。
実際に使う例ではoperatorを明示してください。operatorのリファレンスでは、feMorphology の erode と dilate を、feComposite の in と out から区別しています。同じ属性名でも、2つの要素で役割が異なります。モルフォロジー処理の既定のoperatorは erode なので、省略すると意図した膨張とは反対の効果になることがあります。
半径の単位とフィルター領域を区別する
radiusのリファレンスでは、1つまたは2つの数値を指定できます。1つなら両軸に適用され、2つなら最初がx方向、次がy方向の半径です。追加の2種類の輪郭を作るには、完全な例の radius="10" だけを radius="22" または radius="18 4" に変えてください。
これらは primitiveUnits が選ぶ座標系での半径であり、どの表示サイズでも固定のCSSピクセル幅になるという意味ではありません。ここでは 0 0 300 220 のviewBoxと primitiveUnits="userSpaceOnUse" を明示しています。SVGの表示サイズを変えると、見た目も図形とともに拡大縮小します。
似た名前の2つの属性は、別のものを制御します。
| 属性 | 制御するもの | この例の設定 |
|---|---|---|
| primitiveUnits | モルフォロジーの半径など、プリミティブ内の長さの値 | userSpaceOnUse |
| filterUnits | フィルター領域のx、y、width、height | userSpaceOnUse |
| ルートのviewBox | SVGビューポートに対応するユーザー座標の長方形 | 0 0 300 220 |
MDNのprimitiveUnitsリファレンスは、プリミティブの座標系を説明しています。filterUnitsのリファレンスは、外側の領域を別に説明しています。filterUnits の既定値は objectBoundingBox、primitiveUnits の既定値は userSpaceOnUse です。両方を明示すると、例を確認しやすくなります。
元の図形はx=50〜250、y=60〜195を占めます。検証した縦横同じ半径の最大値は22で、300 × 220 のフィルター領域とビューポートには、その結果を収める余裕があります。図形や半径を変えたら、両方の境界を再確認してください。仕様ではフィルター領域を厳密なクリップ境界としています。半径を増やしても領域の外側は描画できません。SVGのフィルターとビューポートのクリッピングガイドでは、この2つの別々の制限を示しています。
フィルターが効いていないようなら、radius も確認してください。既定値は0で、リファレンスでは負値または0でプリミティブの効果が無効になるとしています。上の例では、両軸に正の値を明示しています。
納品前に穴と角を確認する
モルフォロジー処理の長方形の近傍により、一般的な丸い輪郭とは見た目が異なります。xとyの半径を同じにしても、近傍が円になるわけではありません。比較画像の下側の段差が付いた尖りは、違いを確認しやすい箇所です。
| 結果 | 使用前の確認事項 |
|---|---|
| 大きくなった外側の輪郭 | 開口部や近くの形状が見た目上つながる場合があります |
| 収縮したシルエット | 細い部分が消えたり、穴が大きくなったりする場合があります |
| 縦横で異なる半径 | 横方向と縦方向の広がりが意図的に異なります |
| 正しいブラウザープレビュー | 受け取り側の編集・書き出しツールでも個別のテストが必要です |
| 編集可能な形状として必要な輪郭 | ライブフィルターの結果は元データに新しい輪郭を作りません |
5つのファイルすべてに、同じ d 属性を持つ2つの元のパスが残っていることを確認しました。フィルター定義は異なりますが、元の形状は同じです。編集、オフセット、生産への送信に使うパスが必要な場合は、実際のSVGオブジェクトを確認してください。その要件には編集ソフトの形状操作を使い、作成された輪郭を確認します。このフィルターを変更したり削除したりするだけでは、そうした輪郭は得られません。
フィルターなしのマスターと、別の納品用コピーを保持してください。納品ファイルを実際の利用先で開き、角、内側の開口部、色、境界を比較します。このブラウザーでの例は、特定のデザインアプリ、書き出しツール、カッティングマシンでの対応を保証するものではありません。
効果を付ける前に元データを準備する
図形がPNGやJPGでしか存在しない場合は、PerfectVectorを利用できます。SVGの候補を作成し、フィルターを適用する前に、復元された元のシルエット、小さな穴、色の境界を確認してください。モルフォロジーの輪郭は、そのシルエットの問題を増幅させるもので、意図した形状を代わりに修復するものではありません。
ベクターのマスターがあれば、そのまま使ってください。単純な記号なら手で描き直した方が制御しやすい場合もあります。輪郭が焼き込まれた統合画像をトレースしても、元のフィルター設定を復元したり、どの部分がフィルター前の形状だったかを確定したりはできません。画像ベクター化の概要では、その再構築の限界を説明しています。
よくある質問
feMorphologyは編集可能な輪郭パスを作りますか? いいえ。変更するのはフィルターの描画結果です。この例は同じ2つの元のパスを保持しているため、別の輪郭を必要とする利用先には、形状操作と読み込み確認が必要です。
金色の輪郭がSVGの穴を埋めるのはなぜですか? 輪は、透明な開口部も含めた不透明なアルファの外側に広がります。この例では半径22で正方形の穴が埋まります。他の図形では、それぞれ隙間と半径を確認する必要があります。
SourceGraphicではなくSourceAlphaを使う理由は何ですか? SourceAlphaなら、元の各色チャンネルを膨張させずにシルエットを処理できます。この例ではアルファの輪に別途色を付け、SourceGraphicを最後に合成して元の色を表示しています。
参考資料
- W3C — Filter Effects Level 1 — モルフォロジーの長方形の近傍、合成入力、フィルター領域のクリップを定義しています。
- MDN — radius属性 — 1値・2値の半径構文、単位、効果を無効にする値を定義しています。
- MDN — operator属性 — モルフォロジー操作と合成操作を区別しています。
- MDN — primitiveUnits属性 — モルフォロジーの半径の座標系を定めています。
- MDN — filterUnits属性 — 外側のフィルター領域の座標系を定めています。
元データがラスター画像だけの場合は、PerfectVectorでベクター図形を作成し、フィルターなしのシルエットと開口部を検証してください。その後で輪郭を加え、最終ファイルを受け取り側のビューアーで比較します。

