affine: User nillable pattern for ColorM

This commit is contained in:
Hajime Hoshi 2018-02-28 01:40:37 +09:00
parent 1b39c05fd4
commit 8c8e512059
6 changed files with 137 additions and 114 deletions

View File

@ -32,12 +32,12 @@ const ColorMDim = affine.ColorMDim
// //
// The initial value is identity. // The initial value is identity.
type ColorM struct { type ColorM struct {
impl affine.ColorM impl *affine.ColorM
} }
// Reset resets the ColorM as identity. // Reset resets the ColorM as identity.
func (c *ColorM) Reset() { func (c *ColorM) Reset() {
c.impl.Reset() c.impl = nil
} }
// Apply pre-multiplies a vector (r, g, b, a, 1) by the matrix // Apply pre-multiplies a vector (r, g, b, a, 1) by the matrix
@ -50,23 +50,23 @@ func (c *ColorM) Apply(clr color.Color) color.Color {
// Concat multiplies a color matrix with the other color matrix. // 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. // This is same as muptiplying the matrix other and the matrix c in this order.
func (c *ColorM) Concat(other ColorM) { func (c *ColorM) Concat(other ColorM) {
c.impl.Concat(&other.impl) c.impl = c.impl.Concat(other.impl)
} }
// Add is deprecated as of 1.5.0-alpha. // Add is deprecated as of 1.5.0-alpha.
// Note that this doesn't make sense as an operation for affine matrices. // Note that this doesn't make sense as an operation for affine matrices.
func (c *ColorM) Add(other ColorM) { func (c *ColorM) Add(other ColorM) {
c.impl.Add(other.impl) c.impl = c.impl.Add(other.impl)
} }
// Scale scales the matrix by (r, g, b, a). // Scale scales the matrix by (r, g, b, a).
func (c *ColorM) Scale(r, g, b, a float64) { func (c *ColorM) Scale(r, g, b, a float64) {
c.impl.Scale(float32(r), float32(g), float32(b), float32(a)) c.impl = c.impl.Scale(float32(r), float32(g), float32(b), float32(a))
} }
// Translate translates the matrix by (r, g, b, a). // Translate translates the matrix by (r, g, b, a).
func (c *ColorM) Translate(r, g, b, a float64) { func (c *ColorM) Translate(r, g, b, a float64) {
c.impl.Translate(float32(r), float32(g), float32(b), float32(a)) c.impl = c.impl.Translate(float32(r), float32(g), float32(b), float32(a))
} }
// RotateHue rotates the hue. // RotateHue rotates the hue.
@ -82,7 +82,7 @@ func (c *ColorM) RotateHue(theta float64) {
// //
// This conversion uses RGB to/from YCrCb conversion. // This conversion uses RGB to/from YCrCb conversion.
func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale float64) { func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale float64) {
c.impl.ChangeHSV(hueTheta, float32(saturationScale), float32(valueScale)) c.impl = c.impl.ChangeHSV(hueTheta, float32(saturationScale), float32(valueScale))
} }
// Element returns a value of a matrix at (i, j). // Element returns a value of a matrix at (i, j).
@ -96,7 +96,7 @@ func (c *ColorM) Element(i, j int) float64 {
// SetElement sets an element at (i, j). // SetElement sets an element at (i, j).
func (c *ColorM) SetElement(i, j int, element float64) { func (c *ColorM) SetElement(i, j int, element float64) {
c.impl.SetElement(i, j, float32(element)) c.impl = c.impl.SetElement(i, j, float32(element))
} }
// Monochrome is deprecated as of 1.6.0-alpha. Use ChangeHSV(0, 0, 1) instead. // Monochrome is deprecated as of 1.6.0-alpha. Use ChangeHSV(0, 0, 1) instead.

View File

@ -212,7 +212,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
filter = graphics.Filter(img.filter) filter = graphics.Filter(img.filter)
} }
i.restorable.DrawImage(img.restorable, sx0, sy0, sx1, sy1, options.GeoM.impl, &options.ColorM.impl, mode, filter) i.restorable.DrawImage(img.restorable, sx0, sy0, sx1, sy1, options.GeoM.impl, options.ColorM.impl, mode, filter)
return nil return nil
} }

View File

