<filter id = "「「5component」」"> <feComponentTransfer"> <feFuncR type = "「「1identity/linear/gamma/discrete/table」」" ../> <feFuncG ../> <feFuncB ../> <feFuncA ../> </feComponentTransfer> </filter>
「「3<!--プレゼンテーション属性-->」」 <rect filter = "url(「「5#component」」)" ../> 「「3/*CSS*/」」 #elm {filter:url(「「5#component」」)}
こんにちは、「ふ」です。
今回はSVGのfeComponentTransferフィルターについて紹介します。
feComponentTransferは色変換をおこなうフィルターで、R/G/B成分およびAlpha(不透明度)の4つを個別に操作することができます。
なお単なる色成分の「上げ下げ」ではなく、関数を設定→その法則に従って入力値を変換する仕組みです。
feComponentTransferは前回のfeMorphologyに比べると、かなり複雑な設定を要します。
今回は前編としてその仕組みと、基本的な関数について紹介していきます。
仕組みからしっかりと理解することで、コピペに留まらない応用力を身につけましょう。
SVGコーディングブック
Apple BooksでみるこのたびSVG本を出版しました。基本から応用まで、SVGの学習に役立ててください。
feComponentTrasferを使っていくにあたり、その色変換の仕組みを知っておきましょう。
PCやスマートフォンの画像は、色の付いたpx(画素)の集まりで表示されています。色付きの四角い「つぶつぶの集まり」と考えても差し支えありません。
その1つ1つのpxは、R(red)/G(green)/B(blue)の3つの色成分の値によって、色が決定されます。
では1つのpxにおいて、R(red)値だけを考えてみましょう。いまこのpxのR値は「50」になっています。
このR値を入力したバヤイ。
ディスプレイの補正機能などが作用していない限り、R=50なのでそのままR=50として出力されます。当たり前ですが。
同様に、R=100が入力されたらR=100として表示されます。R=10なら出力もR=10。
この「あたりまえの関係」は横軸を入力、縦軸を出力とすると、⬆︎のようなグラフで表すことができます。
これを関数式で表すとすると、
out = in
となりますね。
それではinとoutの関係が、⬆︎のようになっているとどうでしょうか。
関数式で表すと、
out = in*2
です。入力したR値を2倍したものが出力のR値となります。
このinとoutの関係を、さきほど抽出したpxに施してみます。
緑のほうはわずかに明るくなっただけですが、青のほうはほぼ紫色になりました。
px単位のR値をいじった結果、はじめにあった緑と青の輪っか画像は⬆︎のように変化します。
各pxの持つR成分の値はそれぞれ違っています。個々の色成分の値を、指定した関数に基づいて変換・出力させるのがfeComponentTransferフィルターです。
ここではR値を使った例を紹介してきましたが、入力-出力の関係はR/G/B値及びA値(不透明度)、それぞれを個別に操作することが可能です。
⬆︎の2つの画像、左はG値の出力を極端に下げたもの。また右はR値の出力を思い切り上げたものです。いづれも元画像は緑と青の輪っかを使っています。
なおfeComponentTransferの指定では、RGB値は0〜1の範囲に置き換えたものを使用します。0〜1を255分割することになるので、例えばG=100だと100/255ということになります。
通常webやCG制作においてはRGB値は0〜255の範囲で扱うことが多いので、少し戸惑うかもしれません。ただfeComponentTransferでは入力と出力の関係を指定するだけなので、
「R = 100だから、100/255は .. 」
といっためんどくさい算出は要求されませんので、ご安心を。
<svg>
<filter id = "「「5component1」」">
<「「1feComponentTransfer」」><「「1/feComponentTransfer」」>
</filter>
</svg>
SVG内に<filter>要素で、フィルターコンテナを定義します。参照用のidは「component1」としておきました。
その中に<feComponentTransfer>要素を配置。ただしこれだけではフィルターを掛けたとしても、何も起きません。この中に更に、R/G/B/Aごとの入力-出力操作を定義していく必要があります。
<filter id = "component1">
<feComponentTransfer>
<「「1feFuncR」」 type = "...."/>
<「「1feFuncG」」 type = "...."/>
<「「1feFuncB」」 type = "...."/>
<「「1feFuncA」」 type = "...."/>
</feComponentTransfer>
</filter>
RGBAそれぞれの出力状態を操作するのが、<feFuncR> <feFuncG> <FeFuncB> <feFuncA> 要素です。これらの要素を <feComponentTransfer> 要素の入れ子にすることで、はじめてフィルター効果を働かせることができます。
<feFuncR type = "「「1linear/identity/gamma/discrate/table」」"/>
feFuncR/G/B/A要素にはtype属性を持っています。
値には5種類が用意されており、入力-出力の関係操作時に使用する、関数のタイプを指定することができます。
・linear :直線を使った一次関数
・identity :入力 = 出力とする恒等変換
・gamma :ガンマ曲線による指定
・discrate :階段(ステップ)関数
・table :折れ線による指定
前編である本記事では基本的なlinear関数、identitiy関数について紹介していきます。その他の関数については後編でお伝えします。
feFunc「「2X」」 type = "linear"
slope = "数値" (初期値:1)
intercept = "数値" (初期値:0)
linear関数は、直線による一次関数です。
入力に対して、n倍に変換されたものが出力となります。
linear関数の傾きはslope属性(数値)で指定します。
初期値は1で、入力を等倍したものが出力に返されます。
直線は必ずしも原点を通るとは限りません。通過しない場合、原点からの垂直距離を切片といいます。
切片の指定はintercept属性(数値)で指定します。intercept属性の初期値は0で、指定がない場合には直線は原点を通過します。
<img src = "sleep.png">
◾️ sleep.png
気持ちよく寝ているところ申し訳ないのですが、この子でlinearを試してみましょう。
G(green)成分の入出力を操作してみます。 直線の傾きを0.5とすると、入力に対して半分の値が出力されるようになります。
<svg style = "position:fixed;">「「1 ..@1@」」
<filter id = "「「5linear01」」">「「1 ..@2@」」
<feComponentTransfer>
<feFuncG type = "linear" slope = "「「10.5」」"/>「「1 ..@3@」」
</feComponentTransfer>
</filter>
</svg>
@1@ <filter> 要素は、<svg> 要素の中に記述する必要があります。ただしHTMLの <body> 内に <svg> を定義した場合、自動的に300*150pxのSVG領域が確保されてしまいます。
そのためposition : fixedとし、HTMLのレイアウトに影響を与えないようにしています。
@2@ filterコンテナを配置。idは「linear01」としました。
@3@ G成分を操作するので、<feFuncG> を記述しています。傾きをslope属性で「0.5」としました。
<img src = "sleep.png" style = "filter:url(「「5#linear01」」)">
<img>要素の中で定義したフィルターを呼び出します。
結果、全体的に赤みが掛かったよう⬆︎になりました。
G(green)成分の傾きを0.5としたので、G値だけが入力に対して半分の出力で描画されたのです。他のR値やB値はそのまま等倍で返されているので、「赤みが強まった」というよりは「緑が弱められた」というのが実際です。
今度は傾きを「2」とし、入力に対し2倍の出力を返すようにしてみます。
<feComponentTransfer>
<feFuncG type = "linear" slope = "「「12」」"/>
</feComponentTransfer>
G成分が入力値に対し、2倍の出力値を返すようになりました。
その結果、緑っ気が強まりましたね。
ところでslopeを「2」としたとき、グラフの矢印の部分はどのように扱われるのでしょうか?
feComponentTransferでは、値が0〜1の範囲からはみ出したときは強制的に0または1に丸められます。なので実際のグラフは⬆︎のようになります。
通常RGBを扱うときにも、0未満や255より大きい値はありえないですもんね。
いまのサンプルで言うと、G成分が0.5以上のpxはG = 1.0として出力されたことになります。
intercept(切片)を指定すると、全体の出力値が「底上げ」されます。
先づはinterceptなしのフィルターを掛けてみます。今回はfeFuncBを使って、B(blue)成分を操作します。
<svg style = "position:fixed;">
<filter id = "linear01">
<feComponentTransfer>
<feFuncB type = "linear"
slope = "「「10.5」」"/>
</feComponentTransfer>
</filter>
</svg>
slopeを「0.5」としたので、青色成分は入力に対し半分の出力値が採用されます。
B(blue)成分が弱くなったため、R(red)成分とG(green)成分が強調されることになります。その結果、全体が黄色に寄せられました。
<feComponentTransfer>
<feFuncB type = "linear"
「「1intercept = "0.5"」」
slope = "0.5"/>
</feComponentTransfer>
ここにinterceptを加えてみます。値は「0.5」としました。
指定した内容をグラフにしたもの⬆︎。
入力値が最小の0であっても、0.5として出力されます。傾き0.5のグラフ全体が「底上げ」される形になります。
結果は予想通り、全体的に青成分が強まりました。
slope(傾き)を大きくしたときよりも顕著です。
slope属性には負の値を指定することも可能です。
傾きを「-1」とし、切片を「0.5」にしてみます。
<feComponentTransfer>
<feFuncB type = "linear"
intercept = "0.5"
slope = 「「1"-1"」」/>
</feComponentTransfer>
青成分が弱いところは青が強調され、青成分が強いところは黄色っぽくなりました。
インパクトある色合いになりましたね。
前述のとおりfeComponentTransferには、<feFuncR> <feFuncR> <FeFuncB> <feFuncA> という4つのチャンネルが用意されています。
<feComponentTransfer>
<feFuncG type = "linear" slope = "1.5"/>
</feComponentTransfer>
ここで<feFuncG>を操作したとします。
そのとき記述していない他の <feFuncR> <FeFuncB> <feFuncA> チャンネルのtype属性には、初期値である「identity」関数が採用されます。
identitiyは「恒等式(つねに等しい式)」という意味を持ちます。
identity関数をグラフにすると原点を通る傾き「1」の直線となり、これはin値に対して全く同じout値を返す状態です。
つまり初期値であるidentityが採用されているチャンネルは「何もしない」ということです。
<feComponentTransfer>
<feFuncR type = identity/>
<feFuncG type = "linear" slope = "1.5"/>
<feFuncB type = identity/>
<feFuncA type = identity/>
</feComponentTransfer>
feFuncG以外のチャンネルを敢えて記述するとしたら、⬆︎のようになります。
<feComponentTransfer>
<feFuncA ..../>
</feComponentTransfer>
feComponentTransferでは、RGBの他にA(Alpha)値を操作することもできます。
ここでは <feFuncA> を使って、A値を操作した場合の特性について紹介していきます。
Alpha値とは各ピクセルの色情報(R/G/B)に加えて、不透明度の情報を持たせたものです。
CSSの色指定でRGBAを使ったことのある方もいるかと思います。最小値は「0」で完全な透明、最大値は「1」で完全な不透明となります。
feComponentTransferではfeFuncAを使うことで、Alpha値の操作が可能です。ただしRGB情報のない部分は透明な黒として扱う、という性質をもっており、使用する際には配慮が必要です。
以下、試していきましょう。
<feFuncA>は不透明度を操作します。
様子を観察しやすいよう、背景色をつけてお送りします。
<filter id = "linear01">
<feComponentTransfer>
<「「1feFuncA」」 type = "linear" slope = "0.5" />
</feComponentTransfer>
</filter>
Aチャンネルに対して傾き「0.5」の変換を施しました。
結果、全体的に半透明になりました。
サンプルに使っている画像。構成しているpxのAlpha値はすべて最大の「1」です。0.5の傾きを指定することにより、すべてのpxのAlpha値が「0.5」として出力されています。
ここで、関数にinterceptを追加してみます。
<filter id = "linear01">
<feComponentTransfer>
<feFuncA type = "linear" slope = "0.5" intercept = "「「10.3」」" />
</feComponentTransfer>
</filter>
!?
画像部分はいいとして、なんだか謎のborderが付いています。
<filter filterUnits = "「「1objectBoundingBox」」">
</filter>
<filter>には、フィルター効果の描画範囲を指定するfilterUnits属性というものがあり、初期値は「objectBoundingBox」となっています。
初期値の場合、要素を囲むボックスの120%の範囲にわたってfilter効果が描画されます。
要素に掛けるfeComponentTransferを、linear関数で傾き0.5、切片0.3としたとき。
画像の部分はすべてのpxがRGB情報を持っており、A値(不透明度)は1になっています。指定された関数によって変換すると1*0.5+0.3 = 0.8。RGB値はそのままで、不透明度0.8で出力されます。
そして外側の10%の部分。ここは透過部分なので、RGBの情報がありません。
RGB情報を持たないpxは、RGBA(0,0,0,0)。つまり透明度0の黒として扱われます。
Alpha値を上げていくと、徐々に不透明な黒で描画されていくようになります。
完全不透明の黒に対して、A値を変換したものが出力されます。
外側10%のRGB情報がない部分はAlpha = 0*0.5+0.3 = 0.3となり、不透明度0.3の黒で描画されてしまっていたのでした。
interceptを使って出力値を底上げする場合は、外側10%の部分も影響を受けてしまうので注意してください。
最後までお読みくださり、ありがとうございました。
今回はfeComponentTransferの前編として、その構造とシンプルなlinear関数を取り上げてきました。これだけでも豊富な色変化が表現できますね。
次回はそれ以外の関数。gamma、table、discrete関数について紹介します。
ではまた〜 🎵
ベクターグラフィック、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。