diff --git a/colorm.go b/colorm.go index 1d9e90cc2..83fb64a39 100644 --- a/colorm.go +++ b/colorm.go @@ -39,12 +39,12 @@ type ColorM struct { // String returns a string representation of ColorM. func (c *ColorM) String() string { - es := c.impl.UnsafeElements() + b, t := c.impl.UnsafeElements() return fmt.Sprintf("[[%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f], [%f, %f, %f, %f, %f]]", - es[0], es[4], es[8], es[12], es[16], - es[1], es[5], es[9], es[13], es[17], - es[2], es[6], es[10], es[14], es[18], - es[3], es[7], es[11], es[15], es[19]) + b[0], b[4], b[8], b[12], t[0], + b[1], b[5], b[9], b[13], t[1], + b[2], b[6], b[10], b[14], t[2], + b[3], b[7], b[11], b[15], t[3]) } // Reset resets the ColorM as identity. @@ -99,8 +99,11 @@ func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale // Element returns a value of a matrix at (i, j). func (c *ColorM) Element(i, j int) float64 { - es := c.impl.UnsafeElements() - return float64(es[i+j*(ColorMDim-1)]) + b, t := c.impl.UnsafeElements() + if j < ColorMDim-1 { + return float64(b[i+j*(ColorMDim-1)]) + } + return float64(t[i]) } // SetElement sets an element at (i, j). diff --git a/internal/affine/affine.go b/internal/affine/affine.go index 8a0e490b7..26b1afa8a 100644 --- a/internal/affine/affine.go +++ b/internal/affine/affine.go @@ -14,25 +14,15 @@ package affine -func affineAt(es []float32, i, j int, dim int) float32 { - if i == dim-1 { - if j == dim-1 { - return 1 - } - return 0 - } - return es[i+j*(dim-1)] -} - -func mulAffine(lhs, rhs []float32, dim int) []float32 { +func mulSquare(lhs, rhs []float32, dim int) []float32 { result := make([]float32, len(lhs)) - for i := 0; i < dim-1; i++ { + for i := 0; i < dim; i++ { for j := 0; j < dim; j++ { e := float32(0.0) for k := 0; k < dim; k++ { - e += affineAt(lhs, i, k, dim) * affineAt(rhs, k, j, dim) + e += lhs[i*dim+k] * rhs[k*dim+j] } - result[i+j*(dim-1)] = e + result[i*dim+j] = e } } return result diff --git a/internal/affine/colorm.go b/internal/affine/colorm.go index 4bbb426c1..460b7a675 100644 --- a/internal/affine/colorm.go +++ b/internal/affine/colorm.go @@ -23,11 +23,13 @@ import ( const ColorMDim = 5 var ( - colorMIdentity = []float32{ + colorMIdentityBody = []float32{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, + } + colorMIdentityTranslate = []float32{ 0, 0, 0, 0, } ) @@ -43,8 +45,8 @@ var ( type ColorM struct { // When elements is nil, this matrix is identity. // elements are immutable and a new array must be created when updating. - // Note that elements are transposed for OpenGL. - elements []float32 + body []float32 + translate []float32 } func clamp(x float32) float32 { @@ -58,7 +60,7 @@ func clamp(x float32) float32 { } func (c *ColorM) isInited() bool { - return c != nil && c.elements != nil + return c != nil && (c.body != nil || c.translate != nil) } func (c *ColorM) Apply(clr color.Color) color.Color { @@ -74,14 +76,18 @@ func (c *ColorM) Apply(clr color.Color) color.Color { bf = float32(b) / float32(a) af = float32(a) / 0xffff } - es := c.elements - if es == nil { - es = colorMIdentity + eb := c.body + if eb == nil { + eb = colorMIdentityBody } - rf2 := es[0]*rf + es[4]*gf + es[8]*bf + es[12]*af + es[16] - gf2 := es[1]*rf + es[5]*gf + es[9]*bf + es[13]*af + es[17] - bf2 := es[2]*rf + es[6]*gf + es[10]*bf + es[14]*af + es[18] - af2 := es[3]*rf + es[7]*gf + es[11]*bf + es[15]*af + es[19] + et := c.translate + if et == nil { + et = colorMIdentityTranslate + } + rf2 := eb[0]*rf + eb[4]*gf + eb[8]*bf + eb[12]*af + et[0] + gf2 := eb[1]*rf + eb[5]*gf + eb[9]*bf + eb[13]*af + et[1] + bf2 := eb[2]*rf + eb[6]*gf + eb[10]*bf + eb[14]*af + et[2] + af2 := eb[3]*rf + eb[7]*gf + eb[11]*bf + eb[15]*af + et[3] rf2 = clamp(rf2) gf2 = clamp(gf2) bf2 = clamp(bf2) @@ -94,23 +100,42 @@ func (c *ColorM) Apply(clr color.Color) color.Color { } } -func (c *ColorM) UnsafeElements() []float32 { +func (c *ColorM) UnsafeElements() ([]float32, []float32) { if !c.isInited() { - return colorMIdentity + return colorMIdentityBody, colorMIdentityTranslate } - return c.elements + eb := c.body + if eb == nil { + eb = colorMIdentityBody + } + et := c.translate + if et == nil { + et = colorMIdentityTranslate + } + return eb, et } // SetElement sets an element at (i, j). func (c *ColorM) SetElement(i, j int, element float32) *ColorM { newC := &ColorM{ - elements: make([]float32, 20), + body: make([]float32, 16), + translate: make([]float32, 4), } - copy(newC.elements, colorMIdentity) + copy(newC.body, colorMIdentityBody) + copy(newC.translate, colorMIdentityTranslate) if c.isInited() { - copy(newC.elements, c.elements) + if c.body != nil { + copy(newC.body, c.body) + } + if c.translate != nil { + copy(newC.translate, c.translate) + } + } + if j < (ColorMDim - 1) { + newC.body[i+j*(ColorMDim-1)] = element + } else { + newC.translate[i] = element } - newC.elements[i+j*(ColorMDim-1)] = element return newC } @@ -124,36 +149,72 @@ func (c *ColorM) Concat(other *ColorM) *ColorM { return c } - lhs := colorMIdentity - rhs := colorMIdentity + lhsb := colorMIdentityBody + lhst := colorMIdentityTranslate + rhsb := colorMIdentityBody + rhst := colorMIdentityTranslate if other.isInited() { - lhs = other.elements + if other.body != nil { + lhsb = other.body + } + if other.translate != nil { + lhst = other.translate + } } if c.isInited() { - rhs = c.elements + if c.body != nil { + rhsb = c.body + } + if c.translate != nil { + rhst = c.translate + } } return &ColorM{ - elements: mulAffine(lhs, rhs, ColorMDim), + // 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], + }, } } // Add is deprecated. func (c *ColorM) Add(other *ColorM) *ColorM { - lhs := colorMIdentity - rhs := colorMIdentity + lhsb := colorMIdentityBody + lhst := colorMIdentityTranslate + rhsb := colorMIdentityBody + rhst := colorMIdentityTranslate if other.isInited() { - lhs = other.elements + if other.body != nil { + lhsb = other.body + } + if other.translate != nil { + lhst = other.translate + } } if c.isInited() { - rhs = c.elements + if c.body != nil { + rhsb = c.body + } + if c.translate != nil { + rhst = c.translate + } } newC := &ColorM{ - elements: make([]float32, 20), + body: make([]float32, 16), + translate: make([]float32, 4), } - for i := range lhs { - newC.elements[i] = lhs[i] + rhs[i] + for i := range lhsb { + newC.body[i] = lhsb[i] + rhsb[i] + } + for i := range lhst { + newC.translate[i] = lhst[i] + rhst[i] } return newC @@ -163,26 +224,41 @@ func (c *ColorM) Add(other *ColorM) *ColorM { func (c *ColorM) Scale(r, g, b, a float32) *ColorM { if !c.isInited() { return &ColorM{ - elements: []float32{ + body: []float32{ r, 0, 0, 0, 0, g, 0, 0, 0, 0, b, 0, 0, 0, 0, a, - 0, 0, 0, 0, }, } } - es := make([]float32, len(colorMIdentity)) - copy(es, c.elements) - for i := 0; i < ColorMDim; i++ { - es[i*(ColorMDim-1)] *= r - es[i*(ColorMDim-1)+1] *= g - es[i*(ColorMDim-1)+2] *= b - es[i*(ColorMDim-1)+3] *= a + eb := make([]float32, len(colorMIdentityBody)) + if c.body != nil { + copy(eb, c.body) + for i := 0; i < ColorMDim-1; i++ { + eb[i*(ColorMDim-1)] *= r + eb[i*(ColorMDim-1)+1] *= g + eb[i*(ColorMDim-1)+2] *= b + eb[i*(ColorMDim-1)+3] *= a + } + } else { + eb[0] = r + eb[5] = g + eb[10] = b + eb[15] = a + } + + et := make([]float32, len(colorMIdentityTranslate)) + if c.translate != nil { + et[0] = c.translate[0] * r + et[1] = c.translate[1] * g + et[2] = c.translate[2] * b + et[3] = c.translate[3] * a } return &ColorM{ - elements: es, + body: eb, + translate: et, } } @@ -190,23 +266,20 @@ func (c *ColorM) Scale(r, g, b, a float32) *ColorM { func (c *ColorM) Translate(r, g, b, a float32) *ColorM { if !c.isInited() { return &ColorM{ - elements: []float32{ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - r, g, b, a, - }, + translate: []float32{r, g, b, a}, } } - es := make([]float32, len(colorMIdentity)) - copy(es, c.elements) - es[16] += r - es[17] += g - es[18] += b - es[19] += a + es := make([]float32, len(colorMIdentityTranslate)) + if c.translate != nil { + copy(es, c.translate) + } + es[0] += r + es[1] += g + es[2] += b + es[3] += a return &ColorM{ - elements: es, + body: c.body, + translate: es, } } @@ -217,21 +290,19 @@ var ( // Cr: [-0.5 - 0.5] rgbToYCbCr = &ColorM{ - elements: []float32{ + 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, - 0, 0, 0, 0, }, } yCbCrToRgb = &ColorM{ - elements: []float32{ + body: []float32{ 1, 1, 1, 0, 0, -0.34414, 1.77200, 0, 1.40200, -0.71414, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, }, } ) @@ -247,12 +318,11 @@ func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float32, valueScale s32, c32 := float32(sin), float32(cos) c = c.Concat(rgbToYCbCr) c = c.Concat(&ColorM{ - elements: []float32{ + body: []float32{ 1, 0, 0, 0, 0, c32, s32, 0, 0, -s32, c32, 0, 0, 0, 0, 1, - 0, 0, 0, 0, }, }) s := saturationScale diff --git a/internal/graphicsutil/vertices.go b/internal/graphicsutil/vertices.go index 5a85879c1..5cadc446f 100644 --- a/internal/graphicsutil/vertices.go +++ b/internal/graphicsutil/vertices.go @@ -75,7 +75,7 @@ func quadVerticesImpl(x, y, u0, v0, u1, v1, a, b, c, d, tx, ty float32, colorm * vs := theVerticesBackend.sliceForOneQuad()[0:104] ax, by, cx, dy := a*x, b*y, c*x, d*y - ce := colorm.UnsafeElements() + cbody, ctranslate := colorm.UnsafeElements() // Vertex coordinates vs[0] = tx @@ -112,11 +112,17 @@ func quadVerticesImpl(x, y, u0, v0, u1, v1, a, b, c, d, tx, ty float32, colorm * vs[83] = v0 // Use for loop since subslicing is heavy on GopherJS. - for i, e := range ce { - vs[6+i] = e - vs[32+i] = e - vs[58+i] = e - vs[84+i] = e + for i := 0; i < 16; i++ { + vs[6+i] = cbody[i] + vs[32+i] = cbody[i] + vs[58+i] = cbody[i] + vs[84+i] = cbody[i] + } + for i := 0; i < 4; i++ { + vs[22+i] = ctranslate[i] + vs[48+i] = ctranslate[i] + vs[74+i] = ctranslate[i] + vs[100+i] = ctranslate[i] } return vs