diff --git a/colorm.go b/colorm.go index bd4782038..7dcbf3a1b 100644 --- a/colorm.go +++ b/colorm.go @@ -15,48 +15,11 @@ package ebiten import ( - "math" - - "github.com/hajimehoshi/ebiten/internal/endian" + "github.com/hajimehoshi/ebiten/internal/affine" ) // 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++ { - for j := 0; j < ColorMDim; j++ { - b = append(b, uint64ToBytes(math.Float64bits(values[i][j]))...) - } - } - 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}, - }) -) +const ColorMDim = affine.ColorMDim // A ColorM represents a matrix to transform coloring when rendering an image. // @@ -67,76 +30,35 @@ var ( // // The initial value is identity. type ColorM struct { - // when values is empty, this matrix is identity. - values string -} - -func (c *ColorM) dim() int { - return ColorMDim + impl affine.ColorM } // 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) { - result := ColorM{} - mul(&other, c, &result) - *c = result + c.impl.Concat(other.impl) } // Add adds a color matrix with the other color matrix. func (c *ColorM) Add(other ColorM) { - result := ColorM{} - add(&other, c, &result) - *c = result + c.impl.Add(other.impl) } // Scale scales the matrix by (r, g, b, a). func (c *ColorM) Scale(r, g, b, a float64) { - for i := 0; i < ColorMDim-1; i++ { - c.SetElement(0, i, c.Element(0, i)*r) - c.SetElement(1, i, c.Element(1, i)*g) - c.SetElement(2, i, c.Element(2, i)*b) - c.SetElement(3, i, c.Element(3, i)*a) - } + c.impl.Scale(r, g, b, a) } // Translate translates the matrix by (r, g, b, a). func (c *ColorM) Translate(r, g, b, a float64) { - c.SetElement(0, 4, c.Element(0, 4)+r) - c.SetElement(1, 4, c.Element(1, 4)+g) - c.SetElement(2, 4, c.Element(2, 4)+b) - c.SetElement(3, 4, c.Element(3, 4)+a) + c.impl.Translate(r, g, b, a) } // RotateHue rotates the hue. func (c *ColorM) RotateHue(theta float64) { - c.ChangeHSV(theta, 1, 1) + c.impl.RotateHue(theta) } -var ( - // The YCbCr value ranges are: - // Y: [ 0 - 1 ] - // Cb: [-0.5 - 0.5] - // Cr: [-0.5 - 0.5] - - rgbToYCbCr = ColorM{ - values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ - {0.2990, 0.5870, 0.1140, 0, 0}, - {-0.1687, -0.3313, 0.5000, 0, 0}, - {0.5000, -0.4187, -0.0813, 0, 0}, - {0, 0, 0, 1, 0}, - }), - } - yCbCrToRgb = ColorM{ - values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ - {1, 0, 1.40200, 0, 0}, - {1, -0.34414, -0.71414, 0, 0}, - {1, 1.77200, 0, 0, 0}, - {0, 0, 0, 1, 0}, - }), - } -) - // ChangeHSV changes HSV (Hue-Saturation-Value) values. // hueTheta is a radian value to ratate hue. // saturationScale is a value to scale saturation. @@ -144,60 +66,35 @@ var ( // // This conversion uses RGB to/from YCrCb conversion. func (c *ColorM) ChangeHSV(hueTheta float64, saturationScale float64, valueScale float64) { - sin, cos := math.Sincos(hueTheta) - c.Concat(rgbToYCbCr) - c.Concat(ColorM{ - values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ - {1, 0, 0, 0, 0}, - {0, cos, -sin, 0, 0}, - {0, sin, cos, 0, 0}, - {0, 0, 0, 1, 0}, - }), - }) - s := saturationScale - v := valueScale - c.Scale(v, s*v, s*v, 1) - c.Concat(yCbCrToRgb) + c.impl.ChangeHSV(hueTheta, saturationScale, valueScale) } -var monochrome ColorM +// Element returns a value of a matrix at (i, j). +func (c *ColorM) Element(i, j int) float64 { + return c.impl.Element(i, j) +} -func init() { - monochrome.ChangeHSV(0, 0, 1) +// SetElement sets an element at (i, j). +func (c *ColorM) SetElement(i, j int, value float64) { + c.impl.SetElement(i, j, value) } // Monochrome returns a color matrix to make an image monochrome. func Monochrome() ColorM { - return monochrome + return ColorM{affine.Monochrome()} } // ScaleColor is deprecated as of 1.2.0-alpha. Use Scale instead. func ScaleColor(r, g, b, a float64) ColorM { - return ColorM{ - values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ - {r, 0, 0, 0, 0}, - {0, g, 0, 0, 0}, - {0, 0, b, 0, 0}, - {0, 0, 0, a, 0}, - }), - } + return ColorM{affine.ScaleColor(r, g, b, a)} } // TranslateColor is deprecated as of 1.2.0-alpha. Use Translate instead. func TranslateColor(r, g, b, a float64) ColorM { - return ColorM{ - values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ - {1, 0, 0, 0, r}, - {0, 1, 0, 0, g}, - {0, 0, 1, 0, b}, - {0, 0, 0, 1, a}, - }), - } + return ColorM{affine.TranslateColor(r, g, b, a)} } // RotateHue is deprecated as of 1.2.0-alpha. Use RotateHue member function instead. func RotateHue(theta float64) ColorM { - c := ColorM{} - c.RotateHue(theta) - return c + return ColorM{affine.RotateHue(theta)} } diff --git a/geom.go b/geom.go index 72d1bd43b..cfea42abb 100644 --- a/geom.go +++ b/geom.go @@ -15,132 +15,66 @@ package ebiten import ( - "math" + "github.com/hajimehoshi/ebiten/internal/affine" ) // GeoMDim is a dimension of a GeoM. -const GeoMDim = 3 +const GeoMDim = affine.GeoMDim // 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 -} - -func (g *GeoM) dim() int { - return GeoMDim -} - -func (g *GeoM) initialize() { - g.initialized = true - g.es[0][0] = 1 - g.es[1][1] = 1 + impl affine.GeoM } // 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][j] + return g.impl.Element(i, j) } // 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 + g.impl.Concat(other.impl) } // Add adds a geometry matrix with the other geometry matrix. func (g *GeoM) Add(other GeoM) { - if !g.initialized { - g.initialize() - } - result := GeoM{} - add(&other, g, &result) - *g = result + g.impl.Add(other.impl) } // 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[0][i] *= x - g.es[1][i] *= y - } + g.impl.Scale(x, y) } // Translate translates the matrix by (x, y). func (g *GeoM) Translate(tx, ty float64) { - if !g.initialized { - g.initialize() - } - g.es[0][2] += tx - g.es[1][2] += ty + g.impl.Translate(tx, ty) } // Rotate rotates the matrix by theta. func (g *GeoM) Rotate(theta float64) { - sin, cos := math.Sincos(theta) - g.Concat(GeoM{ - initialized: true, - es: [2][3]float64{ - {cos, -sin, 0}, - {sin, cos, 0}, - }, - }) + g.impl.Rotate(theta) } // SetElement sets an element at (i, j). func (g *GeoM) SetElement(i, j int, element float64) { - if !g.initialized { - g.initialize() - } - g.es[i][j] = element + g.impl.SetElement(i, 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: [2][3]float64{ - {x, 0, 0}, - {0, y, 0}, - }, - } + return GeoM{affine.ScaleGeo(x, y)} } // TranslateGeo is deprecated as of 1.2.0-alpha. Use Translate instead. func TranslateGeo(tx, ty float64) GeoM { - return GeoM{ - initialized: true, - es: [2][3]float64{ - {1, 0, tx}, - {0, 1, ty}, - }, - } + return GeoM{affine.TranslateGeo(tx, ty)} } // RotateGeo is deprecated as of 1.2.0-alpha. Use Rotate instead. func RotateGeo(theta float64) GeoM { - sin, cos := math.Sincos(theta) - return GeoM{ - initialized: true, - es: [2][3]float64{ - {cos, -sin, 0}, - {sin, cos, 0}, - }, - } + return GeoM{affine.RotateGeo(theta)} } diff --git a/affine.go b/internal/affine/affine.go similarity index 99% rename from affine.go rename to internal/affine/affine.go index 83b30d300..ecf0fdc8a 100644 --- a/affine.go +++ b/internal/affine/affine.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package ebiten +package affine type affine interface { dim() int diff --git a/internal/affine/colorm.go b/internal/affine/colorm.go new file mode 100644 index 000000000..28ec6c390 --- /dev/null +++ b/internal/affine/colorm.go @@ -0,0 +1,203 @@ +// Copyright 2014 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +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++ { + for j := 0; j < ColorMDim; j++ { + b = append(b, uint64ToBytes(math.Float64bits(values[i][j]))...) + } + } + 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 +// 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 initial value is identity. +type ColorM struct { + // when values is empty, this matrix is identity. + values string +} + +func (c *ColorM) dim() int { + return ColorMDim +} + +// 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) { + result := ColorM{} + mul(&other, c, &result) + *c = result +} + +// Add adds a color matrix with the other color matrix. +func (c *ColorM) Add(other ColorM) { + result := ColorM{} + add(&other, c, &result) + *c = result +} + +// Scale scales the matrix by (r, g, b, a). +func (c *ColorM) Scale(r, g, b, a float64) { + for i := 0; i < ColorMDim-1; i++ { + c.SetElement(0, i, c.Element(0, i)*r) + c.SetElement(1, i, c.Element(1, i)*g) + c.SetElement(2, i, c.Element(2, i)*b) + c.SetElement(3, i, c.Element(3, i)*a) + } +} + +// Translate translates the matrix by (r, g, b, a). +func (c *ColorM) Translate(r, g, b, a float64) { + c.SetElement(0, 4, c.Element(0, 4)+r) + c.SetElement(1, 4, c.Element(1, 4)+g) + c.SetElement(2, 4, c.Element(2, 4)+b) + c.SetElement(3, 4, c.Element(3, 4)+a) +} + +// RotateHue rotates the hue. +func (c *ColorM) RotateHue(theta float64) { + c.ChangeHSV(theta, 1, 1) +} + +var ( + // The YCbCr value ranges are: + // Y: [ 0 - 1 ] + // Cb: [-0.5 - 0.5] + // Cr: [-0.5 - 0.5] + + rgbToYCbCr = ColorM{ + values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ + {0.2990, 0.5870, 0.1140, 0, 0}, + {-0.1687, -0.3313, 0.5000, 0, 0}, + {0.5000, -0.4187, -0.0813, 0, 0}, + {0, 0, 0, 1, 0}, + }), + } + yCbCrToRgb = ColorM{ + values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ + {1, 0, 1.40200, 0, 0}, + {1, -0.34414, -0.71414, 0, 0}, + {1, 1.77200, 0, 0, 0}, + {0, 0, 0, 1, 0}, + }), + } +) + +// ChangeHSV changes HSV (Hue-Saturation-Value) values. +// hueTheta is a radian value to ratate hue. +// saturationScale is a value to scale saturation. +// 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 float64, valueScale float64) { + sin, cos := math.Sincos(hueTheta) + c.Concat(rgbToYCbCr) + c.Concat(ColorM{ + values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ + {1, 0, 0, 0, 0}, + {0, cos, -sin, 0, 0}, + {0, sin, cos, 0, 0}, + {0, 0, 0, 1, 0}, + }), + }) + s := saturationScale + v := valueScale + c.Scale(v, s*v, s*v, 1) + c.Concat(yCbCrToRgb) +} + +var monochrome ColorM + +func init() { + monochrome.ChangeHSV(0, 0, 1) +} + +// Monochrome returns a color matrix to make an image monochrome. +func Monochrome() ColorM { + return monochrome +} + +// ScaleColor is deprecated as of 1.2.0-alpha. Use Scale instead. +func ScaleColor(r, g, b, a float64) ColorM { + return ColorM{ + values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ + {r, 0, 0, 0, 0}, + {0, g, 0, 0, 0}, + {0, 0, b, 0, 0}, + {0, 0, 0, a, 0}, + }), + } +} + +// TranslateColor is deprecated as of 1.2.0-alpha. Use Translate instead. +func TranslateColor(r, g, b, a float64) ColorM { + return ColorM{ + values: colorMValueString([ColorMDim - 1][ColorMDim]float64{ + {1, 0, 0, 0, r}, + {0, 1, 0, 0, g}, + {0, 0, 1, 0, b}, + {0, 0, 0, 1, a}, + }), + } +} + +// RotateHue is deprecated as of 1.2.0-alpha. Use RotateHue member function instead. +func RotateHue(theta float64) ColorM { + c := ColorM{} + c.RotateHue(theta) + return c +} diff --git a/colorm_js.go b/internal/affine/colorm_js.go similarity index 98% rename from colorm_js.go rename to internal/affine/colorm_js.go index f18354d18..bdbfcc2ce 100644 --- a/colorm_js.go +++ b/internal/affine/colorm_js.go @@ -14,7 +14,7 @@ // +build js -package ebiten +package affine import ( "github.com/gopherjs/gopherjs/js" diff --git a/colorm_nojs.go b/internal/affine/colorm_nojs.go similarity index 98% rename from colorm_nojs.go rename to internal/affine/colorm_nojs.go index 287193541..672fdd89c 100644 --- a/colorm_nojs.go +++ b/internal/affine/colorm_nojs.go @@ -14,7 +14,7 @@ // +build !js -package ebiten +package affine import ( "math" diff --git a/internal/affine/geom.go b/internal/affine/geom.go new file mode 100644 index 000000000..99eea1e04 --- /dev/null +++ b/internal/affine/geom.go @@ -0,0 +1,146 @@ +// Copyright 2014 Hajime Hoshi +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package affine + +import ( + "math" +) + +// GeoMDim is a dimension of a GeoM. +const GeoMDim = 3 + +// 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 +} + +func (g *GeoM) dim() int { + return GeoMDim +} + +func (g *GeoM) initialize() { + g.initialized = true + g.es[0][0] = 1 + g.es[1][1] = 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][j] +} + +// 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. +func (g *GeoM) Add(other GeoM) { + if !g.initialized { + g.initialize() + } + result := GeoM{} + add(&other, g, &result) + *g = result +} + +// 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[0][i] *= x + g.es[1][i] *= y + } +} + +// Translate translates the matrix by (x, y). +func (g *GeoM) Translate(tx, ty float64) { + if !g.initialized { + g.initialize() + } + g.es[0][2] += tx + g.es[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: [2][3]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][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: [2][3]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: [2][3]float64{ + {1, 0, tx}, + {0, 1, ty}, + }, + } +} + +// RotateGeo is deprecated as of 1.2.0-alpha. Use Rotate instead. +func RotateGeo(theta float64) GeoM { + sin, cos := math.Sincos(theta) + return GeoM{ + initialized: true, + es: [2][3]float64{ + {cos, -sin, 0}, + {sin, cos, 0}, + }, + } +}