【SVG制御妄想２】Mohoから出力したSVGのマスクがバグる

この記事は「続・Mohoから出力したSVGを制御したい妄想の話」の一部です。

■実物
【GitHub】SourceOf0-HTML/path_control: SVGを制御したい願望
https://github.com/SourceOf0-HTML/path_control

【GitHub Pages】ベクターデータをいじり倒したい気持ち
https://sourceof0-html.github.io/path_control/


■記事一覧
【SVG制御妄想１】SVG解析しないと始まらない
https://qiita.com/flying_echidna/items/5a628db0d652d1558208

【SVG制御妄想２】Mohoから出力したSVGのマスクがバグる
https://qiita.com/flying_echidna/items/3930caf04626deec7bfb

【SVG制御妄想３】連番データをどげんかせんと
https://qiita.com/flying_echidna/items/ded3f3590c3d67fadb86

【SVG制御妄想４】変形させたいよなぁ？
https://qiita.com/flying_echidna/items/188634f35a05bbde9a51

【SVG制御妄想５】ボーンぐりぐり
https://qiita.com/flying_echidna/items/a34648da8a650fe34824

【SVG制御妄想６】助けてマルチスレッド
https://qiita.com/flying_echidna/items/80b101c1a1eedb534137

【SVG制御妄想７】SVGの限界
https://qiita.com/flying_echidna/items/2f53a461c5e6c05109df


■過去記事
【2019-03-06】「SVGでアニメーションさせたいんじゃ」の詳細報告
https://qiita.com/flying_echidna/items/ff3a061f4e348e62cca0

【2020-02-13】Mohoから出力したSVGを制御したい妄想の話
https://qiita.com/flying_echidna/items/da7ecc721650fa9ab651


#マスクが効かない

ブロマガにて愚痴っていた内容…

【2019-03-06】「SVGでアニメーションさせたいんじゃ」の詳細報告:変人のブロマガ - ブロマガ
https://ch.nicovideo.jp/FlyingEchidna/blomaga/ar1708679

> いや、それより一番の問題は…
> 実際にsvgを表示したときに、設定していた一部のマスクが効いてない。
> 詳しく差分データを用意してタグを解読していくと…
> どうやら、マスク設定を有効にしたグループ内にグループを入れ子で置いた場合、マスク設定が出力されない模様。
> mp4で出力したときは問題なかったし、Moho側のバグっぽい。
> 
> オマケにタグをよく見ると、表示しているパスをそのままマスクとしても流用する設定を入れていた場合、内容が全く同じパスを表示用とマスク用として出力していることが発覚。
> どうにかならんかとsvgの仕様を調べたところ、useタグを使うことでパス情報の使いまわしができることが発覚。
> どうにかしてuseタグに移行させたい。

これの詳細の話をしようと思う。
実際の処理のコードはこっち。

【GitHub】path_control/addMaskTag.rb at master ・ SourceOf0-HTML/path_control
https://github.com/SourceOf0-HTML/path_control/blob/master/convert/addMaskTag.rb

実際に見てもらった方が早いと思うので、比較画像。

![diff.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542630/9393d2b8-13ba-3e29-ee02-83d390d7d56f.jpeg)

左が理想。
右がすべてのマスクをオフにした状態。
真ん中が問題の、Mohoから出力したSVG。
グループを入れ子にしてるマスクだけ吹っ飛んでる状態。

瞳は白目を使ってマスクをかけてるだけのもので、真ん中でもマスクが効いてる。
ところが、その目も含めて顔でマスクをかけてる部分については、マスクされずに顔の外まではみ出てる。

結局どうしたかというと…
レイヤー名がgタグのidに付与されるのを使って、吹っ飛ぶマスク設定部分だけ、レイヤー名に特殊な名前をつけるようにした。
マスクする側にはMask、マスクされる側にはMtarget、と。
で、SVGを圧縮する処理を走らせる際に、レイヤー名から判別して、マスクを別途追加するようにした。


#マスクのタグに無駄が多い

どう無駄が多いのか。
圧縮する前のSVGを見てみる。

