From f0c42c6e56ef67ec1aaa9bfdfda522acb21d7d90 Mon Sep 17 00:00:00 2001 From: faiface Date: Sun, 28 May 2017 18:44:30 +0200 Subject: [PATCH 01/10] add Canvas.Frame method --- pixelgl/canvas.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index 070a382..93065ea 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -218,6 +218,11 @@ func (c *Canvas) Texture() *glhf.Texture { return c.gf.Texture() } +// Frame return the underlying OpenGL Frame of this Canvas. +func (c *Canvas) Frame() *glhf.Frame { + return c.gf.frame +} + // SetPixels replaces the content of the Canvas with the provided pixels. The provided slice must be // an alpha-premultiplied RGBA sequence of correct length (4 * width * height). func (c *Canvas) SetPixels(pixels []uint8) { From 793d1e6a9a33c4761273a4ed30c8c94705489f7e Mon Sep 17 00:00:00 2001 From: faiface Date: Sun, 28 May 2017 18:50:56 +0200 Subject: [PATCH 02/10] fix typo in doc --- pixelgl/canvas.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index 93065ea..a088350 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -218,7 +218,7 @@ func (c *Canvas) Texture() *glhf.Texture { return c.gf.Texture() } -// Frame return the underlying OpenGL Frame of this Canvas. +// Frame returns the underlying OpenGL Frame of this Canvas. func (c *Canvas) Frame() *glhf.Frame { return c.gf.frame } From 9bd9df98f23781f97530f45146993274a67b7003 Mon Sep 17 00:00:00 2001 From: faiface Date: Tue, 30 May 2017 02:53:24 +0200 Subject: [PATCH 03/10] add text tutorial link to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ac347e9..e1553de 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ covering several topics of Pixel. Here's the content of the tutorial parts so fa - [Pressing keys and clicking mouse](https://github.com/faiface/pixel/wiki/Pressing-keys-and-clicking-mouse) - [Drawing efficiently with Batch](https://github.com/faiface/pixel/wiki/Drawing-efficiently-with-Batch) - [Drawing shapes with IMDraw](https://github.com/faiface/pixel/wiki/Drawing-shapes-with-IMDraw) +- [Typing text on the screen](https://github.com/faiface/pixel/wiki/Typing-text-on-the-screen) ## Examples From 1586e600a0e924f26309e6367e4fcc9b38fc3fdb Mon Sep 17 00:00:00 2001 From: Seebs Date: Sun, 4 Jun 2017 12:55:43 -0500 Subject: [PATCH 04/10] The initializer is surprisingly expensive. Removing the call to Alpha(1) and replacing it with an inline definition produces measurable improvements. Replacing each instance of ZV with Vec{} further improves things. We keep an inline RGBA because there are circumstances (mostly when using pictures) where we don't want to have to set colors to get default behavior. For a fairly triangle-heavy thing, this reduces time spent in SetLen from something over 10% of execution time to around 2.5% of execution time. --- data.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data.go b/data.go index 4e6c528..c941241 100644 --- a/data.go +++ b/data.go @@ -45,7 +45,7 @@ func (td *TrianglesData) SetLen(len int) { Color RGBA Picture Vec Intensity float64 - }{ZV, Alpha(1), ZV, 0}) + }{Color: RGBA{1, 1, 1, 1}}) } } if len < td.Len() { From c321515d3cb37157d6587e010b7a97acfe8eeebd Mon Sep 17 00:00:00 2001 From: Seebs Date: Mon, 5 Jun 2017 18:54:53 -0500 Subject: [PATCH 05/10] Simplify Matrix math, use 6-value affine matrixes. It turns out that affine matrices are much simpler than the 3x3 matrices they imply, and we can use this to dramatically streamline some code. For a test program, this was about a 50% gain in frame rate just from the cost of the applyMatrixAndMask calls in imdraw, which were calling matrix.Project() many times. Simplifying matrix.Project, alone, got a nearly 50% frame rate boost! Also modify pixelgl's SetMatrix to copy the six values of a 3x2 Affine into the corresponding locations of a 3x3 matrix. --- geometry.go | 75 ++++++++++++++++++++++++++--------------------- pixelgl/canvas.go | 11 +++++-- 2 files changed, 51 insertions(+), 35 deletions(-) diff --git a/geometry.go b/geometry.go index f934839..928c9c5 100644 --- a/geometry.go +++ b/geometry.go @@ -3,8 +3,6 @@ package pixel import ( "fmt" "math" - - "github.com/go-gl/mathgl/mgl64" ) // Vec is a 2D vector type with X and Y coordinates. @@ -251,7 +249,7 @@ func (r Rect) Union(s Rect) Rect { ) } -// Matrix is a 3x3 transformation matrix that can be used for all kinds of spacial transforms, such +// Matrix is a 3x2 affine matrix that can be used for all kinds of spatial transforms, such // as movement, scaling and rotations. // // Matrix has a handful of useful methods, each of which adds a transformation to the matrix. For @@ -261,38 +259,41 @@ func (r Rect) Union(s Rect) Rect { // // This code creates a Matrix that first moves everything by 100 units horizontally and 200 units // vertically and then rotates everything by 90 degrees around the origin. -type Matrix [9]float64 +// +// Layout is: +// [0] [2] [4] +// [1] [3] [5] +// 0 0 1 [implicit row] +type Matrix [6]float64 // IM stands for identity matrix. Does nothing, no transformation. -var IM = Matrix(mgl64.Ident3()) +var IM = Matrix{1, 0, 0, 1, 0, 0} // String returns a string representation of the Matrix. // // m := pixel.IM -// fmt.Println(m) // Matrix(1 0 0 | 0 1 0 | 0 0 1) +// fmt.Println(m) // Matrix(1 0 0 | 0 1 0) func (m Matrix) String() string { return fmt.Sprintf( - "Matrix(%v %v %v | %v %v %v | %v %v %v)", - m[0], m[3], m[6], - m[1], m[4], m[7], - m[2], m[5], m[8], + "Matrix(%v %v %v | %v %v %v)", + m[0], m[2], m[4], + m[1], m[3], m[5], ) } // Moved moves everything by the delta vector. func (m Matrix) Moved(delta Vec) Matrix { - m3 := mgl64.Mat3(m) - m3 = mgl64.Translate2D(delta.XY()).Mul3(m3) - return Matrix(m3) + m[4], m[5] = m[4]+delta.X, m[5]+delta.Y + return m } // ScaledXY scales everything around a given point by the scale factor in each axis respectively. func (m Matrix) ScaledXY(around Vec, scale Vec) Matrix { - m3 := mgl64.Mat3(m) - m3 = mgl64.Translate2D(around.Scaled(-1).XY()).Mul3(m3) - m3 = mgl64.Scale2D(scale.XY()).Mul3(m3) - m3 = mgl64.Translate2D(around.XY()).Mul3(m3) - return Matrix(m3) + m[4], m[5] = m[4]-around.X, m[5]-around.Y + m[0], m[2], m[4] = m[0]*scale.X, m[2]*scale.X, m[4]*scale.X + m[1], m[3], m[5] = m[1]*scale.Y, m[3]*scale.Y, m[5]*scale.Y + m[4], m[5] = m[4]+around.X, m[5]+around.Y + return m } // Scaled scales everything around a given point by the scale factor. @@ -302,36 +303,44 @@ func (m Matrix) Scaled(around Vec, scale float64) Matrix { // Rotated rotates everything around a given point by the given angle in radians. func (m Matrix) Rotated(around Vec, angle float64) Matrix { - m3 := mgl64.Mat3(m) - m3 = mgl64.Translate2D(around.Scaled(-1).XY()).Mul3(m3) - m3 = mgl64.Rotate3DZ(angle).Mul3(m3) - m3 = mgl64.Translate2D(around.XY()).Mul3(m3) - return Matrix(m3) + sint, cost := math.Sincos(angle) + m[4], m[5] = m[4]-around.X, m[5]-around.Y + m = m.Chained(Matrix{cost, sint, -sint, cost, 0, 0}) + m[4], m[5] = m[4]+around.X, m[5]+around.Y + return m } // Chained adds another Matrix to this one. All tranformations by the next Matrix will be applied // after the transformations of this Matrix. func (m Matrix) Chained(next Matrix) Matrix { - m3 := mgl64.Mat3(m) - m3 = mgl64.Mat3(next).Mul3(m3) - return Matrix(m3) + return Matrix{ + m[0]*next[0] + m[2]*next[1], + m[1]*next[0] + m[3]*next[1], + m[0]*next[2] + m[2]*next[3], + m[1]*next[2] + m[3]*next[3], + m[0]*next[4] + m[2]*next[5] + m[4], + m[1]*next[4] + m[3]*next[5] + m[5], + } } // Project applies all transformations added to the Matrix to a vector u and returns the result. // // Time complexity is O(1). func (m Matrix) Project(u Vec) Vec { - m3 := mgl64.Mat3(m) - proj := m3.Mul3x1(mgl64.Vec3{u.X, u.Y, 1}) - return V(proj.X(), proj.Y()) + return Vec{X: m[0]*u.X + m[2]*u.Y + m[4], Y: m[1]*u.X + m[3]*u.Y + m[5]} } // Unproject does the inverse operation to Project. // +// It turns out that multiplying a vector by the inverse matrix of m +// can be nearly-accomplished by subtracting the translate part of the +// matrix and multplying by the inverse of the top-left 2x2 matrix, +// and the inverse of a 2x2 matrix is simple enough to just be +// inlined in the computation. +// // Time complexity is O(1). func (m Matrix) Unproject(u Vec) Vec { - m3 := mgl64.Mat3(m) - inv := m3.Inv() - unproj := inv.Mul3x1(mgl64.Vec3{u.X, u.Y, 1}) - return V(unproj.X(), unproj.Y()) + d := (m[0] * m[3]) - (m[1] * m[2]) + u.X, u.Y = (u.X-m[4])/d, (u.Y-m[5])/d + return Vec{u.X*m[3] - u.Y*m[1], u.Y*m[0] - u.X*m[2]} } diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index a088350..b7b5cfc 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -90,9 +90,16 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture { } // SetMatrix sets a Matrix that every point will be projected by. +// pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So +// [0] [2] [4] [0] [3] [6] +// [1] [3] [5] => [1] [4] [7] +// 0 0 1 0 0 1 +// since all matrix ops are affine, the last row never changes, +// and we don't need to copy it +// func (c *Canvas) SetMatrix(m pixel.Matrix) { - for i := range m { - c.mat[i] = float32(m[i]) + for i, j := range [6]int{ 0, 1, 3, 4, 6, 7} { + c.mat[j] = float32(m[i]) } } From 9d1fc5bd1f4fb7b32ed5e9b0a82c2e150ac9e7f2 Mon Sep 17 00:00:00 2001 From: Seebs Date: Mon, 5 Jun 2017 19:37:53 -0500 Subject: [PATCH 06/10] use point pool For internal operations (anything using getAndClearPoints), there's a pretty good chance that the operation will repeatedly invoke something like fillPolygon(), meaning that it needs to push "a few" points and then invoke something that uses those points. So, we add a slice for containing spare slices of points, and on the way out of each such function, shove the current imd.points (as used inside that function) onto a stack, and set imd.points to [0:0] of the thing it was called with. Performance goes from 11-13fps to 17-18fps on my test case. --- imdraw/imdraw.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go index 626cb2f..2fe1fad 100644 --- a/imdraw/imdraw.go +++ b/imdraw/imdraw.go @@ -52,6 +52,7 @@ type IMDraw struct { EndShape EndShape points []point + pool [][]point matrix pixel.Matrix mask pixel.RGBA @@ -109,7 +110,7 @@ func (imd *IMDraw) Clear() { // // This does not affect matrix and color mask set by SetMatrix and SetColorMask. func (imd *IMDraw) Reset() { - imd.points = nil + imd.points = imd.points[:0] imd.Color = pixel.Alpha(1) imd.Picture = pixel.ZV imd.Intensity = 0 @@ -256,10 +257,22 @@ func (imd *IMDraw) EllipseArc(radius pixel.Vec, low, high, thickness float64) { func (imd *IMDraw) getAndClearPoints() []point { points := imd.points - imd.points = nil + // use one of the existing pools so we don't reallocate as often + if len(imd.pool) > 0 { + pos := len(imd.pool) - 1 + imd.points = imd.pool[pos] + imd.pool = imd.pool[0:pos] + } else { + imd.points = nil + } return points } +func (imd *IMDraw) restorePoints(points []point) { + imd.pool = append(imd.pool, imd.points) + imd.points = points[:0] +} + func (imd *IMDraw) applyMatrixAndMask(off int) { for i := range (*imd.tri)[off:] { (*imd.tri)[off+i].Position = imd.matrix.Project((*imd.tri)[off+i].Position) @@ -271,6 +284,7 @@ func (imd *IMDraw) fillRectangle() { points := imd.getAndClearPoints() if len(points) < 2 { + imd.restorePoints(points) return } @@ -302,12 +316,14 @@ func (imd *IMDraw) fillRectangle() { imd.applyMatrixAndMask(off) imd.batch.Dirty() + imd.restorePoints(points) } func (imd *IMDraw) outlineRectangle(thickness float64) { points := imd.getAndClearPoints() if len(points) < 2 { + imd.restorePoints(points) return } @@ -323,12 +339,14 @@ func (imd *IMDraw) outlineRectangle(thickness float64) { imd.pushPt(pixel.V(b.pos.X, a.pos.Y), mid) imd.polyline(thickness, true) } + imd.restorePoints(points) } func (imd *IMDraw) fillPolygon() { points := imd.getAndClearPoints() if len(points) < 3 { + imd.restorePoints(points) return } @@ -346,6 +364,7 @@ func (imd *IMDraw) fillPolygon() { imd.applyMatrixAndMask(off) imd.batch.Dirty() + imd.restorePoints(points) } func (imd *IMDraw) fillEllipseArc(radius pixel.Vec, low, high float64) { @@ -387,6 +406,7 @@ func (imd *IMDraw) fillEllipseArc(radius pixel.Vec, low, high float64) { imd.applyMatrixAndMask(off) imd.batch.Dirty() } + imd.restorePoints(points) } func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness float64, doEndShape bool) { @@ -485,12 +505,14 @@ func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness floa } } } + imd.restorePoints(points) } func (imd *IMDraw) polyline(thickness float64, closed bool) { points := imd.getAndClearPoints() if len(points) == 0 { + imd.restorePoints(points) return } if len(points) == 1 { @@ -591,4 +613,5 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normal.Angle(), normal.Angle()-math.Pi) } } + imd.restorePoints(points) } From b6533006e7b3e897f59a3c4c92fe08ebbf389c0a Mon Sep 17 00:00:00 2001 From: Seebs Date: Mon, 5 Jun 2017 19:46:16 -0500 Subject: [PATCH 07/10] smaller imdraw optimizations For polyline, don't compute each normal twice; when we're going through a line, the "next" normal for segment N is always the "previous" normal for segment N+1, and we can compute fewer of them. --- imdraw/imdraw.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go index 2fe1fad..0d40452 100644 --- a/imdraw/imdraw.go +++ b/imdraw/imdraw.go @@ -543,6 +543,8 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { imd.pushPt(points[j].pos.Sub(normal), points[j]) // middle points + // compute "previous" normal: + ijNormal := points[1].pos.Sub(points[0].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) for i := 0; i < len(points); i++ { j, k := i+1, i+2 @@ -558,7 +560,6 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { k %= len(points) } - ijNormal := points[j].pos.Sub(points[i].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) jkNormal := points[k].pos.Sub(points[j].pos).Rotated(math.Pi / 2).Unit().Scaled(thickness / 2) orientation := 1.0 @@ -589,6 +590,8 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { imd.pushPt(points[j].pos.Add(jkNormal), points[j]) imd.pushPt(points[j].pos.Sub(jkNormal), points[j]) } + // "next" normal becomes previous normal + ijNormal = jkNormal } // last point From c6e7a834679b85a464b2f1617280f600d0fdf61c Mon Sep 17 00:00:00 2001 From: Seebs Date: Mon, 5 Jun 2017 20:12:35 -0500 Subject: [PATCH 08/10] Reduce copying in fillPolygon A slice of points means copying every point into the slice, then copying every point's data from the slice to TrianglesData. An array of indicies lets the compiler make better choices. --- imdraw/imdraw.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go index 0d40452..e5a8c95 100644 --- a/imdraw/imdraw.go +++ b/imdraw/imdraw.go @@ -354,11 +354,12 @@ func (imd *IMDraw) fillPolygon() { imd.tri.SetLen(imd.tri.Len() + 3*(len(points)-2)) for i, j := 1, off; i+1 < len(points); i, j = i+1, j+3 { - for k, p := range []point{points[0], points[i], points[i+1]} { - (*imd.tri)[j+k].Position = p.pos - (*imd.tri)[j+k].Color = p.col - (*imd.tri)[j+k].Picture = p.pic - (*imd.tri)[j+k].Intensity = p.in + for k, p := range []int{0, i, i + 1} { + tri := &(*imd.tri)[j+k] + tri.Position = points[p].pos + tri.Color = points[p].col + tri.Picture = points[p].pic + tri.Intensity = points[p].in } } From ce8687b80fc0dec46328fcbb4e8436f54d8595b8 Mon Sep 17 00:00:00 2001 From: Seebs Date: Fri, 9 Jun 2017 00:07:08 -0500 Subject: [PATCH 09/10] Don't duplicate computations in gltriangles.go The computation including a call to Stride() can't be optimized away safely because the compiler can't tell that Stride() is effectively constant, but we know it won't change so we can make a slice pointing at that part of the array. CPU time for updateData goes from 26.35% to 18.65% in my test case. --- pixelgl/gltriangles.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go index 64b7355..bf7a895 100644 --- a/pixelgl/gltriangles.go +++ b/pixelgl/gltriangles.go @@ -103,15 +103,17 @@ func (gt *GLTriangles) updateData(t pixel.Triangles) { tx, ty = (*t)[i].Picture.XY() in = (*t)[i].Intensity ) - gt.data[i*gt.vs.Stride()+0] = float32(px) - gt.data[i*gt.vs.Stride()+1] = float32(py) - gt.data[i*gt.vs.Stride()+2] = float32(col.R) - gt.data[i*gt.vs.Stride()+3] = float32(col.G) - gt.data[i*gt.vs.Stride()+4] = float32(col.B) - gt.data[i*gt.vs.Stride()+5] = float32(col.A) - gt.data[i*gt.vs.Stride()+6] = float32(tx) - gt.data[i*gt.vs.Stride()+7] = float32(ty) - gt.data[i*gt.vs.Stride()+8] = float32(in) + s := gt.vs.Stride() + d := gt.data[i*s : i*s+9] + d[0] = float32(px) + d[1] = float32(py) + d[2] = float32(col.R) + d[3] = float32(col.G) + d[4] = float32(col.B) + d[5] = float32(col.A) + d[6] = float32(tx) + d[7] = float32(ty) + d[8] = float32(in) } return } From c0766504e303df8314472fda84695a260efc6e73 Mon Sep 17 00:00:00 2001 From: faiface Date: Fri, 9 Jun 2017 18:13:05 +0200 Subject: [PATCH 10/10] minor, mostly stylistic, changes --- geometry.go | 10 ++++------ imdraw/imdraw.go | 14 ++++++++++---- pixelgl/canvas.go | 14 ++++++-------- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/geometry.go b/geometry.go index 928c9c5..13a574d 100644 --- a/geometry.go +++ b/geometry.go @@ -263,7 +263,7 @@ func (r Rect) Union(s Rect) Rect { // Layout is: // [0] [2] [4] // [1] [3] [5] -// 0 0 1 [implicit row] +// 0 0 1 (implicit row) type Matrix [6]float64 // IM stands for identity matrix. Does nothing, no transformation. @@ -332,11 +332,9 @@ func (m Matrix) Project(u Vec) Vec { // Unproject does the inverse operation to Project. // -// It turns out that multiplying a vector by the inverse matrix of m -// can be nearly-accomplished by subtracting the translate part of the -// matrix and multplying by the inverse of the top-left 2x2 matrix, -// and the inverse of a 2x2 matrix is simple enough to just be -// inlined in the computation. +// It turns out that multiplying a vector by the inverse matrix of m can be nearly-accomplished by +// subtracting the translate part of the matrix and multplying by the inverse of the top-left 2x2 +// matrix, and the inverse of a 2x2 matrix is simple enough to just be inlined in the computation. // // Time complexity is O(1). func (m Matrix) Unproject(u Vec) Vec { diff --git a/imdraw/imdraw.go b/imdraw/imdraw.go index e5a8c95..f7ce769 100644 --- a/imdraw/imdraw.go +++ b/imdraw/imdraw.go @@ -260,8 +260,8 @@ func (imd *IMDraw) getAndClearPoints() []point { // use one of the existing pools so we don't reallocate as often if len(imd.pool) > 0 { pos := len(imd.pool) - 1 - imd.points = imd.pool[pos] - imd.pool = imd.pool[0:pos] + imd.points = imd.pool[pos][:0] + imd.pool = imd.pool[:pos] } else { imd.points = nil } @@ -306,7 +306,7 @@ func (imd *IMDraw) fillRectangle() { in: (a.in + b.in) / 2, } - for k, p := range []point{a, b, c, a, b, d} { + for k, p := range [...]point{a, b, c, a, b, d} { (*imd.tri)[j+k].Position = p.pos (*imd.tri)[j+k].Color = p.col (*imd.tri)[j+k].Picture = p.pic @@ -316,6 +316,7 @@ func (imd *IMDraw) fillRectangle() { imd.applyMatrixAndMask(off) imd.batch.Dirty() + imd.restorePoints(points) } @@ -339,6 +340,7 @@ func (imd *IMDraw) outlineRectangle(thickness float64) { imd.pushPt(pixel.V(b.pos.X, a.pos.Y), mid) imd.polyline(thickness, true) } + imd.restorePoints(points) } @@ -354,7 +356,7 @@ func (imd *IMDraw) fillPolygon() { imd.tri.SetLen(imd.tri.Len() + 3*(len(points)-2)) for i, j := 1, off; i+1 < len(points); i, j = i+1, j+3 { - for k, p := range []int{0, i, i + 1} { + for k, p := range [...]int{0, i, i + 1} { tri := &(*imd.tri)[j+k] tri.Position = points[p].pos tri.Color = points[p].col @@ -365,6 +367,7 @@ func (imd *IMDraw) fillPolygon() { imd.applyMatrixAndMask(off) imd.batch.Dirty() + imd.restorePoints(points) } @@ -407,6 +410,7 @@ func (imd *IMDraw) fillEllipseArc(radius pixel.Vec, low, high float64) { imd.applyMatrixAndMask(off) imd.batch.Dirty() } + imd.restorePoints(points) } @@ -506,6 +510,7 @@ func (imd *IMDraw) outlineEllipseArc(radius pixel.Vec, low, high, thickness floa } } } + imd.restorePoints(points) } @@ -617,5 +622,6 @@ func (imd *IMDraw) polyline(thickness float64, closed bool) { imd.fillEllipseArc(pixel.V(thickness/2, thickness/2), normal.Angle(), normal.Angle()-math.Pi) } } + imd.restorePoints(points) } diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go index b7b5cfc..b8e4057 100644 --- a/pixelgl/canvas.go +++ b/pixelgl/canvas.go @@ -90,15 +90,13 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture { } // SetMatrix sets a Matrix that every point will be projected by. -// pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So -// [0] [2] [4] [0] [3] [6] -// [1] [3] [5] => [1] [4] [7] -// 0 0 1 0 0 1 -// since all matrix ops are affine, the last row never changes, -// and we don't need to copy it -// func (c *Canvas) SetMatrix(m pixel.Matrix) { - for i, j := range [6]int{ 0, 1, 3, 4, 6, 7} { + // pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So + // [0] [2] [4] [0] [3] [6] + // [1] [3] [5] => [1] [4] [7] + // 0 0 1 0 0 1 + // since all matrix ops are affine, the last row never changes, and we don't need to copy it + for i, j := range [...]int{0, 1, 3, 4, 6, 7} { c.mat[j] = float32(m[i]) } }