【追記あり】SVGコードゴルフと,GIZMODO「Google新ロゴ」記事へ反論
<ここから追記:2015-09-10>
当初投稿した内容が、前提から誤った思い込みで書かれておりました、詳しくは後半の追記を参照してください。
</追記ここまで>
はじめに
先日こうしたツイートが話題になりました。
https://twitter.com/thespite/status/639107572679712772
これは先頃リニューアルが発表されたGoogle社のロゴ
Evolving the Google Identity - Library - Google Design
がシンプルな構成に見えることから、それを荒く手軽に作るなら(quick and dirty version)290バイトで作れるよ!という内容です。
リンク先の中身を見てみるとこのような490バイトのSVGファイルになっており
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250"><g stroke-width="16" fill="none"><path d="M173 102a51 51 0 1 1-13-30m20 37h-53" stroke="#4a87ee"/><circle cx="227" cy="128" r="32" stroke="#d83038"/><circle cx="313" cy="128" r="32" stroke="#f4c022"/><path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17" stroke="#4a87ee"/><path stroke="#4ab95a" d="M449 51v115"/><path d="M529 118a30 30 0 1 0-2 24m5-32l-62 28" stroke="#d83038"/></g></svg>
gzip圧縮すると手元の環境では292バイトになりました*1。
なかなか面白い試みではありますが、その一方でこのSVGファイルにはまだまだ無駄が多くファイルサイズは削れるな……とも感じました。というのも、私は2013年にも似たようなことをやっているんですよね。
SVG画像を1バイトでも削るためのコードゴルフ
無駄を省いてなるべく短く記述する、というのはコードゴルフと呼ばれる遊びです。
ただSVGではどのようにすると短く書けるのか?といったノウハウはまだまだ知られていないと思うのでそうしたSVGゴルフのテクニック紹介と、後半ではこのことを取り上げたGIZMODOの記事が本当に酷かったのでそれへの検証と反論を行っていきます。
簡易目次
- コードゴルフとは
- 今回のコードゴルフのルール
- コードゴルフ実践編:ソースコードを短くする
- 圧縮アルゴリズム最適化
- SVGコードゴルフのまとめと最適化ツール
- GIZMODOの記事について
- ちなみにGoogle公式のSVGファイルは
コードゴルフとは
まずコードゴルフとはなにか?の説明から。
コードゴルフとは任意のソースコードを出来るだけ短く書くことを目的にしたものです。そのコードの動作を損なわなず、かつ可能な限り短く記述できる手法を試行錯誤していく様子が、ゴルフのように少ない打法で競うところに似ていることからこのように呼ばれています。
既に行われているコードゴルフ
とは言え実はTwitter上では既にコードゴルフが行われています。色々なバリエーションがありますが、比較的穏当なもの*2はこちら
https://twitter.com/fgnass/status/639215596958261249
<svg viewbox="0 0 600 200" fill=none stroke-width=16><path d="M102 20a51 51 0 1 0 20 40h-53M375 80a30 30 0 1 0 0 1m0-40v80a30 30 0 0 1-51 20"stroke=#4285f4 /><path d="M400 0v115"stroke=#34a853 /><path d="M290 80a30 30 0 1 0 0 .01"stroke=#fbbc05 /><path d="M205 80a30 30 0 1 0 0 .01M480 95a30 30 0 1 1 0-30l-50 25"stroke=#ea4335>
このツイートのものは328バイト、gzip圧縮すると手元の環境では196バイトになりした。*3
デモページ
どうやっているのか?というと、これはSVGファイルではなくHTMLファイルなんですよね。
SVGはXMLを基盤としている規格のため、例えばviewbox
ではなくviewBox
と記述する必要があるなど、厳格なルールがあります。しかし、HTML5ではインラインSVGでの配置が可能になりましたから、XMLと異なり比較的解釈も緩く少しくらい雑な書き方をしても表示されます。
なによりSVG名前空間宣言も省略できますし。
これはこれでコードゴルフの一つではありますが、SVGファイルの形態を変えると応用が効かないのと、そもそも色もサイズも変えるのはどうなのよ……と私は思うので今回はこれとは別の方向性でコードゴルフを行ってみます。
今回のコードゴルフの自己ルール
ここでの自己ルール
今回のルールとしては
- SVG1.1SEの仕様に準拠する
- 内容については変更を加えない
- 最終成果物はsvgzのファイルサイズで行う
としました。
それぞれのルールを補足すると、
SVG1.1SEの仕様に準拠する
HTMLファイルとして有効であっても、SVGファイルとしては利用できないのであれば使い勝手が悪いですし、最低限SVGファイルの体裁は維持します。そのためここではSVG 1.1 Second Editionの仕様に準拠する範囲でコードゴルフを行います。
内容については変更を加えない
色を変更するとか、図形を変えるとか、内容に対して手を入れればファイルサイズは確実に軽くできますが、それでは際限が無いので。
最終成果物はsvgzのファイルサイズで行う
SVGファイルはテキストデータなので、HTML/JavaScript/CSSファイルのようにgzip圧縮の効果が効きます。
そのためHTMLファイルなどと同様にmod_deflate
などでgzip圧縮して使われるのですが、最初からgzip圧縮したsvgzファイル形式というのが仕様にもありますから、ここではそれで行います。
というわけで前置きがかなり長くなりましたが、ここからコードゴルフを実際に行っていきます。
コードゴルフ実践編:ソースコードを短くする
不要なg要素を削除する
490バイト(改行を除く)の元ファイルです。gzip圧縮後は292バイト。
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250"> <g stroke-width="16" fill="none"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53" stroke="#4a87ee"/> <circle cx="227" cy="128" r="32" stroke="#d83038"/> <circle cx="313" cy="128" r="32" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17" stroke="#4a87ee"/> <path stroke="#4ab95a" d="M449 51v115"/> <path d="M529 118a30 30 0 1 0-2 24m5-32l-62 28" stroke="#d83038"/> </g> </svg>
ここからは見やすいよう、改行を適宜加えてあります。
このコードを見ると、g要素にプレゼンテーション属性を指定していますがそれはルート要素に移動できるので、g要素は削除できます。
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53" stroke="#4a87ee"/> <circle cx="227" cy="128" r="32" stroke="#d83038"/> <circle cx="313" cy="128" r="32" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17" stroke="#4a87ee"/> <path stroke="#4ab95a" d="M449 51v115"/> <path d="M529 118a30 30 0 1 0-2 24m5-32l-62 28" stroke="#d83038"/> </svg>
色の指定をまとめる
画像を見ると「G」「g」の色が同じでstroke="#4a87ee"
が重複していますね。これは無駄なので、ルート要素にまとめましょう。
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none" stroke="#4a87ee"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53"/> <circle cx="227" cy="128" r="32" stroke="#d83038"/> <circle cx="313" cy="128" r="32" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17"/> <path stroke="#4ab95a" d="M449 51v115"/> <path d="M529 118a30 30 0 1 0-2 24m5-32l-62 28" stroke="#d83038"/> </svg>
linetoコマンドの省略
最後に配置されているpath要素、画像では「e」の直線部分の指定にlinetoコマンドが使われています。
<path d="M529 118a30 30 0 1 0-2 24m5-32l-62 28" stroke="#d83038"/>
これはmovetoコマンドの直後に記述されているため、仕様により省略できます。
moveto に続けて複数の座標成分ペアが与えられた場合、2番目以降のペアは暗黙の lineto 命令として扱われる。 この暗黙の lineto 命令は moveto が相対の場合は相対に、絶対の場合は絶対と見なされる。
http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html#PathDataMovetoCommands
つまりこうなるわけですね。
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none" stroke="#4a87ee"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53"/> <circle cx="227" cy="128" r="32" stroke="#d83038"/> <circle cx="313" cy="128" r="32" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17"/> <path stroke="#4ab95a" d="M449 51v115"/> <path d="M529 118a30 30 0 1 0-2 24m5-32-62 28" stroke="#d83038"/> </svg>
これにより、1バイト削減でき、465バイトになりました。
この時点でgzip圧縮すると284バイトのsvgzファイルが生成されます。内容に手を加えないコードの削減はここまでが限度ですから、ここからはgzip圧縮アルゴリズムに最適化したコードゴルフになっていきます。
圧縮アルゴリズムの気持ちになるですよ
記述の削減はこれ以上行えませんが、仮にファイルサイズは同じあっても圧縮アルゴリズムに最適化することで、圧縮効率を高めることはできます。
例えばこの文字列は、aからzまでのアルファベット26文字が20回出てくるランダムな内容です。
onqwmbeyxtqgssfsrqddubditstqjxwsktiagxdeloeygwcslihecctmdbgoanxbfcknlltvjbbfjmfsdpzzhkduolejrswirqzhgkfgnsumtpffprjryqyscodjzuonfqbnmjnxyvbcilyyafazbeywruzokseyhozangmwdpagxaftolxgnummjhhmgkfpyvabfvtaqspwujfeireodylcltcrxvakbwhbyinsmlzlhelaxvkphuvqardqsvvoojukqhukkiscpqkwyynipgwmtwwhvpqfvlkegypnbtukxtubxbrugilfcvihqezvcxoizozemrcjokxgmxhwidrbtvrntylckaezwmsbkqubunpiaksprwwaehtzwpzivmjvzlrhiyjsllpmxadvwfgemjuheviqrazmvuygdjyglqoqzghmapzkjawpsrubojtcjgxoitxgzcrceocbtjzadpfmqnyfexhdncpeccdhvnkhrxfxwslidrfunniqudnpjdto
26文字×20なので、520バイトのテキストファイルです。
これをgzip圧縮すると357バイトになります。
しかし、こう並び替えるとどうでしょうか?
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz
これも同じ、aからzまでのアルファベット26文字が20回出てくる520バイトの文字列です。
ファイルに含まれる文字の種類、数は同じですがこのように並べ替えるとgzip圧縮効果が高まり、53バイトになりました。
なぜこうした違いが現れるかというと、同じフレーズの繰り返しは圧縮アルゴリズムにより、効率的に情報が整理できるためです。
もちろん内容の変更はできませんから、行える範囲の限度はあるものの、このことを意識して先ほどのSVGファイルを見てみるとまだ改善の余地があると分かりますね。
属性の並び替え
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none" stroke="#4a87ee"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53"/> <circle cx="227" cy="128" r="32" stroke="#d83038"/> <circle cx="313" cy="128" r="32" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17"/> <path stroke="#4ab95a" d="M449 51v115"/> <path d="M529 118a30 30 0 1 0-2 24m5-32-62 28" stroke="#d83038"/> </svg>
これの各属性を並び替えると、
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none" stroke="#4a87ee"> <path d="M173 102a51 51 0 1 1-13-30m20 37h-53"/> <circle cy="128" r="32" cx="227" stroke="#d83038"/> <circle cy="128" r="32" cx="313" stroke="#f4c022"/> <path d="M401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17"/> <path d="M449 51v115" stroke="#4ab95a"/> <path d="M529 118a30 30 0 1 0-2 24m5-32-62 28" stroke="#d83038"/> </svg>
<path d="M
や <circle cy="128" r="32" cx="
などの繰り返し部分が増えSVGのファイルサイズは465バイトのまま変わりませんが、gzip圧縮後の比較では先の284バイトから280バイトと4バイト削減できました。
movetoコマンドの変更
<path d="M173 102a51 51 0 1 1-13-30m20 37h-53"/>
path要素の最初にmovetoコマンドのM
が使われていますが、ファイル全体を通して大文字のM
はここでしか使われておらず、圧縮アルゴリズム的には効率が悪いですからこれを小文字のm
に変えます。
つまりこうなるわけですね。
<svg xmlns="http://www.w3.org/2000/svg" width="600" height="250" stroke-width="16" fill="none" stroke="#4a87ee"> <path d="m173 102a51 51 0 1 1-13-30m20 37h-53"/> <circle cy="128" r="32" cx="227" stroke="#d83038"/> <circle cy="128" r="32" cx="313" stroke="#f4c022"/> <path d="m401 160a31 31 0 1 1 0-61m-4 0a24 29 0 1 1 0 61m26-67v79m-1-12a20 20 0 1 1-52 17"/> <path d="m449 51v115" stroke="#4ab95a"/> <path d="m529 118a30 30 0 1 0-2 24m5-32-62 28" stroke="#d83038"/> </svg>
パスデータは "moveto" 命令で始められなければならない。
http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html#PathDataMovetoCommands
相対の "moveto"(m)がパスの最初に現れた場合のパラメタは絶対座標成分ペアと見なされる。
http://www.hcn.zaq.ne.jp/___/SVG11-2nd/paths.html#PathDataMovetoCommands
となるため、最初のM
絶対座標movetoコマンドを小文字m
の相対座標movetoコマンドに書き替えても問題ありません。
これによりハフマン符号の効果が高まるので、先の例からは2バイト削減され、278バイトになりました。
SVGコードゴルフのまとめと最適化ツール
今回はここまで終了ですね。
SVGコードゴルフではこのように無駄な記述の削減から始め、最終段階ではgzip圧縮アルゴリズムを鑑みた効率的な記述を試行錯誤するのが定石になっています。
とは言え、これを全てのSVGファイルに対して行うのは骨が折れる作業ですから、普段の制作には簡易的な最適化ツールを利用するのが良いでしょう。大抵はそれで十分です。
SVGO https://github.com/svg/svgo
SVGの最適化ツールにはSVGOが便利です。GUI版やAdobe Illustratorプラグイン版など様々なバージョンがあり使いやすいです。ただその反面、少し過剰に最適化し過ぎて表示が壊れてしまうケースもあるため、いくつかのプラグインは無効にしておいた方が良いでしょう。
参考記事
- SVGOを使ったSVGの軽量化方法(アニメーションさせるときの注意とか)|2.IDEA
- 注意した方が良いプラグインの解説などが詳しい記事。
- Useful SVGO(ptimization) Tools
- SVGO関連ツールのまとめ
突然の宣伝
宣伝になりますが、こんな風にSVGに関する解説や、最新ニュースを毎週お届けする「週刊SVG」というブログを私はやっています。
週刊SVG http://ssvvgg.net/
古いブラウザも減り、使える環境が整ってきたとは言えまだまだ誤解の多いSVGの使い方の説明や、便利なツールやアプリの紹介などを行っています。
興味のある方は是非!
そんな「週刊SVG」を運営している立場からするとSVGに関心が集まるのは歓迎する面もあるのですが、今回のGIZMODOの記事が酷いのでここからは検証と反論を行っていきます。
GIZMODOの記事について
- How Could Google's New Logo Be Only 305 Bytes When Its Old Logo Was 14,000 Bytes? - GIZMODO
- グーグルの旧ロゴは14,000バイトもあったのに、新ロゴはたった305バイトなのはどうして? : ギズモード・ジャパン
キャッチーなタイトルで多くの注目を集めたこの記事、よくよく読むと細部があやふやです。
<ここから追記:2015-09-10>
myakuraさんからコメントで指摘をいただいたのですが Google Designの記事に14,000バイト、305バイトのSVGファイルについて記載があり、低帯域での利用の際には305バイトのSVGファイルが表示される仕様になっているようです。
SVGファイルそのものは確認できませんでしたが、動画を見る限りおそらく均一幅のロゴであるようです。つまり以降の文章の前提が大きく異なっております。
該当の記事にリンクを張っていたにもかかわらず、きちんと内容を読み込まずに却って誤った記事を書いてしまい大変申し訳ありませんでした。</追記ここまで>
14,000バイトのグーグルの旧ロゴはどこにあるのか?
タイトルにもなっている14,000バイトの旧ロゴですが、そのファイルそのものは原文の記事内にもQuoraにも全く示されていません。
またタイトルでは14,000バイトとしつつ、記事本文では
The old logo uses a complicated serif font which can only be created using bezier curves. All together, it has 100 anchor points, resulting in a 6 KB (6,380 bytes) file. When compressed, the size comes down to 2 KB (2,145 bytes).
とバラバラ。
そもそもそのSVGファイルがGoogle公式なのかすらはっきりしません。
SVGではアンカーポイントが100個だろうが、ファイルサイズをわざと重くしようと思えばいくらでもできる(参考記事)ので、比較するならばきちんと中身を提示しなければフェアではありません。
Google公式では旧ロゴをSVGで使ったケースは知る限りでは無いんですよね。私は2015年5月にこうした記事を書いたのですが
SVGを使用してる企業・団体のサイトを22ヶ国、160件以上調べてみた
このときに、Google社の関連サービスも含めSVG使用状況を調査しましたが、ロゴをSVGファイルにしていた事例はありませんでした。*4
一応探してみると Wikimedia Commons に条件に該当しそうなGoogleのロゴがあったのですが、
File:Logo Google 2013 Official.svg - Wikimedia Commons
これはソースを読むと、CorelDrawやInkscapeで作図されたメタデータがそのまま残っていますから、Google公式ではなくユーザーが勝手にトレースして作ったSVGファイルですよね……。
仮にこれだとするとGoogle公式でもない、無関係の第三者が勝手に作ったSVGファイルが根拠になっているわけです。
305バイトの新しいロゴはどこにあるのか?
これも記事にはどこにもありません。(※ありました追記を参照)
To verify this, I recreated the first letter (G) in the SVG format, resulting in a file that's 302 bytes uncompressed, and 195 bytes compressed.
と軽さをアピールしていますが、これはあくまで「G」一文字のデータ量であって単純には比較できないでしょう。
そして冒頭でも取り上げた @thespiteさんのツイート を記事では紹介しているわけですが、
@thespiteさん自身も書かれている通り、あくまで「quick and dirty version」であってこれは元のロゴからデザインを変更しています。このロゴではファイルサイズ削減のため太さが一定になっていますが、実際のロゴを確認すると微妙に太さを変更していることが分かります。
小文字の「g」が分かりやすいですね、実際のロゴは太さが一定ではありません。
太さを一定にした線でトレースして比較。
こちらの記事が詳しいのですが
新しいGoogleのロゴは線(ストローク)だけでは表現できません - jdash2000 site
書体は一見 同じ幅で描かれているよう見えても繊細に線幅の調整を行って少しでも良いもの、読みやすいものになるようデザインされています。ロゴならなおさら手間をかけて試行錯誤を繰り返します。
こうしたこだわりを全く切り捨てて、線幅を一定にしたからファイルサイズを削減できた!というのは遊びなら良いかもしれませんが、比較として使うのは適切では無いです。
そして重要なこと:Googleでは通常はロゴにSVGは使われていません
Googleの検索ページではPNG画像ファイルなんですよね。
そもそもSVGファイルはロゴに使われていません、少なくとも現時点では。*5
Googleのロゴは円と長方形だけでは無い
そして
シンプルになった新しいロゴの場合、小文字のg以外の大部分が丸と長方形だけを使って作られています。
この前提から違います。
大文字の「G」も単純な円と長方形ではありません。
僅かに太くなっているのは、デザイナーの工夫の現れでしょう。
つまり
旧ロゴがこれだけファイルサイズが大きかった!とタイトルで大きく掲げるも、根拠となる元のファイルを示さず。
Google公式でもなんでもない第三者が作った「ロゴもどき」を持ち出してこんなに軽くなった!と誇大に煽り
さらには細部まで調整したデザイナーのこだわりを全く切り捨てて円と長方形だけと乱暴に断言しているわけで、いい加減にもほどがある雑な分析です。
ロゴのデザインをした人からしてもこんな妙な分析が流布するのは迷惑だろうな、きっと……と思ったので書いてみました。
まー、こうして反論記事を書いてみたものの、大げさなに面白おかしく煽った方がきっと広まってしまうんだろうなー、と思うと残念な気持ちになりますね……。
ちなみにGoogle公式のSVGファイルは
GoogleによるロゴのSVGはこのページの一番下にあるので、
Evolving the Google Identity - Articles - Google Design
論評するのであればまずそれを取り上げるのが筋だと思うんですよね。
これはあくまで黒一色バージョンですが*6、単純な円と長方形ではないことがよく分かるかと。
ただ、ゴルファー的な観点からするとviewBox
の
width="180" height="62" viewBox="-0.1 0 180 62"
の-0.1
ってどう考えても無駄なので、削除すればいいのに、と思うのですが。
><