graphics: Make copying GeoM faster

This commit is contained in:
Hajime Hoshi 2017-01-19 02:29:49 +09:00
parent 3331f17723
commit 9087269212
5 changed files with 106 additions and 130 deletions

View File

@ -14,27 +14,46 @@
package affine package affine
import (
"github.com/hajimehoshi/ebiten/internal/endian"
)
var identityValues = map[int]string{
ColorMDim: colorMValueString([ColorMDim - 1][ColorMDim]float64{
{1, 0, 0, 0, 0},
{0, 1, 0, 0, 0},
{0, 0, 1, 0, 0},
{0, 0, 0, 1, 0},
}),
GeoMDim: geoMValueString([GeoMDim - 1][GeoMDim]float64{
{1, 0, 0},
{0, 1, 0},
}),
}
func uint64ToBytes(value uint64) []uint8 {
result := make([]uint8, 8)
if endian.IsLittle() {
for i := 0; i < 8; i++ {
result[i] = uint8(value)
value >>= 8
}
} else {
for i := 7; 0 <= i; i-- {
result[i] = uint8(value)
value >>= 8
}
}
return result
}
type affine interface { type affine interface {
dim() int dim() int
Element(i, j int) float64 Element(i, j int) float64
SetElement(i, j int, element float64) SetElement(i, j int, element float64)
} }
func isIdentity(ebiten affine) bool { // add is deprecated
dim := ebiten.dim()
for i := 0; i < dim-1; i++ {
for j := 0; j < dim; j++ {
element := ebiten.Element(i, j)
if i == j && element != 1 {
return false
} else if i != j && element != 0 {
return false
}
}
}
return true
}
func add(lhs, rhs, result affine) { func add(lhs, rhs, result affine) {
dim := lhs.dim() dim := lhs.dim()
if dim != rhs.dim() { if dim != rhs.dim() {

View File

@ -20,27 +20,25 @@ import (
"github.com/gopherjs/gopherjs/js" "github.com/gopherjs/gopherjs/js"
) )
// Element returns a value of a matrix at (i, j). func element(values string, dim int, i, j int) float64 {
func (c *ColorM) Element(i, j int) float64 { if values == "" {
if c.values == "" {
if i == j { if i == j {
return 1 return 1
} }
return 0 return 0
} }
a := js.NewArrayBuffer([]uint8(c.values)) a := js.NewArrayBuffer([]uint8(values))
af64 := js.Global.Get("Float64Array").New(a) af64 := js.Global.Get("Float64Array").New(a)
return af64.Index(i*ColorMDim + j).Float() return af64.Index(i*dim + j).Float()
} }
// SetElement sets an element at (i, j). func setElement(values string, dim int, i, j int, value float64) string {
func (c *ColorM) SetElement(i, j int, value float64) { if values == "" {
if c.values == "" { values = identityValues[dim]
c.values = colorMIdentityValue
} }
a := js.NewArrayBuffer([]uint8(c.values)) a := js.NewArrayBuffer([]uint8(values))
a8 := js.Global.Get("Uint8Array").New(a) a8 := js.Global.Get("Uint8Array").New(a)
af64 := js.Global.Get("Float64Array").New(a) af64 := js.Global.Get("Float64Array").New(a)
af64.SetIndex(i*ColorMDim+j, value) af64.SetIndex(i*dim+j, value)
c.values = string(a8.Interface().([]uint8)) return string(a8.Interface().([]uint8))
} }

View File

@ -22,36 +22,34 @@ import (
"github.com/hajimehoshi/ebiten/internal/endian" "github.com/hajimehoshi/ebiten/internal/endian"
) )
// Element returns a value of a matrix at (i, j). func element(values string, dim int, i, j int) float64 {
func (c *ColorM) Element(i, j int) float64 { if values == "" {
if c.values == "" {
if i == j { if i == j {
return 1 return 1
} }
return 0 return 0
} }
offset := 8 * (i*ColorMDim + j) offset := 8 * (i*dim + j)
v := uint64(0) v := uint64(0)
if endian.IsLittle() { if endian.IsLittle() {
for k := 7; 0 <= k; k-- { for k := 7; 0 <= k; k-- {
v <<= 8 v <<= 8
v += uint64(c.values[offset+k]) v += uint64(values[offset+k])
} }
} else { } else {
for k := 0; k < 8; k++ { for k := 0; k < 8; k++ {
v <<= 8 v <<= 8
v += uint64(c.values[offset+k]) v += uint64(values[offset+k])
} }
} }
return math.Float64frombits(v) return math.Float64frombits(v)
} }
// SetElement sets an element at (i, j). func setElement(values string, dim int, i, j int, value float64) string {
func (c *ColorM) SetElement(i, j int, value float64) { if values == "" {
if c.values == "" { values = identityValues[dim]
c.values = colorMIdentityValue
} }
b := uint64ToBytes(math.Float64bits(value)) b := uint64ToBytes(math.Float64bits(value))
offset := 8 * (i*ColorMDim + j) offset := 8 * (i*dim + j)
c.values = c.values[:offset] + string(b) + c.values[offset+8:] return values[:offset] + string(b) + values[offset+8:]
} }

