[インデックス 19720] ファイルの概要
このコミットは、Go言語の標準ライブラリ image/png
パッケージにPNG画像のインターレース(Adam7方式)のデコードサポートを追加するものです。これにより、プログレッシブ表示が可能なPNG画像をGoプログラムで正しく読み込めるようになります。
コミット
image/png: interlacing support for png.
Fixes #6293.
Image "testdata/benchRGB-interlace.png" was generated by opening "testdata/benchRGB.png" in the editor Gimp and saving it with interlacing enabled.
Benchmark:
BenchmarkDecodeRGB 500 7014194 ns/op 37.37 MB/s
ok pkg/image/png 4.657s
BenchmarkDecodeInterlacing 100 10623241 ns/op 24.68 MB/s
ok pkg/image/png 1.339s
LGTM=nigeltao
R=nigeltao, andybons, matrixik
CC=golang-codereviews
https://golang.org/cl/102130044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5c2f01f3923cae85f50260778b8ad0f9d857fd1d
元コミット内容
commit 5c2f01f3923cae85f50260778b8ad0f9d857fd1d
Author: Dustin Long <dustmop@gmail.com>
Date: Fri Jul 11 11:02:02 2014 +1000
image/png: interlacing support for png.
Fixes #6293.
Image "testdata/benchRGB-interlace.png" was generated by opening "testdata/benchRGB.png" in the editor Gimp and saving it with interlacing enabled.
Benchmark:
BenchmarkDecodeRGB 500 7014194 ns/op 37.37 MB/s
ok pkg/image/png 4.657s
BenchmarkDecodeInterlacing 100 10623241 ns/op 24.68 MB/s
ok pkg/image/png 1.339s
LGTM=nigeltao
R=nigeltao, andybons, matrixik
CC=golang-codereviews
https://golang.org/cl/102130044
変更の背景
この変更の背景には、Go言語の image/png
パッケージがPNG画像のインターレース形式(特にAdam7インターレース)をサポートしていなかったという問題があります。Issue #6293 で報告されているように、インターレースされたPNG画像をGoでデコードしようとするとエラーが発生していました。
インターレースは、画像が完全にダウンロードされる前に、低解像度から徐々に高解像度へと表示されるようにする技術です。ウェブブラウザなどで画像を読み込む際に、ユーザーに「画像が読み込まれている」という視覚的なフィードバックを提供し、ユーザーエクスペリエンスを向上させるために利用されます。PNG形式ではAdam7インターレース方式が採用されており、これは画像を7つのパスに分割して転送するものです。
このコミットは、Goの image/png
パッケージがPNG標準に完全に準拠し、より幅広いPNG画像を扱えるようにするために、インターレースデコード機能を追加する必要性から生まれました。
前提知識の解説
PNG (Portable Network Graphics)
PNGは、ラスターグラフィックスファイル形式であり、可逆圧縮をサポートしています。ウェブ上で広く使用されており、透明度(アルファチャンネル)をサポートする点が特徴です。
インターレース (Interlacing)
インターレースとは、画像を転送する際に、最初から完全な画像を一度に送るのではなく、段階的に詳細な情報を送ることで、受信側で画像が徐々に鮮明になっていくように表示させる技術です。これにより、低速なネットワーク環境でもユーザーは画像の一部を早期に視認でき、読み込み中の待機時間を短く感じることができます。
Adam7インターレース
PNGで採用されているインターレース方式は「Adam7インターレース」と呼ばれます。これは、画像を7つの「パス」に分割して転送する複雑なアルゴリズムです。各パスは、元の画像から特定のピクセルを抽出して構成されます。
Adam7インターレースの仕組みは以下の通りです。
- パス1: 8x8ピクセルのブロックの左上隅のピクセル((0,0), (8,0), (0,8), (8,8) ...)を転送します。これにより、非常に粗いプレビューが表示されます。
- パス2: パス1で転送されなかったピクセルで、8x8ブロックの(4,0), (12,0) ... の位置にあるピクセルを転送します。
- パス3: 8x4ブロックの(0,4), (8,4) ... の位置にあるピクセルを転送します。
- パス4: 4x4ブロックの(2,0), (6,0) ... の位置にあるピクセルを転送します。
- パス5: 4x2ブロックの(0,2), (4,2) ... の位置にあるピクセルを転送します。
- パス6: 2x2ブロックの(1,0), (3,0) ... の位置にあるピクセルを転送します。
- パス7: 残りのすべてのピクセルを転送します。
各パスは、元の画像よりも小さい「サブ画像」として扱われ、それぞれが独立して圧縮・転送されます。受信側では、これらのサブ画像を元の画像にマージしていくことで、徐々に詳細な画像が再構築されます。
Go言語の image
パッケージ
Go言語の image
パッケージは、様々な画像形式(PNG, JPEG, GIFなど)のエンコードとデコードを扱うための基本的なインターフェースと実装を提供します。image.Image
インターフェースは、画像データへの一般的なアクセス方法を定義しており、各画像形式のパッケージ(例: image/png
)がこのインターフェースを実装します。
技術的詳細
このコミットの主要な技術的変更点は、src/pkg/image/png/reader.go
におけるAdam7インターレースのデコードロジックの実装です。
-
インターレースタイプの定義:
itNone
(0) とitAdam7
(1) という定数が追加され、PNGのIHDRチャンクで指定されるインターレース方式を識別します。 -
interlaceScan
構造体とinterlacing
配列:interlaceScan
構造体は、Adam7インターレースの各パスにおけるピクセルの配置とサイズを定義します。xFactor
,yFactor
はピクセル間の間隔、xOffset
,yOffset
は開始オフセットを示します。interlacing
グローバル変数は、Adam7の7つのパスそれぞれのinterlaceScan
定義を配列として保持しています。これは、PNG仕様のAdam7インターレースのセクションに記載されている情報に基づいています。 -
decoder
構造体へのinterlace
フィールド追加:decoder
構造体にinterlace int
フィールドが追加され、デコード中のPNG画像がインターレースされているかどうか、およびその方式(現在はAdam7のみ)を保持します。 -
IHDRチャンクのパースロジックの変更:
parseIHDR
関数が変更され、IHDRチャンクのインターレース方式フィールド(d.tmp[12]
)を読み取り、itNone
またはitAdam7
以外の値であればFormatError
を返すようになりました。これにより、不正なインターレース方式が検出されます。 -
decode
関数のリファクタリングとインターレース処理:decode
関数は、インターレースの有無に応じて処理を分岐するようになりました。- インターレースがない場合 (
d.interlace == itNone
) は、従来の単一パスのデコード処理 (readImagePass
) を呼び出します。 - Adam7インターレースがある場合 (
d.interlace == itAdam7
) は、まずフルサイズの空の画像を割り当てます。その後、7つのパスそれぞれに対してreadImagePass
を呼び出してサブ画像をデコードし、mergePassInto
関数を使ってそのサブ画像をフルサイズの画像にマージしていきます。
- インターレースがない場合 (
-
readImagePass
関数の導入:readImagePass
は新しく導入された関数で、単一の画像パス(インターレースされていない画像全体、またはAdam7インターレースの各サブ画像)を読み込み、デコードする役割を担います。この関数は、パス番号に基づいて画像の幅と高さを計算し、それに応じてimage.NewGray
,image.NewNRGBA
などの画像オブジェクトを生成します。 -
mergePassInto
関数の導入:mergePassInto
は、Adam7インターレースの各パスでデコードされたサブ画像を、最終的なフルサイズの画像にマージする役割を担います。この関数は、各画像タイプ(*image.Alpha
,*image.Gray
,*image.NRGBA
など)に対応し、interlacing
配列で定義されたxFactor
,yFactor
,xOffset
,yOffset
を使用して、サブ画像のピクセルを正しい位置に配置します。 -
テストケースの追加:
src/pkg/image/png/reader_test.go
に、インターレースされたPNG画像 (basn3p04-31i.png
) をテストケースに追加し、BenchmarkDecodeInterlacing
ベンチマークも追加されています。これにより、インターレースデコード機能が正しく動作し、そのパフォーマンスが測定できるようになりました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に src/pkg/image/png/reader.go
に集中しています。
-
src/pkg/image/png/reader.go
:itNone
,itAdam7
定数の追加 (L57-L60)interlaceScan
構造体とinterlacing
グローバル変数の追加 (L63-L76)decoder
構造体へのinterlace
フィールド追加 (L87)parseIHDR
関数におけるインターレース方式のチェックと設定 (L113-L120)decode
関数の大幅な変更。インターレース処理の分岐とreadImagePass
,mergePassInto
の呼び出し (L287-L317)readImagePass
関数の新規追加 (L319-L507)mergePassInto
関数の新規追加 (L510-L581)- ピクセルデータ処理ループにおける
d.width
をwidth
に変更し、パスごとの幅に対応 (L370以降の多数の行)
-
src/pkg/image/png/reader_test.go
:filenames
配列にbasn3p04-31i
を追加 (L33)sng
関数にビット深度に応じたパディング処理を追加 (L186-L191)BenchmarkDecodeInterlacing
ベンチマークの追加 (L350-L352)
-
src/pkg/image/png/testdata/benchRGB-interlace.png
: 新規追加されたバイナリテストデータ -
src/pkg/image/png/testdata/pngsuite/basn3p04-31i.png
: 新規追加されたバイナリテストデータ -
src/pkg/image/png/testdata/pngsuite/basn3p04-31i.sng
: 新規追加されたテストデータ(SNG形式)
コアとなるコードの解説
reader.go
の変更
最も重要な変更は、decode
関数がインターレースの有無を判断し、Adam7インターレースの場合は7つのパスを個別にデコードし、それらを最終的な画像にマージするロジックが追加された点です。
-
interlacing
配列: この配列はAdam7インターレースの各パスの特性を定義します。例えば、{8, 8, 0, 0}
は最初のパスが8ピクセルごとにサンプリングされ、オフセットが(0,0)であることを示します。この定義に基づいて、各パスのサブ画像のサイズが計算されます。 -
readImagePass
関数: この関数は、インターレースされていない画像全体、またはインターレースされた画像の単一のパス(サブ画像)を読み込み、デコードします。重要なのは、allocateOnly
パラメータがtrue
の場合、ピクセルデータを読み込まずに画像オブジェクトだけを割り当てる点です。これは、Adam7インターレースの最初のステップでフルサイズの画像を準備するために使用されます。また、pass
番号に基づいてwidth
とheight
を計算し、各パスのサブ画像の正しいサイズを決定します。 -
mergePassInto
関数: この関数は、readImagePass
でデコードされた各サブ画像を、事前に割り当てられたフルサイズの画像にコピーします。interlacing
配列のxFactor
,yFactor
,xOffset
,yOffset
を利用して、サブ画像のピクセルがフルサイズの画像の正しい位置に配置されるように計算が行われます。これにより、7つのパスがすべてマージされると、完全な画像が再構築されます。
reader_test.go
の変更
テストファイルには、インターレースされたPNG画像 (basn3p04-31i.png
) が追加され、この画像が正しくデコードされることを確認するテストが実行されます。また、BenchmarkDecodeInterlacing
が追加されたことで、インターレースデコードのパフォーマンスを測定できるようになりました。ベンチマーク結果を見ると、インターレースデコードは通常のデコードよりも時間がかかることが示されていますが、これはAdam7インターレースの性質上、複数のパスを処理する必要があるため予想される挙動です。
関連リンク
- Go Issue #6293: image/png: interlacing support for png
- PNG (Portable Network Graphics) Specification: http://www.w3.org/TR/PNG/
- PNG Adam7 Interlacing Explanation: http://www.w3.org/TR/PNG/#8Interlace