Bug fix: Consider color.Color.RGBA returns alpha-premultiplied values

This commit is contained in:
Hajime Hoshi 2014-12-22 00:02:14 +09:00
parent d34c94aa09
commit 99434fa9d3
11 changed files with 67 additions and 53 deletions

View File

@ -17,7 +17,6 @@ limitations under the License.
package ebiten package ebiten
import ( import (
"image/color"
"math" "math"
) )
@ -81,28 +80,26 @@ func Monochrome() ColorMatrix {
} }
} }
// ScaleColor returns a color matrix that scales a color matrix by clr. // ScaleColor returns a color matrix that scales a color matrix by the given color (r, g, b, a).
func ScaleColor(clr color.Color) ColorMatrix { func ScaleColor(r, g, b, a float64) ColorMatrix {
rf, gf, bf, af := rgba(clr)
return ColorMatrix{ return ColorMatrix{
[ColorMatrixDim - 1][ColorMatrixDim]float64{ [ColorMatrixDim - 1][ColorMatrixDim]float64{
{rf, 0, 0, 0, 0}, {r, 0, 0, 0, 0},
{0, gf, 0, 0, 0}, {0, g, 0, 0, 0},
{0, 0, bf, 0, 0}, {0, 0, b, 0, 0},
{0, 0, 0, af, 0}, {0, 0, 0, a, 0},
}, },
} }
} }
// TranslateColor returns a color matrix that translates a color matrix by clr. // TranslateColor returns a color matrix that translates a color matrix by the given color (r, g, b, a).
func TranslateColor(clr color.Color) ColorMatrix { func TranslateColor(r, g, b, a float64) ColorMatrix {
rf, gf, bf, af := rgba(clr)
return ColorMatrix{ return ColorMatrix{
[ColorMatrixDim - 1][ColorMatrixDim]float64{ [ColorMatrixDim - 1][ColorMatrixDim]float64{
{1, 0, 0, 0, rf}, {1, 0, 0, 0, r},
{0, 1, 0, 0, gf}, {0, 1, 0, 0, g},
{0, 0, 1, 0, bf}, {0, 0, 1, 0, b},
{0, 0, 0, 1, af}, {0, 0, 0, 1, a},
}, },
} }
} }
@ -123,11 +120,11 @@ func RotateHue(theta float64) ColorMatrix {
} }
} }
func rgba(clr color.Color) (float64, float64, float64, float64) { func rgba(r, g, b, a uint8) (float64, float64, float64, float64) {
r, g, b, a := clr.RGBA() const max = math.MaxUint8
rf := float64(r) / float64(math.MaxUint16) rf := float64(r) / max
gf := float64(g) / float64(math.MaxUint16) gf := float64(g) / max
bf := float64(b) / float64(math.MaxUint16) bf := float64(b) / max
af := float64(a) / float64(math.MaxUint16) af := float64(a) / max
return rf, gf, bf, af return rf, gf, bf, af
} }

View File

