Skip to content

Commit

Permalink
internal/atlas: move the color scaling from internal/atlas to ebiten
Browse files Browse the repository at this point in the history
Updates #1820
  • Loading branch information
hajimehoshi committed Apr 1, 2022
1 parent 5c79178 commit 0c6362c
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 49 deletions.
58 changes: 51 additions & 7 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,13 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
sy0 := float32(bounds.Min.Y)
sx1 := float32(bounds.Max.X)
sy1 := float32(bounds.Max.Y)
vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1)
colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM())
vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, cr, cg, cb, ca)
is := graphics.QuadIndices()

srcs := [graphics.ShaderImageNum]*ui.Image{img.image}

i.image.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
i.image.DrawTriangles(srcs, vs, is, colorm, mode, filter, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
}

// Vertex represents a vertex passed to DrawTriangles.
Expand Down Expand Up @@ -332,23 +333,25 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o

filter := graphicsdriver.Filter(options.Filter)

colorm, cr, cg, cb, ca := colorMToScale(options.ColorM.affineColorM())

vs := graphics.Vertices(len(vertices))
for i, v := range vertices {
vs[i*graphics.VertexFloatNum] = v.DstX
vs[i*graphics.VertexFloatNum+1] = v.DstY
vs[i*graphics.VertexFloatNum+2] = v.SrcX
vs[i*graphics.VertexFloatNum+3] = v.SrcY
vs[i*graphics.VertexFloatNum+4] = v.ColorR
vs[i*graphics.VertexFloatNum+5] = v.ColorG
vs[i*graphics.VertexFloatNum+6] = v.ColorB
vs[i*graphics.VertexFloatNum+7] = v.ColorA
vs[i*graphics.VertexFloatNum+4] = v.ColorR * cr
vs[i*graphics.VertexFloatNum+5] = v.ColorG * cg
vs[i*graphics.VertexFloatNum+6] = v.ColorB * cb
vs[i*graphics.VertexFloatNum+7] = v.ColorA * ca
}
is := make([]uint16, len(indices))
copy(is, indices)

srcs := [graphics.ShaderImageNum]*ui.Image{img.image}

i.image.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.FillRule == EvenOdd, false)
i.image.DrawTriangles(srcs, vs, is, colorm, mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.FillRule == EvenOdd, false)
}

// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
Expand Down Expand Up @@ -828,3 +831,44 @@ func NewImageFromImage(source image.Image) *Image {
i.ReplacePixels(imageToBytes(source))
return i
}

// colorMToScale returns a new color matrix and color sclaes that equal to the given matrix in terms of the effect.
//
// If the given matrix is merely a scaling matrix, colorMToScale returns
// an identity matrix and its scaling factors. This is useful to optimize
// the rendering speed by avoiding the use of the color matrix and instead
// multiplying all vertex colors by the scale.
//
// NOTE: this is only safe when not using a custom Kage shader,
// as custom shaders may be using vertex colors for different purposes
// than colorization. However, currently there are no Ebiten APIs that
// support both shaders and color matrices.
func colorMToScale(colorm affine.ColorM) (newColorM affine.ColorM, r, g, b, a float32) {
if colorm.IsIdentity() {
return colorm, 1, 1, 1, 1
}

if !colorm.ScaleOnly() {
return colorm, 1, 1, 1, 1
}
r = colorm.At(0, 0)
g = colorm.At(1, 1)
b = colorm.At(2, 2)
a = colorm.At(3, 3)

// Color matrices work on non-premultiplied colors.
// This color matrix can only make colors darker or equal,
// and thus can never invoke color clamping.
// Thus the simpler vertex color scale based shader can be used.
//
// Negative color values can become positive and out-of-range
// after applying to vertex colors below, which can make the min() in the shader kick in.
//
// Alpha values smaller than 0, combined with negative vertex colors,
// can also make the min() kick in, so that shall be ruled out too.
if r < 0 || g < 0 || b < 0 || a < 0 || r > 1 || g > 1 || b > 1 {
return colorm, 1, 1, 1, 1
}

return affine.ColorMIdentity{}, r, g, b, a
}
42 changes: 0 additions & 42 deletions internal/atlas/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -420,40 +420,6 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
i.processSrc(src)
}

// If a color matrix is used, but the matrix is merely a scaling matrix,
// and the scaling cannot cause out-of-range colors, do not use a color matrix
// when rendering but instead multiply all vertex colors by the scale.
// This speeds up rendering.
//
// NOTE: this is only safe when not using a custom Kage shader,
// as custom shaders may be using vertex colors for different purposes
// than colorization. However, currently there are no Ebiten APIs that
// support both shaders and color matrices.
cr := float32(1)
cg := float32(1)
cb := float32(1)
ca := float32(1)
if !colorm.IsIdentity() && colorm.ScaleOnly() {
r := colorm.At(0, 0)
g := colorm.At(1, 1)
b := colorm.At(2, 2)
a := colorm.At(3, 3)
if r >= 0 && g >= 0 && b >= 0 && a >= 0 && r <= 1 && g <= 1 && b <= 1 {
// Color matrices work on non-premultiplied colors.
// This color matrix can only make colors darker or equal,
// and thus can never invoke color clamping.
// Thus the simpler vertex color scale based shader can be used.
//
// Negative color values can become positive and out-of-range
// after applying to vertex colors below, which can make the min() in the shader kick in.
//
// Alpha values smaller than 0, combined with negative vertex colors,
// can also make the min() kick in, so that shall be ruled out too.
cr, cg, cb, ca = r, g, b, a
colorm = affine.ColorMIdentity{}
}
}

var dx, dy float32
// A screen image doesn't have its padding.
if !i.screen {
Expand All @@ -477,10 +443,6 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
vertices[i+1] += dy
vertices[i+2] += oxf
vertices[i+3] += oyf
vertices[i+4] *= cr
vertices[i+5] *= cg
vertices[i+6] *= cb
vertices[i+7] *= ca
}
// srcRegion can be delibarately empty when this is not needed in order to avoid unexpected
// performance issue (#1293).
Expand All @@ -493,10 +455,6 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
for i := 0; i < n; i += graphics.VertexFloatNum {
vertices[i] += dx
vertices[i+1] += dy
vertices[i+4] *= cr
vertices[i+5] *= cg
vertices[i+6] *= cb
vertices[i+7] *= ca
}
}

Expand Down

0 comments on commit 0c6362c

Please sign in to comment.