internal/affine: Refactoring: Make ColorM interface

This commit is contained in:
Hajime Hoshi 2021-07-27 10:37:14 +09:00
parent 5f03f4f195
commit 21aa96f9f5
17 changed files with 337 additions and 368 deletions

View File

@ -32,42 +32,49 @@ const ColorMDim = affine.ColorMDim
//
// The initial value is identity.
type ColorM struct {
impl *affine.ColorM
impl affine.ColorM
_ [0]func() // Marks as non-comparable.
}
func (c *ColorM) affineColorM() affine.ColorM {
if c.impl != nil {
return c.impl
}
return affine.ColorMIdentity{}
}
// String returns a string representation of ColorM.
func (c *ColorM) String() string {
return c.impl.String()
return affine.ColorMString(c.affineColorM())
}
// Reset resets the ColorM as identity.
func (c *ColorM) Reset() {
c.impl = nil
c.impl = affine.ColorMIdentity{}
}
// Apply pre-multiplies a vector (r, g, b, a, 1) by the matrix
// where r, g, b, and a are clr's values in straight-alpha format.
// In other words, Apply calculates ColorM * (r, g, b, a, 1)^T.
func (c *ColorM) Apply(clr color.Color) color.Color {
return c.impl.Apply(clr)
return c.affineColorM().Apply(clr)
}
// Concat multiplies a color matrix with the other color matrix.
// This is same as muptiplying the matrix other and the matrix c in this order.
func (c *ColorM) Concat(other ColorM) {
c.impl = c.impl.Concat(other.impl)
c.impl = c.affineColorM().Concat(other.impl)
}
// Scale scales the matrix by (r, g, b, a).
func (c *ColorM) Scale(r, g, b, a float64) {
c.impl = c.impl.Scale(float32(r), float32(g), float32(b), float32(a))
c.impl = c.affineColorM().Scale(float32(r), float32(g), float32(b), float32(a))
}
// Translate translates the matrix by (r, g, b, a).
func (c *ColorM) Translate(r, g, b, a float64) {
c.impl = c.impl.Translate(float32(r), float32(g), float32(b), float32(a))
c.impl = c.affineColorM().Translate(float32(r), float32(g), float32(b), float32(a))
}
// RotateHue rotates the hue.
@ -83,27 +90,27 @@ func (c *ColorM) RotateHue(theta float64) {
//
// This conversion uses RGB to/from YCrCb conversion.
func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale float64) {
c.impl = c.impl.ChangeHSV(hueTheta, float32(saturationScale), float32(valueScale))
c.impl = affine.ChangeHSV(c.affineColorM(), hueTheta, float32(saturationScale), float32(valueScale))
}
// Element returns a value of a matrix at (i, j).
func (c *ColorM) Element(i, j int) float64 {
return float64(c.impl.Element(i, j))
return float64(affine.ColorMElement(c.affineColorM(), i, j))
}
// SetElement sets an element at (i, j).
func (c *ColorM) SetElement(i, j int, element float64) {
c.impl = c.impl.SetElement(i, j, float32(element))
c.impl = affine.ColorMSetElement(c.affineColorM(), i, j, float32(element))
}
// IsInvertible returns a boolean value indicating
// whether the matrix c is invertible or not.
func (c *ColorM) IsInvertible() bool {
return c.impl.IsInvertible()
return c.affineColorM().IsInvertible()
}
// Invert inverts the matrix.
// If c is not invertible, Invert panics.
func (c *ColorM) Invert() {
c.impl = c.impl.Invert()
c.impl = c.affineColorM().Invert()
}

View File

@ -19,6 +19,7 @@ import (
"image"
"image/color"
"github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
@ -213,7 +214,8 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
}
// Vertex represents a vertex passed to DrawTriangles.
@ -362,7 +364,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.EvenOdd, false)
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.affineColorM(), mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.EvenOdd, false)
}
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
@ -511,7 +513,8 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
}
us := shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, options.EvenOdd, false)
i.mipmap.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, options.EvenOdd, false)
}
// DrawRectShaderOptions represents options for DrawRectShader.
@ -623,7 +626,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
}
us := shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false, canSkipMipmap(options.GeoM, driver.FilterNearest))
i.mipmap.DrawTriangles(imgs, vs, is, affine.ColorMIdentity{}, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false, canSkipMipmap(options.GeoM, driver.FilterNearest))
}
// SubImage returns an image representing the portion of the image p visible through r.

View File

