[インデックス 17969] ファイルの概要
このコミットは、Go言語の標準ライブラリである image
パッケージと image/color
パッケージに、新しいカラーモデル RGB
(24ビット) と RGB48
(48ビット) を追加するものです。これにより、より広範な画像フォーマットや色深度の表現が可能になり、特にアルファチャネルを持たない不透明なRGB画像を効率的に扱うための基盤が強化されます。
コミット
commit e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
Author: ChaiShushan <chaishushan@gmail.com>
Date: Thu Dec 12 11:24:27 2013 -0800
image: add RGB and RGB48
R=golang-dev, r, nigeltao
CC=golang-dev
https://golang.org/cl/13239051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
元コミット内容
image
: RGBおよびRGB48を追加
変更の背景
Go言語の image
パッケージは、様々な画像フォーマットを扱うための基本的なインターフェースと実装を提供します。既存の image/color
パッケージには、RGBA
(32ビット、アルファチャネル付き) や RGBA64
(64ビット、アルファチャネル付き) といったカラーモデルが存在していましたが、アルファチャネルを持たない純粋なRGBカラーモデル、特に24ビット(各チャネル8ビット)や48ビット(各チャネル16ビット)の色深度を直接表現する型が不足していました。
多くの画像フォーマット(例: JPEG、BMPの一部、TIFFの一部)は、アルファチャネルを持たないRGB形式でピクセルデータを格納します。これらのフォーマットを効率的に読み書きし、メモリ上で表現するためには、アルファチャネルの処理オーバーヘッドなしにRGBデータのみを扱う専用の型が望ましいです。
このコミットは、以下のニーズに応えるために導入されました。
- 効率的なメモリ利用: アルファチャネルが不要な場合に、その分のメモリを節約し、よりコンパクトなデータ構造で画像を表現するため。
- 広範な画像フォーマットのサポート: 24ビットRGBや48ビットRGBといった一般的な色深度を持つ画像フォーマットとの互換性を高めるため。
- 色深度の柔軟性: 各チャネル8ビット(24ビットRGB)と16ビット(48ビットRGB)の両方に対応することで、より広い色域や高精度な色表現が求められる場面(例: プロフェッショナルな画像処理、HDR画像)にも対応できるようにするため。
- 既存のGoイメージングエコシステムとの統合:
image.Image
インターフェースとimage/color.Color
インターフェースに準拠した形で新しい型を提供し、既存の画像処理ツールやライブラリとのシームレスな連携を可能にするため。
前提知識の解説
このコミットの理解には、Go言語の image
および image/color
パッケージに関する基本的な知識が必要です。
-
image/color.Color
インターフェース: Go言語における色の抽象化を表すインターフェースです。唯一のメソッドRGBA() (r, g, b, a uint32)
を持ち、任意の色をRGBA形式(各チャネル16ビット、アルファ値は0x0000から0xFFFFの範囲)で表現できるようにします。これにより、異なるカラーモデル間での色の変換や比較が可能になります。 -
image/color.Model
インターフェース: あるカラーモデルから別のカラーモデルへ色を変換するためのインターフェースです。Convert(c Color) Color
メソッドを持ち、入力されたColor
をそのモデルが表現できる最も近い色に変換して返します。例えば、RGBA
モデルは入力された色をアルファ値を持つ32ビットRGBAに変換します。 -
image.Image
インターフェース: Go言語における画像の抽象化を表すインターフェースです。以下のメソッドを持ちます。ColorModel() color.Model
: 画像のカラーモデルを返します。Bounds() image.Rectangle
: 画像の論理的な境界(ピクセル座標の範囲)を返します。At(x, y int) color.Color
: 指定された座標(x, y)
のピクセルの色をcolor.Color
インターフェースとして返します。
-
ピクセルデータ表現: Goの
image
パッケージでは、画像データは通常、Pix
という[]uint8
型のスライスに格納されます。このスライスには、各ピクセルの色チャネル値が特定の順序で並べられます。 -
Stride
:Stride
は、画像データにおいて、ある行のピクセルデータから次の行の同じX座標のピクセルデータまでのバイト数を表します。これにより、メモリ上の連続したデータから任意のピクセルを効率的に計算してアクセスできます。Stride
は通常、幅 * ピクセルあたりのバイト数
で計算されますが、アライメントやパディングのためにこれより大きくなることもあります。 -
Rect
(Rectangle):image.Rectangle
は、画像の論理的な境界を定義する構造体で、Min
(左上隅の点) とMax
(右下隅の点) の2つのimage.Point
を持ちます。これにより、画像の全体または部分領域を表現します。 -
色深度 (Bit Depth): 色深度は、各ピクセルが表現できる色の数を決定するビット数です。
- 24ビットRGB: 各色チャネル(赤、緑、青)に8ビットずつ割り当てられ、合計24ビットで1ピクセルを表現します。各チャネルは0から255までの256段階の強度を持ち、約1670万色(2^24)を表現できます。
- 48ビットRGB: 各色チャネルに16ビットずつ割り当てられ、合計48ビットで1ピクセルを表現します。各チャネルは0から65535までの65536段階の強度を持ち、非常に広い色域と滑らかなグラデーションを表現できます。これは、高精度な画像処理やHDR (High Dynamic Range) 画像で利用されます。
-
アルファチャネル: 色の透明度(不透明度)を表すチャネルです。アルファ値が0の場合は完全に透明、最大値の場合は完全に不透明です。このコミットで追加される
RGB
およびRGB48
は、アルファチャネルを持たない「不透明な」色を扱います。
技術的詳細
このコミットは、src/pkg/image/color/color.go
と src/pkg/image/image.go
の2つの主要なファイルに変更を加えています。
src/pkg/image/color/color.go
の変更
-
RGB
構造体の追加:type RGB struct { R, G, B uint8 }
- 各チャネルが
uint8
(8ビット) で表現される24ビットRGBカラーモデルを定義します。 RGBA() (r, g, b, a uint32)
メソッドを実装し、color.Color
インターフェースを満たします。この実装では、uint8
の値をuint32
に拡張し、アルファ値は常に0xFFFF
(完全に不透明) となります。r |= r << 8
のようにビットシフトとOR演算を行うことで、8ビット値を16ビット値に「複製」し、uint32
の上位16ビットと下位16ビットの両方に同じ値が入るようにしています。これは、color.Color
インターフェースのRGBA()
メソッドが16ビット精度を要求するためです。
-
RGB48
構造体の追加:type RGB48 struct { R, G, B uint16 }
- 各チャネルが
uint16
(16ビット) で表現される48ビットRGBカラーモデルを定義します。 RGBA() (r, g, b, a uint32)
メソッドを実装し、color.Color
インターフェースを満たします。uint16
の値をそのままuint32
にキャストし、アルファ値は常に0xFFFF
(完全に不透明) となります。
-
RGBModel
およびRGB48Model
の追加:var RGBModel Model = ModelFunc(rgbModel)
var RGB48Model Model = ModelFunc(rgb48Model)
- 新しいカラーモデルに対応する
color.Model
インスタンスが追加されます。これらはModelFunc
を使用して、対応する変換関数 (rgbModel
,rgb48Model
) をラップしています。
-
rgbModel
およびrgb48Model
関数の追加:func rgbModel(c Color) Color
: 入力されたColor
をRGB
型に変換します。もし入力が既にRGB
型であればそのまま返し、そうでなければRGBA()
メソッドで取得したuint32
値を8ビットに丸めてRGB
を生成します。func rgb48Model(c Color) Color
: 入力されたColor
をRGB48
型に変換します。もし入力が既にRGB48
型であればそのまま返し、そうでなければRGBA()
メソッドで取得したuint32
値をそのままuint16
にキャストしてRGB48
を生成します。
src/pkg/image/image.go
の変更
-
RGB
構造体の追加:type RGB struct { Pix []uint8; Stride int; Rect Rectangle }
- 24ビットRGB画像をメモリ上で表現するための構造体です。
Pix
: ピクセルデータを格納する[]uint8
スライス。データはR, G, Bの順で並びます。Stride
: 各行のバイト数。幅 * 3
で計算されます。Rect
: 画像の境界。ColorModel() color.Model
:color.RGBModel
を返します。Bounds() Rectangle
:Rect
を返します。At(x, y int) color.Color
: 指定された座標のピクセル色をcolor.RGB
型で返します。PixOffset(x, y int) int
: 指定された座標のピクセルデータの開始インデックスを計算します。Set(x, y int, c color.Color)
: 指定された座標のピクセル色を設定します。入力されたcolor.Color
をcolor.RGBModel
で変換してから設定します。SetRGB(x, y int, c color.RGB)
:Set
と同様ですが、引数が直接color.RGB
型であるため、変換が不要です。SubImage(r Rectangle) Image
: 指定された矩形領域のサブイメージを返します。元の画像とピクセルデータを共有します。Opaque() bool
: 常にtrue
を返します。これは、RGB
画像がアルファチャネルを持たず、常に不透明であることを示します。NewRGB(r Rectangle) *RGB
: 指定された境界を持つ新しいRGB
画像を生成し、ピクセルデータ用のバッファを割り当てます。
-
RGB48
構造体の追加:type RGB48 struct { Pix []uint8; Stride int; Rect Rectangle }
- 48ビットRGB画像をメモリ上で表現するための構造体です。
Pix
: ピクセルデータを格納する[]uint8
スライス。データはR, G, Bの順で、各チャネルがビッグエンディアン形式の2バイトで格納されます。つまり、1ピクセルあたり6バイト(R上位8ビット, R下位8ビット, G上位8ビット, G下位8ビット, B上位8ビット, B下位8ビット)です。Stride
: 各行のバイト数。幅 * 6
で計算されます。Rect
: 画像の境界。ColorModel() color.Model
:color.RGB48Model
を返します。Bounds() Rectangle
:Rect
を返します。At(x, y int) color.Color
: 指定された座標のピクセル色をcolor.RGB48
型で返します。ピクセルデータからuint16
値を再構築するために、バイトオーダー(ビッグエンディアン)を考慮したビットシフトとOR演算を行います。PixOffset(x, y int) int
: 指定された座標のピクセルデータの開始インデックスを計算します。Set(x, y int, c color.Color)
: 指定された座標のピクセル色を設定します。入力されたcolor.Color
をcolor.RGB48Model
で変換してから、各uint16
チャネルを2バイトに分割してPix
に格納します。SetRGB48(x, y int, c color.RGB48)
:Set
と同様ですが、引数が直接color.RGB48
型であるため、変換が不要です。SubImage(r Rectangle) Image
: 指定された矩形領域のサブイメージを返します。元の画像とピクセルデータを共有します。Opaque() bool
: 常にtrue
を返します。NewRGB48(r Rectangle) *RGB48
: 指定された境界を持つ新しいRGB48
画像を生成し、ピクセルデータ用のバッファを割り当てます。
src/pkg/image/image_test.go
の変更
- 新しい
RGB
およびRGB48
画像型がテストスイートに追加され、既存の画像インターフェースのテストがこれらの新しい型に対しても実行されるようにしています。これにより、新しい実装が既存のimage.Image
インターフェースの期待される振る舞いを満たしていることを確認します。
コアとなるコードの変更箇所
src/pkg/image/color/color.go
// RGB represents a traditional 24-bit fully opaque color,
// having 8 bits for each of red, green and blue.
type RGB struct {
R, G, B uint8
}
func (c RGB) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = 0xFFFF
return
}
// RGB48 represents a 48-bit fully opaque color,
// having 16 bits for each of red, green and blue.
type RGB48 struct {
R, G, B uint16
}
func (c RGB48) RGBA() (r, g, b, a uint32) {
return uint32(c.R), uint32(c.G), uint32(c.B), 0xFFFF
}
// Models for the standard color types.
var (
RGBModel Model = ModelFunc(rgbModel)
RGB48Model Model = ModelFunc(rgb48Model)
RGBAModel Model = ModelFunc(rgbaModel)
RGBA64Model Model = ModelFunc(rgba64Model)
NRGBAModel Model = ModelFunc(nrgbaModel)
NRGBA64Model Model = ModelFunc(nrgba64Model)
AlphaModel Model = ModelFunc(alphaModel)
Alpha16Model Model = ModelFunc(alpha16Model)
GrayModel Model = ModelFunc(grayModel)
Gray16Model Model = ModelFunc(gray16Model)
)
func rgbModel(c Color) Color {
if _, ok := c.(RGB); ok {
return c
}
r, g, b, _ := c.RGBA()
return RGB{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}
func rgb48Model(c Color) Color {
if _, ok := c.(RGB48); ok {
return c
}
r, g, b, _ := c.RGBA()
return RGB48{uint16(r), uint16(g), uint16(b)}
}
src/pkg/image/image.go
// RGB is an in-memory image whose At method returns color.RGB values.
type RGB struct {
// Pix holds the image's pixels, in R, G, B order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
func (p *RGB) ColorModel() color.Model { return color.RGBModel }
func (p *RGB) Bounds() Rectangle { return p.Rect }
func (p *RGB) At(x, y int) color.Color {
if !(Point{x, y}.In(p.Rect)) {
return color.RGB{}
}
i := p.PixOffset(x, y)
return color.RGB{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2]}
}
// ... (PixOffset, Set, SetRGB, SubImage, Opaque, NewRGB methods) ...
// RGB48 is an in-memory image whose At method returns color.RGB48 values.
type RGB48 struct {
// Pix holds the image's pixels, in R, G, B order and big-endian format. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*6].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
func (p *RGB48) ColorModel() color.Model { return color.RGB48Model }
func (p *RGB48) Bounds() Rectangle { return p.Rect }
func (p *RGB48) At(x, y int) color.Color {
if !(Point{x, y}.In(p.Rect)) {
return color.RGB48{}
}
i := p.PixOffset(x, y)
return color.RGB48{
uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]),
uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]),
uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]),
}
}
// ... (PixOffset, Set, SetRGB48, SubImage, Opaque, NewRGB48 methods) ...
コアとなるコードの解説
image/color/color.go
の解説
-
RGB
およびRGB48
構造体: これらは、それぞれ24ビットと48ビットのRGBカラーを表現するための基本的なデータ構造です。uint8
とuint16
を使用することで、各チャネルのビット深度を直接的に定義しています。 -
RGBA()
メソッドの実装:color.Color
インターフェースの要件を満たすために、これらの構造体はRGBA()
メソッドを実装しています。RGB.RGBA()
:uint8
のR, G, B値をuint32
に変換する際に、r |= r << 8
のようにビットを複製しています。これは、color.Color
インターフェースがuint32
で16ビット精度(0x0000から0xFFFF)を期待するためです。例えば、uint8
の0xFF
はuint32
の0xFFFF
にマッピングされます。アルファ値は常に0xFFFF
(完全に不透明) です。RGB48.RGBA()
:uint16
のR, G, B値を直接uint32
にキャストしています。これは、uint16
が既に16ビット精度であるため、追加のビット操作が不要だからです。アルファ値は同様に0xFFFF
です。
-
RGBModel
およびRGB48Model
: これらの変数は、新しいカラーモデルへの変換を可能にするcolor.Model
インスタンスを提供します。ModelFunc
は、関数をcolor.Model
インターフェースに適合させるためのヘルパーです。 -
rgbModel
およびrgb48Model
関数: これらの関数は、任意のcolor.Color
をそれぞれのRGB
またはRGB48
モデルに変換するロジックを含んでいます。- 変換時には、入力された色の
RGBA()
メソッドを呼び出してuint32
値を取得し、それを適切なビット深度(8ビットまたは16ビット)に丸めて新しいRGB
またはRGB48
インスタンスを生成します。例えば、rgbModel
ではr >> 8
を使って上位8ビットを取得し、uint8
にキャストしています。
- 変換時には、入力された色の
image/image.go
の解説
-
RGB
およびRGB48
構造体 (画像表現): これらの構造体は、実際のピクセルデータをメモリ上に保持し、image.Image
インターフェースを実装します。Pix
: 生のピクセルデータがバイトスライスとして格納されます。RGB
では1ピクセルあたり3バイト(R, G, B)、RGB48
では1ピクセルあたり6バイト(R上位, R下位, G上位, G下位, B上位, B下位)です。Stride
: 画像の行ごとのバイトオフセットを定義します。これにより、Pix
スライス内の任意のピクセルに効率的にアクセスできます。RGB
では幅 * 3
、RGB48
では幅 * 6
となります。
-
At(x, y int) color.Color
メソッド: 指定された座標のピクセル色をcolor.Color
インターフェースとして返します。RGB.At()
:Pix
スライスから3バイトを読み込み、それらを直接color.RGB
構造体のR, G, Bフィールドに割り当てます。RGB48.At()
:Pix
スライスから6バイトを読み込み、各チャネルの2バイトをビッグエンディアン形式で結合してuint16
値を再構築し、color.RGB48
構造体のR, G, Bフィールドに割り当てます。例えば、uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])
は、上位バイトを8ビット左シフトし、下位バイトとOR演算することで16ビット値を生成します。
-
Set(x, y int, c color.Color)
およびSetRGB(x, y int, c color.RGB)
/SetRGB48(x, y int, c color.RGB48)
メソッド: 指定された座標のピクセル色を設定します。Set
メソッドは、入力されたcolor.Color
を対応するカラーモデル(RGBModel
またはRGB48Model
)で変換してからピクセルデータを書き込みます。SetRGB
およびSetRGB48
は、引数が既に適切な型であるため、変換なしで直接ピクセルデータを書き込みます。RGB48.Set
およびSetRGB48
では、uint16
値を2つのuint8
バイト(上位バイトと下位バイト)に分割してPix
スライスに格納するロジックが含まれます。
-
Opaque() bool
メソッド: これらの画像型はアルファチャネルを持たないため、常にtrue
を返します。これは、画像が完全に不透明であることを示します。 -
NewRGB
およびNewRGB48
関数: これらのコンストラクタ関数は、指定された境界を持つ新しい画像インスタンスを生成し、ピクセルデータを格納するための適切なサイズの[]uint8
スライスを初期化します。
これらの変更により、Goの image
パッケージは、アルファチャネルを持たない一般的なRGB画像フォーマットをより効率的かつ自然に扱うことができるようになり、画像処理アプリケーションの柔軟性が向上しました。
関連リンク
- Go言語
image
パッケージ公式ドキュメント: https://pkg.go.dev/image - Go言語
image/color
パッケージ公式ドキュメント: https://pkg.go.dev/image/color - Go言語の画像処理に関するブログ記事やチュートリアル (一般的な情報源):
- A Tour of Go: Images: https://go.dev/tour/moretypes/23
- The Go Blog: The Go image package: https://go.dev/blog/go-image-package
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/17969.txt
- GitHubコミットページ: https://github.com/golang/go/commit/e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
- Go言語の公式ドキュメント (
image
およびimage/color
パッケージ) - 一般的な画像処理および色深度に関する知識
- Go言語のビット操作に関する知識
- Go言語のインターフェースと構造体の実装に関する知識
- Go言語のテストコードの読み方# [インデックス 17969] ファイルの概要
このコミットは、Go言語の標準ライブラリである image
パッケージと image/color
パッケージに、新しいカラーモデル RGB
(24ビット) と RGB48
(48ビット) を追加するものです。これにより、より広範な画像フォーマットや色深度の表現が可能になり、特にアルファチャネルを持たない不透明なRGB画像を効率的に扱うための基盤が強化されます。
コミット
commit e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
Author: ChaiShushan <chaishushan@gmail.com>
Date: Thu Dec 12 11:24:27 2013 -0800
image: add RGB and RGB48
R=golang-dev, r, nigeltao
CC=golang-dev
https://golang.org/cl/13239051
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
元コミット内容
image
: RGBおよびRGB48を追加
変更の背景
Go言語の image
パッケージは、様々な画像フォーマットを扱うための基本的なインターフェースと実装を提供します。既存の image/color
パッケージには、RGBA
(32ビット、アルファチャネル付き) や RGBA64
(64ビット、アルファチャネル付き) といったカラーモデルが存在していましたが、アルファチャネルを持たない純粋なRGBカラーモデル、特に24ビット(各チャネル8ビット)や48ビット(各チャネル16ビット)の色深度を直接表現する型が不足していました。
多くの画像フォーマット(例: JPEG、BMPの一部、TIFFの一部)は、アルファチャネルを持たないRGB形式でピクセルデータを格納します。これらのフォーマットを効率的に読み書きし、メモリ上で表現するためには、アルファチャネルの処理オーバーヘッドなしにRGBデータのみを扱う専用の型が望ましいです。
このコミットは、以下のニーズに応えるために導入されました。
- 効率的なメモリ利用: アルファチャネルが不要な場合に、その分のメモリを節約し、よりコンパクトなデータ構造で画像を表現するため。
- 広範な画像フォーマットのサポート: 24ビットRGBや48ビットRGBといった一般的な色深度を持つ画像フォーマットとの互換性を高めるため。
- 色深度の柔軟性: 各チャネル8ビット(24ビットRGB)と16ビット(48ビットRGB)の両方に対応することで、より広い色域や高精度な色表現が求められる場面(例: プロフェッショナルな画像処理、HDR画像)にも対応できるようにするため。
- 既存のGoイメージングエコシステムとの統合:
image.Image
インターフェースとimage/color.Color
インターフェースに準拠した形で新しい型を提供し、既存の画像処理ツールやライブラリとのシームレスな連携を可能にするため。
前提知識の解説
このコミットの理解には、Go言語の image
および image/color
パッケージに関する基本的な知識が必要です。
-
image/color.Color
インターフェース: Go言語における色の抽象化を表すインターフェースです。唯一のメソッドRGBA() (r, g, b, a uint32)
を持ち、任意の色をRGBA形式(各チャネル16ビット、アルファ値は0x0000から0xFFFFの範囲)で表現できるようにします。これにより、異なるカラーモデル間での色の変換や比較が可能になります。 -
image/color.Model
インターフェース: あるカラーモデルから別のカラーモデルへ色を変換するためのインターフェースです。Convert(c Color) Color
メソッドを持ち、入力されたColor
をそのモデルが表現できる最も近い色に変換して返します。例えば、RGBA
モデルは入力された色をアルファ値を持つ32ビットRGBAに変換します。 -
image.Image
インターフェース: Go言語における画像の抽象化を表すインターフェースです。以下のメソッドを持ちます。ColorModel() color.Model
: 画像のカラーモデルを返します。Bounds() image.Rectangle
: 画像の論理的な境界(ピクセル座標の範囲)を返します。At(x, y int) color.Color
: 指定された座標(x, y)
のピクセルの色をcolor.Color
インターフェースとして返します。
-
ピクセルデータ表現: Goの
image
パッケージでは、画像データは通常、Pix
という[]uint8
型のスライスに格納されます。このスライスには、各ピクセルの色チャネル値が特定の順序で並べられます。 -
Stride
:Stride
は、画像データにおいて、ある行のピクセルデータから次の行の同じX座標のピクセルデータまでのバイト数を表します。これにより、メモリ上の連続したデータから任意のピクセルを効率的に計算してアクセスできます。Stride
は通常、幅 * ピクセルあたりのバイト数
で計算されますが、アライメントやパディングのためにこれより大きくなることもあります。 -
Rect
(Rectangle):image.Rectangle
は、画像の論理的な境界を定義する構造体で、Min
(左上隅の点) とMax
(右下隅の点) の2つのimage.Point
を持ちます。これにより、画像の全体または部分領域を表現します。 -
色深度 (Bit Depth): 色深度は、各ピクセルが表現できる色の数を決定するビット数です。
- 24ビットRGB: 各色チャネル(赤、緑、青)に8ビットずつ割り当てられ、合計24ビットで1ピクセルを表現します。各チャネルは0から255までの256段階の強度を持ち、約1670万色(2^24)を表現できます。
- 48ビットRGB: 各色チャネルに16ビットずつ割り当てられ、合計48ビットで1ピクセルを表現します。各チャネルは0から65535までの65536段階の強度を持ち、非常に広い色域と滑らかなグラデーションを表現できます。これは、高精度な画像処理やHDR (High Dynamic Range) 画像で利用されます。
-
アルファチャネル: 色の透明度(不透明度)を表すチャネルです。アルファ値が0の場合は完全に透明、最大値の場合は完全に不透明です。このコミットで追加される
RGB
およびRGB48
は、アルファチャネルを持たない「不透明な」色を扱います。
技術的詳細
このコミットは、src/pkg/image/color/color.go
と src/pkg/image/image.go
の2つの主要なファイルに変更を加えています。
src/pkg/image/color/color.go
の変更
-
RGB
構造体の追加:type RGB struct { R, G, B uint8 }
- 各チャネルが
uint8
(8ビット) で表現される24ビットRGBカラーモデルを定義します。 RGBA() (r, g, b, a uint32)
メソッドを実装し、color.Color
インターフェースを満たします。この実装では、uint8
の値をuint32
に拡張し、アルファ値は常に0xFFFF
(完全に不透明) となります。r |= r << 8
のようにビットシフトとOR演算を行うことで、8ビット値を16ビット値に「複製」し、uint32
の上位16ビットと下位16ビットの両方に同じ値が入るようにしています。これは、color.Color
インターフェースのRGBA()
メソッドが16ビット精度を要求するためです。
-
RGB48
構造体の追加:type RGB48 struct { R, G, B uint16 }
- 各チャネルが
uint16
(16ビット) で表現される48ビットRGBカラーモデルを定義します。 RGBA() (r, g, b, a uint32)
メソッドを実装し、color.Color
インターフェースを満たします。uint16
の値をそのままuint32
にキャストし、アルファ値は常に0xFFFF
(完全に不透明) となります。
-
RGBModel
およびRGB48Model
の追加:var RGBModel Model = ModelFunc(rgbModel)
var RGB48Model Model = ModelFunc(rgb48Model)
- 新しいカラーモデルに対応する
color.Model
インスタンスが追加されます。これらはModelFunc
を使用して、対応する変換関数 (rgbModel
,rgb48Model
) をラップしています。
-
rgbModel
およびrgb48Model
関数の追加:func rgbModel(c Color) Color
: 入力されたColor
をRGB
型に変換します。もし入力が既にRGB
型であればそのまま返し、そうでなければRGBA()
メソッドで取得したuint32
値を8ビットに丸めてRGB
を生成します。func rgb48Model(c Color) Color
: 入力されたColor
をRGB48
型に変換します。もし入力が既にRGB48
型であればそのまま返し、そうでなければRGBA()
メソッドで取得したuint32
値をそのままuint16
にキャストしてRGB48
を生成します。
src/pkg/image/image.go
の変更
-
RGB
構造体の追加:type RGB struct { Pix []uint8; Stride int; Rect Rectangle }
- 24ビットRGB画像をメモリ上で表現するための構造体です。
Pix
: ピクセルデータを格納する[]uint8
スライス。データはR, G, Bの順で並びます。Stride
: 各行のバイト数。幅 * 3
で計算されます。Rect
: 画像の境界。ColorModel() color.Model
:color.RGBModel
を返します。Bounds() Rectangle
:Rect
を返します。At(x, y int) color.Color
: 指定された座標のピクセル色をcolor.RGB
型で返します。PixOffset(x, y int) int
: 指定された座標のピクセルデータの開始インデックスを計算します。Set(x, y int, c color.Color)
: 指定された座標のピクセル色を設定します。入力されたcolor.Color
をcolor.RGBModel
で変換してから設定します。SetRGB(x, y int, c color.RGB)
:Set
と同様ですが、引数が直接color.RGB
型であるため、変換が不要です。SubImage(r Rectangle) Image
: 指定された矩形領域のサブイメージを返します。元の画像とピクセルデータを共有します。Opaque() bool
: 常にtrue
を返します。これは、RGB
画像がアルファチャネルを持たず、常に不透明であることを示します。NewRGB(r Rectangle) *RGB
: 指定された境界を持つ新しいRGB
画像を生成し、ピクセルデータ用のバッファを割り当てます。
-
RGB48
構造体の追加:type RGB48 struct { Pix []uint8; Stride int; Rect Rectangle }
- 48ビットRGB画像をメモリ上で表現するための構造体です。
Pix
: ピクセルデータを格納する[]uint8
スライス。データはR, G, Bの順で、各チャネルがビッグエンディアン形式の2バイトで格納されます。つまり、1ピクセルあたり6バイト(R上位8ビット, R下位8ビット, G上位8ビット, G下位8ビット, B上位8ビット, B下位8ビット)です。Stride
: 各行のバイト数。幅 * 6
で計算されます。Rect
: 画像の境界。ColorModel() color.Model
:color.RGB48Model
を返します。Bounds() Rectangle
:Rect
を返します。At(x, y int) color.Color
: 指定された座標のピクセル色をcolor.RGB48
型で返します。ピクセルデータからuint16
値を再構築するために、バイトオーダー(ビッグエンディアン)を考慮したビットシフトとOR演算を行います。PixOffset(x, y int) int
: 指定された座標のピクセルデータの開始インデックスを計算します。Set(x, y int, c color.Color)
: 指定された座標のピクセル色を設定します。入力されたcolor.Color
をcolor.RGB48Model
で変換してから、各uint16
チャネルを2バイトに分割してPix
に格納します。SetRGB48(x, y int, c color.RGB48)
:Set
と同様ですが、引数が直接color.RGB48
型であるため、変換が不要です。SubImage(r Rectangle) Image
: 指定された矩形領域のサブイメージを返します。元の画像とピクセルデータを共有します。Opaque() bool
: 常にtrue
を返します。NewRGB48(r Rectangle) *RGB48
: 指定された境界を持つ新しいRGB48
画像を生成し、ピクセルデータ用のバッファを割り当てます。
src/pkg/image/image_test.go
の変更
- 新しい
RGB
およびRGB48
画像型がテストスイートに追加され、既存の画像インターフェースのテストがこれらの新しい型に対しても実行されるようにしています。これにより、新しい実装が既存のimage.Image
インターフェースの期待される振る舞いを満たしていることを確認します。
コアとなるコードの変更箇所
src/pkg/image/color/color.go
// RGB represents a traditional 24-bit fully opaque color,
// having 8 bits for each of red, green and blue.
type RGB struct {
R, G, B uint8
}
func (c RGB) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = 0xFFFF
return
}
// RGB48 represents a 48-bit fully opaque color,
// having 16 bits for each of red, green and blue.
type RGB48 struct {
R, G, B uint16
}
func (c RGB48) RGBA() (r, g, b, a uint32) {
return uint32(c.R), uint32(c.G), uint32(c.B), 0xFFFF
}
// Models for the standard color types.
var (
RGBModel Model = ModelFunc(rgbModel)
RGB48Model Model = ModelFunc(rgb48Model)
RGBAModel Model = ModelFunc(rgbaModel)
RGBA64Model Model = ModelFunc(rgba64Model)
NRGBAModel Model = ModelFunc(nrgbaModel)
NRGBA64Model Model = Model = ModelFunc(nrgba64Model)
AlphaModel Model = ModelFunc(alphaModel)
Alpha16Model Model = ModelFunc(alpha16Model)
GrayModel Model = ModelFunc(grayModel)
Gray16Model Model = ModelFunc(gray16Model)
)
func rgbModel(c Color) Color {
if _, ok := c.(RGB); ok {
return c
}
r, g, b, _ := c.RGBA()
return RGB{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8)}
}
func rgb48Model(c Color) Color {
if _, ok := c.(RGB48); ok {
return c
}
r, g, b, _ := c.RGBA()
return RGB48{uint16(r), uint16(g), uint16(b)}
}
src/pkg/image/image.go
// RGB is an in-memory image whose At method returns color.RGB values.
type RGB struct {
// Pix holds the image's pixels, in R, G, B order. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*3].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
func (p *RGB) ColorModel() color.Model { return color.RGBModel }
func (p *RGB) Bounds() Rectangle { return p.Rect }
func (p *RGB) At(x, y int) color.Color {
if !(Point{x, y}.In(p.Rect)) {
return color.RGB{}
}
i := p.PixOffset(x, y)
return color.RGB{p.Pix[i+0], p.Pix[i+1], p.Pix[i+2]}
}
// ... (PixOffset, Set, SetRGB, SubImage, Opaque, NewRGB methods) ...
// RGB48 is an in-memory image whose At method returns color.RGB48 values.
type RGB48 struct {
// Pix holds the image's pixels, in R, G, B order and big-endian format. The pixel at
// (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*6].
Pix []uint8
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
Stride int
// Rect is the image's bounds.
Rect Rectangle
}
func (p *RGB48) ColorModel() color.Model { return color.RGB48Model }
func (p *RGB48) Bounds() Rectangle { return p.Rect }
func (p *RGB48) At(x, y int) color.Color {
if !(Point{x, y}.In(p.Rect)) {
return color.RGB48{}
}
i := p.PixOffset(x, y)
return color.RGB48{
uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1]),
uint16(p.Pix[i+2])<<8 | uint16(p.Pix[i+3]),
uint16(p.Pix[i+4])<<8 | uint16(p.Pix[i+5]),
}
}
// ... (PixOffset, Set, SetRGB48, SubImage, Opaque, NewRGB48 methods) ...
コアとなるコードの解説
image/color/color.go
の解説
-
RGB
およびRGB48
構造体: これらは、それぞれ24ビットと48ビットのRGBカラーを表現するための基本的なデータ構造です。uint8
とuint16
を使用することで、各チャネルのビット深度を直接的に定義しています。 -
RGBA()
メソッドの実装:color.Color
インターフェースの要件を満たすために、これらの構造体はRGBA()
メソッドを実装しています。RGB.RGBA()
:uint8
のR, G, B値をuint32
に変換する際に、r |= r << 8
のようにビットを複製しています。これは、color.Color
インターフェースがuint32
で16ビット精度(0x0000から0xFFFF)を期待するためです。例えば、uint8
の0xFF
はuint32
の0xFFFF
にマッピングされます。アルファ値は常に0xFFFF
(完全に不透明) です。RGB48.RGBA()
:uint16
のR, G, B値を直接uint32
にキャストしています。これは、uint16
が既に16ビット精度であるため、追加のビット操作が不要だからです。アルファ値は同様に0xFFFF
です。
-
RGBModel
およびRGB48Model
: これらの変数は、新しいカラーモデルへの変換を可能にするcolor.Model
インスタンスを提供します。ModelFunc
は、関数をcolor.Model
インターフェースに適合させるためのヘルパーです。 -
rgbModel
およびrgb48Model
関数: これらの関数は、任意のcolor.Color
をそれぞれのRGB
またはRGB48
モデルに変換するロジックを含んでいます。- 変換時には、入力された色の
RGBA()
メソッドを呼び出してuint32
値を取得し、それを適切なビット深度(8ビットまたは16ビット)に丸めて新しいRGB
またはRGB48
インスタンスを生成します。例えば、rgbModel
ではr >> 8
を使って上位8ビットを取得し、uint8
にキャストしています。
- 変換時には、入力された色の
image/image.go
の解説
-
RGB
およびRGB48
構造体 (画像表現): これらの構造体は、実際のピクセルデータをメモリ上に保持し、image.Image
インターフェースを実装します。Pix
: 生のピクセルデータがバイトスライスとして格納されます。RGB
では1ピクセルあたり3バイト(R, G, B)、RGB48
では1ピクセルあたり6バイト(R上位, R下位, G上位, G下位, B上位, B下位)です。Stride
: 画像の行ごとのバイトオフセットを定義します。これにより、Pix
スライス内の任意のピクセルに効率的にアクセスできます。RGB
では幅 * 3
、RGB48
では幅 * 6
となります。
-
At(x, y int) color.Color
メソッド: 指定された座標のピクセル色をcolor.Color
インターフェースとして返します。RGB.At()
:Pix
スライスから3バイトを読み込み、それらを直接color.RGB
構造体のR, G, Bフィールドに割り当てます。RGB48.At()
:Pix
スライスから6バイトを読み込み、各チャネルの2バイトをビッグエンディアン形式で結合してuint16
値を再構築し、color.RGB48
構造体のR, G, Bフィールドに割り当てます。例えば、uint16(p.Pix[i+0])<<8 | uint16(p.Pix[i+1])
は、上位バイトを8ビット左シフトし、下位バイトとOR演算することで16ビット値を生成します。
-
Set(x, y int, c color.Color)
およびSetRGB(x, y int, c color.RGB)
/SetRGB48(x, y int, c color.RGB48)
メソッド: 指定された座標のピクセル色を設定します。Set
メソッドは、入力されたcolor.Color
を対応するカラーモデル(RGBModel
またはRGB48Model
)で変換してからピクセルデータを書き込みます。SetRGB
およびSetRGB48
は、引数が既に適切な型であるため、変換なしで直接ピクセルデータを書き込みます。RGB48.Set
およびSetRGB48
では、uint16
値を2つのuint8
バイト(上位バイトと下位バイト)に分割してPix
スライスに格納するロジックが含まれます。
-
Opaque() bool
メソッド: これらの画像型はアルファチャネルを持たないため、常にtrue
を返します。これは、画像が完全に不透明であることを示します。 -
NewRGB
およびNewRGB48
関数: これらのコンストラクタ関数は、指定された境界を持つ新しい画像インスタンスを生成し、ピクセルデータを格納するための適切なサイズの[]uint8
スライスを初期化します。
これらの変更により、Goの image
パッケージは、アルファチャネルを持たない一般的なRGB画像フォーマットをより効率的かつ自然に扱うことができるようになり、画像処理アプリケーションの柔軟性が向上しました。
関連リンク
- Go言語
image
パッケージ公式ドキュメント: https://pkg.go.dev/image - Go言語
image/color
パッケージ公式ドキュメント: https://pkg.go.dev/image/color - Go言語の画像処理に関するブログ記事やチュートリアル (一般的な情報源):
- A Tour of Go: Images: https://go.dev/tour/moretypes/23
- The Go Blog: The Go image package: https://go.dev/blog/go-image-package
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/17969.txt
- GitHubコミットページ: https://github.com/golang/go/commit/e5902fc70f1a50920ce0ac95ffbed3a9af2b80b5
- Go言語の公式ドキュメント (
image
およびimage/color
パッケージ) - 一般的な画像処理および色深度に関する知識
- Go言語のビット操作に関する知識
- Go言語のインターフェースと構造体の実装に関する知識
- Go言語のテストコードの読み方