From d598e68c030a62353bc395095bf874dc533b7530 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Tue, 28 Feb 2017 18:24:25 +0100
Subject: [PATCH] implement and document PictureData

---
 data.go     | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 graphics.go | 234 -------------------------------------------
 2 files changed, 281 insertions(+), 234 deletions(-)
 create mode 100644 data.go
 delete mode 100644 graphics.go

diff --git a/data.go b/data.go
new file mode 100644
index 0000000..1406660
--- /dev/null
+++ b/data.go
@@ -0,0 +1,281 @@
+package pixel
+
+import (
+	"fmt"
+	"image"
+	"image/color"
+	"image/draw"
+	"math"
+)
+
+// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
+// Color and Texture.
+type TrianglesData []struct {
+	Position Vec
+	Color    NRGBA
+	Picture  Vec
+}
+
+// MakeTrianglesData creates TrianglesData of length len initialized with default property values.
+//
+// Prefer this function to make(TrianglesData, len), because make zeros them, while this function
+// does a correct intialization.
+func MakeTrianglesData(len int) TrianglesData {
+	td := TrianglesData{}
+	td.SetLen(len)
+	return td
+}
+
+// Len returns the number of vertices in TrianglesData.
+func (td *TrianglesData) Len() int {
+	return len(*td)
+}
+
+// SetLen resizes TrianglesData to len, while keeping the original content.
+//
+// If len is greater than TrianglesData's current length, the new data is filled with default
+// values ((0, 0), white, (-1, -1)).
+func (td *TrianglesData) SetLen(len int) {
+	if len > td.Len() {
+		needAppend := len - td.Len()
+		for i := 0; i < needAppend; i++ {
+			*td = append(*td, struct {
+				Position Vec
+				Color    NRGBA
+				Picture  Vec
+			}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
+		}
+	}
+	if len < td.Len() {
+		*td = (*td)[:len]
+	}
+}
+
+// Slice returns a sub-Triangles of this TrianglesData.
+func (td *TrianglesData) Slice(i, j int) Triangles {
+	s := TrianglesData((*td)[i:j])
+	return &s
+}
+
+func (td *TrianglesData) updateData(t Triangles) {
+	// fast path optimization
+	if t, ok := t.(*TrianglesData); ok {
+		copy(*td, *t)
+		return
+	}
+
+	// slow path manual copy
+	if t, ok := t.(TrianglesPosition); ok {
+		for i := range *td {
+			(*td)[i].Position = t.Position(i)
+		}
+	}
+	if t, ok := t.(TrianglesColor); ok {
+		for i := range *td {
+			(*td)[i].Color = t.Color(i)
+		}
+	}
+	if t, ok := t.(TrianglesPicture); ok {
+		for i := range *td {
+			(*td)[i].Picture = t.Picture(i)
+		}
+	}
+}
+
+// Update copies vertex properties from the supplied Triangles into this TrianglesData.
+//
+// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
+func (td *TrianglesData) Update(t Triangles) {
+	if td.Len() != t.Len() {
+		panic(fmt.Errorf("%T.Update: invalid triangles length", td))
+	}
+	td.updateData(t)
+}
+
+// Copy returns an exact independent copy of this TrianglesData.
+func (td *TrianglesData) Copy() Triangles {
+	copyTd := TrianglesData{}
+	copyTd.SetLen(td.Len())
+	copyTd.Update(td)
+	return &copyTd
+}
+
+// Position returns the position property of i-th vertex.
+func (td *TrianglesData) Position(i int) Vec {
+	return (*td)[i].Position
+}
+
+// Color returns the color property of i-th vertex.
+func (td *TrianglesData) Color(i int) NRGBA {
+	return (*td)[i].Color
+}
+
+// Picture returns the picture property of i-th vertex.
+func (td *TrianglesData) Picture(i int) Vec {
+	return (*td)[i].Picture
+}
+
+// PictureData specifies an in-memory rectangular area of NRGBA pixels and implements Picture and
+// PictureColor.
+//
+// Pixels are small rectangles of unit size of form (x, y, x+1, y+1), where x and y are integers.
+// PictureData contains and assigns a color to all pixels that are at least partially contained
+// within it's Bounds (Rect).
+//
+// The struct's innards are exposed for convenience, manual modification is at your own risk.
+type PictureData struct {
+	Pix    []NRGBA
+	Stride int
+	Rect   Rect
+}
+
+// MakePictureData creates a zero-initialized PictureData covering the given rectangle.
+func MakePictureData(rect Rect) PictureData {
+	w := int(math.Ceil(rect.Pos.X()+rect.Size.X())) - int(math.Floor(rect.Pos.X()))
+	h := int(math.Ceil(rect.Pos.Y()+rect.Size.Y())) - int(math.Floor(rect.Pos.Y()))
+	pd := PictureData{
+		Stride: w,
+		Rect:   rect,
+	}
+	pd.Pix = make([]NRGBA, w*h)
+	return pd
+}
+
+func verticalFlip(nrgba *image.NRGBA) {
+	bounds := nrgba.Bounds()
+	width := bounds.Dx()
+
+	tmpRow := make([]uint8, width*4)
+	for i, j := 0, bounds.Dy()-1; i < j; i, j = i+1, j-1 {
+		iRow := nrgba.Pix[i*nrgba.Stride : i*nrgba.Stride+width*4]
+		jRow := nrgba.Pix[j*nrgba.Stride : j*nrgba.Stride+width*4]
+
+		copy(tmpRow, iRow)
+		copy(iRow, jRow)
+		copy(jRow, tmpRow)
+	}
+}
+
+// PictureDataFromImage converts an image.Image into PictureData.
+//
+// The resulting PictureData's Bounds will be the equivalent of the supplied image.Image's Bounds.
+func PictureDataFromImage(img image.Image) PictureData {
+	nrgba := image.NewNRGBA(image.Rect(
+		0, 0,
+		img.Bounds().Dx(), img.Bounds().Dy(),
+	))
+	draw.Draw(nrgba, nrgba.Bounds(), img, img.Bounds().Min, draw.Src)
+
+	verticalFlip(nrgba)
+
+	pd := MakePictureData(R(
+		float64(nrgba.Bounds().Min.X),
+		float64(nrgba.Bounds().Min.Y),
+		float64(nrgba.Bounds().Dx()),
+		float64(nrgba.Bounds().Dy()),
+	))
+
+	for i := range pd.Pix {
+		pd.Pix[i] = NRGBA{
+			R: float64(nrgba.Pix[i*4+0]) / 255,
+			G: float64(nrgba.Pix[i*4+1]) / 255,
+			B: float64(nrgba.Pix[i*4+2]) / 255,
+			A: float64(nrgba.Pix[i*4+3]) / 255,
+		}
+	}
+
+	return pd
+}
+
+// PictureDataFromPicture converts an arbitrary Picture into PictureData (the conversion may be
+// lossy, because PictureData works with unit-sized pixels).
+//
+// Bounds are preserved.
+func PictureDataFromPicture(pic Picture) PictureData {
+	if pd, ok := pic.(PictureData); ok {
+		return pd
+	}
+
+	bounds := pic.Bounds()
+	pd := MakePictureData(bounds)
+
+	if pic, ok := pic.(PictureColor); ok {
+		for y := bounds.Pos.Y(); y < bounds.Pos.Y()+bounds.Size.Y(); y++ {
+			for x := bounds.Pos.X(); x < bounds.Pos.X()+bounds.Size.X(); x++ {
+				at := V(x, y)
+				pd.SetColor(at, pic.Color(at))
+			}
+		}
+	}
+
+	return pd
+}
+
+// Image converts PictureData into an image.NRGBA.
+//
+// The resulting image.NRGBA's Bounds will be equivalent of the PictureData's Bounds.
+func (pd PictureData) Image() *image.NRGBA {
+	fmt.Println(pd.Rect.Pos + pd.Rect.Size)
+	bounds := image.Rect(
+		int(math.Floor(pd.Rect.Pos.X())),
+		int(math.Floor(pd.Rect.Pos.Y())),
+		int(math.Ceil(pd.Rect.Pos.X()+pd.Rect.Size.X())),
+		int(math.Ceil(pd.Rect.Pos.Y()+pd.Rect.Size.Y())),
+	)
+	nrgba := image.NewNRGBA(bounds)
+	fmt.Println(nrgba.Rect)
+
+	i := 0
+	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+		for x := bounds.Min.X; x < bounds.Max.X; x++ {
+			off := pd.offset(V(float64(x), float64(y)))
+			nrgba.Pix[i*4+0] = uint8(pd.Pix[off].R * 255)
+			nrgba.Pix[i*4+1] = uint8(pd.Pix[off].G * 255)
+			nrgba.Pix[i*4+2] = uint8(pd.Pix[off].B * 255)
+			nrgba.Pix[i*4+3] = uint8(pd.Pix[off].A * 255)
+			i++
+		}
+	}
+
+	verticalFlip(nrgba)
+
+	fmt.Println(len(nrgba.Pix) / 4)
+
+	return nrgba
+}
+
+func (pd PictureData) offset(at Vec) int {
+	at -= pd.Rect.Pos
+	x, y := int(at.X()), int(at.Y())
+	return y*pd.Stride + x
+}
+
+// Bounds returns the bounds of this PictureData.
+func (pd PictureData) Bounds() Rect {
+	return pd.Rect
+}
+
+// Slice returns a sub-Picture of this PictureData inside the supplied rectangle.
+func (pd PictureData) Slice(r Rect) Picture {
+	return PictureData{
+		Pix:    pd.Pix[pd.offset(r.Pos):],
+		Stride: pd.Stride,
+		Rect:   r,
+	}
+}
+
+// Color returns the color located at the given position.
+func (pd PictureData) Color(at Vec) NRGBA {
+	if !pd.Rect.Contains(at) {
+		return NRGBA{0, 0, 0, 0}
+	}
+	return pd.Pix[pd.offset(at)]
+}
+
+// SetColor changes the color located at the given position.
+func (pd PictureData) SetColor(at Vec, color color.Color) {
+	if !pd.Rect.Contains(at) {
+		return
+	}
+	pd.Pix[pd.offset(at)] = NRGBAModel.Convert(color).(NRGBA)
+}
diff --git a/graphics.go b/graphics.go
deleted file mode 100644
index 9b78898..0000000
--- a/graphics.go
+++ /dev/null
@@ -1,234 +0,0 @@
-package pixel
-
-import (
-	"fmt"
-	"image/color"
-)
-
-// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
-// Color and Texture.
-type TrianglesData []struct {
-	Position Vec
-	Color    NRGBA
-	Picture  Vec
-}
-
-// MakeTrianglesData creates TrianglesData of length len initialized with default property values.
-//
-// Prefer this function to make(TrianglesData, len), because make zeros them, while this function
-// does a correct intialization.
-func MakeTrianglesData(len int) TrianglesData {
-	td := TrianglesData{}
-	td.SetLen(len)
-	return td
-}
-
-// Len returns the number of vertices in TrianglesData.
-func (td *TrianglesData) Len() int {
-	return len(*td)
-}
-
-// SetLen resizes TrianglesData to len, while keeping the original content.
-//
-// If len is greater than TrianglesData's current length, the new data is filled with default
-// values ((0, 0), white, (-1, -1)).
-func (td *TrianglesData) SetLen(len int) {
-	if len > td.Len() {
-		needAppend := len - td.Len()
-		for i := 0; i < needAppend; i++ {
-			*td = append(*td, struct {
-				Position Vec
-				Color    NRGBA
-				Picture  Vec
-			}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
-		}
-	}
-	if len < td.Len() {
-		*td = (*td)[:len]
-	}
-}
-
-// Slice returns a sub-Triangles of this TrianglesData.
-func (td *TrianglesData) Slice(i, j int) Triangles {
-	s := TrianglesData((*td)[i:j])
-	return &s
-}
-
-func (td *TrianglesData) updateData(t Triangles) {
-	// fast path optimization
-	if t, ok := t.(*TrianglesData); ok {
-		copy(*td, *t)
-		return
-	}
-
-	// slow path manual copy
-	if t, ok := t.(TrianglesPosition); ok {
-		for i := range *td {
-			(*td)[i].Position = t.Position(i)
-		}
-	}
-	if t, ok := t.(TrianglesColor); ok {
-		for i := range *td {
-			(*td)[i].Color = t.Color(i)
-		}
-	}
-	if t, ok := t.(TrianglesPicture); ok {
-		for i := range *td {
-			(*td)[i].Picture = t.Picture(i)
-		}
-	}
-}
-
-// Update copies vertex properties from the supplied Triangles into this TrianglesData.
-//
-// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
-func (td *TrianglesData) Update(t Triangles) {
-	if td.Len() != t.Len() {
-		panic(fmt.Errorf("%T.Update: invalid triangles length", td))
-	}
-	td.updateData(t)
-}
-
-// Copy returns an exact independent copy of this TrianglesData.
-func (td *TrianglesData) Copy() Triangles {
-	copyTd := TrianglesData{}
-	copyTd.SetLen(td.Len())
-	copyTd.Update(td)
-	return &copyTd
-}
-
-// Position returns the position property of i-th vertex.
-func (td *TrianglesData) Position(i int) Vec {
-	return (*td)[i].Position
-}
-
-// Color returns the color property of i-th vertex.
-func (td *TrianglesData) Color(i int) NRGBA {
-	return (*td)[i].Color
-}
-
-// 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
-// the Sprite, use Target's SetTransform method.
-type Sprite struct {
-	data TrianglesData
-	d    Drawer
-}
-
-// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
-// the dimensions of the Picture.
-func NewSprite(pic Picture) *Sprite {
-	s := &Sprite{
-		data: TrianglesData{
-			{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.d = Drawer{Triangles: &s.data}
-	s.SetPicture(pic)
-	return s
-}
-
-// SetPicture changes the Picture of the Sprite and resizes it accordingly.
-func (s *Sprite) SetPicture(pic Picture) {
-	oldPic := s.d.Picture
-	s.d.Picture = pic
-	if oldPic != nil && oldPic.Bounds().Size == pic.Bounds().Size {
-		return
-	}
-	w, h := pic.Bounds().Size.XY()
-	s.data[0].Position = V(0, 0)
-	s.data[1].Position = V(w, 0)
-	s.data[2].Position = V(w, h)
-	s.data[3].Position = V(0, 0)
-	s.data[4].Position = V(w, h)
-	s.data[5].Position = V(0, h)
-	s.d.Dirty()
-}
-
-// Picture returns the current Picture of the Sprite.
-func (s *Sprite) Picture() Picture {
-	return s.d.Picture
-}
-
-// Draw draws the Sprite onto the provided Target.
-func (s *Sprite) Draw(t Target) {
-	s.d.Draw(t)
-}
-
-// Polygon is a convex polygon shape filled with a single color.
-type Polygon struct {
-	data TrianglesData
-	d    Drawer
-	col  NRGBA
-}
-
-// NewPolygon creates a Polygon with specified color and points. Points can be in clock-wise or
-// counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
-func NewPolygon(c color.Color, points ...Vec) *Polygon {
-	p := &Polygon{
-		data: TrianglesData{},
-	}
-	p.d = Drawer{Triangles: &p.data}
-	p.SetColor(c)
-	p.SetPoints(points...)
-	return p
-}
-
-// SetColor changes the color of the Polygon.
-//
-// If the Polygon is very large, this method might end up being too expensive. Consider using
-// a color mask on a Target, in such a case.
-func (p *Polygon) SetColor(c color.Color) {
-	p.col = NRGBAModel.Convert(c).(NRGBA)
-	for i := range p.data {
-		p.data[i].Color = p.col
-	}
-	p.d.Dirty()
-}
-
-// Color returns the current color of the Polygon.
-func (p *Polygon) Color() NRGBA {
-	return p.col
-}
-
-// SetPoints sets the points of the Polygon. The number of points might differ from the original
-// count.
-//
-// This method is more effective, than creating a new Polygon with the given points.
-//
-// However, it is less expensive than using a transform on a Target.
-func (p *Polygon) SetPoints(points ...Vec) {
-	p.data.SetLen(3 * (len(points) - 2))
-	for i := 2; i < len(points); i++ {
-		p.data[(i-2)*3+0].Position = points[0]
-		p.data[(i-2)*3+1].Position = points[i-1]
-		p.data[(i-2)*3+2].Position = points[i]
-	}
-	for i := range p.data {
-		p.data[i].Color = p.col
-	}
-	p.d.Dirty()
-}
-
-// Points returns a slice of points of the Polygon in the order they where supplied.
-func (p *Polygon) Points() []Vec {
-	points := make([]Vec, p.data.Len())
-	for i := range p.data {
-		points[i] = p.data[i].Position
-	}
-	return points
-}
-
-// Draw draws the Polygon onto the Target.
-func (p *Polygon) Draw(t Target) {
-	p.d.Draw(t)
-}
-- 
GitLab