@ -36,33 +36,41 @@ var (
}
)
// A ColorM represents a matrix to transform coloring when rendering an image.
// ColorM represents a matrix to transform coloring when rendering an image.
//
// A ColorM is applied to the source alpha color
// ColorM is applied to the source alpha color
// while an Image's pixels' format is alpha premultiplied.
// Before applying a matrix, a color is un-multiplied, and after applying the matrix,
// the color is multiplied again.
//
// The nil and initial value is identity.
type ColorM struct {
impl colorMImpl
}
type colorMImpl interface {
type ColorM interface {
IsIdentity() bool
ScaleOnly() bool
UnsafeScaleElements() *[4]float32
UnsafeElements() (*[16]float32, *[4]float32)
Apply(r, g, b, a float32) color.Color
Apply(clr color.Color) color.Color
// IsInvertible returns a boolean value indicating
// whether the matrix c is invertible or not.
IsInvertible() bool
Invert() *ColorM
Equals(other *ColorM) bool
Concat(other *ColorM) *ColorM
Scale(r, g, b, a float32) *ColorM
Translate(r, g, b, a float32) *ColorM
// Invert inverts the matrix.
// If c is not invertible, Invert panics.
Invert() ColorM
Equals(other ColorM) bool
// Concat multiplies a color matrix with the other color matrix.
// This is same as muptiplying the matrix other and the matrix c in this order.
Concat(other ColorM) ColorM
// Scale scales the matrix by (r, g, b, a).
Scale(r, g, b, a float32) ColorM
// Translate translates the matrix by (r, g, b, a).
Translate(r, g, b, a float32) ColorM
}
func (c *ColorM) String() string {
func ColorMString(c ColorM) string {
b, t := c.UnsafeElements()
return fmt.Sprintf("[[%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f]]",
b[0], b[4], b[8], b[12], t[0],
@ -71,6 +79,8 @@ func (c *ColorM) String() string {
b[3], b[7], b[11], b[15], t[3])
}
type ColorMIdentity struct{}
type colorMImplScale struct {
scale [4]float32
}
@ -90,14 +100,8 @@ func clamp(x float32) float32 {
return x
}
func (c *ColorM) isIdentity() bool {
if c == nil {
return true
}
if c.impl == nil {
return true
}
return c.impl.IsIdentity()
func (c ColorMIdentity) IsIdentity() bool {
return true
}
func (c *colorMImplScale) IsIdentity() bool {
@ -108,11 +112,8 @@ func (c *colorMImplBodyTranslate) IsIdentity() bool {
return c.body == colorMIdentityBody && c.translate == colorMIdentityTranslate
}
func (c *ColorM) ScaleOnly() bool {
if c.isIdentity() {
return true
}
return c.impl.ScaleOnly()
func (c ColorMIdentity) ScaleOnly() bool {
return true
}
func (c *colorMImplScale) ScaleOnly() bool {
@ -164,6 +165,10 @@ func (c *colorMImplBodyTranslate) ScaleOnly() bool {
return true
}
func (c ColorMIdentity) UnsafeScaleElements() *[4]float32 {
return &[...]float32{1, 1, 1, 1}
}
func (c *colorMImplScale) UnsafeScaleElements() *[4]float32 {
return &c.scale
}
@ -172,11 +177,7 @@ func (c *colorMImplBodyTranslate) UnsafeScaleElements() *[4]float32 {
return &[...]float32{c.body[0], c.body[5], c.body[10], c.body[15]}
}
func (c *ColorM) Apply(clr color.Color) color.Color {
if c.isIdentity() {
return clr
}
func colorToFloat32s(clr color.Color) (float32, float32, float32, float32) {
r, g, b, a := clr.RGBA()
rf, gf, bf, af := float32(0.0), float32(0.0), float32(0.0), float32(0.0)
// Unmultiply alpha
@ -186,10 +187,21 @@ func (c *ColorM) Apply(clr color.Color) color.Color {
bf = float32(b) / float32(a)
af = float32(a) / 0xffff
}
return c.impl.Apply(rf, gf, bf, af)
return rf, gf, bf, af
}
func (c *colorMImplScale) Apply(rf, gf, bf, af float32) color.Color {
func (c ColorMIdentity) Apply(clr color.Color) color.Color {
rf, gf, bf, af := colorToFloat32s(clr)
return color.NRGBA64{
R: uint16(rf * 0xffff),
G: uint16(gf * 0xffff),
B: uint16(bf * 0xffff),
A: uint16(af * 0xffff),
}
}
func (c *colorMImplScale) Apply(clr color.Color) color.Color {
rf, gf, bf, af := colorToFloat32s(clr)
rf *= c.scale[0]
gf *= c.scale[1]
bf *= c.scale[2]
@ -206,7 +218,8 @@ func (c *colorMImplScale) Apply(rf, gf, bf, af float32) color.Color {
}
}
func (c *colorMImplBodyTranslate) Apply(rf, gf, bf, af float32) color.Color {
func (c *colorMImplBodyTranslate) Apply(clr color.Color) color.Color {
rf, gf, bf, af := colorToFloat32s(clr)
eb := &c.body
et := &c.translate
rf2 := eb[0]*rf + eb[4]*gf + eb[8]*bf + eb[12]*af + et[0]
@ -225,11 +238,8 @@ func (c *colorMImplBodyTranslate) Apply(rf, gf, bf, af float32) color.Color {
}
}
func (c *ColorM) UnsafeElements() (*[16]float32, *[4]float32) {
if c.isIdentity() {
return &colorMIdentityBody, &colorMIdentityTranslate
}
return c.impl.UnsafeElements()
func (c ColorMIdentity) UnsafeElements() (*[16]float32, *[4]float32) {
return &colorMIdentityBody, &colorMIdentityTranslate
}
func (c *colorMImplScale) UnsafeElements() (*[16]float32, *[4]float32) {
@ -276,13 +286,8 @@ func (c *colorMImplBodyTranslate) det() float32 {
m03*(m10*b124234-m11*b024234+m12*b014234)
}
// IsInvertible returns a boolean value indicating
// whether the matrix c is invertible or not.
func (c *ColorM) IsInvertible() bool {
if c.isIdentity() {
return true
}
return c.impl.IsInvertible()
func (c ColorMIdentity) IsInvertible() bool {
return true
}
func (c *colorMImplScale) IsInvertible() bool {
@ -293,29 +298,22 @@ func (c *colorMImplBodyTranslate) IsInvertible() bool {
return c.det() != 0
}
// Invert inverts the matrix.
// If c is not invertible, Invert panics.
func (c *ColorM) Invert() *ColorM {
if c.isIdentity() {
return nil
}
return c.impl.Invert()
func (c ColorMIdentity) Invert() ColorM {
return c
}
func (c *colorMImplScale) Invert() *ColorM {
return &ColorM{
impl: &colorMImplScale{
scale: [4]float32{
1 / c.scale[0],
1 / c.scale[1],
1 / c.scale[2],
1 / c.scale[3],
},
func (c *colorMImplScale) Invert() ColorM {
return &colorMImplScale{
scale: [4]float32{
1 / c.scale[0],
1 / c.scale[1],
1 / c.scale[2],
1 / c.scale[3],
},
}
}
func (c *colorMImplBodyTranslate) Invert() *ColorM {
func (c *colorMImplBodyTranslate) Invert() ColorM {
det := c.det()
if det == 0 {
panic("affine: c is not invertible")
@ -417,13 +415,11 @@ func (c *colorMImplBodyTranslate) Invert() *ColorM {
m.translate[2] = idet * (m00*b123134 - m01*b023134 + m02*b013134 - m03*b012134)
m.translate[3] = idet * -(m00*b123124 - m01*b023124 + m02*b013124 - m03*b012124)
return &ColorM{
impl: m,
}
return m
}
// Element returns a value of a matrix at (i, j).
func (c *ColorM) Element(i, j int) float32 {
// ColorMElement returns a value of a matrix at (i, j).
func ColorMElement(c ColorM, i, j int) float32 {
b, t := c.UnsafeElements()
if j < ColorMDim-1 {
return b[i+j*(ColorMDim-1)]
@ -431,13 +427,13 @@ func (c *ColorM) Element(i, j int) float32 {
return t[i]
}
// SetElement sets an element at (i, j).
func (c *ColorM) SetElement(i, j int, element float32) *ColorM {
// ColorMSetElement sets an element at (i, j).
func ColorMSetElement(c ColorM, i, j int, element float32) ColorM {
newImpl := &colorMImplBodyTranslate{
body: colorMIdentityBody,
}
if !c.isIdentity() {
b, t := c.impl.UnsafeElements()
if !c.IsIdentity() {
b, t := c.UnsafeElements()
newImpl.body = *b
newImpl.translate = *t
}
@ -446,26 +442,18 @@ func (c *ColorM) SetElement(i, j int, element float32) *ColorM {
} else {
newImpl.translate[i] = element
}
return &ColorM{
impl: newImpl,
}
return newImpl
}
func (c *ColorM) Equals(other *ColorM) bool {
if c.isIdentity() {
return other.isIdentity()
}
if other.isIdentity() {
return false
}
return c.impl.Equals(other)
func (c ColorMIdentity) Equals(other ColorM) bool {
return other.IsIdentity()
}
func (c *colorMImplScale) Equals(other *ColorM) bool {
if !other.impl.ScaleOnly() {
func (c *colorMImplScale) Equals(other ColorM) bool {
if !other.ScaleOnly() {
return false
}
for i, s := range other.impl.UnsafeScaleElements() {
for i, s := range other.UnsafeScaleElements() {
if c.scale[i] != s {
return false
}
@ -473,92 +461,83 @@ func (c *colorMImplScale) Equals(other *ColorM) bool {
return true
}
func (c *colorMImplBodyTranslate) Equals(other *ColorM) bool {
lhsb, lhst := other.impl.UnsafeElements()
func (c *colorMImplBodyTranslate) Equals(other ColorM) bool {
lhsb, lhst := other.UnsafeElements()
rhsb := &c.body
rhst := &c.translate
return *lhsb == *rhsb && *lhst == *rhst
}
// Concat multiplies a color matrix with the other color matrix.
// This is same as muptiplying the matrix other and the matrix c in this order.
func (c *ColorM) Concat(other *ColorM) *ColorM {
if c.isIdentity() {
return other
}
if other.isIdentity() {
return c
}
return c.impl.Concat(other)
func (c ColorMIdentity) Concat(other ColorM) ColorM {
return other
}
func (c *colorMImplScale) Concat(other *ColorM) *ColorM {
func (c *colorMImplScale) Concat(other ColorM) ColorM {
if other.IsIdentity() {
return c
}
if other.ScaleOnly() {
s := other.impl.UnsafeScaleElements()
s := other.UnsafeScaleElements()
return c.Scale(s[0], s[1], s[2], s[3])
}
lhsb, lhst := other.impl.UnsafeElements()
lhsb, lhst := other.UnsafeElements()
s := &c.scale
return &ColorM{
impl: &colorMImplBodyTranslate{
body: [...]float32{
lhsb[0] * s[0], lhsb[1] * s[0], lhsb[2] * s[0], lhsb[3] * s[0],
lhsb[4] * s[1], lhsb[5] * s[1], lhsb[6] * s[1], lhsb[7] * s[1],
lhsb[8] * s[2], lhsb[9] * s[2], lhsb[10] * s[2], lhsb[11] * s[2],
lhsb[12] * s[3], lhsb[13] * s[3], lhsb[14] * s[3], lhsb[15] * s[3],
},
translate: *lhst,
return &colorMImplBodyTranslate{
body: [...]float32{
lhsb[0] * s[0], lhsb[1] * s[0], lhsb[2] * s[0], lhsb[3] * s[0],
lhsb[4] * s[1], lhsb[5] * s[1], lhsb[6] * s[1], lhsb[7] * s[1],
lhsb[8] * s[2], lhsb[9] * s[2], lhsb[10] * s[2], lhsb[11] * s[2],
lhsb[12] * s[3], lhsb[13] * s[3], lhsb[14] * s[3], lhsb[15] * s[3],
},
translate: *lhst,
}
}
func (c *colorMImplBodyTranslate) Concat(other *ColorM) *ColorM {
lhsb, lhst := other.impl.UnsafeElements()
func (c *colorMImplBodyTranslate) Concat(other ColorM) ColorM {
if other.IsIdentity() {
return c
}
lhsb, lhst := other.UnsafeElements()
rhsb := &c.body
rhst := &c.translate
return &ColorM{
impl: &colorMImplBodyTranslate{
// TODO: This is a temporary hack to calculate multiply of transposed matrices.
// Fix mulSquare implmentation and swap the arguments.
body: mulSquare(rhsb, lhsb, ColorMDim-1),
translate: [...]float32{
lhsb[0]*rhst[0] + lhsb[4]*rhst[1] + lhsb[8]*rhst[2] + lhsb[12]*rhst[3] + lhst[0],
lhsb[1]*rhst[0] + lhsb[5]*rhst[1] + lhsb[9]*rhst[2] + lhsb[13]*rhst[3] + lhst[1],
lhsb[2]*rhst[0] + lhsb[6]*rhst[1] + lhsb[10]*rhst[2] + lhsb[14]*rhst[3] + lhst[2],
lhsb[3]*rhst[0] + lhsb[7]*rhst[1] + lhsb[11]*rhst[2] + lhsb[15]*rhst[3] + lhst[3],
},
return &colorMImplBodyTranslate{
// TODO: This is a temporary hack to calculate multiply of transposed matrices.
// Fix mulSquare implmentation and swap the arguments.
body: mulSquare(rhsb, lhsb, ColorMDim-1),
translate: [...]float32{
lhsb[0]*rhst[0] + lhsb[4]*rhst[1] + lhsb[8]*rhst[2] + lhsb[12]*rhst[3] + lhst[0],
lhsb[1]*rhst[0] + lhsb[5]*rhst[1] + lhsb[9]*rhst[2] + lhsb[13]*rhst[3] + lhst[1],
lhsb[2]*rhst[0] + lhsb[6]*rhst[1] + lhsb[10]*rhst[2] + lhsb[14]*rhst[3] + lhst[2],
lhsb[3]*rhst[0] + lhsb[7]*rhst[1] + lhsb[11]*rhst[2] + lhsb[15]*rhst[3] + lhst[3],
},
}
}
// Scale scales the matrix by (r, g, b, a).
func (c *ColorM) Scale(r, g, b, a float32) *ColorM {
if c.isIdentity() {
return getCachedScalingColorM(r, g, b, a)
func (c ColorMIdentity) Scale(r, g, b, a float32) ColorM {
return getCachedScalingColorM(r, g, b, a)
}
func (c *colorMImplScale) Scale(r, g, b, a float32) ColorM {
return &colorMImplScale{
scale: [...]float32{
c.scale[0] * r,
c.scale[1] * g,
c.scale[2] * b,
c.scale[3] * a,
},
}
}
func (c *colorMImplBodyTranslate) Scale(r, g, b, a float32) ColorM {
if c.ScaleOnly() {
s := c.impl.UnsafeScaleElements()
s := c.UnsafeScaleElements()
return getCachedScalingColorM(r*s[0], g*s[1], b*s[2], a*s[3])
}
return c.impl.Scale(r, g, b, a)
}
func (c *colorMImplScale) Scale(r, g, b, a float32) *ColorM {
return &ColorM{
impl: &colorMImplScale{
scale: [...]float32{
c.scale[0] * r,
c.scale[1] * g,
c.scale[2] * b,
c.scale[3] * a,
},
},
}
}
func (c *colorMImplBodyTranslate) Scale(r, g, b, a float32) *ColorM {
eb := c.body
for i := 0; i < ColorMDim-1; i++ {
eb[i*(ColorMDim-1)] *= r
@ -574,52 +553,40 @@ func (c *colorMImplBodyTranslate) Scale(r, g, b, a float32) *ColorM {
c.translate[3] * a,
}
return &ColorM{
impl: &colorMImplBodyTranslate{
body: eb,
translate: et,
return &colorMImplBodyTranslate{
body: eb,
translate: et,
}
}
func (c ColorMIdentity) Translate(r, g, b, a float32) ColorM {
return &colorMImplBodyTranslate{
body: colorMIdentityBody,
translate: [...]float32{r, g, b, a},
}
}
func (c *colorMImplScale) Translate(r, g, b, a float32) ColorM {
return &colorMImplBodyTranslate{
body: [...]float32{
c.scale[0], 0, 0, 0,
0, c.scale[1], 0, 0,
0, 0, c.scale[2], 0,
0, 0, 0, c.scale[3],
},
translate: [...]float32{r, g, b, a},
}
}
// Translate translates the matrix by (r, g, b, a).
func (c *ColorM) Translate(r, g, b, a float32) *ColorM {
if c.isIdentity() {
return &ColorM{
impl: &colorMImplBodyTranslate{
body: colorMIdentityBody,
translate: [...]float32{r, g, b, a},
},
}
}
return c.impl.Translate(r, g, b, a)
}
func (c *colorMImplScale) Translate(r, g, b, a float32) *ColorM {
return &ColorM{
impl: &colorMImplBodyTranslate{
body: [...]float32{
c.scale[0], 0, 0, 0,
0, c.scale[1], 0, 0,
0, 0, c.scale[2], 0,
0, 0, 0, c.scale[3],
},
translate: [...]float32{r, g, b, a},
},
}
}
func (c *colorMImplBodyTranslate) Translate(r, g, b, a float32) *ColorM {
func (c *colorMImplBodyTranslate) Translate(r, g, b, a float32) ColorM {
es := c.translate
es[0] += r
es[1] += g
es[2] += b
es[3] += a
return &ColorM{
impl: &colorMImplBodyTranslate{
body: c.body,
translate: es,
},
return &colorMImplBodyTranslate{
body: c.body,
translate: es,
}
}
@ -629,24 +596,20 @@ var (
// Cb: [-0.5 - 0.5]
// Cr: [-0.5 - 0.5]
rgbToYCbCr = &ColorM{
impl: &colorMImplBodyTranslate{
body: [...]float32{
0.2990, -0.1687, 0.5000, 0,
0.5870, -0.3313, -0.4187, 0,
0.1140, 0.5000, -0.0813, 0,
0, 0, 0, 1,
},
rgbToYCbCr = &colorMImplBodyTranslate{
body: [...]float32{
0.2990, -0.1687, 0.5000, 0,
0.5870, -0.3313, -0.4187, 0,
0.1140, 0.5000, -0.0813, 0,
0, 0, 0, 1,
},
}
yCbCrToRgb = &ColorM{
impl: &colorMImplBodyTranslate{
body: [...]float32{
1, 1, 1, 0,
0, -0.34414, 1.77200, 0,
1.40200, -0.71414, 0, 0,
0, 0, 0, 1,
},
yCbCrToRgb = &colorMImplBodyTranslate{
body: [...]float32{
1, 1, 1, 0,
0, -0.34414, 1.77200, 0,
1.40200, -0.71414, 0, 0,
0, 0, 0, 1,
},
}
)
@ -657,18 +620,16 @@ var (
// valueScale is a value to scale value (a.k.a. brightness).
//
// This conversion uses RGB to/from YCrCb conversion.
func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float32, valueScale float32) *ColorM {
func ChangeHSV(c ColorM, hueTheta float64, saturationScale float32, valueScale float32) ColorM {
sin, cos := math.Sincos(hueTheta)
s32, c32 := float32(sin), float32(cos)
c = c.Concat(rgbToYCbCr)
c = c.Concat(&ColorM{
impl: &colorMImplBodyTranslate{
body: [...]float32{
1, 0, 0, 0,
0, c32, s32, 0,
0, -s32, c32, 0,
0, 0, 0, 1,
},
c = c.Concat(&colorMImplBodyTranslate{
body: [...]float32{
1, 0, 0, 0,
0, c32, s32, 0,
0, -s32, c32, 0,
0, 0, 0, 1,
},
})
s := saturationScale
@ -683,7 +644,7 @@ type cachedScalingColorMKey struct {
}
type cachedScalingColorMValue struct {
c *ColorM
c *colorMImplScale
atime uint64
}
@ -693,7 +654,7 @@ var (
cacheMonotonicClock uint64
)
func getCachedScalingColorM(r, g, b, a float32) *ColorM {
func getCachedScalingColorM(r, g, b, a float32) ColorM {
key := cachedScalingColorMKey{r, g, b, a}
cachedScalingColorMM.Lock()
@ -722,10 +683,8 @@ func getCachedScalingColorM(r, g, b, a float32) *ColorM {
}
v := &cachedScalingColorMValue{
c: &ColorM{
impl: &colorMImplScale{
scale: [...]float32{r, g, b, a},
},
c: &colorMImplScale{
scale: [...]float32{r, g, b, a},
},
atime: now,
}

View File

@ -24,26 +24,26 @@ import (
func TestColorMScale(t *testing.T) {
cases := []struct {
In *ColorM
Out *ColorM
In ColorM
Out ColorM
}{
{
nil,
(*ColorM)(nil).Scale(0.25, 0.5, 0.75, 1),
ColorMIdentity{},
ColorMIdentity{}.Scale(0.25, 0.5, 0.75, 1),
},
{
(*ColorM)(nil).Scale(0.5, 0.5, 0.5, 0.8),
(*ColorM)(nil).Scale(0.125, 0.25, 0.375, 0.8),
ColorMIdentity{}.Scale(0.5, 0.5, 0.5, 0.8),
ColorMIdentity{}.Scale(0.125, 0.25, 0.375, 0.8),
},
{
(*ColorM)(nil).Translate(0, 0, 0, 0),
(*ColorM)(nil).Scale(0.25, 0.5, 0.75, 1),
ColorMIdentity{}.Translate(0, 0, 0, 0),
ColorMIdentity{}.Scale(0.25, 0.5, 0.75, 1),
},
}
for _, c := range cases {
got := c.In.Scale(0.25, 0.5, 0.75, 1)
want := c.Out
if got != want {
if !got.Equals(want) {
t.Errorf("%v.Scale(): got: %v, want: %v", c.In, got, want)
}
}
@ -51,51 +51,51 @@ func TestColorMScale(t *testing.T) {
func TestColorMScaleOnly(t *testing.T) {
cases := []struct {
In *ColorM
In ColorM
Out bool
}{
{
nil,
ColorMIdentity{},
true,
},
{
(*ColorM)(nil).Translate(0, 0, 0, 0),
ColorMIdentity{}.Translate(0, 0, 0, 0),
true,
},
{
(*ColorM)(nil).Translate(1, 0, 0, 0),
ColorMIdentity{}.Translate(1, 0, 0, 0),
false,
},
{
(*ColorM)(nil).Translate(0, 0, 0, -1),
ColorMIdentity{}.Translate(0, 0, 0, -1),
false,
},
{
(*ColorM)(nil).Scale(1, 1, 1, 1),
ColorMIdentity{}.Scale(1, 1, 1, 1),
true,
},
{
(*ColorM)(nil).Scale(0, 0, 0, 0),
ColorMIdentity{}.Scale(0, 0, 0, 0),
true,
},
{
(*ColorM)(nil).Scale(0.1, 0.2, 0.3, 0.4),
ColorMIdentity{}.Scale(0.1, 0.2, 0.3, 0.4),
true,
},
{
(*ColorM)(nil).Scale(0.1, 0.2, 0.3, 0.4).Translate(1, 0, 0, 0),
ColorMIdentity{}.Scale(0.1, 0.2, 0.3, 0.4).Translate(1, 0, 0, 0),
false,
},
{
(*ColorM)(nil).ChangeHSV(math.Pi/2, 0.5, 0.5),
ChangeHSV(ColorMIdentity{}, math.Pi/2, 0.5, 0.5),
false,
},
{
(*ColorM)(nil).SetElement(0, 0, 2),
ColorMSetElement(ColorMIdentity{}, 0, 0, 2),
true,
},
{
(*ColorM)(nil).SetElement(0, 1, 2),
ColorMSetElement(ColorMIdentity{}, 0, 1, 2),
false,
},
}
@ -109,28 +109,24 @@ func TestColorMScaleOnly(t *testing.T) {
}
func TestColorMIsInvertible(t *testing.T) {
m := &ColorM{}
m = m.SetElement(1, 0, .5)
m = m.SetElement(1, 1, .5)
m = m.SetElement(1, 2, .5)
m = m.SetElement(1, 3, .5)
m = m.SetElement(1, 4, .5)
var m ColorM = ColorMIdentity{}
m = ColorMSetElement(m, 1, 0, .5)
m = ColorMSetElement(m, 1, 1, .5)
m = ColorMSetElement(m, 1, 2, .5)
m = ColorMSetElement(m, 1, 3, .5)
m = ColorMSetElement(m, 1, 4, .5)
cidentity := &ColorM{}
cinvalid := &ColorM{}
cinvalid = cinvalid.SetElement(0, 0, 0)
cinvalid = cinvalid.SetElement(1, 1, 0)
cinvalid = cinvalid.SetElement(2, 2, 0)
cinvalid = cinvalid.SetElement(3, 3, 0)
var cidentity ColorM = ColorMIdentity{}
var cinvalid ColorM = ColorMIdentity{}
cinvalid = ColorMSetElement(cinvalid, 0, 0, 0)
cinvalid = ColorMSetElement(cinvalid, 1, 1, 0)
cinvalid = ColorMSetElement(cinvalid, 2, 2, 0)
cinvalid = ColorMSetElement(cinvalid, 3, 3, 0)
cases := []struct {
In *ColorM
In ColorM
Out bool
}{
{
nil,
true,
},
{
cidentity,
true,
@ -153,11 +149,11 @@ func TestColorMIsInvertible(t *testing.T) {
}
}
func arrayToColorM(es [4][5]float32) *ColorM {
var a = &ColorM{}
func arrayToColorM(es [4][5]float32) ColorM {
var a ColorM = ColorMIdentity{}
for j := 0; j < 5; j++ {
for i := 0; i < 4; i++ {
a = a.SetElement(i, j, es[i][j])
a = ColorMSetElement(a, i, j, es[i][j])
}
}
return a
@ -170,11 +166,11 @@ func abs(x float32) float32 {
return x
}
func equalWithDelta(a, b *ColorM, delta float32) bool {
func equalWithDelta(a, b ColorM, delta float32) bool {
for j := 0; j < 5; j++ {
for i := 0; i < 4; i++ {
ea := a.Element(i, j)
eb := b.Element(i, j)
ea := ColorMElement(a, i, j)
eb := ColorMElement(b, i, j)
if abs(ea-eb) > delta {
return false
}
@ -185,12 +181,12 @@ func equalWithDelta(a, b *ColorM, delta float32) bool {
func TestColorMInvert(t *testing.T) {
cases := []struct {
In *ColorM
Out *ColorM
In ColorM
Out ColorM
}{
{
In: nil,
Out: nil,
In: ColorMIdentity{},
Out: ColorMIdentity{},
},
{
In: arrayToColorM([4][5]float32{
@ -207,7 +203,7 @@ func TestColorMInvert(t *testing.T) {
}),
},
{
In: (*ColorM)(nil).Scale(1, 2, 4, 8),
In: ColorMIdentity{}.Scale(1, 2, 4, 8),
Out: arrayToColorM([4][5]float32{
{1, 0, 0, 0, 0},
{0, 0.5, 0, 0, 0},
@ -256,8 +252,8 @@ func BenchmarkColorMInvert(b *testing.B) {
r := rand.Float32
b.StopTimer()
var m *ColorM
for m == nil || !m.IsInvertible() {
var m ColorM = ColorMIdentity{}
for m.IsIdentity() || !m.IsInvertible() {
m = arrayToColorM([4][5]float32{
{r(), r(), r(), r(), r() * 10},
{r(), r(), r(), r(), r() * 10},
@ -274,24 +270,24 @@ func BenchmarkColorMInvert(b *testing.B) {
func TestColorMConcat(t *testing.T) {
cases := []struct {
In0 *ColorM
In1 *ColorM
Out *ColorM
In0 ColorM
In1 ColorM
Out ColorM
}{
{
nil,
nil,
nil,
ColorMIdentity{},
ColorMIdentity{},
ColorMIdentity{},
},
{
(*ColorM)(nil).Scale(1, 2, 3, 4),
(*ColorM)(nil).Scale(5, 6, 7, 8),
(*ColorM)(nil).Scale(5, 12, 21, 32),
ColorMIdentity{}.Scale(1, 2, 3, 4),
ColorMIdentity{}.Scale(5, 6, 7, 8),
ColorMIdentity{}.Scale(5, 12, 21, 32),
},
{
(*ColorM)(nil).Scale(5, 6, 7, 8),
(*ColorM)(nil).Scale(1, 2, 3, 4),
(*ColorM)(nil).Scale(5, 12, 21, 32),
ColorMIdentity{}.Scale(5, 6, 7, 8),
ColorMIdentity{}.Scale(1, 2, 3, 4),
ColorMIdentity{}.Scale(5, 12, 21, 32),
},
{
arrayToColorM([4][5]float32{
@ -300,7 +296,7 @@ func TestColorMConcat(t *testing.T) {
{4, 5, 1, 2, 3},
{3, 4, 5, 1, 2},
}),
(*ColorM)(nil).Scale(1, 2, 3, 4),
ColorMIdentity{}.Scale(1, 2, 3, 4),
arrayToColorM([4][5]float32{
{1, 2, 3, 4, 5},
{10, 2, 4, 6, 8},
@ -309,7 +305,7 @@ func TestColorMConcat(t *testing.T) {
}),
},
{
(*ColorM)(nil).Scale(1, 2, 3, 4),
ColorMIdentity{}.Scale(1, 2, 3, 4),
arrayToColorM([4][5]float32{
{1, 2, 3, 4, 5},
{5, 1, 2, 3, 4},

View File

@ -298,7 +298,7 @@ func (i *Image) ensureIsolated() {
Width: float32(w - 2*paddingSize),
Height: float32(h - 2*paddingSize),
}
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
newImg.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
i.dispose(false)
i.backend = &backend{
@ -354,7 +354,7 @@ func (i *Image) putOnAtlas() error {
Width: w,
Height: h,
}
newI.drawTriangles([graphics.ShaderImageNum]*Image{i}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, true)
newI.drawTriangles([graphics.ShaderImageNum]*Image{i}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, true)
}
newI.moveTo(i)
@ -402,13 +402,13 @@ func (i *Image) processSrc(src *Image) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
backendsM.Lock()
defer backendsM.Unlock()
i.drawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false)
}
func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, keepOnAtlas bool) {
func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, keepOnAtlas bool) {
if i.disposed {
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
}

View File

@ -19,6 +19,7 @@ import (
"runtime"
"testing"
"github.com/hajimehoshi/ebiten/v2/internal/affine"
. "github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
@ -102,7 +103,7 @@ func TestEnsureIsolated(t *testing.T) {
Width: size,
Height: size,
}
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
want := false
if got := img4.IsOnAtlasForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -132,7 +133,7 @@ func TestEnsureIsolated(t *testing.T) {
// Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948.
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
}
func TestReputOnAtlas(t *testing.T) {
@ -179,7 +180,7 @@ func TestReputOnAtlas(t *testing.T) {
Width: size,
Height: size,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -191,7 +192,7 @@ func TestReputOnAtlas(t *testing.T) {
if err := PutImagesOnAtlasForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -219,7 +220,7 @@ func TestReputOnAtlas(t *testing.T) {
}
// img1 is on an atlas again.
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -243,7 +244,7 @@ func TestReputOnAtlas(t *testing.T) {
}
// Use img1 as a render target again.
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -255,7 +256,7 @@ func TestReputOnAtlas(t *testing.T) {
t.Fatal(err)
}
img1.ReplacePixels(make([]byte, 4*size*size))
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -265,7 +266,7 @@ func TestReputOnAtlas(t *testing.T) {
}
// img1 is not on an atlas due to ReplacePixels.
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -275,7 +276,7 @@ func TestReputOnAtlas(t *testing.T) {
if err := PutImagesOnAtlasForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -375,7 +376,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h)
@ -423,7 +424,7 @@ func TestSmallImages(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
@ -471,7 +472,7 @@ func TestLongImages(t *testing.T) {
Width: dstW,
Height: dstH,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil {
@ -559,7 +560,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
Width: size,
Height: size,
}
src.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
src.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := src.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -569,7 +570,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
if err := PutImagesOnAtlasForTesting(); err != nil {
t.Fatal(err)
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := src.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -609,7 +610,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
}
// Use src2 as a rendering target, and make src2 an independent image.
src2.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
src2.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -630,7 +631,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
if err := PutImagesOnAtlasForTesting(); err != nil {
t.Fatal(err)
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}

View File

@ -202,7 +202,7 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
// DrawTriangles draws the src image with the given vertices.
//
// Copying vertices and indices is the caller's responsibility.
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
for _, src := range srcs {
if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")

View File

@ -58,7 +58,7 @@ type Graphics interface {
//
// * float32
// * []float32
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region, uniforms []interface{}, evenOdd bool) error
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, colorM affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region, uniforms []interface{}, evenOdd bool) error
}
// GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.

View File

@ -128,7 +128,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
}
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if len(indices) > graphics.IndicesNum {
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum))
}
@ -317,7 +317,7 @@ type drawTrianglesCommand struct {
offsets [graphics.ShaderImageNum - 1][2]float32
vertices []float32
nindices int
color *affine.ColorM
color affine.ColorM
mode driver.CompositeMode
filter driver.Filter
address driver.Address
@ -456,7 +456,7 @@ func (c *drawTrianglesCommand) addNumIndices(n int) {
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
// with the drawTrianglesCommand c.
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, vertices []float32, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, vertices []float32, color affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
// If a shader is used, commands are not merged.
//
// TODO: Merge shader commands considering uniform variables.

View File

@ -139,7 +139,7 @@ func (i *Image) InternalSize() (int, int) {
//
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
// elements for the source image are not used.
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if shader == nil {
// Fast path for rendering without a shader (#1355).
img := srcs[0]

View File

@ -18,6 +18,7 @@ import (
"image/color"
"testing"
"github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
. "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
@ -50,7 +51,7 @@ func TestClear(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
pix, err := dst.Pixels()
if err != nil {
@ -81,8 +82,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
// TODO: Check the result.
@ -100,11 +101,11 @@ func TestShader(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
pix, err := dst.Pixels()
if err != nil {

View File

@ -868,7 +868,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion drive
return nil
}
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
dst := g.images[dstID]
if dst.screen {
@ -893,7 +893,7 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
} {
rpss[stencil] = g.rpss[rpsKey{
screen: dst.screen,
useColorM: colorM != nil,
useColorM: !colorM.IsIdentity(),
filter: filter,
address: address,
compositeMode: mode,

View File

@ -146,7 +146,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
g.context.elementArrayBufferSubData(indices)
}
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
destination := g.images[dstID]
g.drawCalled = true
@ -166,7 +166,7 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
var uniformVars []uniformVariable
if shaderID == driver.InvalidShaderID {
program = g.state.programs[programKey{
useColorM: colorM != nil,
useColorM: !colorM.IsIdentity(),
filter: filter,
address: address,
}]
@ -187,7 +187,7 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
typ: shaderir.Type{Main: shaderir.Vec4},
})
if colorM != nil {
if !colorM.IsIdentity() {
// ColorM's elements are immutable. It's OK to hold the reference without copying.
esBody, esTranslate := colorM.UnsafeElements()
uniformVars = append(uniformVars, uniformVariable{

View File

@ -85,7 +85,7 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height)
}
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, canSkipMipmap bool) {
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, canSkipMipmap bool) {
if len(indices) == 0 {
return
}
@ -123,13 +123,13 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [
}
}
if colorm != nil && colorm.ScaleOnly() {
if colorm.ScaleOnly() {
body, _ := colorm.UnsafeElements()
cr := body[0]
cg := body[5]
cb := body[10]
ca := body[15]
colorm = nil
colorm = affine.ColorMIdentity{}
const n = graphics.VertexFloatNum
for i := 0; i < len(vertices)/n; i++ {
vertices[i*n+4] *= cr
@ -226,7 +226,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
Width: float32(w2),
Height: float32(h2),
}
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
m.imgs[level] = s
return m.imgs[level]

View File

@ -68,7 +68,7 @@ type drawTrianglesHistoryItem struct {
offsets [graphics.ShaderImageNum - 1][2]float32
vertices []float32
indices []uint16
colorm *affine.ColorM
colorm affine.ColorM
mode driver.CompositeMode
filter driver.Filter
address driver.Address
@ -187,7 +187,7 @@ func (i *Image) Extend(width, height int) *Image {
Width: float32(sw),
Height: float32(sh),
}
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
newImg.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
// Overwrite the history as if the image newImg is created only by ReplacePixels. Now drawTrianglesHistory
// and basePixels cannot be mixed.
@ -248,7 +248,7 @@ func clearImage(i *graphicscommand.Image) {
Width: float32(dw),
Height: float32(dh),
}
i.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
i.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
}
// BasePixelsForTesting returns the image's basePixels for testing.
@ -351,7 +351,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image")
}
@ -396,7 +396,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
}
// appendDrawTrianglesHistory appends a draw-image history item to the image.
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if i.stale || i.volatile || i.screen {
return
}

View File

@ -19,6 +19,7 @@ import (
"image/color"
"testing"
"github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
. "github.com/hajimehoshi/ebiten/v2/internal/restorable"
@ -137,7 +138,7 @@ func TestRestoreChain(t *testing.T) {
Width: 1,
Height: 1,
}
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
}
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
@ -185,10 +186,10 @@ func TestRestoreChain2(t *testing.T) {
Width: w,
Height: h,
}
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
for i := 0; i < 7; i++ {
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
}
if err := ResolveStaleImages(); err != nil {
@ -234,10 +235,10 @@ func TestRestoreOverrideSource(t *testing.T) {
Width: w,
Height: h,
}
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -323,23 +324,23 @@ func TestRestoreComplexGraph(t *testing.T) {
Height: h,
}
var offsets [graphics.ShaderImageNum - 1][2]float32
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 1, 0)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 1, 0)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 2, 0)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 0, 0)
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 0, 0)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 1, 0)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 0, 0)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -437,8 +438,8 @@ func TestRestoreRecursive(t *testing.T) {
Width: w,
Height: h,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -538,7 +539,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
Width: 2,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := ResolveStaleImages(); err != nil {
@ -581,8 +582,8 @@ func TestDispose(t *testing.T) {
Width: 1,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.Dispose()
if err := ResolveStaleImages(); err != nil {
@ -696,7 +697,7 @@ func TestReplacePixelsOnly(t *testing.T) {
Width: 1,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing.
@ -756,7 +757,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale.
@ -783,7 +784,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
// ReplacePixels for a whole image doesn't panic.
}
@ -807,7 +808,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
}
@ -884,7 +885,7 @@ func TestMutateSlices(t *testing.T) {
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
for i := range vs {
vs[i] = 0
}

View File

@ -18,6 +18,7 @@ import (
"image/color"
"testing"
"github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/driver"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
. "github.com/hajimehoshi/ebiten/v2/internal/restorable"
@ -48,7 +49,7 @@ func clearImage(img *Image, w, h int) {
Width: float32(w),
Height: float32(h),
}
img.DrawTriangles([graphics.ShaderImageNum]*Image{emptyImage}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
img.DrawTriangles([graphics.ShaderImageNum]*Image{emptyImage}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
}
func TestShader(t *testing.T) {