【GitHub】SVGTest/walk_00001.svg at master ・ SourceOf0-HTML/SVGTest
https://github.com/SourceOf0-HTML/SVGTest/blob/master/source/walk_00001.svg?short_path=4404bb7

ちゃんとマスクされてる目のタグのところをピックアップ。
219行目から。

```xml:source/walk_00001.svg
<clipPath id="f3906406-f197-4bf1-91d8-744c09cca836">
<path fill="#000000" fill-rule="evenodd" stroke="none" d="M 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 M 210.165 176.623 C 211.081 175.131 212.391 172.882 212.175 171.145 C 211.933 169.202 210.395 166.083 208.428 165.800 C 203.091 164.909 184.365 158.426 177.618 155.804 C 170.880 153.351 157.455 144.948 153.049 151.785 C 151.399 154.224 155.260 154.951 156.543 158.850 C 162.789 177.829 168.232 195.513 188.783 196.850 C 206.539 198.006 205.542 184.146 210.165 176.623 M 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 M 284.226 169.324 C 287.359 157.305 288.051 141.177 285.723 140.096 C 280.772 137.798 267.953 146.931 260.489 150.219 C 250.708 154.528 235.129 163.680 228.414 165.724 C 226.469 166.393 224.717 166.914 224.435 168.309 C 223.874 171.078 229.112 172.972 230.872 175.183 C 233.988 179.098 234.860 186.175 240.048 189.113 C 249.844 195.067 257.344 192.926 268.283 188.147 C 276.819 184.458 282.013 177.815 284.226 169.324 Z"/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 "/>
<path fill="#ffffff" fill-rule="evenodd" stroke="none" d="M 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 Z"/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 "/>
<path fill="#ffffff" fill-rule="evenodd" stroke="none" d="M 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 Z"/>
</clipPath>
<path fill="#000000" fill-rule="evenodd" stroke="none" d="M 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 M 210.165 176.623 C 211.081 175.131 212.391 172.882 212.175 171.145 C 211.933 169.202 210.395 166.083 208.428 165.800 C 203.091 164.909 184.365 158.426 177.618 155.804 C 170.880 153.351 157.455 144.948 153.049 151.785 C 151.399 154.224 155.260 154.951 156.543 158.850 C 162.789 177.829 168.232 195.513 188.783 196.850 C 206.539 198.006 205.542 184.146 210.165 176.623 M 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 M 284.226 169.324 C 287.359 157.305 288.051 141.177 285.723 140.096 C 280.772 137.798 267.953 146.931 260.489 150.219 C 250.708 154.528 235.129 163.680 228.414 165.724 C 226.469 166.393 224.717 166.914 224.435 168.309 C 223.874 171.078 229.112 172.972 230.872 175.183 C 233.988 179.098 234.860 186.175 240.048 189.113 C 249.844 195.067 257.344 192.926 268.283 188.147 C 276.819 184.458 282.013 177.815 284.226 169.324 Z"/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 "/>
<path fill="#ffffff" fill-rule="evenodd" stroke="none" d="M 276.700 149.631 C 280.176 150.210 278.361 153.272 278.239 162.899 C 278.057 177.260 265.602 187.609 252.666 187.143 C 245.579 186.888 236.313 181.345 236.548 170.297 C 236.619 166.966 250.171 161.921 256.261 158.791 C 262.238 155.720 273.624 149.491 276.700 149.631 Z"/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 "/>
<path fill="none" stroke="#ffffff" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" d="M 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 "/>
<path fill="#ffffff" fill-rule="evenodd" stroke="none" d="M 167.137 158.547 C 171.784 160.147 203.026 168.738 203.564 173.431 C 205.046 186.366 196.487 194.701 183.589 191.051 C 176.129 188.718 172.848 183.154 170.296 176.403 C 168.786 172.847 161.441 156.587 167.137 158.547 Z"/>
<path fill="#000000" fill-rule="evenodd" stroke="none" d="M 242.489 163.522 C 244.771 162.991 247.936 166.180 248.650 174.600 C 249.188 180.956 247.453 183.530 245.693 183.700 C 243.790 183.883 242.190 181.264 241.026 174.466 C 239.784 167.211 240.502 163.985 242.489 163.522 Z" clip-path="url(#f3906406-f197-4bf1-91d8-744c09cca836)"/>
<path fill="#000000" fill-rule="evenodd" stroke="none" d="M 195.457 165.675 C 197.804 165.415 199.549 169.292 199.954 178.079 C 200.184 183.078 199.601 185.103 198.290 185.399 C 196.728 185.753 194.643 183.765 193.337 177.940 C 191.522 169.843 193.244 165.920 195.457 165.675 Z" clip-path="url(#f3906406-f197-4bf1-91d8-744c09cca836)"/>
```

