From c1843b8608590c2fdd27529de6d6c2c7956c9672 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Thu, 23 Feb 2017 13:07:40 +0100
Subject: [PATCH] add Drawer struct

---
 batch.go               |  5 +--
 drawer.go              | 87 ++++++++++++++++++++++++++++++++++++++++++
 graphics.go            | 73 +++++++----------------------------
 pixelgl/canvas.go      | 13 +++----
 pixelgl/gltriangles.go |  6 +--
 5 files changed, 111 insertions(+), 73 deletions(-)
 create mode 100644 drawer.go

diff --git a/batch.go b/batch.go
index 32ff464..2676891 100644
--- a/batch.go
+++ b/batch.go
@@ -41,7 +41,6 @@ func (b *Batch) Clear() {
 
 // Draw draws all objects that are currently in the Batch onto another Target.
 func (b *Batch) Draw(t Target) {
-	t.SetPicture(b.fixpic)
 	b.cont.Draw(t)
 }
 
@@ -98,8 +97,8 @@ func (bt *batchTriangles) Draw() {
 		})
 		bt.data[i].Position = V(float64(transPos.X()), float64(transPos.Y()))
 		bt.data[i].Color = bt.data[i].Color.Mul(bt.batch.col)
-		if bt.batch.pic != nil && bt.data[i].Texture != V(-1, -1) {
-			bt.data[i].Texture = pictureBounds(bt.batch.pic, bt.data[i].Texture)
+		if bt.batch.pic != nil && bt.data[i].Picture != V(-1, -1) {
+			bt.data[i].Picture = pictureBounds(bt.batch.pic, bt.data[i].Picture)
 		}
 	}
 	bt.trans.Update(&bt.data)
diff --git a/drawer.go b/drawer.go
new file mode 100644
index 0000000..d5ec2fe
--- /dev/null
+++ b/drawer.go
@@ -0,0 +1,87 @@
+package pixel
+
+// Drawer glues all the fundamental interfaces (Target, Triangles, Picture) into a coherent and the
+// only intended usage pattern.
+//
+// Drawer makes it possible to draw any combination of Triangles and Picture onto any Target
+// efficiently.
+//
+// To create a Drawer, just assign it's Triangles and Picture fields:
+//   d := pixel.Drawer{Triangles: t, Picture: p}
+//
+// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a
+// Picture.
+//
+// Whenever you change the Triangles, call Dirty to notify Drawer that Triangles changed. You don't
+// need to notify Drawer about a change of Picture.
+type Drawer struct {
+	Triangles Triangles
+	Picture   Picture
+
+	tris   map[Target]TargetTriangles
+	clean  map[Target]bool
+	pics   map[targetPicturePair]TargetPicture
+	inited bool
+}
+
+type targetPicturePair struct {
+	Target  Target
+	Picture Picture
+}
+
+func (d *Drawer) lazyInit() {
+	if !d.inited {
+		d.tris = make(map[Target]TargetTriangles)
+		d.clean = make(map[Target]bool)
+		d.pics = make(map[targetPicturePair]TargetPicture)
+		d.inited = true
+	}
+}
+
+// Dirty marks the Triangles of this Drawer as changed. If not called, changes will not be visible
+// when drawing.
+func (d *Drawer) Dirty() {
+	d.lazyInit()
+
+	for t := range d.clean {
+		d.clean[t] = false
+	}
+}
+
+// Draw efficiently draws Triangles with Picture onto the provided Target.
+//
+// If Triangles is nil, nothing will be drawn. If Picture is nil, Triangles will be drawn without a
+// Picture.
+func (d *Drawer) Draw(t Target) {
+	d.lazyInit()
+
+	if d.Triangles == nil {
+		return
+	}
+
+	tri := d.tris[t]
+	if tri == nil {
+		tri = t.MakeTriangles(d.Triangles)
+		d.tris[t] = tri
+		d.clean[t] = true
+	}
+
+	if !d.clean[t] {
+		tri.SetLen(d.Triangles.Len())
+		tri.Update(d.Triangles)
+		d.clean[t] = true
+	}
+
+	if d.Picture == nil {
+		tri.Draw()
+		return
+	}
+
+	pic := d.pics[targetPicturePair{t, d.Picture}]
+	if pic == nil {
+		pic = t.MakePicture(d.Picture)
+		d.pics[targetPicturePair{t, d.Picture}] = pic
+	}
+
+	pic.Draw(tri)
+}
diff --git a/graphics.go b/graphics.go
index 1ac0cf0..2e81cd8 100644
--- a/graphics.go
+++ b/graphics.go
@@ -10,7 +10,7 @@ import (
 type TrianglesData []struct {
 	Position Vec
 	Color    NRGBA
-	Texture  Vec
+	Picture  Vec
 }
 
 // MakeTrianglesData creates TrianglesData of length len initialized with default property values.
@@ -39,7 +39,7 @@ func (td *TrianglesData) SetLen(len int) {
 			*td = append(*td, struct {
 				Position Vec
 				Color    NRGBA
-				Texture  Vec
+				Picture  Vec
 			}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
 		}
 	}
@@ -72,9 +72,9 @@ func (td *TrianglesData) updateData(t Triangles) {
 			(*td)[i].Color = t.Color(i)
 		}
 	}