@ -18,6 +18,7 @@ package ebitenutil
import ( import (
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/internal"
"github.com/hajimehoshi/ebiten/internal/assets" "github.com/hajimehoshi/ebiten/internal/assets"
"image/color" "image/color"
) )
@ -34,7 +35,7 @@ func DebugPrint(r *ebiten.RenderTarget, str string) {
defaultDebugPrintState.DebugPrint(r, str) defaultDebugPrintState.DebugPrint(r, str)
} }
func (d *debugPrintState) drawText(r *ebiten.RenderTarget, str string, x, y int, clr color.Color) { func (d *debugPrintState) drawText(rt *ebiten.RenderTarget, str string, x, y int, c color.Color) {
parts := []ebiten.ImagePart{} parts := []ebiten.ImagePart{}
locationX, locationY := 0, 0 locationX, locationY := 0, 0
for _, c := range str { for _, c := range str {
@ -53,11 +54,10 @@ func (d *debugPrintState) drawText(r *ebiten.RenderTarget, str string, x, y int,
}) })
locationX += assets.TextImageCharWidth locationX += assets.TextImageCharWidth
} }
geom := ebiten.GeometryMatrixI() geo := ebiten.TranslateGeometry(float64(x)+1, float64(y))
geom.Concat(ebiten.TranslateGeometry(float64(x)+1, float64(y))) r, g, b, a := internal.RGBA(c)
clrm := ebiten.ColorMatrixI() clr := ebiten.ScaleColor(r, g, b, a)
clrm.Concat(ebiten.ScaleColor(clr)) rt.DrawImage(d.textTexture, parts, geo, clr)
r.DrawImage(d.textTexture, parts, geom, clrm)
} }
func (d *debugPrintState) DebugPrint(r *ebiten.RenderTarget, str string) { func (d *debugPrintState) DebugPrint(r *ebiten.RenderTarget, str string) {
@ -79,6 +79,6 @@ func (d *debugPrintState) DebugPrint(r *ebiten.RenderTarget, str string) {
panic(err) panic(err)
} }
} }
d.drawText(r, str, 1, d.y+1, &color.RGBA{0x00, 0x00, 0x00, 0x80}) d.drawText(r, str, 1, d.y+1, color.NRGBA{0x00, 0x00, 0x00, 0x80})
d.drawText(r, str, 0, d.y, &color.RGBA{0xff, 0xff, 0xff, 0xff}) d.drawText(r, str, 0, d.y, color.NRGBA{0xff, 0xff, 0xff, 0xff})
} }

View File

@ -52,13 +52,13 @@ func (g *Game) Update(r *ebiten.RenderTarget) error {
} }
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20) geo := ebiten.TranslateGeometry(15+float64(i)*(diff), 20)
clr := ebiten.ScaleColor(color.RGBA{0xff, 0xff, 0xff, 0x80}) clr := ebiten.ScaleColor(1.0, 1.0, 1.0, 0.5)
if err := ebiten.DrawWholeImage(g.tmpRenderTarget, g.ebitenImage, geo, clr); err != nil { if err := ebiten.DrawWholeImage(g.tmpRenderTarget, g.ebitenImage, geo, clr); err != nil {
return err return err
} }
} }
r.Fill(color.RGBA{0x00, 0x00, 0x80, 0xff}) r.Fill(color.NRGBA{0x00, 0x00, 0x80, 0xff})
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
geo := ebiten.TranslateGeometry(0, float64(i)*(diff)) geo := ebiten.TranslateGeometry(0, float64(i)*(diff))
clr := ebiten.ColorMatrixI() clr := ebiten.ColorMatrixI()

View File

@ -19,6 +19,7 @@ package blocks
import ( import (
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"image/color" "image/color"
"math"
) )
func init() { func init() {
@ -32,7 +33,7 @@ func textWidth(str string) int {
return charWidth * len(str) return charWidth * len(str)
} }
func drawText(r *ebiten.RenderTarget, images *Images, str string, ox, oy, scale int, clr color.Color) { func drawText(rt *ebiten.RenderTarget, images *Images, str string, ox, oy, scale int, c color.Color) {
fontImageId := images.GetImage("font") fontImageId := images.GetImage("font")
parts := []ebiten.ImagePart{} parts := []ebiten.ImagePart{}
@ -53,13 +54,19 @@ func drawText(r *ebiten.RenderTarget, images *Images, str string, ox, oy, scale
locationX += charWidth locationX += charWidth
} }
geoMat := ebiten.ScaleGeometry(float64(scale), float64(scale)) geo := ebiten.ScaleGeometry(float64(scale), float64(scale))
geoMat.Concat(ebiten.TranslateGeometry(float64(ox), float64(oy))) geo.Concat(ebiten.TranslateGeometry(float64(ox), float64(oy)))
clrMat := ebiten.ScaleColor(clr) c2 := color.NRGBA64Model.Convert(c).(color.NRGBA64)
r.DrawImage(fontImageId, parts, geoMat, clrMat) const max = math.MaxUint16
r := float64(c2.R) / max
g := float64(c2.G) / max
b := float64(c2.B) / max
a := float64(c2.A) / max
clr := ebiten.ScaleColor(r, g, b, a)
rt.DrawImage(fontImageId, parts, geo, clr)
} }
func drawTextWithShadow(r *ebiten.RenderTarget, images *Images, str string, x, y, scale int, clr color.Color) { func drawTextWithShadow(rt *ebiten.RenderTarget, images *Images, str string, x, y, scale int, clr color.Color) {
drawText(r, images, str, x+1, y+1, scale, color.RGBA{0, 0, 0, 0x80}) drawText(rt, images, str, x+1, y+1, scale, color.NRGBA{0, 0, 0, 0x80})
drawText(r, images, str, x, y, scale, clr) drawText(rt, images, str, x, y, scale, clr)
} }