引用した下2行の末尾に
`clip-path="url(#f3906406-f197-4bf1-91d8-744c09cca836)`
とあるけども、これがマスクの参照設定。
で、この参照先のidを持つマスク本体が1行目からのclipPathにある。

…んだけども！
よくよく見ると、clipPathタグでくくられているpathタグ群と、まったく同じ内容が下に続いている。
マスクは表示されないものなので、マスクと同じ形のものを表示する場合は、まったく同じタグを出力すればいい、という発想なんだろう。
とはいえMoho上で設定するマスクにはいくつか種類があり…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542630/59c0a4df-0e0d-a525-1339-c35c7efffe60.png)

パッと見からしてカオス。各々の効果の説明はさすがに省略するけども…
一般的なマスクの通常の考え方は、この中の「マスクを追加するが、不可視にする」にあたると思う。
マスクとして用意されたものは、マスクをかける範囲指定しかなく、マスク本体は不可視なのは当然でしょ？と。

ところが「マスクを追加」は、マスクをかける範囲指定としても使いつつ、表示する設定にあたる。
今であれば、白目の部分は白目として表示しつつ、瞳に対するマスクでもある、という状態。
内部的にやっていることは、マスク用のデータと、表示用のデータを、この設定１つで生成してしまってるわけだ。
便利ではある。
しかし「マスクを追加」を使う限り、こういう同じタグの生成は避けられないことになってしまう。

バカ言え、そのためにどんだけ文字数出力しとるんじゃ。

ということで、ここでuseタグの登場。
指定したidの要素を参照し、同じパスがそこにある前提で、扱ってくれる代物。

use - SVG: Scalable Vector Graphics | MDN
https://developer.mozilla.org/ja/docs/Web/SVG/Element/use

ただし、これはclipPathタグの中では使用できない。
clipPathタグの中にはちゃんと表示させるパスを直接記載しなきゃいけない仕様の模様。
maskタグというのも存在していて、そっちであればuseタグが使える。
設定もこっちの方が融通が利く。
ということで、実際にmaskタグとuseタグを使って圧縮したのがこれ。

【GitHub】SVGTest/walk_00001.svg at master ・ SourceOf0-HTML/SVGTest
https://github.com/SourceOf0-HTML/SVGTest/blob/master/destination/walk_00001.svg?short_path=a6ac2ec

219行目から。

