diff options
Diffstat (limited to 'vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder')
| -rw-r--r-- | vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html | 707 |
1 files changed, 363 insertions, 344 deletions
diff --git a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html index 4255b561..11831bdf 100644 --- a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html +++ b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html @@ -13,8 +13,7 @@ <meta property="og:locale" content="ja_JP"> <link rel="icon" type="image/svg+xml" href="/favicon.svg"> <title>PNG 画像の最小構成エンコーダを実装する|REPL: Rest-Eat-Program Loop</title> - <link rel="stylesheet" href="/style.css?h=79020a898c7052f79b32e90376a4497d"> - <link rel="stylesheet" href="/hl.css?h=340e65ffd5c17713efc9107c06304f7b"> + <link rel="stylesheet" href="/style.css?h=60eb349e583f5bd51518a7eb98598043"> </head> <body class="single"> <header class="header"> @@ -98,44 +97,46 @@ 以下のソースコードをベースにする。今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ <code>image/png</code> を用いる。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">package</span> main - -<span class="hljs-keyword">import</span> ( - <span class="hljs-string">"image"</span> - _ <span class="hljs-string">"image/png"</span> - <span class="hljs-string">"io"</span> - <span class="hljs-string">"os"</span> -) - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { - inFile, err := os.Open(<span class="hljs-string">"input.png"</span>) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - <span class="hljs-keyword">defer</span> inFile.Close() - - img, _, err := image.Decode(inFile) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - - outFile, err := os.Create(<span class="hljs-string">"output.png"</span>) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - <span class="hljs-keyword">defer</span> outFile.Close() - - writePng(outFile, img) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writePng</span><span class="hljs-params">(w io.Writer, img image.Image)</span></span> { - width := <span class="hljs-type">uint32</span>(img.Bounds().Dx()) - height := <span class="hljs-type">uint32</span>(img.Bounds().Dy()) - writeSignature(w) - writeChunkIhdr(w, width, height) - writeChunkIdat(w, width, height, img) - writeChunkIend(w) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">package</span><span style="color:#6F42C1"> main</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">import</span><span style="color:#24292E"> (</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">image</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#24292E"> _ </span><span style="color:#032F62">"</span><span style="color:#6F42C1">image/png</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">io</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">os</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> main</span><span style="color:#24292E">() {</span></span> +<span class="line"><span style="color:#24292E"> inFile, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> os.</span><span style="color:#6F42C1">Open</span><span style="color:#24292E">(</span><span style="color:#032F62">"input.png"</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> defer</span><span style="color:#24292E"> inFile.</span><span style="color:#6F42C1">Close</span><span style="color:#24292E">()</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> img, _, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> image.</span><span style="color:#6F42C1">Decode</span><span style="color:#24292E">(inFile)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> outFile, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> os.</span><span style="color:#6F42C1">Create</span><span style="color:#24292E">(</span><span style="color:#032F62">"output.png"</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> defer</span><span style="color:#24292E"> outFile.</span><span style="color:#6F42C1">Close</span><span style="color:#24292E">()</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writePng</span><span style="color:#24292E">(outFile, img)</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writePng</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">img</span><span style="color:#6F42C1"> image</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Image</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> width </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(img.</span><span style="color:#6F42C1">Bounds</span><span style="color:#24292E">().</span><span style="color:#6F42C1">Dx</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#24292E"> height </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(img.</span><span style="color:#6F42C1">Bounds</span><span style="color:#24292E">().</span><span style="color:#6F42C1">Dy</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#6F42C1"> writeSignature</span><span style="color:#24292E">(w)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIhdr</span><span style="color:#24292E">(w, width, height)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIdat</span><span style="color:#24292E">(w, width, height, img)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIend</span><span style="color:#24292E">(w)</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> <p> 以降は、<code>writeSignature</code> や <code>writeChunkIhdr</code> などを実装していく。 @@ -189,21 +190,23 @@ <code>writeSignature</code> の実装はこちら: </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">import</span> <span class="hljs-string">"encoding/binary"</span> - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeSignature</span><span class="hljs-params">(w io.Writer)</span></span> { - sig := [<span class="hljs-number">8</span>]<span class="hljs-type">uint8</span>{ - <span class="hljs-number">0x89</span>, - <span class="hljs-number">0x50</span>, <span class="hljs-comment">// P</span> - <span class="hljs-number">0x4E</span>, <span class="hljs-comment">// N</span> - <span class="hljs-number">0x47</span>, <span class="hljs-comment">// G</span> - <span class="hljs-number">0x0D</span>, <span class="hljs-comment">// CR</span> - <span class="hljs-number">0x0A</span>, <span class="hljs-comment">// LF</span> - <span class="hljs-number">0x1A</span>, <span class="hljs-comment">// EOF (^Z)</span> - <span class="hljs-number">0x0A</span>, <span class="hljs-comment">// LF</span> - } - binary.Write(w, binary.BigEndian, sig) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">import</span><span style="color:#032F62"> "</span><span style="color:#6F42C1">encoding/binary</span><span style="color:#032F62">"</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeSignature</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> sig </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> [</span><span style="color:#005CC5">8</span><span style="color:#24292E">]</span><span style="color:#D73A49">uint8</span><span style="color:#24292E">{</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">89</span><span style="color:#24292E">,</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">50</span><span style="color:#24292E">, </span><span style="color:#6A737D">// P</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">4E</span><span style="color:#24292E">, </span><span style="color:#6A737D">// N</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">47</span><span style="color:#24292E">, </span><span style="color:#6A737D">// G</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0D</span><span style="color:#24292E">, </span><span style="color:#6A737D">// CR</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// LF</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">1A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// EOF (^Z)</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// LF</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, sig)</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> <p> <code>encoding/binary</code> パッケージの <code>binary.Write</code> を使い、固定の 8 バイトを書き込む。 @@ -238,55 +241,59 @@ CRC (Cyclic Redundancy Check) は誤り検出符号の一種。Go 言語では <code>hash/crc32</code> パッケージにあるが、今回はこれも自前で実装する。PNG の仕様書に C 言語のサンプルコードが載っている (<a href="https://www.w3.org/TR/png/#D-CRCAppendix" rel="noreferrer" target="_blank">D. Sample CRC implementation</a>) ので、これを Go に移植する。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">var</span> ( - crcTable [<span class="hljs-number">256</span>]<span class="hljs-type">uint32</span> - crcTableComputed <span class="hljs-type">bool</span> -) - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeCrcTable</span><span class="hljs-params">()</span></span> { - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-number">256</span>; n++ { - c := <span class="hljs-type">uint32</span>(n) - <span class="hljs-keyword">for</span> k := <span class="hljs-number">0</span>; k < <span class="hljs-number">8</span>; k++ { - <span class="hljs-keyword">if</span> (c & <span class="hljs-number">1</span>) != <span class="hljs-number">0</span> { - c = <span class="hljs-number">0xEDB88320</span> ^ (c >> <span class="hljs-number">1</span>) - } <span class="hljs-keyword">else</span> { - c = c >> <span class="hljs-number">1</span> - } - } - crcTable[n] = c - } - crcTableComputed = <span class="hljs-literal">true</span> -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateCrc</span><span class="hljs-params">(crc <span class="hljs-type">uint32</span>, buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">if</span> !crcTableComputed { - makeCrcTable() - } - - c := crc - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-built_in">len</span>(buf); n++ { - c = crcTable[(c^<span class="hljs-type">uint32</span>(buf[n]))&<span class="hljs-number">0xFF</span>] ^ (c >> <span class="hljs-number">8</span>) - } - <span class="hljs-keyword">return</span> c -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">crc</span><span class="hljs-params">(buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">return</span> updateCrc(<span class="hljs-number">0xFFFFFFFF</span>, buf) ^ <span class="hljs-number">0xFFFFFFFF</span> -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">var</span><span style="color:#24292E"> (</span></span> +<span class="line"><span style="color:#24292E"> crcTable [</span><span style="color:#005CC5">256</span><span style="color:#24292E">]</span><span style="color:#D73A49">uint32</span></span> +<span class="line"><span style="color:#24292E"> crcTableComputed </span><span style="color:#D73A49">bool</span></span> +<span class="line"><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> makeCrcTable</span><span style="color:#24292E">() {</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 256</span><span style="color:#24292E">; n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(n)</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> k </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; k </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 8</span><span style="color:#24292E">; k</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">&</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">) </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">EDB88320</span><span style="color:#D73A49"> ^</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 1</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#24292E"> c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 1</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> crcTable[n] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> c</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> crcTableComputed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> updateCrc</span><span style="color:#24292E">(</span><span style="color:#E36209">crc</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> !</span><span style="color:#24292E">crcTableComputed {</span></span> +<span class="line"><span style="color:#6F42C1"> makeCrcTable</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> crc</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(buf); n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#24292E"> crcTable[(c</span><span style="color:#D73A49">^uint32</span><span style="color:#24292E">(buf[n]))</span><span style="color:#D73A49">&0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">] </span><span style="color:#D73A49">^</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 8</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> c</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> crc</span><span style="color:#24292E">(</span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#6F42C1"> updateCrc</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">FFFFFFFF</span><span style="color:#24292E">, buf) </span><span style="color:#D73A49">^</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFFFFFF</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> <p> できた <code>crc</code> 関数を使って、chunk 一般を書き込む関数も用意しておこう。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunk</span><span class="hljs-params">(w io.Writer, chunkType <span class="hljs-type">string</span>, data []<span class="hljs-type">byte</span>)</span></span> { - typeAndData := <span class="hljs-built_in">make</span>([]<span class="hljs-type">byte</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(chunkType)+<span class="hljs-built_in">len</span>(data)) - typeAndData = <span class="hljs-built_in">append</span>(typeAndData, []<span class="hljs-type">byte</span>(chunkType)...) - typeAndData = <span class="hljs-built_in">append</span>(typeAndData, data...) - - binary.Write(w, binary.BigEndian, <span class="hljs-type">uint32</span>(<span class="hljs-built_in">len</span>(data))) - binary.Write(w, binary.BigEndian, typeAndData) - binary.Write(w, binary.BigEndian, crc(typeAndData)) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">chunkType</span><span style="color:#D73A49"> string</span><span style="color:#24292E">, </span><span style="color:#E36209">data</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> make</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">, </span><span style="color:#005CC5">0</span><span style="color:#24292E">, </span><span style="color:#6F42C1">len</span><span style="color:#24292E">(chunkType)</span><span style="color:#D73A49">+</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(data))</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> append</span><span style="color:#24292E">(typeAndData, []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(chunkType)</span><span style="color:#D73A49">...</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> append</span><span style="color:#24292E">(typeAndData, data</span><span style="color:#D73A49">...</span><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, </span><span style="color:#D73A49">uint32</span><span style="color:#24292E">(</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(data)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, typeAndData)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, </span><span style="color:#6F42C1">crc</span><span style="color:#24292E">(typeAndData))</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> <p> 仕様どおり、<code>chunkType</code> と <code>data</code> から CRC を計算し、<code>data</code> の長さと合わせて書き込んでいる。PNG では基本的に big endian を使うことに注意する。 @@ -372,20 +379,22 @@ 今回ほとんどのデータは決め打ちするので、データに応じて変わるのは width と height だけになる。コードは次のようになる。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">import</span> <span class="hljs-string">"bytes"</span> - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIhdr</span><span class="hljs-params">(w io.Writer, width, height <span class="hljs-type">uint32</span>)</span></span> { - <span class="hljs-keyword">var</span> buf bytes.Buffer - binary.Write(&buf, binary.BigEndian, width) - binary.Write(&buf, binary.BigEndian, height) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">8</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">2</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - - writeChunk(w, <span class="hljs-string">"IHDR"</span>, buf.Bytes()) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">import</span><span style="color:#032F62"> "</span><span style="color:#6F42C1">bytes</span><span style="color:#032F62">"</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIhdr</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">width</span><span style="color:#24292E">, </span><span style="color:#E36209">height</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> buf </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, width)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, height)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">8</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">2</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IHDR"</span><span style="color:#24292E">, buf.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> </section> <section id="section--implement-png-encoder--idat-chunk"> @@ -426,22 +435,24 @@ Adler-32 も CRC と同じく誤り検出符号である。こちらも zlib の仕様書に C 言語でサンプルコードが記載されている (<a href="https://www.rfc-editor.org/rfc/rfc1950#section-9" rel="noreferrer" target="_blank">9. Appendix: Sample code</a>) ので、Go に移植する。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">const</span> adler32Base = <span class="hljs-number">65521</span> - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateAdler32</span><span class="hljs-params">(adler <span class="hljs-type">uint32</span>, buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - s1 := adler & <span class="hljs-number">0xFFFF</span> - s2 := (adler >> <span class="hljs-number">16</span>) & <span class="hljs-number">0xFFFF</span> - - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-built_in">len</span>(buf); n++ { - s1 = (s1 + <span class="hljs-type">uint32</span>(buf[n])) % adler32Base - s2 = (s2 + s1) % adler32Base - } - <span class="hljs-keyword">return</span> (s2 << <span class="hljs-number">16</span>) + s1 -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">adler32</span><span class="hljs-params">(buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">return</span> updateAdler32(<span class="hljs-number">1</span>, buf) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> adler32Base</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> 65521</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> updateAdler32</span><span style="color:#24292E">(</span><span style="color:#E36209">adler</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> s1 </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> adler </span><span style="color:#D73A49">&</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFF</span></span> +<span class="line"><span style="color:#24292E"> s2 </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> (adler </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">&</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFF</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(buf); n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> s1 </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (s1 </span><span style="color:#D73A49">+</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(buf[n])) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> adler32Base</span></span> +<span class="line"><span style="color:#24292E"> s2 </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (s2 </span><span style="color:#D73A49">+</span><span style="color:#24292E"> s1) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> adler32Base</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> (s2 </span><span style="color:#D73A49"><<</span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#24292E"> s1</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> adler32</span><span style="color:#24292E">(</span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#6F42C1"> updateAdler32</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">, buf)</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> <p> 「データ」の部分には圧縮したデータが入るのだが、真面目に deflate アルゴリズムを実装する必要はない。Zlib には無圧縮のデータブロックを格納することができるので、これを使う。本来は、データの圧縮効率の悪いランダムなデータをそのまま格納するためのものだが、今回は deflate の実装をサボるために使う。 @@ -473,30 +484,32 @@ 実際にこの手抜き zlib を実装したものがこちら: </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">encodeZlib</span><span class="hljs-params">(data []<span class="hljs-type">byte</span>)</span></span> []<span class="hljs-type">byte</span> { - <span class="hljs-keyword">var</span> buf bytes.Buffer - - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0x78</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0x01</span>)) - blockSize := <span class="hljs-number">65535</span> - isFinalBlock := <span class="hljs-literal">false</span> - <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; !isFinalBlock; i++ { - <span class="hljs-keyword">var</span> block []<span class="hljs-type">byte</span> - <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(data) <= (i+<span class="hljs-number">1</span>)*blockSize { - block = data[i*blockSize:] - isFinalBlock = <span class="hljs-literal">true</span> - } <span class="hljs-keyword">else</span> { - block = data[i*blockSize : (i+<span class="hljs-number">1</span>)*blockSize] - } - binary.Write(&buf, binary.BigEndian, isFinalBlock) - binary.Write(&buf, binary.LittleEndian, <span class="hljs-type">uint16</span>(<span class="hljs-built_in">len</span>(block))) - binary.Write(&buf, binary.LittleEndian, <span class="hljs-type">uint16</span>(^<span class="hljs-built_in">len</span>(block))) - binary.Write(&buf, binary.LittleEndian, block) - } - binary.Write(&buf, binary.BigEndian, adler32(data)) - - <span class="hljs-keyword">return</span> buf.Bytes() -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> encodeZlib</span><span style="color:#24292E">(</span><span style="color:#E36209">data</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) []</span><span style="color:#D73A49">byte</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> buf </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">78</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">01</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> blockSize </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 65535</span></span> +<span class="line"><span style="color:#24292E"> isFinalBlock </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> false</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; </span><span style="color:#D73A49">!</span><span style="color:#24292E">isFinalBlock; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> block []</span><span style="color:#D73A49">byte</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(data) </span><span style="color:#D73A49"><=</span><span style="color:#24292E"> (i</span><span style="color:#D73A49">+</span><span style="color:#005CC5">1</span><span style="color:#24292E">)</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize {</span></span> +<span class="line"><span style="color:#24292E"> block </span><span style="color:#D73A49">=</span><span style="color:#24292E"> data[i</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize:]</span></span> +<span class="line"><span style="color:#24292E"> isFinalBlock </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span></span> +<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> block </span><span style="color:#D73A49">=</span><span style="color:#24292E"> data[i</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize : (i</span><span style="color:#D73A49">+</span><span style="color:#005CC5">1</span><span style="color:#24292E">)</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize]</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, isFinalBlock)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, </span><span style="color:#D73A49">uint16</span><span style="color:#24292E">(</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(block)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, </span><span style="color:#D73A49">uint16</span><span style="color:#24292E">(</span><span style="color:#D73A49">^</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(block)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, block)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#6F42C1">adler32</span><span style="color:#24292E">(data))</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> buf.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> </section> <section id="section--implement-png-encoder--idat-chunk--image-data"> @@ -513,20 +526,22 @@ 先ほどの <code>encodeZlib</code> も使って実際に実装したものがこちら: </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIdat</span><span class="hljs-params">(w io.Writer, width, height <span class="hljs-type">uint32</span>, img image.Image)</span></span> { - <span class="hljs-keyword">var</span> pixels bytes.Buffer - <span class="hljs-keyword">for</span> y := <span class="hljs-type">uint32</span>(<span class="hljs-number">0</span>); y < height; y++ { - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - <span class="hljs-keyword">for</span> x := <span class="hljs-type">uint32</span>(<span class="hljs-number">0</span>); x < width; x++ { - r, g, b, _ := img.At(<span class="hljs-type">int</span>(x), <span class="hljs-type">int</span>(y)).RGBA() - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(r)) - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(g)) - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(b)) - } - } - - writeChunk(w, <span class="hljs-string">"IDAT"</span>, encodeZlib(pixels.Bytes())) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIdat</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">width</span><span style="color:#24292E">, </span><span style="color:#E36209">height</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">img</span><span style="color:#6F42C1"> image</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Image</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> pixels </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> y </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">); y </span><span style="color:#D73A49"><</span><span style="color:#24292E"> height; y</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> x </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">); x </span><span style="color:#D73A49"><</span><span style="color:#24292E"> width; x</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> r, g, b, _ </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> img.</span><span style="color:#6F42C1">At</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#24292E">(x), </span><span style="color:#D73A49">int</span><span style="color:#24292E">(y)).</span><span style="color:#6F42C1">RGBA</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(r))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(g))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(b))</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IDAT"</span><span style="color:#24292E">, </span><span style="color:#6F42C1">encodeZlib</span><span style="color:#24292E">(pixels.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">()))</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> </section> </section> @@ -540,9 +555,11 @@ 特に追加のデータはなく、必要なのは chunk type の <code>IEND</code> くらいなので実装は簡単: </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIend</span><span class="hljs-params">(w io.Writer)</span></span> { - writeChunk(w, <span class="hljs-string">"IEND"</span>, <span class="hljs-literal">nil</span>) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIend</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IEND"</span><span style="color:#24292E">, </span><span style="color:#005CC5">nil</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> </section> </section> @@ -552,180 +569,182 @@ 最後に全ソースコードを再掲しておく。 </p> - <pre class="highlight" language="go"><code class="highlight"><span class="hljs-keyword">package</span> main - -<span class="hljs-keyword">import</span> ( - <span class="hljs-string">"bytes"</span> - <span class="hljs-string">"encoding/binary"</span> - <span class="hljs-string">"image"</span> - _ <span class="hljs-string">"image/png"</span> - <span class="hljs-string">"io"</span> - <span class="hljs-string">"os"</span> -) - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { - inFile, err := os.Open(<span class="hljs-string">"input.png"</span>) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - <span class="hljs-keyword">defer</span> inFile.Close() - - img, _, err := image.Decode(inFile) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - - outFile, err := os.Create(<span class="hljs-string">"output.png"</span>) - <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { - <span class="hljs-built_in">panic</span>(err) - } - <span class="hljs-keyword">defer</span> outFile.Close() - - writePng(outFile, img) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writePng</span><span class="hljs-params">(w io.Writer, img image.Image)</span></span> { - width := <span class="hljs-type">uint32</span>(img.Bounds().Dx()) - height := <span class="hljs-type">uint32</span>(img.Bounds().Dy()) - writeSignature(w) - writeChunkIhdr(w, width, height) - writeChunkIdat(w, width, height, img) - writeChunkIend(w) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeSignature</span><span class="hljs-params">(w io.Writer)</span></span> { - sig := [<span class="hljs-number">8</span>]<span class="hljs-type">uint8</span>{ - <span class="hljs-number">0x89</span>, - <span class="hljs-number">0x50</span>, <span class="hljs-comment">// P</span> - <span class="hljs-number">0x4E</span>, <span class="hljs-comment">// N</span> - <span class="hljs-number">0x47</span>, <span class="hljs-comment">// G</span> - <span class="hljs-number">0x0D</span>, <span class="hljs-comment">// CR</span> - <span class="hljs-number">0x0A</span>, <span class="hljs-comment">// LF</span> - <span class="hljs-number">0x1A</span>, <span class="hljs-comment">// EOF (^Z)</span> - <span class="hljs-number">0x0A</span>, <span class="hljs-comment">// LF</span> - } - binary.Write(w, binary.BigEndian, sig) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIhdr</span><span class="hljs-params">(w io.Writer, width, height <span class="hljs-type">uint32</span>)</span></span> { - <span class="hljs-keyword">var</span> buf bytes.Buffer - binary.Write(&buf, binary.BigEndian, width) - binary.Write(&buf, binary.BigEndian, height) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">8</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">2</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - - writeChunk(w, <span class="hljs-string">"IHDR"</span>, buf.Bytes()) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIdat</span><span class="hljs-params">(w io.Writer, width, height <span class="hljs-type">uint32</span>, img image.Image)</span></span> { - <span class="hljs-keyword">var</span> pixels bytes.Buffer - <span class="hljs-keyword">for</span> y := <span class="hljs-type">uint32</span>(<span class="hljs-number">0</span>); y < height; y++ { - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0</span>)) - <span class="hljs-keyword">for</span> x := <span class="hljs-type">uint32</span>(<span class="hljs-number">0</span>); x < width; x++ { - r, g, b, _ := img.At(<span class="hljs-type">int</span>(x), <span class="hljs-type">int</span>(y)).RGBA() - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(r)) - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(g)) - binary.Write(&pixels, binary.BigEndian, <span class="hljs-type">uint8</span>(b)) - } - } - - writeChunk(w, <span class="hljs-string">"IDAT"</span>, encodeZlib(pixels.Bytes())) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">encodeZlib</span><span class="hljs-params">(data []<span class="hljs-type">byte</span>)</span></span> []<span class="hljs-type">byte</span> { - <span class="hljs-keyword">var</span> buf bytes.Buffer - - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0x78</span>)) - binary.Write(&buf, binary.BigEndian, <span class="hljs-type">uint8</span>(<span class="hljs-number">0x01</span>)) - blockSize := <span class="hljs-number">65535</span> - isFinalBlock := <span class="hljs-literal">false</span> - <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; !isFinalBlock; i++ { - <span class="hljs-keyword">var</span> block []<span class="hljs-type">byte</span> - <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(data) <= (i+<span class="hljs-number">1</span>)*blockSize { - block = data[i*blockSize:] - isFinalBlock = <span class="hljs-literal">true</span> - } <span class="hljs-keyword">else</span> { - block = data[i*blockSize : (i+<span class="hljs-number">1</span>)*blockSize] - } - binary.Write(&buf, binary.BigEndian, isFinalBlock) - binary.Write(&buf, binary.LittleEndian, <span class="hljs-type">uint16</span>(<span class="hljs-built_in">len</span>(block))) - binary.Write(&buf, binary.LittleEndian, <span class="hljs-type">uint16</span>(^<span class="hljs-built_in">len</span>(block))) - binary.Write(&buf, binary.LittleEndian, block) - } - binary.Write(&buf, binary.BigEndian, adler32(data)) - - <span class="hljs-keyword">return</span> buf.Bytes() -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunkIend</span><span class="hljs-params">(w io.Writer)</span></span> { - writeChunk(w, <span class="hljs-string">"IEND"</span>, <span class="hljs-literal">nil</span>) -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeChunk</span><span class="hljs-params">(w io.Writer, chunkType <span class="hljs-type">string</span>, data []<span class="hljs-type">byte</span>)</span></span> { - typeAndData := <span class="hljs-built_in">make</span>([]<span class="hljs-type">byte</span>, <span class="hljs-number">0</span>, <span class="hljs-built_in">len</span>(chunkType)+<span class="hljs-built_in">len</span>(data)) - typeAndData = <span class="hljs-built_in">append</span>(typeAndData, []<span class="hljs-type">byte</span>(chunkType)...) - typeAndData = <span class="hljs-built_in">append</span>(typeAndData, data...) - - binary.Write(w, binary.BigEndian, <span class="hljs-type">uint32</span>(<span class="hljs-built_in">len</span>(data))) - binary.Write(w, binary.BigEndian, typeAndData) - binary.Write(w, binary.BigEndian, crc(typeAndData)) -} - -<span class="hljs-keyword">var</span> ( - crcTable [<span class="hljs-number">256</span>]<span class="hljs-type">uint32</span> - crcTableComputed <span class="hljs-type">bool</span> -) - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeCrcTable</span><span class="hljs-params">()</span></span> { - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-number">256</span>; n++ { - c := <span class="hljs-type">uint32</span>(n) - <span class="hljs-keyword">for</span> k := <span class="hljs-number">0</span>; k < <span class="hljs-number">8</span>; k++ { - <span class="hljs-keyword">if</span> (c & <span class="hljs-number">1</span>) != <span class="hljs-number">0</span> { - c = <span class="hljs-number">0xEDB88320</span> ^ (c >> <span class="hljs-number">1</span>) - } <span class="hljs-keyword">else</span> { - c = c >> <span class="hljs-number">1</span> - } - } - crcTable[n] = c - } - crcTableComputed = <span class="hljs-literal">true</span> -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateCrc</span><span class="hljs-params">(crc <span class="hljs-type">uint32</span>, buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">if</span> !crcTableComputed { - makeCrcTable() - } - - c := crc - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-built_in">len</span>(buf); n++ { - c = crcTable[(c^<span class="hljs-type">uint32</span>(buf[n]))&<span class="hljs-number">0xFF</span>] ^ (c >> <span class="hljs-number">8</span>) - } - <span class="hljs-keyword">return</span> c -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">crc</span><span class="hljs-params">(buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">return</span> updateCrc(<span class="hljs-number">0xFFFFFFFF</span>, buf) ^ <span class="hljs-number">0xFFFFFFFF</span> -} - -<span class="hljs-keyword">const</span> adler32Base = <span class="hljs-number">65521</span> - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateAdler32</span><span class="hljs-params">(adler <span class="hljs-type">uint32</span>, buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - s1 := adler & <span class="hljs-number">0xFFFF</span> - s2 := (adler >> <span class="hljs-number">16</span>) & <span class="hljs-number">0xFFFF</span> - - <span class="hljs-keyword">for</span> n := <span class="hljs-number">0</span>; n < <span class="hljs-built_in">len</span>(buf); n++ { - s1 = (s1 + <span class="hljs-type">uint32</span>(buf[n])) % adler32Base - s2 = (s2 + s1) % adler32Base - } - <span class="hljs-keyword">return</span> (s2 << <span class="hljs-number">16</span>) + s1 -} - -<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">adler32</span><span class="hljs-params">(buf []<span class="hljs-type">byte</span>)</span></span> <span class="hljs-type">uint32</span> { - <span class="hljs-keyword">return</span> updateAdler32(<span class="hljs-number">1</span>, buf) -}</code></pre> + <div class="codeblock" language="go"> + <pre class="shiki github-light" style="background-color:#f5f5f5;color:#24292e" tabindex="0"><code><span class="line"><span style="color:#D73A49">package</span><span style="color:#6F42C1"> main</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">import</span><span style="color:#24292E"> (</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">bytes</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">encoding/binary</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">image</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#24292E"> _ </span><span style="color:#032F62">"</span><span style="color:#6F42C1">image/png</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">io</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#032F62"> "</span><span style="color:#6F42C1">os</span><span style="color:#032F62">"</span></span> +<span class="line"><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> main</span><span style="color:#24292E">() {</span></span> +<span class="line"><span style="color:#24292E"> inFile, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> os.</span><span style="color:#6F42C1">Open</span><span style="color:#24292E">(</span><span style="color:#032F62">"input.png"</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> defer</span><span style="color:#24292E"> inFile.</span><span style="color:#6F42C1">Close</span><span style="color:#24292E">()</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> img, _, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> image.</span><span style="color:#6F42C1">Decode</span><span style="color:#24292E">(inFile)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> outFile, err </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> os.</span><span style="color:#6F42C1">Create</span><span style="color:#24292E">(</span><span style="color:#032F62">"output.png"</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> err </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> nil</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#6F42C1"> panic</span><span style="color:#24292E">(err)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> defer</span><span style="color:#24292E"> outFile.</span><span style="color:#6F42C1">Close</span><span style="color:#24292E">()</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writePng</span><span style="color:#24292E">(outFile, img)</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writePng</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">img</span><span style="color:#6F42C1"> image</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Image</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> width </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(img.</span><span style="color:#6F42C1">Bounds</span><span style="color:#24292E">().</span><span style="color:#6F42C1">Dx</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#24292E"> height </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(img.</span><span style="color:#6F42C1">Bounds</span><span style="color:#24292E">().</span><span style="color:#6F42C1">Dy</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#6F42C1"> writeSignature</span><span style="color:#24292E">(w)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIhdr</span><span style="color:#24292E">(w, width, height)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIdat</span><span style="color:#24292E">(w, width, height, img)</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunkIend</span><span style="color:#24292E">(w)</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeSignature</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> sig </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> [</span><span style="color:#005CC5">8</span><span style="color:#24292E">]</span><span style="color:#D73A49">uint8</span><span style="color:#24292E">{</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">89</span><span style="color:#24292E">,</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">50</span><span style="color:#24292E">, </span><span style="color:#6A737D">// P</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">4E</span><span style="color:#24292E">, </span><span style="color:#6A737D">// N</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">47</span><span style="color:#24292E">, </span><span style="color:#6A737D">// G</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0D</span><span style="color:#24292E">, </span><span style="color:#6A737D">// CR</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// LF</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">1A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// EOF (^Z)</span></span> +<span class="line"><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">0A</span><span style="color:#24292E">, </span><span style="color:#6A737D">// LF</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, sig)</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIhdr</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">width</span><span style="color:#24292E">, </span><span style="color:#E36209">height</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> buf </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, width)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, height)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">8</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">2</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IHDR"</span><span style="color:#24292E">, buf.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">())</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIdat</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">width</span><span style="color:#24292E">, </span><span style="color:#E36209">height</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">img</span><span style="color:#6F42C1"> image</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Image</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> pixels </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> y </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">); y </span><span style="color:#D73A49"><</span><span style="color:#24292E"> height; y</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> x </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(</span><span style="color:#005CC5">0</span><span style="color:#24292E">); x </span><span style="color:#D73A49"><</span><span style="color:#24292E"> width; x</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> r, g, b, _ </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> img.</span><span style="color:#6F42C1">At</span><span style="color:#24292E">(</span><span style="color:#D73A49">int</span><span style="color:#24292E">(x), </span><span style="color:#D73A49">int</span><span style="color:#24292E">(y)).</span><span style="color:#6F42C1">RGBA</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(r))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(g))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">pixels, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(b))</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IDAT"</span><span style="color:#24292E">, </span><span style="color:#6F42C1">encodeZlib</span><span style="color:#24292E">(pixels.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">()))</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> encodeZlib</span><span style="color:#24292E">(</span><span style="color:#E36209">data</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) []</span><span style="color:#D73A49">byte</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> buf </span><span style="color:#6F42C1">bytes</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Buffer</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">78</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#D73A49">uint8</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">01</span><span style="color:#24292E">))</span></span> +<span class="line"><span style="color:#24292E"> blockSize </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 65535</span></span> +<span class="line"><span style="color:#24292E"> isFinalBlock </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> false</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> i </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; </span><span style="color:#D73A49">!</span><span style="color:#24292E">isFinalBlock; i</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> var</span><span style="color:#24292E"> block []</span><span style="color:#D73A49">byte</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(data) </span><span style="color:#D73A49"><=</span><span style="color:#24292E"> (i</span><span style="color:#D73A49">+</span><span style="color:#005CC5">1</span><span style="color:#24292E">)</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize {</span></span> +<span class="line"><span style="color:#24292E"> block </span><span style="color:#D73A49">=</span><span style="color:#24292E"> data[i</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize:]</span></span> +<span class="line"><span style="color:#24292E"> isFinalBlock </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span></span> +<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> block </span><span style="color:#D73A49">=</span><span style="color:#24292E"> data[i</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize : (i</span><span style="color:#D73A49">+</span><span style="color:#005CC5">1</span><span style="color:#24292E">)</span><span style="color:#D73A49">*</span><span style="color:#24292E">blockSize]</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, isFinalBlock)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, </span><span style="color:#D73A49">uint16</span><span style="color:#24292E">(</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(block)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, </span><span style="color:#D73A49">uint16</span><span style="color:#24292E">(</span><span style="color:#D73A49">^</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(block)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.LittleEndian, block)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(</span><span style="color:#D73A49">&</span><span style="color:#24292E">buf, binary.BigEndian, </span><span style="color:#6F42C1">adler32</span><span style="color:#24292E">(data))</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> buf.</span><span style="color:#6F42C1">Bytes</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunkIend</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(w, </span><span style="color:#032F62">"IEND"</span><span style="color:#24292E">, </span><span style="color:#005CC5">nil</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> writeChunk</span><span style="color:#24292E">(</span><span style="color:#E36209">w</span><span style="color:#6F42C1"> io</span><span style="color:#24292E">.</span><span style="color:#6F42C1">Writer</span><span style="color:#24292E">, </span><span style="color:#E36209">chunkType</span><span style="color:#D73A49"> string</span><span style="color:#24292E">, </span><span style="color:#E36209">data</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) {</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">:=</span><span style="color:#6F42C1"> make</span><span style="color:#24292E">([]</span><span style="color:#D73A49">byte</span><span style="color:#24292E">, </span><span style="color:#005CC5">0</span><span style="color:#24292E">, </span><span style="color:#6F42C1">len</span><span style="color:#24292E">(chunkType)</span><span style="color:#D73A49">+</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(data))</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> append</span><span style="color:#24292E">(typeAndData, []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">(chunkType)</span><span style="color:#D73A49">...</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> typeAndData </span><span style="color:#D73A49">=</span><span style="color:#6F42C1"> append</span><span style="color:#24292E">(typeAndData, data</span><span style="color:#D73A49">...</span><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, </span><span style="color:#D73A49">uint32</span><span style="color:#24292E">(</span><span style="color:#6F42C1">len</span><span style="color:#24292E">(data)))</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, typeAndData)</span></span> +<span class="line"><span style="color:#24292E"> binary.</span><span style="color:#6F42C1">Write</span><span style="color:#24292E">(w, binary.BigEndian, </span><span style="color:#6F42C1">crc</span><span style="color:#24292E">(typeAndData))</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">var</span><span style="color:#24292E"> (</span></span> +<span class="line"><span style="color:#24292E"> crcTable [</span><span style="color:#005CC5">256</span><span style="color:#24292E">]</span><span style="color:#D73A49">uint32</span></span> +<span class="line"><span style="color:#24292E"> crcTableComputed </span><span style="color:#D73A49">bool</span></span> +<span class="line"><span style="color:#24292E">)</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> makeCrcTable</span><span style="color:#24292E">() {</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 256</span><span style="color:#24292E">; n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">:=</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(n)</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> k </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; k </span><span style="color:#D73A49"><</span><span style="color:#005CC5"> 8</span><span style="color:#24292E">; k</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">&</span><span style="color:#005CC5"> 1</span><span style="color:#24292E">) </span><span style="color:#D73A49">!=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">EDB88320</span><span style="color:#D73A49"> ^</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 1</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> } </span><span style="color:#D73A49">else</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#24292E"> c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 1</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> crcTable[n] </span><span style="color:#D73A49">=</span><span style="color:#24292E"> c</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#24292E"> crcTableComputed </span><span style="color:#D73A49">=</span><span style="color:#005CC5"> true</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> updateCrc</span><span style="color:#24292E">(</span><span style="color:#E36209">crc</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> if</span><span style="color:#D73A49"> !</span><span style="color:#24292E">crcTableComputed {</span></span> +<span class="line"><span style="color:#6F42C1"> makeCrcTable</span><span style="color:#24292E">()</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> crc</span></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(buf); n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> c </span><span style="color:#D73A49">=</span><span style="color:#24292E"> crcTable[(c</span><span style="color:#D73A49">^uint32</span><span style="color:#24292E">(buf[n]))</span><span style="color:#D73A49">&0x</span><span style="color:#005CC5">FF</span><span style="color:#24292E">] </span><span style="color:#D73A49">^</span><span style="color:#24292E"> (c </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 8</span><span style="color:#24292E">)</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> c</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> crc</span><span style="color:#24292E">(</span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#6F42C1"> updateCrc</span><span style="color:#24292E">(</span><span style="color:#D73A49">0x</span><span style="color:#005CC5">FFFFFFFF</span><span style="color:#24292E">, buf) </span><span style="color:#D73A49">^</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFFFFFF</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">const</span><span style="color:#005CC5"> adler32Base</span><span style="color:#D73A49"> =</span><span style="color:#005CC5"> 65521</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> updateAdler32</span><span style="color:#24292E">(</span><span style="color:#E36209">adler</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">, </span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> s1 </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> adler </span><span style="color:#D73A49">&</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFF</span></span> +<span class="line"><span style="color:#24292E"> s2 </span><span style="color:#D73A49">:=</span><span style="color:#24292E"> (adler </span><span style="color:#D73A49">>></span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">&</span><span style="color:#D73A49"> 0x</span><span style="color:#005CC5">FFFF</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49"> for</span><span style="color:#24292E"> n </span><span style="color:#D73A49">:=</span><span style="color:#005CC5"> 0</span><span style="color:#24292E">; n </span><span style="color:#D73A49"><</span><span style="color:#6F42C1"> len</span><span style="color:#24292E">(buf); n</span><span style="color:#D73A49">++</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#24292E"> s1 </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (s1 </span><span style="color:#D73A49">+</span><span style="color:#D73A49"> uint32</span><span style="color:#24292E">(buf[n])) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> adler32Base</span></span> +<span class="line"><span style="color:#24292E"> s2 </span><span style="color:#D73A49">=</span><span style="color:#24292E"> (s2 </span><span style="color:#D73A49">+</span><span style="color:#24292E"> s1) </span><span style="color:#D73A49">%</span><span style="color:#24292E"> adler32Base</span></span> +<span class="line"><span style="color:#24292E"> }</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#24292E"> (s2 </span><span style="color:#D73A49"><<</span><span style="color:#005CC5"> 16</span><span style="color:#24292E">) </span><span style="color:#D73A49">+</span><span style="color:#24292E"> s1</span></span> +<span class="line"><span style="color:#24292E">}</span></span> +<span class="line"></span> +<span class="line"><span style="color:#D73A49">func</span><span style="color:#6F42C1"> adler32</span><span style="color:#24292E">(</span><span style="color:#E36209">buf</span><span style="color:#24292E"> []</span><span style="color:#D73A49">byte</span><span style="color:#24292E">) </span><span style="color:#D73A49">uint32</span><span style="color:#24292E"> {</span></span> +<span class="line"><span style="color:#D73A49"> return</span><span style="color:#6F42C1"> updateAdler32</span><span style="color:#24292E">(</span><span style="color:#005CC5">1</span><span style="color:#24292E">, buf)</span></span> +<span class="line"><span style="color:#24292E">}</span></span></code></pre> + </div> </section> <section id="section--references"> |
