aboutsummaryrefslogtreecommitdiffhomepage
path: root/services/nuldoc/content/posts/2023-04-01
diff options
context:
space:
mode:
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)