```xml:destination/walk_00001.svg
<mask style="mask-type:alpha;" id="f3906406-f197-4bf1-91d8-744c09cca836">
<use xlink:href="#cmn_f3906406-f197-4bf1-91d8-744c09cca836"/>
</mask>
<g id="cmn_f3906406-f197-4bf1-91d8-744c09cca836">
<path class="path-0" d="M203.564,173.431C205.046,186.366,196.487,194.701,183.589,191.051C176.129,188.718,172.848,183.154,170.296,176.403C168.786,172.847,161.441,156.587,167.137,158.547C171.784,160.147,203.026,168.738,203.564,173.431M210.165,176.623C211.081,175.131,212.391,172.882,212.175,171.145C211.933,169.202,210.395,166.083,208.428,165.800C203.091,164.909,184.365,158.426,177.618,155.804C170.880,153.351,157.455,144.948,153.049,151.785C151.399,154.224,155.260,154.951,156.543,158.850C162.789,177.829,168.232,195.513,188.783,196.850C206.539,198.006,205.542,184.146,210.165,176.623M252.666,187.143C245.579,186.888,236.313,181.345,236.548,170.297C236.619,166.966,250.171,161.921,256.261,158.791C262.238,155.720,273.624,149.491,276.700,149.631C280.176,150.210,278.361,153.272,278.239,162.899C278.057,177.260,265.602,187.609,252.666,187.143M284.226,169.324C287.359,157.305,288.051,141.177,285.723,140.096C280.772,137.798,267.953,146.931,260.489,150.219C250.708,154.528,235.129,163.680,228.414,165.724C226.469,166.393,224.717,166.914,224.435,168.309C223.874,171.078,229.112,172.972,230.872,175.183C233.988,179.098,234.860,186.175,240.048,189.113C249.844,195.067,257.344,192.926,268.283,188.147C276.819,184.458,282.013,177.815,284.226,169.324Z"/>
<path class="path-17" d="M276.700,149.631C280.176,150.210,278.361,153.272,278.239,162.899"/>
<path class="path-17" d="M278.239,162.899C278.057,177.260,265.602,187.609,252.666,187.143"/>
<path class="path-17" d="M252.666,187.143C245.579,186.888,236.313,181.345,236.548,170.297"/>
<path class="path-17" d="M236.548,170.297C236.619,166.966,250.171,161.921,256.261,158.791"/>
<path class="path-17" d="M256.261,158.791C262.238,155.720,273.624,149.491,276.700,149.631"/>
<path class="path-18" d="M276.700,149.631C280.176,150.210,278.361,153.272,278.239,162.899C278.057,177.260,265.602,187.609,252.666,187.143C245.579,186.888,236.313,181.345,236.548,170.297C236.619,166.966,250.171,161.921,256.261,158.791C262.238,155.720,273.624,149.491,276.700,149.631Z"/>
<path class="path-17" d="M167.137,158.547C171.784,160.147,203.026,168.738,203.564,173.431"/>
<path class="path-17" d="M203.564,173.431C205.046,186.366,196.487,194.701,183.589,191.051"/>
<path class="path-17" d="M183.589,191.051C176.129,188.718,172.848,183.154,170.296,176.403"/>
<path class="path-17" d="M170.296,176.403C168.786,172.847,161.441,156.587,167.137,158.547"/>
<path class="path-18" d="M167.137,158.547C171.784,160.147,203.026,168.738,203.564,173.431C205.046,186.366,196.487,194.701,183.589,191.051C176.129,188.718,172.848,183.154,170.296,176.403C168.786,172.847,161.441,156.587,167.137,158.547Z"/>
</g>
<path class="path-0" d="M242.489,163.522C244.771,162.991,247.936,166.180,248.650,174.600C249.188,180.956,247.453,183.530,245.693,183.700C243.790,183.883,242.190,181.264,241.026,174.466C239.784,167.211,240.502,163.985,242.489,163.522Z" mask="url(#f3906406-f197-4bf1-91d8-744c09cca836)"/>
<path class="path-0" d="M195.457,165.675C197.804,165.415,199.549,169.292,199.954,178.079C200.184,183.078,199.601,185.103,198.290,185.399C196.728,185.753,194.643,183.765,193.337,177.940C191.522,169.843,193.244,165.920,195.457,165.675Z" mask="url(#f3906406-f197-4bf1-91d8-744c09cca836)"/>
</g>
```

あらすっきり。

マスクとしても流用するpathタグ群をgタグでくくって、
idとして既存の
`f3906406-f197-4bf1-91d8-744c09cca836`
に、接頭辞にcmn(commonの略のつもり)を付けたidの
`cmn_f3906406-f197-4bf1-91d8-744c09cca836`
を付与してる。

元clipPathタグの部分をmaskにした上で、中身をuseを使った書き方に差し替え。
で、maskはデフォルトのルミナンスマスク（輝度をマスクの強度扱いする）から
アルファマスク（不透明度をマスクの強度扱いする）にしたいので、
`mask-type:alpha;`を追加で指定してる。


…まだ去年（2019年）の3月までの話ってマジですか？（遠い目

次の記事：【SVG制御妄想３】連番データをどげんかせんと
https://qiita.com/flying_echidna/items/ded3f3590c3d67fadb86