Go言語でピクセル補完的なプログラムを書いてみる その6

4隅に指定した色で画像をグラデーションで塗りつぶし、PNGフォーマットでファイルに保存する

今回からやっと補完ぽいものが出ます。
画像の4隅の色を指定して、その間の色を補完してグラデーションを作ります。
ソースコードは最後に張ってあります。


ある二つの数値AとBがあります。
weightが0.0の時にA、1.0の時にBとすると、その間にある値は次の式で求められます。


// 0.0 <= weight <= 1.0
C = weight * (B-A) + A

例えばAとBのちょうど中間の値を出したいときは0.5 * (B-A) + Aになります。
最初の書ける数値は0.0から1.0の値になります。
ピクセルの要素も数値なので同じように出来ます。


実は今回の色の混ぜ合わせは、アルファ値が完全な不透明の時のみ正しくできます。
透明の時の方法は次の回にします。


blend関数はpixel0とpixel1をweightの値で混ぜ合わた色を返します。
weightは0以上1.0以下の値になります。


func blend(pixel0, pixel1 color.NRGBA, weight float64) color.NRGBA {
	red := uint8((float64(pixel1.R)-float64(pixel0.R))*weight + float64(pixel0.R))
	green := uint8((float64(pixel1.G)-float64(pixel0.G))*weight + float64(pixel0.G))
	blue := uint8((float64(pixel1.B)-float64(pixel0.B))*weight + float64(pixel0.B))
	alpha := uint8((float64(pixel1.A)-float64(pixel0.A))*weight + float64(pixel0.A))
	return color.NRGBA{R: red, G: green, B: blue, A: alpha}
}

interpolation関数はcolor.NRGBA型のサイズが4の配列で隅の色を指定して、その間の色を補完してimgを塗りつぶします。
色の順番は左上、右上、左下、右下の順です。


func interpolation(img *image.NRGBA, cols *[4]color.NRGBA) error {
    if img == nil {
        return errors.New("img is nil")
    }
    rect := img.Rect
    size := rect.Size()
    for v := 0; v < size.Y; v++ {
        weightY := float64(v) / float64(size.Y-1)
        leftCol := blend(cols[0], cols[2], weightY)
        rightCol := blend(cols[1], cols[3], weightY)
        y := v + rect.Min.Y
        for h := 0; h < size.X; h++ {
            weightX := float64(h) / float64(size.X-1)
            col := blend(leftCol, rightCol, weightX)
            x := h + rect.Min.X
            img.Set(x, y, col)
        }
    }
    return nil
}

まず求めたいピクセルのY座標にあるX座標が一番左のピクセルの色と一番右のピクセルの色を求めます。
そしてその二つのピクセルの色から求めたいピクセルの色を求めます。
次の図を見るとわかりやすと思います。

interpolation0.png

X座標とY座標を逆にして求めても同じ値になります。


4隅のカラーをcolor.NRGBAの配列に入れています。
順に左上、右上、左下、右下です。色々値を変えてみましょう。


cols := [4]color.NRGBA{
    {R: 255, G: 0, B: 0, A: 255},
    {R: 0, G: 0, B: 255, A: 255},
    {R: 0, G: 255, B: 0, A: 255},
    {R: 0, G: 0, B: 0, A: 255}}

Playgroundとソースコードです。
二つは画像の表示とファイルへ保存するコードが違います。
次回は半透明のカラーのブレンド処理を説明します。


package main
import (
    "errors"
    "fmt"
    "image"
    "image/color"
    "image/png"
    "log"
    "os"
)
func main() {
    rect := image.Rect(0, 0, 100, 100)
    img := image.NewNRGBA(rect)
    cols := [4]color.NRGBA{
        {R: 255, G: 0, B: 0, A: 255},
        {R: 0, G: 0, B: 255, A: 255},
        {R: 0, G: 255, B: 0, A: 255},
        {R: 0, G: 0, B: 0, A: 255}}
    err := interpolation(img, &cols)
    if err != nil {
        log.Fatal(err)
    }
    f, err := os.Create("image.png")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    if err := png.Encode(f, img); err != nil {
        f.Close()
        log.Fatal(err)
    }
}
func blend(pixel0, pixel1 color.NRGBA, weight float64) color.NRGBA {
	red := uint8((float64(pixel1.R)-float64(pixel0.R))*weight + float64(pixel0.R))
	green := uint8((float64(pixel1.G)-float64(pixel0.G))*weight + float64(pixel0.G))
	blue := uint8((float64(pixel1.B)-float64(pixel0.B))*weight + float64(pixel0.B))
	alpha := uint8((float64(pixel1.A)-float64(pixel0.A))*weight + float64(pixel0.A))
	return color.NRGBA{R: red, G: green, B: blue, A: alpha}
}
func interpolation(img *image.NRGBA, cols *[4]color.NRGBA) error {
    if img == nil {
        return errors.New("img is nil")
    }
    rect := img.Rect
    size := rect.Size()
    for v := 0; v < size.Y; v++ {
        weightY := float64(v) / float64(size.Y-1)
        leftCol := blend(cols[0], cols[2], weightY)
        rightCol := blend(cols[1], cols[3], weightY)
        y := v + rect.Min.Y
        for h := 0; h < size.X; h++ {
            weightX := float64(h) / float64(size.X-1)
            col := blend(leftCol, rightCol, weightX)
            x := h + rect.Min.X
            img.Set(x, y, col)
        }
    }
    return nil
}

この記事へのコメント

最近のトラックバック