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
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 {
dim() int
Element(i, j int) float64
SetElement(i, j int, element float64)
}
func isIdentity(ebiten affine) bool {
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
}
// add is deprecated
func add(lhs, rhs, result affine) {
dim := lhs.dim()
if dim != rhs.dim() {

View File

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

View File

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

View File

@ -16,29 +16,11 @@ package affine
import (
"math"
"github.com/hajimehoshi/ebiten/internal/endian"
)
// ColorMDim is a dimension of a ColorM.
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 {
b := make([]uint8, 0, (ColorMDim-1)*(ColorMDim)*8)
for i := 0; i < ColorMDim-1; i++ {
@ -49,15 +31,6 @@ func colorMValueString(values [ColorMDim - 1][ColorMDim]float64) string {
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 is applied to the source alpha color
@ -75,12 +48,22 @@ func (c *ColorM) dim() int {
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 {
if c.values == "" {
c.values = colorMIdentityValue
c.values = identityValues[ColorMDim]
}
if other.values == "" {
other.values = colorMIdentityValue
other.values = identityValues[ColorMDim]
}
return c.values == other.values
}
@ -93,7 +76,7 @@ func (c *ColorM) Concat(other ColorM) {
*c = result
}
// Add adds a color matrix with the other color matrix.
// Add is deprecated.
func (c *ColorM) Add(other ColorM) {
result := ColorM{}
add(&other, c, &result)

View File

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