diff options
Diffstat (limited to 'services/nuldoc/content/posts/2023-04-01')
| -rw-r--r-- | services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.md (renamed from services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.dj) | 65 |
1 files changed, 22 insertions, 43 deletions
diff --git a/services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.dj b/services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.md index 55d1519..6f4fb3c 100644 --- a/services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.dj +++ b/services/nuldoc/content/posts/2023-04-01/implementation-of-minimal-png-image-encoder.md @@ -9,15 +9,13 @@ tags = [] date = "2023-04-01" remark = "公開" --- -{#intro} -# はじめに +# はじめに {#intro} この記事では、PNG 画像として valid な範囲で最大限手抜きしたエンコーダを書く。 PNG 画像に対応したビューアであれば読み込めるが、圧縮効率については一切考えない。 また、実装には Go 言語を使うが、Go の標準ライブラリにあるさまざまなアルゴリズム (PNG 画像に関係する範囲だと、zlib や CRC32、Adler-32 など) は使わない。 -{#basic-structure-of-png} -# PNG ファイルの基本構造 +# PNG ファイルの基本構造 {#basic-structure-of-png} PNG ファイルの基本構造は次のようになっている。 @@ -31,8 +29,7 @@ Chunk には画像データを入れる IDAT chunk、パレットデータを入 次節で、それぞれの具体的な構造を確認しつつ実装していく。 -{#implement-png-encoder} -# PNG のエンコーダを実装する +# PNG のエンコーダを実装する {#implement-png-encoder} 以下のソースコードをベースにする。 今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ `image/png` を用いる。 @@ -80,8 +77,7 @@ func writePng(w io.Writer, img image.Image) { 以降は、`writeSignature` や `writeChunkIhdr` などを実装していく。 -{#png-signature} -## PNG signature +## PNG signature {#png-signature} PNG signature は、PNG 画像の先頭に固定で付与されるバイト列で、8 バイトからなる。 @@ -118,8 +114,7 @@ func writeSignature(w io.Writer) { `encoding/binary` パッケージの `binary.Write` を使い、固定の 8 バイトを書き込む。 -{#structure-of-chunk} -## Chunk の構造 +## Chunk の構造 {#structure-of-chunk} IHDR chunk に進む前に、chunk 一般の構造を確認する。 @@ -187,36 +182,26 @@ PNG では基本的に big endian を使うことに注意する。 準備ができたところで、具体的な chunk をエンコードしていく。 -{#ihdr-chunk} -## IHDR chunk +## IHDR chunk {#ihdr-chunk} IHDR chunk は最初に配置される chunk である。次のようなデータからなる。 1. 画像の幅 (符号なし 4 バイト整数) 1. 画像の高さ (符号なし 4 バイト整数) 1. ビット深度 (符号なし 1 バイト整数) - - * 1 色に使うビット数。1 ピクセルに 24 bit 使う truecolor 画像では 8 になる - + * 1 色に使うビット数。1 ピクセルに 24 bit 使う truecolor 画像では 8 になる 1. 色タイプ (符号なし 1 バイト整数) - - * 0: グレースケール - * 2: Truecolor (今回はこれに決め打ち) - * 3: パレットのインデックス - * 4: グレースケール + アルファ - * 6: Truecolor + アルファ - + * 0: グレースケール + * 2: Truecolor (今回はこれに決め打ち) + * 3: パレットのインデックス + * 4: グレースケール + アルファ + * 6: Truecolor + アルファ 1. 圧縮方式 (符号なし 1 バイト整数) - - * PNG の仕様書に 0 しか定義されていないので 0 で固定 - + * PNG の仕様書に 0 しか定義されていないので 0 で固定 1. フィルタ方式 (符号なし 1 バイト整数) - - * PNG の仕様書に 0 しか定義されていないので 0 で固定 - + * PNG の仕様書に 0 しか定義されていないので 0 で固定 1. インターレース方式 (符号なし 1 バイト整数) - - * 今回はインターレースしないので 0 + * 今回はインターレースしないので 0 今回ほとんどのデータは決め打ちするので、データに応じて変わるのは width と height だけになる。コードは次のようになる。 @@ -237,13 +222,11 @@ func writeChunkIhdr(w io.Writer, width, height uint32) { } ``` -{#idat-chunk} -## IDAT chunk +## IDAT chunk {#idat-chunk} IDAT chunk は、実際の画像データが格納された chunk である。IDAT chunk は deflate アルゴリズムにより圧縮され、zlib 形式で格納される。 -{#zlib} -### Zlib +### Zlib {#zlib} まずは zlib について確認する。おおよそ次のような構造になっている。 @@ -277,7 +260,7 @@ func adler32(buf []byte) uint32 { 「データ」の部分には圧縮したデータが入るのだが、真面目に deflate アルゴリズムを実装する必要はない。Zlib には無圧縮のデータブロックを格納することができるので、これを使う。本来は、データの圧縮効率の悪いランダムなデータをそのまま格納するためのものだが、今回は deflate の実装をサボるために使う。 -1 つの無圧縮ブロックには 65535 (2^16^ - 1) バイトまで格納できる。それぞれのブロックは次のような構成になっている。 +1 つの無圧縮ブロックには 65535 (2<sup>16</sup> - 1) バイトまで格納できる。それぞれのブロックは次のような構成になっている。 1. 最終ブロックなら 1、そうでなければ 0 (符号なし 1 バイト整数) 1. ブロックのバイト長 (符号なし 2 バイト整数) @@ -313,8 +296,7 @@ func encodeZlib(data []byte) []byte { } ``` -{#image-data} -### 画像データ +### 画像データ {#image-data} では次に、zlib 形式で格納するデータを用意する。PNG 画像は次のような順にスキャンする。 画像の左上のピクセルから同じ行を横にスキャンしていき、一番右まで到達したら次の行の左に向かう。 @@ -342,8 +324,7 @@ func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) { } ``` -{#iend-chunk} -## IEND chunk +## IEND chunk {#iend-chunk} 最後に IEND chunk を書き込む。これは PNG 画像の最後に配置される chunk で、PNG のデコーダはこの chunk に出会うとそこでデコードを停止する。 @@ -355,8 +336,7 @@ func writeChunkIend(w io.Writer) { } ``` -{#outro} -# おわりに +# おわりに {#outro} 最後に全ソースコードを再掲しておく。 @@ -537,8 +517,7 @@ func adler32(buf []byte) uint32 { } ``` -{#references} -# 参考 +# 参考 {#references} * [Portable Network Graphics (PNG) Specification (Third Edition)](https://www.w3.org/TR/png) * [ZLIB Compressed Data Format Specification version 3.3](https://www.rfc-editor.org/rfc/rfc1950) |
