From 21aa96f9f574c423068134b389d63391c8cdfaf1 Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Tue, 27 Jul 2021 10:37:14 +0900 Subject: [PATCH] internal/affine: Refactoring: Make ColorM interface --- colorm.go | 31 +- image.go | 11 +- internal/affine/colorm.go | 389 +++++++++------------ internal/affine/colorm_test.go | 126 ++++--- internal/atlas/image.go | 8 +- internal/atlas/image_test.go | 33 +- internal/buffered/image.go | 2 +- internal/driver/graphics.go | 2 +- internal/graphicscommand/command.go | 6 +- internal/graphicscommand/image.go | 2 +- internal/graphicscommand/image_test.go | 11 +- internal/graphicsdriver/metal/graphics.go | 4 +- internal/graphicsdriver/opengl/graphics.go | 6 +- internal/mipmap/mipmap.go | 8 +- internal/restorable/image.go | 10 +- internal/restorable/images_test.go | 53 +-- internal/restorable/shader_test.go | 3 +- 17 files changed, 337 insertions(+), 368 deletions(-) diff --git a/colorm.go b/colorm.go index 388e602f7..2880775ae 100644 --- a/colorm.go +++ b/colorm.go @@ -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() } diff --git a/image.go b/image.go index 382ba4470..a7c9db72e 100644 --- a/image.go +++ b/image.go @@ -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. diff --git a/internal/affine/colorm.go b/internal/affine/colorm.go index 3114d3879..833e49673 100644 --- a/internal/affine/colorm.go +++ b/internal/affine/colorm.go @@ -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, } diff --git a/internal/affine/colorm_test.go b/internal/affine/colorm_test.go index 8ac5ac411..6ba7fc704 100644 --- a/internal/affine/colorm_test.go +++ b/internal/affine/colorm_test.go @@ -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}, diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 9d4376a33..edf70f9ed 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -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)") } diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index b02b25721..dc84c936e 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -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) } diff --git a/internal/buffered/image.go b/internal/buffered/image.go index c3e3fb653..db35efccd 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -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") diff --git a/internal/driver/graphics.go b/internal/driver/graphics.go index ce40329f2..1f3d9cebf 100644 --- a/internal/driver/graphics.go +++ b/internal/driver/graphics.go @@ -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. diff --git a/internal/graphicscommand/command.go b/internal/graphicscommand/command.go index a82a86dc9..93bab7746 100644 --- a/internal/graphicscommand/command.go +++ b/internal/graphicscommand/command.go @@ -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. diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index e29d2e167..54f56bd86 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -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] diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index c41868890..43ab6a8e6 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -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 { diff --git a/internal/graphicsdriver/metal/graphics.go b/internal/graphicsdriver/metal/graphics.go index 6472a7242..d52cccc5f 100644 --- a/internal/graphicsdriver/metal/graphics.go +++ b/internal/graphicsdriver/metal/graphics.go @@ -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, diff --git a/internal/graphicsdriver/opengl/graphics.go b/internal/graphicsdriver/opengl/graphics.go index 739519387..f21a32729 100644 --- a/internal/graphicsdriver/opengl/graphics.go +++ b/internal/graphicsdriver/opengl/graphics.go @@ -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{ diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 2fa150a8a..e15569bff 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -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] diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 04a613f65..5dffbcb5f 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -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 } diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index e7e8e5cf5..724ac667c 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -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 } diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 28baab82c..7167347be 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -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) {