@ -49,11 +49,6 @@ type ColorM struct {
translate []float32 translate []float32
} }
func (c *ColorM) Reset() {
c.body = nil
c.translate = nil
}
func clamp(x float32) float32 { func clamp(x float32) float32 {
if x > 1 { if x > 1 {
return 1 return 1
@ -65,7 +60,7 @@ func clamp(x float32) float32 {
} }
func (c *ColorM) Apply(clr color.Color) color.Color { func (c *ColorM) Apply(clr color.Color) color.Color {
if c.body == nil { if c == nil || c.body == nil {
return clr return clr
} }
r, g, b, a := clr.RGBA() r, g, b, a := clr.RGBA()
@ -96,51 +91,58 @@ func (c *ColorM) Apply(clr color.Color) color.Color {
} }
func (c *ColorM) UnsafeElements() ([]float32, []float32) { func (c *ColorM) UnsafeElements() ([]float32, []float32) {
if c.body == nil { if c == nil || c.body == nil {
c.body = colorMIdentityBody return colorMIdentityBody, colorMIdentityTranslate
c.translate = colorMIdentityTranslate
} }
return c.body, c.translate return c.body, c.translate
} }
// SetElement sets an element at (i, j). // SetElement sets an element at (i, j).
func (c *ColorM) SetElement(i, j int, element float32) { func (c *ColorM) SetElement(i, j int, element float32) *ColorM {
if c.body == nil { newC := &ColorM{
c.body = colorMIdentityBody body: make([]float32, 16),
c.translate = colorMIdentityTranslate translate: make([]float32, 4),
}
if c == nil || c.body == nil {
copy(newC.body, colorMIdentityBody)
copy(newC.translate, colorMIdentityTranslate)
} else {
copy(newC.body, c.body)
copy(newC.translate, c.translate)
} }
if j < (ColorMDim - 1) { if j < (ColorMDim - 1) {
es := make([]float32, len(c.body)) newC.body[i+j*(ColorMDim-1)] = element
copy(es, c.body)
es[i+j*(ColorMDim-1)] = element
c.body = es
} else { } else {
es := make([]float32, len(c.translate)) newC.translate[i] = element
copy(es, c.translate)
es[i] = element
c.translate = es
} }
return newC
} }
func (c *ColorM) Equals(other *ColorM) bool { func (c *ColorM) Equals(other *ColorM) bool {
if c.body == nil { if (c == nil || c.body == nil) && (other == nil || other.body == nil) {
if other.body == nil { return true
return true
}
c.body = colorMIdentityBody
c.translate = colorMIdentityTranslate
} }
if other.body == nil {
other.body = colorMIdentityBody lhsb := colorMIdentityBody
other.translate = colorMIdentityTranslate lhst := colorMIdentityTranslate
rhsb := colorMIdentityBody
rhst := colorMIdentityTranslate
if other != nil {
lhsb = other.body
lhst = other.translate
} }
for i := range c.body { if c != nil {
if c.body[i] != other.body[i] { rhsb = c.body
rhst = c.translate
}
for i := range lhsb {
if lhsb[i] != rhsb[i] {
return false return false
} }
} }
for i := range c.translate { for i := range lhst {
if c.translate[i] != other.translate[i] { if lhst[i] != rhst[i] {
return false return false
} }
} }
@ -149,67 +151,81 @@ func (c *ColorM) Equals(other *ColorM) bool {
// Concat multiplies a color matrix with the other color matrix. // 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. // This is same as muptiplying the matrix other and the matrix c in this order.
func (c *ColorM) Concat(other *ColorM) { func (c *ColorM) Concat(other *ColorM) *ColorM {
if c.body == nil { if c == nil || c.body == nil {
c.body = colorMIdentityBody return other
c.translate = colorMIdentityTranslate
} }
if other.body == nil { if other == nil || other.body == nil {
other.body = colorMIdentityBody return c
other.translate = colorMIdentityTranslate
} }
// TODO: This is a temporary hack to calculate multiply of transposed matrices.
// Fix mulSquare implmentation and swap the arguments.
c.body = mulSquare(c.body, other.body, ColorMDim-1)
lhsb := other.body lhsb := colorMIdentityBody
lhst := other.translate lhst := colorMIdentityTranslate
rhst := c.translate rhsb := colorMIdentityBody
c.translate = []float32{ rhst := colorMIdentityTranslate
lhsb[0]*rhst[0] + lhsb[4]*rhst[1] + lhsb[8]*rhst[2] + lhsb[12]*rhst[3] + lhst[0], if other != nil {
lhsb[1]*rhst[0] + lhsb[5]*rhst[1] + lhsb[9]*rhst[2] + lhsb[13]*rhst[3] + lhst[1], lhsb = other.body
lhsb[2]*rhst[0] + lhsb[6]*rhst[1] + lhsb[10]*rhst[2] + lhsb[14]*rhst[3] + lhst[2], lhst = other.translate
lhsb[3]*rhst[0] + lhsb[7]*rhst[1] + lhsb[11]*rhst[2] + lhsb[15]*rhst[3] + lhst[3], }
if c != nil {
rhsb = c.body
rhst = c.translate
}
return &ColorM{
// 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. // Add is deprecated.
func (c *ColorM) Add(other ColorM) { func (c *ColorM) Add(other *ColorM) *ColorM {
// Implementation is just for backward compatibility. lhsb := colorMIdentityBody
if c.body == nil { lhst := colorMIdentityTranslate
c.body = colorMIdentityBody rhsb := colorMIdentityBody
c.translate = colorMIdentityTranslate rhst := colorMIdentityTranslate
if other != nil {
lhsb = other.body
lhst = other.translate
} }
if other.body == nil { if c != nil {
other.body = colorMIdentityBody rhsb = c.body
other.translate = colorMIdentityTranslate rhst = c.translate
} }
body := make([]float32, len(c.body)) newC := &ColorM{
for i := range c.body { body: make([]float32, 16),
body[i] = c.body[i] + other.body[i] translate: make([]float32, 4),
}
for i := range lhsb {
newC.body[i] = lhsb[i] + rhsb[i]
}
for i := range lhst {
newC.translate[i] = lhst[i] + rhst[i]
} }
translate := make([]float32, len(c.translate)) return newC
for i := range c.translate {
translate[i] = c.translate[i] + other.translate[i]
}
c.body = body
c.translate = translate
} }
// Scale scales the matrix by (r, g, b, a). // Scale scales the matrix by (r, g, b, a).
func (c *ColorM) Scale(r, g, b, a float32) { func (c *ColorM) Scale(r, g, b, a float32) *ColorM {
if c.body == nil { if c == nil || c.body == nil {
c.body = []float32{ return &ColorM{
r, 0, 0, 0, body: []float32{
0, g, 0, 0, r, 0, 0, 0,
0, 0, b, 0, 0, g, 0, 0,
0, 0, 0, a, 0, 0, b, 0,
0, 0, 0, a,
},
translate: colorMIdentityTranslate,
} }
c.translate = colorMIdentityTranslate
return
} }
es := make([]float32, len(c.body)) es := make([]float32, len(c.body))
copy(es, c.body) copy(es, c.body)
@ -219,22 +235,25 @@ func (c *ColorM) Scale(r, g, b, a float32) {
es[i*(ColorMDim-1)+2] *= b es[i*(ColorMDim-1)+2] *= b
es[i*(ColorMDim-1)+3] *= a es[i*(ColorMDim-1)+3] *= a
} }
c.body = es
c.translate = []float32{ return &ColorM{
c.translate[0] * r, body: es,
c.translate[1] * g, translate: []float32{
c.translate[2] * b, c.translate[0] * r,
c.translate[3] * a, c.translate[1] * g,
c.translate[2] * b,
c.translate[3] * a,
},
} }
} }
// Translate translates the matrix by (r, g, b, a). // Translate translates the matrix by (r, g, b, a).
func (c *ColorM) Translate(r, g, b, a float32) { func (c *ColorM) Translate(r, g, b, a float32) *ColorM {
if c.body == nil { if c == nil || c.body == nil {
c.body = colorMIdentityBody return &ColorM{
c.translate = []float32{r, g, b, a} body: colorMIdentityBody,
return translate: []float32{r, g, b, a},
}
} }
es := make([]float32, len(c.translate)) es := make([]float32, len(c.translate))
copy(es, c.translate) copy(es, c.translate)
@ -242,7 +261,10 @@ func (c *ColorM) Translate(r, g, b, a float32) {
es[1] += g es[1] += g
es[2] += b es[2] += b
es[3] += a es[3] += a
c.translate = es return &ColorM{
body: c.body,
translate: es,
}
} }
var ( var (
@ -251,7 +273,7 @@ var (
// Cb: [-0.5 - 0.5] // Cb: [-0.5 - 0.5]
// Cr: [-0.5 - 0.5] // Cr: [-0.5 - 0.5]
rgbToYCbCr = ColorM{ rgbToYCbCr = &ColorM{
body: []float32{ body: []float32{
0.2990, -0.1687, 0.5000, 0, 0.2990, -0.1687, 0.5000, 0,
0.5870, -0.3313, -0.4187, 0, 0.5870, -0.3313, -0.4187, 0,
@ -260,7 +282,7 @@ var (
}, },
translate: []float32{0, 0, 0, 0}, translate: []float32{0, 0, 0, 0},
} }
yCbCrToRgb = ColorM{ yCbCrToRgb = &ColorM{
body: []float32{ body: []float32{
1, 1, 1, 0, 1, 1, 1, 0,
0, -0.34414, 1.77200, 0, 0, -0.34414, 1.77200, 0,
@ -277,11 +299,11 @@ var (
// valueScale is a value to scale value (a.k.a. brightness). // valueScale is a value to scale value (a.k.a. brightness).
// //
// This conversion uses RGB to/from YCrCb conversion. // This conversion uses RGB to/from YCrCb conversion.
func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float32, valueScale float32) { func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float32, valueScale float32) *ColorM {
sin, cos := math.Sincos(hueTheta) sin, cos := math.Sincos(hueTheta)
s32, c32 := float32(sin), float32(cos) s32, c32 := float32(sin), float32(cos)
c.Concat(&rgbToYCbCr) c = c.Concat(rgbToYCbCr)
c.Concat(&ColorM{ c = c.Concat(&ColorM{
body: []float32{ body: []float32{
1, 0, 0, 0, 1, 0, 0, 0,
0, c32, s32, 0, 0, c32, s32, 0,
@ -292,6 +314,7 @@ func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float32, valueScale
}) })
s := saturationScale s := saturationScale
v := valueScale v := valueScale
c.Scale(v, s*v, s*v, 1) c = c.Scale(v, s*v, s*v, 1)
c.Concat(&yCbCrToRgb) c = c.Concat(yCbCrToRgb)
return c
} }

View File

@ -85,7 +85,7 @@ func (q *commandQueue) EnqueueDrawImageCommand(dst, src *Image, vertices []float
dst: dst, dst: dst,
src: src, src: src,
verticesNum: len(vertices), verticesNum: len(vertices),
color: *clr, color: clr,
mode: mode, mode: mode,
filter: filter, filter: filter,
} }
@ -188,7 +188,7 @@ type drawImageCommand struct {
dst *Image dst *Image
src *Image src *Image
verticesNum int verticesNum int
color affine.ColorM color *affine.ColorM
mode opengl.CompositeMode mode opengl.CompositeMode
filter Filter filter Filter
} }

View File

@ -248,7 +248,7 @@ func areSameFloat32Array(a, b []float32) bool {
} }
// useProgram uses the program (programTexture). // useProgram uses the program (programTexture).
func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, dst, src *Image, colorM affine.ColorM, filter Filter) { func (s *openGLState) useProgram(proj []float32, texture opengl.Texture, dst, src *Image, colorM *affine.ColorM, filter Filter) {
c := opengl.GetContext() c := opengl.GetContext()
var program opengl.Program var program opengl.Program

View File

@ -33,7 +33,7 @@ const MaxImageSize = graphics.MaxImageSize
type drawImageHistoryItem struct { type drawImageHistoryItem struct {
image *Image image *Image
vertices []float32 vertices []float32
colorm affine.ColorM colorm *affine.ColorM
mode opengl.CompositeMode mode opengl.CompositeMode
filter graphics.Filter filter graphics.Filter
} }
@ -131,7 +131,7 @@ var (
) )
func init() { func init() {
clearColorM.Scale(0, 0, 0, 0) clearColorM = clearColorM.Scale(0, 0, 0, 0)
} }
// clearIfVolatile clears the image if the image is volatile. // clearIfVolatile clears the image if the image is volatile.
@ -207,7 +207,7 @@ func (i *Image) appendDrawImageHistory(image *Image, vertices []float32, colorm
item := &drawImageHistoryItem{ item := &drawImageHistoryItem{
image: image, image: image,
vertices: vertices, vertices: vertices,
colorm: *colorm, colorm: colorm,
mode: mode, mode: mode,
filter: filter, filter: filter,
} }
@ -332,7 +332,7 @@ func (i *Image) restore() error {
if c.image.hasDependency() { if c.image.hasDependency() {
panic("not reached") panic("not reached")
} }
gimg.DrawImage(c.image.image, c.vertices, &c.colorm, c.mode, c.filter) gimg.DrawImage(c.image.image, c.vertices, c.colorm, c.mode, c.filter)
} }
i.image = gimg i.image = gimg