View File

@ -16,29 +16,11 @@ package affine
import ( import (
"math" "math"
"github.com/hajimehoshi/ebiten/internal/endian"
) )
// ColorMDim is a dimension of a ColorM. // ColorMDim is a dimension of a ColorM.
const ColorMDim = 5 const ColorMDim = 5
func uint64ToBytes(value uint64) []uint8 {
result := make([]uint8, 8)
if endian.IsLittle() {
for i := 0; i < 8; i++ {
result[i] = uint8(value)
value >>= 8
}
} else {
for i := 7; 0 <= i; i-- {
result[i] = uint8(value)
value >>= 8
}
}
return result
}
func colorMValueString(values [ColorMDim - 1][ColorMDim]float64) string { func colorMValueString(values [ColorMDim - 1][ColorMDim]float64) string {
b := make([]uint8, 0, (ColorMDim-1)*(ColorMDim)*8) b := make([]uint8, 0, (ColorMDim-1)*(ColorMDim)*8)
for i := 0; i < ColorMDim-1; i++ { for i := 0; i < ColorMDim-1; i++ {
@ -49,15 +31,6 @@ func colorMValueString(values [ColorMDim - 1][ColorMDim]float64) string {
return string(b) return string(b)
} }
var (
colorMIdentityValue = colorMValueString([ColorMDim - 1][ColorMDim]float64{
{1, 0, 0, 0, 0},
{0, 1, 0, 0, 0},
{0, 0, 1, 0, 0},
{0, 0, 0, 1, 0},
})
)
// A ColorM represents a matrix to transform coloring when rendering an image. // A ColorM represents a matrix to transform coloring when rendering an image.
// //
// A ColorM is applied to the source alpha color // A ColorM is applied to the source alpha color
@ -75,12 +48,22 @@ func (c *ColorM) dim() int {
return ColorMDim return ColorMDim
} }
// Element returns a value of a matrix at (i, j).
func (c *ColorM) Element(i, j int) float64 {
return element(c.values, ColorMDim, i, j)
}
// SetElement sets an element at (i, j).
func (c *ColorM) SetElement(i, j int, element float64) {
c.values = setElement(c.values, ColorMDim, i, j, element)
}
func (c *ColorM) Equals(other *ColorM) bool { func (c *ColorM) Equals(other *ColorM) bool {
if c.values == "" { if c.values == "" {
c.values = colorMIdentityValue c.values = identityValues[ColorMDim]
} }
if other.values == "" { if other.values == "" {
other.values = colorMIdentityValue other.values = identityValues[ColorMDim]
} }
return c.values == other.values return c.values == other.values
} }
@ -93,7 +76,7 @@ func (c *ColorM) Concat(other ColorM) {
*c = result *c = result
} }
// Add adds a color matrix with the other color matrix. // Add is deprecated.
func (c *ColorM) Add(other ColorM) { func (c *ColorM) Add(other ColorM) {
result := ColorM{} result := ColorM{}
add(&other, c, &result) add(&other, c, &result)

View File

@ -21,52 +21,48 @@ import (
// GeoMDim is a dimension of a GeoM. // GeoMDim is a dimension of a GeoM.
const GeoMDim = 3 const GeoMDim = 3
func geoMValueString(values [GeoMDim - 1][GeoMDim]float64) string {
b := make([]uint8, 0, (GeoMDim-1)*(GeoMDim)*8)
for i := 0; i < GeoMDim-1; i++ {
for j := 0; j < GeoMDim; j++ {
b = append(b, uint64ToBytes(math.Float64bits(values[i][j]))...)
}
}
return string(b)
}
// A GeoM represents a matrix to transform geometry when rendering an image. // A GeoM represents a matrix to transform geometry when rendering an image.
// //
// The initial value is identity. // The initial value is identity.
type GeoM struct { type GeoM struct {
initialized bool // When values is empty, this matrix is identity.
es [(GeoMDim - 1) * GeoMDim]float64 values string
} }
func (g *GeoM) dim() int { func (g *GeoM) dim() int {
return GeoMDim return GeoMDim
} }
func (g *GeoM) initialize() {
g.initialized = true
for i := 0; i < GeoMDim-1; i++ {
g.es[i*GeoMDim+i] = 1
}
}
// Element returns a value of a matrix at (i, j). // Element returns a value of a matrix at (i, j).
func (g *GeoM) Element(i, j int) float64 { func (g *GeoM) Element(i, j int) float64 {
if !g.initialized { return element(g.values, GeoMDim, i, j)
if i == j {
return 1
} }
return 0
} // SetElement sets an element at (i, j).
return g.es[i*GeoMDim+j] func (g *GeoM) SetElement(i, j int, element float64) {
g.values = setElement(g.values, GeoMDim, i, j, element)
} }
// Concat multiplies a geometry matrix with the other geometry matrix. // Concat multiplies a geometry matrix with the other geometry matrix.
// This is same as muptiplying the matrix other and the matrix g in this order. // This is same as muptiplying the matrix other and the matrix g in this order.
func (g *GeoM) Concat(other GeoM) { func (g *GeoM) Concat(other GeoM) {
if !g.initialized {
g.initialize()
}
result := GeoM{} result := GeoM{}
mul(&other, g, &result) mul(&other, g, &result)
*g = result *g = result
} }
// Add adds a geometry matrix with the other geometry matrix. // Add is deprecated.
func (g *GeoM) Add(other GeoM) { func (g *GeoM) Add(other GeoM) {
if !g.initialized {
g.initialize()
}
result := GeoM{} result := GeoM{}
add(&other, g, &result) add(&other, g, &result)
*g = result *g = result
@ -74,63 +70,46 @@ func (g *GeoM) Add(other GeoM) {
// Scale scales the matrix by (x, y). // Scale scales the matrix by (x, y).
func (g *GeoM) Scale(x, y float64) { func (g *GeoM) Scale(x, y float64) {
if !g.initialized {
g.initialize()
}
for i := 0; i < GeoMDim; i++ { for i := 0; i < GeoMDim; i++ {
g.es[i] *= x g.SetElement(0, i, g.Element(0, i)*x)
g.es[GeoMDim+i] *= y g.SetElement(1, i, g.Element(1, i)*y)
} }
} }
// Translate translates the matrix by (x, y). // Translate translates the matrix by (x, y).
func (g *GeoM) Translate(tx, ty float64) { func (g *GeoM) Translate(tx, ty float64) {
if !g.initialized { g.SetElement(0, 2, g.Element(0, 2)+tx)
g.initialize() g.SetElement(1, 2, g.Element(1, 2)+ty)
}
g.es[2] += tx
g.es[GeoMDim+2] += ty
} }
// Rotate rotates the matrix by theta. // Rotate rotates the matrix by theta.
func (g *GeoM) Rotate(theta float64) { func (g *GeoM) Rotate(theta float64) {
sin, cos := math.Sincos(theta) sin, cos := math.Sincos(theta)
g.Concat(GeoM{ g.Concat(GeoM{
initialized: true, values: geoMValueString([GeoMDim - 1][GeoMDim]float64{
es: [...]float64{ {cos, -sin, 0},
cos, -sin, 0, {sin, cos, 0},
sin, cos, 0, }),
},
}) })
} }
// SetElement sets an element at (i, j).
func (g *GeoM) SetElement(i, j int, element float64) {
if !g.initialized {
g.initialize()
}
g.es[i*GeoMDim+j] = element
}
// ScaleGeo is deprecated as of 1.2.0-alpha. Use Scale instead. // ScaleGeo is deprecated as of 1.2.0-alpha. Use Scale instead.
func ScaleGeo(x, y float64) GeoM { func ScaleGeo(x, y float64) GeoM {
return GeoM{ return GeoM{
initialized: true, values: geoMValueString([GeoMDim - 1][GeoMDim]float64{
es: [...]float64{ {x, 0, 0},
x, 0, 0, {0, y, 0},
0, y, 0, }),
},
} }
} }
// TranslateGeo is deprecated as of 1.2.0-alpha. Use Translate instead. // TranslateGeo is deprecated as of 1.2.0-alpha. Use Translate instead.
func TranslateGeo(tx, ty float64) GeoM { func TranslateGeo(tx, ty float64) GeoM {
return GeoM{ return GeoM{
initialized: true, values: geoMValueString([GeoMDim - 1][GeoMDim]float64{
es: [...]float64{ {1, 0, tx},
1, 0, tx, {0, 1, ty},
0, 1, ty, }),
},
} }
} }
@ -138,10 +117,9 @@ func TranslateGeo(tx, ty float64) GeoM {
func RotateGeo(theta float64) GeoM { func RotateGeo(theta float64) GeoM {
sin, cos := math.Sincos(theta) sin, cos := math.Sincos(theta)
return GeoM{ return GeoM{
initialized: true, values: geoMValueString([GeoMDim - 1][GeoMDim]float64{
es: [...]float64{ {cos, -sin, 0},
cos, -sin, 0, {sin, cos, 0},
sin, cos, 0, }),
},
} }
} }