View File

@ -116,7 +116,7 @@ func (s *GameScene) Draw(r *ebiten.RenderTarget, images *Images) {
w, h := field.Size() w, h := field.Size()
geoMat := ebiten.ScaleGeometry(float64(fieldWidth)/float64(w), float64(fieldHeight)/float64(h)) geoMat := ebiten.ScaleGeometry(float64(fieldWidth)/float64(w), float64(fieldHeight)/float64(h))
geoMat.Concat(ebiten.TranslateGeometry(20, 20)) // TODO: magic number? geoMat.Concat(ebiten.TranslateGeometry(20, 20)) // TODO: magic number?
colorMat := ebiten.ScaleColor(color.RGBA{0, 0, 0, 0x80}) colorMat := ebiten.ScaleColor(0.0, 0.0, 0.0, 0.5)
ebiten.DrawWholeImage(r, field, geoMat, colorMat) ebiten.DrawWholeImage(r, field, geoMat, colorMat)
geoMat = ebiten.GeometryMatrixI() geoMat = ebiten.GeometryMatrixI()

View File

@ -47,7 +47,7 @@ func (s *TitleScene) Draw(r *ebiten.RenderTarget, images *Images) {
message := "PRESS SPACE TO START" message := "PRESS SPACE TO START"
x := (ScreenWidth - textWidth(message)) / 2 x := (ScreenWidth - textWidth(message)) / 2
y := ScreenHeight - 48 y := ScreenHeight - 48
drawTextWithShadow(r, images, message, x, y, 1, color.RGBA{0x80, 0, 0, 0xff}) drawTextWithShadow(r, images, message, x, y, 1, color.NRGBA{0x80, 0, 0, 0xff})
} }
func drawTitleBackground(r *ebiten.RenderTarget, images *Images, c int) { func drawTitleBackground(r *ebiten.RenderTarget, images *Images, c int) {
@ -78,5 +78,5 @@ func drawLogo(r *ebiten.RenderTarget, images *Images, str string) {
textWidth := textWidth(str) * scale textWidth := textWidth(str) * scale
x := (ScreenWidth - textWidth) / 2 x := (ScreenWidth - textWidth) / 2
y := 32 y := 32
drawTextWithShadow(r, images, str, x, y, scale, color.RGBA{0x00, 0x00, 0x80, 0xff}) drawTextWithShadow(r, images, str, x, y, scale, color.NRGBA{0x00, 0x00, 0x80, 0xff})
} }

View File

@ -51,7 +51,7 @@ func (g *Game) Update(r *ebiten.RenderTarget) error {
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
geo := ebiten.TranslateGeometry(float64(mx), float64(my)) geo := ebiten.TranslateGeometry(float64(mx), float64(my))
clr := ebiten.ScaleColor(color.RGBA{0xff, 0x40, 0x40, 0xff}) clr := ebiten.ScaleColor(1.0, 0.25, 0.25, 1.0)
theta := 2.0 * math.Pi * float64(g.count%60) / 60.0 theta := 2.0 * math.Pi * float64(g.count%60) / 60.0
clr.Concat(ebiten.RotateHue(theta)) clr.Concat(ebiten.RotateHue(theta))
ebiten.DrawWholeImage(g.canvasRenderTarget, g.brushRenderTarget.Image(), geo, clr) ebiten.DrawWholeImage(g.canvasRenderTarget, g.brushRenderTarget.Image(), geo, clr)

View File

@ -53,6 +53,7 @@ const (
) )
// An Image represents an image to be rendered. // An Image represents an image to be rendered.
// An image's pixels are stored as non alpha-premultiplied.
type Image struct { type Image struct {
glTexture *opengl.Texture glTexture *opengl.Texture
} }

View File

@ -16,6 +16,11 @@ limitations under the License.
package internal package internal
import (
"image/color"
"math"
)
func NextPowerOf2(x uint64) uint64 { func NextPowerOf2(x uint64) uint64 {
x -= 1 x -= 1
x |= (x >> 1) x |= (x >> 1)
@ -30,3 +35,13 @@ func NextPowerOf2(x uint64) uint64 {
func NextPowerOf2Int(size int) int { func NextPowerOf2Int(size int) int {
return int(NextPowerOf2(uint64(size))) return int(NextPowerOf2(uint64(size)))
} }
func RGBA(clr color.Color) (r, g, b, a float64) {
clr2 := color.NRGBA64Model.Convert(clr).(color.NRGBA64)
const max = math.MaxUint16
r = float64(clr2.R) / max
g = float64(clr2.G) / max
b = float64(clr2.B) / max
a = float64(clr2.A) / max
return
}

View File

@ -87,7 +87,7 @@ void main(void) {
color0 = (color_matrix * color0) + color_matrix_translation; color0 = (color_matrix * color0) + color_matrix_translation;
// Photoshop-like RGBA blending // Photoshop-like RGBA blending
gl_FragColor.a = color0.a + color1.a - color0.a * color1.a; gl_FragColor.a = color0.a + (1.0 - color0.a) * color1.a;
gl_FragColor.rgb = (color0.a * color0.rgb + (1.0 - color0.a) * color1.a * color1.rgb) / gl_FragColor.a; gl_FragColor.rgb = (color0.a * color0.rgb + (1.0 - color0.a) * color1.a * color1.rgb) / gl_FragColor.a;
} }
`, `,

View File

@ -22,7 +22,6 @@ import (
"github.com/hajimehoshi/ebiten/internal/opengl" "github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/opengl/internal/shader" "github.com/hajimehoshi/ebiten/internal/opengl/internal/shader"
"image/color" "image/color"
"math"
) )
type innerRenderTarget struct { type innerRenderTarget struct {
@ -50,20 +49,15 @@ func (r *innerRenderTarget) size() (width, height int) {
} }
func (r *innerRenderTarget) Clear() error { func (r *innerRenderTarget) Clear() error {
return r.Fill(color.RGBA{0, 0, 0, 0}) return r.Fill(color.Transparent)
} }
func (r *innerRenderTarget) Fill(clr color.Color) error { func (r *innerRenderTarget) Fill(clr color.Color) error {
if err := r.glRenderTarget.SetAsViewport(); err != nil { if err := r.glRenderTarget.SetAsViewport(); err != nil {
return err return err
} }
const max = math.MaxUint16 rf, gf, bf, af := internal.RGBA(clr)
cr, cg, cb, ca := clr.RGBA() gl.ClearColor(gl.GLclampf(rf), gl.GLclampf(gf), gl.GLclampf(bf), gl.GLclampf(af))
rf := gl.GLclampf(float64(cr) / max)
gf := gl.GLclampf(float64(cg) / max)
bf := gl.GLclampf(float64(cb) / max)
af := gl.GLclampf(float64(ca) / max)
gl.ClearColor(rf, gf, bf, af)
gl.Clear(gl.COLOR_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT)
return nil return nil
} }