ようじょのおえかきちょう

ふぇぇ お医者さんにペン持ったらダメっていわれた〜〜

PNGをよんで表示したい!

動機

パフォーマンス向上のためのデザイン設計

パフォーマンス向上のためのデザイン設計

「パフォーマンス向上のためのデザイン設計」という本を読んでいると、縦グラデーションの GIF と横グラデーションの GIF ではファイルサイズが大きく違うことが載っていた。水平方向の冗長性を取り除く圧縮アルゴリズムになっているらしく、気になったので軽く調べてみた。

調べていると、PNG は水平方向だけでなく垂直方向にもつよいことが書いてあった。PNG は CTF の問題でヘッダとか見たことあったので PNG で遊んでみようと思った。

つくったもの

github.com

f:id:yamasy1549:20170914021551p:plain

スクショだけ見てもわからんな… RGB じゃないと動かないのは仕様だしめっちゃ重いのも仕様!!!

フィルタリング形式の実装をミスるとこんなふうにアートになってたのしいあばばばば〜〜〜

f:id:yamasy1549:20170914021912p:plain

バイナリをよんで Tk ってやつで 1 ピクセルずつ四角を描いてる(ほかにやりようが思いつかなかった)。Ruby/Tk のドキュメントが殆どなかったので Python 実装 Tkinter の使い方を読んでオプション名とかを知った。

www.geocities.jp

PNG の中身(超ざっくり)

仕様書はこれかな…?

Portable Network Graphics (PNG) Specification (Second Edition)

日本語訳(PNG 1.0 仕様書の改訂版)は Web アーカイブとして残っている

PNG (Portable Network Graphics) Specification

N 番煎じなんだけど、メモとして。

シグネチャ

PNGシグネチャは 8 byte で、決まっている。 50 4e 47PNG を表している。

89 50 4e 47 0d 0a 1a 0a

チャンク

I***はチャンクと呼ばれて、以下でワンセットになっている。

  • 長さ
  • チャンクの名前
  • データ
  • CRC
チャンクの名前 なに
IHDR サイズとか、画像の情報
IDAT 画像データ本体。複数あっても良い
IEND おしまいを表す

画像データ本体が大きすぎたら分割されて IDAT が複数できるのかな(よくしらない)

わかりやすい PNG の話 for Web

フィルタリング

PNGDeflation 圧縮という圧縮方式みたいで、フィルタリングで工夫すると圧縮効率が良くなるらしい。フィルタリング形式は 0 から 4 まで 5 つあって、これは行ごとに好きなものを選べる。256 を法として modulo をとるので、255 を超えることはない。

フィルタリング形式 なに
0 None そのまま出す
1 Sub ピクセルとの差分
2 Up ピクセルとの差分
3 Average ピクセル・上ピクセルの平均をとったものとの差分
4 Paeth ピクセル・上ピクセル・左上ピクセルPaeth predictor したものとの差分

上に貼った事故画像は None と Sub を正しく実装したもので、1 行目だけ正しく表示されている。

Paeth predictor

Paeth predictor はざっくり言うと、左・上・左上のうちどれが一番近いかなーというのを調べる。縦方向と横方向で変化の小さいほうを選び、縦と横で全く違う感じに変化してたら中間として左上をとるような…

# a: 左
# b: 上
# c: 左上

def paeth_predictor(a, b, c)
  p = a + b - c
  pa = (p - a).abs
  pb = (p - b).abs
  pc = (p - c).abs
  
  if pa <= pb && pa <= pc then a
  elsif pb <= pc then b
  else c
  end
end

雰囲気こんなかんじで、これを R / G / B についてやる。

PNG Specification: Filter Algorithms

かんそう

たのしかった。気が向いたら、実装飛ばしてるところをもうちょっと書きたい。