vvvvでJPGのglitchを実装した

# はじめに
[vvvv Advent Calendar 2016](http://qiita.com/advent-calendar/2016/vvvv) の8日目の記事です。

Glitchエフェクトいいですよね。とりあえずGlitchさせれば良い感じになる。
Twitter眺めてたら良い感じのvvvvによるグリッチ映像が流れてきて(↓のツイート)、心が癒やされました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="und" dir="ltr"><a href="https://t.co/YwJRqaXung">pic.twitter.com/YwJRqaXung</a></p>&mdash; callmenames.v4p (@vvvvmotions) <a href="https://twitter.com/vvvvmotions/status/805992815616409600">2016年12月6日</a></blockquote>

これに影響されて自分もグリッチ実装してみました。実装については[こちらの記事](http://dot-ore.tumblr.com/post/129745484505/%E5%89%8D%E5%9B%9E%E3%81%AE%E5%AE%A3%E8%A8%80%E3%81%A9%E3%81%8A%E3%82%8A%E8%A7%A3%E8%AA%AC%E3%81%97%E3%81%BE%E3%81%99%E3%81%93%E3%82%8Cgithub)が非常に参考になりました。

ただ、すべてを乱数に頼って実装したら思ったよりフォーマットごと壊れて真っ白になってしまう率が高かったので、JPGのファイルフォーマットを調べて壊していい箇所を探してみました。

# JPGのファイルフォーマット

以下のサイトを参考にしました。
[JPG ファイルフォーマット](http://www.setsuki.com/hsp/ext/jpg.htm)

`AsRaw (EX9.Texture)`でFileFormatを"JPG"に設定したところ、出力されるバイナリデータは以下の形になっていました。

| 開始バイト位置 | セグメント種類 | セグメント長 |
|:-:|:-:|:-:|
|0|SOI(0xFFD8)|2 Byte|
|2|APP0(0xFFE0)|18 Byte|
|20|DQT(0xFFDB)|69 Byte|
|89|DQT(0xFFDB)|69 Byte|
|158|SOF0(0xFFC0)|19 Byte|
|177|DHT(0xFFC4)|33 Byte|
|210|DHT(0xFFC4)|183 Byte|
|393|DHT(0xFFC4)|33 Byte|
|426|DHT(0xFFC4)|183 Byte|
|609|SOS(0xFFDA)|14 Byte|
|623|イメージデータ|長い|
|最後の2Byte|EOI(0xFFD9)|2 Byte|

それぞれのセグメントについてグリッチしてうまくいくか試してみたところ、壊しても良さそうなセグメントがおおまかに特定できました。
また、セグメントによって得られるグリッチ効果がわかりやすく違ったので少し面白い結果になりました。

# 量子化テーブル定義セグメント(DQT)

DQTセグメントは2つありますが、両方とも壊して良さそうです。ただし、先頭から5Byteはセグメントのヘッダ部分なので触れないほうが良さそう。
というわけでDQTについては、バイト位置で言うと25～88Byte目、94～157Byte目がグリッチ対象です。

実際にグリッチをかけると下記のような映像が得られます。壊すバイト数をかなり増やしてもあまり原型崩れない感じ。
ちなみにグリッチ前の映像は[以前の記事](http://qiita.com/clomie/items/db6e1f96f82f967a3360)で作成したものです。

![DQT.gif](https://qiita-image-store.s3.amazonaws.com/0/26489/4461181f-a320-73f5-ffe1-b755111f804a.gif)

# ハフマン法テーブル定義セグメント(DHT)

DHTセグメントは4つあります。ただし、データ長が33Byteのセグメントはグリッチすると画像が表示されない場合が多かったので、避けたほうが良さそうです。
また、DHTセグメントにもヘッダ部分が19Byteあるので、グリッチの対象になるバイト位置は、231～392Byte目、447Byte目～608Byte目です。

ここにグリッチをかけると原色のノイズが入ってくるような映像が得られます。良いですね。
![DHT.gif](https://qiita-image-store.s3.amazonaws.com/0/26489/08e1dce7-8149-3aee-a86a-31a90a46fee1.gif)

# イメージ実データ部
実際に圧縮された画像データなので基本的にいじって大丈夫はなず。最後の2ByteはEOIセグメントになっているので、そこだけ避ければあとはグリッチ可能な領域です。

ここをグリッチすると、画像がズレるようなそれらしいグリッチ結果が得られます。
![IMAGE.gif](https://qiita-image-store.s3.amazonaws.com/0/26489/bf197b5c-5008-f3f1-1e71-bf04dd501090.gif)

# 実装

![patch.PNG](https://qiita-image-store.s3.amazonaws.com/0/26489/e4ebd19e-5837-c382-96b0-b03ee6728ad0.png)

サブパッチ化して、各セグメントごとにグリッチする量を調節できるように実装しました。
JPGフォーマットのバイナリを符号なしByteのSpreadに変換した後、セグメントごとに書き換えるインデックスのバイナリを乱数で生成、さらに別の乱数で書き換え後の値を生成してグリッチしています。

乱数は`Random (Value)`のIs IntegerピンをONにして整数で取り出しています。Scaleピンに入れる値は、生成したい乱数の数だけ`Select (Value)`で複製して入れてます。こうするとRandomSpread的に使うことができるのですが、もしかしたらコストは多少高いかもしれません。
書き換え後の値については、0xFFがJPGフォーマットのセグメント開始を意味してしまうので、0〜254までの範囲で生成するようにしてみました。

AsRawノードが1フレームごとにJPG変換しているので、当然ながらかなり重いです。

今回は単純に特定のインデックスを別の値に書き換える、という形でしか実装していませんが、画像データ部のある範囲を別の位置に移動とか、特定の数値を別の数値に置換とかやってみてもおもしろいかもしれないですね。

ソースはこちら。
[vvvv\-sandbox/20161208\_WordpadGlitchJPG at master · clomie/vvvv\-sandbox](https://github.com/clomie/vvvv-sandbox/tree/master/20161208_WordpadGlitchJPG)