-	if t, ok := t.(TrianglesTexture); ok {
+	if t, ok := t.(TrianglesPicture); ok {
 		for i := range *td {
-			(*td)[i].Texture = t.Texture(i)
+			(*td)[i].Picture = t.Picture(i)
 		}
 	}
 }
@@ -107,54 +107,9 @@ func (td *TrianglesData) Color(i int) NRGBA {
 	return (*td)[i].Color
 }
 
-// Texture returns the texture property of i-th vertex.
-func (td *TrianglesData) Texture(i int) Vec {
-	return (*td)[i].Texture
-}
-
-// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
-//
-// It does so by creating a separate Triangles instance for each Target. The instances are
-// correctly updated alongside the wrapped Triangles.
-type TrianglesDrawer struct {
-	Triangles
-
-	tris  map[Target]TargetTriangles
-	dirty bool
-}
-
-func (td *TrianglesDrawer) flush() {
-	if !td.dirty {
-		return
-	}
-	td.dirty = false
-
-	for _, t := range td.tris {
-		t.SetLen(td.Len())
-		t.Update(td.Triangles)
-	}
-}
-
-// Draw draws the wrapped Triangles onto the provided Target.
-func (td *TrianglesDrawer) Draw(target Target) {
-	if td.tris == nil {
-		td.tris = make(map[Target]TargetTriangles)
-	}
-
-	td.flush()
-
-	tri := td.tris[target]
-	if tri == nil {
-		tri = target.MakeTriangles(td.Triangles)
-		td.tris[target] = tri
-	}
-	tri.Draw()
-}
-
-// Dirty marks the underlying container as changed (dirty). Always call this when you change the
-// underlying Triangles (by Update, SetLen, etc.).
-func (td *TrianglesDrawer) Dirty() {
-	td.dirty = true
+// Picture returns the picture property of i-th vertex.
+func (td *TrianglesData) Picture(i int) Vec {
+	return (*td)[i].Picture
 }
 
 // Sprite is a picture that can be drawn onto a Target. To change the position/rotation/scale of
@@ -170,12 +125,12 @@ type Sprite struct {
 func NewSprite(pic *GLPicture) *Sprite {
 	s := &Sprite{
 		data: TrianglesData{
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)},
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
-			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 0)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 0)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 1)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 0)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(1, 1)},
+			{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Picture: V(0, 1)},
 		},
 	}
 	s.td = TrianglesDrawer{Triangles: &s.data}
