From 3d13e52addd054d3adaf28f16c67db1bc9c9ccf2 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Tue, 7 Mar 2017 20:33:07 +0100
Subject: [PATCH] allow efficiently drawing one Canvas onto another

---
 pixelgl/canvas.go | 90 +++++++++++++++++++++++++++++++++++------------
 pixelgl/window.go |  6 ++--
 2 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go
index 4b94fd2..5a57c96 100644
--- a/pixelgl/canvas.go
+++ b/pixelgl/canvas.go
@@ -73,12 +73,26 @@ func (c *Canvas) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
 //
 // PictureColor is supported.
 func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
+	// short paths
 	if cp, ok := p.(*canvasPicture); ok {
 		tp := new(canvasPicture)
 		*tp = *cp
-		tp.c = c
+		tp.dst = c
 		return tp
 	}
+	if ccp, ok := p.(*canvasCanvasPicture); ok {
+		tp := new(canvasCanvasPicture)
+		*tp = *ccp
+		tp.dst = c
+		return tp
+	}
+	if canvas, ok := p.(*Canvas); ok {
+		return &canvasCanvasPicture{
+			src:    canvas,
+			dst:    c,
+			bounds: c.bounds,
+		}
+	}
 
 	bounds := p.Bounds()
 	bx, by, bw, bh := intBounds(bounds)
@@ -112,7 +126,7 @@ func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
 			float64(bw), float64(bh),
 		),
 		bounds: bounds,
-		c:      c,
+		dst:    c,
 	}
 	cp.orig = cp
 	return cp
@@ -239,9 +253,10 @@ func (c *Canvas) Original() pixel.Picture {
 func (c *Canvas) Color(at pixel.Vec) pixel.NRGBA {
 	if c.orig.dirty {
 		mainthread.Call(func() {
-			c.f.Texture.Begin()
-			c.orig.pixels = c.f.Texture.Pixels(0, 0, c.f.Texture.Width(), c.f.Texture.Height())
-			c.f.Texture.End()
+			tex := c.f.Texture()
+			tex.Begin()
+			c.orig.pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
+			tex.End()
 		})
 		c.orig.dirty = false
 	}
@@ -265,7 +280,7 @@ type canvasTriangles struct {
 	c *Canvas
 }
 
-func (ct *canvasTriangles) draw(cp *canvasPicture) {
+func (ct *canvasTriangles) draw(tex *glhf.Texture, borders, bounds pixel.Rect) {
 	ct.c.orig.dirty = true
 
 	// save the current state vars to avoid race condition
@@ -286,35 +301,35 @@ func (ct *canvasTriangles) draw(cp *canvasPicture) {
 		ct.c.s.SetUniformAttr(canvasTransform, mat)
 		ct.c.s.SetUniformAttr(canvasColorMask, col)
 
-		if cp == nil {
+		if tex == nil {
 			ct.vs.Begin()
 			ct.vs.Draw()
 			ct.vs.End()
 		} else {
-			cp.tex.Begin()
+			tex.Begin()
 
 			ct.c.s.SetUniformAttr(canvasTexBorders, mgl32.Vec4{
-				float32(cp.borders.X()),
-				float32(cp.borders.Y()),
-				float32(cp.borders.W()),
-				float32(cp.borders.H()),
+				float32(borders.X()),
+				float32(borders.Y()),
+				float32(borders.W()),
+				float32(borders.H()),
 			})
 			ct.c.s.SetUniformAttr(canvasTexBounds, mgl32.Vec4{
-				float32(cp.bounds.X()),
-				float32(cp.bounds.Y()),
-				float32(cp.bounds.W()),
-				float32(cp.bounds.H()),
+				float32(bounds.X()),
+				float32(bounds.Y()),
+				float32(bounds.W()),
+				float32(bounds.H()),
 			})
 
-			if cp.tex.Smooth() != ct.c.smooth {
-				cp.tex.SetSmooth(ct.c.smooth)
+			if tex.Smooth() != ct.c.smooth {
+				tex.SetSmooth(ct.c.smooth)
 			}
 
 			ct.vs.Begin()
 			ct.vs.Draw()
 			ct.vs.End()
 
-			cp.tex.End()
+			tex.End()
 		}
 
 		ct.c.s.End()
@@ -323,7 +338,7 @@ func (ct *canvasTriangles) draw(cp *canvasPicture) {
 }
 
 func (ct *canvasTriangles) Draw() {
-	ct.draw(nil)
+	ct.draw(nil, pixel.Rect{}, pixel.Rect{})
 }
 
 type canvasPicture struct {
@@ -332,7 +347,7 @@ type canvasPicture struct {
 	bounds  pixel.Rect
 
 	orig *canvasPicture
-	c    *Canvas
+	dst  *Canvas
 }
 
 func (cp *canvasPicture) Bounds() pixel.Rect {
@@ -352,10 +367,39 @@ func (cp *canvasPicture) Original() pixel.Picture {
 
 func (cp *canvasPicture) Draw(t pixel.TargetTriangles) {
 	ct := t.(*canvasTriangles)
-	if cp.c != ct.c {
+	if cp.dst != ct.c {
 		panic(fmt.Errorf("%T.Draw: TargetTriangles generated by different Canvas", cp))
 	}
-	ct.draw(cp)
+	ct.draw(cp.tex, cp.borders, cp.bounds)
+}
+
+type canvasCanvasPicture struct {
+	src, dst *Canvas
+	bounds   pixel.Rect
+	orig     *canvasCanvasPicture
+}
+
+func (ccp *canvasCanvasPicture) Bounds() pixel.Rect {
+	return ccp.bounds
+}
+
+func (ccp *canvasCanvasPicture) Slice(r pixel.Rect) pixel.Picture {
+	sp := new(canvasCanvasPicture)
+	*sp = *ccp
+	sp.bounds = r
+	return sp
+}
+
+func (ccp *canvasCanvasPicture) Original() pixel.Picture {
+	return ccp.orig
+}
+
+func (ccp *canvasCanvasPicture) Draw(t pixel.TargetTriangles) {
+	ct := t.(*canvasTriangles)
+	if ccp.dst != ct.c {
+		panic(fmt.Errorf("%T.Draw: TargetTriangles generated by different Canvas", ccp))
+	}
+	ct.draw(ccp.src.f.Texture(), ccp.src.orig.bounds, ccp.bounds)
 }
 
 const (
diff --git a/pixelgl/window.go b/pixelgl/window.go
index 3f52a66..a76f8a7 100644
--- a/pixelgl/window.go
+++ b/pixelgl/window.go
@@ -166,14 +166,14 @@ func (w *Window) Update() {
 	mainthread.Call(func() {
 		w.begin()
 
-		glhf.Bounds(0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height())
+		glhf.Bounds(0, 0, w.canvas.f.Texture().Width(), w.canvas.f.Texture().Height())
 
 		glhf.Clear(0, 0, 0, 0)
 		w.canvas.f.Begin()
 		w.canvas.f.Blit(
 			nil,
-			0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height(),
-			0, 0, w.canvas.f.Texture.Width(), w.canvas.f.Texture.Height(),
+			0, 0, w.canvas.f.Texture().Width(), w.canvas.f.Texture().Height(),
+			0, 0, w.canvas.f.Texture().Width(), w.canvas.f.Texture().Height(),
 		)
 		w.canvas.f.End()
 
-- 
GitLab