[インデックス 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言語のテストコードの読み方