@@ -207,7 +162,6 @@ func (s *Sprite) Picture() *GLPicture {
 
 // Draw draws the Sprite onto the provided Target.
 func (s *Sprite) Draw(t Target) {
-	t.SetPicture(s.pic)
 	s.td.Draw(t)
 }
 
@@ -277,6 +231,5 @@ func (p *Polygon) Points() []Vec {
 
 // Draw draws the Polygon onto the Target.
 func (p *Polygon) Draw(t Target) {
-	t.SetPicture(nil)
 	p.td.Draw(t)
 }
diff --git a/pixelgl/canvas.go b/pixelgl/canvas.go
index 00b4802..a75d6f7 100644
--- a/pixelgl/canvas.go
+++ b/pixelgl/canvas.go
@@ -59,12 +59,12 @@ func NewCanvas(width, height float64, smooth bool) *Canvas {
 
 	white := pixel.NRGBA{R: 1, G: 1, B: 1, A: 1}
 	c.drawTd = pixel.TrianglesDrawer{Triangles: &pixel.TrianglesData{
-		{Position: pixel.V(-1, -1), Color: white, Texture: pixel.V(0, 0)},
-		{Position: pixel.V(1, -1), Color: white, Texture: pixel.V(1, 0)},
-		{Position: pixel.V(1, 1), Color: white, Texture: pixel.V(1, 1)},
-		{Position: pixel.V(-1, -1), Color: white, Texture: pixel.V(0, 0)},
-		{Position: pixel.V(1, 1), Color: white, Texture: pixel.V(1, 1)},
-		{Position: pixel.V(-1, 1), Color: white, Texture: pixel.V(0, 1)},
+		{Position: pixel.V(-1, -1), Color: white, Picture: pixel.V(0, 0)},
+		{Position: pixel.V(1, -1), Color: white, Picture: pixel.V(1, 0)},
+		{Position: pixel.V(1, 1), Color: white, Picture: pixel.V(1, 1)},
+		{Position: pixel.V(-1, -1), Color: white, Picture: pixel.V(0, 0)},
+		{Position: pixel.V(1, 1), Color: white, Picture: pixel.V(1, 1)},
+		{Position: pixel.V(-1, 1), Color: white, Picture: pixel.V(0, 1)},
 	}}
 
 	c.pic = nil
@@ -126,7 +126,6 @@ func (c *Canvas) Clear(col color.Color) {
 // Draw draws the content of the Canvas onto another Target. If no transform is applied, the content
 // is fully stretched to fit the Target.
 func (c *Canvas) Draw(t pixel.Target) {
-	t.SetPicture(c.Content())
 	c.drawTd.Draw(t)
 }
 
diff --git a/pixelgl/gltriangles.go b/pixelgl/gltriangles.go
index a0b3939..8a7a46b 100644
--- a/pixelgl/gltriangles.go
+++ b/pixelgl/gltriangles.go
@@ -76,7 +76,7 @@ func (gt *glTriangles) updateData(t pixel.Triangles) {
 			var (
 				px, py = (*t)[i].Position.XY()
 				col    = (*t)[i].Color
-				tx, ty = (*t)[i].Texture.XY()
+				tx, ty = (*t)[i].Picture.XY()
 			)
 			gt.data[i*gt.vs.Stride()+0] = float32(px)
 			gt.data[i*gt.vs.Stride()+1] = float32(py)
@@ -106,9 +106,9 @@ func (gt *glTriangles) updateData(t pixel.Triangles) {
 			gt.data[i*gt.vs.Stride()+5] = float32(col.A)
 		}
 	}
-	if t, ok := t.(pixel.TrianglesTexture); ok {
+	if t, ok := t.(pixel.TrianglesPicture); ok {
 		for i := 0; i < gt.Len(); i++ {
-			tx, ty := t.Texture(i).XY()
+			tx, ty := t.Picture(i).XY()
 			gt.data[i*gt.vs.Stride()+6] = float32(tx)
 			gt.data[i*gt.vs.Stride()+7] = float32(ty)
 		}
-- 
GitLab